summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Rhymen/go-whatsapp/message.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Rhymen/go-whatsapp/message.go')
-rw-r--r--vendor/github.com/Rhymen/go-whatsapp/message.go1005
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
+}