Pārlūkot izejas kodu

Make message handling more synchronous and fill history on portal create

Tulir Asokan 6 gadi atpakaļ
vecāks
revīzija
6f2a51410f

+ 2 - 2
go.mod

@@ -3,7 +3,7 @@ module maunium.net/go/mautrix-whatsapp
 go 1.12
 go 1.12
 
 
 require (
 require (
-	github.com/Rhymen/go-whatsapp v0.0.2-0.20190520154255-e9add85056dd
+	github.com/Rhymen/go-whatsapp v0.0.2-0.20190521203213-17d96f9c79ea
 	github.com/golang/protobuf v1.3.1 // indirect
 	github.com/golang/protobuf v1.3.1 // indirect
 	github.com/gorilla/mux v1.7.2 // indirect
 	github.com/gorilla/mux v1.7.2 // indirect
 	github.com/lib/pq v1.1.1
 	github.com/lib/pq v1.1.1
@@ -21,4 +21,4 @@ require (
 
 
 replace gopkg.in/russross/blackfriday.v2 => github.com/russross/blackfriday/v2 v2.0.1
 replace gopkg.in/russross/blackfriday.v2 => github.com/russross/blackfriday/v2 v2.0.1
 
 
-replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.0.2-0.20190520154255-e9add85056dd
+replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.0.2-0.20190521203213-17d96f9c79ea

+ 2 - 0
go.sum

@@ -76,6 +76,8 @@ github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTe
 github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
 github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
 github.com/tulir/go-whatsapp v0.0.2-0.20190520154255-e9add85056dd h1:NN9d4MwNnEAT6X9WO49W3Q5YmydKx6y5xrPCnsjEuFM=
 github.com/tulir/go-whatsapp v0.0.2-0.20190520154255-e9add85056dd h1:NN9d4MwNnEAT6X9WO49W3Q5YmydKx6y5xrPCnsjEuFM=
 github.com/tulir/go-whatsapp v0.0.2-0.20190520154255-e9add85056dd/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
 github.com/tulir/go-whatsapp v0.0.2-0.20190520154255-e9add85056dd/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
+github.com/tulir/go-whatsapp v0.0.2-0.20190521203213-17d96f9c79ea h1:cvrUAxWhx461T2E/vEMGKHy6zuA8/ml7NDYtYH/lx9A=
+github.com/tulir/go-whatsapp v0.0.2-0.20190521203213-17d96f9c79ea/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY=
 golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY=

+ 93 - 17
portal.go

@@ -102,18 +102,27 @@ func (bridge *Bridge) GetAllPortals() []*Portal {
 }
 }
 
 
 func (bridge *Bridge) NewPortal(dbPortal *database.Portal) *Portal {
 func (bridge *Bridge) NewPortal(dbPortal *database.Portal) *Portal {
-	return &Portal{
+	portal := &Portal{
 		Portal: dbPortal,
 		Portal: dbPortal,
 		bridge: bridge,
 		bridge: bridge,
 		log:    bridge.Log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)),
 		log:    bridge.Log.Sub(fmt.Sprintf("Portal/%s", dbPortal.Key)),
 
 
 		messageLocks:    make(map[types.WhatsAppMessageID]sync.Mutex),
 		messageLocks:    make(map[types.WhatsAppMessageID]sync.Mutex),
 		recentlyHandled: [recentlyHandledLength]types.WhatsAppMessageID{},
 		recentlyHandled: [recentlyHandledLength]types.WhatsAppMessageID{},
+
+		messages: make(chan PortalMessage, 128),
 	}
 	}
+	go portal.handleMessageLoop()
+	return portal
 }
 }
 
 
 const recentlyHandledLength = 100
 const recentlyHandledLength = 100
 
 
+type PortalMessage struct {
+	source *User
+	data   interface{}
+}
+
 type Portal struct {
 type Portal struct {
 	*database.Portal
 	*database.Portal
 
 
@@ -128,9 +137,47 @@ type Portal struct {
 	recentlyHandledLock  sync.Mutex
 	recentlyHandledLock  sync.Mutex
 	recentlyHandledIndex uint8
 	recentlyHandledIndex uint8
 
 
+	lastMessageTs uint64
+
+	messages chan PortalMessage
+
 	isPrivate *bool
 	isPrivate *bool
 }
 }
 
 
+func (portal *Portal) handleMessageLoop() {
+	for msg := range portal.messages {
+		if len(portal.MXID) == 0 {
+			_, isRevocation := msg.data.(whatsappExt.MessageRevocation)
+			if isRevocation {
+				continue
+			}
+			err := portal.CreateMatrixRoom(msg.source)
+			if err != nil {
+				portal.log.Errorln("Failed to create portal room:", err)
+				return
+			}
+		}
+		portal.handleMessage(msg)
+	}
+}
+
+func (portal *Portal) handleMessage(msg PortalMessage) {
+	switch data := msg.data.(type) {
+	case whatsapp.TextMessage:
+		portal.HandleTextMessage(msg.source, data)
+	case whatsapp.ImageMessage:
+		portal.HandleMediaMessage(msg.source, data.Download, data.Thumbnail, data.Info, data.Type, data.Caption)
+	case whatsapp.VideoMessage:
+		portal.HandleMediaMessage(msg.source, data.Download, data.Thumbnail, data.Info, data.Type, data.Caption)
+	case whatsapp.AudioMessage:
+		portal.HandleMediaMessage(msg.source, data.Download, nil, data.Info, data.Type, "")
+	case whatsapp.DocumentMessage:
+		portal.HandleMediaMessage(msg.source, data.Download, data.Thumbnail, data.Info, data.Type, data.Title)
+	case whatsappExt.MessageRevocation:
+		portal.HandleMessageRevoke(msg.source, data)
+	}
+}
+
 func (portal *Portal) getMessageLock(messageID types.WhatsAppMessageID) sync.Mutex {
 func (portal *Portal) getMessageLock(messageID types.WhatsAppMessageID) sync.Mutex {
 	portal.messageLocksLock.Lock()
 	portal.messageLocksLock.Lock()
 	defer portal.messageLocksLock.Unlock()
 	defer portal.messageLocksLock.Unlock()
@@ -194,16 +241,17 @@ func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo,
 	portal.recentlyHandled[index] = msg.JID
 	portal.recentlyHandled[index] = msg.JID
 }
 }
 
 
-func (portal *Portal) startHandling(id types.WhatsAppMessageID) (*sync.Mutex, bool) {
-	if portal.isRecentlyHandled(id) {
+func (portal *Portal) startHandling(info whatsapp.MessageInfo) (*sync.Mutex, bool) {
+	if portal.lastMessageTs > info.Timestamp+1 || portal.isRecentlyHandled(info.Id) {
 		return nil, false
 		return nil, false
 	}
 	}
-	lock := portal.getMessageLock(id)
+	lock := portal.getMessageLock(info.Id)
 	lock.Lock()
 	lock.Lock()
-	if portal.isDuplicate(id) {
+	if portal.isDuplicate(info.Id) {
 		lock.Unlock()
 		lock.Unlock()
 		return nil, false
 		return nil, false
 	}
 	}
+	portal.lastMessageTs = info.Timestamp
 	return &lock, true
 	return &lock, true
 }
 }
 
 
@@ -476,6 +524,27 @@ func (portal *Portal) RestrictMetadataChanges(restrict bool) {
 	}
 	}
 }
 }
 
 
+func (portal *Portal) FillHistory(user *User) error {
+	resp, err := user.Conn.LoadMessages(portal.Key.JID, "", 50)
+	if err != nil {
+		return err
+	}
+	messages, ok := resp.Content.([]interface{})
+	if !ok {
+		return fmt.Errorf("history response not list")
+	}
+	for _, rawMessage := range messages {
+		message, ok := rawMessage.(*waProto.WebMessageInfo)
+		if !ok {
+			portal.log.Warnln("Unexpected non-WebMessageInfo item in history response:", rawMessage)
+			continue
+		}
+		fmt.Println("Filling history", message.GetKey(), message.GetMessageTimestamp())
+		portal.handleMessage(PortalMessage{user, whatsapp.ParseProtoMessage(message)})
+	}
+	return nil
+}
+
 func (portal *Portal) CreateMatrixRoom(user *User) error {
 func (portal *Portal) CreateMatrixRoom(user *User) error {
 	portal.roomCreateLock.Lock()
 	portal.roomCreateLock.Lock()
 	defer portal.roomCreateLock.Unlock()
 	defer portal.roomCreateLock.Unlock()
@@ -523,6 +592,10 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
 	}
 	}
 	portal.MXID = resp.RoomID
 	portal.MXID = resp.RoomID
 	portal.Update()
 	portal.Update()
+	err = portal.FillHistory(user)
+	if err != nil {
+		portal.log.Errorln("Failed to fill history:", err)
+	}
 	return nil
 	return nil
 }
 }
 
 
@@ -605,17 +678,15 @@ func (portal *Portal) HandleMessageRevoke(user *User, message whatsappExt.Messag
 }
 }
 
 
 func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessage) {
 func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessage) {
-	lock, ok := portal.startHandling(message.Info.Id)
-	if !ok {
+	if len(portal.MXID) == 0 {
 		return
 		return
 	}
 	}
-	defer lock.Unlock()
 
 
-	err := portal.CreateMatrixRoom(source)
-	if err != nil {
-		portal.log.Errorln("Failed to create portal room:", err)
+	lock, ok := portal.startHandling(message.Info)
+	if !ok {
 		return
 		return
 	}
 	}
+	defer lock.Unlock()
 
 
 	intent := portal.GetMessageIntent(source, message.Info)
 	intent := portal.GetMessageIntent(source, message.Info)
 	if intent == nil {
 	if intent == nil {
@@ -640,17 +711,15 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa
 }
 }
 
 
 func (portal *Portal) HandleMediaMessage(source *User, download func() ([]byte, error), thumbnail []byte, info whatsapp.MessageInfo, mimeType, caption string) {
 func (portal *Portal) HandleMediaMessage(source *User, download func() ([]byte, error), thumbnail []byte, info whatsapp.MessageInfo, mimeType, caption string) {
-	lock, ok := portal.startHandling(info.Id)
-	if !ok {
+	if len(portal.MXID) == 0 {
 		return
 		return
 	}
 	}
-	defer lock.Unlock()
 
 
-	err := portal.CreateMatrixRoom(source)
-	if err != nil {
-		portal.log.Errorln("Failed to create portal room:", err)
+	lock, ok := portal.startHandling(info)
+	if !ok {
 		return
 		return
 	}
 	}
+	defer lock.Unlock()
 
 
 	intent := portal.GetMessageIntent(source, info)
 	intent := portal.GetMessageIntent(source, info)
 	if intent == nil {
 	if intent == nil {
@@ -1018,6 +1087,9 @@ func (portal *Portal) Cleanup(puppetsOnly bool) {
 		return
 		return
 	}
 	}
 	for member, _ := range members.Joined {
 	for member, _ := range members.Joined {
+		if member == intent.UserID {
+			continue
+		}
 		puppet := portal.bridge.GetPuppetByMXID(member)
 		puppet := portal.bridge.GetPuppetByMXID(member)
 		if puppet != nil {
 		if puppet != nil {
 			_, err = puppet.Intent().LeaveRoom(portal.MXID)
 			_, err = puppet.Intent().LeaveRoom(portal.MXID)
@@ -1031,6 +1103,10 @@ func (portal *Portal) Cleanup(puppetsOnly bool) {
 			}
 			}
 		}
 		}
 	}
 	}
+	_, err = intent.LeaveRoom(portal.MXID)
+	if err != nil {
+		portal.log.Errorln("Error leaving with main intent while cleaning up portal:", err)
+	}
 }
 }
 
 
 func (portal *Portal) HandleMatrixLeave(sender *User) {
 func (portal *Portal) HandleMatrixLeave(sender *User) {

+ 35 - 33
user.go

@@ -25,10 +25,11 @@ import (
 	"github.com/skip2/go-qrcode"
 	"github.com/skip2/go-qrcode"
 	log "maunium.net/go/maulogger/v2"
 	log "maunium.net/go/maulogger/v2"
 
 
-	"github.com/Rhymen/go-whatsapp"
 	"maunium.net/go/mautrix"
 	"maunium.net/go/mautrix"
 	"maunium.net/go/mautrix/format"
 	"maunium.net/go/mautrix/format"
 
 
+	"github.com/Rhymen/go-whatsapp"
+
 	"maunium.net/go/mautrix-whatsapp/database"
 	"maunium.net/go/mautrix-whatsapp/database"
 	"maunium.net/go/mautrix-whatsapp/types"
 	"maunium.net/go/mautrix-whatsapp/types"
 	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
 	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
@@ -306,6 +307,10 @@ func (user *User) HandleError(err error) {
 	_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, content)
 	_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, content)
 }
 }
 
 
+func (user *User) ShouldCallSynchronously() bool {
+	return true
+}
+
 func (user *User) HandleJSONParseError(err error) {
 func (user *User) HandleJSONParseError(err error) {
 	user.log.Errorln("WhatsApp JSON parse error:", err)
 	user.log.Errorln("WhatsApp JSON parse error:", err)
 }
 }
@@ -319,33 +324,27 @@ func (user *User) GetPortalByJID(jid types.WhatsAppID) *Portal {
 }
 }
 
 
 func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
 func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
-	portal := user.GetPortalByJID(message.Info.RemoteJid)
-	portal.HandleTextMessage(user, message)
+	user.GetPortalByJID(message.Info.RemoteJid).messages <- PortalMessage{user, message}
 }
 }
 
 
 func (user *User) HandleImageMessage(message whatsapp.ImageMessage) {
 func (user *User) HandleImageMessage(message whatsapp.ImageMessage) {
-	portal := user.GetPortalByJID(message.Info.RemoteJid)
-	portal.HandleMediaMessage(user, message.Download, message.Thumbnail, message.Info, message.Type, message.Caption)
+	user.GetPortalByJID(message.Info.RemoteJid).messages <- PortalMessage{user, message}
 }
 }
 
 
 func (user *User) HandleVideoMessage(message whatsapp.VideoMessage) {
 func (user *User) HandleVideoMessage(message whatsapp.VideoMessage) {
-	portal := user.GetPortalByJID(message.Info.RemoteJid)
-	portal.HandleMediaMessage(user, message.Download, message.Thumbnail, message.Info, message.Type, message.Caption)
+	user.GetPortalByJID(message.Info.RemoteJid).messages <- PortalMessage{user, message}
 }
 }
 
 
 func (user *User) HandleAudioMessage(message whatsapp.AudioMessage) {
 func (user *User) HandleAudioMessage(message whatsapp.AudioMessage) {
-	portal := user.GetPortalByJID(message.Info.RemoteJid)
-	portal.HandleMediaMessage(user, message.Download, nil, message.Info, message.Type, "")
+	user.GetPortalByJID(message.Info.RemoteJid).messages <- PortalMessage{user, message}
 }
 }
 
 
 func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
 func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
-	portal := user.GetPortalByJID(message.Info.RemoteJid)
-	portal.HandleMediaMessage(user, message.Download, message.Thumbnail, message.Info, message.Type, message.Title)
+	user.GetPortalByJID(message.Info.RemoteJid).messages <- PortalMessage{user, message}
 }
 }
 
 
 func (user *User) HandleMessageRevoke(message whatsappExt.MessageRevocation) {
 func (user *User) HandleMessageRevoke(message whatsappExt.MessageRevocation) {
-	portal := user.GetPortalByJID(message.RemoteJid)
-	portal.HandleMessageRevoke(user, message)
+	user.GetPortalByJID(message.RemoteJid).messages <- PortalMessage{user, message}
 }
 }
 
 
 func (user *User) HandlePresence(info whatsappExt.Presence) {
 func (user *User) HandlePresence(info whatsappExt.Presence) {
@@ -382,17 +381,20 @@ func (user *User) HandleMsgInfo(info whatsappExt.MsgInfo) {
 			return
 			return
 		}
 		}
 
 
-		intent := user.bridge.GetPuppetByJID(info.SenderJID).Intent()
-		for _, id := range info.IDs {
-			msg := user.bridge.DB.Message.GetByJID(portal.Key, id)
-			if msg == nil {
-				continue
+		go func() {
+			intent := user.bridge.GetPuppetByJID(info.SenderJID).Intent()
+			for _, id := range info.IDs {
+				msg := user.bridge.DB.Message.GetByJID(portal.Key, id)
+				if msg == nil {
+					continue
+				}
+
+				err := intent.MarkRead(portal.MXID, msg.MXID)
+				if err != nil {
+					user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err)
+				}
 			}
 			}
-			err := intent.MarkRead(portal.MXID, msg.MXID)
-			if err != nil {
-				user.log.Warnln("Failed to mark message %s as read by %s: %v", msg.MXID, info.SenderJID, err)
-			}
-		}
+		}()
 	}
 	}
 }
 }
 
 
@@ -401,10 +403,10 @@ func (user *User) HandleCommand(cmd whatsappExt.Command) {
 	case whatsappExt.CommandPicture:
 	case whatsappExt.CommandPicture:
 		if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) {
 		if strings.HasSuffix(cmd.JID, whatsappExt.NewUserSuffix) {
 			puppet := user.bridge.GetPuppetByJID(cmd.JID)
 			puppet := user.bridge.GetPuppetByJID(cmd.JID)
-			puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
+			go puppet.UpdateAvatar(user, cmd.ProfilePicInfo)
 		} else {
 		} else {
 			portal := user.GetPortalByJID(cmd.JID)
 			portal := user.GetPortalByJID(cmd.JID)
-			portal.UpdateAvatar(user, cmd.ProfilePicInfo)
+			go portal.UpdateAvatar(user, cmd.ProfilePicInfo)
 		}
 		}
 	case whatsappExt.CommandDisconnect:
 	case whatsappExt.CommandDisconnect:
 		var msg string
 		var msg string
@@ -416,7 +418,7 @@ func (user *User) HandleCommand(cmd whatsappExt.Command) {
 			msg = fmt.Sprintf("\u26a0 Your WhatsApp connection was closed by the server (reason code: %s).\n\n"+
 			msg = fmt.Sprintf("\u26a0 Your WhatsApp connection was closed by the server (reason code: %s).\n\n"+
 				"Use the `reconnect` command to reconnect.", cmd.Kind)
 				"Use the `reconnect` command to reconnect.", cmd.Kind)
 		}
 		}
-		_, _ = user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, format.RenderMarkdown(msg))
+		go user.bridge.Bot.SendMessageEvent(user.ManagementRoom, mautrix.EventMessage, format.RenderMarkdown(msg))
 	}
 	}
 }
 }
 
 
@@ -432,19 +434,19 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
 
 
 	switch cmd.Data.Action {
 	switch cmd.Data.Action {
 	case whatsappExt.ChatActionNameChange:
 	case whatsappExt.ChatActionNameChange:
-		portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID)
+		go portal.UpdateName(cmd.Data.NameChange.Name, cmd.Data.SenderJID)
 	case whatsappExt.ChatActionAddTopic:
 	case whatsappExt.ChatActionAddTopic:
-		portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID)
+		go portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID)
 	case whatsappExt.ChatActionRemoveTopic:
 	case whatsappExt.ChatActionRemoveTopic:
-		portal.UpdateTopic("", cmd.Data.SenderJID)
+		go portal.UpdateTopic("", cmd.Data.SenderJID)
 	case whatsappExt.ChatActionPromote:
 	case whatsappExt.ChatActionPromote:
-		portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, true)
+		go portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, true)
 	case whatsappExt.ChatActionDemote:
 	case whatsappExt.ChatActionDemote:
-		portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, false)
+		go portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, false)
 	case whatsappExt.ChatActionAnnounce:
 	case whatsappExt.ChatActionAnnounce:
-		portal.RestrictMessageSending(cmd.Data.Announce)
+		go portal.RestrictMessageSending(cmd.Data.Announce)
 	case whatsappExt.ChatActionRestrict:
 	case whatsappExt.ChatActionRestrict:
-		portal.RestrictMetadataChanges(cmd.Data.Restrict)
+		go portal.RestrictMetadataChanges(cmd.Data.Restrict)
 	}
 	}
 }
 }
 
 

+ 6 - 1
whatsapp-ext/chat.go

@@ -142,6 +142,11 @@ func (ext *ExtendedConn) handleMessageChatUpdate(message []byte) {
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		go chatUpdateHandler.HandleChatUpdate(event)
+
+		if ext.shouldCallSynchronously(chatUpdateHandler) {
+			chatUpdateHandler.HandleChatUpdate(event)
+		} else {
+			go chatUpdateHandler.HandleChatUpdate(event)
+		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/cmd.go

@@ -59,6 +59,11 @@ func (ext *ExtendedConn) handleMessageCommand(message []byte) {
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		go commandHandler.HandleCommand(event)
+
+		if ext.shouldCallSynchronously(commandHandler) {
+			commandHandler.HandleCommand(event)
+		} else {
+			go commandHandler.HandleCommand(event)
+		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/conn.go

@@ -55,6 +55,11 @@ func (ext *ExtendedConn) handleMessageConn(message []byte) {
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		connInfoHandler.HandleConnInfo(event)
+
+		if ext.shouldCallSynchronously(connInfoHandler) {
+			connInfoHandler.HandleConnInfo(event)
+		} else {
+			go connInfoHandler.HandleConnInfo(event)
+		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/jsonmessage.go

@@ -96,7 +96,12 @@ func (ext *ExtendedConn) HandleJsonMessage(message string) {
 			if !ok {
 			if !ok {
 				continue
 				continue
 			}
 			}
-			ujmHandler.HandleUnhandledJSONMessage(message)
+
+			if ext.shouldCallSynchronously(ujmHandler) {
+				ujmHandler.HandleUnhandledJSONMessage(message)
+			} else {
+				go ujmHandler.HandleUnhandledJSONMessage(message)
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/msginfo.go

@@ -85,6 +85,11 @@ func (ext *ExtendedConn) handleMessageMsgInfo(msgType JSONMessageType, message [
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		go msgInfoHandler.HandleMsgInfo(event)
+
+		if ext.shouldCallSynchronously(msgInfoHandler) {
+			msgInfoHandler.HandleMsgInfo(event)
+		} else {
+			go msgInfoHandler.HandleMsgInfo(event)
+		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/presence.go

@@ -62,6 +62,11 @@ func (ext *ExtendedConn) handleMessagePresence(message []byte) {
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		go presenceHandler.HandlePresence(event)
+
+		if ext.shouldCallSynchronously(presenceHandler) {
+			presenceHandler.HandlePresence(event)
+		} else {
+			go presenceHandler.HandlePresence(event)
+		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/props.go

@@ -63,6 +63,11 @@ func (ext *ExtendedConn) handleMessageProps(message []byte) {
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		go protocolPropsHandler.HandleProtocolProps(event)
+
+		if ext.shouldCallSynchronously(protocolPropsHandler) {
+			protocolPropsHandler.HandleProtocolProps(event)
+		} else {
+			go protocolPropsHandler.HandleProtocolProps(event)
+		}
 	}
 	}
 }
 }

+ 7 - 2
whatsapp-ext/protomessage.go

@@ -35,7 +35,7 @@ type MessageRevocation struct {
 
 
 func (ext *ExtendedConn) HandleRawMessage(message *proto.WebMessageInfo) {
 func (ext *ExtendedConn) HandleRawMessage(message *proto.WebMessageInfo) {
 	protoMsg := message.GetMessage().GetProtocolMessage()
 	protoMsg := message.GetMessage().GetProtocolMessage()
-	if protoMsg.GetType() == proto.ProtocolMessage_REVOKE {
+	if protoMsg != nil && protoMsg.GetType() == proto.ProtocolMessage_REVOKE {
 		key := protoMsg.GetKey()
 		key := protoMsg.GetKey()
 		deletedMessage := MessageRevocation{
 		deletedMessage := MessageRevocation{
 			Id:          key.GetId(),
 			Id:          key.GetId(),
@@ -48,7 +48,12 @@ func (ext *ExtendedConn) HandleRawMessage(message *proto.WebMessageInfo) {
 			if !ok {
 			if !ok {
 				continue
 				continue
 			}
 			}
-			mrHandler.HandleMessageRevoke(deletedMessage)
+
+			if ext.shouldCallSynchronously(mrHandler) {
+				mrHandler.HandleMessageRevoke(deletedMessage)
+			} else {
+				go mrHandler.HandleMessageRevoke(deletedMessage)
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 6 - 1
whatsapp-ext/stream.go

@@ -58,6 +58,11 @@ func (ext *ExtendedConn) handleMessageStream(message []json.RawMessage) {
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		go streamHandler.HandleStreamEvent(event)
+
+		if ext.shouldCallSynchronously(streamHandler) {
+			streamHandler.HandleStreamEvent(event)
+		} else {
+			go streamHandler.HandleStreamEvent(event)
+		}
 	}
 	}
 }
 }

+ 9 - 0
whatsapp-ext/whatsapp.go

@@ -46,6 +46,15 @@ func ExtendConn(conn *whatsapp.Conn) *ExtendedConn {
 	return ext
 	return ext
 }
 }
 
 
+func (ext *ExtendedConn) shouldCallSynchronously(handler whatsapp.Handler) bool {
+	sh, ok := handler.(whatsapp.SyncHandler)
+	return ok && sh.ShouldCallSynchronously()
+}
+
+func (ext *ExtendedConn) ShouldCallSynchronously() bool {
+	return true
+}
+
 type GroupInfo struct {
 type GroupInfo struct {
 	JID      string `json:"jid"`
 	JID      string `json:"jid"`
 	OwnerJID string `json:"owner"`
 	OwnerJID string `json:"owner"`