// Copyright (c) 2016-2017 Daniel Oaks // released under the MIT license package irc import ( "sync" "github.com/goshuirc/irc-go/ircmsg" ) // MonitorManager keeps track of who's monitoring which nicks. type MonitorManager struct { sync.RWMutex // tier 2 // client -> nicks it's watching watching map[*Client]map[string]bool // nick -> clients watching it watchedby map[string]map[*Client]bool // (all nicks must be normalized externally by casefolding) } // NewMonitorManager returns a new MonitorManager. func NewMonitorManager() *MonitorManager { mm := MonitorManager{ watching: make(map[*Client]map[string]bool), watchedby: make(map[string]map[*Client]bool), } return &mm } // AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line. func (manager *MonitorManager) AlertAbout(client *Client, online bool) { cfnick := client.NickCasefolded() nick := client.Nick() var watchers []*Client // safely copy the list of clients watching our nick manager.RLock() for client := range manager.watchedby[cfnick] { watchers = append(watchers, client) } manager.RUnlock() command := RPL_MONOFFLINE if online { command = RPL_MONONLINE } for _, mClient := range watchers { mClient.Send(nil, client.server.name, command, mClient.Nick(), nick) } } // Add registers `client` to receive notifications about `nick`. func (manager *MonitorManager) Add(client *Client, nick string, limit int) error { manager.Lock() defer manager.Unlock() if manager.watching[client] == nil { manager.watching[client] = make(map[string]bool) } if manager.watchedby[nick] == nil { manager.watchedby[nick] = make(map[*Client]bool) } if len(manager.watching[client]) >= limit { return errMonitorLimitExceeded } manager.watching[client][nick] = true manager.watchedby[nick][client] = true return nil } // Remove unregisters `client` from receiving notifications about `nick`. func (manager *MonitorManager) Remove(client *Client, nick string) error { manager.Lock() defer manager.Unlock() // deleting from nil maps is fine delete(manager.watching[client], nick) delete(manager.watchedby[nick], client) return nil } // RemoveAll unregisters `client` from receiving notifications about *all* nicks. func (manager *MonitorManager) RemoveAll(client *Client) { manager.Lock() defer manager.Unlock() for nick := range manager.watching[client] { delete(manager.watchedby[nick], client) } delete(manager.watching, client) } // List lists all nicks that `client` is registered to receive notifications about. func (manager *MonitorManager) List(client *Client) (nicks []string) { manager.RLock() defer manager.RUnlock() for nick := range manager.watching[client] { nicks = append(nicks, nick) } return nicks } var ( metadataSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool{ "-": monitorRemoveHandler, "+": monitorAddHandler, "c": monitorClearHandler, "l": monitorListHandler, "s": monitorStatusHandler, } )