Change activity to per-channel

This commit is contained in:
Kufat 2022-09-05 20:36:53 -04:00
parent 26bda54e16
commit edbf9c310b
5 changed files with 76 additions and 65 deletions

View File

@ -36,9 +36,6 @@ type Bdiscord struct {
userMemberMap map[string]*discordgo.Member userMemberMap map[string]*discordgo.Member
nickMemberMap map[string]*discordgo.Member nickMemberMap map[string]*discordgo.Member
noEmbedPartUrls bool
noEmbedUrls bool
// Webhook specific logic // Webhook specific logic
useAutoWebhooks bool useAutoWebhooks bool
transmitter *transmitter.Transmitter transmitter *transmitter.Transmitter
@ -60,12 +57,6 @@ func New(cfg *bridge.Config) bridge.Bridger {
b.nickMemberMap = make(map[string]*discordgo.Member) b.nickMemberMap = make(map[string]*discordgo.Member)
b.channelInfoMap = make(map[string]*config.ChannelInfo) 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") b.useAutoWebhooks = b.GetBool("AutoWebhooks")
if b.useAutoWebhooks { if b.useAutoWebhooks {
b.Log.Debug("Using automatic webhooks") b.Log.Debug("Using automatic webhooks")
@ -278,10 +269,6 @@ func (b *Bdiscord) Send(msg config.Message) (string, error) {
msg.Text = "_" + msg.Text + "_" msg.Text = "_" + msg.Text + "_"
} }
if b.noEmbedUrls || (msg.Event == config.EventJoinLeave && b.noEmbedPartUrls) {
disableEmbedUrls(&msg.Text)
}
// Handle prefix hint for unthreaded messages. // Handle prefix hint for unthreaded messages.
if msg.ParentNotFound() { if msg.ParentNotFound() {
msg.ParentID = "" msg.ParentID = ""

View File

@ -233,11 +233,6 @@ func (b *Bdiscord) splitURL(url string) (string, string, bool) {
return webhookURLSplit[webhookIdxID], webhookURLSplit[webhookIdxToken], true 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 { func enumerateUsernames(s string) []string {
onlySpace := true onlySpace := true
for _, r := range s { for _, r := range s {

View File

@ -114,7 +114,8 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
return return
} }
if event.Source.Name != b.Nick { 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 return
} }
partmsg := "" partmsg := ""
@ -160,11 +161,12 @@ func (b *Birc) handleNick(client *girc.Client, event girc.Event) {
if b.GetBool("nosendjoinpart") { if b.GetBool("nosendjoinpart") {
return return
} }
if isActive, activeTime := b.isUserActive(event.Source.Name); isActive { if activeChannels := b.getActiveChannels(event.Source.Name); len(activeChannels) > 0 {
for _, activityInfo := range activeChannels {
msg := config.Message{ msg := config.Message{
Username: "system", Username: "system",
Text: event.Source.Name + " changed nick to " + event.Params[0], Text: event.Source.Name + " changed nick to " + event.Params[0],
Channel: b.getPseudoChannel(), Channel: activityInfo.channel,
Account: b.Account, Account: b.Account,
Event: config.EventJoinLeave, Event: config.EventJoinLeave,
} }
@ -172,7 +174,8 @@ func (b *Birc) handleNick(client *girc.Client, event girc.Event) {
b.Remote <- msg b.Remote <- msg
if b.ActivityTimeout != 0 { if b.ActivityTimeout != 0 {
// This doesn't count as new activity, but it does preserve the value // This doesn't count as new activity, but it does preserve the value
b.markUserActive(event.Params[0], activeTime) b.markUserActive(event.Params[0], activityInfo.channel, activityInfo.activeTime)
}
} }
} }
} }
@ -229,9 +232,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
return return
} }
channel := strings.ToLower(event.Params[0])
rmsg := config.Message{ rmsg := config.Message{
Username: event.Source.Name, Username: event.Source.Name,
Channel: strings.ToLower(event.Params[0]), Channel: channel,
Account: b.Account, Account: b.Account,
UserID: event.Source.Ident + "@" + event.Source.Host, 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) b.Log.Debugf("<= Sending message from %s on %s to gateway", event.Params[0], b.Account)
if b.ActivityTimeout > 0 { if b.ActivityTimeout > 0 {
b.Log.Debugf("<= Updating last-active time for user %s", event.Source.Name) 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.Remote <- rmsg
b.cleanActiveMap() 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) { func (b *Birc) handleQuit(client *girc.Client, event girc.Event) {
if event.Source.Name == b.Nick && strings.Contains(event.Last(), "Ping timeout") { if event.Source.Name == b.Nick && strings.Contains(event.Last(), "Ping timeout") {
b.Log.Infof("%s reconnecting ..", b.Account) 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 return
} else if b.GetBool("nosendjoinpart") { } else if b.GetBool("nosendjoinpart") {
return 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 := "" verbosequit := ""
quitmsg := "" quitmsg := ""
nowTime := time.Now().Unix()
if len(event.Params) >= 1 { if len(event.Params) >= 1 {
quitmsg = " with message: " + event.Last() quitmsg = " with message: " + event.Last()
} }
if b.GetBool("verbosejoinpart") { if b.GetBool("verbosejoinpart") {
verbosequit = " (" + event.Source.Ident + "@" + event.Source.Host + ")" 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} 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.Log.Debugf("<= Message is %#v", msg)
b.Remote <- msg b.Remote <- msg
return }
}
} }
} }

View File

@ -28,7 +28,7 @@ type Birc struct {
i *girc.Client i *girc.Client
Nick string Nick string
names map[string][]string names map[string][]string
activeUsers map[string]int64 activeUsers map[string]map[string]int64
activeUsersLastCleaned int64 activeUsersLastCleaned int64
activeUsersMutex sync.RWMutex activeUsersMutex sync.RWMutex
connected chan error connected chan error
@ -41,12 +41,17 @@ type Birc struct {
*bridge.Config *bridge.Config
} }
type ActivityInfo struct {
channel string
activeTime int64
}
func New(cfg *bridge.Config) bridge.Bridger { func New(cfg *bridge.Config) bridge.Bridger {
b := &Birc{} b := &Birc{}
b.Config = cfg b.Config = cfg
b.Nick = b.GetString("Nick") b.Nick = b.GetString("Nick")
b.names = make(map[string][]string) 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.connected = make(chan error)
b.channels = make(map[string]bool) b.channels = make(map[string]bool)
@ -434,33 +439,45 @@ func (b *Birc) getTLSConfig() (*tls.Config, error) {
return tlsConfig, nil 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) b.Log.Debugf("checking activity for %s", nick)
if b.ActivityTimeout == 0 { if b.ActivityTimeout == 0 {
return true, 0 return true, 0
} }
b.activeUsersMutex.RLock() b.activeUsersMutex.RLock()
defer b.activeUsersMutex.RUnlock() defer b.activeUsersMutex.RUnlock()
if activeTime, ok := b.activeUsers[nick]; ok { if activeTime, ok := b.activeUsers[nick][channel]; ok {
now := time.Now().Unix() now := time.Now().Unix()
b.Log.Debugf("last activity for %s was %d, currently %d", nick, activeTime, now) b.Log.Debugf("last activity for %s was %d, currently %d", nick, activeTime, now)
if now < activeTime { if now < activeTime {
b.Log.Errorf("User %s has active time in the future: %d", nick, 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 true, now // err on the side of caution
} }
return (now - activeTime) < b.ActivityTimeout, activeTime return b.isActive(now, activeTime), activeTime
} }
return false, 0 return false, 0
} }
func (b *Birc) getPseudoChannel() string { func (b *Birc) getActiveChannels(nick string) []ActivityInfo {
for channelname, active := range b.channels { retval := make([]ActivityInfo, 0)
if active { if channels, found := b.activeUsers[nick]; found {
return channelname 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() { func (b *Birc) cleanActiveMap() {
@ -470,16 +487,26 @@ func (b *Birc) cleanActiveMap() {
} }
b.activeUsersMutex.Lock() b.activeUsersMutex.Lock()
defer b.activeUsersMutex.Unlock() defer b.activeUsersMutex.Unlock()
for nick, activeTime := range b.activeUsers { for nick, activeChannels := range b.activeUsers {
for channel, activeTime := range activeChannels {
if now-activeTime > b.ActivityTimeout { if now-activeTime > b.ActivityTimeout {
b.Log.Debugf("last activity for %s was %d, currently %d. Deleting.", nick, activeTime, now) 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) 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() b.activeUsersMutex.Lock()
defer b.activeUsersMutex.Unlock() 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
} }

View File

@ -947,15 +947,6 @@ IgnoreNicks=""
# IgnoreMessages="^~~ badword" # IgnoreMessages="^~~ badword"
IgnoreMessages="" 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. # ReplaceMessages replaces substrings of messages in outgoing messages.
# Regular expressions are supported. # Regular expressions are supported.
# #