You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

monitor.go 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Copyright (c) 2016-2017 Daniel Oaks <daniel@danieloaks.net>
  2. // released under the MIT license
  3. package irc
  4. import (
  5. "sync"
  6. "github.com/goshuirc/irc-go/ircmsg"
  7. )
  8. // MonitorManager keeps track of who's monitoring which nicks.
  9. type MonitorManager struct {
  10. sync.RWMutex // tier 2
  11. // client -> nicks it's watching
  12. watching map[*Client]map[string]bool
  13. // nick -> clients watching it
  14. watchedby map[string]map[*Client]bool
  15. // (all nicks must be normalized externally by casefolding)
  16. }
  17. // NewMonitorManager returns a new MonitorManager.
  18. func NewMonitorManager() *MonitorManager {
  19. mm := MonitorManager{
  20. watching: make(map[*Client]map[string]bool),
  21. watchedby: make(map[string]map[*Client]bool),
  22. }
  23. return &mm
  24. }
  25. // AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line.
  26. func (manager *MonitorManager) AlertAbout(client *Client, online bool) {
  27. cfnick := client.NickCasefolded()
  28. nick := client.Nick()
  29. var watchers []*Client
  30. // safely copy the list of clients watching our nick
  31. manager.RLock()
  32. for client := range manager.watchedby[cfnick] {
  33. watchers = append(watchers, client)
  34. }
  35. manager.RUnlock()
  36. command := RPL_MONOFFLINE
  37. if online {
  38. command = RPL_MONONLINE
  39. }
  40. for _, mClient := range watchers {
  41. mClient.Send(nil, client.server.name, command, mClient.Nick(), nick)
  42. }
  43. }
  44. // Add registers `client` to receive notifications about `nick`.
  45. func (manager *MonitorManager) Add(client *Client, nick string, limit int) error {
  46. manager.Lock()
  47. defer manager.Unlock()
  48. if manager.watching[client] == nil {
  49. manager.watching[client] = make(map[string]bool)
  50. }
  51. if manager.watchedby[nick] == nil {
  52. manager.watchedby[nick] = make(map[*Client]bool)
  53. }
  54. if len(manager.watching[client]) >= limit {
  55. return errMonitorLimitExceeded
  56. }
  57. manager.watching[client][nick] = true
  58. manager.watchedby[nick][client] = true
  59. return nil
  60. }
  61. // Remove unregisters `client` from receiving notifications about `nick`.
  62. func (manager *MonitorManager) Remove(client *Client, nick string) error {
  63. manager.Lock()
  64. defer manager.Unlock()
  65. // deleting from nil maps is fine
  66. delete(manager.watching[client], nick)
  67. delete(manager.watchedby[nick], client)
  68. return nil
  69. }
  70. // RemoveAll unregisters `client` from receiving notifications about *all* nicks.
  71. func (manager *MonitorManager) RemoveAll(client *Client) {
  72. manager.Lock()
  73. defer manager.Unlock()
  74. for nick := range manager.watching[client] {
  75. delete(manager.watchedby[nick], client)
  76. }
  77. delete(manager.watching, client)
  78. }
  79. // List lists all nicks that `client` is registered to receive notifications about.
  80. func (manager *MonitorManager) List(client *Client) (nicks []string) {
  81. manager.RLock()
  82. defer manager.RUnlock()
  83. for nick := range manager.watching[client] {
  84. nicks = append(nicks, nick)
  85. }
  86. return nicks
  87. }
  88. var (
  89. monitorSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.IrcMessage, rb *ResponseBuffer) bool{
  90. "-": monitorRemoveHandler,
  91. "+": monitorAddHandler,
  92. "c": monitorClearHandler,
  93. "l": monitorListHandler,
  94. "s": monitorStatusHandler,
  95. }
  96. )