Selaa lähdekoodia

Use handlers instead of sleeping for startup chat/contact sync

Tulir Asokan 5 vuotta sitten
vanhempi
sitoutus
1490830be2
6 muutettua tiedostoa jossa 55 lisäystä ja 23 poistoa
  1. 3 3
      commands.go
  2. 1 1
      config/bridge.go
  3. 3 3
      example-config.yaml
  4. 1 1
      go.mod
  5. 2 0
      go.sum
  6. 45 15
      user.go

+ 3 - 3
commands.go

@@ -103,7 +103,7 @@ func (handler *CommandHandler) Handle(roomID types.MatrixRoomID, user *User, mes
 	case "dev-test":
 		handler.CommandDevTest(ce)
 	case "login-matrix", "logout", "sync", "list", "open", "pm":
-		if ce.User.Conn == nil {
+		if !ce.User.HasSession() {
 			ce.Reply("You are not logged in. Use the `login` command to log into WhatsApp.")
 			return
 		} else if !ce.User.IsConnected() {
@@ -333,9 +333,9 @@ func (handler *CommandHandler) CommandSync(ce *CommandEvent) {
 	}
 
 	ce.Reply("Syncing contacts...")
-	user.syncPuppets()
+	user.syncPuppets(nil)
 	ce.Reply("Syncing chats...")
-	user.syncPortals(create)
+	user.syncPortals(nil, create)
 
 	ce.Reply("Sync complete.")
 }

+ 1 - 1
config/bridge.go

@@ -74,7 +74,7 @@ func (bc *BridgeConfig) setDefaults() {
 	bc.MaxConnectionAttempts = 3
 	bc.ConnectionRetryDelay = -1
 	bc.ReportConnectionRetry = true
-	bc.ContactWaitDelay = 1
+	bc.ContactWaitDelay = 30
 
 	bc.CallNotices.Start = true
 	bc.CallNotices.End = true

+ 3 - 3
example-config.yaml

@@ -75,9 +75,9 @@ bridge:
     # Whether or not the bridge should send a notice to the user's management room when it retries connecting.
     # If false, it will only report when it stops retrying.
     report_connection_retry: true
-    # Number of seconds to wait for contacts and chats to be sent at startup before syncing.
-    # If you have lots of chats, it might take more than a second.
-    contact_wait_delay: 1
+    # Maximum number of seconds to wait for chats to be sent at startup.
+    # If this is too low and you have lots of chats, it could cause backfilling to fail.
+    contact_wait_delay: 30
 
     # Whether or not to send call start/end notices to Matrix.
     call_notices:

+ 1 - 1
go.mod

@@ -16,6 +16,6 @@ require (
 )
 
 replace (
-	github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.0.2-0.20190824193805-f9a7dc8e6a5f
+	github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.0.2-0.20190824212315-7c97464c8c54
 	gopkg.in/russross/blackfriday.v2 => github.com/russross/blackfriday/v2 v2.0.1
 )

+ 2 - 0
go.sum

@@ -26,6 +26,8 @@ github.com/tulir/go-whatsapp v0.0.2-0.20190717210948-309b1b2cfa74 h1:Aqnz+nkJ/tB
 github.com/tulir/go-whatsapp v0.0.2-0.20190717210948-309b1b2cfa74/go.mod h1:u3Hdptbz3iB5y/NEoSKgsp9hBzUlm0A5OrLMVdENAX8=
 github.com/tulir/go-whatsapp v0.0.2-0.20190824193805-f9a7dc8e6a5f h1:a6GstbsMbzd3Rteo64+30/US2U6iQF6e5mvXlOVRU3I=
 github.com/tulir/go-whatsapp v0.0.2-0.20190824193805-f9a7dc8e6a5f/go.mod h1:u3Hdptbz3iB5y/NEoSKgsp9hBzUlm0A5OrLMVdENAX8=
+github.com/tulir/go-whatsapp v0.0.2-0.20190824212315-7c97464c8c54 h1:Pe+DJtXEbTe+FhmOxrY74EbqfqGjZTxxaU3iLMqo0CA=
+github.com/tulir/go-whatsapp v0.0.2-0.20190824212315-7c97464c8c54/go.mod h1:u3Hdptbz3iB5y/NEoSKgsp9hBzUlm0A5OrLMVdENAX8=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=

+ 45 - 15
user.go

@@ -55,6 +55,8 @@ type User struct {
 
 	cleanDisconnection bool
 
+	syncPortalsDone chan struct{}
+
 	messages chan PortalMessage
 	syncLock sync.Mutex
 }
@@ -141,6 +143,7 @@ func (bridge *Bridge) NewUser(dbUser *database.User) *User {
 		bridge: bridge,
 		log:    bridge.Log.Sub("User").Sub(string(dbUser.MXID)),
 
+		syncPortalsDone: make(chan struct{}, 1),
 		messages: make(chan PortalMessage, 256),
 	}
 	user.Whitelisted = user.bridge.Config.Bridge.Permissions.IsWhitelisted(user.MXID)
@@ -346,25 +349,37 @@ func (user *User) PostLogin() {
 }
 
 func (user *User) intPostLogin() {
-	dur := time.Duration(user.bridge.Config.Bridge.ContactWaitDelay) * time.Second
-	user.log.Debugfln("Waiting %s for contacts to arrive", dur)
-	// Hacky way to wait for chats and contacts to arrive automatically
-	time.Sleep(dur)
-	user.log.Debugfln("Waited %s, have %d chats and %d contacts", dur, len(user.Conn.Store.Chats), len(user.Conn.Store.Contacts))
-
 	user.createCommunity()
-	go user.syncPuppets()
-	user.syncPortals(false)
-	user.log.Debugln("Post-login sync complete, unlocking processing of incoming messages")
+
+	select {
+	case <- user.syncPortalsDone:
+		user.log.Debugln("Post-login portal sync complete, unlocking processing of incoming messages.")
+	case <- time.After(time.Duration(user.bridge.Config.Bridge.ContactWaitDelay) * time.Second):
+		user.log.Warnln("Timed out waiting for chat list to arrive! Unlocking processing of incoming messages.")
+	}
 	user.syncLock.Unlock()
 }
 
-func (user *User) syncPortals(createAll bool) {
+func (user *User) HandleChatList(chats []whatsapp.Chat) {
+	chatMap := make(map[string]whatsapp.Chat)
+	for _, chat := range user.Conn.Store.Chats {
+		chatMap[chat.Jid] = chat
+	}
+	for _, chat := range chats {
+		chatMap[chat.Jid] = chat
+	}
+	go user.syncPortals(chatMap, false)
+}
+
+func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool) {
+	if chatMap == nil {
+		chatMap = user.Conn.Store.Chats
+	}
 	user.log.Infoln("Reading chat list")
-	chats := make(ChatList, 0, len(user.Conn.Store.Chats))
+	chats := make(ChatList, 0, len(chatMap))
 	existingKeys := user.GetInCommunityMap()
-	portalKeys := make([]database.PortalKeyWithMeta, 0, len(user.Conn.Store.Chats))
-	for _, chat := range user.Conn.Store.Chats {
+	portalKeys := make([]database.PortalKeyWithMeta, 0, len(chatMap))
+	for _, chat := range chatMap {
 		ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
 		if err != nil {
 			user.log.Warnfln("Non-integer last message time in %s: %s", chat.Jid, chat.LastMessageTime)
@@ -409,11 +424,26 @@ func (user *User) syncPortals(createAll bool) {
 		}
 	}
 	user.log.Infoln("Finished syncing portals")
+	select {
+	case user.syncPortalsDone <- struct{}{}:
+	default:
+	}
 }
 
-func (user *User) syncPuppets() {
+func (user *User) HandleContactList(contacts []whatsapp.Contact) {
+	contactMap := make(map[string]whatsapp.Contact)
+	for _, contact := range contacts {
+		contactMap[contact.Jid] = contact
+	}
+	go user.syncPuppets(contactMap)
+}
+
+func (user *User) syncPuppets(contacts map[string]whatsapp.Contact) {
+	if contacts == nil {
+		contacts = user.Conn.Store.Contacts
+	}
 	user.log.Infoln("Syncing puppet info from contacts")
-	for jid, contact := range user.Conn.Store.Contacts {
+	for jid, contact := range contacts {
 		if strings.HasSuffix(jid, whatsappExt.NewUserSuffix) {
 			puppet := user.bridge.GetPuppetByJID(contact.Jid)
 			puppet.Sync(user, contact)