3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-11-23 12:29:25 +01:00
ergo/irc/monitor.go
2019-05-12 20:57:53 -04:00

128 lines
3.4 KiB
Go

// Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
// 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)
}
func (mm *MonitorManager) Initialize() {
mm.watching = make(map[*Client]map[string]bool)
mm.watchedby = make(map[string]map[*Client]bool)
}
// 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
}
func (manager *MonitorManager) Resume(newClient, oldClient *Client) error {
manager.Lock()
defer manager.Unlock()
// newClient is now watching everyone oldClient was watching
oldTargets := manager.watching[oldClient]
delete(manager.watching, oldClient)
manager.watching[newClient] = oldTargets
// update watchedby as well
for watchedNick := range oldTargets {
delete(manager.watchedby[watchedNick], oldClient)
manager.watchedby[watchedNick][newClient] = true
}
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 (
monitorSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool{
"-": monitorRemoveHandler,
"+": monitorAddHandler,
"c": monitorClearHandler,
"l": monitorListHandler,
"s": monitorStatusHandler,
}
)