Compare commits

...

25 Commits

Author SHA1 Message Date
Lukas f. Paluch
680a8a16b2
Merge f27a3c5206fead40d77aebac4424397b58be7f94 into f159947e07f8aed075f2060e65fcc74d68d8c027 2026-04-16 16:25:44 +02:00
Bernhard B.
f159947e07
Merge pull request #833 from Gara-Dorta/optional-group-creation-fields
Mark name and members as optional fields in CreateGroupRequest
2026-04-09 22:48:04 +02:00
Bernhard B.
2838e1f879
Merge pull request #828 from Gara-Dorta/additional-api-checks
refactor: add missing checks on required fields
2026-04-09 22:18:55 +02:00
Era Dorta
59d6912f21 Mark name and members as optional fields in CreateGroupRequest 2026-04-08 19:26:42 +02:00
Era Dorta
5a883826ae refactor: add missing checks on required fields 2026-04-08 19:10:48 +02:00
Bernhard B.
2a776618e9
Merge pull request #827 from Gara-Dorta/split-reaction-request-class
Split reaction request class
2026-04-07 19:53:41 +02:00
Bernhard B.
fc99aba2f4
Merge pull request #831 from philljolly/fix/jsonrpc-variable-shadowing
fix: resolve Go variable shadowing that silently swallows errors in JSON-RPC mode
2026-04-07 19:51:54 +02:00
Phill Jolliffe
a01c2a61fa fix: resolve Go variable shadowing that silently swallows errors in JSON-RPC mode
24 functions in client.go use 'jsonRpc2Client, err :=' inside if blocks
where 'var err error' is already declared at function scope. The := creates
a new block-scoped err that shadows the outer one, causing 15 functions to
silently return nil instead of the actual error in JSON-RPC mode.

Fixes #506
2026-04-07 14:12:57 +01:00
Era Dorta
e46b1c9ea7 Merge branch 'master' into split-reaction-request-class 2026-04-06 21:34:31 +02:00
Bernhard B.
33829b2fa5
Merge pull request #829 from Gara-Dorta/readme-docs
Better formatting for the docs Readme instructions
2026-04-06 21:29:09 +02:00
Era Dorta
2911bc3632 Merge branch 'master' into readme-docs 2026-04-06 21:26:17 +02:00
Bernhard B.
bc0ea66c04
Merge pull request #830 from Gara-Dorta/docs-version
Set docs version dynamically.
2026-04-06 21:07:44 +02:00
Bernhard B.
b3ba748dd3
Merge pull request #826 from Gara-Dorta/add-required-to-json-schema
Add required to json schema
2026-04-06 21:05:52 +02:00
Era Dorta
3dd0001a32 Set docs version dynamically. 2026-04-06 18:15:06 +02:00
Era Dorta
b51505c8d9 Better formatting for the docs Readme instructions 2026-04-06 17:35:45 +02:00
Era Dorta
87ab2bb398 refactor: split Reaction in two classes to mark the Reaction field as optional 2026-04-06 17:05:45 +02:00
Era Dorta
4e541848e2 docs: regenerated docs with required fields 2026-04-06 16:57:57 +02:00
Era Dorta
40f299deff fix: add omitempty and generate swagger json with required fields 2026-04-06 16:57:14 +02:00
Bernhard B
7a9b9919ef updated signal-cli-native.patch 2026-04-04 21:36:01 +02:00
Bernhard B
d1d8ddc711 updated cross build instructions 2026-04-04 21:14:48 +02:00
Bernhard B
8345672499 updated signal-cli to v0.14.2 2026-04-04 21:08:51 +02:00
Bernhard B
eb34542216 increased timeout to account for slow startup on slow machines 2026-04-01 22:47:21 +02:00
Bernhard B
7687c5240b added API endpoints for pinning/unpinning messages in groups
see #820
2026-04-01 22:47:21 +02:00
Lukas Paluch
f27a3c5206
fix rootless entrypoint - clarify and append jsonrpc
- rework statemant to be more clear
    - append check of json-rpc mode to rootless part
2024-02-27 08:54:33 +01:00
Lukas f. Paluch
1be5684ae3
enable rootless start
fixes bbernhard/signal-cli-rest-api#490
2024-02-23 15:10:57 +01:00
15 changed files with 1419 additions and 157 deletions

View File

@ -1,5 +1,5 @@
ARG SIGNAL_CLI_VERSION=0.14.1 ARG SIGNAL_CLI_VERSION=0.14.2
ARG LIBSIGNAL_CLIENT_VERSION=0.87.4 ARG LIBSIGNAL_CLIENT_VERSION=0.90.0
ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION=0.14.1+morph027+2 ARG SIGNAL_CLI_NATIVE_PACKAGE_VERSION=0.14.1+morph027+2
ARG SWAG_VERSION=1.16.4 ARG SWAG_VERSION=1.16.4
@ -142,7 +142,7 @@ COPY src/plugin_loader.go /tmp/signal-cli-rest-api-src/
# build signal-cli-rest-api # build signal-cli-rest-api
RUN ls -la /tmp/signal-cli-rest-api-src RUN ls -la /tmp/signal-cli-rest-api-src
RUN cd /tmp/signal-cli-rest-api-src && ${GOPATH}/bin/swag init RUN cd /tmp/signal-cli-rest-api-src && ${GOPATH}/bin/swag init --requiredByDefault
RUN cd /tmp/signal-cli-rest-api-src && go build -o signal-cli-rest-api main.go RUN cd /tmp/signal-cli-rest-api-src && go build -o signal-cli-rest-api main.go
RUN cd /tmp/signal-cli-rest-api-src && go test ./client -v && go test ./utils -v RUN cd /tmp/signal-cli-rest-api-src && go test ./client -v && go test ./utils -v

View File

@ -3,8 +3,27 @@
set -x set -x
set -e set -e
[ -d /etc/docker ] && echo "$FILE is a directory."
[ -z "${SIGNAL_CLI_CONFIG_DIR}" ] && echo "SIGNAL_CLI_CONFIG_DIR environmental variable needs to be set! Aborting!" && exit 1; [ -z "${SIGNAL_CLI_CONFIG_DIR}" ] && echo "SIGNAL_CLI_CONFIG_DIR environmental variable needs to be set! Aborting!" && exit 1;
if [ "$(id -u)" -eq "${SIGNAL_CLI_UID}" ] && [ "$(id -g)" -eq "${SIGNAL_CLI_GID}" ]]
then
echo "UID and GID are already correct. Trying to start Signal Api"
# TODO: check mode
if [ "$MODE" = "json-rpc" ]
then
/usr/bin/jsonrpc2-helper
if [ -n "$JAVA_OPTS" ] ; then
echo "export JAVA_OPTS='$JAVA_OPTS'" >> /etc/default/supervisor
fi
service supervisor start
supervisorctl start all
fi
signal-cli-rest-api -signal-cli-config=${SIGNAL_CLI_CONFIG_DIR};
fi
usermod -u ${SIGNAL_CLI_UID} signal-api usermod -u ${SIGNAL_CLI_UID} signal-api
groupmod -o -g ${SIGNAL_CLI_GID} signal-api groupmod -o -g ${SIGNAL_CLI_GID} signal-api

View File

@ -0,0 +1,13 @@
[target.aarch64-unknown-linux-gnu]
pre-build = [
"apt update && apt install -y unzip",
"curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v25.9/protoc-25.9-linux-x86_64.zip && unzip protoc-25.9-linux-x86_64.zip -d /usr/",
"chmod 755 /usr/bin/protoc"
]
[target.armv7-unknown-linux-gnueabihf]
pre-build = [
"apt update && apt install -y unzip",
"curl -LO https://github.com/protocolbuffers/protobuf/releases/download/v25.9/protoc-25.9-linux-x86_64.zip && unzip protoc-25.9-linux-x86_64.zip -d /usr/",
"chmod 755 /usr/bin/protoc"
]

View File

@ -4,7 +4,8 @@
* download new release from `https://github.com/signalapp/libsignal-client/releases` * download new release from `https://github.com/signalapp/libsignal-client/releases`
* unzip + change into directory * unzip + change into directory
* cd into `java` directory * run `rm -rf target` to remove any build artifacts from previous builds
* copy `Cross.toml` to downloaded libsignal-client folder
* run `cross build --target x86_64-unknown-linux-gnu --release -p libsignal-jni` * run `cross build --target x86_64-unknown-linux-gnu --release -p libsignal-jni`
run `cross build --target armv7-unknown-linux-gnueabihf --release -p libsignal-jni` run `cross build --target armv7-unknown-linux-gnueabihf --release -p libsignal-jni`

View File

@ -1,22 +1,23 @@
diff --git a/build.gradle.kts b/build.gradle.kts diff --git a/build.gradle.kts b/build.gradle.kts
index d8e36ea4..dba4dae3 100644 index 5d127e4c..7ca155f9 100644
--- a/build.gradle.kts --- a/build.gradle.kts
+++ b/build.gradle.kts +++ b/build.gradle.kts
@@ -91,6 +91,7 @@ dependencies { @@ -91,6 +91,7 @@ dependencies {
implementation(libs.logback) implementation(libs.logback)
implementation(libs.zxing) implementation(libs.zxing)
implementation(project(":libsignal-cli")) implementation(project(":libsignal-cli"))
+ implementation(files("/tmp/libsignal-client.jar")) + implementation(files("/tmp/libsignal-client.jar"))
}
configurations { testImplementation(libs.junit.jupiter)
@@ -99,6 +100,9 @@ configurations { testImplementation(platform(libs.junit.jupiter.bom))
@@ -107,6 +108,10 @@ configurations {
} }
} }
+configurations.all { +configurations.all {
+ exclude(group = "org.signal", module = "libsignal-client") + exclude(group = "org.signal", module = "libsignal-client")
+} +}
+
tasks.withType<AbstractArchiveTask>().configureEach { tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false isPreserveFileTimestamps = false

View File

@ -34,26 +34,37 @@ const (
type UpdateContactRequest struct { type UpdateContactRequest struct {
Recipient string `json:"recipient"` Recipient string `json:"recipient"`
Name *string `json:"name"` Name *string `json:"name,omitempty"`
ExpirationInSeconds *int `json:"expiration_in_seconds"` ExpirationInSeconds *int `json:"expiration_in_seconds,omitempty"`
} }
type CreateGroupRequest struct { type CreateGroupRequest struct {
Name string `json:"name"` Name string `json:"name,omitempty"`
Members []string `json:"members"` Members []string `json:"members,omitempty"`
Description string `json:"description"` Description string `json:"description,omitempty"`
Permissions ds.GroupPermissions `json:"permissions"` Permissions ds.GroupPermissions `json:"permissions,omitempty"`
GroupLinkState string `json:"group_link" enums:"disabled,enabled,enabled-with-approval"` GroupLinkState string `json:"group_link,omitempty" enums:"disabled,enabled,enabled-with-approval"`
ExpirationTime *int `json:"expiration_time"` ExpirationTime *int `json:"expiration_time,omitempty"`
} }
type UpdateGroupRequest struct { type UpdateGroupRequest struct {
Base64Avatar *string `json:"base64_avatar"` Base64Avatar *string `json:"base64_avatar,omitempty"`
Description *string `json:"description"` Description *string `json:"description,omitempty"`
Name *string `json:"name"` Name *string `json:"name,omitempty"`
ExpirationTime *int `json:"expiration_time"` ExpirationTime *int `json:"expiration_time,omitempty"`
GroupLinkState *string `json:"group_link" enums:"disabled,enabled,enabled-with-approval"` GroupLinkState *string `json:"group_link,omitempty" enums:"disabled,enabled,enabled-with-approval"`
Permissions *ds.GroupPermissions `json:"permissions"` Permissions *ds.GroupPermissions `json:"permissions,omitempty"`
}
type PinMessageInGroupRequest struct {
TargetAuthor string `json:"target_author"`
Timestamp int64 `json:"timestamp"`
Duration *int `json:"duration,omitempty"`
}
type UnpinMessageInGroupRequest struct {
TargetAuthor string `json:"target_author"`
Timestamp int64 `json:"timestamp"`
} }
type ChangeGroupMembersRequest struct { type ChangeGroupMembersRequest struct {
@ -73,26 +84,33 @@ type Configuration struct {
} }
type RegisterNumberRequest struct { type RegisterNumberRequest struct {
UseVoice bool `json:"use_voice"` UseVoice bool `json:"use_voice,omitempty"`
Captcha string `json:"captcha"` Captcha string `json:"captcha,omitempty"`
} }
type UnregisterNumberRequest struct { type UnregisterNumberRequest struct {
DeleteAccount bool `json:"delete_account" example:"false"` DeleteAccount bool `json:"delete_account,omitempty" example:"false"`
DeleteLocalData bool `json:"delete_local_data" example:"false"` DeleteLocalData bool `json:"delete_local_data,omitempty" example:"false"`
} }
type VerifyNumberSettings struct { type VerifyNumberSettings struct {
Pin string `json:"pin"` Pin string `json:"pin,omitempty"`
} }
type Reaction struct { type SendReactionRequest struct {
Recipient string `json:"recipient"` Recipient string `json:"recipient"`
Reaction string `json:"reaction"` Reaction string `json:"reaction"`
TargetAuthor string `json:"target_author"` TargetAuthor string `json:"target_author"`
Timestamp int64 `json:"timestamp"` Timestamp int64 `json:"timestamp"`
} }
type RemoveReactionRequest struct {
Recipient string `json:"recipient"`
Reaction string `json:"reaction,omitempty"`
TargetAuthor string `json:"target_author"`
Timestamp int64 `json:"timestamp"`
}
type Receipt struct { type Receipt struct {
Recipient string `json:"recipient"` Recipient string `json:"recipient"`
ReceiptType string `json:"receipt_type" enums:"read,viewed"` ReceiptType string `json:"receipt_type" enums:"read,viewed"`
@ -103,27 +121,27 @@ type SendMessageV1 struct {
Number string `json:"number"` Number string `json:"number"`
Recipients []string `json:"recipients"` Recipients []string `json:"recipients"`
Message string `json:"message"` Message string `json:"message"`
Base64Attachment string `json:"base64_attachment" example:"'<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'"` Base64Attachment string `json:"base64_attachment,omitempty" example:"'<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;base64,<BASE64 ENCODED DATA>' OR 'data:<MIME-TYPE>;filename=<FILENAME>;base64,<BASE64 ENCODED DATA>'"`
IsGroup bool `json:"is_group"` IsGroup bool `json:"is_group,omitempty"`
} }
type SendMessageV2 struct { type SendMessageV2 struct {
Number string `json:"number"` Number string `json:"number"`
Recipients []string `json:"recipients"` Recipients []string `json:"recipients"`
Recipient string `json:"recipient" swaggerignore:"true"` //some REST API consumers (like the Synology NAS) do not support an array as recipients, so we provide this string parameter here as backup. In order to not confuse anyone, the parameter won't be exposed in the Swagger UI (most users are fine with the recipients parameter). Recipient string `json:"recipient,omitempty" swaggerignore:"true"` //some REST API consumers (like the Synology NAS) do not support an array as recipients, so we provide this string parameter here as backup. In order to not confuse anyone, the parameter won't be exposed in the Swagger UI (most users are fine with the recipients parameter).
Message string `json:"message"` Message string `json:"message"`
Base64Attachments []string `json:"base64_attachments" example:"<BASE64 ENCODED DATA>,data:<MIME-TYPE>;base64<comma><BASE64 ENCODED DATA>,data:<MIME-TYPE>;filename=<FILENAME>;base64<comma><BASE64 ENCODED DATA>"` Base64Attachments []string `json:"base64_attachments,omitempty" example:"<BASE64 ENCODED DATA>,data:<MIME-TYPE>;base64<comma><BASE64 ENCODED DATA>,data:<MIME-TYPE>;filename=<FILENAME>;base64<comma><BASE64 ENCODED DATA>"`
Sticker string `json:"sticker"` Sticker string `json:"sticker,omitempty"`
Mentions []ds.MessageMention `json:"mentions"` Mentions []ds.MessageMention `json:"mentions,omitempty"`
QuoteTimestamp *int64 `json:"quote_timestamp"` QuoteTimestamp *int64 `json:"quote_timestamp,omitempty"`
QuoteAuthor *string `json:"quote_author"` QuoteAuthor *string `json:"quote_author,omitempty"`
QuoteMessage *string `json:"quote_message"` QuoteMessage *string `json:"quote_message,omitempty"`
QuoteMentions []ds.MessageMention `json:"quote_mentions"` QuoteMentions []ds.MessageMention `json:"quote_mentions,omitempty"`
TextMode *string `json:"text_mode" enums:"normal,styled"` TextMode *string `json:"text_mode,omitempty" enums:"normal,styled"`
EditTimestamp *int64 `json:"edit_timestamp"` EditTimestamp *int64 `json:"edit_timestamp,omitempty"`
NotifySelf *bool `json:"notify_self"` NotifySelf *bool `json:"notify_self,omitempty"`
LinkPreview *ds.LinkPreviewType `json:"link_preview"` LinkPreview *ds.LinkPreviewType `json:"link_preview,omitempty"`
ViewOnce *bool `json:"view_once"` ViewOnce *bool `json:"view_once,omitempty"`
} }
type TypingIndicatorRequest struct { type TypingIndicatorRequest struct {
@ -146,13 +164,13 @@ type CreateGroupResponse struct {
type UpdateProfileRequest struct { type UpdateProfileRequest struct {
Name string `json:"name"` Name string `json:"name"`
Base64Avatar string `json:"base64_avatar"` Base64Avatar string `json:"base64_avatar,omitempty"`
About *string `json:"about"` About *string `json:"about,omitempty"`
} }
type TrustIdentityRequest struct { type TrustIdentityRequest struct {
VerifiedSafetyNumber *string `json:"verified_safety_number"` VerifiedSafetyNumber *string `json:"verified_safety_number,omitempty"`
TrustAllKnownKeys *bool `json:"trust_all_known_keys" example:"false"` TrustAllKnownKeys *bool `json:"trust_all_known_keys,omitempty" example:"false"`
} }
type SendMessageResponse struct { type SendMessageResponse struct {
@ -192,8 +210,8 @@ type RateLimitChallengeRequest struct {
} }
type UpdateAccountSettingsRequest struct { type UpdateAccountSettingsRequest struct {
DiscoverableByNumber *bool `json:"discoverable_by_number"` DiscoverableByNumber *bool `json:"discoverable_by_number,omitempty"`
ShareNumber *bool `json:"share_number"` ShareNumber *bool `json:"share_number,omitempty"`
} }
type SetUsernameRequest struct { type SetUsernameRequest struct {
@ -215,7 +233,7 @@ type RemoteDeleteRequest struct {
} }
type DeleteLocalAccountDataRequest struct { type DeleteLocalAccountDataRequest struct {
IgnoreRegistered bool `json:"ignore_registered" example:"false"` IgnoreRegistered bool `json:"ignore_registered,omitempty" example:"false"`
} }
type DeviceLinkUriResponse struct { type DeviceLinkUriResponse struct {
@ -226,7 +244,7 @@ type CreatePollRequest struct {
Recipient string `json:"recipient" example:"<phone number> OR <username> OR <group id>"` Recipient string `json:"recipient" example:"<phone number> OR <username> OR <group id>"`
Question string `json:"question" example:"What's your favourite fruit?"` Question string `json:"question" example:"What's your favourite fruit?"`
Answers []string `json:"answers" example:"apple,banana,orange"` Answers []string `json:"answers" example:"apple,banana,orange"`
AllowMultipleSelections *bool `json:"allow_multiple_selections" example:"true"` AllowMultipleSelections *bool `json:"allow_multiple_selections,omitempty" example:"true"`
} }
type CreatePollResponse struct { type CreatePollResponse struct {
@ -855,6 +873,11 @@ func (a *Api) AddMembersToGroup(c *gin.Context) {
return return
} }
if len(req.Members) == 0 {
c.JSON(400, Error{Msg: "Couldn't process request - group members missing"})
return
}
err = a.signalClient.AddMembersToGroup(number, groupId, req.Members) err = a.signalClient.AddMembersToGroup(number, groupId, req.Members)
if err != nil { if err != nil {
switch err.(type) { switch err.(type) {
@ -1173,6 +1196,113 @@ func (a *Api) DeleteGroup(c *gin.Context) {
} }
} }
// @Summary Pin a message in a Signal Group.
// @Tags Groups
// @Description Pin a message in a Signal Group.
// @Accept json
// @Produce json
// @Success 200 {string} string "OK"
// @Failure 400 {object} Error
// @Param data body PinMessageInGroupRequest true "Pin"
// @Param number path string true "Registered Phone Number"
// @Param groupid path string true "Group Id"
// @Router /v1/groups/{number}/{groupid}/pin-message [post]
func (a *Api) PinMessageInGroup(c *gin.Context) {
base64EncodedGroupId := c.Param("groupid")
number, err := url.PathUnescape(c.Param("number"))
if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
return
}
if base64EncodedGroupId == "" {
c.JSON(400, Error{Msg: "Please specify a group id"})
return
}
groupId, err := client.ConvertGroupIdToInternalGroupId(base64EncodedGroupId)
if err != nil {
c.JSON(400, Error{Msg: err.Error()})
return
}
var req PinMessageInGroupRequest
err = c.BindJSON(&req)
if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
return
}
duration := -1
if req.Duration != nil {
duration = *req.Duration
}
err = a.signalClient.PinMessageInGroup(number, groupId, req.TargetAuthor, req.Timestamp, duration)
if err != nil {
c.JSON(400, Error{Msg: err.Error()})
return
}
c.Status(201)
}
// @Summary Unpin a message in a Signal Group.
// @Tags Groups
// @Description Unpin a message in a Signal Group.
// @Accept json
// @Produce json
// @Success 200 {string} string "OK"
// @Failure 400 {object} Error
// @Param data body UnpinMessageInGroupRequest true "Unpin"
// @Param number path string true "Registered Phone Number"
// @Param groupid path string true "Group Id"
// @Router /v1/groups/{number}/{groupid}/pin-message [delete]
func (a *Api) UnpinMessageInGroup(c *gin.Context) {
base64EncodedGroupId := c.Param("groupid")
number, err := url.PathUnescape(c.Param("number"))
if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - malformed number"})
return
}
if base64EncodedGroupId == "" {
c.JSON(400, Error{Msg: "Please specify a group id"})
return
}
groupId, err := client.ConvertGroupIdToInternalGroupId(base64EncodedGroupId)
if err != nil {
c.JSON(400, Error{Msg: err.Error()})
return
}
var req UnpinMessageInGroupRequest
err = c.BindJSON(&req)
if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
return
}
if req.TargetAuthor == "" {
c.JSON(400, Error{Msg: "Couldn't process request - target author missing"})
return
}
if req.Timestamp == 0 {
c.JSON(400, Error{Msg: "Couldn't process request - timestamp missing"})
return
}
err = a.signalClient.UnpinMessageInGroup(number, groupId, req.TargetAuthor, req.Timestamp)
if err != nil {
c.JSON(400, Error{Msg: err.Error()})
return
}
c.Status(201)
}
// @Summary Link device and generate QR code. // @Summary Link device and generate QR code.
// @Tags Devices // @Tags Devices
// @Description Link device and generate QR code // @Description Link device and generate QR code
@ -1739,11 +1869,11 @@ func (a *Api) UpdateGroup(c *gin.Context) {
// @Produce json // @Produce json
// @Success 204 {string} OK // @Success 204 {string} OK
// @Failure 400 {object} Error // @Failure 400 {object} Error
// @Param data body Reaction true "Reaction" // @Param data body SendReactionRequest true "Reaction"
// @Param number path string true "Registered phone number" // @Param number path string true "Registered phone number"
// @Router /v1/reactions/{number} [post] // @Router /v1/reactions/{number} [post]
func (a *Api) SendReaction(c *gin.Context) { func (a *Api) SendReaction(c *gin.Context) {
var req Reaction var req SendReactionRequest
err := c.BindJSON(&req) err := c.BindJSON(&req)
if err != nil { if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
@ -1792,11 +1922,11 @@ func (a *Api) SendReaction(c *gin.Context) {
// @Produce json // @Produce json
// @Success 204 {string} OK // @Success 204 {string} OK
// @Failure 400 {object} Error // @Failure 400 {object} Error
// @Param data body Reaction true "Reaction" // @Param data body RemoveReactionRequest true "Reaction"
// @Param number path string true "Registered phone number" // @Param number path string true "Registered phone number"
// @Router /v1/reactions/{number} [delete] // @Router /v1/reactions/{number} [delete]
func (a *Api) RemoveReaction(c *gin.Context) { func (a *Api) RemoveReaction(c *gin.Context) {
var req Reaction var req RemoveReactionRequest
err := c.BindJSON(&req) err := c.BindJSON(&req)
if err != nil { if err != nil {
c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) c.JSON(400, Error{Msg: "Couldn't process request - invalid request"})
@ -2270,6 +2400,16 @@ func (a *Api) SubmitRateLimitChallenge(c *gin.Context) {
return return
} }
if req.ChallengeToken == "" {
c.JSON(400, Error{Msg: "Couldn't process request - challenge token missing"})
return
}
if req.Captcha == "" {
c.JSON(400, Error{Msg: "Couldn't process request - captcha missing"})
return
}
err = a.signalClient.SubmitRateLimitChallenge(number, req.ChallengeToken, req.Captcha) err = a.signalClient.SubmitRateLimitChallenge(number, req.ChallengeToken, req.Captcha)
if err != nil { if err != nil {
c.JSON(400, Error{Msg: err.Error()}) c.JSON(400, Error{Msg: err.Error()})

View File

@ -1080,8 +1080,8 @@ func (s *SignalClient) CreateGroup(number string, name string, members []string,
var internalGroupId string var internalGroupId string
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
Name string `json:"name"` Name string `json:"name,omitempty"`
Members []string `json:"members"` Members []string `json:"members,omitempty"`
Link string `json:"link,omitempty"` Link string `json:"link,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
EditGroupPermissions string `json:"setPermissionEditDetails,omitempty"` EditGroupPermissions string `json:"setPermissionEditDetails,omitempty"`
@ -1135,8 +1135,16 @@ func (s *SignalClient) CreateGroup(number string, name string, members []string,
} }
internalGroupId = resp.GroupId internalGroupId = resp.GroupId
} else { } else {
cmd := []string{"--config", s.signalCliConfig, "-a", number, "updateGroup", "-n", name, "-m"} cmd := []string{"--config", s.signalCliConfig, "-a", number, "updateGroup"}
cmd = append(cmd, prefixUsernameMembers(members)...)
if name != "" {
cmd = append(cmd, []string{"--n", name}...)
}
if len(members) > 0 {
cmd = append(cmd, "-m")
cmd = append(cmd, prefixUsernameMembers(members)...)
}
if addMembersPermission != DefaultGroupPermission { if addMembersPermission != DefaultGroupPermission {
cmd = append(cmd, []string{"--set-permission-add-member", addMembersPermission.String()}...) cmd = append(cmd, []string{"--set-permission-add-member", addMembersPermission.String()}...)
@ -1319,10 +1327,11 @@ func (s *SignalClient) GetGroupsExpanded(number string) ([]ExpandedGroupEntry, e
var signalCliGroupEntries []SignalCliGroupEntry var signalCliGroupEntries []SignalCliGroupEntry
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return groupEntries, err return groupEntries, err
} }
@ -1456,8 +1465,68 @@ func (s *SignalClient) GetGroupExpanded(number string, groupId string) (*Expande
return nil, nil return nil, nil
} }
func (s *SignalClient) PinMessageInGroup(number string, groupId string, targetAuthor string, timestamp int64, duration int) error {
if s.signalCliMode == JsonRpc {
type Request struct {
TargetAuthor string `json:"target-author"`
TargetTimestamp int64 `json:"target-timestamp"`
PinDuration int `json:"pin-duration"`
GroupId string `json:"group-id"`
}
req := Request{TargetAuthor: targetAuthor, TargetTimestamp: timestamp, PinDuration: duration, GroupId: groupId}
jsonRpc2Client, err := s.getJsonRpc2Client()
if err != nil {
return err
}
_, err = jsonRpc2Client.getRaw("sendPinMessage", &number, req)
if err != nil {
return err
}
} else {
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "sendPinMessage", "-g", groupId,
"-a", targetAuthor, "-t", strconv.FormatInt(timestamp, 10), "-d", strconv.Itoa(duration)}
_, err := s.cliClient.Execute(true, cmd, "")
if err != nil {
return err
}
}
return nil
}
func (s *SignalClient) UnpinMessageInGroup(number string, groupId string, targetAuthor string, timestamp int64) error {
if s.signalCliMode == JsonRpc {
type Request struct {
TargetAuthor string `json:"target-author"`
TargetTimestamp int64 `json:"target-timestamp"`
GroupId string `json:"group-id"`
}
req := Request{TargetAuthor: targetAuthor, TargetTimestamp: timestamp, GroupId: groupId}
jsonRpc2Client, err := s.getJsonRpc2Client()
if err != nil {
return err
}
_, err = jsonRpc2Client.getRaw("sendUnpinMessage", &number, req)
if err != nil {
return err
}
} else {
cmd := []string{"--config", s.signalCliConfig, "-o", "json", "-a", number, "sendUnpinMessage", "-g", groupId,
"-a", targetAuthor, "-t", strconv.FormatInt(timestamp, 10)}
_, err := s.cliClient.Execute(true, cmd, "")
if err != nil {
return err
}
}
return nil
}
func (s *SignalClient) GetAvatar(number string, id string, avatarType AvatarType) ([]byte, error) { func (s *SignalClient) GetAvatar(number string, id string, avatarType AvatarType) ([]byte, error) {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
if avatarType == GroupAvatar { if avatarType == GroupAvatar {
@ -1484,7 +1553,7 @@ func (s *SignalClient) GetAvatar(number string, id string, avatarType AvatarType
request.Profile = id request.Profile = id
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return []byte{}, err return []byte{}, err
} }
@ -1668,9 +1737,10 @@ func (s *SignalClient) GetAccounts() ([]string, error) {
accounts := make([]string, 0) accounts := make([]string, 0)
var rawData string var rawData string
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return accounts, err return accounts, err
} }
@ -1840,10 +1910,11 @@ func (s *SignalClient) UpdateProfile(number string, profileName string, base64Av
func (s *SignalClient) ListIdentities(number string) (*[]IdentityEntry, error) { func (s *SignalClient) ListIdentities(number string) (*[]IdentityEntry, error) {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
identityEntries := []IdentityEntry{} identityEntries := []IdentityEntry{}
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1878,6 +1949,7 @@ func (s *SignalClient) ListIdentities(number string) (*[]IdentityEntry, error) {
func (s *SignalClient) TrustIdentity(number string, numberToTrust string, verifiedSafetyNumber *string, trustAllKnownKeys *bool) error { func (s *SignalClient) TrustIdentity(number string, numberToTrust string, verifiedSafetyNumber *string, trustAllKnownKeys *bool) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
VerifiedSafetyNumber string `json:"verified-safety-number,omitempty"` VerifiedSafetyNumber string `json:"verified-safety-number,omitempty"`
@ -1894,7 +1966,7 @@ func (s *SignalClient) TrustIdentity(number string, numberToTrust string, verifi
request.TrustAllKnownKeys = *trustAllKnownKeys request.TrustAllKnownKeys = *trustAllKnownKeys
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -1917,12 +1989,13 @@ func (s *SignalClient) TrustIdentity(number string, numberToTrust string, verifi
func (s *SignalClient) BlockGroup(number string, groupId string) error { func (s *SignalClient) BlockGroup(number string, groupId string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
GroupId string `json:"groupId"` GroupId string `json:"groupId"`
} }
request := Request{GroupId: groupId} request := Request{GroupId: groupId}
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -1935,12 +2008,13 @@ func (s *SignalClient) BlockGroup(number string, groupId string) error {
func (s *SignalClient) JoinGroup(number string, groupId string) error { func (s *SignalClient) JoinGroup(number string, groupId string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
GroupId string `json:"groupId"` GroupId string `json:"groupId"`
} }
request := Request{GroupId: groupId} request := Request{GroupId: groupId}
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -1953,12 +2027,13 @@ func (s *SignalClient) JoinGroup(number string, groupId string) error {
func (s *SignalClient) QuitGroup(number string, groupId string) error { func (s *SignalClient) QuitGroup(number string, groupId string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
GroupId string `json:"groupId"` GroupId string `json:"groupId"`
} }
request := Request{GroupId: groupId} request := Request{GroupId: groupId}
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2101,6 +2176,7 @@ func (s *SignalClient) UpdateGroup(number string, groupId string, base64Avatar *
func (s *SignalClient) SendReaction(number string, recipient string, emoji string, target_author string, timestamp int64, remove bool) error { func (s *SignalClient) SendReaction(number string, recipient string, emoji string, target_author string, timestamp int64, remove bool) error {
// see https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc#sendreaction // see https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc#sendreaction
var err error var err error
var jsonRpc2Client *JsonRpc2Client
recp := recipient recp := recipient
isGroup := false isGroup := false
if strings.HasPrefix(recipient, groupPrefix) { if strings.HasPrefix(recipient, groupPrefix) {
@ -2135,7 +2211,7 @@ func (s *SignalClient) SendReaction(number string, recipient string, emoji strin
if remove { if remove {
request.Remove = remove request.Remove = remove
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2164,6 +2240,7 @@ func (s *SignalClient) SendReaction(number string, recipient string, emoji strin
func (s *SignalClient) SendReceipt(number string, recipient string, receipt_type string, timestamp int64) error { func (s *SignalClient) SendReceipt(number string, recipient string, receipt_type string, timestamp int64) error {
// see https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc#sendreceipt // see https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc#sendreceipt
var err error var err error
var jsonRpc2Client *JsonRpc2Client
recp := recipient recp := recipient
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
@ -2177,7 +2254,7 @@ func (s *SignalClient) SendReceipt(number string, recipient string, receipt_type
request.ReceiptType = receipt_type request.ReceiptType = receipt_type
request.Timestamp = timestamp request.Timestamp = timestamp
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2200,6 +2277,7 @@ func (s *SignalClient) SendReceipt(number string, recipient string, receipt_type
func (s *SignalClient) SendStartTyping(number string, recipient string) error { func (s *SignalClient) SendStartTyping(number string, recipient string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
recp := recipient recp := recipient
isGroup := false isGroup := false
if strings.HasPrefix(recipient, groupPrefix) { if strings.HasPrefix(recipient, groupPrefix) {
@ -2222,7 +2300,7 @@ func (s *SignalClient) SendStartTyping(number string, recipient string) error {
request.GroupId = recp request.GroupId = recp
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2242,6 +2320,7 @@ func (s *SignalClient) SendStartTyping(number string, recipient string) error {
func (s *SignalClient) SendStopTyping(number string, recipient string) error { func (s *SignalClient) SendStopTyping(number string, recipient string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
recp := recipient recp := recipient
isGroup := false isGroup := false
if strings.HasPrefix(recipient, groupPrefix) { if strings.HasPrefix(recipient, groupPrefix) {
@ -2265,7 +2344,7 @@ func (s *SignalClient) SendStopTyping(number string, recipient string) error {
request.GroupId = recp request.GroupId = recp
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2343,8 +2422,9 @@ func (s *SignalClient) SearchForNumbers(number string, numbers []string) ([]Sear
func (s *SignalClient) SendContacts(number string) error { func (s *SignalClient) SendContacts(number string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2358,6 +2438,7 @@ func (s *SignalClient) SendContacts(number string) error {
func (s *SignalClient) UpdateContact(number string, recipient string, name *string, expirationInSeconds *int) error { func (s *SignalClient) UpdateContact(number string, recipient string, name *string, expirationInSeconds *int) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
Recipient string `json:"recipient"` Recipient string `json:"recipient"`
@ -2371,7 +2452,7 @@ func (s *SignalClient) UpdateContact(number string, recipient string, name *stri
if expirationInSeconds != nil { if expirationInSeconds != nil {
request.Expiration = *expirationInSeconds request.Expiration = *expirationInSeconds
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2391,12 +2472,13 @@ func (s *SignalClient) UpdateContact(number string, recipient string, name *stri
func (s *SignalClient) AddDevice(number string, uri string) error { func (s *SignalClient) AddDevice(number string, uri string) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
Uri string `json:"uri"` Uri string `json:"uri"`
} }
request := Request{Uri: uri} request := Request{Uri: uri}
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2419,9 +2501,10 @@ func (s *SignalClient) ListDevices(number string) ([]ListDevicesResponse, error)
} }
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return resp, err return resp, err
} }
@ -2456,12 +2539,13 @@ func (s *SignalClient) ListDevices(number string) ([]ListDevicesResponse, error)
func (s *SignalClient) RemoveDevice(number string, deviceId int64) error { func (s *SignalClient) RemoveDevice(number string, deviceId int64) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
DeviceId int64 `json:"deviceId"` DeviceId int64 `json:"deviceId"`
} }
request := Request{DeviceId: deviceId} request := Request{DeviceId: deviceId}
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -2518,13 +2602,14 @@ func (s *SignalClient) SetUsername(number string, username string) (SetUsernameR
var resp SetUsernameResponse var resp SetUsernameResponse
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
type Request struct { type Request struct {
Username string `json:"username"` Username string `json:"username"`
} }
request := Request{Username: username} request := Request{Username: username}
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return resp, err return resp, err
} }
@ -2607,9 +2692,10 @@ func (s *SignalClient) ListInstalledStickerPacks(number string) ([]ListInstalled
resp := []ListInstalledStickerPacksResponse{} resp := []ListInstalledStickerPacksResponse{}
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return resp, err return resp, err
} }
@ -2690,6 +2776,7 @@ func (s *SignalClient) ListContacts(number string, allRecipients bool, recipient
resp := []ListContactsResponse{} resp := []ListContactsResponse{}
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
if s.signalCliMode == JsonRpc { if s.signalCliMode == JsonRpc {
@ -2705,7 +2792,7 @@ func (s *SignalClient) ListContacts(number string, allRecipients bool, recipient
req.Recipient = recipient req.Recipient = recipient
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -2812,6 +2899,7 @@ func (s *SignalClient) RemovePin(number string) error {
func (s *SignalClient) RemoteDelete(number string, recipient string, timestamp int64) (RemoteDeleteResponse, error) { func (s *SignalClient) RemoteDelete(number string, recipient string, timestamp int64) (RemoteDeleteResponse, error) {
// see https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc#remotedelete // see https://github.com/AsamK/signal-cli/blob/master/man/signal-cli.1.adoc#remotedelete
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var resp RemoteDeleteResponse var resp RemoteDeleteResponse
var rawData string var rawData string
@ -2847,7 +2935,7 @@ func (s *SignalClient) RemoteDelete(number string, recipient string, timestamp i
} }
request.Timestamp = timestamp request.Timestamp = timestamp
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return resp, err return resp, err
} }
@ -2886,6 +2974,7 @@ func (s *SignalClient) RemoteDelete(number string, recipient string, timestamp i
func (s *SignalClient) CreatePoll(number string, recipient string, question string, answers []string, allowMultipleSelections bool) (string, error) { func (s *SignalClient) CreatePoll(number string, recipient string, question string, answers []string, allowMultipleSelections bool) (string, error) {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
var rawData string var rawData string
type Response struct { type Response struct {
@ -2927,7 +3016,7 @@ func (s *SignalClient) CreatePoll(number string, recipient string, question stri
req.Username = recp req.Username = recp
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return "", err return "", err
} }
@ -2977,6 +3066,7 @@ func (s *SignalClient) CreatePoll(number string, recipient string, question stri
func (s *SignalClient) VoteInPoll(number string, recipient string, pollAuthor string, pollTimestamp int64, selectedAnswers []int32) error { func (s *SignalClient) VoteInPoll(number string, recipient string, pollAuthor string, pollTimestamp int64, selectedAnswers []int32) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
recp := recipient recp := recipient
recipientType, err := getRecipientType(recipient) recipientType, err := getRecipientType(recipient)
@ -3020,7 +3110,7 @@ func (s *SignalClient) VoteInPoll(number string, recipient string, pollAuthor st
req.Username = recp req.Username = recp
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }
@ -3063,6 +3153,7 @@ func (s *SignalClient) VoteInPoll(number string, recipient string, pollAuthor st
func (s *SignalClient) ClosePoll(number string, recipient string, pollTimestamp int64) error { func (s *SignalClient) ClosePoll(number string, recipient string, pollTimestamp int64) error {
var err error var err error
var jsonRpc2Client *JsonRpc2Client
recp := recipient recp := recipient
recipientType, err := getRecipientType(recipient) recipientType, err := getRecipientType(recipient)
@ -3096,7 +3187,7 @@ func (s *SignalClient) ClosePoll(number string, recipient string, pollTimestamp
req.Username = recp req.Username = recp
} }
jsonRpc2Client, err := s.getJsonRpc2Client() jsonRpc2Client, err = s.getJsonRpc2Client()
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,32 +1,47 @@
These files are generated from the [swaggo/swag](https://github.com/swaggo/swag) tool. # Documentation
To regenerate them, run in /src: These files are generated using the [swaggo/swag](https://github.com/swaggo/swag) tool.
```bash There are two steps, first generating the docs and then running the web server.
docker run --rm -v $(pwd):/code ghcr.io/swaggo/swag:latest init
```
Or, if you have `swag` installed: ## Generating the docs
```bash Regenerate the files with your local source code changes.
swag init
```
Then run the app in `/src` 1. Set the current working dir to `src`
```bash
cd src
```
1. Run swag to generate the docs
* Option 1, via docker
```bash
docker run --rm -v $(pwd):/code ghcr.io/swaggo/swag:latest init --requiredByDefault
```
* Option 2, install swag and run the command line tool
```bash
swag init --requiredByDefault
```
```bash ## Run the web server
go run main.go
```
Or with docker compose in the root of the repository Run the web server to visualize the generated docs.
```bash
docker compose up
```
Then navigate to the following address to view the docs 1. Run the main script
* Option 1, via docker, run the command at the root of the repository
```bash
docker compose up
```
* Option 2, install go and run the command line tool
```bash
cd src
```
```bash
go run main.go
```
http://127.0.0.1:8080/swagger/index.html
On docker you'll get a Network error, replace the IP for the docker internal IP in the error, e.g: ## Navigate to the docs
http://172.18.0.2:8080/swagger/index.html The docs are served at: http://127.0.0.1:8080/swagger/index.html
When serving with docker, if you get a Network error, replace the IP for the docker internal IP in the error, e.g: http://172.18.0.2:8080/swagger/index.html

View File

@ -1482,6 +1482,112 @@ const docTemplate = `{
} }
} }
}, },
"/v1/groups/{number}/{groupid}/pin-message": {
"post": {
"description": "Pin a message in a Signal Group.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Groups"
],
"summary": "Pin a message in a Signal Group.",
"parameters": [
{
"description": "Pin",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PinMessageInGroupRequest"
}
},
{
"type": "string",
"description": "Registered Phone Number",
"name": "number",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Group Id",
"name": "groupid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
},
"delete": {
"description": "Unpin a message in a Signal Group.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Groups"
],
"summary": "Unpin a message in a Signal Group.",
"parameters": [
{
"description": "Unpin",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.UnpinMessageInGroupRequest"
}
},
{
"type": "string",
"description": "Registered Phone Number",
"name": "number",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Group Id",
"name": "groupid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
},
"/v1/groups/{number}/{groupid}/quit": { "/v1/groups/{number}/{groupid}/quit": {
"post": { "post": {
"description": "Quit the specified Signal Group.", "description": "Quit the specified Signal Group.",
@ -1897,7 +2003,7 @@ const docTemplate = `{
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/api.Reaction" "$ref": "#/definitions/api.SendReactionRequest"
} }
}, },
{ {
@ -1942,7 +2048,7 @@ const docTemplate = `{
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/api.Reaction" "$ref": "#/definitions/api.RemoveReactionRequest"
} }
}, },
{ {
@ -2600,6 +2706,9 @@ const docTemplate = `{
"definitions": { "definitions": {
"api.AddDeviceRequest": { "api.AddDeviceRequest": {
"type": "object", "type": "object",
"required": [
"uri"
],
"properties": { "properties": {
"uri": { "uri": {
"type": "string" "type": "string"
@ -2608,6 +2717,10 @@ const docTemplate = `{
}, },
"api.AddStickerPackRequest": { "api.AddStickerPackRequest": {
"type": "object", "type": "object",
"required": [
"pack_id",
"pack_key"
],
"properties": { "properties": {
"pack_id": { "pack_id": {
"type": "string", "type": "string",
@ -2621,6 +2734,9 @@ const docTemplate = `{
}, },
"api.ChangeGroupAdminsRequest": { "api.ChangeGroupAdminsRequest": {
"type": "object", "type": "object",
"required": [
"admins"
],
"properties": { "properties": {
"admins": { "admins": {
"type": "array", "type": "array",
@ -2632,6 +2748,9 @@ const docTemplate = `{
}, },
"api.ChangeGroupMembersRequest": { "api.ChangeGroupMembersRequest": {
"type": "object", "type": "object",
"required": [
"members"
],
"properties": { "properties": {
"members": { "members": {
"type": "array", "type": "array",
@ -2643,6 +2762,10 @@ const docTemplate = `{
}, },
"api.ClosePollRequest": { "api.ClosePollRequest": {
"type": "object", "type": "object",
"required": [
"poll_timestamp",
"recipient"
],
"properties": { "properties": {
"poll_timestamp": { "poll_timestamp": {
"type": "string", "type": "string",
@ -2656,6 +2779,9 @@ const docTemplate = `{
}, },
"api.Configuration": { "api.Configuration": {
"type": "object", "type": "object",
"required": [
"logging"
],
"properties": { "properties": {
"logging": { "logging": {
"$ref": "#/definitions/api.LoggingConfiguration" "$ref": "#/definitions/api.LoggingConfiguration"
@ -2695,6 +2821,9 @@ const docTemplate = `{
}, },
"api.CreateGroupResponse": { "api.CreateGroupResponse": {
"type": "object", "type": "object",
"required": [
"id"
],
"properties": { "properties": {
"id": { "id": {
"type": "string" "type": "string"
@ -2703,6 +2832,11 @@ const docTemplate = `{
}, },
"api.CreatePollRequest": { "api.CreatePollRequest": {
"type": "object", "type": "object",
"required": [
"answers",
"question",
"recipient"
],
"properties": { "properties": {
"allow_multiple_selections": { "allow_multiple_selections": {
"type": "boolean", "type": "boolean",
@ -2731,6 +2865,9 @@ const docTemplate = `{
}, },
"api.CreatePollResponse": { "api.CreatePollResponse": {
"type": "object", "type": "object",
"required": [
"timestamp"
],
"properties": { "properties": {
"timestamp": { "timestamp": {
"type": "string", "type": "string",
@ -2749,6 +2886,9 @@ const docTemplate = `{
}, },
"api.DeviceLinkUriResponse": { "api.DeviceLinkUriResponse": {
"type": "object", "type": "object",
"required": [
"device_link_uri"
],
"properties": { "properties": {
"device_link_uri": { "device_link_uri": {
"type": "string" "type": "string"
@ -2757,6 +2897,9 @@ const docTemplate = `{
}, },
"api.Error": { "api.Error": {
"type": "object", "type": "object",
"required": [
"error"
],
"properties": { "properties": {
"error": { "error": {
"type": "string" "type": "string"
@ -2765,14 +2908,39 @@ const docTemplate = `{
}, },
"api.LoggingConfiguration": { "api.LoggingConfiguration": {
"type": "object", "type": "object",
"required": [
"Level"
],
"properties": { "properties": {
"Level": { "Level": {
"type": "string" "type": "string"
} }
} }
}, },
"api.PinMessageInGroupRequest": {
"type": "object",
"required": [
"target_author",
"timestamp"
],
"properties": {
"duration": {
"type": "integer"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.RateLimitChallengeRequest": { "api.RateLimitChallengeRequest": {
"type": "object", "type": "object",
"required": [
"captcha",
"challenge_token"
],
"properties": { "properties": {
"captcha": { "captcha": {
"type": "string", "type": "string",
@ -2784,25 +2952,13 @@ const docTemplate = `{
} }
} }
}, },
"api.Reaction": {
"type": "object",
"properties": {
"reaction": {
"type": "string"
},
"recipient": {
"type": "string"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.Receipt": { "api.Receipt": {
"type": "object", "type": "object",
"required": [
"receipt_type",
"recipient",
"timestamp"
],
"properties": { "properties": {
"receipt_type": { "receipt_type": {
"type": "string", "type": "string",
@ -2832,6 +2988,10 @@ const docTemplate = `{
}, },
"api.RemoteDeleteRequest": { "api.RemoteDeleteRequest": {
"type": "object", "type": "object",
"required": [
"recipient",
"timestamp"
],
"properties": { "properties": {
"recipient": { "recipient": {
"type": "string" "type": "string"
@ -2843,14 +3003,43 @@ const docTemplate = `{
}, },
"api.RemoteDeleteResponse": { "api.RemoteDeleteResponse": {
"type": "object", "type": "object",
"required": [
"timestamp"
],
"properties": { "properties": {
"timestamp": { "timestamp": {
"type": "string" "type": "string"
} }
} }
}, },
"api.RemoveReactionRequest": {
"type": "object",
"required": [
"recipient",
"target_author",
"timestamp"
],
"properties": {
"reaction": {
"type": "string"
},
"recipient": {
"type": "string"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.SearchResponse": { "api.SearchResponse": {
"type": "object", "type": "object",
"required": [
"number",
"registered"
],
"properties": { "properties": {
"number": { "number": {
"type": "string" "type": "string"
@ -2862,6 +3051,10 @@ const docTemplate = `{
}, },
"api.SendMessageError": { "api.SendMessageError": {
"type": "object", "type": "object",
"required": [
"account",
"error"
],
"properties": { "properties": {
"account": { "account": {
"type": "string" "type": "string"
@ -2879,6 +3072,9 @@ const docTemplate = `{
}, },
"api.SendMessageResponse": { "api.SendMessageResponse": {
"type": "object", "type": "object",
"required": [
"timestamp"
],
"properties": { "properties": {
"timestamp": { "timestamp": {
"type": "string" "type": "string"
@ -2887,6 +3083,11 @@ const docTemplate = `{
}, },
"api.SendMessageV1": { "api.SendMessageV1": {
"type": "object", "type": "object",
"required": [
"message",
"number",
"recipients"
],
"properties": { "properties": {
"base64_attachment": { "base64_attachment": {
"type": "string", "type": "string",
@ -2911,6 +3112,11 @@ const docTemplate = `{
}, },
"api.SendMessageV2": { "api.SendMessageV2": {
"type": "object", "type": "object",
"required": [
"message",
"number",
"recipients"
],
"properties": { "properties": {
"base64_attachments": { "base64_attachments": {
"type": "array", "type": "array",
@ -2980,8 +3186,34 @@ const docTemplate = `{
} }
} }
}, },
"api.SendReactionRequest": {
"type": "object",
"required": [
"reaction",
"recipient",
"target_author",
"timestamp"
],
"properties": {
"reaction": {
"type": "string"
},
"recipient": {
"type": "string"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.SetPinRequest": { "api.SetPinRequest": {
"type": "object", "type": "object",
"required": [
"pin"
],
"properties": { "properties": {
"pin": { "pin": {
"type": "string" "type": "string"
@ -2990,6 +3222,9 @@ const docTemplate = `{
}, },
"api.SetUsernameRequest": { "api.SetUsernameRequest": {
"type": "object", "type": "object",
"required": [
"username"
],
"properties": { "properties": {
"username": { "username": {
"type": "string", "type": "string",
@ -3011,6 +3246,9 @@ const docTemplate = `{
}, },
"api.TrustModeRequest": { "api.TrustModeRequest": {
"type": "object", "type": "object",
"required": [
"trust_mode"
],
"properties": { "properties": {
"trust_mode": { "trust_mode": {
"type": "string" "type": "string"
@ -3019,6 +3257,9 @@ const docTemplate = `{
}, },
"api.TrustModeResponse": { "api.TrustModeResponse": {
"type": "object", "type": "object",
"required": [
"trust_mode"
],
"properties": { "properties": {
"trust_mode": { "trust_mode": {
"type": "string" "type": "string"
@ -3027,12 +3268,30 @@ const docTemplate = `{
}, },
"api.TypingIndicatorRequest": { "api.TypingIndicatorRequest": {
"type": "object", "type": "object",
"required": [
"recipient"
],
"properties": { "properties": {
"recipient": { "recipient": {
"type": "string" "type": "string"
} }
} }
}, },
"api.UnpinMessageInGroupRequest": {
"type": "object",
"required": [
"target_author",
"timestamp"
],
"properties": {
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.UnregisterNumberRequest": { "api.UnregisterNumberRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3059,6 +3318,9 @@ const docTemplate = `{
}, },
"api.UpdateContactRequest": { "api.UpdateContactRequest": {
"type": "object", "type": "object",
"required": [
"recipient"
],
"properties": { "properties": {
"expiration_in_seconds": { "expiration_in_seconds": {
"type": "integer" "type": "integer"
@ -3101,6 +3363,9 @@ const docTemplate = `{
}, },
"api.UpdateProfileRequest": { "api.UpdateProfileRequest": {
"type": "object", "type": "object",
"required": [
"name"
],
"properties": { "properties": {
"about": { "about": {
"type": "string" "type": "string"
@ -3123,6 +3388,12 @@ const docTemplate = `{
}, },
"api.VoteRequest": { "api.VoteRequest": {
"type": "object", "type": "object",
"required": [
"poll_author",
"poll_timestamp",
"recipient",
"selected_answers"
],
"properties": { "properties": {
"poll_author": { "poll_author": {
"type": "string", "type": "string",
@ -3149,6 +3420,13 @@ const docTemplate = `{
}, },
"client.About": { "client.About": {
"type": "object", "type": "object",
"required": [
"build",
"capabilities",
"mode",
"version",
"versions"
],
"properties": { "properties": {
"build": { "build": {
"type": "integer" "type": "integer"
@ -3178,6 +3456,13 @@ const docTemplate = `{
}, },
"client.ContactProfile": { "client.ContactProfile": {
"type": "object", "type": "object",
"required": [
"about",
"given_name",
"has_avatar",
"last_updated_timestamp",
"lastname"
],
"properties": { "properties": {
"about": { "about": {
"type": "string" "type": "string"
@ -3198,6 +3483,19 @@ const docTemplate = `{
}, },
"client.GroupEntry": { "client.GroupEntry": {
"type": "object", "type": "object",
"required": [
"admins",
"blocked",
"description",
"id",
"internal_id",
"invite_link",
"members",
"name",
"pending_invites",
"pending_requests",
"permissions"
],
"properties": { "properties": {
"admins": { "admins": {
"type": "array", "type": "array",
@ -3248,6 +3546,14 @@ const docTemplate = `{
}, },
"client.IdentityEntry": { "client.IdentityEntry": {
"type": "object", "type": "object",
"required": [
"added",
"fingerprint",
"number",
"safety_number",
"status",
"uuid"
],
"properties": { "properties": {
"added": { "added": {
"type": "string" "type": "string"
@ -3271,6 +3577,20 @@ const docTemplate = `{
}, },
"client.ListContactsResponse": { "client.ListContactsResponse": {
"type": "object", "type": "object",
"required": [
"blocked",
"color",
"given_name",
"message_expiration",
"name",
"nickname",
"note",
"number",
"profile",
"profile_name",
"username",
"uuid"
],
"properties": { "properties": {
"blocked": { "blocked": {
"type": "boolean" "type": "boolean"
@ -3312,6 +3632,12 @@ const docTemplate = `{
}, },
"client.ListDevicesResponse": { "client.ListDevicesResponse": {
"type": "object", "type": "object",
"required": [
"creation_timestamp",
"id",
"last_seen_timestamp",
"name"
],
"properties": { "properties": {
"creation_timestamp": { "creation_timestamp": {
"type": "integer" "type": "integer"
@ -3329,6 +3655,13 @@ const docTemplate = `{
}, },
"client.ListInstalledStickerPacksResponse": { "client.ListInstalledStickerPacksResponse": {
"type": "object", "type": "object",
"required": [
"author",
"installed",
"pack_id",
"title",
"url"
],
"properties": { "properties": {
"author": { "author": {
"type": "string" "type": "string"
@ -3349,6 +3682,11 @@ const docTemplate = `{
}, },
"client.Nickname": { "client.Nickname": {
"type": "object", "type": "object",
"required": [
"family_name",
"given_name",
"name"
],
"properties": { "properties": {
"family_name": { "family_name": {
"type": "string" "type": "string"
@ -3363,6 +3701,10 @@ const docTemplate = `{
}, },
"client.SetUsernameResponse": { "client.SetUsernameResponse": {
"type": "object", "type": "object",
"required": [
"username",
"username_link"
],
"properties": { "properties": {
"username": { "username": {
"type": "string" "type": "string"
@ -3374,6 +3716,11 @@ const docTemplate = `{
}, },
"data.GroupPermissions": { "data.GroupPermissions": {
"type": "object", "type": "object",
"required": [
"add_members",
"edit_group",
"send_messages"
],
"properties": { "properties": {
"add_members": { "add_members": {
"type": "string", "type": "string",
@ -3400,6 +3747,12 @@ const docTemplate = `{
}, },
"data.LinkPreviewType": { "data.LinkPreviewType": {
"type": "object", "type": "object",
"required": [
"base64_thumbnail",
"description",
"title",
"url"
],
"properties": { "properties": {
"base64_thumbnail": { "base64_thumbnail": {
"type": "string" "type": "string"
@ -3417,6 +3770,11 @@ const docTemplate = `{
}, },
"data.MessageMention": { "data.MessageMention": {
"type": "object", "type": "object",
"required": [
"author",
"length",
"start"
],
"properties": { "properties": {
"author": { "author": {
"type": "string" "type": "string"

View File

@ -1479,6 +1479,112 @@
} }
} }
}, },
"/v1/groups/{number}/{groupid}/pin-message": {
"post": {
"description": "Pin a message in a Signal Group.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Groups"
],
"summary": "Pin a message in a Signal Group.",
"parameters": [
{
"description": "Pin",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.PinMessageInGroupRequest"
}
},
{
"type": "string",
"description": "Registered Phone Number",
"name": "number",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Group Id",
"name": "groupid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
},
"delete": {
"description": "Unpin a message in a Signal Group.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Groups"
],
"summary": "Unpin a message in a Signal Group.",
"parameters": [
{
"description": "Unpin",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/api.UnpinMessageInGroupRequest"
}
},
{
"type": "string",
"description": "Registered Phone Number",
"name": "number",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Group Id",
"name": "groupid",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/api.Error"
}
}
}
}
},
"/v1/groups/{number}/{groupid}/quit": { "/v1/groups/{number}/{groupid}/quit": {
"post": { "post": {
"description": "Quit the specified Signal Group.", "description": "Quit the specified Signal Group.",
@ -1894,7 +2000,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/api.Reaction" "$ref": "#/definitions/api.SendReactionRequest"
} }
}, },
{ {
@ -1939,7 +2045,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/api.Reaction" "$ref": "#/definitions/api.RemoveReactionRequest"
} }
}, },
{ {
@ -2597,6 +2703,9 @@
"definitions": { "definitions": {
"api.AddDeviceRequest": { "api.AddDeviceRequest": {
"type": "object", "type": "object",
"required": [
"uri"
],
"properties": { "properties": {
"uri": { "uri": {
"type": "string" "type": "string"
@ -2605,6 +2714,10 @@
}, },
"api.AddStickerPackRequest": { "api.AddStickerPackRequest": {
"type": "object", "type": "object",
"required": [
"pack_id",
"pack_key"
],
"properties": { "properties": {
"pack_id": { "pack_id": {
"type": "string", "type": "string",
@ -2618,6 +2731,9 @@
}, },
"api.ChangeGroupAdminsRequest": { "api.ChangeGroupAdminsRequest": {
"type": "object", "type": "object",
"required": [
"admins"
],
"properties": { "properties": {
"admins": { "admins": {
"type": "array", "type": "array",
@ -2629,6 +2745,9 @@
}, },
"api.ChangeGroupMembersRequest": { "api.ChangeGroupMembersRequest": {
"type": "object", "type": "object",
"required": [
"members"
],
"properties": { "properties": {
"members": { "members": {
"type": "array", "type": "array",
@ -2640,6 +2759,10 @@
}, },
"api.ClosePollRequest": { "api.ClosePollRequest": {
"type": "object", "type": "object",
"required": [
"poll_timestamp",
"recipient"
],
"properties": { "properties": {
"poll_timestamp": { "poll_timestamp": {
"type": "string", "type": "string",
@ -2653,6 +2776,9 @@
}, },
"api.Configuration": { "api.Configuration": {
"type": "object", "type": "object",
"required": [
"logging"
],
"properties": { "properties": {
"logging": { "logging": {
"$ref": "#/definitions/api.LoggingConfiguration" "$ref": "#/definitions/api.LoggingConfiguration"
@ -2692,6 +2818,9 @@
}, },
"api.CreateGroupResponse": { "api.CreateGroupResponse": {
"type": "object", "type": "object",
"required": [
"id"
],
"properties": { "properties": {
"id": { "id": {
"type": "string" "type": "string"
@ -2700,6 +2829,11 @@
}, },
"api.CreatePollRequest": { "api.CreatePollRequest": {
"type": "object", "type": "object",
"required": [
"answers",
"question",
"recipient"
],
"properties": { "properties": {
"allow_multiple_selections": { "allow_multiple_selections": {
"type": "boolean", "type": "boolean",
@ -2728,6 +2862,9 @@
}, },
"api.CreatePollResponse": { "api.CreatePollResponse": {
"type": "object", "type": "object",
"required": [
"timestamp"
],
"properties": { "properties": {
"timestamp": { "timestamp": {
"type": "string", "type": "string",
@ -2746,6 +2883,9 @@
}, },
"api.DeviceLinkUriResponse": { "api.DeviceLinkUriResponse": {
"type": "object", "type": "object",
"required": [
"device_link_uri"
],
"properties": { "properties": {
"device_link_uri": { "device_link_uri": {
"type": "string" "type": "string"
@ -2754,6 +2894,9 @@
}, },
"api.Error": { "api.Error": {
"type": "object", "type": "object",
"required": [
"error"
],
"properties": { "properties": {
"error": { "error": {
"type": "string" "type": "string"
@ -2762,14 +2905,39 @@
}, },
"api.LoggingConfiguration": { "api.LoggingConfiguration": {
"type": "object", "type": "object",
"required": [
"Level"
],
"properties": { "properties": {
"Level": { "Level": {
"type": "string" "type": "string"
} }
} }
}, },
"api.PinMessageInGroupRequest": {
"type": "object",
"required": [
"target_author",
"timestamp"
],
"properties": {
"duration": {
"type": "integer"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.RateLimitChallengeRequest": { "api.RateLimitChallengeRequest": {
"type": "object", "type": "object",
"required": [
"captcha",
"challenge_token"
],
"properties": { "properties": {
"captcha": { "captcha": {
"type": "string", "type": "string",
@ -2781,25 +2949,13 @@
} }
} }
}, },
"api.Reaction": {
"type": "object",
"properties": {
"reaction": {
"type": "string"
},
"recipient": {
"type": "string"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.Receipt": { "api.Receipt": {
"type": "object", "type": "object",
"required": [
"receipt_type",
"recipient",
"timestamp"
],
"properties": { "properties": {
"receipt_type": { "receipt_type": {
"type": "string", "type": "string",
@ -2829,6 +2985,10 @@
}, },
"api.RemoteDeleteRequest": { "api.RemoteDeleteRequest": {
"type": "object", "type": "object",
"required": [
"recipient",
"timestamp"
],
"properties": { "properties": {
"recipient": { "recipient": {
"type": "string" "type": "string"
@ -2840,14 +3000,43 @@
}, },
"api.RemoteDeleteResponse": { "api.RemoteDeleteResponse": {
"type": "object", "type": "object",
"required": [
"timestamp"
],
"properties": { "properties": {
"timestamp": { "timestamp": {
"type": "string" "type": "string"
} }
} }
}, },
"api.RemoveReactionRequest": {
"type": "object",
"required": [
"recipient",
"target_author",
"timestamp"
],
"properties": {
"reaction": {
"type": "string"
},
"recipient": {
"type": "string"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.SearchResponse": { "api.SearchResponse": {
"type": "object", "type": "object",
"required": [
"number",
"registered"
],
"properties": { "properties": {
"number": { "number": {
"type": "string" "type": "string"
@ -2859,6 +3048,10 @@
}, },
"api.SendMessageError": { "api.SendMessageError": {
"type": "object", "type": "object",
"required": [
"account",
"error"
],
"properties": { "properties": {
"account": { "account": {
"type": "string" "type": "string"
@ -2876,6 +3069,9 @@
}, },
"api.SendMessageResponse": { "api.SendMessageResponse": {
"type": "object", "type": "object",
"required": [
"timestamp"
],
"properties": { "properties": {
"timestamp": { "timestamp": {
"type": "string" "type": "string"
@ -2884,6 +3080,11 @@
}, },
"api.SendMessageV1": { "api.SendMessageV1": {
"type": "object", "type": "object",
"required": [
"message",
"number",
"recipients"
],
"properties": { "properties": {
"base64_attachment": { "base64_attachment": {
"type": "string", "type": "string",
@ -2908,6 +3109,11 @@
}, },
"api.SendMessageV2": { "api.SendMessageV2": {
"type": "object", "type": "object",
"required": [
"message",
"number",
"recipients"
],
"properties": { "properties": {
"base64_attachments": { "base64_attachments": {
"type": "array", "type": "array",
@ -2977,8 +3183,34 @@
} }
} }
}, },
"api.SendReactionRequest": {
"type": "object",
"required": [
"reaction",
"recipient",
"target_author",
"timestamp"
],
"properties": {
"reaction": {
"type": "string"
},
"recipient": {
"type": "string"
},
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.SetPinRequest": { "api.SetPinRequest": {
"type": "object", "type": "object",
"required": [
"pin"
],
"properties": { "properties": {
"pin": { "pin": {
"type": "string" "type": "string"
@ -2987,6 +3219,9 @@
}, },
"api.SetUsernameRequest": { "api.SetUsernameRequest": {
"type": "object", "type": "object",
"required": [
"username"
],
"properties": { "properties": {
"username": { "username": {
"type": "string", "type": "string",
@ -3008,6 +3243,9 @@
}, },
"api.TrustModeRequest": { "api.TrustModeRequest": {
"type": "object", "type": "object",
"required": [
"trust_mode"
],
"properties": { "properties": {
"trust_mode": { "trust_mode": {
"type": "string" "type": "string"
@ -3016,6 +3254,9 @@
}, },
"api.TrustModeResponse": { "api.TrustModeResponse": {
"type": "object", "type": "object",
"required": [
"trust_mode"
],
"properties": { "properties": {
"trust_mode": { "trust_mode": {
"type": "string" "type": "string"
@ -3024,12 +3265,30 @@
}, },
"api.TypingIndicatorRequest": { "api.TypingIndicatorRequest": {
"type": "object", "type": "object",
"required": [
"recipient"
],
"properties": { "properties": {
"recipient": { "recipient": {
"type": "string" "type": "string"
} }
} }
}, },
"api.UnpinMessageInGroupRequest": {
"type": "object",
"required": [
"target_author",
"timestamp"
],
"properties": {
"target_author": {
"type": "string"
},
"timestamp": {
"type": "integer"
}
}
},
"api.UnregisterNumberRequest": { "api.UnregisterNumberRequest": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -3056,6 +3315,9 @@
}, },
"api.UpdateContactRequest": { "api.UpdateContactRequest": {
"type": "object", "type": "object",
"required": [
"recipient"
],
"properties": { "properties": {
"expiration_in_seconds": { "expiration_in_seconds": {
"type": "integer" "type": "integer"
@ -3098,6 +3360,9 @@
}, },
"api.UpdateProfileRequest": { "api.UpdateProfileRequest": {
"type": "object", "type": "object",
"required": [
"name"
],
"properties": { "properties": {
"about": { "about": {
"type": "string" "type": "string"
@ -3120,6 +3385,12 @@
}, },
"api.VoteRequest": { "api.VoteRequest": {
"type": "object", "type": "object",
"required": [
"poll_author",
"poll_timestamp",
"recipient",
"selected_answers"
],
"properties": { "properties": {
"poll_author": { "poll_author": {
"type": "string", "type": "string",
@ -3146,6 +3417,13 @@
}, },
"client.About": { "client.About": {
"type": "object", "type": "object",
"required": [
"build",
"capabilities",
"mode",
"version",
"versions"
],
"properties": { "properties": {
"build": { "build": {
"type": "integer" "type": "integer"
@ -3175,6 +3453,13 @@
}, },
"client.ContactProfile": { "client.ContactProfile": {
"type": "object", "type": "object",
"required": [
"about",
"given_name",
"has_avatar",
"last_updated_timestamp",
"lastname"
],
"properties": { "properties": {
"about": { "about": {
"type": "string" "type": "string"
@ -3195,6 +3480,19 @@
}, },
"client.GroupEntry": { "client.GroupEntry": {
"type": "object", "type": "object",
"required": [
"admins",
"blocked",
"description",
"id",
"internal_id",
"invite_link",
"members",
"name",
"pending_invites",
"pending_requests",
"permissions"
],
"properties": { "properties": {
"admins": { "admins": {
"type": "array", "type": "array",
@ -3245,6 +3543,14 @@
}, },
"client.IdentityEntry": { "client.IdentityEntry": {
"type": "object", "type": "object",
"required": [
"added",
"fingerprint",
"number",
"safety_number",
"status",
"uuid"
],
"properties": { "properties": {
"added": { "added": {
"type": "string" "type": "string"
@ -3268,6 +3574,20 @@
}, },
"client.ListContactsResponse": { "client.ListContactsResponse": {
"type": "object", "type": "object",
"required": [
"blocked",
"color",
"given_name",
"message_expiration",
"name",
"nickname",
"note",
"number",
"profile",
"profile_name",
"username",
"uuid"
],
"properties": { "properties": {
"blocked": { "blocked": {
"type": "boolean" "type": "boolean"
@ -3309,6 +3629,12 @@
}, },
"client.ListDevicesResponse": { "client.ListDevicesResponse": {
"type": "object", "type": "object",
"required": [
"creation_timestamp",
"id",
"last_seen_timestamp",
"name"
],
"properties": { "properties": {
"creation_timestamp": { "creation_timestamp": {
"type": "integer" "type": "integer"
@ -3326,6 +3652,13 @@
}, },
"client.ListInstalledStickerPacksResponse": { "client.ListInstalledStickerPacksResponse": {
"type": "object", "type": "object",
"required": [
"author",
"installed",
"pack_id",
"title",
"url"
],
"properties": { "properties": {
"author": { "author": {
"type": "string" "type": "string"
@ -3346,6 +3679,11 @@
}, },
"client.Nickname": { "client.Nickname": {
"type": "object", "type": "object",
"required": [
"family_name",
"given_name",
"name"
],
"properties": { "properties": {
"family_name": { "family_name": {
"type": "string" "type": "string"
@ -3360,6 +3698,10 @@
}, },
"client.SetUsernameResponse": { "client.SetUsernameResponse": {
"type": "object", "type": "object",
"required": [
"username",
"username_link"
],
"properties": { "properties": {
"username": { "username": {
"type": "string" "type": "string"
@ -3371,6 +3713,11 @@
}, },
"data.GroupPermissions": { "data.GroupPermissions": {
"type": "object", "type": "object",
"required": [
"add_members",
"edit_group",
"send_messages"
],
"properties": { "properties": {
"add_members": { "add_members": {
"type": "string", "type": "string",
@ -3397,6 +3744,12 @@
}, },
"data.LinkPreviewType": { "data.LinkPreviewType": {
"type": "object", "type": "object",
"required": [
"base64_thumbnail",
"description",
"title",
"url"
],
"properties": { "properties": {
"base64_thumbnail": { "base64_thumbnail": {
"type": "string" "type": "string"
@ -3414,6 +3767,11 @@
}, },
"data.MessageMention": { "data.MessageMention": {
"type": "object", "type": "object",
"required": [
"author",
"length",
"start"
],
"properties": { "properties": {
"author": { "author": {
"type": "string" "type": "string"

View File

@ -4,6 +4,8 @@ definitions:
properties: properties:
uri: uri:
type: string type: string
required:
- uri
type: object type: object
api.AddStickerPackRequest: api.AddStickerPackRequest:
properties: properties:
@ -13,6 +15,9 @@ definitions:
pack_key: pack_key:
example: 19546e18eba0ff69dea78eb591465289d39e16f35e58389ae779d4f9455aff3a example: 19546e18eba0ff69dea78eb591465289d39e16f35e58389ae779d4f9455aff3a
type: string type: string
required:
- pack_id
- pack_key
type: object type: object
api.ChangeGroupAdminsRequest: api.ChangeGroupAdminsRequest:
properties: properties:
@ -20,6 +25,8 @@ definitions:
items: items:
type: string type: string
type: array type: array
required:
- admins
type: object type: object
api.ChangeGroupMembersRequest: api.ChangeGroupMembersRequest:
properties: properties:
@ -27,6 +34,8 @@ definitions:
items: items:
type: string type: string
type: array type: array
required:
- members
type: object type: object
api.ClosePollRequest: api.ClosePollRequest:
properties: properties:
@ -36,11 +45,16 @@ definitions:
recipient: recipient:
example: <phone number> OR <username> OR <group id> example: <phone number> OR <username> OR <group id>
type: string type: string
required:
- poll_timestamp
- recipient
type: object type: object
api.Configuration: api.Configuration:
properties: properties:
logging: logging:
$ref: '#/definitions/api.LoggingConfiguration' $ref: '#/definitions/api.LoggingConfiguration'
required:
- logging
type: object type: object
api.CreateGroupRequest: api.CreateGroupRequest:
properties: properties:
@ -67,6 +81,8 @@ definitions:
properties: properties:
id: id:
type: string type: string
required:
- id
type: object type: object
api.CreatePollRequest: api.CreatePollRequest:
properties: properties:
@ -87,12 +103,18 @@ definitions:
recipient: recipient:
example: <phone number> OR <username> OR <group id> example: <phone number> OR <username> OR <group id>
type: string type: string
required:
- answers
- question
- recipient
type: object type: object
api.CreatePollResponse: api.CreatePollResponse:
properties: properties:
timestamp: timestamp:
example: "1769271479" example: "1769271479"
type: string type: string
required:
- timestamp
type: object type: object
api.DeleteLocalAccountDataRequest: api.DeleteLocalAccountDataRequest:
properties: properties:
@ -104,16 +126,34 @@ definitions:
properties: properties:
device_link_uri: device_link_uri:
type: string type: string
required:
- device_link_uri
type: object type: object
api.Error: api.Error:
properties: properties:
error: error:
type: string type: string
required:
- error
type: object type: object
api.LoggingConfiguration: api.LoggingConfiguration:
properties: properties:
Level: Level:
type: string type: string
required:
- Level
type: object
api.PinMessageInGroupRequest:
properties:
duration:
type: integer
target_author:
type: string
timestamp:
type: integer
required:
- target_author
- timestamp
type: object type: object
api.RateLimitChallengeRequest: api.RateLimitChallengeRequest:
properties: properties:
@ -123,17 +163,9 @@ definitions:
challenge_token: challenge_token:
example: <challenge token> example: <challenge token>
type: string type: string
type: object required:
api.Reaction: - captcha
properties: - challenge_token
reaction:
type: string
recipient:
type: string
target_author:
type: string
timestamp:
type: integer
type: object type: object
api.Receipt: api.Receipt:
properties: properties:
@ -146,6 +178,10 @@ definitions:
type: string type: string
timestamp: timestamp:
type: integer type: integer
required:
- receipt_type
- recipient
- timestamp
type: object type: object
api.RegisterNumberRequest: api.RegisterNumberRequest:
properties: properties:
@ -160,11 +196,31 @@ definitions:
type: string type: string
timestamp: timestamp:
type: integer type: integer
required:
- recipient
- timestamp
type: object type: object
api.RemoteDeleteResponse: api.RemoteDeleteResponse:
properties: properties:
timestamp: timestamp:
type: string type: string
required:
- timestamp
type: object
api.RemoveReactionRequest:
properties:
reaction:
type: string
recipient:
type: string
target_author:
type: string
timestamp:
type: integer
required:
- recipient
- target_author
- timestamp
type: object type: object
api.SearchResponse: api.SearchResponse:
properties: properties:
@ -172,6 +228,9 @@ definitions:
type: string type: string
registered: registered:
type: boolean type: boolean
required:
- number
- registered
type: object type: object
api.SendMessageError: api.SendMessageError:
properties: properties:
@ -183,11 +242,16 @@ definitions:
type: array type: array
error: error:
type: string type: string
required:
- account
- error
type: object type: object
api.SendMessageResponse: api.SendMessageResponse:
properties: properties:
timestamp: timestamp:
type: string type: string
required:
- timestamp
type: object type: object
api.SendMessageV1: api.SendMessageV1:
properties: properties:
@ -206,6 +270,10 @@ definitions:
items: items:
type: string type: string
type: array type: array
required:
- message
- number
- recipients
type: object type: object
api.SendMessageV2: api.SendMessageV2:
properties: properties:
@ -254,17 +322,41 @@ definitions:
type: string type: string
view_once: view_once:
type: boolean type: boolean
required:
- message
- number
- recipients
type: object
api.SendReactionRequest:
properties:
reaction:
type: string
recipient:
type: string
target_author:
type: string
timestamp:
type: integer
required:
- reaction
- recipient
- target_author
- timestamp
type: object type: object
api.SetPinRequest: api.SetPinRequest:
properties: properties:
pin: pin:
type: string type: string
required:
- pin
type: object type: object
api.SetUsernameRequest: api.SetUsernameRequest:
properties: properties:
username: username:
example: test example: test
type: string type: string
required:
- username
type: object type: object
api.TrustIdentityRequest: api.TrustIdentityRequest:
properties: properties:
@ -278,16 +370,32 @@ definitions:
properties: properties:
trust_mode: trust_mode:
type: string type: string
required:
- trust_mode
type: object type: object
api.TrustModeResponse: api.TrustModeResponse:
properties: properties:
trust_mode: trust_mode:
type: string type: string
required:
- trust_mode
type: object type: object
api.TypingIndicatorRequest: api.TypingIndicatorRequest:
properties: properties:
recipient: recipient:
type: string type: string
required:
- recipient
type: object
api.UnpinMessageInGroupRequest:
properties:
target_author:
type: string
timestamp:
type: integer
required:
- target_author
- timestamp
type: object type: object
api.UnregisterNumberRequest: api.UnregisterNumberRequest:
properties: properties:
@ -313,6 +421,8 @@ definitions:
type: string type: string
recipient: recipient:
type: string type: string
required:
- recipient
type: object type: object
api.UpdateGroupRequest: api.UpdateGroupRequest:
properties: properties:
@ -341,6 +451,8 @@ definitions:
type: string type: string
name: name:
type: string type: string
required:
- name
type: object type: object
api.VerifyNumberSettings: api.VerifyNumberSettings:
properties: properties:
@ -364,6 +476,11 @@ definitions:
items: items:
type: integer type: integer
type: array type: array
required:
- poll_author
- poll_timestamp
- recipient
- selected_answers
type: object type: object
client.About: client.About:
properties: properties:
@ -383,6 +500,12 @@ definitions:
items: items:
type: string type: string
type: array type: array
required:
- build
- capabilities
- mode
- version
- versions
type: object type: object
client.ContactProfile: client.ContactProfile:
properties: properties:
@ -396,6 +519,12 @@ definitions:
type: integer type: integer
lastname: lastname:
type: string type: string
required:
- about
- given_name
- has_avatar
- last_updated_timestamp
- lastname
type: object type: object
client.GroupEntry: client.GroupEntry:
properties: properties:
@ -429,6 +558,18 @@ definitions:
type: array type: array
permissions: permissions:
$ref: '#/definitions/data.GroupPermissions' $ref: '#/definitions/data.GroupPermissions'
required:
- admins
- blocked
- description
- id
- internal_id
- invite_link
- members
- name
- pending_invites
- pending_requests
- permissions
type: object type: object
client.IdentityEntry: client.IdentityEntry:
properties: properties:
@ -444,6 +585,13 @@ definitions:
type: string type: string
uuid: uuid:
type: string type: string
required:
- added
- fingerprint
- number
- safety_number
- status
- uuid
type: object type: object
client.ListContactsResponse: client.ListContactsResponse:
properties: properties:
@ -471,6 +619,19 @@ definitions:
type: string type: string
uuid: uuid:
type: string type: string
required:
- blocked
- color
- given_name
- message_expiration
- name
- nickname
- note
- number
- profile
- profile_name
- username
- uuid
type: object type: object
client.ListDevicesResponse: client.ListDevicesResponse:
properties: properties:
@ -482,6 +643,11 @@ definitions:
type: integer type: integer
name: name:
type: string type: string
required:
- creation_timestamp
- id
- last_seen_timestamp
- name
type: object type: object
client.ListInstalledStickerPacksResponse: client.ListInstalledStickerPacksResponse:
properties: properties:
@ -495,6 +661,12 @@ definitions:
type: string type: string
url: url:
type: string type: string
required:
- author
- installed
- pack_id
- title
- url
type: object type: object
client.Nickname: client.Nickname:
properties: properties:
@ -504,6 +676,10 @@ definitions:
type: string type: string
name: name:
type: string type: string
required:
- family_name
- given_name
- name
type: object type: object
client.SetUsernameResponse: client.SetUsernameResponse:
properties: properties:
@ -511,6 +687,9 @@ definitions:
type: string type: string
username_link: username_link:
type: string type: string
required:
- username
- username_link
type: object type: object
data.GroupPermissions: data.GroupPermissions:
properties: properties:
@ -529,6 +708,10 @@ definitions:
- only-admins - only-admins
- every-member - every-member
type: string type: string
required:
- add_members
- edit_group
- send_messages
type: object type: object
data.LinkPreviewType: data.LinkPreviewType:
properties: properties:
@ -540,6 +723,11 @@ definitions:
type: string type: string
url: url:
type: string type: string
required:
- base64_thumbnail
- description
- title
- url
type: object type: object
data.MessageMention: data.MessageMention:
properties: properties:
@ -549,6 +737,10 @@ definitions:
type: integer type: integer
start: start:
type: integer type: integer
required:
- author
- length
- start
type: object type: object
host: localhost:8080 host: localhost:8080
info: info:
@ -1543,6 +1735,77 @@ paths:
summary: Add one or more members to an existing Signal Group. summary: Add one or more members to an existing Signal Group.
tags: tags:
- Groups - Groups
/v1/groups/{number}/{groupid}/pin-message:
delete:
consumes:
- application/json
description: Unpin a message in a Signal Group.
parameters:
- description: Unpin
in: body
name: data
required: true
schema:
$ref: '#/definitions/api.UnpinMessageInGroupRequest'
- description: Registered Phone Number
in: path
name: number
required: true
type: string
- description: Group Id
in: path
name: groupid
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"400":
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
summary: Unpin a message in a Signal Group.
tags:
- Groups
post:
consumes:
- application/json
description: Pin a message in a Signal Group.
parameters:
- description: Pin
in: body
name: data
required: true
schema:
$ref: '#/definitions/api.PinMessageInGroupRequest'
- description: Registered Phone Number
in: path
name: number
required: true
type: string
- description: Group Id
in: path
name: groupid
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"400":
description: Bad Request
schema:
$ref: '#/definitions/api.Error'
summary: Pin a message in a Signal Group.
tags:
- Groups
/v1/groups/{number}/{groupid}/quit: /v1/groups/{number}/{groupid}/quit:
post: post:
consumes: consumes:
@ -1817,7 +2080,7 @@ paths:
name: data name: data
required: true required: true
schema: schema:
$ref: '#/definitions/api.Reaction' $ref: '#/definitions/api.RemoveReactionRequest'
- description: Registered phone number - description: Registered phone number
in: path in: path
name: number name: number
@ -1847,7 +2110,7 @@ paths:
name: data name: data
required: true required: true
schema: schema:
$ref: '#/definitions/api.Reaction' $ref: '#/definitions/api.SendReactionRequest'
- description: Registered phone number - description: Registered phone number
in: path in: path
name: number name: number

View File

@ -84,6 +84,7 @@ func main() {
} else { } else {
docs.SwaggerInfo.Schemes = []string{"https", "http"} docs.SwaggerInfo.Schemes = []string{"https", "http"}
} }
docs.SwaggerInfo.Version = utils.GetEnv("BUILD_VERSION", "1.0")
router := gin.New() router := gin.New()
router.Use(gin.LoggerWithConfig(gin.LoggerConfig{ router.Use(gin.LoggerWithConfig(gin.LoggerConfig{
@ -163,7 +164,7 @@ func main() {
jsonRpc2ClientConfigPathPath := *signalCliConfig + "/jsonrpc2.yml" jsonRpc2ClientConfigPathPath := *signalCliConfig + "/jsonrpc2.yml"
signalCliApiConfigPath := *signalCliConfig + "/api-config.yml" signalCliApiConfigPath := *signalCliConfig + "/api-config.yml"
signalClient := client.NewSignalClient(*signalCliConfig, *attachmentTmpDir, *avatarTmpDir, signalCliMode, jsonRpc2ClientConfigPathPath, signalCliApiConfigPath, webhookUrl) signalClient := client.NewSignalClient(*signalCliConfig, *attachmentTmpDir, *avatarTmpDir, signalCliMode, jsonRpc2ClientConfigPathPath, signalCliApiConfigPath, webhookUrl)
err = signalClient.Init(15) err = signalClient.Init(60)
if err != nil { if err != nil {
log.Fatal("Couldn't init Signal Client: ", err.Error()) log.Fatal("Couldn't init Signal Client: ", err.Error())
} }
@ -225,6 +226,8 @@ func main() {
groups.DELETE(":number/:groupid/members", api.RemoveMembersFromGroup) groups.DELETE(":number/:groupid/members", api.RemoveMembersFromGroup)
groups.POST(":number/:groupid/admins", api.AddAdminsToGroup) groups.POST(":number/:groupid/admins", api.AddAdminsToGroup)
groups.DELETE(":number/:groupid/admins", api.RemoveAdminsFromGroup) groups.DELETE(":number/:groupid/admins", api.RemoveAdminsFromGroup)
groups.POST(":number/:groupid/pin-message", api.PinMessageInGroup)
groups.DELETE(":number/:groupid/pin-message", api.UnpinMessageInGroup)
} }
link := v1.Group("qrcodelink") link := v1.Group("qrcodelink")