commit 1943998fec067c139899400b4b3adb0fc46b70fb
Author: Joseph Crowell <joseph.w.crowell@gmail.com>
Date: Sat Dec 13 02:26:33 2025 +0000
diff --git a/bridge/matrix/helpers.go b/bridge/matrix/helpers.go
index 5e2b66e..2b5acdc 100644
--- a/bridge/matrix/helpers.go
+++ b/bridge/matrix/helpers.go
@@ -5321 +5323 @@ func interface2Struct(in interface{}, out interface{}) error {
}
// getDisplayName retrieves the displayName for mxid, querying the homeserver if the mxid is not in the cache.
-func (b *Bmatrix) getDisplayName(mxid id.UserID) string {
+func (b *Bmatrix) getDisplayName(ctx context.Context, mxid id.UserID) string {
// Localpart is the user name. Return it if UseUserName is set.
if b.GetBool("UseUserName") {
return mxid.Localpart()
}
b.RLock()
+
if val, present := b.NicknameMap[mxid.Localpart()]; present {
b.RUnlock()
return val.displayName
}
+
b.RUnlock()
- resp, err := b.mc.GetDisplayName(context.TODO(), mxid)
+ resp, err := b.mc.GetDisplayName(ctx, mxid)
if err != nil {
b.Log.Errorf("Retrieving the display name for %s failed: %s", mxid, err)
@@ -896 +917 @@ func (b *Bmatrix) cacheDisplayName(mxid id.UserID, displayName string) string {
conflict := false
b.Lock()
+
for localpart, v := range b.NicknameMap {
// to prevent username reuse across matrix servers - or even on the same server, append
// the mxid to the username when there is a conflict
@@ -1628 +1658 @@ func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool {
}
// getAvatarURL returns the avatar URL of the specified sender.
-func (b *Bmatrix) getAvatarURL(sender id.UserID) string {
- urlPath, err := b.mc.GetAvatarURL(context.TODO(), sender)
+func (b *Bmatrix) getAvatarURL(ctx context.Context, sender id.UserID) string {
+ urlPath, err := b.mc.GetAvatarURL(ctx, sender)
if err != nil {
b.Log.Errorf("getAvatarURL failed: %s", err)
diff --git a/bridge/matrix/matrix.go b/bridge/matrix/matrix.go
index fe1ce4f..0f8985b 100644
--- a/bridge/matrix/matrix.go
+++ b/bridge/matrix/matrix.go
@@ -709 +7010 @@ type MessageRelation struct {
}
type EditedMessage struct {
+ event.MessageEventContent
+
NewContent SubTextMessage `json:"m.new_content"`
RelatedTo MessageRelation `json:"m.relates_to"`
- event.MessageEventContent
}
type InReplyToRelationContent struct {
@@ -848 +859 @@ type InReplyToRelation struct {
}
type ReplyMessage struct {
- RelatedTo InReplyToRelation `json:"m.relates_to"`
event.MessageEventContent
+
+ RelatedTo InReplyToRelation `json:"m.relates_to"`
}
func New(cfg *bridge.Config) bridge.Bridger {
@@ -10112 +10314 @@ func (b *Bmatrix) Connect() error {
if b.GetString("MxID") != "" && b.GetString("Token") != "" {
userID := id.NewUserID(b.GetString("MxID"), b.GetString("Server"))
+
b.mc, err = mautrix.NewClient(
b.GetString("Server"), userID, b.GetString("Token"),
)
if err != nil {
return err
}
+
b.UserID = userID
b.AccessToken = b.GetString("Token")
b.Log.Info("Using existing Matrix credentials")
@@ -1157 +1198 @@ func (b *Bmatrix) Connect() error {
if err != nil {
return err
}
- resp, err := b.mc.Login(
+
+ resp, err2 := b.mc.Login(
context.TODO(),
&mautrix.ReqLogin{
Type: mautrix.AuthTypePassword,
@@ -1248 +1298 @@ func (b *Bmatrix) Connect() error {
StoreCredentials: true,
},
)
- if err != nil {
- return err
+ if err2 != nil {
+ return err2
}
b.UserID = resp.UserID
b.AccessToken = resp.AccessToken
@@ -1346 +1397 @@ func (b *Bmatrix) Connect() error {
// BEGIN CACHED MESSAGES FIX
**/
var initialSyncComplete = false
+
accountStore := mautrix.NewAccountDataStore("org.example.mybot.synctoken", b.mc)
b.mc.Store = accountStore
@@ -1526 +1587 @@ func (b *Bmatrix) Connect() error {
if err != nil {
b.Log.Fatalf("Failed to create filter: %v", err)
}
+
filterID := filterResponse.FilterID
err = b.mc.Store.SaveFilterID(context.Background(), b.UserID, filterID)
@@ -1647 +1717 @@ func (b *Bmatrix) Connect() error {
b.Log.Fatalf("Failed to save initial sync token: %v", err)
}
- syncer := b.mc.Syncer.(*mautrix.DefaultSyncer)
+ syncer := b.mc.Syncer.(*mautrix.DefaultSyncer) //nolint:forcetypeassert // We're only using DefaultSyncer
syncer.OnEventType(event.EventMessage, func(ctx context.Context, evt *event.Event) {
// Check if we are still in the initial sync phase
if !initialSyncComplete {
@@ -1806 +1877 @@ func (b *Bmatrix) Connect() error {
if syncErr != nil {
b.Log.Debugf("Sync() returned %v, retrying in 5 seconds...\n", syncErr)
time.Sleep(time.Second * 5)
+
continue
}
@@ -4817 +4897 @@ func (b *Bmatrix) NewHttpRequest(method, uri string, body io.Reader) (*http.Requ
}
func (b *Bmatrix) handlematrix() {
- syncer := b.mc.Syncer.(*mautrix.DefaultSyncer)
+ syncer := b.mc.Syncer.(*mautrix.DefaultSyncer) //nolint:forcetypeassert // We're only using DefaultSyncer
syncer.OnEventType(event.EventRedaction, b.handleRedactionEvent)
syncer.OnEventType(event.EventMessage, b.handleMessageEvent)
syncer.OnEventType(event.StateMember, b.handleMemberChange)
@@ -5426 +5507 @@ func (b *Bmatrix) handleReply(ev *event.Event, rmsg config.Message) bool {
}
rmsg.Text = body
+
rmsg.ParentID = relation.InReplyTo.EventID.String()
b.Remote <- rmsg
@@ -579144 +588164 @@ func (b *Bmatrix) handleMemberChange(ctx context.Context, ev *event.Event) {
}
}
+//nolint:funlen // This function is necessarily long because it is an event handler
func (b *Bmatrix) handleRedactionEvent(ctx context.Context, ev *event.Event) {
b.Log.Debugf("== Receiving redaction event: %#v", ev)
- if ev.Sender != b.UserID {
- b.RLock()
- channel, ok := b.RoomMap[ev.RoomID]
- b.RUnlock()
- if !ok {
- b.Log.Debugf("Unknown room %s", ev.RoomID)
- return
- }
- // Create our message
- rmsg := config.Message{
- Username: b.getDisplayName(ev.Sender),
- Channel: channel,
- Account: b.Account,
- UserID: ev.Sender.String(),
- ID: ev.ID.String(),
- Avatar: b.getAvatarURL(ev.Sender),
- }
+ if ev.Sender == b.UserID {
+ return
+ }
- // Remove homeserver suffix if configured
- if b.GetBool("NoHomeServerSuffix") {
- re := regexp.MustCompile(`\s+\(@.*`)
- rmsg.Username = re.ReplaceAllString(rmsg.Username, `$1`)
- }
+ b.RLock()
+ channel, ok := b.RoomMap[ev.RoomID]
+ b.RUnlock()
- // Delete event
- if ev.Type == event.EventRedaction {
- rmsg.Event = config.EventMsgDelete
- rmsg.ID = ev.Redacts.String()
- rmsg.Text = config.EventMsgDelete
- b.Remote <- rmsg
- return
- }
+ if !ok {
+ b.Log.Debugf("Unknown room %s", ev.RoomID)
+ return
+ }
- // Text must be a string
- if rmsg.Text, ok = ev.Content.GetRaw()["body"].(string); !ok {
- contentBytes, err := json.Marshal(ev)
- if err != nil {
- b.Log.Errorf("Error marshalling event content to JSON: %v", err)
- return
- }
+ // Create our message
+ rmsg := config.Message{
+ Username: b.getDisplayName(ctx, ev.Sender),
+ Channel: channel,
+ Account: b.Account,
+ UserID: ev.Sender.String(),
+ ID: ev.ID.String(),
+ Avatar: b.getAvatarURL(ctx, ev.Sender),
+ }
- eventString := string(contentBytes)
+ // Remove homeserver suffix if configured
+ if b.GetBool("NoHomeServerSuffix") {
+ re := regexp.MustCompile(`\s+\(@.*`)
+ rmsg.Username = re.ReplaceAllString(rmsg.Username, `$1`)
+ }
- b.Log.Errorf("Content[body] is not a string: %T\n%#v", ev.Content.GetRaw()["body"], eventString)
- return
- }
+ // Delete event
+ if ev.Type == event.EventRedaction {
+ rmsg.Event = config.EventMsgDelete
+ rmsg.ID = ev.Redacts.String()
- b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
+ rmsg.Text = config.EventMsgDelete
b.Remote <- rmsg
- // not crucial, so no ratelimit check here
- if err := b.mc.MarkRead(context.TODO(), ev.RoomID, ev.ID); err != nil {
- b.Log.Errorf("couldn't mark message as read %s", err.Error())
+ return
+ }
+
+ // Text must be a string
+ if rmsg.Text, ok = ev.Content.GetRaw()["body"].(string); !ok {
+ contentBytes, err := json.Marshal(ev)
+ if err != nil {
+ b.Log.Errorf("Error marshalling event content to JSON: %v", err)
+ return
}
+
+ eventString := string(contentBytes)
+
+ b.Log.Errorf("Content[body] is not a string: %T\n%#v", ev.Content.GetRaw()["body"], eventString)
+
+ return
+ }
+
+ b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
+
+ b.Remote <- rmsg
+
+ // not crucial, so no ratelimit check here
+ err := b.mc.MarkRead(ctx, ev.RoomID, ev.ID)
+ if err != nil {
+ b.Log.Errorf("couldn't mark message as read %s", err.Error())
}
}
+//nolint:funlen // This function is necessarily long because it is an event handler
func (b *Bmatrix) handleMessageEvent(ctx context.Context, ev *event.Event) {
b.Log.Debugf("== Receiving message event: %#v", ev)
- if ev.Sender != b.UserID {
- b.RLock()
- channel, ok := b.RoomMap[ev.RoomID]
- b.RUnlock()
- if !ok {
- b.Log.Debugf("Unknown room %s", ev.RoomID)
- return
- }
- // Create our message
- rmsg := config.Message{
- Username: b.getDisplayName(ev.Sender),
- Channel: channel,
- Account: b.Account,
- UserID: ev.Sender.String(),
- ID: ev.ID.String(),
- Avatar: b.getAvatarURL(ev.Sender),
- }
+ if ev.Sender == b.UserID {
+ return
+ }
- // Remove homeserver suffix if configured
- if b.GetBool("NoHomeServerSuffix") {
- re := regexp.MustCompile(`\s+\(@.*`)
- rmsg.Username = re.ReplaceAllString(rmsg.Username, `$1`)
- }
+ b.RLock()
+ channel, ok := b.RoomMap[ev.RoomID]
+ b.RUnlock()
- // Delete event as a relation
- if ev.Unsigned.RedactedBecause != nil {
- rmsg.Event = config.EventMsgDelete
- rmsg.ID = ev.Unsigned.RedactedBecause.Redacts.String()
- rmsg.Text = config.EventMsgDelete
- b.Remote <- rmsg
- return
- }
+ if !ok {
+ b.Log.Debugf("Unknown room %s", ev.RoomID)
+ return
+ }
- // Text must be a string
- if rmsg.Text, ok = ev.Content.GetRaw()["body"].(string); !ok {
- contentBytes, err := json.Marshal(ev)
- if err != nil {
- b.Log.Errorf("Error marshalling event content to JSON: %v", err)
- return
- }
+ // Create our message
+ rmsg := config.Message{
+ Username: b.getDisplayName(ctx, ev.Sender),
+ Channel: channel,
+ Account: b.Account,
+ UserID: ev.Sender.String(),
+ ID: ev.ID.String(),
+ Avatar: b.getAvatarURL(ctx, ev.Sender),
+ }
- eventString := string(contentBytes)
+ // Remove homeserver suffix if configured
+ if b.GetBool("NoHomeServerSuffix") {
+ re := regexp.MustCompile(`\s+\(@.*`)
+ rmsg.Username = re.ReplaceAllString(rmsg.Username, `$1`)
+ }
- b.Log.Errorf("Content[body] is not a string: %T\n%#v", ev.Content.GetRaw()["body"], eventString)
- return
- }
+ // Delete event as a relation
+ if ev.Unsigned.RedactedBecause != nil {
+ rmsg.Event = config.EventMsgDelete
+ rmsg.ID = ev.Unsigned.RedactedBecause.Redacts.String()
- // Do we have a /me action
- if ev.Content.AsMessage().MsgType == event.MsgEmote {
- rmsg.Event = config.EventUserAction
- }
+ rmsg.Text = config.EventMsgDelete
+ b.Remote <- rmsg
- // Is it an edit?
- if b.handleEdit(ev, rmsg) {
- return
- }
+ return
+ }
- // Is it a reply?
- if b.handleReply(ev, rmsg) {
+ // Text must be a string
+ if rmsg.Text, ok = ev.Content.GetRaw()["body"].(string); !ok {
+ contentBytes, err := json.Marshal(ev)
+ if err != nil {
+ b.Log.Errorf("Error marshalling event content to JSON: %v", err)
return
}
- // Do we have an attachment
- // TODO: does matrix support multiple attachments?
- if b.handleAttachment(ev, rmsg) {
- return
- }
+ eventString := string(contentBytes)
- b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
- b.Remote <- rmsg
+ b.Log.Errorf("Content[body] is not a string: %T\n%#v", ev.Content.GetRaw()["body"], eventString)
- // not crucial, so no ratelimit check here
- if err := b.mc.MarkRead(context.TODO(), ev.RoomID, ev.ID); err != nil {
- b.Log.Errorf("couldn't mark message as read %s", err.Error())
- }
+ return
+ }
+
+ // Do we have a /me action
+ if ev.Content.AsMessage().MsgType == event.MsgEmote {
+ rmsg.Event = config.EventUserAction
+ }
+
+ // Is it an edit?
+ if b.handleEdit(ev, rmsg) {
+ return
+ }
+
+ // Is it a reply?
+ if b.handleReply(ev, rmsg) {
+ return
+ }
+
+ // Do we have an attachment
+ // TODO: does matrix support multiple attachments?
+ if b.handleAttachment(ev, rmsg) {
+ return
+ }
+
+ b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
+
+ b.Remote <- rmsg
+
+ // not crucial, so no ratelimit check here
+ var err = b.mc.MarkRead(ctx, ev.RoomID, ev.ID)
+ if err != nil {
+ b.Log.Errorf("couldn't mark message as read %s", err.Error())
}
}
@@ -7866 +8158 @@ func (b *Bmatrix) handleUploadFiles(msg *config.Message, roomID id.RoomID) (stri
}
// handleUploadFile handles native upload of a file.
+//
+//nolint:funlen // This function is necessarily long because it is an event handler
func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *config.FileInfo) {
username := newMatrixUsername(msg.Username)
content := bytes.NewReader(*fi.Data)
@@ -8009 +8319 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *co
Format: event.FormatHTML,
}
- _, err := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
+ _, err2 := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
- return err
+ return err2
})
if err != nil {
b.Log.Errorf("file comment failed: %#v", err)
@@ -8199 +85011 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *co
ContentLength: int64(len(*fi.Data)),
}
- res, err = b.mc.UploadMedia(context.TODO(), media)
+ var err2 error
- return err
+ res, err2 = b.mc.UploadMedia(context.TODO(), media)
+
+ return err2
})
if err != nil {
@@ -83915 +87216 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *co
URL: id.ContentURIString(res.ContentURI.String()),
}
- _, err := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
+ _, err2 := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
- return err
+ return err2
})
if err != nil {
b.Log.Errorf("sendVideo failed: %#v", err)
}
case strings.Contains(mtype, "image"):
b.Log.Debugf("sendImage %s", res.ContentURI)
+
err = b.retry(func() error {
content := event.MessageEventContent{
MsgType: event.MsgImage,
@@ -8559 +8899 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *co
URL: id.ContentURIString(res.ContentURI.String()),
}
- _, err := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
+ _, err2 := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
- return err
+ return err2
})
if err != nil {
b.Log.Errorf("sendImage failed: %#v", err)
@@ -8759 +9099 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *co
},
}
- _, err := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
+ _, err2 := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
- return err
+ return err2
})
if err != nil {
b.Log.Errorf("sendAudio failed: %#v", err)
@@ -8959 +9299 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, roomID id.RoomID, fi *co
},
}
- _, err := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
+ _, err2 := b.mc.SendMessageEvent(context.TODO(), roomID, event.EventMessage, content)
- return err
+ return err2
})
if err != nil {
b.Log.Errorf("sendFile failed: %#v", err)