refactor update broadcast

This commit is contained in:
Shivaram Lingamneni 2025-06-15 13:14:16 -04:00
parent e6aaaf1b88
commit 3966c17dec
3 changed files with 42 additions and 26 deletions

View file

@ -7,6 +7,7 @@ package irc
import ( import (
"fmt" "fmt"
"iter"
"maps" "maps"
"strconv" "strconv"
"strings" "strings"
@ -1676,6 +1677,20 @@ func (channel *Channel) auditoriumFriends(client *Client) (friends []*Client) {
return return
} }
func (channel *Channel) sessionsWithCap(capabs ...caps.Capability) iter.Seq[*Session] {
return func(yield func(*Session) bool) {
for _, member := range channel.Members() {
for _, sess := range member.Sessions() {
if sess.capabilities.HasAll(capabs...) {
if !yield(sess) {
return
}
}
}
}
}
}
// returns whether the client is visible to unprivileged users in the channel // returns whether the client is visible to unprivileged users in the channel
// (i.e., respecting auditorium mode). note that this assumes that the client // (i.e., respecting auditorium mode). note that this assumes that the client
// is a member; if the client is not, it may return true anyway // is a member; if the client is not, it may return true anyway

View file

@ -3129,11 +3129,13 @@ func metadataHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res
targetChannel = server.channels.Get(target) targetChannel = server.channels.Get(target)
if targetChannel != nil { if targetChannel != nil {
targetObj = targetChannel targetObj = targetChannel
target = targetChannel.Name() // canonicalize case
} }
} else { } else {
targetClient = server.clients.Get(target) targetClient = server.clients.Get(target)
if targetClient != nil { if targetClient != nil {
targetObj = targetClient targetObj = targetClient
target = targetClient.Nick() // canonicalize case
} }
} }
if targetObj == nil { if targetObj == nil {
@ -3180,13 +3182,14 @@ func metadataHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res
server.logger.Debug("metadata", "setting", key, value, "on", target) server.logger.Debug("metadata", "setting", key, value, "on", target)
targetObj.SetMetadata(key, value) targetObj.SetMetadata(key, value)
notifySubscribers(server, rb.session, target, key, value) notifySubscribers(server, rb.session, targetObj, target, key, value)
rb.Add(nil, server.name, RPL_KEYVALUE, client.Nick(), originalTarget, key, "*", value) rb.Add(nil, server.name, RPL_KEYVALUE, client.Nick(), originalTarget, key, "*", value)
} else { } else {
server.logger.Debug("metadata", "deleting", key, "on", target) server.logger.Debug("metadata", "deleting", key, "on", target)
// TODO check success or failure here
targetObj.DeleteMetadata(key) targetObj.DeleteMetadata(key)
notifySubscribers(server, rb.session, target, key, "") notifySubscribers(server, rb.session, targetObj, target, key, "")
rb.Add(nil, server.name, RPL_KEYNOTSET, client.Nick(), target, key, client.t("Key deleted")) rb.Add(nil, server.name, RPL_KEYNOTSET, client.Nick(), target, key, client.t("Key deleted"))
} }

View file

@ -2,12 +2,13 @@ package irc
import ( import (
"errors" "errors"
"iter"
"maps"
"regexp" "regexp"
"strings" "strings"
"github.com/ergochat/ergo/irc/caps" "github.com/ergochat/ergo/irc/caps"
"github.com/ergochat/ergo/irc/modes" "github.com/ergochat/ergo/irc/modes"
"github.com/ergochat/ergo/irc/utils"
) )
var ( var (
@ -24,34 +25,31 @@ type MetadataHaver = interface {
CountMetadata() int CountMetadata() int
} }
func notifySubscribers(server *Server, session *Session, target string, key string, value string) { func notifySubscribers(server *Server, session *Session, targetObj MetadataHaver, targetName, key, value string) {
var notify utils.HashSet[*Session] = make(utils.HashSet[*Session]) var recipientSessions iter.Seq[*Session]
targetChannel := server.channels.Get(target)
targetClient := server.clients.Get(target)
if targetClient != nil { switch target := targetObj.(type) {
notify = targetClient.FriendsMonitors(caps.Metadata) case *Client:
// notify clients about changes regarding themselves // TODO this case is expensive and might warrant rate-limiting
for _, s := range targetClient.Sessions() { friends := target.FriendsMonitors(caps.Metadata)
notify.Add(s) // broadcast metadata update to other connected sessions
} for _, s := range target.Sessions() {
} friends.Add(s)
if targetChannel != nil {
members := targetChannel.Members()
for _, m := range members {
for _, s := range m.Sessions() {
if s.capabilities.Has(caps.Metadata) {
notify.Add(s)
}
}
} }
recipientSessions = maps.Keys(friends)
case *Channel:
recipientSessions = target.sessionsWithCap(caps.Metadata)
default:
return // impossible
} }
broadcastMetadataUpdate(server, recipientSessions, session, targetName, key, value)
}
func broadcastMetadataUpdate(server *Server, sessions iter.Seq[*Session], originator *Session, target, key, value string) {
for s := range sessions {
// don't notify the session that made the change // don't notify the session that made the change
notify.Remove(session) if s == originator || !s.isSubscribedTo(key) {
for s := range notify {
if !s.isSubscribedTo(key) {
continue continue
} }