commit 7cfe0d24345a3b7621d6959535abef6b44baab94
Author: rani <clagv.randomgames@gmail.com>
Date: Tue Feb 17 15:19:23 2026 +0000
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