-
Notifications
You must be signed in to change notification settings - Fork 0
/
monitor.go
131 lines (110 loc) · 3.48 KB
/
monitor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// 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)
}
// 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
}
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,
}
)