user.go 9.1 KB

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