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.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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/ergochat/ergo/irc/caps"
  7. "github.com/ergochat/ergo/irc/utils"
  8. "github.com/ergochat/irc-go/ircmsg"
  9. )
  10. // MonitorManager keeps track of who's monitoring which nicks.
  11. type MonitorManager struct {
  12. sync.RWMutex // tier 2
  13. // client -> (casefolded nick it's watching -> uncasefolded nick)
  14. watching map[*Session]map[string]string
  15. // casefolded nick -> clients watching it
  16. watchedby map[string]utils.HashSet[*Session]
  17. }
  18. func (mm *MonitorManager) Initialize() {
  19. mm.watching = make(map[*Session]map[string]string)
  20. mm.watchedby = make(map[string]utils.HashSet[*Session])
  21. }
  22. // AddMonitors adds clients using extended-monitor monitoring `client`'s nick to the passed user set.
  23. func (manager *MonitorManager) AddMonitors(users utils.HashSet[*Session], cfnick string, capabs ...caps.Capability) {
  24. manager.RLock()
  25. defer manager.RUnlock()
  26. for session := range manager.watchedby[cfnick] {
  27. if session.capabilities.Has(caps.ExtendedMonitor) && session.capabilities.HasAll(capabs...) {
  28. users.Add(session)
  29. }
  30. }
  31. }
  32. // AlertAbout alerts everyone monitoring `client`'s nick that `client` is now {on,off}line.
  33. func (manager *MonitorManager) AlertAbout(nick, cfnick string, online bool) {
  34. var watchers []*Session
  35. // safely copy the list of clients watching our nick
  36. manager.RLock()
  37. for session := range manager.watchedby[cfnick] {
  38. watchers = append(watchers, session)
  39. }
  40. manager.RUnlock()
  41. command := RPL_MONOFFLINE
  42. if online {
  43. command = RPL_MONONLINE
  44. }
  45. for _, session := range watchers {
  46. session.Send(nil, session.client.server.name, command, session.client.Nick(), nick)
  47. }
  48. }
  49. // Add registers `client` to receive notifications about `nick`.
  50. func (manager *MonitorManager) Add(session *Session, nick string, limit int) error {
  51. cfnick, err := CasefoldName(nick)
  52. if err != nil {
  53. return err
  54. }
  55. manager.Lock()
  56. defer manager.Unlock()
  57. if manager.watching[session] == nil {
  58. manager.watching[session] = make(map[string]string)
  59. }
  60. if manager.watchedby[cfnick] == nil {
  61. manager.watchedby[cfnick] = make(utils.HashSet[*Session])
  62. }
  63. if len(manager.watching[session]) >= limit {
  64. return errMonitorLimitExceeded
  65. }
  66. manager.watching[session][cfnick] = nick
  67. manager.watchedby[cfnick].Add(session)
  68. return nil
  69. }
  70. // Remove unregisters `client` from receiving notifications about `nick`.
  71. func (manager *MonitorManager) Remove(session *Session, nick string) (err error) {
  72. cfnick, err := CasefoldName(nick)
  73. if err != nil {
  74. return
  75. }
  76. manager.Lock()
  77. defer manager.Unlock()
  78. delete(manager.watching[session], cfnick)
  79. manager.watchedby[cfnick].Remove(session)
  80. return nil
  81. }
  82. // RemoveAll unregisters `client` from receiving notifications about *all* nicks.
  83. func (manager *MonitorManager) RemoveAll(session *Session) {
  84. manager.Lock()
  85. defer manager.Unlock()
  86. for cfnick := range manager.watching[session] {
  87. manager.watchedby[cfnick].Remove(session)
  88. }
  89. delete(manager.watching, session)
  90. }
  91. // List lists all nicks that `client` is registered to receive notifications about.
  92. func (manager *MonitorManager) List(session *Session) (nicks []string) {
  93. manager.RLock()
  94. defer manager.RUnlock()
  95. for _, nick := range manager.watching[session] {
  96. nicks = append(nicks, nick)
  97. }
  98. return nicks
  99. }
  100. var (
  101. monitorSubcommands = map[string]func(server *Server, client *Client, msg ircmsg.Message, rb *ResponseBuffer) bool{
  102. "-": monitorRemoveHandler,
  103. "+": monitorAddHandler,
  104. "c": monitorClearHandler,
  105. "l": monitorListHandler,
  106. "s": monitorStatusHandler,
  107. }
  108. )