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 (
"fmt"
"iter"
"maps"
"strconv"
"strings"
@ -1676,6 +1677,20 @@ func (channel *Channel) auditoriumFriends(client *Client) (friends []*Client) {
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
// (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

View file

@ -3129,11 +3129,13 @@ func metadataHandler(server *Server, client *Client, msg ircmsg.Message, rb *Res
targetChannel = server.channels.Get(target)
if targetChannel != nil {
targetObj = targetChannel
target = targetChannel.Name() // canonicalize case
}
} else {
targetClient = server.clients.Get(target)
if targetClient != nil {
targetObj = targetClient
target = targetClient.Nick() // canonicalize case
}
}
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)
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)
} else {
server.logger.Debug("metadata", "deleting", key, "on", target)
// TODO check success or failure here
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"))
}

View file

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