user.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825
  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "strings"
  7. "sync"
  8. "github.com/bwmarrin/discordgo"
  9. log "maunium.net/go/maulogger/v2"
  10. "maunium.net/go/mautrix"
  11. "maunium.net/go/mautrix/appservice"
  12. "maunium.net/go/mautrix/bridge"
  13. "maunium.net/go/mautrix/bridge/bridgeconfig"
  14. "maunium.net/go/mautrix/event"
  15. "maunium.net/go/mautrix/id"
  16. "go.mau.fi/mautrix-discord/database"
  17. )
  18. var (
  19. ErrNotConnected = errors.New("not connected")
  20. ErrNotLoggedIn = errors.New("not logged in")
  21. )
  22. type User struct {
  23. *database.User
  24. sync.Mutex
  25. bridge *DiscordBridge
  26. log log.Logger
  27. PermissionLevel bridgeconfig.PermissionLevel
  28. guilds map[string]*database.Guild
  29. guildsLock sync.Mutex
  30. Session *discordgo.Session
  31. }
  32. func (user *User) GetPermissionLevel() bridgeconfig.PermissionLevel {
  33. return user.PermissionLevel
  34. }
  35. func (user *User) GetManagementRoomID() id.RoomID {
  36. return user.ManagementRoom
  37. }
  38. func (user *User) GetMXID() id.UserID {
  39. return user.MXID
  40. }
  41. func (user *User) GetCommandState() map[string]interface{} {
  42. return nil
  43. }
  44. func (user *User) GetIDoublePuppet() bridge.DoublePuppet {
  45. p := user.bridge.GetPuppetByCustomMXID(user.MXID)
  46. if p == nil || p.CustomIntent() == nil {
  47. return nil
  48. }
  49. return p
  50. }
  51. func (user *User) GetIGhost() bridge.Ghost {
  52. if user.ID == "" {
  53. return nil
  54. }
  55. p := user.bridge.GetPuppetByID(user.ID)
  56. if p == nil {
  57. return nil
  58. }
  59. return p
  60. }
  61. var _ bridge.User = (*User)(nil)
  62. // this assume you are holding the guilds lock!!!
  63. func (user *User) loadGuilds() {
  64. user.guilds = map[string]*database.Guild{}
  65. for _, guild := range user.bridge.DB.Guild.GetAll(user.ID) {
  66. user.guilds[guild.GuildID] = guild
  67. }
  68. }
  69. func (br *DiscordBridge) loadUser(dbUser *database.User, mxid *id.UserID) *User {
  70. // If we weren't passed in a user we attempt to create one if we were given
  71. // a matrix id.
  72. if dbUser == nil {
  73. if mxid == nil {
  74. return nil
  75. }
  76. dbUser = br.DB.User.New()
  77. dbUser.MXID = *mxid
  78. dbUser.Insert()
  79. }
  80. user := br.NewUser(dbUser)
  81. // We assume the usersLock was acquired by our caller.
  82. br.usersByMXID[user.MXID] = user
  83. if user.ID != "" {
  84. br.usersByID[user.ID] = user
  85. }
  86. if user.ManagementRoom != "" {
  87. // Lock the management rooms for our update
  88. br.managementRoomsLock.Lock()
  89. br.managementRooms[user.ManagementRoom] = user
  90. br.managementRoomsLock.Unlock()
  91. }
  92. // Load our guilds state from the database and turn it into a map
  93. user.guildsLock.Lock()
  94. user.loadGuilds()
  95. user.guildsLock.Unlock()
  96. return user
  97. }
  98. func (br *DiscordBridge) GetUserByMXID(userID id.UserID) *User {
  99. // TODO: check if puppet
  100. br.usersLock.Lock()
  101. defer br.usersLock.Unlock()
  102. user, ok := br.usersByMXID[userID]
  103. if !ok {
  104. return br.loadUser(br.DB.User.GetByMXID(userID), &userID)
  105. }
  106. return user
  107. }
  108. func (br *DiscordBridge) GetUserByID(id string) *User {
  109. br.usersLock.Lock()
  110. defer br.usersLock.Unlock()
  111. user, ok := br.usersByID[id]
  112. if !ok {
  113. return br.loadUser(br.DB.User.GetByID(id), nil)
  114. }
  115. return user
  116. }
  117. func (br *DiscordBridge) NewUser(dbUser *database.User) *User {
  118. user := &User{
  119. User: dbUser,
  120. bridge: br,
  121. log: br.Log.Sub("User").Sub(string(dbUser.MXID)),
  122. guilds: map[string]*database.Guild{},
  123. }
  124. user.PermissionLevel = br.Config.Bridge.Permissions.Get(user.MXID)
  125. return user
  126. }
  127. func (br *DiscordBridge) getAllUsers() []*User {
  128. br.usersLock.Lock()
  129. defer br.usersLock.Unlock()
  130. dbUsers := br.DB.User.GetAll()
  131. users := make([]*User, len(dbUsers))
  132. for idx, dbUser := range dbUsers {
  133. user, ok := br.usersByMXID[dbUser.MXID]
  134. if !ok {
  135. user = br.loadUser(dbUser, nil)
  136. }
  137. users[idx] = user
  138. }
  139. return users
  140. }
  141. func (br *DiscordBridge) startUsers() {
  142. br.Log.Debugln("Starting users")
  143. for _, u := range br.getAllUsers() {
  144. go func(user *User) {
  145. err := user.Connect()
  146. if err != nil {
  147. user.log.Errorfln("Error connecting: %v", err)
  148. }
  149. }(u)
  150. }
  151. br.Log.Debugln("Starting custom puppets")
  152. for _, customPuppet := range br.GetAllPuppetsWithCustomMXID() {
  153. go func(puppet *Puppet) {
  154. br.Log.Debugln("Starting custom puppet", puppet.CustomMXID)
  155. if err := puppet.StartCustomMXID(true); err != nil {
  156. puppet.log.Errorln("Failed to start custom puppet:", err)
  157. }
  158. }(customPuppet)
  159. }
  160. }
  161. func (user *User) SetManagementRoom(roomID id.RoomID) {
  162. user.bridge.managementRoomsLock.Lock()
  163. defer user.bridge.managementRoomsLock.Unlock()
  164. existing, ok := user.bridge.managementRooms[roomID]
  165. if ok {
  166. // If there's a user already assigned to this management room, clear it
  167. // out.
  168. // I think this is due a name change or something? I dunno, leaving it
  169. // for now.
  170. existing.ManagementRoom = ""
  171. existing.Update()
  172. }
  173. user.ManagementRoom = roomID
  174. user.bridge.managementRooms[user.ManagementRoom] = user
  175. user.Update()
  176. }
  177. func (user *User) tryAutomaticDoublePuppeting() {
  178. user.Lock()
  179. defer user.Unlock()
  180. if !user.bridge.Config.CanAutoDoublePuppet(user.MXID) {
  181. return
  182. }
  183. user.log.Debugln("Checking if double puppeting needs to be enabled")
  184. puppet := user.bridge.GetPuppetByID(user.ID)
  185. if puppet.CustomMXID != "" {
  186. user.log.Debugln("User already has double-puppeting enabled")
  187. return
  188. }
  189. accessToken, err := puppet.loginWithSharedSecret(user.MXID)
  190. if err != nil {
  191. user.log.Warnln("Failed to login with shared secret:", err)
  192. return
  193. }
  194. err = puppet.SwitchCustomMXID(accessToken, user.MXID)
  195. if err != nil {
  196. puppet.log.Warnln("Failed to switch to auto-logined custom puppet:", err)
  197. return
  198. }
  199. user.log.Infoln("Successfully automatically enabled custom puppet")
  200. }
  201. func (user *User) syncChatDoublePuppetDetails(portal *Portal, justCreated bool) {
  202. doublePuppet := portal.bridge.GetPuppetByCustomMXID(user.MXID)
  203. if doublePuppet == nil {
  204. return
  205. }
  206. if doublePuppet == nil || doublePuppet.CustomIntent() == nil || portal.MXID == "" {
  207. return
  208. }
  209. // TODO sync mute status
  210. }
  211. func (user *User) Login(token string) error {
  212. user.Token = token
  213. user.Update()
  214. return user.Connect()
  215. }
  216. func (user *User) IsLoggedIn() bool {
  217. user.Lock()
  218. defer user.Unlock()
  219. return user.Token != ""
  220. }
  221. func (user *User) Logout() error {
  222. user.Lock()
  223. defer user.Unlock()
  224. if user.Session == nil {
  225. return ErrNotLoggedIn
  226. }
  227. puppet := user.bridge.GetPuppetByID(user.ID)
  228. if puppet.CustomMXID != "" {
  229. err := puppet.SwitchCustomMXID("", "")
  230. if err != nil {
  231. user.log.Warnln("Failed to logout-matrix while logging out of Discord:", err)
  232. }
  233. }
  234. if err := user.Session.Close(); err != nil {
  235. return err
  236. }
  237. user.Session = nil
  238. user.Token = ""
  239. user.Update()
  240. return nil
  241. }
  242. func (user *User) Connected() bool {
  243. user.Lock()
  244. defer user.Unlock()
  245. return user.Session != nil
  246. }
  247. func (user *User) Connect() error {
  248. user.Lock()
  249. defer user.Unlock()
  250. if user.Token == "" {
  251. return ErrNotLoggedIn
  252. }
  253. user.log.Debugln("connecting to discord")
  254. session, err := discordgo.New(user.Token)
  255. if err != nil {
  256. return err
  257. }
  258. user.Session = session
  259. // Add our event handlers
  260. user.Session.AddHandler(user.readyHandler)
  261. user.Session.AddHandler(user.connectedHandler)
  262. user.Session.AddHandler(user.disconnectedHandler)
  263. user.Session.AddHandler(user.guildCreateHandler)
  264. user.Session.AddHandler(user.guildDeleteHandler)
  265. user.Session.AddHandler(user.guildUpdateHandler)
  266. user.Session.AddHandler(user.channelCreateHandler)
  267. user.Session.AddHandler(user.channelDeleteHandler)
  268. user.Session.AddHandler(user.channelPinsUpdateHandler)
  269. user.Session.AddHandler(user.channelUpdateHandler)
  270. user.Session.AddHandler(user.messageCreateHandler)
  271. user.Session.AddHandler(user.messageDeleteHandler)
  272. user.Session.AddHandler(user.messageUpdateHandler)
  273. user.Session.AddHandler(user.reactionAddHandler)
  274. user.Session.AddHandler(user.reactionRemoveHandler)
  275. user.Session.Identify.Presence.Status = "online"
  276. return user.Session.Open()
  277. }
  278. func (user *User) Disconnect() error {
  279. user.Lock()
  280. defer user.Unlock()
  281. if user.Session == nil {
  282. return ErrNotConnected
  283. }
  284. if err := user.Session.Close(); err != nil {
  285. return err
  286. }
  287. user.Session = nil
  288. return nil
  289. }
  290. func (user *User) bridgeMessage(guildID string) bool {
  291. // Non guild message always get bridged.
  292. if guildID == "" {
  293. return true
  294. }
  295. user.guildsLock.Lock()
  296. defer user.guildsLock.Unlock()
  297. if guild, found := user.guilds[guildID]; found {
  298. if guild.Bridge {
  299. return true
  300. }
  301. }
  302. user.log.Debugfln("ignoring message for non-bridged guild %s-%s", user.ID, guildID)
  303. return false
  304. }
  305. func (user *User) readyHandler(s *discordgo.Session, r *discordgo.Ready) {
  306. user.log.Debugln("discord connection ready")
  307. // Update our user fields
  308. user.ID = r.User.ID
  309. // Update our guild map to match watch discord thinks we're in. This is the
  310. // only time we can get the full guild map as discordgo doesn't make it
  311. // available to us later. Also, discord might not give us the full guild
  312. // information here, so we use this to remove guilds the user left and only
  313. // add guilds whose full information we have. The are told about the
  314. // "unavailable" guilds later via the GuildCreate handler.
  315. user.guildsLock.Lock()
  316. defer user.guildsLock.Unlock()
  317. // build a list of the current guilds we're in so we can prune the old ones
  318. current := []string{}
  319. user.log.Debugln("database guild count", len(user.guilds))
  320. user.log.Debugln("discord guild count", len(r.Guilds))
  321. for _, guild := range r.Guilds {
  322. current = append(current, guild.ID)
  323. // If we already know about this guild, make sure we reset it's bridge
  324. // status.
  325. if val, found := user.guilds[guild.ID]; found {
  326. bridge := val.Bridge
  327. user.guilds[guild.ID].Bridge = bridge
  328. // Update the name if the guild is available
  329. if !guild.Unavailable {
  330. user.guilds[guild.ID].GuildName = guild.Name
  331. }
  332. val.Upsert()
  333. } else {
  334. g := user.bridge.DB.Guild.New()
  335. g.DiscordID = user.ID
  336. g.GuildID = guild.ID
  337. user.guilds[guild.ID] = g
  338. if !guild.Unavailable {
  339. g.GuildName = guild.Name
  340. }
  341. g.Upsert()
  342. }
  343. }
  344. // Sync the guilds to the database.
  345. user.bridge.DB.Guild.Prune(user.ID, current)
  346. // Finally reload from the database since it purged servers we're not in
  347. // anymore.
  348. user.loadGuilds()
  349. user.log.Debugln("updated database guild count", len(user.guilds))
  350. user.Update()
  351. }
  352. func (user *User) connectedHandler(s *discordgo.Session, c *discordgo.Connect) {
  353. user.log.Debugln("connected to discord")
  354. user.tryAutomaticDoublePuppeting()
  355. }
  356. func (user *User) disconnectedHandler(s *discordgo.Session, d *discordgo.Disconnect) {
  357. user.log.Debugln("disconnected from discord")
  358. }
  359. func (user *User) guildCreateHandler(s *discordgo.Session, g *discordgo.GuildCreate) {
  360. user.guildsLock.Lock()
  361. defer user.guildsLock.Unlock()
  362. // If we somehow already know about the guild, just update it's name
  363. if guild, found := user.guilds[g.ID]; found {
  364. guild.GuildName = g.Name
  365. guild.Upsert()
  366. return
  367. }
  368. // This is a brand new guild so lets get it added.
  369. guild := user.bridge.DB.Guild.New()
  370. guild.DiscordID = user.ID
  371. guild.GuildID = g.ID
  372. guild.GuildName = g.Name
  373. guild.Upsert()
  374. user.guilds[g.ID] = guild
  375. }
  376. func (user *User) guildDeleteHandler(s *discordgo.Session, g *discordgo.GuildDelete) {
  377. user.guildsLock.Lock()
  378. defer user.guildsLock.Unlock()
  379. if guild, found := user.guilds[g.ID]; found {
  380. guild.Delete()
  381. delete(user.guilds, g.ID)
  382. user.log.Debugln("deleted guild", g.Guild.ID)
  383. }
  384. }
  385. func (user *User) guildUpdateHandler(s *discordgo.Session, g *discordgo.GuildUpdate) {
  386. user.guildsLock.Lock()
  387. defer user.guildsLock.Unlock()
  388. // If we somehow already know about the guild, just update it's name
  389. if guild, found := user.guilds[g.ID]; found {
  390. guild.GuildName = g.Name
  391. guild.Upsert()
  392. user.log.Debugln("updated guild", g.ID)
  393. }
  394. }
  395. func (user *User) createChannel(c *discordgo.Channel) {
  396. key := database.NewPortalKey(c.ID, user.User.ID)
  397. portal := user.bridge.GetPortalByID(key)
  398. if portal.MXID != "" {
  399. return
  400. }
  401. portal.Name = c.Name
  402. portal.Topic = c.Topic
  403. portal.Type = c.Type
  404. if portal.Type == discordgo.ChannelTypeDM {
  405. portal.DMUser = c.Recipients[0].ID
  406. }
  407. if c.Icon != "" {
  408. user.log.Debugln("channel icon", c.Icon)
  409. }
  410. portal.Update()
  411. portal.createMatrixRoom(user, c)
  412. }
  413. func (user *User) channelCreateHandler(s *discordgo.Session, c *discordgo.ChannelCreate) {
  414. user.createChannel(c.Channel)
  415. }
  416. func (user *User) channelDeleteHandler(s *discordgo.Session, c *discordgo.ChannelDelete) {
  417. user.log.Debugln("channel delete handler")
  418. }
  419. func (user *User) channelPinsUpdateHandler(s *discordgo.Session, c *discordgo.ChannelPinsUpdate) {
  420. user.log.Debugln("channel pins update")
  421. }
  422. func (user *User) channelUpdateHandler(s *discordgo.Session, c *discordgo.ChannelUpdate) {
  423. key := database.NewPortalKey(c.ID, user.User.ID)
  424. portal := user.bridge.GetPortalByID(key)
  425. portal.update(user, c.Channel)
  426. }
  427. func (user *User) messageCreateHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
  428. if !user.bridgeMessage(m.GuildID) {
  429. return
  430. }
  431. key := database.NewPortalKey(m.ChannelID, user.ID)
  432. portal := user.bridge.GetPortalByID(key)
  433. msg := portalDiscordMessage{
  434. msg: m,
  435. user: user,
  436. }
  437. portal.discordMessages <- msg
  438. }
  439. func (user *User) messageDeleteHandler(s *discordgo.Session, m *discordgo.MessageDelete) {
  440. if !user.bridgeMessage(m.GuildID) {
  441. return
  442. }
  443. key := database.NewPortalKey(m.ChannelID, user.ID)
  444. portal := user.bridge.GetPortalByID(key)
  445. msg := portalDiscordMessage{
  446. msg: m,
  447. user: user,
  448. }
  449. portal.discordMessages <- msg
  450. }
  451. func (user *User) messageUpdateHandler(s *discordgo.Session, m *discordgo.MessageUpdate) {
  452. if !user.bridgeMessage(m.GuildID) {
  453. return
  454. }
  455. key := database.NewPortalKey(m.ChannelID, user.ID)
  456. portal := user.bridge.GetPortalByID(key)
  457. msg := portalDiscordMessage{
  458. msg: m,
  459. user: user,
  460. }
  461. portal.discordMessages <- msg
  462. }
  463. func (user *User) reactionAddHandler(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
  464. if !user.bridgeMessage(m.MessageReaction.GuildID) {
  465. return
  466. }
  467. key := database.NewPortalKey(m.ChannelID, user.User.ID)
  468. portal := user.bridge.GetPortalByID(key)
  469. msg := portalDiscordMessage{
  470. msg: m,
  471. user: user,
  472. }
  473. portal.discordMessages <- msg
  474. }
  475. func (user *User) reactionRemoveHandler(s *discordgo.Session, m *discordgo.MessageReactionRemove) {
  476. if !user.bridgeMessage(m.MessageReaction.GuildID) {
  477. return
  478. }
  479. key := database.NewPortalKey(m.ChannelID, user.User.ID)
  480. portal := user.bridge.GetPortalByID(key)
  481. msg := portalDiscordMessage{
  482. msg: m,
  483. user: user,
  484. }
  485. portal.discordMessages <- msg
  486. }
  487. func (user *User) ensureInvited(intent *appservice.IntentAPI, roomID id.RoomID, isDirect bool) bool {
  488. ret := false
  489. inviteContent := event.Content{
  490. Parsed: &event.MemberEventContent{
  491. Membership: event.MembershipInvite,
  492. IsDirect: isDirect,
  493. },
  494. Raw: map[string]interface{}{},
  495. }
  496. customPuppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
  497. if customPuppet != nil && customPuppet.CustomIntent() != nil {
  498. inviteContent.Raw["fi.mau.will_auto_accept"] = true
  499. }
  500. _, err := intent.SendStateEvent(roomID, event.StateMember, user.MXID.String(), &inviteContent)
  501. var httpErr mautrix.HTTPError
  502. if err != nil && errors.As(err, &httpErr) && httpErr.RespError != nil && strings.Contains(httpErr.RespError.Err, "is already in the room") {
  503. user.bridge.StateStore.SetMembership(roomID, user.MXID, event.MembershipJoin)
  504. ret = true
  505. } else if err != nil {
  506. user.log.Warnfln("Failed to invite user to %s: %v", roomID, err)
  507. } else {
  508. ret = true
  509. }
  510. if customPuppet != nil && customPuppet.CustomIntent() != nil {
  511. err = customPuppet.CustomIntent().EnsureJoined(roomID, appservice.EnsureJoinedParams{IgnoreCache: true})
  512. if err != nil {
  513. user.log.Warnfln("Failed to auto-join %s: %v", roomID, err)
  514. ret = false
  515. } else {
  516. ret = true
  517. }
  518. }
  519. return ret
  520. }
  521. func (user *User) getDirectChats() map[id.UserID][]id.RoomID {
  522. chats := map[id.UserID][]id.RoomID{}
  523. privateChats := user.bridge.DB.Portal.FindPrivateChats(user.ID)
  524. for _, portal := range privateChats {
  525. if portal.MXID != "" {
  526. puppetMXID := user.bridge.FormatPuppetMXID(portal.Key.Receiver)
  527. chats[puppetMXID] = []id.RoomID{portal.MXID}
  528. }
  529. }
  530. return chats
  531. }
  532. func (user *User) updateDirectChats(chats map[id.UserID][]id.RoomID) {
  533. if !user.bridge.Config.Bridge.SyncDirectChatList {
  534. return
  535. }
  536. puppet := user.bridge.GetPuppetByMXID(user.MXID)
  537. if puppet == nil {
  538. return
  539. }
  540. intent := puppet.CustomIntent()
  541. if intent == nil {
  542. return
  543. }
  544. method := http.MethodPatch
  545. if chats == nil {
  546. chats = user.getDirectChats()
  547. method = http.MethodPut
  548. }
  549. user.log.Debugln("Updating m.direct list on homeserver")
  550. var err error
  551. if user.bridge.Config.Homeserver.Asmux {
  552. urlPath := intent.BuildURL(mautrix.ClientURLPath{"unstable", "com.beeper.asmux", "dms"})
  553. _, err = intent.MakeFullRequest(mautrix.FullRequest{
  554. Method: method,
  555. URL: urlPath,
  556. Headers: http.Header{"X-Asmux-Auth": {user.bridge.AS.Registration.AppToken}},
  557. RequestJSON: chats,
  558. })
  559. } else {
  560. existingChats := map[id.UserID][]id.RoomID{}
  561. err = intent.GetAccountData(event.AccountDataDirectChats.Type, &existingChats)
  562. if err != nil {
  563. user.log.Warnln("Failed to get m.direct list to update it:", err)
  564. return
  565. }
  566. for userID, rooms := range existingChats {
  567. if _, ok := user.bridge.ParsePuppetMXID(userID); !ok {
  568. // This is not a ghost user, include it in the new list
  569. chats[userID] = rooms
  570. } else if _, ok := chats[userID]; !ok && method == http.MethodPatch {
  571. // This is a ghost user, but we're not replacing the whole list, so include it too
  572. chats[userID] = rooms
  573. }
  574. }
  575. err = intent.SetAccountData(event.AccountDataDirectChats.Type, &chats)
  576. }
  577. if err != nil {
  578. user.log.Warnln("Failed to update m.direct list:", err)
  579. }
  580. }
  581. func (user *User) bridgeGuild(guildID string, everything bool) error {
  582. user.guildsLock.Lock()
  583. defer user.guildsLock.Unlock()
  584. guild, found := user.guilds[guildID]
  585. if !found {
  586. return fmt.Errorf("guildID not found")
  587. }
  588. // Update the guild
  589. guild.Bridge = true
  590. guild.Upsert()
  591. // If this is a full bridge, create portals for all the channels
  592. if everything {
  593. channels, err := user.Session.GuildChannels(guildID)
  594. if err != nil {
  595. return err
  596. }
  597. for _, channel := range channels {
  598. if channelIsBridgeable(channel) {
  599. user.createChannel(channel)
  600. }
  601. }
  602. }
  603. return nil
  604. }
  605. func (user *User) unbridgeGuild(guildID string) error {
  606. user.guildsLock.Lock()
  607. defer user.guildsLock.Unlock()
  608. guild, exists := user.guilds[guildID]
  609. if !exists {
  610. return fmt.Errorf("guildID not found")
  611. }
  612. if !guild.Bridge {
  613. return fmt.Errorf("guild not bridged")
  614. }
  615. // First update the guild so we don't have any other go routines recreating
  616. // channels we're about to destroy.
  617. guild.Bridge = false
  618. guild.Upsert()
  619. // Now run through the channels in the guild and remove any portals we
  620. // have for them.
  621. channels, err := user.Session.GuildChannels(guildID)
  622. if err != nil {
  623. return err
  624. }
  625. for _, channel := range channels {
  626. if channelIsBridgeable(channel) {
  627. key := database.PortalKey{
  628. ChannelID: channel.ID,
  629. Receiver: user.ID,
  630. }
  631. portal := user.bridge.GetPortalByID(key)
  632. portal.leave(user)
  633. }
  634. }
  635. return nil
  636. }