Thumbnail

rani/matterbridge.git

Clone URL: https://git.buni.party/rani/matterbridge.git

commit 7cfe0d24345a3b7621d6959535abef6b44baab94 Author: rani <clagv.randomgames@gmail.com> Date: Tue Feb 17 15:19:23 2026 +0000 Add config diff --git a/bridge/config/config.go b/bridge/config/config.go index 37a99eb..744ebfb 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -1789 +1789 @@ type Protocol struct {   PrefixMessagesWithNick bool // mattemost, slack   PreserveThreading bool // slack   Protocol string // all protocols - QuoteDisable bool // telegram - QuoteFormat string // telegram - QuoteLengthLimit int // telegram + QuoteDisable bool // telegram, discord + QuoteFormat string // telegram, discord + QuoteLengthLimit int // telegram, discord   RealName string // IRC   RecoveryKey string // matrix   RejoinDelay int // IRC @@ -2127 +2128 @@ type Protocol struct {   UseTLS bool // IRC   UseDiscriminator bool // discord   UseFirstName bool // telegram - UseUserName bool // discord, matrix, mattermost + UseUserName bool // discord, matrix, mattermost + UsePerProtocolJID bool // xmpp   UseInsecureURL bool // telegram   UserName string // IRC   VerboseJoinPart bool // IRC diff --git a/bridge/discord/handlers.go b/bridge/discord/handlers.go index dc3480e..a39acd4 100644 --- a/bridge/discord/handlers.go +++ b/bridge/discord/handlers.go @@ -16 +19 @@  package bdiscord    import ( + "time" + "strings" +   "github.com/bwmarrin/discordgo"   "github.com/davecgh/go-spew/spew"   "github.com/matterbridge-org/matterbridge/bridge/config" @@ -746 +7712 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat   }   // only when message is actually edited   if m.Message.EditedTimestamp != nil { + // don't relay old edits + // there are often spurrious "ghost" messages + delay := time.Now().Sub(*m.Message.EditedTimestamp) + if delay >= time.Duration(6) * time.Hour { + return + }   b.Log.Debugf("Sending edit message")   m.Content += b.GetString("EditSuffix")   msg := &discordgo.MessageCreate{ @@ -836 +9250 @@ func (b *Bdiscord) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdat   }  }   +func (b *Bdiscord) handleQuote(s *discordgo.Session, m *discordgo.Message, msg string) string { + if b.GetBool("QuoteDisable") { + return msg + } + if m.MessageReference == nil { + return msg + } + refMsgRef := m.MessageReference + refMsg, err := s.ChannelMessage(refMsgRef.ChannelID, refMsgRef.MessageID) + if err != nil { + b.Log.Errorf("Error getting quoted message %s:%s: %s", refMsgRef.ChannelID, refMsgRef.MessageID, err) + return msg + } + + quoteMessage := refMsg.Content + quoteNick := b.getNick(refMsg.Author, refMsg.GuildID) + fromWebhook := m.WebhookID != "" + if !fromWebhook && b.GetBool("UseDiscriminator") { + quoteNick += "#" + refMsg.Author.Discriminator + + } + + format := b.GetString("quoteformat") + if format == "" { + format = "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})" + } + quoteMessagelength := len([]rune(quoteMessage)) + if b.GetInt("QuoteLengthLimit") != 0 && quoteMessagelength >= b.GetInt("QuoteLengthLimit") { + runes := []rune(quoteMessage) + quoteMessage = string(runes[0:b.GetInt("QuoteLengthLimit")]) + if quoteMessagelength > b.GetInt("QuoteLengthLimit") { + quoteMessage += "..." + } + } + + replacer := strings.NewReplacer( + "{MESSAGE}", msg, + "{QUOTENICK}", quoteNick, + "{QUOTEMESSAGE}", quoteMessage, + ) + format = replacer.Replace(format) + return format +} +  func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { //nolint:unparam   if m.GuildID != b.guildID {   b.Log.Debugf("Ignoring messageCreate because it originates from a different guild") @@ -1827 +23510 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat   }     // Replace emotes - rmsg.Text = replaceEmotes(rmsg.Text) +// rmsg.Text = replaceEmotes(rmsg.Text) + + // Handle Reply thread + rmsg.Text = b.handleQuote(s, m.Message, rmsg.Text)     // Add our parent id if it exists, and if it's not referring to a message in another channel   if ref := m.MessageReference; ref != nil && ref.ChannelID == m.ChannelID { @@ -2877 +34316 @@ func handleEmbed(embed *discordgo.MessageEmbed) string {     t = append(t, embed.Title)   t = append(t, embed.Description) + + for _, f := range embed.Fields { + field := f.Name + ": " + f.Value + t = append(t, field) + } +   t = append(t, embed.URL) + if embed.Footer != nil { + t = append(t, embed.Footer.Text) + }     i := 0   for _, e := range t { @@ -3017 +3667 @@ func handleEmbed(embed *discordgo.MessageEmbed) string {   continue   }   - result += " - " + e + result += "\n" + e   }     if result != "" { diff --git a/bridge/xmpp/handler.go b/bridge/xmpp/handler.go deleted file mode 100644 index dd7f578..0000000 --- a/bridge/xmpp/handler.go +++ /dev/null @@ -134 +00 @@ -package bxmpp - -import ( - "github.com/matterbridge-org/matterbridge/bridge/config" - "github.com/matterbridge-org/matterbridge/bridge/helper" - "github.com/xmppo/go-xmpp" -) - -// handleDownloadAvatar downloads the avatar of userid from channel -// sends a EVENT_AVATAR_DOWNLOAD message to the gateway if successful. -// logs an error message if it fails -func (b *Bxmpp) handleDownloadAvatar(avatar xmpp.AvatarData) { - rmsg := config.Message{ - Username: "system", - Text: "avatar", - Channel: b.parseChannel(avatar.From), - Account: b.Account, - UserID: avatar.From, - Event: config.EventAvatarDownload, - Extra: make(map[string][]interface{}), - } - if _, ok := b.avatarMap[avatar.From]; !ok { - b.Log.Debugf("Avatar.From: %s", avatar.From) - - err := helper.HandleDownloadSize(b.Log, &rmsg, avatar.From+".png", int64(len(avatar.Data)), b.General) - if err != nil { - b.Log.Error(err) - return - } - helper.HandleDownloadData(b.Log, &rmsg, avatar.From+".png", rmsg.Text, "", &avatar.Data, b.General) - b.Log.Debugf("Avatar download complete") - b.Remote <- rmsg - } -} diff --git a/bridge/xmpp/helpers.go b/bridge/xmpp/helpers.go deleted file mode 100644 index 562d705..0000000 --- a/bridge/xmpp/helpers.go +++ /dev/null @@ -130 +00 @@ -package bxmpp - -import ( - "regexp" - - "github.com/matterbridge-org/matterbridge/bridge/config" -) - -var pathRegex = regexp.MustCompile("[^a-zA-Z0-9]+") - -// GetAvatar constructs a URL for a given user-avatar if it is available in the cache. -func getAvatar(av map[string]string, userid string, general *config.Protocol) string { - if hash, ok := av[userid]; ok { - // NOTE: This does not happen in bridge/helper/helper.go but messes up XMPP - id := pathRegex.ReplaceAllString(userid, "_") - return general.MediaServerDownload + "/" + hash + "/" + id + ".png" - } - return "" -} - -func (b *Bxmpp) cacheAvatar(msg *config.Message) string { - fi := msg.Extra["file"][0].(config.FileInfo) - /* if we have a sha we have successfully uploaded the file to the media server, - so we can now cache the sha */ - if fi.SHA != "" { - b.Log.Debugf("Added %s to %s in avatarMap", fi.SHA, msg.UserID) - b.avatarMap[msg.UserID] = fi.SHA - } - return "" -} diff --git a/bridge/xmpp/xmpp.go b/bridge/xmpp/xmpp.go index a51baec..be97699 100644 --- a/bridge/xmpp/xmpp.go +++ b/bridge/xmpp/xmpp.go @@ -112 +19 @@  package bxmpp    import ( - "bytes" - "crypto/tls" - "encoding/json" + "crypto/sha1" + "encoding/hex"   "fmt" - "net/http" - "net/url"   "strings"   "sync"   "time" @@ -14440 +11387 @@ import (   "github.com/jpillora/backoff"   "github.com/matterbridge-org/matterbridge/bridge"   "github.com/matterbridge-org/matterbridge/bridge/config" - "github.com/matterbridge-org/matterbridge/bridge/helper" - "github.com/rs/xid" - "github.com/xmppo/go-xmpp" + "gosrc.io/xmpp" + "gosrc.io/xmpp/stanza"  )    type Bxmpp struct {   *bridge.Config   - startTime time.Time - xc *xmpp.Client - xmppMap map[string]string - connected bool - sync.RWMutex + component *xmpp.Component + router *xmpp.Router   - avatarAvailability map[string]bool - avatarMap map[string]string + domain string + mucDomain string + botNick string + + mu sync.RWMutex + rooms map[string]*mucRoom + connected bool + reconnectCh chan struct{} +} + +type mucRoom struct { + Channel string + MucJID string + BotNick string + PuppetNicks map[string]string  }    func New(cfg *bridge.Config) bridge.Bridger {   return &Bxmpp{ - Config: cfg, - xmppMap: make(map[string]string), - avatarAvailability: make(map[string]bool), - avatarMap: make(map[string]string), + Config: cfg, + rooms: make(map[string]*mucRoom),   }  }    func (b *Bxmpp) Connect() error { - b.Log.Infof("Connecting %s", b.GetString("Server")) - if err := b.createXMPP(); err != nil { - b.Log.Debugf("%#v", err) + b.domain = b.GetString("Domain") + if b.domain == "" { + return fmt.Errorf("xmpp Domain is required for component gateway") + } + + b.mucDomain = b.GetString("Muc") + if b.mucDomain == "" { + b.mucDomain = "muc." + b.domain + } + + b.botNick = b.GetString("Nick") + if b.botNick == "" { + b.botNick = "matterbridge" + } + + b.Log.Debugf("XMPP component config: server=%s domain=%s muc=%s bot=%s", b.GetString("Server"), b.domain, b.mucDomain, b.botNick) + b.reconnectCh = make(chan struct{}, 1) + + opts := xmpp.ComponentOptions{ + TransportConfiguration: xmpp.TransportConfiguration{ + Address: b.GetString("Server"), + Domain: b.domain, + }, + Domain: b.domain, + Secret: b.GetString("Secret"), + Name: "matterbridge", + } + + b.router = xmpp.NewRouter() + b.router.HandleFunc("message", b.handleMessage) + + b.Log.Debug("XMPP component: creating component") + component, err := xmpp.NewComponent(opts, b.router, func(err error) { + b.handleComponentError(err) + }) + if err != nil {   return err   } + b.component = component   - b.Log.Info("Connection succeeded") + if err := b.component.Connect(); err != nil { + return err + } + + b.setConnected(true)   go b.manageConnection() + b.Log.Info("XMPP component connected")   return nil  }    func (b *Bxmpp) Disconnect() error { + b.leaveAllRooms() + if b.reconnectCh != nil { + close(b.reconnectCh) + b.reconnectCh = nil + } + b.setConnected(false)   return nil  }    func (b *Bxmpp) JoinChannel(channel config.ChannelInfo) error { - if channel.Options.Key != "" { - b.Log.Debugf("using key %s for channel %s", channel.Options.Key, channel.Name) - b.xc.JoinProtectedMUC(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick"), channel.Options.Key, xmpp.NoHistory, 0, nil) - } else { - b.xc.JoinMUCNoHistory(channel.Name+"@"+b.GetString("Muc"), b.GetString("Nick")) - } - return nil + room := b.ensureRoom(channel.Name) + b.Log.Debugf("XMPP join MUC: channel=%s muc=%s bot=%s", room.Channel, room.MucJID, room.BotNick) + return b.joinMuc(room.MucJID, room.BotNick, b.botJID())  }    func (b *Bxmpp) Send(msg config.Message) (string, error) { - // should be fixed by using a cache instead of dropping   if !b.Connected() { - return "", fmt.Errorf("bridge %s not connected, dropping message %#v to bridge", b.Account, msg) + b.Log.Warnf("XMPP not connected, dropping message on bridge %s", b.Account) + return "", fmt.Errorf("bridge %s not connected, dropping message", b.Account)   } - // ignore delete messages - if msg.Event == config.EventMsgDelete { + + if msg.Event == config.EventMsgDelete || msg.Event == config.EventAvatarDownload { + return "", nil + } + if msg.Event != "" && msg.Event != config.EventUserAction {   return "", nil   }   - b.Log.Debugf("=> Receiving %#v", msg) + room := b.ensureRoom(msg.Channel) + puppetJID, _ := b.ensurePuppet(room, msg.Username, msg.UserID, msg.Protocol)   - if msg.Event == config.EventAvatarDownload { - return b.cacheAvatar(&msg), nil + body := msg.Text + if msg.Event == config.EventUserAction { + body = "/me " + body   }   - // Make a action /me of the message, prepend the username with it. - // https://xmpp.org/extensions/xep-0245.html - if msg.Event == config.EventUserAction { - msg.Username = "/me " + msg.Username - } - - // Upload a file (in XMPP case send the upload URL because XMPP has no native upload support). - var err error - if msg.Extra != nil { - for _, rmsg := range helper.HandleExtra(&msg, b.General) { - b.Log.Debugf("=> Sending attachement message %#v", rmsg) - if b.GetString("WebhookURL") != "" { - err = b.postSlackCompatibleWebhook(msg) - } else { - _, err = b.xc.Send(xmpp.Chat{ - Type: "groupchat", - Remote: rmsg.Channel + "@" + b.GetString("Muc"), - Text: rmsg.Username + rmsg.Text, - }) - } + return "", b.sendMucMessage(room.MucJID, puppetJID, body) +}   - if err != nil { - b.Log.WithError(err).Error("Unable to send message with share URL.") - } - } - if len(msg.Extra["file"]) > 0 { - return "", b.handleUploadFile(&msg) - } +func (b *Bxmpp) handleMessage(s xmpp.Sender, p stanza.Packet) { + msg, ok := p.(stanza.Message) + if !ok { + return + } + if msg.Type != stanza.MessageTypeGroupchat { + return + } + if msg.Body == "" { + return + } + if msg.To != b.botJID() { + return   }   - if b.GetString("WebhookURL") != "" { - b.Log.Debugf("Sending message using Webhook") - err := b.postSlackCompatibleWebhook(msg) - if err != nil { - b.Log.Errorf("Failed to send message using webhook: %s", err) - return "", err - } + b.Log.Debugf("XMPP recv message: from=%s to=%s type=%s id=%s body_len=%d", msg.From, msg.To, msg.Type, msg.Id, len(msg.Body))   - return "", nil + roomJID, nick := parseRoomNick(msg.From) + if roomJID == "" || nick == "" { + return   }   - // Post normal message. - b.Log.Debugf("=> Sending message %#v", msg) - if _, err := b.xc.Send(xmpp.Chat{ - Type: "groupchat", - Remote: msg.Channel + "@" + b.GetString("Muc"), - Text: msg.Username + msg.Text, - }); err != nil { - return "", err + room := b.getRoomByJID(roomJID) + if room == nil { + return   }   - // Generate a dummy ID because to avoid collision with other internal messages - // However this does not provide proper Edits/Replies integration on XMPP side. - msgID := xid.New().String() - return msgID, nil -} - -func (b *Bxmpp) postSlackCompatibleWebhook(msg config.Message) error { - type XMPPWebhook struct { - Username string `json:"username"` - Text string `json:"text"` + if nick == room.BotNick { + return   } - webhookBody, err := json.Marshal(XMPPWebhook{ - Username: msg.Username, - Text: msg.Text, - }) - if err != nil { - b.Log.Errorf("Failed to marshal webhook: %s", err) - return err + if _, ok := room.PuppetNicks[nick]; ok { + return   }   - resp, err := http.Post(b.GetString("WebhookURL")+"/"+url.QueryEscape(msg.Channel), "application/json", bytes.NewReader(webhookBody)) - if err != nil { - b.Log.Errorf("Failed to POST webhook: %s", err) - return err + text := msg.Body + event := "" + if strings.HasPrefix(text, "/me ") { + text = strings.TrimPrefix(text, "/me ") + event = config.EventUserAction   }   - resp.Body.Close() - return nil + rmsg := config.Message{ + Username: nick, + Text: text, + Channel: room.Channel, + Account: b.Account, + UserID: msg.From, + Event: event, + } + b.Remote <- rmsg  }   -func (b *Bxmpp) createXMPP() error { - // TODO: remove in release after first community fork release (N+2) - if b.GetBool("NoTLS") { - b.Log.Fatalf("NoTLS setting has been deprecated. If you'd like to disable StartTLS and start a plaintext connection, use NoStartTLS instead.") - }   - var serverName string - switch { - case !b.GetBool("Anonymous"): - if !strings.Contains(b.GetString("Jid"), "@") { - return fmt.Errorf("the Jid %s doesn't contain an @", b.GetString("Jid")) - } - serverName = strings.Split(b.GetString("Jid"), "@")[1] - case !strings.Contains(b.GetString("Server"), ":"): - serverName = strings.Split(b.GetString("Server"), ":")[0] - default: - serverName = b.GetString("Server") - } - - tc := &tls.Config{ - ServerName: serverName, - InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec - } - - options := xmpp.Options{ - Host: b.GetString("Server"), - User: b.GetString("Jid"), - Password: b.GetString("Password"), - NoTLS: !b.GetBool("UseDirectTLS"), - StartTLS: !b.GetBool("NoStartTLS"), - TLSConfig: tc, - Debug: b.GetBool("debug"), - Session: true, - Status: "", - StatusMessage: "", - Resource: "", - InsecureAllowUnencryptedAuth: !b.GetBool("UseDirectTLS") && b.GetBool("NoStartTLS"), - DebugWriter: b.Log.Writer(), - Mechanism: b.GetString("Mechanism"), - NoPLAIN: b.GetBool("NoPLAIN"), - } - var err error - b.xc, err = options.NewClient() - return err -}   -func (b *Bxmpp) manageConnection() { - b.setConnected(true) - initial := true - bf := &backoff.Backoff{ - Min: time.Second, - Max: 5 * time.Minute, - Jitter: true, - } +func (b *Bxmpp) ensureRoom(channel string) *mucRoom { + b.mu.Lock() + defer b.mu.Unlock()   - // Main connection loop. Each iteration corresponds to a successful - // connection attempt and the subsequent handling of the connection. - for { - if initial { - initial = false - } else { - b.Remote <- config.Message{ - Username: "system", - Text: "rejoin", - Channel: "", - Account: b.Account, - Event: config.EventRejoinChannels, - } - } + if room, ok := b.rooms[channel]; ok { + return room + }   - if err := b.handleXMPP(); err != nil { - b.Log.WithError(err).Error("Disconnected.") - b.setConnected(false) - } + mucJID := b.buildMucJID(channel) + room := &mucRoom{ + Channel: channel, + MucJID: mucJID, + BotNick: b.botNick, + PuppetNicks: make(map[string]string), + } + b.rooms[channel] = room + return room +}   - // Reconnection loop using an exponential back-off strategy. We - // only break out of the loop if we have successfully reconnected. - for { - d := bf.Duration() - b.Log.Infof("Reconnecting in %s.", d) - time.Sleep(d) +func (b *Bxmpp) getRoomByJID(roomJID string) *mucRoom { + b.mu.RLock() + defer b.mu.RUnlock()   - b.Log.Infof("Reconnecting now.") - if err := b.createXMPP(); err == nil { - b.setConnected(true) - bf.Reset() - break - } - b.Log.Warn("Failed to reconnect.") + for _, room := range b.rooms { + if room.MucJID == roomJID { + return room   }   } + return nil  }   -func (b *Bxmpp) xmppKeepAlive() chan bool { - done := make(chan bool) - go func() { - ticker := time.NewTicker(90 * time.Second) - defer ticker.Stop() - for { - select { - case <-ticker.C: - b.Log.Debugf("PING") - if err := b.xc.PingC2S("", ""); err != nil { - b.Log.Debugf("PING failed %#v", err) - } - case <-done: - return - } - } - }() - return done +func (b *Bxmpp) buildMucJID(channel string) string { + return channel + "@" + b.mucDomain  }   -func (b *Bxmpp) handleXMPP() error { - b.startTime = time.Now() +func (b *Bxmpp) botJID() string { + return b.botNick + "@" + b.domain +}   - done := b.xmppKeepAlive() - defer close(done) +func (b *Bxmpp) ensurePuppet(room *mucRoom, displayName, userID, protocol string) (string, string) { + puppetNick := displayName + puppetJID := b.buildPuppetJID(displayName, userID, protocol)   - for { - m, err := b.xc.Recv() - if err != nil { - // An error together with AvatarData is non-fatal - switch m.(type) { - case xmpp.AvatarData: - continue - default: - return err - } + if _, ok := room.PuppetNicks[puppetNick]; !ok { + b.ensureMemberAffiliation(room.MucJID, puppetJID) + if err := b.joinMuc(room.MucJID, puppetNick, puppetJID); err != nil { + b.Log.WithError(err).Errorf("XMPP puppet join failed: room=%s nick=%s jid=%s", room.MucJID, puppetNick, puppetJID)   } + room.PuppetNicks[puppetNick] = puppetJID + }   - switch v := m.(type) { - case xmpp.Chat: - if v.Type == "groupchat" { - b.Log.Debugf("== Receiving %#v", v) - - // Skip invalid messages. - if b.skipMessage(v) { - continue - } + return puppetJID, puppetNick +}   - var event string - if strings.Contains(v.Text, "has set the subject to:") { - event = config.EventTopicChange - } +func (b *Bxmpp) buildPuppetJID(displayName, userID, protocol string) string { + if b.GetBool("UsePerProtocolJID") { + local := protocol + "-" + shortHash(userID) + return local + "@" + b.domain + } else { + local := shortHash(displayName) + return local + "@" + b.domain + } +}   - available, sok := b.avatarAvailability[v.Remote] - avatar := "" - if !sok { - b.Log.Debugf("Requesting avatar data") - b.avatarAvailability[v.Remote] = false - b.xc.AvatarRequestData(v.Remote) - } else if available { - avatar = getAvatar(b.avatarMap, v.Remote, b.General) - } +func shortHash(value string) string { + sum := sha1.Sum([]byte(value)) + return hex.EncodeToString(sum[:])[:12] +}   - rmsg := config.Message{ - Username: b.parseNick(v.Remote), - Text: v.Text, - Channel: b.parseChannel(v.Remote), - Account: b.Account, - Avatar: avatar, - UserID: v.Remote, - // Here the stanza-id has been set by the server and can be used to provide replies - // as explained in XEP-0461 https://xmpp.org/extensions/xep-0461.html#business-id - ID: v.StanzaID.ID, - Event: event, - } +func (b *Bxmpp) ensureMemberAffiliation(mucJID, puppetJID string) { + // horrible, but go-xmpp doesn't have muc#admin yet + iq := fmt.Sprintf("<iq type='set' from='%s' to='%s' id='mb-affil'><query xmlns='http://jabber.org/protocol/muc#admin'><item affiliation='member' jid='%s'/></query></iq>", b.botJID(), mucJID, puppetJID) + if err := b.component.SendRaw(iq); err != nil { + b.Log.WithError(err).Errorf("XMPP set affiliation failed: room=%s jid=%s", mucJID, puppetJID) + } +}   - // Check if we have an action event. - var ok bool - rmsg.Text, ok = b.replaceAction(rmsg.Text) - if ok { - rmsg.Event = config.EventUserAction - } +func (b *Bxmpp) joinMuc(mucJID, nick, fromJID string) error { + pres := stanza.NewPresence(stanza.Attrs{ + To: mucJID + "/" + nick, + From: fromJID, + })   - b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account) - b.Log.Debugf("<= Message is %#v", rmsg) - b.Remote <- rmsg - } - case xmpp.AvatarData: - b.handleDownloadAvatar(v) - b.avatarAvailability[v.From] = true - b.Log.Debugf("Avatar for %s is now available", v.From) - case xmpp.Presence: - // Do nothing. - } + mucExt := stanza.MucPresence{ + History: stanza.History{ + MaxStanzas: stanza.NewNullableInt(0), + },   } -} + pres.Extensions = append(pres.Extensions, mucExt)   -func (b *Bxmpp) replaceAction(text string) (string, bool) { - if strings.HasPrefix(text, "/me ") { - return strings.ReplaceAll(text, "/me ", ""), true + if err := b.component.Send(pres); err != nil { + b.Log.WithError(err).Errorf("XMPP join presence failed: to=%s from=%s", mucJID+"/"+nick, fromJID) + return err   } - return text, false + return nil  }   -// handleUploadFile handles native upload of files -func (b *Bxmpp) handleUploadFile(msg *config.Message) error { - var urlDesc string - - for _, file := range msg.Extra["file"] { - fileInfo := file.(config.FileInfo) - if fileInfo.Comment != "" { - msg.Text += fileInfo.Comment + ": " - } - if fileInfo.URL != "" { - msg.Text = fileInfo.URL - if fileInfo.Comment != "" { - msg.Text = fileInfo.Comment + ": " + fileInfo.URL - urlDesc = fileInfo.Comment - } - } - if _, err := b.xc.Send(xmpp.Chat{ - Type: "groupchat", - Remote: msg.Channel + "@" + b.GetString("Muc"), - Text: msg.Username + msg.Text, - }); err != nil { - return err - } +func (b *Bxmpp) sendMucMessage(mucJID, fromJID, body string) error { + b.Log.Debugf("XMPP send message: to=%s from=%s body_len=%d", mucJID, fromJID, len(body)) + msg := stanza.NewMessage(stanza.Attrs{ + Type: stanza.MessageTypeGroupchat, + To: mucJID, + From: fromJID, + }) + msg.Body = body   - if fileInfo.URL != "" { - if _, err := b.xc.SendOOB(xmpp.Chat{ - Type: "groupchat", - Remote: msg.Channel + "@" + b.GetString("Muc"), - Ooburl: fileInfo.URL, - Oobdesc: urlDesc, - }); err != nil { - b.Log.WithError(err).Warn("Failed to send share URL.") - } - } + if err := b.component.Send(msg); err != nil { + b.Log.WithError(err).Errorf("XMPP send failed: to=%s from=%s", mucJID, fromJID) + return err   }   return nil  }   -func (b *Bxmpp) parseNick(remote string) string { - s := strings.Split(remote, "@") - if len(s) > 1 { - s = strings.Split(s[1], "/") - if len(s) == 2 { - return s[1] // nick +func (b *Bxmpp) leaveAllRooms() { + b.mu.RLock() + defer b.mu.RUnlock() + + for _, room := range b.rooms { + pres := stanza.NewPresence(stanza.Attrs{ + Type: stanza.PresenceTypeUnavailable, + To: room.MucJID + "/" + room.BotNick, + From: b.botJID(), + }) + _ = b.component.Send(pres) + + for nick, jid := range room.PuppetNicks { + pres := stanza.NewPresence(stanza.Attrs{ + Type: stanza.PresenceTypeUnavailable, + To: room.MucJID + "/" + nick, + From: jid, + }) + _ = b.component.Send(pres)   }   } - return ""  }   -func (b *Bxmpp) parseChannel(remote string) string { - s := strings.Split(remote, "@") - if len(s) >= 2 { - return s[0] // channel - } - return "" +func (b *Bxmpp) handleComponentError(err error) { + b.Log.WithError(err).Error("XMPP component error") + _ = b.component.Disconnect() + b.setConnected(false) + b.signalReconnect()  }   -// skipMessage skips messages that need to be skipped -func (b *Bxmpp) skipMessage(message xmpp.Chat) bool { - // skip messages from ourselves - if b.parseNick(message.Remote) == b.GetString("Nick") { - return true +func (b *Bxmpp) signalReconnect() { + if b.reconnectCh == nil { + return   } - - // skip empty messages - if message.Text == "" { - return true + select { + case b.reconnectCh <- struct{}{}: + default:   } +}   - // skip subject messages - if strings.Contains(message.Text, "</subject>") { - return true +func (b *Bxmpp) manageConnection() { + bf := &backoff.Backoff{ + Min: time.Second, + Max: 5 * time.Minute, + Jitter: true,   }   - // do not show subjects on connect #732 - if strings.Contains(message.Text, "has set the subject to:") && time.Since(b.startTime) < time.Second*5 { - return true + for { + _, ok := <-b.reconnectCh + if !ok { + return + } + + for { + d := bf.Duration() + b.Log.Infof("Reconnecting in %s.", d) + time.Sleep(d) + + b.Log.Infof("Reconnecting now.") + if err := b.component.Resume(); err == nil { + b.setConnected(true) + bf.Reset() + + b.Remote <- config.Message{ + Username: "system", + Text: "rejoin", + Channel: "", + Account: b.Account, + Event: config.EventRejoinChannels, + } + break + } + b.Log.Warn("Failed to reconnect.") + }   } +}   - // skip delayed messages - return !message.Stamp.IsZero() && time.Since(message.Stamp).Minutes() > 5 +func parseRoomNick(from string) (string, string) { + parts := strings.Split(from, "/") + if len(parts) != 2 { + return "", "" + } + return parts[0], parts[1]  }    func (b *Bxmpp) setConnected(state bool) {   b.Lock()   b.connected = state - defer b.Unlock() + b.Unlock()  }    func (b *Bxmpp) Connected() bool { diff --git a/gateway/gateway.go b/gateway/gateway.go index 7ca84e4..57014ef 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -107 +107 @@ import (   "github.com/d5/tengo/v2"   "github.com/d5/tengo/v2/stdlib"   lru "github.com/hashicorp/golang-lru" - "github.com/kyokomi/emoji/v2" +// "github.com/kyokomi/emoji/v2"   "github.com/matterbridge-org/matterbridge/bridge"   "github.com/matterbridge-org/matterbridge/bridge/config"   "github.com/matterbridge-org/matterbridge/internal" @@ -4178 +4178 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {   }     // replace :emoji: to unicode - emoji.ReplacePadding = "" - msg.Text = emoji.Sprint(msg.Text) +// emoji.ReplacePadding = "" +// msg.Text = emoji.Sprint(msg.Text)     br := gw.Bridges[msg.Account]   // loop to replace messages diff --git a/go.mod b/go.mod index 1638863..a6f966f 100644 --- a/go.mod +++ b/go.mod @@ -387 +386 @@ require (   github.com/stretchr/testify v1.11.1   github.com/vincent-petithory/dataurl v1.0.0   github.com/writeas/go-strip-markdown v2.0.1+incompatible - github.com/xmppo/go-xmpp v0.3.1   github.com/yaegashi/msgraph.go v0.1.4   github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289   go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4 @@ -476 +467 @@ require (   golang.org/x/text v0.32.0   gomod.garykim.dev/nc-talk v0.3.0   google.golang.org/protobuf v1.36.10 + gosrc.io/xmpp v0.5.1   layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14   maunium.net/go/mautrix v0.26.0   modernc.org/sqlite v1.32.0 @@ -13522 +13529 @@ require (   go.uber.org/multierr v1.11.0 // indirect   golang.org/x/crypto v0.46.0 // indirect   golang.org/x/exp v0.0.0-20251113190631-e25ba8c21ef6 // indirect + golang.org/x/mod v0.32.0 // indirect   golang.org/x/net v0.48.0 // indirect + golang.org/x/sync v0.19.0 // indirect   golang.org/x/sys v0.39.0 // indirect   golang.org/x/term v0.38.0 // indirect   golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.41.0 // indirect + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect   google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect   google.golang.org/grpc v1.65.0 // indirect   gopkg.in/ini.v1 v1.67.0 // indirect   gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect   gopkg.in/yaml.v2 v2.4.0 // indirect   gopkg.in/yaml.v3 v3.0.1 // indirect + mellium.im/reader v0.1.0 // indirect + mellium.im/sasl v0.3.2 // indirect   modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect   modernc.org/libc v1.55.3 // indirect   modernc.org/mathutil v1.6.0 // indirect   modernc.org/memory v1.8.0 // indirect   modernc.org/strutil v1.2.0 // indirect   modernc.org/token v1.1.0 // indirect + nhooyr.io/websocket v1.6.5 // indirect   rsc.io/qr v0.2.0 // indirect  )   diff --git a/go.sum b/go.sum index 090927b..97fd014 100644 --- a/go.sum +++ b/go.sum @@ -226 +227 @@ github.com/SevereCloud/vksdk/v2 v2.17.0 h1:Wll63JSuBTdE0L7+V/PMn9PyhLrWSWIjX76Xp  github.com/SevereCloud/vksdk/v2 v2.17.0/go.mod h1:y3q3XAdqnQ2Wf0B+Wi7qNdqJc5ZZsz4ve+DoSQsrChk=  github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM=  github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/agnivade/wasmbrowsertest v0.3.1/go.mod h1:zQt6ZTdl338xxRaMW395qccVE2eQm0SjC/SDz0mPWQI=  github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=  github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=  github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= @@ -466 +4712 @@ github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp  github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=  github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=  github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= +github.com/chromedp/cdproto v0.0.0-20190614062957-d6d2f92b486d/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw= +github.com/chromedp/cdproto v0.0.0-20190621002710-8cbd498dd7a0/go.mod h1:S8mB5wY3vV+vRIzf39xDXsw3XKYewW9X6rW2aEmkrSw= +github.com/chromedp/cdproto v0.0.0-20190812224334-39ef923dcb8d/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0= +github.com/chromedp/cdproto v0.0.0-20190926234355-1b4886c6fad6/go.mod h1:0YChpVzuLJC5CPr+x3xkHN6Z8KOSXjNbL7qV8Wc4GW0= +github.com/chromedp/chromedp v0.3.1-0.20190619195644-fd957a4d2901/go.mod h1:mJdvfrVn594N9tfiPecUidF6W5jPRKHymqHfzbobPsM= +github.com/chromedp/chromedp v0.4.0/go.mod h1:DC3QUn4mJ24dwjcaGQLoZrhm4X/uPHZ6spDbS2uFhm4=  github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=  github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=  github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= @@ -638 +7010 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp  github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=  github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a h1:etIrTD8BQqzColk9nKRusM9um5+1q0iOEJLqfBMIK64=  github.com/dyatlov/go-opengraph/opengraph v0.0.0-20220524092352-606d7b1e5f8a/go.mod h1:emQhSYTXqB0xxjLITTw4EaWZ+8IIQYw+kx9GqNUKdLg= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=  github.com/elliotchance/orderedmap/v3 v3.1.0 h1:j4DJ5ObEmMBt/lcwIecKcoRxIQUEnw0L804lXYDt/pg=  github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCnGeKf0bTYCGTO4uhjSo= +github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=  github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=  github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=  github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= @@ -839 +9214 @@ github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev  github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=  github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=  github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-interpreter/wagon v0.5.1-0.20190713202023-55a163980b6c/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc= +github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=  github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=  github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=  github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=  github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=  github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=  github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= @@ -9612 +11014 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb  github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=  github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=  github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=  github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=  github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=  github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2 h1:yEt5djSYb4iNtmV9iJGVday+i4e9u6Mrn5iP64HH5QM=  github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA=  github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=  github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=  github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=  github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=  github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= @@ -1106 +1268 @@ github.com/google/gops v0.3.27 h1:BDdWfedShsBbeatZ820oA4DbVOC8yJ4NI8xAlDFWfgI=  github.com/google/gops v0.3.27/go.mod h1:lYqabmfnq4Q6UumWNx96Hjup5BDAVc8zmfIy0SkNCSk=  github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=  github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190908185732-236ed259b199/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=  github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=  github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=  github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1526 +1707 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS  github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=  github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=  github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=  github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=  github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=  github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -1626 +1819 @@ github.com/kettek/apng v0.0.0-20191108220231-414630eed80f/go.mod h1:x78/VRQYKuCf  github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=  github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=  github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/knq/sysutil v0.0.0-20181215143952-f05b59f0f307/go.mod h1:BjPj+aVjl9FW/cCGiF3nGh5v+9Gd3VCgBQbod/GlMaQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=  github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=  github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=  github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -1846 +20611 @@ github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm  github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=  github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=  github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190403194419-1ea4449da983/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190620125010-da37f6c1e481/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=  github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696 h1:pmPKkN3RJM9wVMZidR99epzK0+gatQiqVtvP1FacZcQ=  github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20211016222428-79310a412696/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=  github.com/matterbridge/gozulipbot v0.0.0-20211023205727-a19d6c1f3b75 h1:GslZKF7lW7oSisycGLpxPO+TnKJuA4VZuTWIfYZrClc= @@ -2026 +2297 @@ github.com/mattermost/logr/v2 v2.0.21 h1:CMHsP+nrbRlEC4g7BwOk1GAnMtHkniFhlSQPXy5  github.com/mattermost/logr/v2 v2.0.21/go.mod h1:kZkB/zqKL9e+RY5gB3vGpsyenC+TpuiOenjMkvJJbzc=  github.com/mattermost/mattermost/server/public v0.1.6 h1:k3upRnYfDY9CtVifQ+hQS6ZP4dGsU6I3oG1DCcBYy0c=  github.com/mattermost/mattermost/server/public v0.1.6/go.mod h1:Dm5uf3z8ckDOKYD1cbnb1Uqm/G9WYIaouSP/HnH+Rbs= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=  github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=  github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=  github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -2098 +23710 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb  github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=  github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=  github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=  github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=  github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=  github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=  github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=  github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -2548 +28410 @@ github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DV  github.com/olahol/melody v1.2.1 h1:xdwRkzHxf+B0w4TKbGpUSSkV516ZucQZJIWLztOWICQ=  github.com/olahol/melody v1.2.1/go.mod h1:GgkTl6Y7yWj/HtfD48Q5vLKPVoZOH+Qqgfa7CvJgJM4=  github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=  github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=  github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=  github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=  github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=  github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= @@ -2736 +3057 @@ github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490 h1:QTvNkZ5ylY0PGg  github.com/petermattis/goid v0.0.0-20250904145737-900bdf8bb490/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=  github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986 h1:jYi87L8j62qkXzaYHAQAhEapgukhenIMZRBKTNRLHJ4=  github.com/philhofer/fwd v1.1.3-0.20240612014219-fbbf4953d986/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=  github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=  github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=  github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -3376 +3708 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l  github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=  github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=  github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=  github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=  github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=  github.com/sizeofint/webpanimation v0.0.0-20210809145948-1d2b32119882 h1:A7o8tOERTtpD/poS+2VoassCjXpjHn916luXbf5QKD0= @@ -35416 +38919 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=  github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=  github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=  github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=  github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=  github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=  github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=  github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=  github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=  github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=  github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=  github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=  github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=  github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=  github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=  github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=  github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -3976 +4357 @@ github.com/tj/go-kinesis v0.0.0-20171128231115-08b17f58cb1b/go.mod h1:/yhzCV0xPf  github.com/tj/go-spin v1.1.0/go.mod h1:Mg1mzmePZm4dva8Qz60H2lHwmJ2loum4VIrLgVnKwh4=  github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=  github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=  github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=  github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=  github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= @@ -4286 +4677 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de  github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=  github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289 h1:dEdcEes8Aki8XrgZFyrZvtazFlW4U7eNvX9NuyFJAtQ=  github.com/zfjagann/golang-ring v0.0.0-20220330170733-19bcea1b6289/go.mod h1:0MsIttMJIF/8Y7x0XjonJP7K99t3sR6bjj4m5S4JmqU= +go.coder.com/go-tools v0.0.0-20190317003359-0c6a35b74a16/go.mod h1:iKV5yK9t+J5nG9O3uF6KYdPEz3dyfMyB15MN1rbQ8Qw=  go.mau.fi/libsignal v0.2.1 h1:vRZG4EzTn70XY6Oh/pVKrQGuMHBkAWlGRC22/85m9L0=  go.mau.fi/libsignal v0.2.1/go.mod h1:iVvjrHyfQqWajOUaMEsIfo3IqgVMrhWcPiiEzk7NgoU=  go.mau.fi/util v0.9.3 h1:aqNF8KDIN8bFpFbybSk+mEBil7IHeBwlujfyTnvP0uU= @@ -43510 +47513 @@ go.mau.fi/util v0.9.3/go.mod h1:krWWfBM1jWTb5f8NCa2TLqWMQuM81X7TGQjhMjBeXmQ=  go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4 h1:7hXdxCFs2Me4nypiWjdBNonaFrPfmYJvEtTOwLctSHU=  go.mau.fi/whatsmeow v0.0.0-20251116104239-3aca43070cd4/go.mod h1:5aYaEa3FF5e5XWsA8Xa80ttUXZvb6HyaBGgo2SfzUkE=  go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=  go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=  go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=  go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=  golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20180426230345-b49d69b5da94/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=  golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=  golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -45717 +50022 @@ golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys  golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=  golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=  golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=  golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=  golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=  golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=  golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=  golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=  golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=  golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=  golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181102091132-c10e9556a7bc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=  golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=  golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=  golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=  golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=  golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=  golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -5008 +54814 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h  golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=  golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=  golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=  golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=  golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190618155005-516e3c20635f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190927073244-c990c680b611/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=  golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=  golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=  golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -5336 +5877 @@ golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=  golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=  golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=  golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=  golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=  golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=  golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -5406 +5958 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm  golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=  golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=  golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=  golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=  golang.org/x/tools v0.0.0-20200529172331-a64b76657301/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=  golang.org/x/tools v0.0.0-20200731060945-b5fad4ed8dd6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -5486 +6058 @@ golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ  golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=  golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=  golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=  gomod.garykim.dev/nc-talk v0.3.0 h1:MZxLc/gX2/+bdOw4xt6pi+qQFUQld1woGfw1hEJ0fbM=  gomod.garykim.dev/nc-talk v0.3.0/go.mod h1:q/Adot/H7iqi+H4lANopV7/xcMf+sX3AZXUXqiITwok=  google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= @@ -57211 +63114 @@ google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=  google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=  google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=  google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=  gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=  gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=  gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=  gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=  gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=  gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=  gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=  gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -5926 +65410 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C  gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=  gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gosrc.io/xmpp v0.5.1 h1:Rgrm5s2rt+npGggJH3HakQxQXR8ZZz3+QRzakRQqaq4= +gosrc.io/xmpp v0.5.1/go.mod h1:L3NFMqYOxyLz3JGmgFyWf7r9htE91zVGiK40oW4RwdY= +gotest.tools v2.1.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/gotestsum v0.3.5/go.mod h1:Mnf3e5FUzXbkCfynWBGOwLssY7gTQgCHObK9tMpAriY=  grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=  honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=  honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -6016 +66714 @@ layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14 h1:wY8eeq7DpM5iAugNbFrvuhdtm  layeh.com/gumble v0.0.0-20221205141517-d1df60a3cc14/go.mod h1:tWPVA9ZAfImNwabjcd9uDE+Mtz0Hfs7a7G3vxrnrwyc=  maunium.net/go/mautrix v0.26.0 h1:valc2VmZF+oIY4bMq4Cd5H9cEKMRe8eP4FM7iiaYLxI=  maunium.net/go/mautrix v0.26.0/go.mod h1:NWMv+243NX/gDrLofJ2nNXJPrG8vzoM+WUCWph85S6Q= +mellium.im/reader v0.1.0 h1:UUEMev16gdvaxxZC7fC08j7IzuDKh310nB6BlwnxTww= +mellium.im/reader v0.1.0/go.mod h1:F+X5HXpkIfJ9EE1zHQG9lM/hO946iYAmU7xjg5dsQHI= +mellium.im/sasl v0.3.2 h1:PT6Xp7ccn9XaXAnJ03FcEjmAn7kK1x7aoXV6F+Vmrl0= +mellium.im/sasl v0.3.2/go.mod h1:NKXDi1zkr+BlMHLQjY3ofYuU4KSPFxknb8mfEu6SveY= +mellium.im/xmlstream v0.15.4 h1:gLKxcWl4rLMUpKgtzrTBvr4OexPeO/edYus+uK3F6ZI= +mellium.im/xmlstream v0.15.4/go.mod h1:yXaCW2++fmVO4L9piKVkyLDqnCmictVYF7FDQW8prb4= +mellium.im/xmpp v0.22.0 h1:UthQVSwEAr7SNrmyc90c2ykGpVHxjn/3yw8Ey4+Im8s= +mellium.im/xmpp v0.22.0/go.mod h1:WSjq12nhREFD88Vy/0WD6Q8inE8t6a8w7QjzwivWitw=  modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=  modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=  modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= @@ -6276 +7019 @@ modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=  modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=  modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=  modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +mvdan.cc/sh v2.6.4+incompatible/go.mod h1:IeeQbZq+x2SUGBensq/jge5lLQbS3XT2ktyp3wrt4x8= +nhooyr.io/websocket v1.6.5 h1:8TzpkldRfefda5JST+CnOH135bzVPz5uzfn/AF+gVKg= +nhooyr.io/websocket v1.6.5/go.mod h1:F259lAzPRAH0htX2y3ehpJe09ih1aSHN7udWki1defY=  rsc.io/qr v0.2.0 h1:6vBLea5/NRMVTz8V66gipeLycZMl/+UlFmk8DvqQ6WY=  rsc.io/qr v0.2.0/go.mod h1:IF+uZjkb9fqyeF/4tlBoynqmQxUoPfWEKh921coOuXs=  sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index 7653e14..93b128a 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -9506 +95018 @@ SyncTopic=false  # Default "<clipped message>"  MessageClipped="<clipped message>"   +#Disable quoted/reply messages +#OPTIONAL (default false) +QuoteDisable=false + +#Set the max. quoted length if 0 the whole message will be quoted +#OPTIONAL (default 0) +QuoteLengthLimit=0 + +#Format quoted/reply messages +#OPTIONAL (default "{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})") +QuoteFormat="{MESSAGE} (re @{QUOTENICK}: {QUOTEMESSAGE})" +  # Before clipping, try to split messages into at most this many parts. 0 is treated like 1.  # Be careful with large numbers, as this might cause flooding.  # Example: A maximum telegram message of 4096 bytes is received. This requires 3 Discord