user.go 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  1. // mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
  2. // Copyright (C) 2020 Tulir Asokan
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "encoding/json"
  19. "errors"
  20. "fmt"
  21. "net/http"
  22. "sort"
  23. "strconv"
  24. "strings"
  25. "sync"
  26. "time"
  27. "github.com/skip2/go-qrcode"
  28. log "maunium.net/go/maulogger/v2"
  29. "github.com/Rhymen/go-whatsapp"
  30. waBinary "github.com/Rhymen/go-whatsapp/binary"
  31. waProto "github.com/Rhymen/go-whatsapp/binary/proto"
  32. "maunium.net/go/mautrix"
  33. "maunium.net/go/mautrix/event"
  34. "maunium.net/go/mautrix/format"
  35. "maunium.net/go/mautrix/id"
  36. "maunium.net/go/mautrix-whatsapp/database"
  37. "maunium.net/go/mautrix-whatsapp/types"
  38. "maunium.net/go/mautrix-whatsapp/whatsapp-ext"
  39. )
  40. type User struct {
  41. *database.User
  42. Conn *whatsappExt.ExtendedConn
  43. bridge *Bridge
  44. log log.Logger
  45. Admin bool
  46. Whitelisted bool
  47. RelaybotWhitelisted bool
  48. IsRelaybot bool
  49. ConnectionErrors int
  50. CommunityID string
  51. cleanDisconnection bool
  52. batteryWarningsSent int
  53. lastReconnection int64
  54. chatListReceived chan struct{}
  55. syncPortalsDone chan struct{}
  56. messages chan PortalMessage
  57. syncStart chan struct{}
  58. syncWait sync.WaitGroup
  59. mgmtCreateLock sync.Mutex
  60. }
  61. func (bridge *Bridge) GetUserByMXID(userID id.UserID) *User {
  62. _, isPuppet := bridge.ParsePuppetMXID(userID)
  63. if isPuppet || userID == bridge.Bot.UserID {
  64. return nil
  65. }
  66. bridge.usersLock.Lock()
  67. defer bridge.usersLock.Unlock()
  68. user, ok := bridge.usersByMXID[userID]
  69. if !ok {
  70. return bridge.loadDBUser(bridge.DB.User.GetByMXID(userID), &userID)
  71. }
  72. return user
  73. }
  74. func (bridge *Bridge) GetUserByJID(userID types.WhatsAppID) *User {
  75. bridge.usersLock.Lock()
  76. defer bridge.usersLock.Unlock()
  77. user, ok := bridge.usersByJID[userID]
  78. if !ok {
  79. return bridge.loadDBUser(bridge.DB.User.GetByJID(userID), nil)
  80. }
  81. return user
  82. }
  83. func (user *User) addToJIDMap() {
  84. user.bridge.usersLock.Lock()
  85. user.bridge.usersByJID[user.JID] = user
  86. user.bridge.usersLock.Unlock()
  87. }
  88. func (user *User) removeFromJIDMap() {
  89. user.bridge.usersLock.Lock()
  90. delete(user.bridge.usersByJID, user.JID)
  91. user.bridge.usersLock.Unlock()
  92. user.bridge.Metrics.TrackLoginState(user.JID, false)
  93. }
  94. func (bridge *Bridge) GetAllUsers() []*User {
  95. bridge.usersLock.Lock()
  96. defer bridge.usersLock.Unlock()
  97. dbUsers := bridge.DB.User.GetAll()
  98. output := make([]*User, len(dbUsers))
  99. for index, dbUser := range dbUsers {
  100. user, ok := bridge.usersByMXID[dbUser.MXID]
  101. if !ok {
  102. user = bridge.loadDBUser(dbUser, nil)
  103. }
  104. output[index] = user
  105. }
  106. return output
  107. }
  108. func (bridge *Bridge) loadDBUser(dbUser *database.User, mxid *id.UserID) *User {
  109. if dbUser == nil {
  110. if mxid == nil {
  111. return nil
  112. }
  113. dbUser = bridge.DB.User.New()
  114. dbUser.MXID = *mxid
  115. dbUser.Insert()
  116. }
  117. user := bridge.NewUser(dbUser)
  118. bridge.usersByMXID[user.MXID] = user
  119. if len(user.JID) > 0 {
  120. bridge.usersByJID[user.JID] = user
  121. }
  122. if len(user.ManagementRoom) > 0 {
  123. bridge.managementRooms[user.ManagementRoom] = user
  124. }
  125. return user
  126. }
  127. func (user *User) GetPortals() []*Portal {
  128. keys := user.User.GetPortalKeys()
  129. portals := make([]*Portal, len(keys))
  130. user.bridge.portalsLock.Lock()
  131. for i, key := range keys {
  132. portal, ok := user.bridge.portalsByJID[key]
  133. if !ok {
  134. portal = user.bridge.loadDBPortal(user.bridge.DB.Portal.GetByJID(key), &key)
  135. }
  136. portals[i] = portal
  137. }
  138. user.bridge.portalsLock.Unlock()
  139. return portals
  140. }
  141. func (bridge *Bridge) NewUser(dbUser *database.User) *User {
  142. user := &User{
  143. User: dbUser,
  144. bridge: bridge,
  145. log: bridge.Log.Sub("User").Sub(string(dbUser.MXID)),
  146. IsRelaybot: false,
  147. chatListReceived: make(chan struct{}, 1),
  148. syncPortalsDone: make(chan struct{}, 1),
  149. syncStart: make(chan struct{}, 1),
  150. messages: make(chan PortalMessage, bridge.Config.Bridge.UserMessageBuffer),
  151. }
  152. user.RelaybotWhitelisted = user.bridge.Config.Bridge.Permissions.IsRelaybotWhitelisted(user.MXID)
  153. user.Whitelisted = user.bridge.Config.Bridge.Permissions.IsWhitelisted(user.MXID)
  154. user.Admin = user.bridge.Config.Bridge.Permissions.IsAdmin(user.MXID)
  155. go user.handleMessageLoop()
  156. return user
  157. }
  158. func (user *User) GetManagementRoom() id.RoomID {
  159. if len(user.ManagementRoom) == 0 {
  160. user.mgmtCreateLock.Lock()
  161. defer user.mgmtCreateLock.Unlock()
  162. if len(user.ManagementRoom) > 0 {
  163. return user.ManagementRoom
  164. }
  165. resp, err := user.bridge.Bot.CreateRoom(&mautrix.ReqCreateRoom{
  166. Topic: "WhatsApp bridge notices",
  167. IsDirect: true,
  168. })
  169. if err != nil {
  170. user.log.Errorln("Failed to auto-create management room:", err)
  171. } else {
  172. user.SetManagementRoom(resp.RoomID)
  173. }
  174. }
  175. return user.ManagementRoom
  176. }
  177. func (user *User) SetManagementRoom(roomID id.RoomID) {
  178. existingUser, ok := user.bridge.managementRooms[roomID]
  179. if ok {
  180. existingUser.ManagementRoom = ""
  181. existingUser.Update()
  182. }
  183. user.ManagementRoom = roomID
  184. user.bridge.managementRooms[user.ManagementRoom] = user
  185. user.Update()
  186. }
  187. func (user *User) SetSession(session *whatsapp.Session) {
  188. user.Session = session
  189. if session == nil {
  190. user.LastConnection = 0
  191. }
  192. user.Update()
  193. }
  194. func (user *User) Connect(evenIfNoSession bool) bool {
  195. if user.Conn != nil {
  196. return true
  197. } else if !evenIfNoSession && user.Session == nil {
  198. return false
  199. }
  200. user.log.Debugln("Connecting to WhatsApp")
  201. timeout := time.Duration(user.bridge.Config.Bridge.ConnectionTimeout)
  202. if timeout == 0 {
  203. timeout = 20
  204. }
  205. conn, err := whatsapp.NewConn(timeout * time.Second)
  206. if err != nil {
  207. user.log.Errorln("Failed to connect to WhatsApp:", err)
  208. user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp server. " +
  209. "This indicates a network problem on the bridge server. See bridge logs for more info.")
  210. return false
  211. }
  212. user.Conn = whatsappExt.ExtendConn(conn)
  213. _ = user.Conn.SetClientName(user.bridge.Config.WhatsApp.OSName, user.bridge.Config.WhatsApp.BrowserName, WAVersion)
  214. user.log.Debugln("WhatsApp connection successful")
  215. user.Conn.AddHandler(user)
  216. return user.RestoreSession()
  217. }
  218. func (user *User) RestoreSession() bool {
  219. if user.Session != nil {
  220. sess, err := user.Conn.RestoreWithSession(*user.Session)
  221. if err == whatsapp.ErrAlreadyLoggedIn {
  222. return true
  223. } else if err != nil {
  224. user.log.Errorln("Failed to restore session:", err)
  225. if errors.Is(err, whatsapp.ErrUnpaired) {
  226. user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp: unpaired from phone. " +
  227. "To re-pair your phone, use `delete-session` and then `login`.")
  228. } else {
  229. user.sendMarkdownBridgeAlert("\u26a0 Failed to connect to WhatsApp. Make sure WhatsApp " +
  230. "on your phone is reachable and use `reconnect` to try connecting again.")
  231. }
  232. user.log.Debugln("Disconnecting due to failed session restore...")
  233. _, err := user.Conn.Disconnect()
  234. if err != nil {
  235. user.log.Errorln("Failed to disconnect after failed session restore:", err)
  236. }
  237. return false
  238. }
  239. user.ConnectionErrors = 0
  240. user.SetSession(&sess)
  241. user.log.Debugln("Session restored successfully")
  242. user.PostLogin()
  243. }
  244. return true
  245. }
  246. func (user *User) HasSession() bool {
  247. return user.Session != nil
  248. }
  249. func (user *User) IsConnected() bool {
  250. return user.Conn != nil && user.Conn.IsConnected() && user.Conn.IsLoggedIn()
  251. }
  252. func (user *User) IsLoginInProgress() bool {
  253. return user.Conn != nil && user.Conn.IsLoginInProgress()
  254. }
  255. func (user *User) loginQrChannel(ce *CommandEvent, qrChan <-chan string, eventIDChan chan<- id.EventID) {
  256. var qrEventID id.EventID
  257. for code := range qrChan {
  258. if code == "stop" {
  259. return
  260. }
  261. qrCode, err := qrcode.Encode(code, qrcode.Low, 256)
  262. if err != nil {
  263. user.log.Errorln("Failed to encode QR code:", err)
  264. ce.Reply("Failed to encode QR code: %v", err)
  265. return
  266. }
  267. bot := user.bridge.AS.BotClient()
  268. resp, err := bot.UploadBytes(qrCode, "image/png")
  269. if err != nil {
  270. user.log.Errorln("Failed to upload QR code:", err)
  271. ce.Reply("Failed to upload QR code: %v", err)
  272. return
  273. }
  274. if qrEventID == "" {
  275. sendResp, err := bot.SendImage(ce.RoomID, code, resp.ContentURI)
  276. if err != nil {
  277. user.log.Errorln("Failed to send QR code to user:", err)
  278. return
  279. }
  280. qrEventID = sendResp.EventID
  281. eventIDChan <- qrEventID
  282. } else {
  283. _, err = bot.SendMessageEvent(ce.RoomID, event.EventMessage, &event.MessageEventContent{
  284. MsgType: event.MsgImage,
  285. Body: code,
  286. URL: resp.ContentURI.CUString(),
  287. NewContent: &event.MessageEventContent{
  288. MsgType: event.MsgImage,
  289. Body: code,
  290. URL: resp.ContentURI.CUString(),
  291. },
  292. RelatesTo: &event.RelatesTo{
  293. Type: event.RelReplace,
  294. EventID: qrEventID,
  295. },
  296. })
  297. if err != nil {
  298. user.log.Errorln("Failed to send edited QR code to user:", err)
  299. }
  300. }
  301. }
  302. }
  303. func (user *User) Login(ce *CommandEvent) {
  304. qrChan := make(chan string, 3)
  305. eventIDChan := make(chan id.EventID, 1)
  306. go user.loginQrChannel(ce, qrChan, eventIDChan)
  307. session, err := user.Conn.LoginWithRetry(qrChan, user.bridge.Config.Bridge.LoginQRRegenCount)
  308. qrChan <- "stop"
  309. if err != nil {
  310. var eventID id.EventID
  311. select {
  312. case eventID = <-eventIDChan:
  313. default:
  314. }
  315. reply := event.MessageEventContent{
  316. MsgType: event.MsgText,
  317. }
  318. if err == whatsapp.ErrAlreadyLoggedIn {
  319. reply.Body = "You're already logged in"
  320. } else if err == whatsapp.ErrLoginInProgress {
  321. reply.Body = "You have a login in progress already."
  322. } else if err == whatsapp.ErrLoginTimedOut {
  323. reply.Body = "QR code scan timed out. Please try again."
  324. } else {
  325. user.log.Warnln("Failed to log in:", err)
  326. reply.Body = fmt.Sprintf("Unknown error while logging in: %v", err)
  327. }
  328. msg := reply
  329. if eventID != "" {
  330. msg.NewContent = &reply
  331. msg.RelatesTo = &event.RelatesTo{
  332. Type: event.RelReplace,
  333. EventID: eventID,
  334. }
  335. }
  336. _, _ = ce.Bot.SendMessageEvent(ce.RoomID, event.EventMessage, &msg)
  337. return
  338. }
  339. // TODO there's a bit of duplication between this and the provisioning API login method
  340. // Also between the two logout methods (commands.go and provisioning.go)
  341. user.ConnectionErrors = 0
  342. user.JID = strings.Replace(user.Conn.Info.Wid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, 1)
  343. user.addToJIDMap()
  344. user.SetSession(&session)
  345. ce.Reply("Successfully logged in, synchronizing chats...")
  346. user.PostLogin()
  347. }
  348. type Chat struct {
  349. Portal *Portal
  350. LastMessageTime uint64
  351. Contact whatsapp.Contact
  352. }
  353. type ChatList []Chat
  354. func (cl ChatList) Len() int {
  355. return len(cl)
  356. }
  357. func (cl ChatList) Less(i, j int) bool {
  358. return cl[i].LastMessageTime > cl[j].LastMessageTime
  359. }
  360. func (cl ChatList) Swap(i, j int) {
  361. cl[i], cl[j] = cl[j], cl[i]
  362. }
  363. func (user *User) PostLogin() {
  364. user.bridge.Metrics.TrackConnectionState(user.JID, true)
  365. user.bridge.Metrics.TrackLoginState(user.JID, true)
  366. user.log.Debugln("Locking processing of incoming messages and starting post-login sync")
  367. user.syncWait.Add(1)
  368. user.syncStart <- struct{}{}
  369. go user.intPostLogin()
  370. }
  371. func (user *User) tryAutomaticDoublePuppeting() {
  372. if len(user.bridge.Config.Bridge.LoginSharedSecret) == 0 {
  373. // Automatic login not enabled
  374. return
  375. } else if _, homeserver, _ := user.MXID.Parse(); homeserver != user.bridge.Config.Homeserver.Domain {
  376. // user is on another homeserver
  377. return
  378. }
  379. puppet := user.bridge.GetPuppetByJID(user.JID)
  380. if len(puppet.CustomMXID) > 0 {
  381. // Custom puppet already enabled
  382. return
  383. }
  384. accessToken, err := puppet.loginWithSharedSecret(user.MXID)
  385. if err != nil {
  386. user.log.Warnln("Failed to login with shared secret:", err)
  387. return
  388. }
  389. err = puppet.SwitchCustomMXID(accessToken, user.MXID)
  390. if err != nil {
  391. puppet.log.Warnln("Failed to switch to auto-logined custom puppet:", err)
  392. return
  393. }
  394. user.log.Infoln("Successfully automatically enabled custom puppet")
  395. }
  396. func (user *User) sendBridgeNotice(formatString string, args ...interface{}) {
  397. notice := fmt.Sprintf(formatString, args...)
  398. _, err := user.bridge.Bot.SendNotice(user.GetManagementRoom(), notice)
  399. if err != nil {
  400. user.log.Warnf("Failed to send bridge notice \"%s\": %v", notice, err)
  401. }
  402. }
  403. func (user *User) sendMarkdownBridgeAlert(formatString string, args ...interface{}) {
  404. notice := fmt.Sprintf(formatString, args...)
  405. content := format.RenderMarkdown(notice, true, false)
  406. _, err := user.bridge.Bot.SendMessageEvent(user.GetManagementRoom(), event.EventMessage, content)
  407. if err != nil {
  408. user.log.Warnf("Failed to send bridge alert \"%s\": %v", notice, err)
  409. }
  410. }
  411. func (user *User) postConnPing() bool {
  412. err := user.Conn.AdminTest()
  413. if err != nil {
  414. user.log.Errorfln("Post-connection ping failed: %v. Disconnecting and then reconnecting after a second", err)
  415. sess, disconnectErr := user.Conn.Disconnect()
  416. if disconnectErr != nil {
  417. user.log.Warnln("Error while disconnecting after failed post-connection ping:", disconnectErr)
  418. } else {
  419. user.Session = &sess
  420. }
  421. user.bridge.Metrics.TrackDisconnection(user.MXID)
  422. go func() {
  423. time.Sleep(1 * time.Second)
  424. user.tryReconnect(fmt.Sprintf("Post-connection ping failed: %v", err))
  425. }()
  426. return false
  427. } else {
  428. user.log.Debugln("Post-connection ping OK")
  429. return true
  430. }
  431. }
  432. func (user *User) intPostLogin() {
  433. defer user.syncWait.Done()
  434. user.lastReconnection = time.Now().Unix()
  435. user.createCommunity()
  436. user.tryAutomaticDoublePuppeting()
  437. select {
  438. case <-user.chatListReceived:
  439. user.log.Debugln("Chat list receive confirmation received in PostLogin")
  440. case <-time.After(time.Duration(user.bridge.Config.Bridge.ChatListWait) * time.Second):
  441. user.log.Warnln("Timed out waiting for chat list to arrive!")
  442. user.postConnPing()
  443. return
  444. }
  445. if !user.postConnPing() {
  446. return
  447. }
  448. select {
  449. case <-user.syncPortalsDone:
  450. user.log.Debugln("Post-login portal sync complete, unlocking processing of incoming messages.")
  451. case <-time.After(time.Duration(user.bridge.Config.Bridge.PortalSyncWait) * time.Second):
  452. user.log.Warnln("Timed out waiting for portal sync to complete! Unlocking processing of incoming messages.")
  453. }
  454. }
  455. func (user *User) HandleStreamEvent(evt whatsappExt.StreamEvent) {
  456. if evt.Type == whatsappExt.StreamSleep {
  457. if user.lastReconnection+60 > time.Now().Unix() {
  458. user.lastReconnection = 0
  459. user.log.Infoln("Stream went to sleep soon after reconnection, making new post-connection ping in 20 seconds")
  460. go func() {
  461. time.Sleep(20 * time.Second)
  462. user.postConnPing()
  463. }()
  464. }
  465. } else {
  466. user.log.Infofln("Stream event: %+v", evt)
  467. }
  468. }
  469. func (user *User) HandleChatList(chats []whatsapp.Chat) {
  470. user.log.Infoln("Chat list received")
  471. chatMap := make(map[string]whatsapp.Chat)
  472. for _, chat := range user.Conn.Store.Chats {
  473. chatMap[chat.Jid] = chat
  474. }
  475. for _, chat := range chats {
  476. chatMap[chat.Jid] = chat
  477. }
  478. select {
  479. case user.chatListReceived <- struct{}{}:
  480. default:
  481. }
  482. go user.syncPortals(chatMap, false)
  483. }
  484. func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool) {
  485. if chatMap == nil {
  486. chatMap = user.Conn.Store.Chats
  487. }
  488. user.log.Infoln("Reading chat list")
  489. chats := make(ChatList, 0, len(chatMap))
  490. existingKeys := user.GetInCommunityMap()
  491. portalKeys := make([]database.PortalKeyWithMeta, 0, len(chatMap))
  492. for _, chat := range chatMap {
  493. ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
  494. if err != nil {
  495. user.log.Warnfln("Non-integer last message time in %s: %s", chat.Jid, chat.LastMessageTime)
  496. continue
  497. }
  498. portal := user.GetPortalByJID(chat.Jid)
  499. chats = append(chats, Chat{
  500. Portal: portal,
  501. Contact: user.Conn.Store.Contacts[chat.Jid],
  502. LastMessageTime: ts,
  503. })
  504. var inCommunity, ok bool
  505. if inCommunity, ok = existingKeys[portal.Key]; !ok || !inCommunity {
  506. inCommunity = user.addPortalToCommunity(portal)
  507. if portal.IsPrivateChat() {
  508. puppet := user.bridge.GetPuppetByJID(portal.Key.JID)
  509. user.addPuppetToCommunity(puppet)
  510. }
  511. }
  512. portalKeys = append(portalKeys, database.PortalKeyWithMeta{PortalKey: portal.Key, InCommunity: inCommunity})
  513. }
  514. user.log.Infoln("Read chat list, updating user-portal mapping")
  515. err := user.SetPortalKeys(portalKeys)
  516. if err != nil {
  517. user.log.Warnln("Failed to update user-portal mapping:", err)
  518. }
  519. sort.Sort(chats)
  520. limit := user.bridge.Config.Bridge.InitialChatSync
  521. if limit < 0 {
  522. limit = len(chats)
  523. }
  524. now := uint64(time.Now().Unix())
  525. user.log.Infoln("Syncing portals")
  526. for i, chat := range chats {
  527. if chat.LastMessageTime+user.bridge.Config.Bridge.SyncChatMaxAge < now {
  528. break
  529. }
  530. create := (chat.LastMessageTime >= user.LastConnection && user.LastConnection > 0) || i < limit
  531. if len(chat.Portal.MXID) > 0 || create || createAll {
  532. chat.Portal.Sync(user, chat.Contact)
  533. err := chat.Portal.BackfillHistory(user, chat.LastMessageTime)
  534. if err != nil {
  535. chat.Portal.log.Errorln("Error backfilling history:", err)
  536. }
  537. }
  538. }
  539. user.UpdateDirectChats(nil)
  540. user.log.Infoln("Finished syncing portals")
  541. select {
  542. case user.syncPortalsDone <- struct{}{}:
  543. default:
  544. }
  545. }
  546. func (user *User) getDirectChats() map[id.UserID][]id.RoomID {
  547. res := make(map[id.UserID][]id.RoomID)
  548. privateChats := user.bridge.DB.Portal.FindPrivateChats(user.JID)
  549. for _, portal := range privateChats {
  550. if len(portal.MXID) > 0 {
  551. res[user.bridge.FormatPuppetMXID(portal.Key.JID)] = []id.RoomID{portal.MXID}
  552. }
  553. }
  554. return res
  555. }
  556. func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
  557. if !user.bridge.Config.Bridge.SyncDirectChatList {
  558. return
  559. }
  560. puppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
  561. if puppet == nil || puppet.CustomIntent() == nil {
  562. return
  563. }
  564. intent := puppet.CustomIntent()
  565. method := http.MethodPatch
  566. if chats == nil {
  567. chats = user.getDirectChats()
  568. method = http.MethodPut
  569. }
  570. user.log.Debugln("Updating m.direct list on homeserver")
  571. var err error
  572. if user.bridge.Config.Homeserver.Asmux {
  573. urlPath := intent.BuildBaseURL("_matrix", "client", "unstable", "net.maunium.asmux", "dms")
  574. _, err = intent.MakeFullRequest(method, urlPath, http.Header{
  575. "X-Asmux-Auth": {user.bridge.AS.Registration.AppToken},
  576. }, chats, nil)
  577. } else {
  578. existingChats := make(map[id.UserID][]id.RoomID)
  579. err = intent.GetAccountData(event.AccountDataDirectChats.Type, &existingChats)
  580. if err != nil {
  581. user.log.Warnln("Failed to get m.direct list to update it:", err)
  582. return
  583. }
  584. for userID, rooms := range existingChats {
  585. if _, ok := user.bridge.ParsePuppetMXID(userID); !ok {
  586. // This is not a ghost user, include it in the new list
  587. chats[userID] = rooms
  588. } else if _, ok := chats[userID]; !ok && method == http.MethodPatch {
  589. // This is a ghost user, but we're not replacing the whole list, so include it too
  590. chats[userID] = rooms
  591. }
  592. }
  593. err = intent.SetAccountData(event.AccountDataDirectChats.Type, &chats)
  594. }
  595. if err != nil {
  596. user.log.Warnln("Failed to update m.direct list:", err)
  597. }
  598. }
  599. func (user *User) HandleContactList(contacts []whatsapp.Contact) {
  600. contactMap := make(map[string]whatsapp.Contact)
  601. for _, contact := range contacts {
  602. contactMap[contact.Jid] = contact
  603. }
  604. go user.syncPuppets(contactMap)
  605. }
  606. func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
  607. if contacts == nil {
  608. contacts = user.Conn.Store.Contacts
  609. }
  610. user.log.Infoln("Syncing puppet info from contacts")
  611. for jid, contact := range contacts {
  612. if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) {
  613. puppet := user.bridge.GetPuppetByJID(contact.Jid)
  614. puppet.Sync(user, contact)
  615. }
  616. }
  617. user.log.Infoln("Finished syncing puppet info from contacts")
  618. }
  619. func (user *User) updateLastConnectionIfNecessary() {
  620. if user.LastConnection+60 < uint64(time.Now().Unix()) {
  621. user.UpdateLastConnection()
  622. }
  623. }
  624. func (user *User) HandleError(err error) {
  625. if !errors.Is(err, whatsapp.ErrInvalidWsData) {
  626. user.log.Errorfln("WhatsApp error: %v", err)
  627. }
  628. if closed, ok := err.(*whatsapp.ErrConnectionClosed); ok {
  629. user.bridge.Metrics.TrackDisconnection(user.MXID)
  630. if closed.Code == 1000 && user.cleanDisconnection {
  631. user.bridge.Metrics.TrackConnectionState(user.JID, false)
  632. user.cleanDisconnection = false
  633. user.log.Infoln("Clean disconnection by server")
  634. return
  635. }
  636. go user.tryReconnect(fmt.Sprintf("Your WhatsApp connection was closed with websocket status code %d", closed.Code))
  637. } else if failed, ok := err.(*whatsapp.ErrConnectionFailed); ok {
  638. user.bridge.Metrics.TrackDisconnection(user.MXID)
  639. user.ConnectionErrors++
  640. go user.tryReconnect(fmt.Sprintf("Your WhatsApp connection failed: %v", failed.Err))
  641. }
  642. // Otherwise unknown error, probably mostly harmless
  643. }
  644. func (user *User) tryReconnect(msg string) {
  645. user.bridge.Metrics.TrackConnectionState(user.JID, false)
  646. if user.ConnectionErrors > user.bridge.Config.Bridge.MaxConnectionAttempts {
  647. user.sendMarkdownBridgeAlert("%s. Use the `reconnect` command to reconnect.", msg)
  648. return
  649. }
  650. if user.bridge.Config.Bridge.ReportConnectionRetry {
  651. user.sendBridgeNotice("%s. Reconnecting...", msg)
  652. // Don't want the same error to be repeated
  653. msg = ""
  654. }
  655. var tries uint
  656. var exponentialBackoff bool
  657. baseDelay := time.Duration(user.bridge.Config.Bridge.ConnectionRetryDelay)
  658. if baseDelay < 0 {
  659. exponentialBackoff = true
  660. baseDelay = -baseDelay + 1
  661. }
  662. delay := baseDelay
  663. for user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
  664. err := user.Conn.Restore()
  665. if err == nil {
  666. user.ConnectionErrors = 0
  667. if user.bridge.Config.Bridge.ReportConnectionRetry {
  668. user.sendBridgeNotice("Reconnected successfully")
  669. }
  670. user.PostLogin()
  671. return
  672. } else if err.Error() == "init responded with 400" {
  673. user.log.Infoln("Got init 400 error when trying to reconnect, resetting connection...")
  674. sess, err := user.Conn.Disconnect()
  675. if err != nil {
  676. user.log.Debugln("Error while disconnecting for connection reset:", err)
  677. }
  678. if len(sess.Wid) > 0 {
  679. user.SetSession(&sess)
  680. }
  681. }
  682. user.log.Errorln("Error while trying to reconnect after disconnection:", err)
  683. tries++
  684. user.ConnectionErrors++
  685. if user.ConnectionErrors <= user.bridge.Config.Bridge.MaxConnectionAttempts {
  686. if exponentialBackoff {
  687. delay = (1 << tries) + baseDelay
  688. }
  689. if user.bridge.Config.Bridge.ReportConnectionRetry {
  690. user.sendBridgeNotice("Reconnection attempt failed: %v. Retrying in %d seconds...", err, delay)
  691. }
  692. time.Sleep(delay * time.Second)
  693. }
  694. }
  695. if user.bridge.Config.Bridge.ReportConnectionRetry {
  696. user.sendMarkdownBridgeAlert("%d reconnection attempts failed. Use the `reconnect` command to try to reconnect manually.", tries)
  697. } else {
  698. user.sendMarkdownBridgeAlert("\u26a0 %s. Additionally, %d reconnection attempts failed. Use the `reconnect` command to try to reconnect.", msg, tries)
  699. }
  700. }
  701. func (user *User) ShouldCallSynchronously() bool {
  702. return true
  703. }
  704. func (user *User) HandleJSONParseError(err error) {
  705. user.log.Errorln("WhatsApp JSON parse error:", err)
  706. }
  707. func (user *User) PortalKey(jid types.WhatsAppID) database.PortalKey {
  708. return database.NewPortalKey(jid, user.JID)
  709. }
  710. func (user *User) GetPortalByJID(jid types.WhatsAppID) *Portal {
  711. return user.bridge.GetPortalByJID(user.PortalKey(jid))
  712. }
  713. func (user *User) handleMessageLoop() {
  714. for {
  715. select {
  716. case msg := <-user.messages:
  717. user.GetPortalByJID(msg.chat).messages <- msg
  718. case <-user.syncStart:
  719. user.syncWait.Wait()
  720. }
  721. }
  722. }
  723. func (user *User) putMessage(message PortalMessage) {
  724. select {
  725. case user.messages <- message:
  726. default:
  727. user.log.Warnln("Buffer is full, dropping message in", message.chat)
  728. }
  729. }
  730. func (user *User) HandleNewContact(contact whatsapp.Contact) {
  731. user.log.Debugfln("Contact message: %+v", contact)
  732. go func() {
  733. if strings.HasSuffix(contact.Jid, whatsappExt.OldUserSuffix) {
  734. contact.Jid = strings.Replace(contact.Jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1)
  735. }
  736. puppet := user.bridge.GetPuppetByJID(contact.Jid)
  737. puppet.UpdateName(user, contact)
  738. }()
  739. }
  740. func (user *User) HandleBatteryMessage(battery whatsapp.BatteryMessage) {
  741. user.log.Debugfln("Battery message: %+v", battery)
  742. var notice string
  743. if !battery.Plugged && battery.Percentage < 15 && user.batteryWarningsSent < 1 {
  744. notice = fmt.Sprintf("Phone battery low (%d %% remaining)", battery.Percentage)
  745. user.batteryWarningsSent = 1
  746. } else if !battery.Plugged && battery.Percentage < 5 && user.batteryWarningsSent < 2 {
  747. notice = fmt.Sprintf("Phone battery very low (%d %% remaining)", battery.Percentage)
  748. user.batteryWarningsSent = 2
  749. } else if battery.Percentage > 15 || battery.Plugged {
  750. user.batteryWarningsSent = 0
  751. }
  752. if notice != "" {
  753. go user.sendBridgeNotice("%s", notice)
  754. }
  755. }
  756. func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
  757. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  758. }
  759. func (user *User) HandleImageMessage(message whatsapp.ImageMessage) {
  760. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  761. }
  762. func (user *User) HandleStickerMessage(message whatsapp.StickerMessage) {
  763. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  764. }
  765. func (user *User) HandleVideoMessage(message whatsapp.VideoMessage) {
  766. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  767. }
  768. func (user *User) HandleAudioMessage(message whatsapp.AudioMessage) {
  769. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  770. }
  771. func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
  772. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  773. }
  774. func (user *User) HandleContactMessage(message whatsapp.ContactMessage) {
  775. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  776. }
  777. func (user *User) HandleLocationMessage(message whatsapp.LocationMessage) {
  778. user.putMessage(PortalMessage{message.Info.RemoteJid, user, message, message.Info.Timestamp})
  779. }
  780. func (user *User) HandleMessageRevoke(message whatsappExt.MessageRevocation) {
  781. user.putMessage(PortalMessage{message.RemoteJid, user, message, 0})
  782. }
  783. type FakeMessage struct {
  784. Text string
  785. ID string
  786. Alert bool
  787. }
  788. func (user *User) HandleCallInfo(info whatsappExt.CallInfo) {
  789. if info.Data != nil {
  790. return
  791. }
  792. data := FakeMessage{
  793. ID: info.ID,
  794. }
  795. switch info.Type {
  796. case whatsappExt.CallOffer:
  797. if !user.bridge.Config.Bridge.CallNotices.Start {
  798. return
  799. }
  800. data.Text = "Incoming call"
  801. data.Alert = true
  802. case whatsappExt.CallOfferVideo:
  803. if !user.bridge.Config.Bridge.CallNotices.Start {
  804. return
  805. }
  806. data.Text = "Incoming video call"
  807. data.Alert = true
  808. case whatsappExt.CallTerminate:
  809. if !user.bridge.Config.Bridge.CallNotices.End {
  810. return
  811. }
  812. data.Text = "Call ended"
  813. data.ID += "E"
  814. default:
  815. return
  816. }
  817. portal := user.GetPortalByJID(info.From)
  818. if portal != nil {
  819. portal.messages <- PortalMessage{info.From, user, data, 0}
  820. }
  821. }
  822. func (user *User) HandlePresence(info whatsappExt.Presence) {
  823. puppet := user.bridge.GetPuppetByJID(info.SenderJID)
  824. switch info.Status {
  825. case whatsapp.PresenceUnavailable:
  826. _ = puppet.DefaultIntent().SetPresence("offline")
  827. case whatsapp.PresenceAvailable:
  828. if len(puppet.typingIn) > 0 && puppet.typingAt+15 > time.Now().Unix() {
  829. portal := user.bridge.GetPortalByMXID(puppet.typingIn)
  830. _, _ = puppet.IntentFor(portal).UserTyping(puppet.typingIn, false, 0)
  831. puppet.typingIn = ""
  832. puppet.typingAt = 0
  833. } else {
  834. _ = puppet.DefaultIntent().SetPresence("online")
  835. }
  836. case whatsapp.PresenceComposing:
  837. portal := user.GetPortalByJID(info.JID)
  838. if len(puppet.typingIn) > 0 && puppet.typingAt+15 > time.Now().Unix() {
  839. if puppet.typingIn == portal.MXID {
  840. return
  841. }
  842. _, _ = puppet.IntentFor(portal).UserTyping(puppet.typingIn, false, 0)
  843. }
  844. puppet.typingIn = portal.MXID
  845. puppet.typingAt = time.Now().Unix()
  846. _, _ = puppet.IntentFor(portal).UserTyping(portal.MXID, true, 15*1000)
  847. }
  848. }
  849. func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) {
  850. if (info.Command == whatsappExt.MsgInfoCommandAck || info.Command == whatsappExt.MsgInfoCommandAcks) && info.Acknowledgement == whatsappExt.AckMessageRead {
  851. portal := user.GetPortalByJID(info.ToJID)
  852. if len(portal.MXID) == 0 {
  853. return
  854. }
  855. go func() {
  856. intent := user.bridge.GetPuppetByJID(info.SenderJID).IntentFor(portal)
  857. for _, msgID := range info.IDs {
  858. msg := user.bridge.DB.Message.GetByJID(portal.Key, msgID)
  859. if msg == nil {
  860. continue
  861. }
  862. err := intent.MarkRead(portal.MXID, msg.MXID)
  863. if err != nil {
  864. user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err)
  865. }
  866. }
  867. }()
  868. }
  869. }
  870. func (user *User) HandleReceivedMessage(received whatsapp.ReceivedMessage) {
  871. if received.Type == "read" {
  872. user.markSelfRead(received.Jid, received.Index)
  873. } else {
  874. user.log.Debugfln("Unknown received message type: %+v", received)
  875. }
  876. }
  877. func (user *User) HandleReadMessage(read whatsapp.ReadMessage) {
  878. user.log.Debugfln("Received chat read message: %+v", read)
  879. user.markSelfRead(read.Jid, "")
  880. }
  881. func (user *User) markSelfRead(jid, messageID string) {
  882. if strings.HasSuffix(jid, whatsappExt.OldUserSuffix) {
  883. jid = strings.Replace(jid, whatsappExt.OldUserSuffix, whatsappExt.NewUserSuffix, -1)
  884. }
  885. puppet := user.bridge.GetPuppetByJID(user.JID)
  886. if puppet == nil {
  887. return
  888. }
  889. intent := puppet.CustomIntent()
  890. if intent == nil {
  891. return
  892. }
  893. portal := user.GetPortalByJID(jid)
  894. if portal == nil {
  895. return
  896. }
  897. var message *database.Message
  898. if messageID == "" {
  899. message = user.bridge.DB.Message.GetLastInChat(portal.Key)
  900. if message == nil {
  901. return
  902. }
  903. user.log.Debugfln("User read chat %s/%s in WhatsApp mobile (last known event: %s/%s)", portal.Key.JID, portal.MXID, message.JID, message.MXID)
  904. } else {
  905. message = user.bridge.DB.Message.GetByJID(portal.Key, messageID)
  906. if message == nil {
  907. return
  908. }
  909. user.log.Debugfln("User read message %s/%s in %s/%s in WhatsApp mobile", message.JID, message.MXID, portal.Key.JID, portal.MXID)
  910. }
  911. err := intent.MarkRead(portal.MXID, message.MXID)
  912. if err != nil {
  913. user.log.Warnfln("Failed to bridge own read receipt in %s: %v", jid, err)
  914. }
  915. }
  916. func (user *User) HandleCommand(cmd whatsappExt.Command) {
  917. switch cmd.Type {
  918. case whatsappExt.CommandPicture:
  919. if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) {
  920. puppet := user.bridge.GetPuppetByJID(cmd.JID)
  921. go puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
  922. } else {
  923. portal := user.GetPortalByJID(cmd.JID)
  924. go portal.UpdateAvatar(user, cmd.ProfilePicInfo, true)
  925. }
  926. case whatsappExt.CommandDisconnect:
  927. user.cleanDisconnection = true
  928. if cmd.Kind == "replaced" {
  929. go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server because you opened another WhatsApp Web client.\n\n" +
  930. "Use the `reconnect` command to disconnect the other client and resume bridging.")
  931. } else {
  932. user.log.Warnln("Unknown kind of disconnect:", string(cmd.Raw))
  933. go user.sendMarkdownBridgeAlert("\u26a0 Your WhatsApp connection was closed by the server (reason code: %s).\n\n"+
  934. "Use the `reconnect` command to reconnect.", cmd.Kind)
  935. }
  936. }
  937. }
  938. func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
  939. if cmd.Command != whatsappExt.ChatUpdateCommandAction {
  940. return
  941. }
  942. portal := user.GetPortalByJID(cmd.JID)
  943. if len(portal.MXID) == 0 {
  944. if cmd.Data.Action == whatsappExt.ChatActionIntroduce || cmd.Data.Action == whatsappExt.ChatActionCreate {
  945. go func() {
  946. err := portal.CreateMatrixRoom(user)
  947. if err != nil {
  948. user.log.Errorln("Failed to create portal room after receiving join event:", err)
  949. }
  950. }()
  951. }
  952. return
  953. }
  954. switch cmd.Data.Action {
  955. case whatsappExt.ChatActionNameChange:
  956. go portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID, true)
  957. case whatsappExt.ChatActionAddTopic:
  958. go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID, true)
  959. case whatsappExt.ChatActionRemoveTopic:
  960. go portal.UpdateTopic("", cmd.Data.SenderJID, true)
  961. case whatsappExt.ChatActionPromote:
  962. go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
  963. case whatsappExt.ChatActionDemote:
  964. go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
  965. case whatsappExt.ChatActionAnnounce:
  966. go portal.RestrictMessageSending(cmd.Data.Announce)
  967. case whatsappExt.ChatActionRestrict:
  968. go portal.RestrictMetadataChanges(cmd.Data.Restrict)
  969. case whatsappExt.ChatActionRemove:
  970. go portal.HandleWhatsAppKick(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
  971. case whatsappExt.ChatActionAdd:
  972. go portal.HandleWhatsAppInvite(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
  973. case whatsappExt.ChatActionIntroduce:
  974. if cmd.Data.SenderJID != "unknown" {
  975. go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID})
  976. }
  977. }
  978. }
  979. func (user *User) HandleJsonMessage(message string) {
  980. var msg json.RawMessage
  981. err := json.Unmarshal([]byte(message), &msg)
  982. if err != nil {
  983. return
  984. }
  985. user.log.Debugln("JSON message:", message)
  986. user.updateLastConnectionIfNecessary()
  987. }
  988. func (user *User) HandleRawMessage(message *waProto.WebMessageInfo) {
  989. user.updateLastConnectionIfNecessary()
  990. }
  991. func (user *User) HandleUnknownBinaryNode(node *waBinary.Node) {
  992. user.log.Debugfln("Unknown binary message: %+v", node)
  993. }
  994. func (user *User) NeedsRelaybot(portal *Portal) bool {
  995. return !user.HasSession() || !user.IsInPortal(portal.Key)
  996. }