From 9325022e460163bcbf928d461ae912e11941a213 Mon Sep 17 00:00:00 2001 From: Era Dorta Date: Thu, 28 Aug 2025 00:27:09 +0200 Subject: [PATCH] Add message deletion endpoint --- src/api/api.go | 47 +++++++++++++++++++++++++++++ src/client/client.go | 71 ++++++++++++++++++++++++++++++++++++++++++++ src/main.go | 5 ++++ 3 files changed, 123 insertions(+) diff --git a/src/api/api.go b/src/api/api.go index d0a476a..dbb8cf7 100644 --- a/src/api/api.go +++ b/src/api/api.go @@ -163,6 +163,10 @@ type SendMessageResponse struct { Timestamp string `json:"timestamp"` } +type RemoteDeleteResponse struct { + Timestamp string `json:"timestamp"` +} + type TrustModeRequest struct { TrustMode string `json:"trust_mode"` } @@ -209,6 +213,11 @@ type SetPinRequest struct { Pin string `json:"pin"` } +type RemoteDeleteRequest struct { + Recipient string `json:"recipient"` + Timestamp int64 `json:"timestamp"` +} + type Api struct { signalClient *client.SignalClient wsMutex sync.Mutex @@ -2322,3 +2331,41 @@ func (a *Api) RemovePin(c *gin.Context) { c.Status(204) } + +// @Summary Send a signal message. +// @Tags Messages +// @Description Send a signal message +// @Accept json +// @Produce json +// @Success 201 {string} string "OK" +// @Failure 400 {object} Error +// @Param number path string true "Registered Phone Number" +// @Param data body TypingIndicatorRequest true "Type" +// @Router /v1/remote-delete/{number} [post] +func (a *Api) RemoteDelete(c *gin.Context) { + + var req RemoteDeleteRequest + err := c.BindJSON(&req) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - invalid request"}) + return + } + + number, err := url.PathUnescape(c.Param("number")) + if err != nil { + c.JSON(400, Error{Msg: "Couldn't process request - malformed number"}) + return + } + if number == "" { + c.JSON(400, Error{Msg: "Couldn't process request - number missing"}) + return + } + + timestamp, err := a.signalClient.RemoteDelete(number, req.Recipient, req.Timestamp) + + if err != nil { + c.JSON(400, Error{Msg: err.Error()}) + return + } + c.JSON(201, RemoteDeleteResponse{Timestamp: strconv.FormatInt(timestamp.Timestamp, 10)}) +} diff --git a/src/client/client.go b/src/client/client.go index d38fd47..b6d6de8 100644 --- a/src/client/client.go +++ b/src/client/client.go @@ -163,6 +163,10 @@ type SendResponse struct { Timestamp int64 `json:"timestamp"` } +type RemoteDeleteResponse struct { + Timestamp int64 `json:"timestamp"` +} + type About struct { SupportedApiVersions []string `json:"versions"` BuildNr int `json:"build"` @@ -2530,3 +2534,70 @@ func (s *SignalClient) RemovePin(number string) error { } return nil } + +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 + var err error + var resp RemoteDeleteResponse + var rawData string + + recp := recipient + isGroup := false + if strings.HasPrefix(recipient, groupPrefix) { + isGroup = true + recp, err = ConvertGroupIdToInternalGroupId(recipient) + if err != nil { + return resp, errors.New("Invalid group id") + } + } + + if s.signalCliMode == JsonRpc { + type Request struct { + Recipient string `json:"recipient,omitempty"` + GroupId string `json:"group-id,omitempty"` + Timestamp int64 `json:"target-timestamp"` + } + request := Request{} + if !isGroup { + request.Recipient = recp + } else { + request.GroupId = recp + } + request.Timestamp = timestamp + + jsonRpc2Client, err := s.getJsonRpc2Client() + if err != nil { + return resp, err + } + rawData, err = jsonRpc2Client.getRaw("remoteDelete", &number, request) + if err != nil { + return resp, err + } + + err = json.Unmarshal([]byte(rawData), &resp) + + return resp, err + } else { + cmd := []string{ + "--config", s.signalCliConfig, + "-a", number, + "remoteDelete", + } + if !isGroup { + cmd = append(cmd, recp) + } else { + cmd = append(cmd, []string{"-g", recp}...) + } + cmd = append(cmd, []string{"-t", strconv.FormatInt(timestamp, 10)}...) + rawData, err = s.cliClient.Execute(true, cmd, "") + if err != nil { + return resp, err + } + + resp.Timestamp, err = strconv.ParseInt(strings.TrimSuffix(rawData, "\n"), 10, 64) + if err != nil { + return resp, err + } + return resp, nil + } +} diff --git a/src/main.go b/src/main.go index e9d0eb5..1c1d0c0 100644 --- a/src/main.go +++ b/src/main.go @@ -279,6 +279,11 @@ func main() { typingIndicator.DELETE(":number", api.SendStopTyping) } + remoteDelete := v1.Group("remote-delete") + { + remoteDelete.POST(":number", api.RemoteDelete) + } + reactions := v1.Group("/reactions") { reactions.POST(":number", api.SendReaction)