added API endpoints for pinning/unpinning messages in groups

see #820
This commit is contained in:
Bernhard B 2026-03-27 21:15:17 +01:00
parent ef16efca12
commit 7687c5240b
3 changed files with 169 additions and 0 deletions

View File

@ -56,6 +56,17 @@ type UpdateGroupRequest struct {
Permissions *ds.GroupPermissions `json:"permissions"`
}
type PinMessageInGroupRequest struct {
TargetAuthor string `json:"target_author"`
Timestamp int64 `json:"timestamp"`
Duration *int `json:"duration"`
}
type UnpinMessageInGroupRequest struct {
TargetAuthor string `json:"target_author"`
Timestamp int64 `json:"timestamp"`
}
type ChangeGroupMembersRequest struct {
Members []string `json:"members"`
}
@ -1173,6 +1184,103 @@ 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
}
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.
// @Tags Devices
// @Description Link device and generate QR code

View File

@ -1456,6 +1456,65 @@ func (s *SignalClient) GetGroupExpanded(number string, groupId string) (*Expande
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) {
var err error
var rawData string

View File

@ -225,6 +225,8 @@ func main() {
groups.DELETE(":number/:groupid/members", api.RemoveMembersFromGroup)
groups.POST(":number/:groupid/admins", api.AddAdminsToGroup)
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")