puppet.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. package bridge
  2. import (
  3. "fmt"
  4. "regexp"
  5. "sync"
  6. log "maunium.net/go/maulogger/v2"
  7. "maunium.net/go/mautrix/appservice"
  8. "maunium.net/go/mautrix/id"
  9. "go.mau.fi/mautrix-discord/database"
  10. )
  11. type Puppet struct {
  12. *database.Puppet
  13. bridge *Bridge
  14. log log.Logger
  15. MXID id.UserID
  16. customIntent *appservice.IntentAPI
  17. customUser *User
  18. syncLock sync.Mutex
  19. }
  20. var userIDRegex *regexp.Regexp
  21. func (b *Bridge) NewPuppet(dbPuppet *database.Puppet) *Puppet {
  22. return &Puppet{
  23. Puppet: dbPuppet,
  24. bridge: b,
  25. log: b.log.Sub(fmt.Sprintf("Puppet/%s", dbPuppet.ID)),
  26. MXID: b.FormatPuppetMXID(dbPuppet.ID),
  27. }
  28. }
  29. func (b *Bridge) ParsePuppetMXID(mxid id.UserID) (string, bool) {
  30. if userIDRegex == nil {
  31. pattern := fmt.Sprintf(
  32. "^@%s:%s$",
  33. b.Config.Bridge.FormatUsername("([0-9]+)"),
  34. b.Config.Homeserver.Domain,
  35. )
  36. userIDRegex = regexp.MustCompile(pattern)
  37. }
  38. match := userIDRegex.FindStringSubmatch(string(mxid))
  39. if len(match) == 2 {
  40. return match[1], true
  41. }
  42. return "", false
  43. }
  44. func (b *Bridge) GetPuppetByMXID(mxid id.UserID) *Puppet {
  45. id, ok := b.ParsePuppetMXID(mxid)
  46. if !ok {
  47. return nil
  48. }
  49. return b.GetPuppetByID(id)
  50. }
  51. func (b *Bridge) GetPuppetByID(id string) *Puppet {
  52. b.puppetsLock.Lock()
  53. defer b.puppetsLock.Unlock()
  54. puppet, ok := b.puppets[id]
  55. if !ok {
  56. dbPuppet := b.db.Puppet.Get(id)
  57. if dbPuppet == nil {
  58. dbPuppet = b.db.Puppet.New()
  59. dbPuppet.ID = id
  60. dbPuppet.Insert()
  61. }
  62. puppet = b.NewPuppet(dbPuppet)
  63. b.puppets[puppet.ID] = puppet
  64. }
  65. return puppet
  66. }
  67. func (b *Bridge) GetPuppetByCustomMXID(mxid id.UserID) *Puppet {
  68. b.puppetsLock.Lock()
  69. defer b.puppetsLock.Unlock()
  70. puppet, ok := b.puppetsByCustomMXID[mxid]
  71. if !ok {
  72. dbPuppet := b.db.Puppet.GetByCustomMXID(mxid)
  73. if dbPuppet == nil {
  74. return nil
  75. }
  76. puppet = b.NewPuppet(dbPuppet)
  77. b.puppets[puppet.ID] = puppet
  78. b.puppetsByCustomMXID[puppet.CustomMXID] = puppet
  79. }
  80. return puppet
  81. }
  82. func (b *Bridge) GetAllPuppetsWithCustomMXID() []*Puppet {
  83. return b.dbPuppetsToPuppets(b.db.Puppet.GetAllWithCustomMXID())
  84. }
  85. func (b *Bridge) GetAllPuppets() []*Puppet {
  86. return b.dbPuppetsToPuppets(b.db.Puppet.GetAll())
  87. }
  88. func (b *Bridge) dbPuppetsToPuppets(dbPuppets []*database.Puppet) []*Puppet {
  89. b.puppetsLock.Lock()
  90. defer b.puppetsLock.Unlock()
  91. output := make([]*Puppet, len(dbPuppets))
  92. for index, dbPuppet := range dbPuppets {
  93. if dbPuppet == nil {
  94. continue
  95. }
  96. puppet, ok := b.puppets[dbPuppet.ID]
  97. if !ok {
  98. puppet = b.NewPuppet(dbPuppet)
  99. b.puppets[dbPuppet.ID] = puppet
  100. if dbPuppet.CustomMXID != "" {
  101. b.puppetsByCustomMXID[dbPuppet.CustomMXID] = puppet
  102. }
  103. }
  104. output[index] = puppet
  105. }
  106. return output
  107. }
  108. func (b *Bridge) FormatPuppetMXID(did string) id.UserID {
  109. return id.NewUserID(
  110. b.Config.Bridge.FormatUsername(did),
  111. b.Config.Homeserver.Domain,
  112. )
  113. }
  114. func (p *Puppet) DefaultIntent() *appservice.IntentAPI {
  115. return p.bridge.as.Intent(p.MXID)
  116. }
  117. func (p *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI {
  118. if p.customIntent == nil {
  119. return p.DefaultIntent()
  120. }
  121. return p.customIntent
  122. }
  123. func (p *Puppet) CustomIntent() *appservice.IntentAPI {
  124. return p.customIntent
  125. }
  126. func (p *Puppet) updatePortalMeta(meta func(portal *Portal)) {
  127. for _, portal := range p.bridge.GetAllPortalsByID(p.ID) {
  128. // Get room create lock to prevent races between receiving contact info and room creation.
  129. portal.roomCreateLock.Lock()
  130. meta(portal)
  131. portal.roomCreateLock.Unlock()
  132. }
  133. }
  134. func (p *Puppet) updateName(source *User) bool {
  135. user, err := source.Session.User(p.ID)
  136. if err != nil {
  137. p.log.Warnln("failed to get user from id:", err)
  138. return false
  139. }
  140. newName := p.bridge.Config.Bridge.FormatDisplayname(user)
  141. if p.DisplayName != newName {
  142. err := p.DefaultIntent().SetDisplayName(newName)
  143. if err == nil {
  144. p.DisplayName = newName
  145. go p.updatePortalName()
  146. p.Update()
  147. } else {
  148. p.log.Warnln("failed to set display name:", err)
  149. }
  150. return true
  151. }
  152. return false
  153. }
  154. func (p *Puppet) updatePortalName() {
  155. p.updatePortalMeta(func(portal *Portal) {
  156. if portal.MXID != "" {
  157. _, err := portal.MainIntent().SetRoomName(portal.MXID, p.DisplayName)
  158. if err != nil {
  159. portal.log.Warnln("Failed to set name:", err)
  160. }
  161. }
  162. portal.Name = p.DisplayName
  163. portal.Update()
  164. })
  165. }
  166. func (p *Puppet) updateAvatar(source *User) bool {
  167. user, err := source.Session.User(p.ID)
  168. if err != nil {
  169. p.log.Warnln("Failed to get user:", err)
  170. return false
  171. }
  172. if p.Avatar == user.Avatar {
  173. return false
  174. }
  175. if user.Avatar == "" {
  176. p.log.Warnln("User does not have an avatar")
  177. return false
  178. }
  179. url, err := uploadAvatar(p.DefaultIntent(), user.AvatarURL(""))
  180. if err != nil {
  181. p.log.Warnln("Failed to upload user avatar:", err)
  182. return false
  183. }
  184. p.AvatarURL = url
  185. err = p.DefaultIntent().SetAvatarURL(p.AvatarURL)
  186. if err != nil {
  187. p.log.Warnln("Failed to set avatar:", err)
  188. }
  189. p.log.Debugln("Updated avatar", p.Avatar, "->", user.Avatar)
  190. p.Avatar = user.Avatar
  191. go p.updatePortalAvatar()
  192. return true
  193. }
  194. func (p *Puppet) updatePortalAvatar() {
  195. p.updatePortalMeta(func(portal *Portal) {
  196. if portal.MXID != "" {
  197. _, err := portal.MainIntent().SetRoomAvatar(portal.MXID, p.AvatarURL)
  198. if err != nil {
  199. portal.log.Warnln("Failed to set avatar:", err)
  200. }
  201. }
  202. portal.AvatarURL = p.AvatarURL
  203. portal.Avatar = p.Avatar
  204. portal.Update()
  205. })
  206. }
  207. func (p *Puppet) SyncContact(source *User) {
  208. p.syncLock.Lock()
  209. defer p.syncLock.Unlock()
  210. p.log.Debugln("syncing contact", p.DisplayName)
  211. err := p.DefaultIntent().EnsureRegistered()
  212. if err != nil {
  213. p.log.Errorln("Failed to ensure registered:", err)
  214. }
  215. update := false
  216. update = p.updateName(source) || update
  217. if p.Avatar == "" {
  218. update = p.updateAvatar(source) || update
  219. p.log.Debugln("update avatar returned", update)
  220. }
  221. if update {
  222. p.Update()
  223. }
  224. }