statestore.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. package appservice
  2. import (
  3. "maunium.net/go/gomatrix"
  4. "strings"
  5. "sync"
  6. "time"
  7. )
  8. type StateStore interface {
  9. IsRegistered(userID string) bool
  10. MarkRegistered(userID string)
  11. IsTyping(roomID, userID string) bool
  12. SetTyping(roomID, userID string, timeout int64)
  13. IsInRoom(roomID, userID string) bool
  14. SetMembership(roomID, userID, membership string)
  15. SetPowerLevels(roomID string, levels *gomatrix.PowerLevels)
  16. GetPowerLevels(roomID string) *gomatrix.PowerLevels
  17. GetPowerLevel(roomID, userID string) int
  18. GetPowerLevelRequirement(roomID string, eventType gomatrix.EventType, isState bool) int
  19. HasPowerLevel(roomID, userID string, eventType gomatrix.EventType, isState bool) bool
  20. }
  21. func (as *AppService) UpdateState(evt *gomatrix.Event) {
  22. switch evt.Type {
  23. case gomatrix.StateMember:
  24. as.StateStore.SetMembership(evt.RoomID, evt.GetStateKey(), evt.Content.Membership)
  25. }
  26. }
  27. type BasicStateStore struct {
  28. registrationsLock sync.RWMutex `json:"-"`
  29. Registrations map[string]bool `json:"registrations"`
  30. membershipsLock sync.RWMutex `json:"-"`
  31. Memberships map[string]map[string]string `json:"memberships"`
  32. powerLevelsLock sync.RWMutex `json:"-"`
  33. PowerLevels map[string]*gomatrix.PowerLevels `json:"power_levels"`
  34. Typing map[string]map[string]int64 `json:"-"`
  35. typingLock sync.RWMutex `json:"-"`
  36. }
  37. func NewBasicStateStore() StateStore {
  38. return &BasicStateStore{
  39. Registrations: make(map[string]bool),
  40. Memberships: make(map[string]map[string]string),
  41. PowerLevels: make(map[string]*gomatrix.PowerLevels),
  42. Typing: make(map[string]map[string]int64),
  43. }
  44. }
  45. func (store *BasicStateStore) IsRegistered(userID string) bool {
  46. store.registrationsLock.RLock()
  47. defer store.registrationsLock.RUnlock()
  48. registered, ok := store.Registrations[userID]
  49. return ok && registered
  50. }
  51. func (store *BasicStateStore) MarkRegistered(userID string) {
  52. store.registrationsLock.Lock()
  53. defer store.registrationsLock.Unlock()
  54. store.Registrations[userID] = true
  55. }
  56. func (store *BasicStateStore) IsTyping(roomID, userID string) bool {
  57. store.typingLock.RLock()
  58. defer store.typingLock.RUnlock()
  59. roomTyping, ok := store.Typing[roomID]
  60. if !ok {
  61. return false
  62. }
  63. typingEndsAt, _ := roomTyping[userID]
  64. return typingEndsAt >= time.Now().Unix()
  65. }
  66. func (store *BasicStateStore) SetTyping(roomID, userID string, timeout int64) {
  67. store.typingLock.Lock()
  68. defer store.typingLock.Unlock()
  69. roomTyping, ok := store.Typing[roomID]
  70. if !ok {
  71. if timeout >= 0 {
  72. roomTyping = map[string]int64{
  73. userID: time.Now().Unix() + timeout,
  74. }
  75. } else {
  76. roomTyping = make(map[string]int64)
  77. }
  78. } else {
  79. if timeout >= 0 {
  80. roomTyping[userID] = time.Now().Unix() + timeout
  81. } else {
  82. delete(roomTyping, userID)
  83. }
  84. }
  85. store.Typing[roomID] = roomTyping
  86. }
  87. func (store *BasicStateStore) GetRoomMemberships(roomID string) map[string]string {
  88. store.membershipsLock.RLock()
  89. memberships, ok := store.Memberships[roomID]
  90. store.membershipsLock.RUnlock()
  91. if !ok {
  92. memberships = make(map[string]string)
  93. store.membershipsLock.Lock()
  94. store.Memberships[roomID] = memberships
  95. store.membershipsLock.Unlock()
  96. }
  97. return memberships
  98. }
  99. func (store *BasicStateStore) GetMembership(roomID, userID string) string {
  100. store.membershipsLock.RLock()
  101. defer store.membershipsLock.RUnlock()
  102. memberships, ok := store.Memberships[roomID]
  103. if !ok {
  104. return "leave"
  105. }
  106. membership, ok := memberships[userID]
  107. if !ok {
  108. return "leave"
  109. }
  110. return membership
  111. }
  112. func (store *BasicStateStore) IsInRoom(roomID, userID string) bool {
  113. return store.GetMembership(roomID, userID) == "join"
  114. }
  115. func (store *BasicStateStore) SetMembership(roomID, userID, membership string) {
  116. store.membershipsLock.Lock()
  117. memberships, ok := store.Memberships[roomID]
  118. if !ok {
  119. memberships = map[string]string{
  120. userID: strings.ToLower(membership),
  121. }
  122. } else {
  123. memberships[userID] = strings.ToLower(membership)
  124. }
  125. store.Memberships[roomID] = memberships
  126. store.membershipsLock.Unlock()
  127. }
  128. func (store *BasicStateStore) SetPowerLevels(roomID string, levels *gomatrix.PowerLevels) {
  129. store.powerLevelsLock.Lock()
  130. store.PowerLevels[roomID] = levels
  131. store.powerLevelsLock.Unlock()
  132. }
  133. func (store *BasicStateStore) GetPowerLevels(roomID string) (levels *gomatrix.PowerLevels) {
  134. store.powerLevelsLock.RLock()
  135. levels, _ = store.PowerLevels[roomID]
  136. store.powerLevelsLock.RUnlock()
  137. return
  138. }
  139. func (store *BasicStateStore) GetPowerLevel(roomID, userID string) int {
  140. return store.GetPowerLevels(roomID).GetUserLevel(userID)
  141. }
  142. func (store *BasicStateStore) GetPowerLevelRequirement(roomID string, eventType gomatrix.EventType, isState bool) int {
  143. levels := store.GetPowerLevels(roomID)
  144. switch eventType {
  145. case "kick":
  146. return levels.Kick()
  147. case "invite":
  148. return levels.Invite()
  149. case "redact":
  150. return levels.Redact()
  151. }
  152. return levels.GetEventLevel(eventType, isState)
  153. }
  154. func (store *BasicStateStore) HasPowerLevel(roomID, userID string, eventType gomatrix.EventType, isState bool) bool {
  155. return store.GetPowerLevel(roomID, userID) >= store.GetPowerLevelRequirement(roomID, eventType, isState)
  156. }