Watch
1
0
Fork
You've already forked mautrix-signal
0
mirror of https://github.com/mautrix/signal.git synced 2026-11-09 23:59:16 -05:00

Compare commits

..
Author SHA1 Message Date
indent[bot]
d451962ba2
msgconv: download stickers from their pack when the attachment is gone (#708)
Co-authored-by: Indent <noreply@indent.com>
2026-11-09 17:42:33 +00:00
indent[bot]
c827c97adb
chatinfo: fall back to profile avatar when contact avatar isn't loaded (#710)
Co-authored-by: Indent <noreply@indent.com>
2026-11-09 17:41:17 +00:00
indent[bot]
c820321548
signalmeow/websocket: log alerts sent by the server when connecting (#709)
Co-authored-by: Indent <noreply@indent.com>
2026-11-09 17:34:19 +00:00
indent[bot]
34b4187e07
chatinfo: stop listing contacts after request cancellation (#705)
Co-authored-by: Indent <noreply@indent.com>
2026-11-05 15:33:19 +00:00
Nick Mills-Barrett
a630835d70
pre-commit: install bridgelint with the CI Go toolchain (#706) 2026-11-05 13:09:30 +00:00
5 changed files with 85 additions and 13 deletions

View file

@ -30,16 +30,11 @@ jobs:
run: | run: |
go install golang.org/x/tools/cmd/goimports@latest go install golang.org/x/tools/cmd/goimports@latest
go install honnef.co/go/tools/cmd/staticcheck@latest go install honnef.co/go/tools/cmd/staticcheck@latest
go install github.com/beeper/pre-commit-go/bridgelint@latest
export PATH="$HOME/go/bin:$PATH" export PATH="$HOME/go/bin:$PATH"
- name: Cache pre-commit
uses: actions/cache@v6
with:
path: ~/.cache/pre-commit
key: pre-commit-go${{ matrix.go-version }}-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit - name: Run pre-commit
run: pipx run pre-commit run --all-files --show-diff-on-failure --color=always uses: pre-commit/action@v3.0.1
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -27,4 +27,5 @@ repos:
- id: zerolog-ban-msgf - id: zerolog-ban-msgf
- id: zerolog-use-stringer - id: zerolog-use-stringer
- id: bridgelint - id: bridgelint
language: system
verbose: true verbose: true

View file

@ -125,9 +125,10 @@ func (s *SignalClient) contactToUserInfo(ctx context.Context, contact *types.Rec
} }
name := s.Main.Config.FormatDisplayname(contact, username) name := s.Main.Config.FormatDisplayname(contact, username)
ui.Name = &name ui.Name = &name
if s.Main.Config.UseContactAvatars && contact.ContactAvatar.Hash != "" { contactAvatarID := networkid.AvatarID("hash:" + contact.ContactAvatar.Hash)
if s.Main.Config.UseContactAvatars && contact.ContactAvatar.Hash != "" && s.canUseContactAvatar(ctx, contact, contactAvatarID) {
ui.Avatar = &bridgev2.Avatar{ ui.Avatar = &bridgev2.Avatar{
ID: networkid.AvatarID("hash:" + contact.ContactAvatar.Hash), ID: contactAvatarID,
Get: func(ctx context.Context) ([]byte, error) { Get: func(ctx context.Context) ([]byte, error) {
if contact.ContactAvatar.Image == nil { if contact.ContactAvatar.Image == nil {
return nil, fmt.Errorf("contact avatar not available") return nil, fmt.Errorf("contact avatar not available")
@ -172,6 +173,20 @@ func (s *SignalClient) contactToUserInfo(ctx context.Context, contact *types.Rec
return ui, nil return ui, nil
} }
func (s *SignalClient) canUseContactAvatar(ctx context.Context, contact *types.Recipient, avatarID networkid.AvatarID) bool {
if contact.ContactAvatar.Image != nil {
return true
} else if contact.ACI == uuid.Nil {
return false
}
ghost, err := s.Main.Bridge.GetExistingGhostByID(ctx, signalid.MakeUserID(contact.ACI))
if err != nil {
zerolog.Ctx(ctx).Err(err).Msg("Failed to get ghost to check contact avatar")
return true
}
return ghost != nil && ghost.AvatarID == avatarID && ghost.AvatarMXC != "" && ghost.AvatarSet
}
func (s *SignalConnector) ValidateUserID(id networkid.UserID) bool { func (s *SignalConnector) ValidateUserID(id networkid.UserID) bool {
_, err := signalid.ParseUserIDAsServiceID(id) _, err := signalid.ParseUserIDAsServiceID(id)
return err == nil return err == nil
@ -445,11 +460,15 @@ func (s *SignalClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveI
} }
resp := make([]*bridgev2.ResolveIdentifierResponse, len(recipients)) resp := make([]*bridgev2.ResolveIdentifierResponse, len(recipients))
for i, recipient := range recipients { for i, recipient := range recipients {
recipientResp := &bridgev2.ResolveIdentifierResponse{ if err = ctx.Err(); err != nil {
Chat: s.makeCreateDMResponse(ctx, recipient, nil), return nil, err
} }
recipientResp := &bridgev2.ResolveIdentifierResponse{}
var username string var username string
if recipient.ACI != uuid.Nil { if recipient.ACI != uuid.Nil {
recipientResp.Chat = &bridgev2.CreateChatResponse{
PortalKey: s.makeDMPortalKey(libsignalgo.NewACIServiceID(recipient.ACI)),
}
recipientResp.UserID = signalid.MakeUserID(recipient.ACI) recipientResp.UserID = signalid.MakeUserID(recipient.ACI)
ghost, err := s.Main.Bridge.GetGhostByID(ctx, recipientResp.UserID) ghost, err := s.Main.Bridge.GetGhostByID(ctx, recipientResp.UserID)
if err != nil { if err != nil {
@ -458,7 +477,11 @@ func (s *SignalClient) GetContactList(ctx context.Context) ([]*bridgev2.ResolveI
recipientResp.Ghost = ghost recipientResp.Ghost = ghost
username = ghost.Metadata.(*signalid.GhostMetadata).Username username = ghost.Metadata.(*signalid.GhostMetadata).Username
} else { } else {
recipientResp.UserID = signalid.MakeUserIDFromServiceID(libsignalgo.NewPNIServiceID(recipient.PNI)) pniServiceID := libsignalgo.NewPNIServiceID(recipient.PNI)
recipientResp.Chat = &bridgev2.CreateChatResponse{
PortalKey: s.makeDMPortalKey(pniServiceID),
}
recipientResp.UserID = signalid.MakeUserIDFromServiceID(pniServiceID)
} }
recipientResp.UserInfo, err = s.contactToUserInfo(ctx, recipient, username, false) recipientResp.UserInfo, err = s.contactToUserInfo(ctx, recipient, username, false)
if err != nil { if err != nil {

View file

@ -502,6 +502,11 @@ func (mc *MessageConverter) convertAttachmentToMatrix(ctx context.Context, index
func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker *signalpb.DataMessage_Sticker, attMap AttachmentMap) *bridgev2.ConvertedMessagePart { func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker *signalpb.DataMessage_Sticker, attMap AttachmentMap) *bridgev2.ConvertedMessagePart {
converted, err := mc.reuploadAttachment(ctx, sticker.GetData(), attMap) converted, err := mc.reuploadAttachment(ctx, sticker.GetData(), attMap)
hasPack := len(sticker.GetPackId()) == PackIDLength && len(sticker.GetPackKey()) == PackKeyLength && !bytes.Equal(sticker.GetPackId(), zeroPackID)
if err != nil && hasPack && (errors.Is(err, signalmeow.ErrAttachmentNotFound) || errors.Is(err, ErrAttachmentNotInBackup) || errors.Is(err, ErrBackupNotSupported)) {
zerolog.Ctx(ctx).Debug().Err(err).Msg("Sticker attachment is unavailable, downloading it from the sticker pack")
converted, err = mc.reuploadStickerFromPack(ctx, sticker)
}
if err != nil { if err != nil {
zerolog.Ctx(ctx).Err(err).Msg("Failed to handle sticker") zerolog.Ctx(ctx).Err(err).Msg("Failed to handle sticker")
return &bridgev2.ConvertedMessagePart{ return &bridgev2.ConvertedMessagePart{
@ -519,7 +524,7 @@ func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker
converted.Content.Info.Height = 200 converted.Content.Info.Height = 200
} }
converted.Content.Body = sticker.GetEmoji() converted.Content.Body = sticker.GetEmoji()
if len(sticker.GetPackId()) == PackIDLength && len(sticker.GetPackKey()) == PackKeyLength && !bytes.Equal(sticker.GetPackId(), zeroPackID) { if hasPack {
converted.Content.Info.BridgedSticker = &event.BridgedSticker{ converted.Content.Info.BridgedSticker = &event.BridgedSticker{
Network: StickerSourceID, Network: StickerSourceID,
ID: strconv.FormatUint(uint64(sticker.GetStickerId()), 10), ID: strconv.FormatUint(uint64(sticker.GetStickerId()), 10),
@ -532,6 +537,49 @@ func (mc *MessageConverter) convertStickerToMatrix(ctx context.Context, sticker
return converted return converted
} }
func (mc *MessageConverter) reuploadStickerFromPack(ctx context.Context, sticker *signalpb.DataMessage_Sticker) (*bridgev2.ConvertedMessagePart, error) {
att := sticker.GetData()
content := &event.MessageEventContent{
Body: att.GetFileName(),
Info: &event.FileInfo{
MimeType: att.GetContentType(),
Width: int(att.GetWidth()),
Height: int(att.GetHeight()),
},
}
if mc.DirectMedia {
mediaID, err := signalid.DirectMediaSticker{
PackID: sticker.GetPackId(),
PackKey: sticker.GetPackKey(),
StickerID: sticker.GetStickerId(),
}.AsMediaID()
if err != nil {
return nil, err
}
content.URL, content.File, err = mc.Bridge.GenerateContentURI(ctx, getPortal(ctx).MXID, mediaID)
if err != nil {
return nil, err
}
} else {
data, err := signalmeow.DownloadStickerPackItem(ctx, sticker.GetPackId(), sticker.GetPackKey(), sticker.GetStickerId())
if err != nil {
return nil, fmt.Errorf("failed to download sticker from pack: %w", err)
}
if content.Info.MimeType == "" {
content.Info.MimeType = http.DetectContentType(data)
}
content.Info.Size = len(data)
content.URL, content.File, err = getIntent(ctx).UploadMedia(ctx, getPortal(ctx).MXID, data, content.Body, content.Info.MimeType)
if err != nil {
return nil, fmt.Errorf("failed to upload sticker from pack: %w", err)
}
}
return &bridgev2.ConvertedMessagePart{
Type: event.EventMessage,
Content: content,
}, nil
}
func (mc *MessageConverter) downloadSignalLongText(ctx context.Context, att *signalpb.AttachmentPointer, attMap AttachmentMap) (*string, error) { func (mc *MessageConverter) downloadSignalLongText(ctx context.Context, att *signalpb.AttachmentPointer, attMap AttachmentMap) (*string, error) {
data, err := mc.downloadAttachment(ctx, att, attMap, nil) data, err := mc.downloadAttachment(ctx, att, attMap, nil)
if err != nil { if err != nil {

View file

@ -319,6 +319,11 @@ func (s *SignalWebsocket) connectLoop(
} }
// Succssfully connected // Succssfully connected
if resp != nil {
if alerts := resp.Header.Values("X-Signal-Alert"); len(alerts) > 0 {
log.Warn().Strs("alerts", alerts).Msg("Signal server sent alerts when opening websocket")
}
}
s.pushStatus(ctx, SignalWebsocketConnectionEventConnected, nil) s.pushStatus(ctx, SignalWebsocketConnectionEventConnected, nil)
s.ws.Store(ws) s.ws.Store(ws)
retrying = false retrying = false