user.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. package bridge
  2. import (
  3. "errors"
  4. "strings"
  5. "github.com/bwmarrin/discordgo"
  6. "github.com/skip2/go-qrcode"
  7. log "maunium.net/go/maulogger/v2"
  8. "maunium.net/go/mautrix"
  9. "maunium.net/go/mautrix/appservice"
  10. "maunium.net/go/mautrix/event"
  11. "maunium.net/go/mautrix/id"
  12. "gitlab.com/beeper/discord/database"
  13. )
  14. type User struct {
  15. *database.User
  16. bridge *Bridge
  17. log log.Logger
  18. }
  19. func (b *Bridge) loadUser(dbUser *database.User, mxid *id.UserID) *User {
  20. // If we weren't passed in a user we attempt to create one if we were given
  21. // a matrix id.
  22. if dbUser == nil {
  23. if mxid == nil {
  24. return nil
  25. }
  26. dbUser = b.db.User.New()
  27. dbUser.MXID = *mxid
  28. dbUser.Insert()
  29. }
  30. user := b.NewUser(dbUser)
  31. // We assume the usersLock was acquired by our caller.
  32. b.usersByMXID[user.MXID] = user
  33. if user.ID != "" {
  34. b.usersByID[user.ID] = user
  35. }
  36. if user.ManagementRoom != "" {
  37. // Lock the management rooms for our update
  38. b.managementRoomsLock.Lock()
  39. b.managementRooms[user.ManagementRoom] = user
  40. b.managementRoomsLock.Unlock()
  41. }
  42. return user
  43. }
  44. func (b *Bridge) GetUserByMXID(userID id.UserID) *User {
  45. // TODO: check if puppet
  46. b.usersLock.Lock()
  47. defer b.usersLock.Unlock()
  48. user, ok := b.usersByMXID[userID]
  49. if !ok {
  50. return b.loadUser(b.db.User.GetByMXID(userID), &userID)
  51. }
  52. return user
  53. }
  54. func (b *Bridge) GetUserByID(id string) *User {
  55. b.usersLock.Lock()
  56. defer b.usersLock.Unlock()
  57. user, ok := b.usersByID[id]
  58. if !ok {
  59. return b.loadUser(b.db.User.GetByID(id), nil)
  60. }
  61. return user
  62. }
  63. func (b *Bridge) NewUser(dbUser *database.User) *User {
  64. user := &User{
  65. User: dbUser,
  66. bridge: b,
  67. log: b.log.Sub("User").Sub(string(dbUser.MXID)),
  68. }
  69. return user
  70. }
  71. func (b *Bridge) getAllUsers() []*User {
  72. b.usersLock.Lock()
  73. defer b.usersLock.Unlock()
  74. dbUsers := b.db.User.GetAll()
  75. users := make([]*User, len(dbUsers))
  76. for idx, dbUser := range dbUsers {
  77. user, ok := b.usersByMXID[dbUser.MXID]
  78. if !ok {
  79. user = b.loadUser(dbUser, nil)
  80. }
  81. users[idx] = user
  82. }
  83. return users
  84. }
  85. func (b *Bridge) startUsers() {
  86. b.log.Debugln("Starting users")
  87. for _, user := range b.getAllUsers() {
  88. // if user.ID != "" {
  89. // haveSessions = true
  90. // }
  91. go user.Connect()
  92. }
  93. }
  94. func (u *User) SetManagementRoom(roomID id.RoomID) {
  95. u.bridge.managementRoomsLock.Lock()
  96. defer u.bridge.managementRoomsLock.Unlock()
  97. existing, ok := u.bridge.managementRooms[roomID]
  98. if ok {
  99. // If there's a user already assigned to this management room, clear it
  100. // out.
  101. // I think this is due a name change or something? I dunno, leaving it
  102. // for now.
  103. existing.ManagementRoom = ""
  104. existing.Update()
  105. }
  106. u.ManagementRoom = roomID
  107. u.bridge.managementRooms[u.ManagementRoom] = u
  108. u.Update()
  109. }
  110. func (u *User) HasSession() bool {
  111. return u.User.Session != nil
  112. }
  113. func (u *User) sendQRCode(bot *appservice.IntentAPI, roomID id.RoomID, code string) (id.EventID, error) {
  114. url, err := u.uploadQRCode(code)
  115. if err != nil {
  116. return "", err
  117. }
  118. content := event.MessageEventContent{
  119. MsgType: event.MsgImage,
  120. Body: code,
  121. URL: url.CUString(),
  122. }
  123. resp, err := bot.SendMessageEvent(roomID, event.EventMessage, &content)
  124. if err != nil {
  125. return "", err
  126. }
  127. return resp.EventID, nil
  128. }
  129. func (u *User) uploadQRCode(code string) (id.ContentURI, error) {
  130. qrCode, err := qrcode.Encode(code, qrcode.Low, 256)
  131. if err != nil {
  132. u.log.Errorln("Failed to encode QR code:", err)
  133. return id.ContentURI{}, err
  134. }
  135. bot := u.bridge.as.BotClient()
  136. resp, err := bot.UploadBytes(qrCode, "image/png")
  137. if err != nil {
  138. u.log.Errorln("Failed to upload QR code:", err)
  139. return id.ContentURI{}, err
  140. }
  141. return resp.ContentURI, nil
  142. }
  143. func (u *User) Login(token string) error {
  144. err := u.User.NewSession(token)
  145. if err != nil {
  146. return err
  147. }
  148. return u.Connect()
  149. }
  150. func (u *User) Connect() error {
  151. u.log.Debugln("connecting to discord")
  152. // get our user info
  153. user, err := u.User.Session.User("@me")
  154. if err != nil {
  155. return err
  156. }
  157. u.User.ID = user.ID
  158. // Add our event handlers
  159. u.User.Session.AddHandler(u.connectedHandler)
  160. u.User.Session.AddHandler(u.disconnectedHandler)
  161. u.User.Session.AddHandler(u.channelCreateHandler)
  162. u.User.Session.AddHandler(u.channelDeleteHandler)
  163. u.User.Session.AddHandler(u.channelPinsUpdateHandler)
  164. u.User.Session.AddHandler(u.channelUpdateHandler)
  165. u.User.Session.AddHandler(u.messageCreateHandler)
  166. u.User.Session.AddHandler(u.messageDeleteHandler)
  167. u.User.Session.AddHandler(u.messageUpdateHandler)
  168. u.User.Session.AddHandler(u.reactionAddHandler)
  169. u.User.Session.AddHandler(u.reactionRemoveHandler)
  170. // u.User.Session.Identify.Capabilities = 125
  171. // // Setup our properties
  172. // u.User.Session.Identify.Properties = discordgo.IdentifyProperties{
  173. // OS: "Windows",
  174. // OSVersion: "10",
  175. // Browser: "Chrome",
  176. // BrowserUserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36",
  177. // BrowserVersion: "92.0.4515.159",
  178. // Referrer: "https://discord.com/channels/@me",
  179. // ReferringDomain: "discord.com",
  180. // ClientBuildNumber: "83364",
  181. // ReleaseChannel: "stable",
  182. // }
  183. u.User.Session.Identify.Presence.Status = "online"
  184. return u.User.Session.Open()
  185. }
  186. func (u *User) connectedHandler(s *discordgo.Session, c *discordgo.Connect) {
  187. u.log.Debugln("connected to discord")
  188. }
  189. func (u *User) disconnectedHandler(s *discordgo.Session, d *discordgo.Disconnect) {
  190. u.log.Debugln("disconnected from discord")
  191. }
  192. func (u *User) channelCreateHandler(s *discordgo.Session, c *discordgo.ChannelCreate) {
  193. key := database.NewPortalKey(c.ID, u.User.ID)
  194. portal := u.bridge.GetPortalByID(key)
  195. portal.Name = c.Name
  196. portal.Topic = c.Topic
  197. portal.Type = c.Type
  198. if portal.Type == discordgo.ChannelTypeDM {
  199. portal.DMUser = c.Recipients[0].ID
  200. }
  201. if c.Icon != "" {
  202. u.log.Debugln("channel icon", c.Icon)
  203. }
  204. portal.Update()
  205. portal.createMatrixRoom(u, c.Channel)
  206. }
  207. func (u *User) channelDeleteHandler(s *discordgo.Session, c *discordgo.ChannelDelete) {
  208. u.log.Debugln("channel delete handler")
  209. }
  210. func (u *User) channelPinsUpdateHandler(s *discordgo.Session, c *discordgo.ChannelPinsUpdate) {
  211. u.log.Debugln("channel pins update")
  212. }
  213. func (u *User) channelUpdateHandler(s *discordgo.Session, c *discordgo.ChannelUpdate) {
  214. key := database.NewPortalKey(c.ID, u.User.ID)
  215. portal := u.bridge.GetPortalByID(key)
  216. portal.Name = c.Name
  217. portal.Topic = c.Topic
  218. u.log.Debugln("channel icon", c.Icon)
  219. portal.Update()
  220. u.log.Debugln("channel update")
  221. }
  222. func (u *User) messageCreateHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
  223. if m.GuildID != "" {
  224. u.log.Debugln("ignoring message for guild")
  225. return
  226. }
  227. key := database.NewPortalKey(m.ChannelID, u.ID)
  228. portal := u.bridge.GetPortalByID(key)
  229. msg := portalDiscordMessage{
  230. msg: m,
  231. user: u,
  232. }
  233. portal.discordMessages <- msg
  234. }
  235. func (u *User) messageDeleteHandler(s *discordgo.Session, m *discordgo.MessageDelete) {
  236. if m.GuildID != "" {
  237. u.log.Debugln("ignoring message delete for guild message")
  238. return
  239. }
  240. key := database.NewPortalKey(m.ChannelID, u.ID)
  241. portal := u.bridge.GetPortalByID(key)
  242. msg := portalDiscordMessage{
  243. msg: m,
  244. user: u,
  245. }
  246. portal.discordMessages <- msg
  247. }
  248. func (u *User) messageUpdateHandler(s *discordgo.Session, m *discordgo.MessageUpdate) {
  249. if m.GuildID != "" {
  250. u.log.Debugln("ignoring message update for guild message")
  251. return
  252. }
  253. key := database.NewPortalKey(m.ChannelID, u.ID)
  254. portal := u.bridge.GetPortalByID(key)
  255. msg := portalDiscordMessage{
  256. msg: m,
  257. user: u,
  258. }
  259. portal.discordMessages <- msg
  260. }
  261. func (u *User) reactionAddHandler(s *discordgo.Session, m *discordgo.MessageReactionAdd) {
  262. if m.GuildID != "" {
  263. u.log.Debugln("ignoring reaction for guild message")
  264. return
  265. }
  266. key := database.NewPortalKey(m.ChannelID, u.User.ID)
  267. portal := u.bridge.GetPortalByID(key)
  268. msg := portalDiscordMessage{
  269. msg: m,
  270. user: u,
  271. }
  272. portal.discordMessages <- msg
  273. }
  274. func (u *User) reactionRemoveHandler(s *discordgo.Session, m *discordgo.MessageReactionRemove) {
  275. if m.GuildID != "" {
  276. u.log.Debugln("ignoring reaction for guild message")
  277. return
  278. }
  279. key := database.NewPortalKey(m.ChannelID, u.User.ID)
  280. portal := u.bridge.GetPortalByID(key)
  281. msg := portalDiscordMessage{
  282. msg: m,
  283. user: u,
  284. }
  285. portal.discordMessages <- msg
  286. }
  287. func (u *User) ensureInvited(intent *appservice.IntentAPI, roomID id.RoomID, isDirect bool) bool {
  288. ret := false
  289. inviteContent := event.Content{
  290. Parsed: &event.MemberEventContent{
  291. Membership: event.MembershipInvite,
  292. IsDirect: isDirect,
  293. },
  294. Raw: map[string]interface{}{},
  295. }
  296. _, err := intent.SendStateEvent(roomID, event.StateMember, u.MXID.String(), &inviteContent)
  297. var httpErr mautrix.HTTPError
  298. if err != nil && errors.As(err, &httpErr) && httpErr.RespError != nil && strings.Contains(httpErr.RespError.Err, "is already in the room") {
  299. u.bridge.StateStore.SetMembership(roomID, u.MXID, event.MembershipJoin)
  300. ret = true
  301. } else if err != nil {
  302. u.log.Warnfln("Failed to invite user to %s: %v", roomID, err)
  303. } else {
  304. ret = true
  305. }
  306. return ret
  307. }