4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-28 12:39:23 +00:00

Update vendor (#932)

* Update vendor

* Fix godiscord api change
This commit is contained in:
Wim
2019-10-27 01:45:57 +02:00
committed by GitHub
parent 45bf1fd63a
commit 0bc159341d
33 changed files with 4154 additions and 1605 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,22 +1,94 @@
syntax = "proto2";
package proto;
message FingerprintData {
optional string publicKey = 1;
optional string identifier = 2;
message HydratedQuickReplyButton {
optional string displayText = 1;
optional string buttonId = 2;
}
message CombinedFingerprint {
optional uint32 version = 1;
optional FingerprintData localFingerprint = 2;
optional FingerprintData remoteFingerprint = 3;
message HydratedURLButton {
optional string displayText = 1;
optional string url = 2;
}
message MessageKey {
optional string remoteJid = 1;
optional bool fromMe = 2;
optional string id = 3;
optional string participant = 4;
message HydratedCallButton {
optional string displayText = 1;
optional string phoneNumber = 2;
}
message HydratedTemplateButton {
oneof hydratedButton {
HydratedQuickReplyButton quickReplyButton = 1;
HydratedURLButton urlButton = 2;
HydratedCallButton callButton = 3;
}
}
message QuickReplyButton {
optional HighlyStructuredMessage displayText = 1;
optional string buttonId = 2;
}
message URLButton {
optional HighlyStructuredMessage displayText = 1;
optional HighlyStructuredMessage url = 2;
}
message CallButton {
optional HighlyStructuredMessage displayText = 1;
optional HighlyStructuredMessage phoneNumber = 2;
}
message TemplateButton {
oneof button {
QuickReplyButton quickReplyButton = 1;
URLButton urlButton = 2;
CallButton callButton = 3;
}
}
message Location {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional string name = 3;
}
message Point {
optional double x = 3;
optional double y = 4;
}
message InteractiveAnnotation {
repeated Point polygonVertices = 1;
oneof action {
Location location = 2;
}
}
message AdReplyInfo {
optional string advertiserName = 1;
enum AD_REPLY_INFO_MEDIATYPE {
NONE = 0;
IMAGE = 1;
VIDEO = 2;
}
optional AD_REPLY_INFO_MEDIATYPE mediaType = 2;
optional bytes jpegThumbnail = 16;
optional string caption = 17;
}
message ContextInfo {
optional string stanzaId = 1;
optional string participant = 2;
optional Message quotedMessage = 3;
optional string remoteJid = 4;
repeated string mentionedJid = 15;
optional string conversionSource = 18;
optional bytes conversionData = 19;
optional uint32 conversionDelaySeconds = 20;
optional uint32 forwardingScore = 21;
optional bool isForwarded = 22;
optional AdReplyInfo quotedAd = 23;
}
message SenderKeyDistributionMessage {
@ -36,10 +108,12 @@ message ImageMessage {
optional bytes fileEncSha256 = 9;
repeated InteractiveAnnotation interactiveAnnotations = 10;
optional string directPath = 11;
optional int64 mediaKeyTimestamp = 12;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bytes firstScanSidecar = 18;
optional uint32 firstScanLength = 19;
optional uint32 experimentGroupId = 20;
}
message ContactMessage {
@ -66,7 +140,7 @@ message ExtendedTextMessage {
optional string title = 6;
optional fixed32 textArgb = 7;
optional fixed32 backgroundArgb = 8;
enum FONTTYPE {
enum EXTENDED_TEXT_MESSAGE_FONTTYPE {
SANS_SERIF = 0;
SERIF = 1;
NORICAN_REGULAR = 2;
@ -74,7 +148,12 @@ message ExtendedTextMessage {
BEBASNEUE_REGULAR = 4;
OSWALD_HEAVY = 5;
}
optional FONTTYPE font = 9;
optional EXTENDED_TEXT_MESSAGE_FONTTYPE font = 9;
enum EXTENDED_TEXT_MESSAGE_PREVIEWTYPE {
NONE = 0;
VIDEO = 1;
}
optional EXTENDED_TEXT_MESSAGE_PREVIEWTYPE previewType = 10;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@ -90,6 +169,7 @@ message DocumentMessage {
optional string fileName = 8;
optional bytes fileEncSha256 = 9;
optional string directPath = 10;
optional int64 mediaKeyTimestamp = 11;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@ -104,6 +184,7 @@ message AudioMessage {
optional bytes mediaKey = 7;
optional bytes fileEncSha256 = 8;
optional string directPath = 9;
optional int64 mediaKeyTimestamp = 10;
optional ContextInfo contextInfo = 17;
optional bytes streamingSidecar = 18;
}
@ -122,15 +203,16 @@ message VideoMessage {
optional bytes fileEncSha256 = 11;
repeated InteractiveAnnotation interactiveAnnotations = 12;
optional string directPath = 13;
optional int64 mediaKeyTimestamp = 14;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
optional bytes streamingSidecar = 18;
enum ATTRIBUTION {
enum VIDEO_MESSAGE_ATTRIBUTION {
NONE = 0;
GIPHY = 1;
TENOR = 2;
}
optional ATTRIBUTION gifAttribution = 19;
optional VIDEO_MESSAGE_ATTRIBUTION gifAttribution = 19;
}
message Call {
@ -144,10 +226,10 @@ message Chat {
message ProtocolMessage {
optional MessageKey key = 1;
enum TYPE {
enum PROTOCOL_MESSAGE_TYPE {
REVOKE = 0;
}
optional TYPE type = 2;
optional PROTOCOL_MESSAGE_TYPE type = 2;
}
message ContactsArrayMessage {
@ -162,7 +244,7 @@ message HSMCurrency {
}
message HSMDateTimeComponent {
enum DAYOFWEEKTYPE {
enum HSM_DATE_TIME_COMPONENT_DAYOFWEEKTYPE {
MONDAY = 1;
TUESDAY = 2;
WEDNESDAY = 3;
@ -171,17 +253,17 @@ message HSMDateTimeComponent {
SATURDAY = 6;
SUNDAY = 7;
}
optional DAYOFWEEKTYPE dayOfWeek = 1;
optional HSM_DATE_TIME_COMPONENT_DAYOFWEEKTYPE dayOfWeek = 1;
optional uint32 year = 2;
optional uint32 month = 3;
optional uint32 dayOfMonth = 4;
optional uint32 hour = 5;
optional uint32 minute = 6;
enum CALENDARTYPE {
enum HSM_DATE_TIME_COMPONENT_CALENDARTYPE {
GREGORIAN = 1;
SOLAR_HIJRI = 2;
}
optional CALENDARTYPE calendar = 7;
optional HSM_DATE_TIME_COMPONENT_CALENDARTYPE calendar = 7;
}
message HSMDateTimeUnixEpoch {
@ -210,17 +292,29 @@ message HighlyStructuredMessage {
optional string fallbackLg = 4;
optional string fallbackLc = 5;
repeated HSMLocalizableParameter localizableParams = 6;
optional string deterministicLg = 7;
optional string deterministicLc = 8;
}
message SendPaymentMessage {
optional Message noteMessage = 2;
optional MessageKey requestMessageKey = 3;
}
message RequestPaymentMessage {
optional Message noteMessage = 4;
optional string currencyCodeIso4217 = 1;
optional uint64 amount1000 = 2;
optional string requestFrom = 3;
optional Message noteMessage = 4;
optional int64 expiryTimestamp = 5;
}
message DeclinePaymentRequestMessage {
optional MessageKey key = 1;
}
message CancelPaymentRequestMessage {
optional MessageKey key = 1;
}
message LiveLocationMessage {
@ -231,6 +325,7 @@ message LiveLocationMessage {
optional uint32 degreesClockwiseFromMagneticNorth = 5;
optional string caption = 6;
optional int64 sequenceNumber = 7;
optional uint32 timeOffset = 8;
optional bytes jpegThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
@ -245,10 +340,77 @@ message StickerMessage {
optional uint32 width = 7;
optional string directPath = 8;
optional uint64 fileLength = 9;
optional int64 mediaKeyTimestamp = 10;
optional bytes pngThumbnail = 16;
optional ContextInfo contextInfo = 17;
}
message FourRowTemplate {
optional HighlyStructuredMessage content = 6;
optional HighlyStructuredMessage footer = 7;
repeated TemplateButton buttons = 8;
oneof title {
DocumentMessage documentMessage = 1;
HighlyStructuredMessage highlyStructuredMessage = 2;
ImageMessage imageMessage = 3;
VideoMessage videoMessage = 4;
LocationMessage locationMessage = 5;
}
}
message HydratedFourRowTemplate {
optional string hydratedContentText = 6;
optional string hydratedFooterText = 7;
repeated HydratedTemplateButton hydratedButtons = 9;
oneof title {
DocumentMessage documentMessage = 1;
string hydratedTitleText = 2;
ImageMessage imageMessage = 3;
VideoMessage videoMessage = 4;
LocationMessage locationMessage = 5;
}
}
message TemplateMessage {
oneof format {
FourRowTemplate fourRowTemplate = 1;
HydratedFourRowTemplate hydratedFourRowTemplate = 2;
}
}
message TemplateButtonReplyMessage {
optional string selectedButtonId = 1;
repeated string selectedButtonDisplayText = 2;
optional ContextInfo contextInfo = 3;
}
message ProductSnapshot {
optional ImageMessage productImage = 1;
optional string productId = 2;
optional string title = 3;
optional string description = 4;
optional string currencyCode = 5;
optional int64 priceAmount1000 = 6;
optional string retailerId = 7;
optional string url = 8;
optional uint32 productImageCount = 9;
}
message ProductMessage {
optional ProductSnapshot product = 1;
optional string businessOwnerJid = 2;
optional ContextInfo contextInfo = 17;
}
message GroupInviteMessage {
optional string groupJid = 1;
optional string inviteCode = 2;
optional int64 inviteExpiration = 3;
optional string groupName = 4;
optional bytes jpegThumbnail = 5;
optional string caption = 6;
}
message Message {
optional string conversation = 1;
optional SenderKeyDistributionMessage senderKeyDistributionMessage = 2;
@ -266,47 +428,104 @@ message Message {
optional HighlyStructuredMessage highlyStructuredMessage = 14;
optional SenderKeyDistributionMessage fastRatchetKeySenderKeyDistributionMessage = 15;
optional SendPaymentMessage sendPaymentMessage = 16;
optional RequestPaymentMessage requestPaymentMessage = 17;
optional LiveLocationMessage liveLocationMessage = 18;
optional StickerMessage stickerMessage = 20;
optional RequestPaymentMessage requestPaymentMessage = 22;
optional DeclinePaymentRequestMessage declinePaymentRequestMessage = 23;
optional CancelPaymentRequestMessage cancelPaymentRequestMessage = 24;
optional TemplateMessage templateMessage = 25;
optional StickerMessage stickerMessage = 26;
optional ProductMessage productMessage = 27;
optional GroupInviteMessage groupInviteMessage = 28;
}
message ContextInfo {
optional string stanzaId = 1;
optional string participant = 2;
repeated Message quotedMessage = 3;
optional string remoteJid = 4;
repeated string mentionedJid = 15;
optional string conversionSource = 18;
optional bytes conversionData = 19;
optional uint32 conversionDelaySeconds = 20;
optional bool isForwarded = 22;
reserved 16, 17;
message MessageKey {
optional string remoteJid = 1;
optional bool fromMe = 2;
optional string id = 3;
optional string participant = 4;
}
message InteractiveAnnotation {
repeated Point polygonVertices = 1;
oneof action {
Location location = 2;
message WebFeatures {
enum WEB_FEATURES_FLAG {
NOT_IMPLEMENTED = 0;
IMPLEMENTED = 1;
OPTIONAL = 2;
}
optional WEB_FEATURES_FLAG labelsDisplay = 1;
optional WEB_FEATURES_FLAG voipIndividualOutgoing = 2;
optional WEB_FEATURES_FLAG groupsV3 = 3;
optional WEB_FEATURES_FLAG groupsV3Create = 4;
optional WEB_FEATURES_FLAG changeNumberV2 = 5;
optional WEB_FEATURES_FLAG queryStatusV3Thumbnail = 6;
optional WEB_FEATURES_FLAG liveLocations = 7;
optional WEB_FEATURES_FLAG queryVname = 8;
optional WEB_FEATURES_FLAG voipIndividualIncoming = 9;
optional WEB_FEATURES_FLAG quickRepliesQuery = 10;
optional WEB_FEATURES_FLAG payments = 11;
optional WEB_FEATURES_FLAG stickerPackQuery = 12;
optional WEB_FEATURES_FLAG liveLocationsFinal = 13;
optional WEB_FEATURES_FLAG labelsEdit = 14;
optional WEB_FEATURES_FLAG mediaUpload = 15;
optional WEB_FEATURES_FLAG mediaUploadRichQuickReplies = 18;
optional WEB_FEATURES_FLAG vnameV2 = 19;
optional WEB_FEATURES_FLAG videoPlaybackUrl = 20;
optional WEB_FEATURES_FLAG statusRanking = 21;
optional WEB_FEATURES_FLAG voipIndividualVideo = 22;
optional WEB_FEATURES_FLAG thirdPartyStickers = 23;
optional WEB_FEATURES_FLAG frequentlyForwardedSetting = 24;
}
message Point {
optional double x = 3;
optional double y = 4;
message TabletNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated NotificationMessageInfo notifyMessage = 5;
}
message Location {
optional double degreesLatitude = 1;
optional double degreesLongitude = 2;
optional string name = 3;
message NotificationMessageInfo {
optional MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
optional string participant = 4;
}
message WebNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated WebMessageInfo notifyMessages = 5;
}
message PaymentInfo {
optional uint64 amount1000 = 2;
optional string receiverJid = 3;
enum PAYMENT_INFO_STATUS {
UNKNOWN_STATUS = 0;
PROCESSING = 1;
SENT = 2;
NEED_TO_ACCEPT = 3;
COMPLETE = 4;
COULD_NOT_COMPLETE = 5;
REFUNDED = 6;
EXPIRED = 7;
REJECTED = 8;
CANCELLED = 9;
WAITING_FOR_PAYER = 10;
WAITING = 11;
}
optional PAYMENT_INFO_STATUS status = 4;
optional uint64 transactionTimestamp = 5;
optional MessageKey requestMessageKey = 6;
optional uint64 expiryTimestamp = 7;
optional bool futureproofed = 8;
optional string currency = 9;
}
message WebMessageInfo {
required MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
enum STATUS {
enum WEB_MESSAGE_INFO_STATUS {
ERROR = 0;
PENDING = 1;
SERVER_ACK = 2;
@ -314,7 +533,7 @@ message WebMessageInfo {
READ = 4;
PLAYED = 5;
}
optional STATUS status = 4 [default=PENDING];
optional WEB_MESSAGE_INFO_STATUS status = 4;
optional string participant = 5;
optional bool ignore = 16;
optional bool starred = 17;
@ -324,7 +543,7 @@ message WebMessageInfo {
optional bool multicast = 21;
optional bool urlText = 22;
optional bool urlNumber = 23;
enum STUBTYPE {
enum WEB_MESSAGE_INFO_STUBTYPE {
UNKNOWN = 0;
REVOKE = 1;
CIPHERTEXT = 2;
@ -369,49 +588,39 @@ message WebMessageInfo {
CALL_MISSED_VIDEO = 41;
INDIVIDUAL_CHANGE_NUMBER = 42;
GROUP_DELETE = 43;
GROUP_ANNOUNCE_MODE_MESSAGE_BOUNCE = 44;
CALL_MISSED_GROUP_VOICE = 45;
CALL_MISSED_GROUP_VIDEO = 46;
PAYMENT_CIPHERTEXT = 47;
PAYMENT_FUTUREPROOF = 48;
PAYMENT_TRANSACTION_STATUS_UPDATE_FAILED = 49;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUNDED = 50;
PAYMENT_TRANSACTION_STATUS_UPDATE_REFUND_FAILED = 51;
PAYMENT_TRANSACTION_STATUS_RECEIVER_PENDING_SETUP = 52;
PAYMENT_TRANSACTION_STATUS_RECEIVER_SUCCESS_AFTER_HICCUP = 53;
PAYMENT_ACTION_ACCOUNT_SETUP_REMINDER = 54;
PAYMENT_ACTION_SEND_PAYMENT_REMINDER = 55;
PAYMENT_ACTION_SEND_PAYMENT_INVITATION = 56;
PAYMENT_ACTION_REQUEST_DECLINED = 57;
PAYMENT_ACTION_REQUEST_EXPIRED = 58;
PAYMENT_ACTION_REQUEST_CANCELLED = 59;
BIZ_VERIFIED_TRANSITION_TOP_TO_BOTTOM = 60;
BIZ_VERIFIED_TRANSITION_BOTTOM_TO_TOP = 61;
BIZ_INTRO_TOP = 62;
BIZ_INTRO_BOTTOM = 63;
BIZ_NAME_CHANGE = 64;
BIZ_MOVE_TO_CONSUMER_APP = 65;
BIZ_TWO_TIER_MIGRATION_TOP = 66;
BIZ_TWO_TIER_MIGRATION_BOTTOM = 67;
OVERSIZED = 68;
GROUP_CHANGE_NO_FREQUENTLY_FORWARDED = 69;
}
optional STUBTYPE messageStubType = 24;
optional WEB_MESSAGE_INFO_STUBTYPE messageStubType = 24;
optional bool clearMedia = 25;
repeated string messageStubParameters = 26;
optional uint32 duration = 27;
repeated string labels = 28;
}
message WebNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated Message notifyMessages = 5;
}
message NotificationMessageInfo {
optional MessageKey key = 1;
optional Message message = 2;
optional uint64 messageTimestamp = 3;
optional string participant = 4;
}
message TabletNotificationsInfo {
optional uint64 timestamp = 2;
optional uint32 unreadChats = 3;
optional uint32 notifyMessageCount = 4;
repeated Message notifyMessage = 5;
}
message WebFeatures {
enum FLAG {
NOT_IMPLEMENTED = 0;
IMPLEMENTED = 1;
OPTIONAL = 2;
}
optional FLAG labelsDisplay = 1;
optional FLAG voipIndividualOutgoing = 2;
optional FLAG groupsV3 = 3;
optional FLAG groupsV3Create = 4;
optional FLAG changeNumberV2 = 5;
optional FLAG queryStatusV3Thumbnail = 6;
optional FLAG liveLocations = 7;
optional FLAG queryVname = 8;
optional FLAG voipIndividualIncoming = 9;
optional FLAG quickRepliesQuery = 10;
optional PaymentInfo paymentInfo = 29;
optional LiveLocationMessage finalLiveLocation = 30;
optional PaymentInfo quotedPaymentInfo = 31;
}

View File

@ -4,6 +4,7 @@ package whatsapp
import (
"math/rand"
"net/http"
"net/url"
"sync"
"time"
@ -91,6 +92,7 @@ type Conn struct {
shortClientName string
loginSessionLock sync.RWMutex
Proxy func(*http.Request) (*url.URL, error)
}
type websocketWrapper struct {
@ -121,6 +123,21 @@ func NewConn(timeout time.Duration) (*Conn, error) {
return wac, wac.connect()
}
// NewConnWithProxy Create a new connect with a given timeout and a http proxy.
func NewConnWithProxy(timeout time.Duration, proxy func(*http.Request) (*url.URL, error)) (*Conn, error) {
wac := &Conn{
handler: make([]Handler, 0),
msgCount: 0,
msgTimeout: timeout,
Store: newStore(),
longClientName: "github.com/rhymen/go-whatsapp",
shortClientName: "go-whatsapp",
Proxy: proxy,
}
return wac, wac.connect()
}
// connect should be guarded with wsWriteMutex
func (wac *Conn) connect() (err error) {
if wac.connected {
@ -137,6 +154,7 @@ func (wac *Conn) connect() (err error) {
ReadBufferSize: 25 * 1024 * 1024,
WriteBufferSize: 10 * 1024 * 1024,
HandshakeTimeout: wac.msgTimeout,
Proxy: wac.Proxy,
}
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
@ -202,7 +220,7 @@ func (wac *Conn) AdminTest() (bool, error) {
return false, ErrInvalidSession
}
result, err := wac.sendAdminTest()
result, err := wac.sendAdminTest()
return result, err
}

View File

@ -51,6 +51,10 @@ func (wac *Conn) LoadMessagesAfter(jid, messageId string, count int) (*binary.No
return wac.query("message", jid, messageId, "after", "true", "", count, 0)
}
func (wac *Conn) LoadMediaInfo(jid, messageId, owner string) (*binary.Node, error) {
return wac.query("media", jid, messageId, "", owner, "", 0, 0)
}
func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error) {
ts := time.Now().Unix()
tag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
@ -163,7 +167,12 @@ func (wac *Conn) query(t, jid, messageId, kind, owner, search string, count, pag
n.Attributes["page"] = strconv.Itoa(page)
}
ch, err := wac.writeBinary(n, group, ignore, tag)
metric := group
if t == "media" {
metric = queryMedia
}
ch, err := wac.writeBinary(n, metric, ignore, tag)
if err != nil {
return nil, err
}

View File

@ -6,18 +6,20 @@ import (
)
var (
ErrAlreadyConnected = errors.New("already connected")
ErrAlreadyLoggedIn = errors.New("already logged in")
ErrInvalidSession = errors.New("invalid session")
ErrLoginInProgress = errors.New("login or restore already running")
ErrNotConnected = errors.New("not connected")
ErrInvalidWsData = errors.New("received invalid data")
ErrInvalidWsState = errors.New("can't handle binary data when not logged in")
ErrConnectionTimeout = errors.New("connection timed out")
ErrMissingMessageTag = errors.New("no messageTag specified or to short")
ErrInvalidHmac = errors.New("invalid hmac")
ErrInvalidServerResponse = errors.New("invalid response received from server")
ErrServerRespondedWith404 = errors.New("server responded with status 404")
ErrAlreadyConnected = errors.New("already connected")
ErrAlreadyLoggedIn = errors.New("already logged in")
ErrInvalidSession = errors.New("invalid session")
ErrLoginInProgress = errors.New("login or restore already running")
ErrNotConnected = errors.New("not connected")
ErrInvalidWsData = errors.New("received invalid data")
ErrInvalidWsState = errors.New("can't handle binary data when not logged in")
ErrConnectionTimeout = errors.New("connection timed out")
ErrMissingMessageTag = errors.New("no messageTag specified or to short")
ErrInvalidHmac = errors.New("invalid hmac")
ErrInvalidServerResponse = errors.New("invalid response received from server")
ErrServerRespondedWith404 = errors.New("server responded with status 404")
ErrMediaDownloadFailedWith404 = errors.New("download failed with status code 404")
ErrMediaDownloadFailedWith410 = errors.New("download failed with status code 410")
)
type ErrConnectionFailed struct {

View File

@ -81,6 +81,14 @@ type LocationMessageHandler interface {
HandleLocationMessage(message LocationMessage)
}
/*
The StickerMessageHandler interface needs to be implemented to receive location messages dispatched by the dispatcher.
*/
type StickerMessageHandler interface {
Handler
HandleStickerMessage(message StickerMessage)
}
/*
The JsonMessageHandler interface needs to be implemented to receive json messages dispatched by the dispatcher.
These json messages contain status updates of every kind sent by WhatsAppWeb servers. WhatsAppWeb uses these messages
@ -247,6 +255,18 @@ func (wac *Conn) handleWithCustomHandlers(message interface{}, handlers []Handle
}
}
}
case StickerMessage:
for _, h := range handlers {
if x, ok := h.(StickerMessageHandler); ok {
if wac.shouldCallSynchronously(h) {
x.HandleStickerMessage(m)
} else {
go x.HandleStickerMessage(m)
}
}
}
case *proto.WebMessageInfo:
for _, h := range handlers {
if x, ok := h.(RawMessageHandler); ok {

View File

@ -74,6 +74,12 @@ func downloadMedia(url string) (file []byte, mac []byte, err error) {
return nil, nil, err
}
if resp.StatusCode != 200 {
if resp.StatusCode == 404 {
return nil, nil, ErrMediaDownloadFailedWith404
}
if resp.StatusCode == 410 {
return nil, nil, ErrMediaDownloadFailedWith410
}
return nil, nil, fmt.Errorf("download failed with status code %d", resp.StatusCode)
}
defer resp.Body.Close()

View File

@ -4,13 +4,14 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"github.com/Rhymen/go-whatsapp/binary"
"github.com/Rhymen/go-whatsapp/binary/proto"
"io"
"math/rand"
"strconv"
"strings"
"time"
"github.com/Rhymen/go-whatsapp/binary"
"github.com/Rhymen/go-whatsapp/binary/proto"
)
type MediaType string
@ -131,6 +132,7 @@ type MessageInfo struct {
PushName string
Status MessageStatus
QuotedMessageID string
QuotedMessage proto.Message
Source *proto.WebMessageInfo
}
@ -170,7 +172,7 @@ func getInfoProto(info *MessageInfo) *proto.WebMessageInfo {
}
info.FromMe = true
status := proto.WebMessageInfo_STATUS(info.Status)
status := proto.WebMessageInfo_WEB_MESSAGE_INFO_STATUS(info.Status)
return &proto.WebMessageInfo{
Key: &proto.MessageKey{
@ -183,6 +185,22 @@ func getInfoProto(info *MessageInfo) *proto.WebMessageInfo {
}
}
func getContextInfoProto(info *MessageInfo) *proto.ContextInfo {
if len(info.QuotedMessageID) > 0 {
contextInfo := &proto.ContextInfo{
StanzaId: &info.QuotedMessageID,
}
if &info.QuotedMessage != nil {
contextInfo.QuotedMessage = &info.QuotedMessage
}
return contextInfo
}
return nil
}
/*
TextMessage represents a text message.
*/
@ -204,9 +222,21 @@ func getTextMessage(msg *proto.WebMessageInfo) TextMessage {
func getTextProto(msg TextMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
p.Message = &proto.Message{
Conversation: &msg.Text,
contextInfo := getContextInfoProto(&msg.Info)
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
}
@ -229,7 +259,8 @@ type ImageMessage struct {
func getImageMessage(msg *proto.WebMessageInfo) ImageMessage {
image := msg.GetMessage().GetImageMessage()
return ImageMessage{
imageMessage := ImageMessage{
Info: getMessageInfo(msg),
Caption: image.GetCaption(),
Thumbnail: image.GetJpegThumbnail(),
@ -240,10 +271,18 @@ func getImageMessage(msg *proto.WebMessageInfo) ImageMessage {
fileSha256: image.GetFileSha256(),
fileLength: image.GetFileLength(),
}
if contextInfo := image.GetContextInfo(); contextInfo != nil {
imageMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return imageMessage
}
func getImageProto(msg ImageMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
ImageMessage: &proto.ImageMessage{
Caption: &msg.Caption,
@ -254,6 +293,7 @@ func getImageProto(msg ImageMessage) *proto.WebMessageInfo {
FileEncSha256: msg.fileEncSha256,
FileSha256: msg.fileSha256,
FileLength: &msg.fileLength,
ContextInfo: contextInfo,
},
}
return p
@ -287,7 +327,8 @@ type VideoMessage struct {
func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage {
vid := msg.GetMessage().GetVideoMessage()
return VideoMessage{
videoMessage := VideoMessage{
Info: getMessageInfo(msg),
Caption: vid.GetCaption(),
Thumbnail: vid.GetJpegThumbnail(),
@ -300,10 +341,18 @@ func getVideoMessage(msg *proto.WebMessageInfo) VideoMessage {
fileSha256: vid.GetFileSha256(),
fileLength: vid.GetFileLength(),
}
if contextInfo := vid.GetContextInfo(); contextInfo != nil {
videoMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return videoMessage
}
func getVideoProto(msg VideoMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
VideoMessage: &proto.VideoMessage{
Caption: &msg.Caption,
@ -316,6 +365,7 @@ func getVideoProto(msg VideoMessage) *proto.WebMessageInfo {
FileSha256: msg.fileSha256,
FileLength: &msg.fileLength,
Mimetype: &msg.Type,
ContextInfo: contextInfo,
},
}
return p
@ -337,6 +387,7 @@ type AudioMessage struct {
Length uint32
Type string
Content io.Reader
Ptt bool
url string
mediaKey []byte
fileEncSha256 []byte
@ -346,7 +397,8 @@ type AudioMessage struct {
func getAudioMessage(msg *proto.WebMessageInfo) AudioMessage {
aud := msg.GetMessage().GetAudioMessage()
return AudioMessage{
audioMessage := AudioMessage{
Info: getMessageInfo(msg),
url: aud.GetUrl(),
mediaKey: aud.GetMediaKey(),
@ -356,10 +408,17 @@ func getAudioMessage(msg *proto.WebMessageInfo) AudioMessage {
fileSha256: aud.GetFileSha256(),
fileLength: aud.GetFileLength(),
}
if contextInfo := aud.GetContextInfo(); contextInfo != nil {
audioMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return audioMessage
}
func getAudioProto(msg AudioMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
AudioMessage: &proto.AudioMessage{
Url: &msg.url,
@ -369,6 +428,8 @@ func getAudioProto(msg AudioMessage) *proto.WebMessageInfo {
FileSha256: msg.fileSha256,
FileLength: &msg.fileLength,
Mimetype: &msg.Type,
ContextInfo: contextInfo,
Ptt: &msg.Ptt,
},
}
return p
@ -402,7 +463,8 @@ type DocumentMessage struct {
func getDocumentMessage(msg *proto.WebMessageInfo) DocumentMessage {
doc := msg.GetMessage().GetDocumentMessage()
return DocumentMessage{
documentMessage := DocumentMessage{
Info: getMessageInfo(msg),
Title: doc.GetTitle(),
PageCount: doc.GetPageCount(),
@ -415,10 +477,17 @@ func getDocumentMessage(msg *proto.WebMessageInfo) DocumentMessage {
fileSha256: doc.GetFileSha256(),
fileLength: doc.GetFileLength(),
}
if contextInfo := doc.GetContextInfo(); contextInfo != nil {
documentMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return documentMessage
}
func getDocumentProto(msg DocumentMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
DocumentMessage: &proto.DocumentMessage{
JpegThumbnail: msg.Thumbnail,
@ -430,6 +499,7 @@ func getDocumentProto(msg DocumentMessage) *proto.WebMessageInfo {
PageCount: &msg.PageCount,
Title: &msg.Title,
Mimetype: &msg.Type,
ContextInfo: contextInfo,
},
}
return p
@ -457,7 +527,8 @@ type LocationMessage struct {
func GetLocationMessage(msg *proto.WebMessageInfo) LocationMessage {
loc := msg.GetMessage().GetLocationMessage()
return LocationMessage{
locationMessage := LocationMessage{
Info: getMessageInfo(msg),
DegreesLatitude: loc.GetDegreesLatitude(),
DegreesLongitude: loc.GetDegreesLongitude(),
@ -466,10 +537,18 @@ func GetLocationMessage(msg *proto.WebMessageInfo) LocationMessage {
Url: loc.GetUrl(),
JpegThumbnail: loc.GetJpegThumbnail(),
}
if contextInfo := loc.GetContextInfo(); contextInfo != nil {
locationMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return locationMessage
}
func GetLocationProto(msg LocationMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
LocationMessage: &proto.LocationMessage{
DegreesLatitude: &msg.DegreesLatitude,
@ -478,6 +557,7 @@ func GetLocationProto(msg LocationMessage) *proto.WebMessageInfo {
Address: &msg.Address,
Url: &msg.Url,
JpegThumbnail: msg.JpegThumbnail,
ContextInfo: contextInfo,
},
}
return p
@ -500,7 +580,8 @@ type LiveLocationMessage struct {
func GetLiveLocationMessage(msg *proto.WebMessageInfo) LiveLocationMessage {
loc := msg.GetMessage().GetLiveLocationMessage()
return LiveLocationMessage{
liveLocationMessage := LiveLocationMessage{
Info: getMessageInfo(msg),
DegreesLatitude: loc.GetDegreesLatitude(),
DegreesLongitude: loc.GetDegreesLongitude(),
@ -511,10 +592,17 @@ func GetLiveLocationMessage(msg *proto.WebMessageInfo) LiveLocationMessage {
SequenceNumber: loc.GetSequenceNumber(),
JpegThumbnail: loc.GetJpegThumbnail(),
}
if contextInfo := loc.GetContextInfo(); contextInfo != nil {
liveLocationMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return liveLocationMessage
}
func GetLiveLocationProto(msg LiveLocationMessage) *proto.WebMessageInfo {
p := getInfoProto(&msg.Info)
contextInfo := getContextInfoProto(&msg.Info)
p.Message = &proto.Message{
LiveLocationMessage: &proto.LiveLocationMessage{
DegreesLatitude: &msg.DegreesLatitude,
@ -525,11 +613,49 @@ func GetLiveLocationProto(msg LiveLocationMessage) *proto.WebMessageInfo {
Caption: &msg.Caption,
SequenceNumber: &msg.SequenceNumber,
JpegThumbnail: msg.JpegThumbnail,
ContextInfo: contextInfo,
},
}
return p
}
/*
StickerMessage represents a sticker message.
*/
type StickerMessage struct {
Info MessageInfo
Thumbnail []byte
Type string
Content io.Reader
url string
mediaKey []byte
fileEncSha256 []byte
fileSha256 []byte
fileLength uint64
}
func getStickerMessage(msg *proto.WebMessageInfo) StickerMessage {
sticker := msg.GetMessage().GetStickerMessage()
StickerMessage := StickerMessage{
Info: getMessageInfo(msg),
Thumbnail: sticker.GetPngThumbnail(),
url: sticker.GetUrl(),
mediaKey: sticker.GetMediaKey(),
Type: sticker.GetMimetype(),
fileEncSha256: sticker.GetFileEncSha256(),
fileSha256: sticker.GetFileSha256(),
fileLength: sticker.GetFileLength(),
}
if contextInfo := sticker.GetContextInfo(); contextInfo != nil {
StickerMessage.Info.QuotedMessageID = contextInfo.GetStanzaId()
}
return StickerMessage
}
func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} {
switch {
@ -557,6 +683,9 @@ func ParseProtoMessage(msg *proto.WebMessageInfo) interface{} {
case msg.GetMessage().GetLiveLocationMessage() != nil:
return GetLiveLocationMessage(msg)
case msg.GetMessage().GetStickerMessage() != nil:
return getStickerMessage(msg)
default:
//cannot match message
}

View File

@ -7,6 +7,8 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
"strconv"
"strings"
"sync/atomic"
"time"
@ -88,6 +90,53 @@ func newInfoFromReq(info map[string]interface{}) *Info {
return ret
}
/*
CheckCurrentServerVersion is based on the login method logic in order to establish the websocket connection and get
the current version from the server with the `admin init` command. This can be very useful for automations in which
you need to quickly perceive new versions (mostly patches) and update your application so it suddenly stops working.
*/
func CheckCurrentServerVersion() ([]int, error) {
wac, err := NewConn(5 * time.Second)
if err != nil {
return nil, fmt.Errorf("fail to create connection")
}
clientId := make([]byte, 16)
if _, err = rand.Read(clientId); err != nil {
return nil, fmt.Errorf("error creating random ClientId: %v", err)
}
b64ClientId := base64.StdEncoding.EncodeToString(clientId)
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, b64ClientId, true}
loginChan, err := wac.writeJson(login)
if err != nil {
return nil, fmt.Errorf("error writing login", err)
}
// Retrieve an answer from the websocket
var r string
select {
case r = <-loginChan:
case <-time.After(wac.msgTimeout):
return nil, fmt.Errorf("login connection timed out")
}
var resp map[string]interface{}
if err = json.Unmarshal([]byte(r), &resp); err != nil {
return nil, fmt.Errorf("error decoding login", err)
}
// Take the curr property as X.Y.Z and split it into as int slice
curr := resp["curr"].(string)
currArray := strings.Split(curr, ".")
version := make([]int, len(currArray))
for i := range version {
version[i], _ = strconv.Atoi(currArray[i])
}
return version, nil
}
/*
SetClientName sets the long and short client names that are sent to WhatsApp when logging in and displayed in the
WhatsApp Web device list. As the values are only sent when logging in, changing them after logging in is not possible.
@ -108,6 +157,11 @@ func (wac *Conn) SetClientVersion(major int, minor int, patch int) {
waVersion = []int{major, minor, patch}
}
// GetClientVersion returns WhatsApp client version
func (wac *Conn) GetClientVersion() []int {
return waVersion
}
/*
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel

View File

@ -78,13 +78,13 @@ func (wac *Conn) sendKeepAlive() error {
return nil
}
/*
/*
When phone is unreachable, WhatsAppWeb sends ["admin","test"] time after time to try a successful contact.
Tested with Airplane mode and no connection at all.
*/
func (wac *Conn) sendAdminTest() (bool, error) {
data := []interface{}{"admin", "test"}
r, err := wac.writeJson(data)
if err != nil {
return false, errors.Wrap(err, "error sending admin test")
@ -103,9 +103,9 @@ func (wac *Conn) sendAdminTest() (bool, error) {
if len(response) == 2 && response[0].(string) == "Pong" && response[1].(bool) == true {
return true, nil
} else{
} else {
return false, nil
}
}
}
func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) {