From edbf9c310bfbce819b77270f73df46a4ea266ced Mon Sep 17 00:00:00 2001 From: Kufat Date: Mon, 5 Sep 2022 20:36:53 -0400 Subject: [PATCH] Change activity to per-channel --- bridge/discord/discord.go | 13 --------- bridge/discord/helpers.go | 5 ---- bridge/irc/handlers.go | 55 +++++++++++++++++++++--------------- bridge/irc/irc.go | 59 ++++++++++++++++++++++++++++----------- matterbridge.toml.sample | 9 ------ 5 files changed, 76 insertions(+), 65 deletions(-) diff --git a/bridge/discord/discord.go b/bridge/discord/discord.go index 14f6a98b..5ae6c572 100644 --- a/bridge/discord/discord.go +++ b/bridge/discord/discord.go @@ -36,9 +36,6 @@ type Bdiscord struct { userMemberMap map[string]*discordgo.Member nickMemberMap map[string]*discordgo.Member - noEmbedPartUrls bool - noEmbedUrls bool - // Webhook specific logic useAutoWebhooks bool transmitter *transmitter.Transmitter @@ -60,12 +57,6 @@ func New(cfg *bridge.Config) bridge.Bridger { b.nickMemberMap = make(map[string]*discordgo.Member) b.channelInfoMap = make(map[string]*config.ChannelInfo) - b.noEmbedPartUrls = b.GetBool(("NoEmbedPartUrls")) - b.noEmbedUrls = b.GetBool(("NoEmbedUrls")) - if b.noEmbedPartUrls && b.noEmbedUrls { - b.Log.Info("NoEmbedUrls supersedes NoEmbedPartUrls") - } - b.useAutoWebhooks = b.GetBool("AutoWebhooks") if b.useAutoWebhooks { b.Log.Debug("Using automatic webhooks") @@ -278,10 +269,6 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) { msg.Text = "_" + msg.Text + "_" } - if b.noEmbedUrls || (msg.Event == config.EventJoinLeave && b.noEmbedPartUrls) { - disableEmbedUrls(&msg.Text) - } - // Handle prefix hint for unthreaded messages. if msg.ParentNotFound() { msg.ParentID = "" diff --git a/bridge/discord/helpers.go b/bridge/discord/helpers.go index 2a62595a..2e18f46c 100644 --- a/bridge/discord/helpers.go +++ b/bridge/discord/helpers.go @@ -233,11 +233,6 @@ func (b *Bdiscord) splitURL(url string) (string, string, bool) { return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken], true } -func disableEmbedUrls(msg *string) { - regex := regexp.MustCompile(`(\w+://\S+)`) - *msg = regex.ReplaceAllString(*msg, "<$1>") -} - func enumerateUsernames(s string) []string { onlySpace := true for _, r := range s { diff --git a/bridge/irc/handlers.go b/bridge/irc/handlers.go index 80c793ee..efe44344 100644 --- a/bridge/irc/handlers.go +++ b/bridge/irc/handlers.go @@ -114,7 +114,8 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) { return } if event.Source.Name != b.Nick { - if isActive, _ := b.isUserActive(event.Source.Name); !isActive || b.GetBool("nosendjoinpart") { + if isActive, _ := b.isUserActive(event.Source.Name, channel); !isActive || + b.GetBool("nosendjoinpart") { return } partmsg := "" @@ -160,19 +161,21 @@ func (b *Birc) handleNick(client *girc.Client, event girc.Event) { if b.GetBool("nosendjoinpart") { return } - if isActive, activeTime := b.isUserActive(event.Source.Name); isActive { - msg := config.Message{ - Username: "system", - Text: event.Source.Name + " changed nick to " + event.Params[0], - Channel: b.getPseudoChannel(), - Account: b.Account, - Event: config.EventJoinLeave, - } - b.Log.Debugf("<= Message is %#v", msg) - b.Remote <- msg - if b.ActivityTimeout != 0 { - // This doesn't count as new activity, but it does preserve the value - b.markUserActive(event.Params[0], activeTime) + if activeChannels := b.getActiveChannels(event.Source.Name); len(activeChannels) > 0 { + for _, activityInfo := range activeChannels { + msg := config.Message{ + Username: "system", + Text: event.Source.Name + " changed nick to " + event.Params[0], + Channel: activityInfo.channel, + Account: b.Account, + Event: config.EventJoinLeave, + } + b.Log.Debugf("<= Message is %#v", msg) + b.Remote <- msg + if b.ActivityTimeout != 0 { + // This doesn't count as new activity, but it does preserve the value + b.markUserActive(event.Params[0], activityInfo.channel, activityInfo.activeTime) + } } } } @@ -229,9 +232,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { return } + channel := strings.ToLower(event.Params[0]) rmsg := config.Message{ Username: event.Source.Name, - Channel: strings.ToLower(event.Params[0]), + Channel: channel, Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host, } @@ -284,7 +288,7 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { b.Log.Debugf("<= Sending message from %s on %s to gateway", event.Params[0], b.Account) if b.ActivityTimeout > 0 { b.Log.Debugf("<= Updating last-active time for user %s", event.Source.Name) - b.markUserActive(event.Source.Name, time.Now().Unix()) + b.markUserActive(event.Source.Name, channel, time.Now().Unix()) } b.Remote <- rmsg b.cleanActiveMap() @@ -293,23 +297,30 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) { func (b *Birc) handleQuit(client *girc.Client, event girc.Event) { if event.Source.Name == b.Nick && strings.Contains(event.Last(), "Ping timeout") { b.Log.Infof("%s reconnecting ..", b.Account) - b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: b.getPseudoChannel(), Account: b.Account, Event: config.EventFailure} + for mychan := range b.channels { + b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: mychan, Account: b.Account, Event: config.EventFailure} + } return } else if b.GetBool("nosendjoinpart") { return - } else if isActive, _ := b.isUserActive(event.Source.Name); isActive || isKill(event.Last()) { + } else if activeChannels, found := b.activeUsers[event.Source.Name]; found { + userWasKilled := isKill(event.Last()) verbosequit := "" quitmsg := "" + nowTime := time.Now().Unix() if len(event.Params) >= 1 { quitmsg = " with message: " + event.Last() } if b.GetBool("verbosejoinpart") { verbosequit = " (" + event.Source.Ident + "@" + event.Source.Host + ")" } - msg := config.Message{Username: "system", Text: event.Source.Name + verbosequit + " quit" + quitmsg, Channel: b.getPseudoChannel(), Account: b.Account, Event: config.EventJoinLeave} - b.Log.Debugf("<= Message is %#v", msg) - b.Remote <- msg - return + for channel, activeTime := range activeChannels { + if userWasKilled || b.isActive(activeTime, nowTime) { + msg := config.Message{Username: "system", Text: event.Source.Name + verbosequit + " quit" + quitmsg, Channel: channel, Account: b.Account, Event: config.EventJoinLeave} + b.Log.Debugf("<= Message is %#v", msg) + b.Remote <- msg + } + } } } diff --git a/bridge/irc/irc.go b/bridge/irc/irc.go index 35c9f5f1..14a62c79 100644 --- a/bridge/irc/irc.go +++ b/bridge/irc/irc.go @@ -28,7 +28,7 @@ type Birc struct { i *girc.Client Nick string names map[string][]string - activeUsers map[string]int64 + activeUsers map[string]map[string]int64 activeUsersLastCleaned int64 activeUsersMutex sync.RWMutex connected chan error @@ -41,12 +41,17 @@ type Birc struct { *bridge.Config } +type ActivityInfo struct { + channel string + activeTime int64 +} + func New(cfg *bridge.Config) bridge.Bridger { b := &Birc{} b.Config = cfg b.Nick = b.GetString("Nick") b.names = make(map[string][]string) - b.activeUsers = make(map[string]int64) + b.activeUsers = make(map[string]map[string]int64) b.connected = make(chan error) b.channels = make(map[string]bool) @@ -434,33 +439,45 @@ func (b *Birc) getTLSConfig() (*tls.Config, error) { return tlsConfig, nil } -func (b *Birc) isUserActive(nick string) (bool, int64) { +func (b *Birc) isActive(activityTime int64, nowTime int64) bool { + return (nowTime - activityTime) < b.ActivityTimeout +} + +func (b *Birc) isUserActive(nick string, channel string) (bool, int64) { b.Log.Debugf("checking activity for %s", nick) if b.ActivityTimeout == 0 { return true, 0 } b.activeUsersMutex.RLock() defer b.activeUsersMutex.RUnlock() - if activeTime, ok := b.activeUsers[nick]; ok { + if activeTime, ok := b.activeUsers[nick][channel]; ok { now := time.Now().Unix() b.Log.Debugf("last activity for %s was %d, currently %d", nick, activeTime, now) if now < activeTime { b.Log.Errorf("User %s has active time in the future: %d", nick, activeTime) return true, now // err on the side of caution } - return (now - activeTime) < b.ActivityTimeout, activeTime + return b.isActive(now, activeTime), activeTime } return false, 0 } -func (b *Birc) getPseudoChannel() string { - for channelname, active := range b.channels { - if active { - return channelname +func (b *Birc) getActiveChannels(nick string) []ActivityInfo { + retval := make([]ActivityInfo, 0) + if channels, found := b.activeUsers[nick]; found { + now := time.Now().Unix() + for channel, activeTime := range channels { + if now < activeTime { + b.Log.Errorf("User %s has active time for channel %s in the future: %d", + nick, + channel, + activeTime) + } else if (now - activeTime) < b.ActivityTimeout { + retval = append(retval, ActivityInfo{channel, activeTime}) + } } } - b.Log.Warningf("Bot not active in any channels!") - return "" + return retval } func (b *Birc) cleanActiveMap() { @@ -470,16 +487,26 @@ func (b *Birc) cleanActiveMap() { } b.activeUsersMutex.Lock() defer b.activeUsersMutex.Unlock() - for nick, activeTime := range b.activeUsers { - if now-activeTime > b.ActivityTimeout { - b.Log.Debugf("last activity for %s was %d, currently %d. Deleting.", nick, activeTime, now) + for nick, activeChannels := range b.activeUsers { + for channel, activeTime := range activeChannels { + if now-activeTime > b.ActivityTimeout { + b.Log.Debugf("last activity for %s was %d, currently %d. Deleting.", nick, activeTime, now) + delete(activeChannels, channel) + } + } + if 0 == len(activeChannels) { delete(b.activeUsers, nick) } } } -func (b *Birc) markUserActive(nick string, activeTime int64) { +func (b *Birc) markUserActive(nick string, channel string, activeTime int64) { b.activeUsersMutex.Lock() defer b.activeUsersMutex.Unlock() - b.activeUsers[nick] = activeTime + nickActivity, found := b.activeUsers[nick] + if !found { + b.activeUsers[nick] = make(map[string]int64) + nickActivity = b.activeUsers[nick] + } + nickActivity[channel] = activeTime } diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index 2cf139fd..0d7e78a1 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -947,15 +947,6 @@ IgnoreNicks="" # IgnoreMessages="^~~ badword" IgnoreMessages="" -# Prevent URL embeds by encasing URLs in <> angle brackets. -# Useful if trolls are a problem on the other end of your bridge. -NoEmbedUrls=false - -# Prevent URL embeds in part/quit messages by encasing URLs in <> angle brackets. -# Useful if trolls spam in their quit messages or you're tired of seeing embeds for -# IRC client homepages. -NoEmbedPartUrls=false - # ReplaceMessages replaces substrings of messages in outgoing messages. # Regular expressions are supported. #