ソースを参照

Add WhatsApp->Matrix read receipts and phone connection notifications

Tulir Asokan 6 年 前
コミット
1f87deb317

+ 1 - 1
ROADMAP.md

@@ -28,7 +28,7 @@
   * [x] Avatars
   * [x] Avatars
   * [ ] Presence
   * [ ] Presence
   * [ ] Typing notifications
   * [ ] Typing notifications
-  * [ ] Read receipts
+  * [x] Read receipts
   * [ ] Admin/superadmin status
   * [ ] Admin/superadmin status
   * [ ] Membership actions
   * [ ] Membership actions
     * [ ] Invite
     * [ ] Invite

+ 1 - 1
database/message.go

@@ -59,7 +59,7 @@ func (mq *MessageQuery) GetAll(owner types.MatrixUserID) (messages []*Message) {
 }
 }
 
 
 func (mq *MessageQuery) GetByJID(owner types.MatrixUserID, jid types.WhatsAppMessageID) *Message {
 func (mq *MessageQuery) GetByJID(owner types.MatrixUserID, jid types.WhatsAppMessageID) *Message {
-	return mq.get("SELECT * FROM message WHERE jid=?", jid)
+	return mq.get("SELECT * FROM message WHERE owner=? AND jid=?", owner, jid)
 }
 }
 
 
 func (mq *MessageQuery) GetByMXID(mxid types.MatrixEventID) *Message {
 func (mq *MessageQuery) GetByMXID(mxid types.MatrixEventID) *Message {

+ 2 - 2
database/puppet.go

@@ -93,7 +93,7 @@ func (puppet *Puppet) Insert() error {
 	_, err := puppet.db.Exec("INSERT INTO puppet VALUES (?, ?, ?, ?)",
 	_, err := puppet.db.Exec("INSERT INTO puppet VALUES (?, ?, ?, ?)",
 		puppet.JID, puppet.Receiver, puppet.Displayname, puppet.Avatar)
 		puppet.JID, puppet.Receiver, puppet.Displayname, puppet.Avatar)
 	if err != nil {
 	if err != nil {
-		puppet.log.Errorln("Failed to insert %s->%s: %v", puppet.JID, puppet.Receiver, err)
+		puppet.log.Errorfln("Failed to insert %s->%s: %v", puppet.JID, puppet.Receiver, err)
 	}
 	}
 	return err
 	return err
 }
 }
@@ -103,7 +103,7 @@ func (puppet *Puppet) Update() error {
 		puppet.Displayname, puppet.Avatar,
 		puppet.Displayname, puppet.Avatar,
 		puppet.JID, puppet.Receiver)
 		puppet.JID, puppet.Receiver)
 	if err != nil {
 	if err != nil {
-		puppet.log.Errorln("Failed to update %s->%s: %v", puppet.JID, puppet.Receiver, err)
+		puppet.log.Errorfln("Failed to update %s->%s: %v", puppet.JID, puppet.Receiver, err)
 	}
 	}
 	return err
 	return err
 }
 }

+ 52 - 25
portal.go

@@ -17,21 +17,24 @@
 package main
 package main
 
 
 import (
 import (
-	"maunium.net/go/mautrix-whatsapp/database"
-	log "maunium.net/go/maulogger"
+	"bytes"
+	"encoding/hex"
 	"fmt"
 	"fmt"
-	"maunium.net/go/mautrix-whatsapp/types"
-	"maunium.net/go/gomatrix"
+	"image"
+	"math/rand"
+	"mime"
+	"net/http"
 	"strings"
 	"strings"
-	"maunium.net/go/mautrix-appservice"
-	"github.com/Rhymen/go-whatsapp"
 	"sync"
 	"sync"
-	"net/http"
-	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
-	"mime"
-	"image"
-	"bytes"
+
+	"github.com/Rhymen/go-whatsapp"
+	"maunium.net/go/gomatrix"
 	"maunium.net/go/gomatrix/format"
 	"maunium.net/go/gomatrix/format"
+	log "maunium.net/go/maulogger"
+	"maunium.net/go/mautrix-appservice"
+	"maunium.net/go/mautrix-whatsapp/database"
+	"maunium.net/go/mautrix-whatsapp/types"
+	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
 )
 )
 
 
 func (user *User) GetPortalByMXID(mxid types.MatrixRoomID) *Portal {
 func (user *User) GetPortalByMXID(mxid types.MatrixRoomID) *Portal {
@@ -221,7 +224,7 @@ func (portal *Portal) CreateMatrixRoom() error {
 	name := portal.Name
 	name := portal.Name
 	topic := portal.Topic
 	topic := portal.Topic
 	isPrivateChat := false
 	isPrivateChat := false
-	if strings.HasSuffix(portal.JID, "s.whatsapp.net") {
+	if strings.HasSuffix(portal.JID, whatsapp_ext.NewUserSuffix) {
 		puppet := portal.user.GetPuppetByJID(portal.JID)
 		puppet := portal.user.GetPuppetByJID(portal.JID)
 		name = puppet.Displayname
 		name = puppet.Displayname
 		topic = "WhatsApp private chat"
 		topic = "WhatsApp private chat"
@@ -244,7 +247,7 @@ func (portal *Portal) CreateMatrixRoom() error {
 }
 }
 
 
 func (portal *Portal) IsPrivateChat() bool {
 func (portal *Portal) IsPrivateChat() bool {
-	return strings.HasSuffix(portal.JID, puppetJIDStrippedSuffix)
+	return strings.HasSuffix(portal.JID, whatsapp_ext.NewUserSuffix)
 }
 }
 
 
 func (portal *Portal) MainIntent() *appservice.IntentAPI {
 func (portal *Portal) MainIntent() *appservice.IntentAPI {
@@ -282,13 +285,18 @@ func (portal *Portal) GetMessageIntent(info whatsapp.MessageInfo) *appservice.In
 	return puppet.Intent()
 	return puppet.Intent()
 }
 }
 
 
-func (portal *Portal) GetRelations(info whatsapp.MessageInfo) (reply gomatrix.RelatesTo) {
+func (portal *Portal) SetReply(content *gomatrix.Content, info whatsapp.MessageInfo) {
 	if len(info.QuotedMessageID) == 0 {
 	if len(info.QuotedMessageID) == 0 {
 		return
 		return
 	}
 	}
 	message := portal.bridge.DB.Message.GetByJID(portal.Owner, info.QuotedMessageID)
 	message := portal.bridge.DB.Message.GetByJID(portal.Owner, info.QuotedMessageID)
 	if message != nil {
 	if message != nil {
-		reply.InReplyTo.EventID = message.MXID
+		event, err := portal.MainIntent().GetEvent(portal.MXID, message.MXID)
+		if err != nil {
+			portal.log.Warnln("Failed to get reply target:", err)
+			return
+		}
+		content.SetReply(event)
 	}
 	}
 	return
 	return
 
 
@@ -299,18 +307,24 @@ func (portal *Portal) HandleTextMessage(message whatsapp.TextMessage) {
 		return
 		return
 	}
 	}
 
 
-	portal.CreateMatrixRoom()
+	err := portal.CreateMatrixRoom()
+	if err != nil {
+		portal.log.Errorln("Failed to create portal room:", err)
+		return
+	}
 
 
 	intent := portal.GetMessageIntent(message.Info)
 	intent := portal.GetMessageIntent(message.Info)
 	if intent == nil {
 	if intent == nil {
 		return
 		return
 	}
 	}
 
 
-	resp, err := intent.SendMassagedMessageEvent(portal.MXID, gomatrix.EventMessage, gomatrix.Content{
-		Body:      message.Text,
-		MsgType:   gomatrix.MsgText,
-		RelatesTo: portal.GetRelations(message.Info),
-	}, int64(message.Info.Timestamp*1000))
+	content := gomatrix.Content{
+		Body:    message.Text,
+		MsgType: gomatrix.MsgText,
+	}
+	portal.SetReply(&content, message.Info)
+
+	resp, err := intent.SendMassagedMessageEvent(portal.MXID, gomatrix.EventMessage, content, int64(message.Info.Timestamp*1000))
 	if err != nil {
 	if err != nil {
 		portal.log.Errorfln("Failed to handle message %s: %v", message.Info.Id, err)
 		portal.log.Errorfln("Failed to handle message %s: %v", message.Info.Id, err)
 		return
 		return
@@ -324,7 +338,11 @@ func (portal *Portal) HandleMediaMessage(download func() ([]byte, error), thumbn
 		return
 		return
 	}
 	}
 
 
-	portal.CreateMatrixRoom()
+	err := portal.CreateMatrixRoom()
+	if err != nil {
+		portal.log.Errorln("Failed to create portal room:", err)
+		return
+	}
 
 
 	intent := portal.GetMessageIntent(info)
 	intent := portal.GetMessageIntent(info)
 	if intent == nil {
 	if intent == nil {
@@ -353,12 +371,12 @@ func (portal *Portal) HandleMediaMessage(download func() ([]byte, error), thumbn
 	content := gomatrix.Content{
 	content := gomatrix.Content{
 		Body: caption,
 		Body: caption,
 		URL:  uploaded.ContentURI,
 		URL:  uploaded.ContentURI,
-		Info: gomatrix.FileInfo{
+		Info: &gomatrix.FileInfo{
 			Size:     len(data),
 			Size:     len(data),
 			MimeType: mimeType,
 			MimeType: mimeType,
 		},
 		},
-		RelatesTo: portal.GetRelations(info),
 	}
 	}
+	portal.SetReply(&content, info)
 
 
 	if thumbnail != nil {
 	if thumbnail != nil {
 		thumbnailMime := http.DetectContentType(thumbnail)
 		thumbnailMime := http.DetectContentType(thumbnail)
@@ -422,6 +440,12 @@ var htmlParser = format.HTMLParser{
 	},
 	},
 }
 }
 
 
+func makeMessageID() string {
+	b := make([]byte, 10)
+	rand.Read(b)
+	return strings.ToUpper(hex.EncodeToString(b))
+}
+
 func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) {
 func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) {
 	var err error
 	var err error
 	switch evt.Content.MsgType {
 	switch evt.Content.MsgType {
@@ -430,18 +454,21 @@ func (portal *Portal) HandleMatrixMessage(evt *gomatrix.Event) {
 		if evt.Content.Format == gomatrix.FormatHTML {
 		if evt.Content.Format == gomatrix.FormatHTML {
 			text = htmlParser.Parse(evt.Content.FormattedBody)
 			text = htmlParser.Parse(evt.Content.FormattedBody)
 		}
 		}
+		id := makeMessageID()
 		err = portal.user.Conn.Send(whatsapp.TextMessage{
 		err = portal.user.Conn.Send(whatsapp.TextMessage{
 			Text: text,
 			Text: text,
 			Info: whatsapp.MessageInfo{
 			Info: whatsapp.MessageInfo{
+				Id: id,
 				RemoteJid: portal.JID,
 				RemoteJid: portal.JID,
 			},
 			},
 		})
 		})
+		portal.MarkHandled(id, evt.ID)
 	default:
 	default:
 		portal.log.Debugln("Unhandled Matrix event:", evt)
 		portal.log.Debugln("Unhandled Matrix event:", evt)
 		return
 		return
 	}
 	}
 	if err != nil {
 	if err != nil {
-		portal.log.Errorln("Error handling Matrix event %s: %v", evt.ID, err)
+		portal.log.Errorfln("Error handling Matrix event %s: %v", evt.ID, err)
 	} else {
 	} else {
 		portal.log.Debugln("Handled Matrix event:", evt)
 		portal.log.Debugln("Handled Matrix event:", evt)
 	}
 	}

+ 9 - 9
puppet.go

@@ -17,19 +17,19 @@
 package main
 package main
 
 
 import (
 import (
-	"maunium.net/go/mautrix-whatsapp/database"
-	log "maunium.net/go/maulogger"
 	"fmt"
 	"fmt"
+	"net/http"
 	"regexp"
 	"regexp"
-	"maunium.net/go/mautrix-whatsapp/types"
 	"strings"
 	"strings"
-	"maunium.net/go/mautrix-appservice"
+
 	"github.com/Rhymen/go-whatsapp"
 	"github.com/Rhymen/go-whatsapp"
-	"net/http"
+	log "maunium.net/go/maulogger"
+	"maunium.net/go/mautrix-appservice"
+	"maunium.net/go/mautrix-whatsapp/database"
+	"maunium.net/go/mautrix-whatsapp/types"
+	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
 )
 )
 
 
-const puppetJIDStrippedSuffix = "@s.whatsapp.net"
-
 func (bridge *Bridge) ParsePuppetMXID(mxid types.MatrixUserID) (types.MatrixUserID, types.WhatsAppID, bool) {
 func (bridge *Bridge) ParsePuppetMXID(mxid types.MatrixUserID) (types.MatrixUserID, types.WhatsAppID, bool) {
 	userIDRegex, err := regexp.Compile(fmt.Sprintf("^@%s:%s$",
 	userIDRegex, err := regexp.Compile(fmt.Sprintf("^@%s:%s$",
 		bridge.Config.Bridge.FormatUsername("([0-9]+)", "([0-9]+)"),
 		bridge.Config.Bridge.FormatUsername("([0-9]+)", "([0-9]+)"),
@@ -47,7 +47,7 @@ func (bridge *Bridge) ParsePuppetMXID(mxid types.MatrixUserID) (types.MatrixUser
 	receiver = strings.Replace(receiver, "=40", "@", 1)
 	receiver = strings.Replace(receiver, "=40", "@", 1)
 	colonIndex := strings.LastIndex(receiver, "=3")
 	colonIndex := strings.LastIndex(receiver, "=3")
 	receiver = receiver[:colonIndex] + ":" + receiver[colonIndex+len("=3"):]
 	receiver = receiver[:colonIndex] + ":" + receiver[colonIndex+len("=3"):]
-	jid := types.WhatsAppID(match[2] + puppetJIDStrippedSuffix)
+	jid := types.WhatsAppID(match[2] + whatsapp_ext.NewUserSuffix)
 	return receiver, jid, true
 	return receiver, jid, true
 }
 }
 
 
@@ -120,7 +120,7 @@ func (user *User) NewPuppet(dbPuppet *database.Puppet) *Puppet {
 				dbPuppet.Receiver,
 				dbPuppet.Receiver,
 				strings.Replace(
 				strings.Replace(
 					dbPuppet.JID,
 					dbPuppet.JID,
-					puppetJIDStrippedSuffix, "", 1)),
+					whatsapp_ext.NewUserSuffix, "", 1)),
 			user.bridge.Config.Homeserver.Domain),
 			user.bridge.Config.Homeserver.Domain),
 	}
 	}
 }
 }

+ 57 - 5
user.go

@@ -17,14 +17,16 @@
 package main
 package main
 
 
 import (
 import (
-	"maunium.net/go/mautrix-whatsapp/database"
-	"github.com/Rhymen/go-whatsapp"
+	"fmt"
+	"strings"
+	"sync"
 	"time"
 	"time"
+
+	"github.com/Rhymen/go-whatsapp"
 	"github.com/skip2/go-qrcode"
 	"github.com/skip2/go-qrcode"
 	log "maunium.net/go/maulogger"
 	log "maunium.net/go/maulogger"
+	"maunium.net/go/mautrix-whatsapp/database"
 	"maunium.net/go/mautrix-whatsapp/types"
 	"maunium.net/go/mautrix-whatsapp/types"
-	"strings"
-	"sync"
 	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
 	"maunium.net/go/mautrix-whatsapp/whatsapp-ext"
 )
 )
 
 
@@ -186,7 +188,7 @@ func (user *User) Sync() {
 	user.log.Debugln("Syncing...")
 	user.log.Debugln("Syncing...")
 	user.Conn.Contacts()
 	user.Conn.Contacts()
 	for jid, contact := range user.Conn.Store.Contacts {
 	for jid, contact := range user.Conn.Store.Contacts {
-		if strings.HasSuffix(jid, puppetJIDStrippedSuffix) {
+		if strings.HasSuffix(jid, whatsapp_ext.NewUserSuffix) {
 			puppet := user.GetPuppetByJID(contact.Jid)
 			puppet := user.GetPuppetByJID(contact.Jid)
 			puppet.Sync(contact)
 			puppet.Sync(contact)
 		}
 		}
@@ -205,6 +207,10 @@ func (user *User) HandleError(err error) {
 	user.log.Errorln("WhatsApp error:", err)
 	user.log.Errorln("WhatsApp error:", err)
 }
 }
 
 
+func (user *User) HandleJSONParseError(err error) {
+	user.log.Errorln("WhatsApp JSON parse error:", err)
+}
+
 func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
 func (user *User) HandleTextMessage(message whatsapp.TextMessage) {
 	user.log.Debugln("Received text message:", message)
 	user.log.Debugln("Received text message:", message)
 	portal := user.GetPortalByJID(message.Info.RemoteJid)
 	portal := user.GetPortalByJID(message.Info.RemoteJid)
@@ -231,6 +237,52 @@ func (user *User) HandleDocumentMessage(message whatsapp.DocumentMessage) {
 	portal.HandleMediaMessage(message.Download, message.Thumbnail, message.Info, message.Type, message.Title)
 	portal.HandleMediaMessage(message.Download, message.Thumbnail, message.Info, message.Type, message.Title)
 }
 }
 
 
+func (user *User) HandleStreamEvent(stream whatsapp_ext.StreamEvent) {
+	if len(user.ManagementRoom) == 0 {
+		return
+	}
+	switch stream.Type {
+	case whatsapp_ext.StreamSleep:
+		user.bridge.AppService.BotIntent().SendNotice(user.ManagementRoom, "WhatsApp client disconnected.")
+	case whatsapp_ext.StreamUpdate:
+		if user.Conn.Info != nil && user.Conn.Info.Phone != nil {
+			user.bridge.AppService.BotIntent().SendNotice(user.ManagementRoom,
+				fmt.Sprintf("WhatsApp v%s client connected from %s %s (OS v%s).",
+					user.Conn.Info.Phone.WaVersion, user.Conn.Info.Phone.DeviceManufacturer, user.Conn.Info.Phone.DeviceModel, user.Conn.Info.Phone.OsVersion))
+		}
+	}
+}
+
+func (user *User) HandleConnInfo(info whatsapp_ext.ConnInfo) {
+	if len(user.ManagementRoom) > 0 && len(info.ProtocolVersion) > 0 {
+		user.bridge.AppService.BotIntent().SendNotice(user.ManagementRoom,
+			fmt.Sprintf("WhatsApp v%s client connected from %s %s (OS v%s).",
+				info.Phone.WhatsAppVersion, info.Phone.DeviceManufacturer, info.Phone.DeviceModel, info.Phone.OSVersion))
+	}
+}
+
+func (user *User) HandleMsgInfo(info whatsapp_ext.MsgInfo) {
+	if (info.Command == whatsapp_ext.MsgInfoCommandAck || info.Command == whatsapp_ext.MsgInfoCommandAcks) && info.Acknowledgement == whatsapp_ext.AckMessageRead {
+		portal := user.GetPortalByJID(info.ToJID)
+		if len(portal.MXID) == 0 {
+			return
+		}
+
+		intent := user.GetPuppetByJID(info.SenderJID).Intent()
+		user.log.Debugln(info.IDs)
+		for _, id := range info.IDs {
+			msg := user.bridge.DB.Message.GetByJID(user.ID, 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)
+			}
+		}
+	}
+}
+
 func (user *User) HandleJsonMessage(message string) {
 func (user *User) HandleJsonMessage(message string) {
 	user.log.Debugln("JSON message:", message)
 	user.log.Debugln("JSON message:", message)
 }
 }

+ 4 - 3
whatsapp-ext/conn.go

@@ -17,8 +17,9 @@
 package whatsapp_ext
 package whatsapp_ext
 
 
 import (
 import (
-	"github.com/Rhymen/go-whatsapp"
 	"encoding/json"
 	"encoding/json"
+
+	"github.com/Rhymen/go-whatsapp"
 )
 )
 
 
 type ConnInfo struct {
 type ConnInfo struct {
@@ -26,8 +27,8 @@ type ConnInfo struct {
 	BinaryVersion   int   `json:"binVersion"`
 	BinaryVersion   int   `json:"binVersion"`
 	Phone           struct {
 	Phone           struct {
 		WhatsAppVersion    string `json:"wa_version"`
 		WhatsAppVersion    string `json:"wa_version"`
-		MCC                int    `json:"mcc"`
-		MNC                int    `json:"mnc"`
+		MCC                string `json:"mcc"`
+		MNC                string `json:"mnc"`
 		OSVersion          string `json:"os_version"`
 		OSVersion          string `json:"os_version"`
 		DeviceManufacturer string `json:"device_manufacturer"`
 		DeviceManufacturer string `json:"device_manufacturer"`
 		DeviceModel        string `json:"device_model"`
 		DeviceModel        string `json:"device_model"`

+ 5 - 3
whatsapp-ext/jsonmessage.go

@@ -18,6 +18,7 @@ package whatsapp_ext
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+
 	"github.com/Rhymen/go-whatsapp"
 	"github.com/Rhymen/go-whatsapp"
 )
 )
 
 
@@ -27,6 +28,7 @@ type JSONMessageType string
 
 
 const (
 const (
 	MessageMsgInfo  JSONMessageType = "MsgInfo"
 	MessageMsgInfo  JSONMessageType = "MsgInfo"
+	MessageMsg      JSONMessageType = "Msg"
 	MessagePresence JSONMessageType = "Presence"
 	MessagePresence JSONMessageType = "Presence"
 	MessageStream   JSONMessageType = "Stream"
 	MessageStream   JSONMessageType = "Stream"
 	MessageConn     JSONMessageType = "Conn"
 	MessageConn     JSONMessageType = "Conn"
@@ -76,11 +78,11 @@ func (ext *ExtendedConn) HandleJsonMessage(message string) {
 	case MessageStream:
 	case MessageStream:
 		ext.handleMessageStream(msg[1:])
 		ext.handleMessageStream(msg[1:])
 	case MessageConn:
 	case MessageConn:
-		ext.handleMessageProps(msg[1])
+		ext.handleMessageConn(msg[1])
 	case MessageProps:
 	case MessageProps:
 		ext.handleMessageProps(msg[1])
 		ext.handleMessageProps(msg[1])
-	case MessageMsgInfo:
-		ext.handleMessageMsgInfo(msg[1])
+	case MessageMsgInfo, MessageMsg:
+		ext.handleMessageMsgInfo(msgType, msg[1])
 	default:
 	default:
 		for _, handler := range ext.handlers {
 		for _, handler := range ext.handlers {
 			ujmHandler, ok := handler.(UnhandledJSONMessageHandler)
 			ujmHandler, ok := handler.(UnhandledJSONMessageHandler)

+ 38 - 11
whatsapp-ext/msginfo.go

@@ -17,25 +17,49 @@
 package whatsapp_ext
 package whatsapp_ext
 
 
 import (
 import (
-	"github.com/Rhymen/go-whatsapp"
 	"encoding/json"
 	"encoding/json"
 	"strings"
 	"strings"
+
+	"github.com/Rhymen/go-whatsapp"
 )
 )
 
 
 type MsgInfoCommand string
 type MsgInfoCommand string
 
 
 const (
 const (
-	MsgInfoCommandAcknowledge MsgInfoCommand = "ack"
+	MsgInfoCommandAck  MsgInfoCommand = "ack"
+	MsgInfoCommandAcks MsgInfoCommand = "acks"
+)
+
+type Acknowledgement int
+
+const (
+	AckMessageSent      Acknowledgement = 1
+	AckMessageDelivered Acknowledgement = 2
+	AckMessageRead      Acknowledgement = 3
 )
 )
 
 
+type JSONStringOrArray []string
+
+func (jsoa *JSONStringOrArray) UnmarshalJSON(data []byte) error {
+	var str string
+	if json.Unmarshal(data, &str) == nil {
+		*jsoa = []string{str}
+		return nil
+	}
+	var strs []string
+	json.Unmarshal(data, &strs)
+	*jsoa = strs
+	return nil
+}
+
 type MsgInfo struct {
 type MsgInfo struct {
-	Command         MsgInfoCommand `json:"cmd"`
-	ID              string         `json:"id"`
-	Acknowledgement int            `json:"ack"`
-	MessageFromJID  string         `json:"from"`
-	SenderJID       string         `json:"participant"`
-	ToJID           string         `json:"to"`
-	Timestamp       int64          `json:"t"`
+	Command         MsgInfoCommand    `json:"cmd"`
+	IDs             JSONStringOrArray `json:"id"`
+	Acknowledgement Acknowledgement   `json:"ack"`
+	MessageFromJID  string            `json:"from"`
+	SenderJID       string            `json:"participant"`
+	ToJID           string            `json:"to"`
+	Timestamp       int64             `json:"t"`
 }
 }
 
 
 type MsgInfoHandler interface {
 type MsgInfoHandler interface {
@@ -43,7 +67,7 @@ type MsgInfoHandler interface {
 	HandleMsgInfo(MsgInfo)
 	HandleMsgInfo(MsgInfo)
 }
 }
 
 
-func (ext *ExtendedConn) handleMessageMsgInfo(message []byte) {
+func (ext *ExtendedConn) handleMessageMsgInfo(msgType JSONMessageType, message []byte) {
 	var event MsgInfo
 	var event MsgInfo
 	err := json.Unmarshal(message, &event)
 	err := json.Unmarshal(message, &event)
 	if err != nil {
 	if err != nil {
@@ -53,11 +77,14 @@ func (ext *ExtendedConn) handleMessageMsgInfo(message []byte) {
 	event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
 	event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
 	event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
 	event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
 	event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
 	event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
+	if msgType == MessageMsg {
+		event.SenderJID = event.MessageFromJID
+	}
 	for _, handler := range ext.handlers {
 	for _, handler := range ext.handlers {
 		msgInfoHandler, ok := handler.(MsgInfoHandler)
 		msgInfoHandler, ok := handler.(MsgInfoHandler)
 		if !ok {
 		if !ok {
 			continue
 			continue
 		}
 		}
-		msgInfoHandler.HandleMsgInfo(event)
+		go msgInfoHandler.HandleMsgInfo(event)
 	}
 	}
 }
 }

+ 1 - 1
whatsapp-ext/presence.go

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

+ 1 - 1
whatsapp-ext/props.go

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

+ 1 - 1
whatsapp-ext/stream.go

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