diff --git a/gencapdefs.py b/gencapdefs.py index 48e734f2..b1d2ebf2 100644 --- a/gencapdefs.py +++ b/gencapdefs.py @@ -177,6 +177,12 @@ CAPDEFS = [ url="https://github.com/ircv3/ircv3-specifications/pull/435", standard="draft IRCv3", ), + CapDef( + identifier="ExtendedMonitor", + name="draft/extended-monitor", + url="https://github.com/ircv3/ircv3-specifications/pull/466", + standard="draft IRCv3", + ), ] def validate_defs(): diff --git a/irc/caps/defs.go b/irc/caps/defs.go index aa988812..7eea8867 100644 --- a/irc/caps/defs.go +++ b/irc/caps/defs.go @@ -7,7 +7,7 @@ package caps const ( // number of recognized capabilities: - numCapabs = 27 + numCapabs = 28 // length of the uint64 array that represents the bitset: bitsetLen = 1 ) @@ -53,6 +53,10 @@ const ( // https://github.com/ircv3/ircv3-specifications/pull/362 EventPlayback Capability = iota + // ExtendedMonitor is the draft IRCv3 capability named "draft/extended-monitor": + // https://github.com/ircv3/ircv3-specifications/pull/466 + ExtendedMonitor Capability = iota + // Languages is the proposed IRCv3 capability named "draft/languages": // https://gist.github.com/DanielOaks/8126122f74b26012a3de37db80e4e0c6 Languages Capability = iota @@ -135,6 +139,7 @@ var ( "draft/channel-rename", "draft/chathistory", "draft/event-playback", + "draft/extended-monitor", "draft/languages", "draft/multiline", "draft/relaymsg", diff --git a/irc/client.go b/irc/client.go index 39013e68..a703592d 100644 --- a/irc/client.go +++ b/irc/client.go @@ -1025,6 +1025,13 @@ func (client *Client) Friends(capabs ...caps.Capability) (result map[*Session]em return } +// Friends refers to clients that share a channel or extended-monitor this client. +func (client *Client) FriendsMonitors(capabs ...caps.Capability) (result map[*Session]empty) { + result = client.Friends(capabs...) + client.server.monitorManager.AddMonitors(result, client.nickCasefolded, capabs...) + return +} + // helper for Friends func addFriendsToSet(set map[*Session]empty, client *Client, capabs ...caps.Capability) { client.stateMutex.RLock() @@ -1049,7 +1056,7 @@ func (client *Client) SetOper(oper *Oper) { func (client *Client) sendChghost(oldNickMask string, vhost string) { details := client.Details() isBot := client.HasMode(modes.Bot) - for fClient := range client.Friends(caps.ChgHost) { + for fClient := range client.FriendsMonitors(caps.ChgHost) { fClient.sendFromClientInternal(false, time.Time{}, "", oldNickMask, details.accountName, isBot, nil, "CHGHOST", details.username, vhost) } } diff --git a/irc/handlers.go b/irc/handlers.go index b76d0e68..abbc5ef1 100644 --- a/irc/handlers.go +++ b/irc/handlers.go @@ -109,7 +109,7 @@ func sendSuccessfulAccountAuth(service *ircService, client *Client, rb *Response if client.Registered() { // dispatch account-notify - for friend := range client.Friends(caps.AccountNotify) { + for friend := range client.FriendsMonitors(caps.AccountNotify) { if friend != rb.session { friend.Send(nil, details.nickMask, "ACCOUNT", details.accountName) } @@ -369,7 +369,7 @@ func dispatchAwayNotify(client *Client, isAway bool, awayMessage string) { // dispatch away-notify details := client.Details() isBot := client.HasMode(modes.Bot) - for session := range client.Friends(caps.AwayNotify) { + for session := range client.FriendsMonitors(caps.AwayNotify) { if isAway { session.sendFromClientInternal(false, time.Time{}, "", details.nickMask, details.accountName, isBot, nil, "AWAY", awayMessage) } else { @@ -2875,7 +2875,7 @@ func setnameHandler(server *Server, client *Client, msg ircmsg.Message, rb *Resp // alert friends now := time.Now().UTC() - friends := client.Friends(caps.SetName) + friends := client.FriendsMonitors(caps.SetName) delete(friends, rb.session) isBot := client.HasMode(modes.Bot) for session := range friends { diff --git a/irc/monitor.go b/irc/monitor.go index aa3818b5..65abe9ad 100644 --- a/irc/monitor.go +++ b/irc/monitor.go @@ -6,6 +6,8 @@ package irc import ( "sync" + "github.com/ergochat/ergo/irc/caps" + "github.com/ergochat/irc-go/ircmsg" ) @@ -23,6 +25,17 @@ func (mm *MonitorManager) Initialize() { mm.watchedby = make(map[string]map[*Session]empty) } +// AddMonitors adds clients using extended-monitor monitoring `client`'s nick to the passed user set. +func (manager *MonitorManager) AddMonitors(users map[*Session]empty, cfnick string, capabs ...caps.Capability) { + manager.RLock() + defer manager.RUnlock() + for session := range manager.watchedby[cfnick] { + if session.capabilities.Has(caps.ExtendedMonitor) && session.capabilities.HasAll(capabs...) { + users[session] = empty{} + } + } +} + // AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line. func (manager *MonitorManager) AlertAbout(nick, cfnick string, online bool) { var watchers []*Session