diff options
author | Wim <wim@42.be> | 2022-03-20 02:20:54 +0100 |
---|---|---|
committer | Wim <wim@42.be> | 2022-03-20 14:57:48 +0100 |
commit | 496d5b4ec7f5f4afae6199f928675d14d18de015 (patch) | |
tree | 3a649c80a76f0ffc33e6b3a63c0e65e892169cde /vendor/github.com/Rhymen/go-whatsapp/message.go | |
parent | 2623a412c42a81104b97ae8c81a5f66760fee4b6 (diff) | |
download | matterbridge-msglm-496d5b4ec7f5f4afae6199f928675d14d18de015.tar.gz matterbridge-msglm-496d5b4ec7f5f4afae6199f928675d14d18de015.tar.bz2 matterbridge-msglm-496d5b4ec7f5f4afae6199f928675d14d18de015.zip |
Add whatsappmulti buildflag for whatsapp with multidevice support (whatsapp)
Diffstat (limited to 'vendor/github.com/Rhymen/go-whatsapp/message.go')
-rw-r--r-- | vendor/github.com/Rhymen/go-whatsapp/message.go | 1005 |
1 files changed, 1005 insertions, 0 deletions
diff --git a/vendor/github.com/Rhymen/go-whatsapp/message.go b/vendor/github.com/Rhymen/go-whatsapp/message.go new file mode 100644 index 00000000..1acf881b --- /dev/null +++ b/vendor/github.com/Rhymen/go-whatsapp/message.go @@ -0,0 +1,1005 @@ +package whatsapp + +import ( + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/rand" + "strconv" + "strings" + "time" + + "github.com/Rhymen/go-whatsapp/binary" + "github.com/Rhymen/go-whatsapp/binary/proto" +) + +type MediaType string + +const ( + MediaImage MediaType = "WhatsApp Image Keys" + MediaVideo MediaType = "WhatsApp Video Keys" + MediaAudio MediaType = "WhatsApp Audio Keys" + MediaDocument MediaType = "WhatsApp Document Keys" +) + +func (wac *Conn) Send(msg interface{}) (string, error) { + var msgProto *proto.WebMessageInfo + + switch m := msg.(type) { + case *proto.WebMessageInfo: + msgProto = m + case TextMessage: + msgProto = getTextProto(m) + case ImageMessage: + var err error + m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage) + if err != nil { + return "ERROR", fmt.Errorf("image upload failed: %v", err) + } + msgProto = getImageProto(m) + case VideoMessage: + var err error + m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo) + if err != nil { + return "ERROR", fmt.Errorf("video upload failed: %v", err) + } + msgProto = getVideoProto(m) + case DocumentMessage: + var err error + m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument) + if err != nil { + return "ERROR", fmt.Errorf("document upload failed: %v", err) + } + msgProto = getDocumentProto(m) + case AudioMessage: + var err error + m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio) + if err != nil { + return "ERROR", fmt.Errorf("audio upload failed: %v", err) + } + msgProto = getAudioProto(m) + case LocationMessage: + msgProto = GetLocationProto(m) + case LiveLocationMessage: + msgProto = GetLiveLocationProto(m) + case ContactMessage: + msgProto = getContactMessageProto(m) + case ProductMessage: + msgProto = getProductMessageProto(m) + case OrderMessage: + msgProto = getOrderMessageProto(m) + default: + return "ERROR", fmt.Errorf("cannot match type %T, use message types declared in the package", msg) + } + status := proto.WebMessageInfo_PENDING + msgProto.Status = &status + ch, err := wac.sendProto(msgProto) + if err != nil { + return "ERROR", fmt.Errorf("could not send proto: %v", err) + } + + select { + case response := <-ch: + var resp map[string]interface{} + if err = json.Unmarshal([]byte(response), &resp); err != nil { + return "ERROR", fmt.Errorf("error decoding sending response: %v\n", err) + } + if int(resp["status"].(float64)) != 200 { + return "ERROR", fmt.Errorf("message sending responded with %v", resp["status"]) + } + if int(resp["status"].(float64)) == 200 { + return getMessageInfo(msgProto).Id, nil + } + case <-time.After(wac.msgTimeout): + return "ERROR", fmt.Errorf("sending message timed out") + } + + return "ERROR", nil +} + +func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) { + n := binary.Node{ + Description: "action", + Attributes: map[string]string{ + "type": "relay", + "epoch": strconv.Itoa(wac.msgCount), + }, + Content: []interface{}{p}, + } + return wac.writeBinary(n, message, ignore, p.Key.GetId()) +} + +// RevokeMessage revokes a message (marks as "message removed") for everyone +func (wac *Conn) RevokeMessage(remotejid, msgid string, fromme bool) (revokeid string, err error) { + // create a revocation ID (required) + rawrevocationID := make([]byte, 10) + rand.Read(rawrevocationID) + revocationID := strings.ToUpper(hex.EncodeToString(rawrevocationID)) + // + ts := uint64(time.Now().Unix()) + status := proto.WebMessageInfo_PENDING + mtype := proto.ProtocolMessage_REVOKE + + revoker := &proto.WebMessageInfo{ + Key: &proto.MessageKey{ + FromMe: &fromme, + Id: &revocationID, + RemoteJid: &remotejid, + }, + MessageTimestamp: &ts, + Message: &proto.Message{ + ProtocolMessage: &proto.ProtocolMessage{ + Type: &mtype, + Key: &proto.MessageKey{ + FromMe: &fromme, + Id: &msgid, + RemoteJid: &remotejid, + }, + }, + }, + Status: &status, + } + if _, err := wac.Send(revoker); err != nil { + return revocationID, err + } + return revocationID, nil +} + +// DeleteMessage deletes a single message for the user (removes the msgbox). To +// delete the message for everyone, use RevokeMessage +func (wac *Conn) DeleteMessage(remotejid, msgid string, fromMe bool) error { + ch, err := wac.deleteChatProto(remotejid, msgid, fromMe) + if err != nil { + return fmt.Errorf("could not send proto: %v", err) + } + + select { + case response := <-ch: + var resp map[string]interface{} + if err = json.Unmarshal([]byte(response), &resp); err != nil { + return fmt.Errorf("error decoding deletion response: %v", err) + } + if int(resp["status"].(float64)) != 200 { + return fmt.Errorf("message deletion responded with %v", resp["status"]) + } + if int(resp["status"].(float64)) == 200 { + return nil + } + case <-time.After(wac.msgTimeout): + return fmt.Errorf("deleting message timed out") + } + + return nil +} + +func (wac *Conn) deleteChatProto(remotejid, msgid string, fromMe bool) (<-chan string, error) { + tag := fmt.Sprintf("%s.--%d", wac.timeTag, wac.msgCount) + + owner := "true" + if !fromMe { + owner = "false" + } + n := binary.Node{ + Description: "action", + Attributes: map[string]string{ + "epoch": strconv.Itoa(wac.msgCount), + "type": "set", + }, + Content: []interface{}{ + binary.Node{ + Description: "chat", + Attributes: map[string]string{ + "type": "clear", + "jid": remotejid, + "media": "true", + }, + Content: []binary.Node{ + { + Description: "item", + Attributes: map[string]string{ + "owner": owner, + "index": msgid, + }, + }, + }, + }, + }, + } + return wac.writeBinary(n, chat, expires|skipOffline, tag) +} + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + +/* +MessageInfo contains general message information. It is part of every of every message type. +*/ +type MessageInfo struct { + Id string + RemoteJid string + SenderJid string + FromMe bool + Timestamp uint64 + PushName string + Status MessageStatus + + Source *proto.WebMessageInfo +} + +type MessageStatus int + +const ( + Error MessageStatus = 0 + Pending = 1 + ServerAck = 2 + DeliveryAck = 3 + Read = 4 + Played = 5 +) + +func getMessageInfo(msg *proto.WebMessageInfo) MessageInfo { + return MessageInfo{ + Id: msg.GetKey().GetId(), + RemoteJid: msg.GetKey().GetRemoteJid(), + SenderJid: msg.GetParticipant(), + FromMe: msg.GetKey().GetFromMe(), + Timestamp: msg.GetMessageTimestamp(), + Status: MessageStatus(msg.GetStatus()), + PushName: msg.GetPushName(), + Source: msg, + } +} + +func getInfoProto(info *MessageInfo) *proto.WebMessageInfo { + if info.Id == "" || len(info.Id) < 2 { + b := make([]byte, 10) + rand.Read(b) + info.Id = strings.ToUpper(hex.EncodeToString(b)) + } + if info.Timestamp == 0 { + info.Timestamp = uint64(time.Now().Unix()) + } + info.FromMe = true + + status := proto.WebMessageInfo_WebMessageInfoStatus(info.Status) + + return &proto.WebMessageInfo{ + Key: &proto.MessageKey{ + FromMe: &info.FromMe, + RemoteJid: &info.RemoteJid, + Id: &info.Id, + }, + MessageTimestamp: &info.Timestamp, + Status: &status, + } +} + +/* +ContextInfo represents contextinfo of every message +*/ +type ContextInfo struct { + QuotedMessageID string //StanzaId + QuotedMessage *proto.Message + Participant string + IsForwarded bool +} + +func getMessageContext(msg *proto.ContextInfo) ContextInfo { + + return ContextInfo{ + QuotedMessageID: msg.GetStanzaId(), //StanzaId + QuotedMessage: msg.GetQuotedMessage(), + Participant: msg.GetParticipant(), + IsForwarded: msg.GetIsForwarded(), + } +} + +func getContextInfoProto(context *ContextInfo) *proto.ContextInfo { + if len(context.QuotedMessageID) > 0 { + contextInfo := &proto.ContextInfo{ + StanzaId: &context.QuotedMessageID, + } + + if &context.QuotedMessage != nil { + contextInfo.QuotedMessage = context.QuotedMessage + contextInfo.Participant = &context.Participant + } + + return contextInfo + } + + return nil +} + +/* +TextMessage represents a text message. +*/ +type TextMessage struct { + Info MessageInfo + Text string + ContextInfo ContextInfo +} + +func getTextMessage(msg *proto.WebMessageInfo) TextMessage { + text := TextMessage{Info: getMessageInfo(msg)} + if m := msg.GetMessage().GetExtendedTextMessage(); m != nil { + text.Text = m.GetText() + + text.ContextInfo = getMessageContext(m.GetContextInfo()) + } else { + text.Text = msg.GetMessage().GetConversation() + + } + + return text +} + +func getTextProto(msg TextMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + if contextInfo == nil { + p.Message = &proto.Message{ + Conversation: &msg.Text, + } + } else { + p.Message = &proto.Message{ + ExtendedTextMessage: &proto.ExtendedTextMessage{ + Text: &msg.Text, + ContextInfo: contextInfo, + }, + } + } + + return p +} + +/* +ImageMessage represents a image message. Unexported fields are needed for media up/downloading and media validation. +Provide a io.Reader as Content for message sending. +*/ +type ImageMessage struct { + Info MessageInfo + Caption string + Thumbnail []byte + Type string + Content io.Reader + url string + mediaKey []byte + fileEncSha256 []byte + fileSha256 []byte + fileLength uint64 + ContextInfo ContextInfo +} + +func getImageMessage(msg *proto.WebMessageInfo) ImageMessage { + image := msg.GetMessage().GetImageMessage() + + imageMessage := ImageMessage{ + Info: getMessageInfo(msg), + Caption: image.GetCaption(), + Thumbnail: image.GetJpegThumbnail(), + url: image.GetUrl(), + mediaKey: image.GetMediaKey(), + Type: image.GetMimetype(), + fileEncSha256: image.GetFileEncSha256(), + fileSha256: image.GetFileSha256(), + fileLength: image.GetFileLength(), + ContextInfo: getMessageContext(image.GetContextInfo()), + } + + return imageMessage +} + +func getImageProto(msg ImageMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + p.Message = &proto.Message{ + ImageMessage: &proto.ImageMessage{ + Caption: &msg.Caption, + JpegThumbnail: msg.Thumbnail, + Url: &msg.url, + MediaKey: msg.mediaKey, + Mimetype: &msg.Type, + FileEncSha256: msg.fileEncSha256, + FileSha256: msg.fileSha256, + FileLength: &msg.fileLength, + ContextInfo: contextInfo, + }, + } + return p +} + +/* +Download is the function to retrieve media data. The media gets downloaded, validated and returned. +*/ +func (m *ImageMessage) Download() ([]byte, error) { + return Download(m.url, m.mediaKey, MediaImage, int(m.fileLength)) +} + +/* +VideoMessage represents a video message. Unexported fields are needed for media up/downloading and media validation. +Provide a io.Reader as Content for message sending. +*/ +type VideoMessage struct { + Info MessageInfo + Caption string + Thumbnail []byte + Length uint32 + Type string + Content io.Reader + GifPlayback bool + url string + mediaKey []byte + fileEncSha256 []byte + fileSha256 []byte + fileLength uint64 + ContextInfo ContextInfo +} + +func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage { + vid := msg.GetMessage().GetVideoMessage() + + videoMessage := VideoMessage{ + Info: getMessageInfo(msg), + Caption: vid.GetCaption(), + Thumbnail: vid.GetJpegThumbnail(), + GifPlayback: vid.GetGifPlayback(), + url: vid.GetUrl(), + mediaKey: vid.GetMediaKey(), + Length: vid.GetSeconds(), + Type: vid.GetMimetype(), + fileEncSha256: vid.GetFileEncSha256(), + fileSha256: vid.GetFileSha256(), + fileLength: vid.GetFileLength(), + ContextInfo: getMessageContext(vid.GetContextInfo()), + } + + return videoMessage +} + +func getVideoProto(msg VideoMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + p.Message = &proto.Message{ + VideoMessage: &proto.VideoMessage{ + Caption: &msg.Caption, + JpegThumbnail: msg.Thumbnail, + Url: &msg.url, + GifPlayback: &msg.GifPlayback, + MediaKey: msg.mediaKey, + Seconds: &msg.Length, + FileEncSha256: msg.fileEncSha256, + FileSha256: msg.fileSha256, + FileLength: &msg.fileLength, + Mimetype: &msg.Type, + ContextInfo: contextInfo, + }, + } + return p +} + +/* +Download is the function to retrieve media data. The media gets downloaded, validated and returned. +*/ +func (m *VideoMessage) Download() ([]byte, error) { + return Download(m.url, m.mediaKey, MediaVideo, int(m.fileLength)) +} + +/* +AudioMessage represents a audio message. Unexported fields are needed for media up/downloading and media validation. +Provide a io.Reader as Content for message sending. +*/ +type AudioMessage struct { + Info MessageInfo + Length uint32 + Type string + Content io.Reader + Ptt bool + url string + mediaKey []byte + fileEncSha256 []byte + fileSha256 []byte + fileLength uint64 + ContextInfo ContextInfo +} + +func getAudioMessage(msg *proto.WebMessageInfo) AudioMessage { + aud := msg.GetMessage().GetAudioMessage() + + audioMessage := AudioMessage{ + Info: getMessageInfo(msg), + url: aud.GetUrl(), + mediaKey: aud.GetMediaKey(), + Length: aud.GetSeconds(), + Type: aud.GetMimetype(), + fileEncSha256: aud.GetFileEncSha256(), + fileSha256: aud.GetFileSha256(), + fileLength: aud.GetFileLength(), + ContextInfo: getMessageContext(aud.GetContextInfo()), + } + + return audioMessage +} + +func getAudioProto(msg AudioMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + p.Message = &proto.Message{ + AudioMessage: &proto.AudioMessage{ + Url: &msg.url, + MediaKey: msg.mediaKey, + Seconds: &msg.Length, + FileEncSha256: msg.fileEncSha256, + FileSha256: msg.fileSha256, + FileLength: &msg.fileLength, + Mimetype: &msg.Type, + ContextInfo: contextInfo, + Ptt: &msg.Ptt, + }, + } + return p +} + +/* +Download is the function to retrieve media data. The media gets downloaded, validated and returned. +*/ +func (m *AudioMessage) Download() ([]byte, error) { + return Download(m.url, m.mediaKey, MediaAudio, int(m.fileLength)) +} + +/* +DocumentMessage represents a document message. Unexported fields are needed for media up/downloading and media +validation. Provide a io.Reader as Content for message sending. +*/ +type DocumentMessage struct { + Info MessageInfo + Title string + PageCount uint32 + Type string + FileName string + Thumbnail []byte + Content io.Reader + url string + mediaKey []byte + fileEncSha256 []byte + fileSha256 []byte + fileLength uint64 + ContextInfo ContextInfo +} + +func getDocumentMessage(msg *proto.WebMessageInfo) DocumentMessage { + doc := msg.GetMessage().GetDocumentMessage() + + documentMessage := DocumentMessage{ + Info: getMessageInfo(msg), + Title: doc.GetTitle(), + PageCount: doc.GetPageCount(), + Type: doc.GetMimetype(), + FileName: doc.GetFileName(), + Thumbnail: doc.GetJpegThumbnail(), + url: doc.GetUrl(), + mediaKey: doc.GetMediaKey(), + fileEncSha256: doc.GetFileEncSha256(), + fileSha256: doc.GetFileSha256(), + fileLength: doc.GetFileLength(), + ContextInfo: getMessageContext(doc.GetContextInfo()), + } + + return documentMessage +} + +func getDocumentProto(msg DocumentMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + p.Message = &proto.Message{ + DocumentMessage: &proto.DocumentMessage{ + JpegThumbnail: msg.Thumbnail, + Url: &msg.url, + MediaKey: msg.mediaKey, + FileEncSha256: msg.fileEncSha256, + FileSha256: msg.fileSha256, + FileLength: &msg.fileLength, + PageCount: &msg.PageCount, + Title: &msg.Title, + Mimetype: &msg.Type, + ContextInfo: contextInfo, + }, + } + return p +} + +/* +Download is the function to retrieve media data. The media gets downloaded, validated and returned. +*/ +func (m *DocumentMessage) Download() ([]byte, error) { + return Download(m.url, m.mediaKey, MediaDocument, int(m.fileLength)) +} + +/* +LocationMessage represents a location message +*/ +type LocationMessage struct { + Info MessageInfo + DegreesLatitude float64 + DegreesLongitude float64 + Name string + Address string + Url string + JpegThumbnail []byte + ContextInfo ContextInfo +} + +func GetLocationMessage(msg *proto.WebMessageInfo) LocationMessage { + loc := msg.GetMessage().GetLocationMessage() + + locationMessage := LocationMessage{ + Info: getMessageInfo(msg), + DegreesLatitude: loc.GetDegreesLatitude(), + DegreesLongitude: loc.GetDegreesLongitude(), + Name: loc.GetName(), + Address: loc.GetAddress(), + Url: loc.GetUrl(), + JpegThumbnail: loc.GetJpegThumbnail(), + ContextInfo: getMessageContext(loc.GetContextInfo()), + } + + return locationMessage +} + +func GetLocationProto(msg LocationMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + p.Message = &proto.Message{ + LocationMessage: &proto.LocationMessage{ + DegreesLatitude: &msg.DegreesLatitude, + DegreesLongitude: &msg.DegreesLongitude, + Name: &msg.Name, + Address: &msg.Address, + Url: &msg.Url, + JpegThumbnail: msg.JpegThumbnail, + ContextInfo: contextInfo, + }, + } + return p +} + +/* +LiveLocationMessage represents a live location message +*/ +type LiveLocationMessage struct { + Info MessageInfo + DegreesLatitude float64 + DegreesLongitude float64 + AccuracyInMeters uint32 + SpeedInMps float32 + DegreesClockwiseFromMagneticNorth uint32 + Caption string + SequenceNumber int64 + JpegThumbnail []byte + ContextInfo ContextInfo +} + +func GetLiveLocationMessage(msg *proto.WebMessageInfo) LiveLocationMessage { + loc := msg.GetMessage().GetLiveLocationMessage() + + liveLocationMessage := LiveLocationMessage{ + Info: getMessageInfo(msg), + DegreesLatitude: loc.GetDegreesLatitude(), + DegreesLongitude: loc.GetDegreesLongitude(), + AccuracyInMeters: loc.GetAccuracyInMeters(), + SpeedInMps: loc.GetSpeedInMps(), + DegreesClockwiseFromMagneticNorth: loc.GetDegreesClockwiseFromMagneticNorth(), + Caption: loc.GetCaption(), + SequenceNumber: loc.GetSequenceNumber(), + JpegThumbnail: loc.GetJpegThumbnail(), + ContextInfo: getMessageContext(loc.GetContextInfo()), + } + + return liveLocationMessage +} + +func GetLiveLocationProto(msg LiveLocationMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + p.Message = &proto.Message{ + LiveLocationMessage: &proto.LiveLocationMessage{ + DegreesLatitude: &msg.DegreesLatitude, + DegreesLongitude: &msg.DegreesLongitude, + AccuracyInMeters: &msg.AccuracyInMeters, + SpeedInMps: &msg.SpeedInMps, + DegreesClockwiseFromMagneticNorth: &msg.DegreesClockwiseFromMagneticNorth, + Caption: &msg.Caption, + SequenceNumber: &msg.SequenceNumber, + JpegThumbnail: msg.JpegThumbnail, + ContextInfo: contextInfo, + }, + } + return p +} + +/* +StickerMessage represents a sticker message. +*/ +type StickerMessage struct { + Info MessageInfo + + Type string + Content io.Reader + url string + mediaKey []byte + fileEncSha256 []byte + fileSha256 []byte + fileLength uint64 + + ContextInfo ContextInfo +} + +func getStickerMessage(msg *proto.WebMessageInfo) StickerMessage { + sticker := msg.GetMessage().GetStickerMessage() + + stickerMessage := StickerMessage{ + Info: getMessageInfo(msg), + url: sticker.GetUrl(), + mediaKey: sticker.GetMediaKey(), + Type: sticker.GetMimetype(), + fileEncSha256: sticker.GetFileEncSha256(), + fileSha256: sticker.GetFileSha256(), + fileLength: sticker.GetFileLength(), + ContextInfo: getMessageContext(sticker.GetContextInfo()), + } + + return stickerMessage +} + +/* +Download is the function to retrieve Sticker media data. The media gets downloaded, validated and returned. +*/ + +func (m *StickerMessage) Download() ([]byte, error) { + return Download(m.url, m.mediaKey, MediaImage, int(m.fileLength)) +} + +/* +ContactMessage represents a contact message. +*/ +type ContactMessage struct { + Info MessageInfo + + DisplayName string + Vcard string + + ContextInfo ContextInfo +} + +func getContactMessage(msg *proto.WebMessageInfo) ContactMessage { + contact := msg.GetMessage().GetContactMessage() + + contactMessage := ContactMessage{ + Info: getMessageInfo(msg), + + DisplayName: contact.GetDisplayName(), + Vcard: contact.GetVcard(), + + ContextInfo: getMessageContext(contact.GetContextInfo()), + } + + return contactMessage +} + +func getContactMessageProto(msg ContactMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + p.Message = &proto.Message{ + ContactMessage: &proto.ContactMessage{ + DisplayName: &msg.DisplayName, + Vcard: &msg.Vcard, + ContextInfo: contextInfo, + }, + } + + return p +} + +/* +OrderMessage represents a order message. +*/ + +type OrderMessage struct { + Info MessageInfo + OrderId string + Thumbnail []byte + ItemCount int32 + Status proto.OrderMessage_OrderMessageOrderStatus + Surface proto.OrderMessage_OrderMessageOrderSurface + Message string + OrderTitle string + SellerJid string + Token string + TotalAmount1000 int64 + TotalCurrencyCode string + ContextInfo ContextInfo +} + +func getOrderMessage(msg *proto.WebMessageInfo) OrderMessage { + order := msg.GetMessage().GetOrderMessage() + + orderMessage := OrderMessage{ + Info: getMessageInfo(msg), + OrderId: order.GetOrderId(), + Thumbnail: order.GetThumbnail(), + ItemCount: order.GetItemCount(), + Status: order.GetStatus(), + Surface: order.GetSurface(), + Message: order.GetMessage(), + OrderTitle: order.GetOrderTitle(), + SellerJid: order.GetSellerJid(), + Token: order.GetToken(), + TotalAmount1000: order.GetTotalAmount1000(), + TotalCurrencyCode: order.GetTotalCurrencyCode(), + ContextInfo: getMessageContext(order.GetContextInfo()), + } + + return orderMessage +} + +func getOrderMessageProto(msg OrderMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + p.Message = &proto.Message{ + OrderMessage: &proto.OrderMessage{ + Thumbnail: msg.Thumbnail, + ItemCount: &msg.ItemCount, + Status: &msg.Status, + Surface: &msg.Surface, + Message: &msg.Message, + OrderTitle: &msg.OrderTitle, + SellerJid: &msg.SellerJid, + Token: &msg.Token, + TotalAmount1000: &msg.TotalAmount1000, + TotalCurrencyCode: &msg.TotalCurrencyCode, + ContextInfo: contextInfo, + }, + } + + return p +} + +/* +ProductMessage represents a product message. +*/ + +type ProductMessage struct { + Info MessageInfo + Product *proto.ProductSnapshot + BusinessOwnerJid string + Catalog *proto.CatalogSnapshot + ContextInfo ContextInfo +} + +func getProductMessage(msg *proto.WebMessageInfo) ProductMessage { + prod := msg.GetMessage().GetProductMessage() + + productMessage := ProductMessage{ + Info: getMessageInfo(msg), + Product: prod.GetProduct(), + BusinessOwnerJid: prod.GetBusinessOwnerJid(), + Catalog: prod.GetCatalog(), + ContextInfo: getMessageContext(prod.GetContextInfo()), + } + + return productMessage +} + +func getProductMessageProto(msg ProductMessage) *proto.WebMessageInfo { + p := getInfoProto(&msg.Info) + contextInfo := getContextInfoProto(&msg.ContextInfo) + + p.Message = &proto.Message{ + ProductMessage: &proto.ProductMessage{ + Product: msg.Product, + BusinessOwnerJid: &msg.BusinessOwnerJid, + Catalog: msg.Catalog, + ContextInfo: contextInfo, + }, + } + + return p +} + +func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} { + + switch { + + case msg.GetMessage().GetAudioMessage() != nil: + return getAudioMessage(msg) + + case msg.GetMessage().GetImageMessage() != nil: + return getImageMessage(msg) + + case msg.GetMessage().GetVideoMessage() != nil: + return getVideoMessage(msg) + + case msg.GetMessage().GetDocumentMessage() != nil: + return getDocumentMessage(msg) + + case msg.GetMessage().GetConversation() != "": + return getTextMessage(msg) + + case msg.GetMessage().GetExtendedTextMessage() != nil: + return getTextMessage(msg) + + case msg.GetMessage().GetLocationMessage() != nil: + return GetLocationMessage(msg) + + case msg.GetMessage().GetLiveLocationMessage() != nil: + return GetLiveLocationMessage(msg) + + case msg.GetMessage().GetStickerMessage() != nil: + return getStickerMessage(msg) + + case msg.GetMessage().GetContactMessage() != nil: + return getContactMessage(msg) + + case msg.GetMessage().GetProductMessage() != nil: + return getProductMessage(msg) + + case msg.GetMessage().GetOrderMessage() != nil: + return getOrderMessage(msg) + + default: + //cannot match message + return ErrMessageTypeNotImplemented + } +} + +/* +BatteryMessage represents a battery level and charging state. +*/ +type BatteryMessage struct { + Plugged bool + Powersave bool + Percentage int +} + +func getBatteryMessage(msg map[string]string) BatteryMessage { + plugged, _ := strconv.ParseBool(msg["live"]) + powersave, _ := strconv.ParseBool(msg["powersave"]) + percentage, _ := strconv.Atoi(msg["value"]) + batteryMessage := BatteryMessage{ + Plugged: plugged, + Powersave: powersave, + Percentage: percentage, + } + + return batteryMessage +} + +func getNewContact(msg map[string]string) Contact { + contact := Contact{ + Jid: msg["jid"], + Notify: msg["notify"], + } + + return contact +} + +func ParseNodeMessage(msg binary.Node) interface{} { + switch msg.Description { + case "battery": + return getBatteryMessage(msg.Attributes) + case "user": + return getNewContact(msg.Attributes) + default: + //cannot match message + } + + return nil +} |