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

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