Documentation ¶
Overview ¶
Package whatsappprotocol implements a client for interacting with the WhatsApp web multidevice API.
Example ¶
package main import ( "context" "fmt" whatsappprotocol "git.vxapi.xyz/joey/whatsappprotocol" "os" "os/signal" "syscall" "git.vxapi.xyz/joey/whatsappprotocol/store/sqlstore" "git.vxapi.xyz/joey/whatsappprotocol/types/events" waLog "git.vxapi.xyz/joey/whatsappprotocol/util/log" ) func eventHandler(evt interface{}) { switch v := evt.(type) { case *events.Message: fmt.Println("Received a message!", v.Message.GetConversation()) } } func main() { dbLog := waLog.Stdout("Database", "DEBUG", true) // Make sure you add appropriate DB connector imports, e.g. github.com/mattn/go-sqlite3 for SQLite container, err := sqlstore.New("sqlite3", "file:examplestore.db?_foreign_keys=on", dbLog) if err != nil { panic(err) } // If you want multiple sessions, remember their JIDs and use .GetDevice(jid) or .GetAllDevices() instead. deviceStore, err := container.GetFirstDevice() if err != nil { panic(err) } clientLog := waLog.Stdout("Client", "DEBUG", true) client := whatsappprotocol.NewClient(deviceStore, clientLog) client.AddEventHandler(eventHandler) if client.Store.ID == nil { // No ID stored, new login qrChan, _ := client.GetQRChannel(context.Background()) err = client.Connect() if err != nil { panic(err) } for evt := range qrChan { if evt.Event == "code" { // Render the QR code here // e.g. qrterminal.GenerateHalfBlock(evt.Code, qrterminal.L, os.Stdout) // or just manually `echo 2@... | qrencode -t ansiutf8` in a terminal fmt.Println("QR code:", evt.Code) } else { fmt.Println("Login event:", evt.Event) } } } else { // Already logged in, just connect err = client.Connect() if err != nil { panic(err) } } // Listen to Ctrl+C (you can also do something else that prevents the program from exiting) c := make(chan os.Signal) signal.Notify(c, os.Interrupt, syscall.SIGTERM) <-c client.Disconnect() }
Output:
Index ¶
- Constants
- Variables
- func DecryptMediaRetryNotification(evt *events.MediaRetry, mediaKey []byte) (*waProto.MediaRetryNotification, error)
- func GenerateMessageID() types.MessageIDdeprecated
- func HashPollOptions(optionNames []string) [][]byte
- func ParseDisappearingTimerString(val string) (time.Duration, bool)
- type CheckUpdateResponse
- type Client
- func (cli *Client) AddEventHandler(handler EventHandler) uint32
- func (cli *Client) BuildEdit(chat types.JID, id types.MessageID, newContent *waProto.Message) *waProto.Message
- func (cli *Client) BuildHistorySyncRequest(lastKnownMessageInfo *types.MessageInfo, count int) *waProto.Message
- func (cli *Client) BuildMessageKey(chat, sender types.JID, id types.MessageID) *waProto.MessageKey
- func (cli *Client) BuildPollCreation(name string, optionNames []string, selectableOptionCount int) *waProto.Message
- func (cli *Client) BuildPollVote(pollInfo *types.MessageInfo, optionNames []string) (*waProto.Message, error)
- func (cli *Client) BuildReaction(chat, sender types.JID, id types.MessageID, reaction string) *waProto.Message
- func (cli *Client) BuildRevoke(chat, sender types.JID, id types.MessageID) *waProto.Message
- func (cli *Client) BuildUnavailableMessageRequest(chat, sender types.JID, id string) *waProto.Message
- func (cli *Client) CheckUpdate() (respData CheckUpdateResponse, err error)
- func (cli *Client) Connect() error
- func (cli *Client) CreateGroup(req ReqCreateGroup) (*types.GroupInfo, error)
- func (cli *Client) DangerousInternals() *DangerousInternalClientdeprecated
- func (cli *Client) DecryptPollVote(vote *events.Message) (*waProto.PollVoteMessage, error)
- func (cli *Client) DecryptReaction(reaction *events.Message) (*waProto.ReactionMessage, error)
- func (cli *Client) Disconnect()
- func (cli *Client) Download(msg DownloadableMessage) ([]byte, error)
- func (cli *Client) DownloadAny(msg *waProto.Message) (data []byte, err error)
- func (cli *Client) DownloadMediaWithPath(directPath string, encFileHash, fileHash, mediaKey []byte, fileLength int, ...) (data []byte, err error)
- func (cli *Client) DownloadThumbnail(msg DownloadableThumbnail) ([]byte, error)
- func (cli *Client) EncryptPollVote(pollInfo *types.MessageInfo, vote *waProto.PollVoteMessage) (*waProto.PollUpdateMessage, error)
- func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) error
- func (cli *Client) GenerateMessageID() types.MessageID
- func (cli *Client) GetBlocklist() (*types.Blocklist, error)
- func (cli *Client) GetContactQRLink(revoke bool) (string, error)
- func (cli *Client) GetGroupInfo(jid types.JID) (*types.GroupInfo, error)
- func (cli *Client) GetGroupInfoFromInvite(jid, inviter types.JID, code string, expiration int64) (*types.GroupInfo, error)
- func (cli *Client) GetGroupInfoFromLink(code string) (*types.GroupInfo, error)
- func (cli *Client) GetGroupInviteLink(jid types.JID, reset bool) (string, error)
- func (cli *Client) GetJoinedGroups() ([]*types.GroupInfo, error)
- func (cli *Client) GetLinkedGroupsParticipants(community types.JID) ([]types.JID, error)
- func (cli *Client) GetPrivacySettings() (settings types.PrivacySettings)
- func (cli *Client) GetProfilePictureInfo(jid types.JID, params *GetProfilePictureParams) (*types.ProfilePictureInfo, error)
- func (cli *Client) GetQRChannel(ctx context.Context) (<-chan QRChannelItem, error)
- func (cli *Client) GetStatusPrivacy() ([]types.StatusPrivacy, error)
- func (cli *Client) GetSubGroups(community types.JID) ([]*types.GroupLinkTarget, error)
- func (cli *Client) GetUserDevices(jids []types.JID) ([]types.JID, error)
- func (cli *Client) GetUserDevicesContext(ctx context.Context, jids []types.JID) ([]types.JID, error)
- func (cli *Client) GetUserInfo(jids []types.JID) (map[types.JID]types.UserInfo, error)
- func (cli *Client) IsConnected() bool
- func (cli *Client) IsLoggedIn() bool
- func (cli *Client) IsOnWhatsApp(phones []string) ([]types.IsOnWhatsAppResponse, error)
- func (cli *Client) JoinGroupWithInvite(jid, inviter types.JID, code string, expiration int64) error
- func (cli *Client) JoinGroupWithLink(code string) (types.JID, error)
- func (cli *Client) LeaveGroup(jid types.JID) error
- func (cli *Client) LinkGroup(parent, child types.JID) error
- func (cli *Client) Logout() error
- func (cli *Client) MarkRead(ids []types.MessageID, timestamp time.Time, chat, sender types.JID) error
- func (cli *Client) MobileLogin(loginDto *app.LoginDto) error
- func (cli *Client) PairPhone(phone string, showPushNotification bool, clientType PairClientType, ...) (string, error)
- func (cli *Client) ParseWebMessage(chatJID types.JID, webMsg *waProto.WebMessageInfo) (*events.Message, error)
- func (cli *Client) RemoveEventHandler(id uint32) bool
- func (cli *Client) RemoveEventHandlers()
- func (cli *Client) ResolveBusinessMessageLink(code string) (*types.BusinessMessageLinkTarget, error)
- func (cli *Client) ResolveContactQRLink(code string) (*types.ContactQRLinkTarget, error)
- func (cli *Client) RevokeMessage(chat types.JID, id types.MessageID) (SendResponse, error)deprecated
- func (cli *Client) SendAppState(patch appstate.PatchInfo) error
- func (cli *Client) SendChatPresence(jid types.JID, state types.ChatPresence, media types.ChatPresenceMedia) error
- func (cli *Client) SendMediaRetryReceipt(message *types.MessageInfo, mediaKey []byte) error
- func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waProto.Message, ...) (resp SendResponse, err error)
- func (cli *Client) SendPresence(state types.Presence) error
- func (cli *Client) SetDisappearingTimer(chat types.JID, timer time.Duration) (err error)
- func (cli *Client) SetForceActiveDeliveryReceipts(active bool)
- func (cli *Client) SetGroupAnnounce(jid types.JID, announce bool) error
- func (cli *Client) SetGroupLocked(jid types.JID, locked bool) error
- func (cli *Client) SetGroupName(jid types.JID, name string) error
- func (cli *Client) SetGroupPhoto(jid types.JID, avatar []byte) (string, error)
- func (cli *Client) SetGroupTopic(jid types.JID, previousID, newID, topic string) error
- func (cli *Client) SetPassive(passive bool) error
- func (cli *Client) SetProxy(proxy socket.Proxy)
- func (cli *Client) SetProxyAddress(addr string) error
- func (cli *Client) SetStatusMessage(msg string) error
- func (cli *Client) SubscribePresence(jid types.JID) error
- func (cli *Client) TryFetchPrivacySettings(ignoreCache bool) (*types.PrivacySettings, error)
- func (cli *Client) UnlinkGroup(parent, child types.JID) error
- func (cli *Client) UpdateBlocklist(jid types.JID, action events.BlocklistChangeAction) (*types.Blocklist, error)
- func (cli *Client) UpdateGroupParticipants(jid types.JID, participantChanges map[types.JID]ParticipantChange) (*waBinary.Node, error)
- func (cli *Client) Upload(ctx context.Context, plaintext []byte, appInfo MediaType) (resp UploadResponse, err error)
- func (cli *Client) WaitForConnection(timeout time.Duration) bool
- type DangerousInfoQuery
- type DangerousInfoQueryType
- type DangerousInternalClient
- func (int *DangerousInternalClient) CancelResponse(reqID string, ch chan *waBinary.Node)
- func (int *DangerousInternalClient) GetServerPreKeyCount() (int, error)
- func (int *DangerousInternalClient) QueryMediaConn() (*MediaConn, error)
- func (int *DangerousInternalClient) RefreshMediaConn(force bool) (*MediaConn, error)
- func (int *DangerousInternalClient) RequestAppStateKeys(ctx context.Context, keyIDs [][]byte)
- func (int *DangerousInternalClient) SendIQ(query DangerousInfoQuery) (*waBinary.Node, error)
- func (int *DangerousInternalClient) SendIQAsync(query DangerousInfoQuery) (<-chan *waBinary.Node, error)
- func (int *DangerousInternalClient) SendNode(node waBinary.Node) error
- func (int *DangerousInternalClient) SendRetryReceipt(node *waBinary.Node, info *types.MessageInfo, forceIncludeIdentity bool)
- func (int *DangerousInternalClient) WaitResponse(reqID string) chan *waBinary.Node
- type DisconnectedError
- type DownloadHTTPError
- type DownloadableMessage
- type DownloadableThumbnail
- type ElementMissingError
- type EventHandler
- type GetProfilePictureParams
- type IQError
- type MediaConn
- type MediaConnHost
- type MediaType
- type MessageDebugTimings
- type MsgSecretType
- type PairClientType
- type PairDatabaseError
- type PairProtoError
- type ParticipantChange
- type QRChannelItem
- type RecentMessage
- type ReqCreateGroup
- type SendRequestExtra
- type SendResponse
- type UploadResponse
Examples ¶
Constants ¶
const ( // WantedPreKeyCount is the number of prekeys that the client should upload to the WhatsApp servers in a single batch. WantedPreKeyCount = 50 // MinPreKeyCount is the number of prekeys when the client will upload a new batch of prekeys to the WhatsApp servers. MinPreKeyCount = 5 )
const ( DisappearingTimerOff = time.Duration(0) DisappearingTimer24Hours = 24 * time.Hour DisappearingTimer7Days = 7 * 24 * time.Hour DisappearingTimer90Days = 90 * 24 * time.Hour )
const ( EditAttributeEmpty = "" EditAttributeMessageEdit = "1" EditAttributeSenderRevoke = "7" EditAttributeAdminRevoke = "8" )
const BusinessMessageLinkDirectPrefix = "https://api.whatsapp.com/message/"
const BusinessMessageLinkPrefix = "https://wa.me/message/"
const CheckUpdateURL = "https://web.whatsapp.com/check-update"
CheckUpdateURL is the base URL to check for WhatsApp web updates.
const ContactQRLinkDirectPrefix = "https://api.whatsapp.com/qr/"
const ContactQRLinkPrefix = "https://wa.me/qr/"
const EditWindow = 20 * time.Minute
EditWindow specifies how long a message can be edited for after it was sent.
const InviteLinkPrefix = "https://chat.whatsapp.com/"
const NoiseHandshakeResponseTimeout = 20 * time.Second
const QRChannelEventCode = "code"
const QRChannelEventError = "error"
const RemoveReactionText = ""
Variables ¶
var ( ErrNoSession = errors.New("can't encrypt message for device: no signal session established") ErrIQTimedOut = errors.New("info query timed out") ErrNotConnected = errors.New("websocket not connected") ErrNotLoggedIn = errors.New("the store doesn't contain a device JID") ErrMessageTimedOut = errors.New("timed out waiting for message send response") ErrAlreadyConnected = errors.New("websocket is already connected") ErrQRAlreadyConnected = errors.New("GetQRChannel must be called before connecting") ErrQRStoreContainsID = errors.New("GetQRChannel can only be called when there's no user ID in the client's Store") ErrNoPushName = errors.New("can't send presence without PushName set") ErrNoPrivacyToken = errors.New("no privacy token stored") ErrAppStateUpdate = errors.New("server returned error updating app state") )
Miscellaneous errors
var ( ErrPairInvalidDeviceIdentityHMAC = errors.New("invalid device identity HMAC in pair success message") ErrPairInvalidDeviceSignature = errors.New("invalid device signature in pair success message") ErrPairRejectedLocally = errors.New("local PrePairCallback rejected pairing") )
Errors that happen while confirming device pairing
var ( // whose privacy settings prevent you from seeing their profile picture (status code 401). ErrProfilePictureUnauthorized = errors.New("the user has hidden their profile picture from you") // ErrProfilePictureNotSet is returned by GetProfilePictureInfo when the given user or group doesn't have a profile // picture (status code 404). ErrProfilePictureNotSet = errors.New("that user or group does not have a profile picture") ErrGroupInviteLinkUnauthorized = errors.New("you don't have the permission to get the group's invite link") // ErrNotInGroup is returned by group info getting methods if you're not in the group (status code 403). ErrNotInGroup = errors.New("you're not participating in that group") // ErrGroupNotFound is returned by group info getting methods if the group doesn't exist (status code 404). ErrGroupNotFound = errors.New("that group does not exist") // ErrInviteLinkInvalid is returned by methods that use group invite links if the invite link is malformed. ErrInviteLinkInvalid = errors.New("that group invite link is not valid") // ErrInviteLinkRevoked is returned by methods that use group invite links if the invite link was valid, but has been revoked and can no longer be used. ErrInviteLinkRevoked = errors.New("that group invite link has been revoked") // ErrBusinessMessageLinkNotFound is returned by ResolveBusinessMessageLink if the link doesn't exist or has been revoked. ErrBusinessMessageLinkNotFound = errors.New("that business message link does not exist or has been revoked") // ErrContactQRLinkNotFound is returned by ResolveContactQRLink if the link doesn't exist or has been revoked. ErrContactQRLinkNotFound = errors.New("that contact QR link does not exist or has been revoked") // ErrInvalidImageFormat is returned by SetGroupPhoto if the given photo is not in the correct format. ErrInvalidImageFormat = errors.New("the given data is not a valid image") // ErrMediaNotAvailableOnPhone is returned by DecryptMediaRetryNotification if the given event contains error code 2. ErrMediaNotAvailableOnPhone = errors.New("media no longer available on phone") // ErrUnknownMediaRetryError is returned by DecryptMediaRetryNotification if the given event contains an unknown error code. ErrUnknownMediaRetryError = errors.New("unknown media retry error") // ErrInvalidDisappearingTimer is returned by SetDisappearingTimer if the given timer is not one of the allowed values. ErrInvalidDisappearingTimer = errors.New("invalid disappearing timer provided") )
var ( ErrBroadcastListUnsupported = errors.New("sending to non-status broadcast lists is not yet supported") ErrUnknownServer = errors.New("can't send message to unknown server") ErrRecipientADJID = errors.New("message recipient must be a user JID with no device part") ErrServerReturnedError = errors.New("server returned error") )
Some errors that Client.SendMessage can return
var ( ErrMediaDownloadFailedWith403 = DownloadHTTPError{Response: &http.Response{StatusCode: 403}} ErrMediaDownloadFailedWith404 = DownloadHTTPError{Response: &http.Response{StatusCode: 404}} ErrMediaDownloadFailedWith410 = DownloadHTTPError{Response: &http.Response{StatusCode: 410}} ErrNoURLPresent = errors.New("no url present") ErrFileLengthMismatch = errors.New("file length does not match") ErrTooShortFile = errors.New("file too short") ErrInvalidMediaHMAC = errors.New("invalid media hmac") ErrInvalidMediaEncSHA256 = errors.New("hash of media ciphertext doesn't match") ErrInvalidMediaSHA256 = errors.New("hash of media plaintext doesn't match") ErrUnknownMediaType = errors.New("unknown media type") ErrNothingDownloadableFound = errors.New("didn't find any attachments in message") )
Some errors that Client.Download can return
var ( ErrOriginalMessageSecretNotFound = errors.New("original message secret key not found") ErrNotEncryptedReactionMessage = errors.New("given message isn't an encrypted reaction message") ErrNotPollUpdateMessage = errors.New("given message isn't a poll update message") )
var ( ErrIQBadRequest error = &IQError{Code: 400, Text: "bad-request"} ErrIQNotAuthorized error = &IQError{Code: 401, Text: "not-authorized"} ErrIQForbidden error = &IQError{Code: 403, Text: "forbidden"} ErrIQNotFound error = &IQError{Code: 404, Text: "item-not-found"} ErrIQNotAllowed error = &IQError{Code: 405, Text: "not-allowed"} ErrIQNotAcceptable error = &IQError{Code: 406, Text: "not-acceptable"} ErrIQGone error = &IQError{Code: 410, Text: "gone"} ErrIQResourceLimit error = &IQError{Code: 419, Text: "resource-limit"} ErrIQLocked error = &IQError{Code: 423, Text: "locked"} ErrIQInternalServerError error = &IQError{Code: 500, Text: "internal-server-error"} ErrIQPartialServerError error = &IQError{Code: 530, Text: "partial-server-error"} )
Common errors returned by info queries for use with errors.Is
var ( // KeepAliveResponseDeadline specifies the duration to wait for a response to websocket keepalive pings. KeepAliveResponseDeadline = 10 * time.Second // KeepAliveIntervalMin specifies the minimum interval for websocket keepalive pings. KeepAliveIntervalMin = 20 * time.Second // KeepAliveIntervalMax specifies the maximum interval for websocket keepalive pings. KeepAliveIntervalMax = 30 * time.Second // KeepAliveMaxFailTime specifies the maximum time to wait before forcing a reconnect if keepalives fail repeatedly. KeepAliveMaxFailTime = 3 * time.Minute )
var ( // QRChannelSuccess is emitted from GetQRChannel when the pairing is successful. QRChannelSuccess = QRChannelItem{Event: "success"} // QRChannelTimeout is emitted from GetQRChannel if the socket gets disconnected by the server before the pairing is successful. QRChannelTimeout = QRChannelItem{Event: "timeout"} // QRChannelErrUnexpectedEvent is emitted from GetQRChannel if an unexpected connection event is received, // as that likely means that the pairing has already happened before the channel was set up. QRChannelErrUnexpectedEvent = QRChannelItem{Event: "err-unexpected-state"} // QRChannelClientOutdated is emitted from GetQRChannel if events.ClientOutdated is received. QRChannelClientOutdated = QRChannelItem{Event: "err-client-outdated"} // QRChannelScannedWithoutMultidevice is emitted from GetQRChannel if events.QRScannedWithoutMultidevice is received. QRChannelScannedWithoutMultidevice = QRChannelItem{Event: "err-scanned-without-multidevice"} )
Possible final items in the QR channel. In addition to these, an `error` event may be emitted, in which case the Error field will have the error that occurred during pairing.
var DefaultStatusPrivacy = []types.StatusPrivacy{{ Type: types.StatusPrivacyTypeContacts, IsDefault: true, }}
var ErrIQDisconnected = &DisconnectedError{Action: "info query"}
var RequestFromPhoneDelay = 5 * time.Second
RequestFromPhoneDelay specifies how long to wait for the sender to resend the message before requesting from your phone. This is only used if Client.AutomaticMessageRerequestFromPhone is true.
Functions ¶
func DecryptMediaRetryNotification ¶
func DecryptMediaRetryNotification(evt *events.MediaRetry, mediaKey []byte) (*waProto.MediaRetryNotification, error)
DecryptMediaRetryNotification decrypts a media retry notification using the media key. See Client.SendMediaRetryReceipt for more info on how to use this.
func GenerateMessageID
deprecated
GenerateMessageID generates a random string that can be used as a message ID on WhatsApp.
msgID := whatsappprotocol.GenerateMessageID() cli.SendMessage(context.Background(), targetJID, &waProto.Message{...}, whatsappprotocol.SendRequestExtra{ID: msgID})
Deprecated: WhatsApp web has switched to using a hash of the current timestamp, user id and random bytes. Use Client.GenerateMessageID instead.
func HashPollOptions ¶
HashPollOptions hashes poll option names using SHA-256 for voting. This is used by BuildPollVote to convert selected option names to hashes.
func ParseDisappearingTimerString ¶
ParseDisappearingTimerString parses common human-readable disappearing message timer strings into Duration values. If the string doesn't look like one of the allowed values (0, 24h, 7d, 90d), the second return value is false.
Types ¶
type CheckUpdateResponse ¶
type CheckUpdateResponse struct { IsBroken bool IsBelowSoft bool IsBelowHard bool CurrentVersion string ParsedVersion store.WAVersionContainer `json:"-"` }
CheckUpdateResponse is the data returned by CheckUpdate.
func CheckUpdate ¶
func CheckUpdate(httpClient *http.Client) (respData CheckUpdateResponse, err error)
CheckUpdate asks the WhatsApp servers if there is an update available.
type Client ¶
type Client struct { Store *store.Device Log waLog.Logger EnableAutoReconnect bool LastSuccessfulConnect time.Time AutoReconnectErrors int // EmitAppStateEventsOnFullSync can be set to true if you want to get app state events emitted // even when re-syncing the whole state. EmitAppStateEventsOnFullSync bool AutomaticMessageRerequestFromPhone bool // GetMessageForRetry is used to find the source message for handling retry receipts // when the message is not found in the recently sent message cache. GetMessageForRetry func(requester, to types.JID, id types.MessageID) *waProto.Message // PreRetryCallback is called before a retry receipt is accepted. // If it returns false, the accepting will be cancelled and the retry receipt will be ignored. PreRetryCallback func(receipt *events.Receipt, id types.MessageID, retryCount int, msg *waProto.Message) bool // PrePairCallback is called before pairing is completed. If it returns false, the pairing will be cancelled and // the client will disconnect. PrePairCallback func(jid types.JID, platform, businessName string) bool // Should untrusted identity errors be handled automatically? If true, the stored identity and existing signal // sessions will be removed on untrusted identity errors, and an events.IdentityChange will be dispatched. // If false, decrypting a message from untrusted devices will fail. AutoTrustIdentity bool // Should sending to own devices be skipped when sending broadcasts? // This works around a bug in the WhatsApp android app where it crashes if you send a status message from a linked device. DontSendSelfBroadcast bool // Should SubscribePresence return an error if no privacy token is stored for the user? ErrorOnSubscribePresenceWithoutToken bool // contains filtered or unexported fields }
Client contains everything necessary to connect to and interact with the WhatsApp web API.
func NewClient ¶
NewClient initializes a new WhatsApp web client.
The logger can be nil, it will default to a no-op logger.
The device store must be set. A default SQL-backed implementation is available in the store/sqlstore package.
container, err := sqlstore.New("sqlite3", "file:yoursqlitefile.db?_foreign_keys=on", nil) if err != nil { panic(err) } // If you want multiple sessions, remember their JIDs and use .GetDevice(jid) or .GetAllDevices() instead. deviceStore, err := container.GetFirstDevice() if err != nil { panic(err) } client := whatsappprotocol.NewClient(deviceStore, nil)
func (*Client) AddEventHandler ¶
func (cli *Client) AddEventHandler(handler EventHandler) uint32
AddEventHandler registers a new function to receive all events emitted by this client.
The returned integer is the event handler ID, which can be passed to RemoveEventHandler to remove it.
All registered event handlers will receive all events. You should use a type switch statement to filter the events you want:
func myEventHandler(evt interface{}) { switch v := evt.(type) { case *events.Message: fmt.Println("Received a message!") case *events.Receipt: fmt.Println("Received a receipt!") } }
If you want to access the Client instance inside the event handler, the recommended way is to wrap the whole handler in another struct:
type MyClient struct { WAClient *whatsappprotocol.Client eventHandlerID uint32 } func (mycli *MyClient) register() { mycli.eventHandlerID = mycli.WAClient.AddEventHandler(mycli.myEventHandler) } func (mycli *MyClient) myEventHandler(evt interface{}) { // Handle event and access mycli.WAClient }
func (*Client) BuildEdit ¶
func (cli *Client) BuildEdit(chat types.JID, id types.MessageID, newContent *waProto.Message) *waProto.Message
BuildEdit builds a message edit message using the given variables. The built message can be sent normally using Client.SendMessage.
resp, err := cli.SendMessage(context.Background(), chat, cli.BuildEdit(chat, originalMessageID, &waProto.Message{ Conversation: proto.String("edited message"), })
func (*Client) BuildHistorySyncRequest ¶
func (cli *Client) BuildHistorySyncRequest(lastKnownMessageInfo *types.MessageInfo, count int) *waProto.Message
BuildHistorySyncRequest builds a message to request additional history from the user's primary device.
The built message can be sent using Client.SendMessage, but you must pass whatsappprotocol.SendRequestExtra{Peer: true} as the last parameter. The response will come as an *events.HistorySync with type `ON_DEMAND`.
The response will contain to `count` messages immediately before the given message. The recommended number of messages to request at a time is 50.
func (*Client) BuildMessageKey ¶
BuildMessageKey builds a MessageKey object, which is used to refer to previous messages for things such as replies, revocations and reactions.
func (*Client) BuildPollCreation ¶
func (cli *Client) BuildPollCreation(name string, optionNames []string, selectableOptionCount int) *waProto.Message
BuildPollCreation builds a poll creation message with the given poll name, options and maximum number of selections. The built message can be sent normally using Client.SendMessage.
resp, err := cli.SendMessage(context.Background(), chat, cli.BuildPollCreation("meow?", []string{"yes", "no"}, 1))
func (*Client) BuildPollVote ¶
func (cli *Client) BuildPollVote(pollInfo *types.MessageInfo, optionNames []string) (*waProto.Message, error)
BuildPollVote builds a poll vote message using the given poll message info and option names. The built message can be sent normally using Client.SendMessage.
For example, to vote for the first option after receiving a message event (*events.Message):
if evt.Message.GetPollCreationMessage() != nil { pollVoteMsg, err := cli.BuildPollVote(&evt.Info, []string{evt.Message.GetPollCreationMessage().GetOptions()[0].GetOptionName()}) if err != nil { fmt.Println(":(", err) return } resp, err := cli.SendMessage(context.Background(), evt.Info.Chat, pollVoteMsg) }
func (*Client) BuildReaction ¶
func (cli *Client) BuildReaction(chat, sender types.JID, id types.MessageID, reaction string) *waProto.Message
BuildReaction builds a message reaction message using the given variables. The built message can be sent normally using Client.SendMessage.
resp, err := cli.SendMessage(context.Background(), chat, cli.BuildReaction(chat, senderJID, targetMessageID, "🐈️")
func (*Client) BuildRevoke ¶
BuildRevoke builds a message revocation message using the given variables. The built message can be sent normally using Client.SendMessage.
To revoke your own messages, pass your JID or an empty JID as the second parameter (sender).
resp, err := cli.SendMessage(context.Background(), chat, cli.BuildRevoke(chat, types.EmptyJID, originalMessageID)
To revoke someone else's messages when you are group admin, pass the message sender's JID as the second parameter.
resp, err := cli.SendMessage(context.Background(), chat, cli.BuildRevoke(chat, senderJID, originalMessageID)
func (*Client) BuildUnavailableMessageRequest ¶
func (cli *Client) BuildUnavailableMessageRequest(chat, sender types.JID, id string) *waProto.Message
BuildUnavailableMessageRequest builds a message to request the user's primary device to send the copy of a message that this client was unable to decrypt.
The built message can be sent using Client.SendMessage, but you must pass whatsappprotocol.SendRequestExtra{Peer: true} as the last parameter. The full response will come as a ProtocolMessage with type `PEER_DATA_OPERATION_REQUEST_RESPONSE_MESSAGE`. The response events will also be dispatched as normal *events.Message's with UnavailableRequestID set to the request message ID.
func (*Client) CheckUpdate ¶
func (cli *Client) CheckUpdate() (respData CheckUpdateResponse, err error)
CheckUpdate asks the WhatsApp servers if there is an update available (using the HTTP client and proxy settings of this whatsappprotocol Client instance).
func (*Client) Connect ¶
Connect connects the client to the WhatsApp web websocket. After connection, it will either authenticate if there's data in the device store, or emit a QREvent to set up a new link.
func (*Client) CreateGroup ¶
func (cli *Client) CreateGroup(req ReqCreateGroup) (*types.GroupInfo, error)
CreateGroup creates a group on WhatsApp with the given name and participants.
See ReqCreateGroup for parameters.
func (*Client) DangerousInternals
deprecated
func (cli *Client) DangerousInternals() *DangerousInternalClient
DangerousInternals allows access to some unexported methods in Client.
Deprecated: dangerous
func (*Client) DecryptPollVote ¶
DecryptPollVote decrypts a poll update message. The vote itself includes SHA-256 hashes of the selected options.
if evt.Message.GetPollUpdateMessage() != nil { pollVote, err := cli.DecryptPollVote(evt) if err != nil { fmt.Println(":(", err) return } fmt.Println("Selected hashes:") for _, hash := range pollVote.GetSelectedOptions() { fmt.Printf("- %X\n", hash) } }
func (*Client) DecryptReaction ¶
DecryptReaction decrypts a reaction update message. This form of reactions hasn't been rolled out yet, so this function is likely not of much use.
if evt.Message.GetEncReactionMessage() != nil { reaction, err := cli.DecryptReaction(evt) if err != nil { fmt.Println(":(", err) return } fmt.Printf("Reaction message: %+v\n", reaction) }
func (*Client) Disconnect ¶
func (cli *Client) Disconnect()
Disconnect disconnects from the WhatsApp web websocket.
This will not emit any events, the Disconnected event is only used when the connection is closed by the server or a network error.
func (*Client) Download ¶
func (cli *Client) Download(msg DownloadableMessage) ([]byte, error)
Download downloads the attachment from the given protobuf message.
The attachment is a specific part of a Message protobuf struct, not the message itself, e.g.
var msg *waProto.Message ... imageData, err := cli.Download(msg.GetImageMessage())
You can also use DownloadAny to download the first non-nil sub-message.
func (*Client) DownloadAny ¶
DownloadAny loops through the downloadable parts of the given message and downloads the first non-nil item.
func (*Client) DownloadMediaWithPath ¶
func (cli *Client) DownloadMediaWithPath(directPath string, encFileHash, fileHash, mediaKey []byte, fileLength int, mediaType MediaType, mmsType string) (data []byte, err error)
DownloadMediaWithPath downloads an attachment by manually specifying the path and encryption details.
func (*Client) DownloadThumbnail ¶
func (cli *Client) DownloadThumbnail(msg DownloadableThumbnail) ([]byte, error)
DownloadThumbnail downloads a thumbnail from a message.
This is primarily intended for downloading link preview thumbnails, which are in ExtendedTextMessage:
var msg *waProto.Message ... thumbnailImageBytes, err := cli.DownloadThumbnail(msg.GetExtendedTextMessage())
func (*Client) EncryptPollVote ¶
func (cli *Client) EncryptPollVote(pollInfo *types.MessageInfo, vote *waProto.PollVoteMessage) (*waProto.PollUpdateMessage, error)
EncryptPollVote encrypts a poll vote message. This is a slightly lower-level function, using BuildPollVote is recommended.
func (*Client) FetchAppState ¶
func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) error
FetchAppState fetches updates to the given type of app state. If fullSync is true, the current cached state will be removed and all app state patches will be re-fetched from the server.
func (*Client) GenerateMessageID ¶
GenerateMessageID generates a random string that can be used as a message ID on WhatsApp.
msgID := cli.GenerateMessageID() cli.SendMessage(context.Background(), targetJID, &waProto.Message{...}, whatsappprotocol.SendRequestExtra{ID: msgID})
func (*Client) GetBlocklist ¶
GetBlocklist gets the list of users that this user has blocked.
func (*Client) GetContactQRLink ¶
GetContactQRLink gets your own contact share QR link that can be resolved using ResolveContactQRLink (or scanned with the official apps when encoded as a QR code).
If the revoke parameter is set to true, it will ask the server to revoke the previous link and generate a new one.
func (*Client) GetGroupInfo ¶
GetGroupInfo requests basic info about a group chat from the WhatsApp servers.
func (*Client) GetGroupInfoFromInvite ¶
func (cli *Client) GetGroupInfoFromInvite(jid, inviter types.JID, code string, expiration int64) (*types.GroupInfo, error)
GetGroupInfoFromInvite gets the group info from an invite message.
Note that this is specifically for invite messages, not invite links. Use GetGroupInfoFromLink for resolving chat.whatsapp.com links.
func (*Client) GetGroupInfoFromLink ¶
GetGroupInfoFromLink resolves the given invite link and asks the WhatsApp servers for info about the group. This will not cause the user to join the group.
func (*Client) GetGroupInviteLink ¶
GetGroupInviteLink requests the invite link to the group from the WhatsApp servers.
If reset is true, then the old invite link will be revoked and a new one generated.
func (*Client) GetJoinedGroups ¶
GetJoinedGroups returns the list of groups the user is participating in.
func (*Client) GetLinkedGroupsParticipants ¶
GetLinkedGroupsParticipants gets all the participants in the groups of the given community.
func (*Client) GetPrivacySettings ¶
func (cli *Client) GetPrivacySettings() (settings types.PrivacySettings)
GetPrivacySettings will get the user's privacy settings. If an error occurs while fetching them, the error will be logged, but the method will just return an empty struct.
func (*Client) GetProfilePictureInfo ¶
func (cli *Client) GetProfilePictureInfo(jid types.JID, params *GetProfilePictureParams) (*types.ProfilePictureInfo, error)
GetProfilePictureInfo gets the URL where you can download a WhatsApp user's profile picture or group's photo.
Optionally, you can pass the last known profile picture ID. If the profile picture hasn't changed, this will return nil with no error.
To get a community photo, you should pass `IsCommunity: true`, as otherwise you may get a 401 error.
func (*Client) GetQRChannel ¶
func (cli *Client) GetQRChannel(ctx context.Context) (<-chan QRChannelItem, error)
GetQRChannel returns a channel that automatically outputs a new QR code when the previous one expires.
This must be called *before* Connect(). It will then listen to all the relevant events from the client.
The last value to be emitted will be a special event like "success", "timeout" or another error code depending on the result of the pairing. The channel will be closed immediately after one of those.
func (*Client) GetStatusPrivacy ¶
func (cli *Client) GetStatusPrivacy() ([]types.StatusPrivacy, error)
GetStatusPrivacy gets the user's status privacy settings (who to send status broadcasts to).
There can be multiple different stored settings, the first one is always the default.
func (*Client) GetSubGroups ¶
GetSubGroups gets the subgroups of the given community.
func (*Client) GetUserDevices ¶
GetUserDevices gets the list of devices that the given user has. The input should be a list of regular JIDs, and the output will be a list of AD JIDs. The local device will not be included in the output even if the user's JID is included in the input. All other devices will be included.
func (*Client) GetUserDevicesContext ¶
func (*Client) GetUserInfo ¶
GetUserInfo gets basic user info (avatar, status, verified business name, device list).
func (*Client) IsConnected ¶
IsConnected checks if the client is connected to the WhatsApp web websocket. Note that this doesn't check if the client is authenticated. See the IsLoggedIn field for that.
func (*Client) IsLoggedIn ¶
IsLoggedIn returns true after the client is successfully connected and authenticated on WhatsApp.
func (*Client) IsOnWhatsApp ¶
func (cli *Client) IsOnWhatsApp(phones []string) ([]types.IsOnWhatsAppResponse, error)
IsOnWhatsApp checks if the given phone numbers are registered on WhatsApp. The phone numbers should be in international format, including the `+` prefix.
func (*Client) JoinGroupWithInvite ¶
JoinGroupWithInvite joins a group using an invite message.
Note that this is specifically for invite messages, not invite links. Use JoinGroupWithLink for joining with chat.whatsapp.com links.
func (*Client) JoinGroupWithLink ¶
JoinGroupWithLink joins the group using the given invite link.
func (*Client) LeaveGroup ¶
LeaveGroup leaves the specified group on WhatsApp.
func (*Client) LinkGroup ¶
LinkGroup adds an existing group as a child group in a community.
To create a new group within a community, set LinkedParentJID in the CreateGroup request.
func (*Client) Logout ¶
Logout sends a request to unlink the device, then disconnects from the websocket and deletes the local device store.
If the logout request fails, the disconnection and local data deletion will not happen either. If an error is returned, but you want to force disconnect/clear data, call Client.Disconnect() and Client.Store.Delete() manually.
Note that this will not emit any events. The LoggedOut event is only used for external logouts (triggered by the user from the main device or by WhatsApp servers).
func (*Client) MarkRead ¶
func (cli *Client) MarkRead(ids []types.MessageID, timestamp time.Time, chat, sender types.JID) error
MarkRead sends a read receipt for the given message IDs including the given timestamp as the read at time.
The first JID parameter (chat) must always be set to the chat ID (user ID in DMs and group ID in group chats). The second JID parameter (sender) must be set in group chats and must be the user ID who sent the message.
You can mark multiple messages as read at the same time, but only if the messages were sent by the same user. To mark messages by different users as read, you must call MarkRead multiple times (once for each user).
func (*Client) PairPhone ¶
func (cli *Client) PairPhone(phone string, showPushNotification bool, clientType PairClientType, clientDisplayName string) (string, error)
PairPhone generates a pairing code that can be used to link to a phone without scanning a QR code.
The exact expiry of pairing codes is unknown, but QR codes are always generated and the login websocket is closed after the QR codes run out, which means there's a 160-second time limit. It is recommended to generate the pairing code immediately after connecting to the websocket to have the maximum time.
The clientType parameter must be one of the PairClient* constants, but which one doesn't matter. The client display name must be formatted as `Browser (OS)`, and only common browsers/OSes are allowed (the server will validate it and return 400 if it's wrong).
See https://faq.whatsapp.com/1324084875126592 for more info
func (*Client) ParseWebMessage ¶
func (cli *Client) ParseWebMessage(chatJID types.JID, webMsg *waProto.WebMessageInfo) (*events.Message, error)
ParseWebMessage parses a WebMessageInfo object into *events.Message to match what real-time messages have.
The chat JID can be found in the Conversation data:
chatJID, err := types.ParseJID(conv.GetId()) for _, historyMsg := range conv.GetMessages() { evt, err := cli.ParseWebMessage(chatJID, historyMsg.GetMessage()) yourNormalEventHandler(evt) }
func (*Client) RemoveEventHandler ¶
RemoveEventHandler removes a previously registered event handler function. If the function with the given ID is found, this returns true.
N.B. Do not run this directly from an event handler. That would cause a deadlock because the event dispatcher holds a read lock on the event handler list, and this method wants a write lock on the same list. Instead run it in a goroutine:
func (mycli *MyClient) myEventHandler(evt interface{}) { if noLongerWantEvents { go mycli.WAClient.RemoveEventHandler(mycli.eventHandlerID) } }
func (*Client) RemoveEventHandlers ¶
func (cli *Client) RemoveEventHandlers()
RemoveEventHandlers removes all event handlers that have been registered with AddEventHandler
func (*Client) ResolveBusinessMessageLink ¶
func (cli *Client) ResolveBusinessMessageLink(code string) (*types.BusinessMessageLinkTarget, error)
ResolveBusinessMessageLink resolves a business message short link and returns the target JID, business name and text to prefill in the input field (if any).
The links look like https://wa.me/message/<code> or https://api.whatsapp.com/message/<code>. You can either provide the full link, or just the <code> part.
func (*Client) ResolveContactQRLink ¶
func (cli *Client) ResolveContactQRLink(code string) (*types.ContactQRLinkTarget, error)
ResolveContactQRLink resolves a link from a contact share QR code and returns the target JID and push name.
The links look like https://wa.me/qr/<code> or https://api.whatsapp.com/qr/<code>. You can either provide the full link, or just the <code> part.
func (*Client) RevokeMessage
deprecated
RevokeMessage deletes the given message from everyone in the chat.
This method will wait for the server to acknowledge the revocation message before returning. The return value is the timestamp of the message from the server.
Deprecated: This method is deprecated in favor of BuildRevoke
func (*Client) SendAppState ¶
SendAppState sends the given app state patch, then resyncs that app state type from the server to update local caches and send events for the updates.
You can use the Build methods in the appstate package to build the parameter for this method, e.g.
cli.SendAppState(appstate.BuildMute(targetJID, true, 24 * time.Hour))
func (*Client) SendChatPresence ¶
func (cli *Client) SendChatPresence(jid types.JID, state types.ChatPresence, media types.ChatPresenceMedia) error
SendChatPresence updates the user's typing status in a specific chat.
The media parameter can be set to indicate the user is recording media (like a voice message) rather than typing a text message.
func (*Client) SendMediaRetryReceipt ¶
func (cli *Client) SendMediaRetryReceipt(message *types.MessageInfo, mediaKey []byte) error
SendMediaRetryReceipt sends a request to the phone to re-upload the media in a message.
This is mostly relevant when handling history syncs and getting a 404 or 410 error downloading media. Rough example on how to use it (will not work out of the box, you must adjust it depending on what you need exactly):
var mediaRetryCache map[types.MessageID]*waProto.ImageMessage evt, err := cli.ParseWebMessage(chatJID, historyMsg.GetMessage()) imageMsg := evt.Message.GetImageMessage() // replace this with the part of the message you want to download data, err := cli.Download(imageMsg) if errors.Is(err, whatsappprotocol.ErrMediaDownloadFailedWith404) || errors.Is(err, whatsappprotocol.ErrMediaDownloadFailedWith410) { err = cli.SendMediaRetryReceipt(&evt.Info, imageMsg.GetMediaKey()) // You need to store the event data somewhere as it's necessary for handling the retry response. mediaRetryCache[evt.Info.ID] = imageMsg }
The response will come as an *events.MediaRetry. The response will then have to be decrypted using DecryptMediaRetryNotification and the same media key passed here. If the media retry was successful, the decrypted notification should contain an updated DirectPath, which can be used to download the file.
func eventHandler(rawEvt interface{}) { switch evt := rawEvt.(type) { case *events.MediaRetry: imageMsg := mediaRetryCache[evt.MessageID] retryData, err := whatsappprotocol.DecryptMediaRetryNotification(evt, imageMsg.GetMediaKey()) if err != nil || retryData.GetResult != waProto.MediaRetryNotification_SUCCESS { return } // Use the new path to download the attachment imageMsg.DirectPath = retryData.DirectPath data, err := cli.Download(imageMsg) // Alternatively, you can use cli.DownloadMediaWithPath and provide the individual fields manually. } }
func (*Client) SendMessage ¶
func (cli *Client) SendMessage(ctx context.Context, to types.JID, message *waProto.Message, extra ...SendRequestExtra) (resp SendResponse, err error)
SendMessage sends the given message.
This method will wait for the server to acknowledge the message before returning. The return value is the timestamp of the message from the server.
Optional parameters like the message ID can be specified with the SendRequestExtra struct. Only one extra parameter is allowed, put all necessary parameters in the same struct.
The message itself can contain anything you want (within the protobuf schema). e.g. for a simple text message, use the Conversation field:
cli.SendMessage(context.Background(), targetJID, &waProto.Message{ Conversation: proto.String("Hello, World!"), })
Things like replies, mentioning users and the "forwarded" flag are stored in ContextInfo, which can be put in ExtendedTextMessage and any of the media message types.
For uploading and sending media/attachments, see the Upload method.
For other message types, you'll have to figure it out yourself. Looking at the protobuf schema in binary/proto/def.proto may be useful to find out all the allowed fields. Printing the RawMessage field in incoming message events to figure out what it contains is also a good way to learn how to send the same kind of message.
func (*Client) SendPresence ¶
SendPresence updates the user's presence status on WhatsApp.
You should call this at least once after connecting so that the server has your pushname. Otherwise, other users will see "-" as the name.
func (*Client) SetDisappearingTimer ¶
SetDisappearingTimer sets the disappearing timer in a chat. Both private chats and groups are supported, but they're set with different methods.
Note that while this function allows passing non-standard durations, official WhatsApp apps will ignore those, and in groups the server will just reject the change. You can use the DisappearingTimer<Duration> constants for convenience.
In groups, the server will echo the change as a notification, so it'll show up as a *events.GroupInfo update.
func (*Client) SetForceActiveDeliveryReceipts ¶
SetForceActiveDeliveryReceipts will force the client to send normal delivery receipts (which will show up as the two gray ticks on WhatsApp), even if the client isn't marked as online.
By default, clients that haven't been marked as online will send delivery receipts with type="inactive", which is transmitted to the sender, but not rendered in the official WhatsApp apps. This is consistent with how WhatsApp web works when it's not in the foreground.
To mark the client as online, use
cli.SendPresence(types.PresenceAvailable)
Note that if you turn this off (i.e. call SetForceActiveDeliveryReceipts(false)), receipts will act like the client is offline until SendPresence is called again.
func (*Client) SetGroupAnnounce ¶
SetGroupAnnounce changes whether the group is in announce mode (i.e. whether only admins can send messages).
func (*Client) SetGroupLocked ¶
SetGroupLocked changes whether the group is locked (i.e. whether only admins can modify group info).
func (*Client) SetGroupName ¶
SetGroupName updates the name (subject) of the given group on WhatsApp.
func (*Client) SetGroupPhoto ¶
SetGroupPhoto updates the group picture/icon of the given group on WhatsApp. The avatar should be a JPEG photo, other formats may be rejected with ErrInvalidImageFormat. The bytes can be nil to remove the photo. Returns the new picture ID.
func (*Client) SetGroupTopic ¶
SetGroupTopic updates the topic (description) of the given group on WhatsApp.
The previousID and newID fields are optional. If the previous ID is not specified, this will automatically fetch the current group info to find the previous topic ID. If the new ID is not specified, one will be generated with Client.GenerateMessageID().
func (*Client) SetPassive ¶
SetPassive tells the WhatsApp server whether this device is passive or not.
This seems to mostly affect whether the device receives certain events. By default, whatsappprotocol will automatically do SetPassive(false) after connecting.
func (*Client) SetProxy ¶
SetProxy sets the proxy to use for WhatsApp web websocket connections and media uploads/downloads.
Must be called before Connect() to take effect in the websocket connection. If you want to change the proxy after connecting, you must call Disconnect() and then Connect() again manually.
By default, the client will find the proxy from the https_proxy environment variable like Go's net/http does.
To disable reading proxy info from environment variables, explicitly set the proxy to nil:
cli.SetProxy(nil)
To use a different proxy for the websocket and media, pass a function that checks the request path or headers:
cli.SetProxy(func(r *http.Request) (*url.URL, error) { if r.URL.Host == "web.whatsapp.com" && r.URL.Path == "/ws/chat" { return websocketProxyURL, nil } else { return mediaProxyURL, nil } })
func (*Client) SetProxyAddress ¶
SetProxyAddress is a helper method that parses a URL string and calls SetProxy.
Returns an error if url.Parse fails to parse the given address.
func (*Client) SetStatusMessage ¶
SetStatusMessage updates the current user's status text, which is shown in the "About" section in the user profile.
This is different from the ephemeral status broadcast messages. Use SendMessage to types.StatusBroadcastJID to send such messages.
func (*Client) SubscribePresence ¶
SubscribePresence asks the WhatsApp servers to send presence updates of a specific user to this client.
After subscribing to this event, you should start receiving *events.Presence for that user in normal event handlers.
Also, it seems that the WhatsApp servers require you to be online to receive presence status from other users, so you should mark yourself as online before trying to use this function:
cli.SendPresence(types.PresenceAvailable)
func (*Client) TryFetchPrivacySettings ¶
func (cli *Client) TryFetchPrivacySettings(ignoreCache bool) (*types.PrivacySettings, error)
TryFetchPrivacySettings will fetch the user's privacy settings, either from the in-memory cache or from the server.
func (*Client) UnlinkGroup ¶
UnlinkGroup removes a child group from a parent community.
func (*Client) UpdateBlocklist ¶
func (cli *Client) UpdateBlocklist(jid types.JID, action events.BlocklistChangeAction) (*types.Blocklist, error)
UpdateBlocklist updates the user's block list and returns the updated list.
func (*Client) UpdateGroupParticipants ¶
func (cli *Client) UpdateGroupParticipants(jid types.JID, participantChanges map[types.JID]ParticipantChange) (*waBinary.Node, error)
UpdateGroupParticipants can be used to add, remove, promote and demote members in a WhatsApp group.
func (*Client) Upload ¶
func (cli *Client) Upload(ctx context.Context, plaintext []byte, appInfo MediaType) (resp UploadResponse, err error)
Upload uploads the given attachment to WhatsApp servers.
You should copy the fields in the response to the corresponding fields in a protobuf message.
For example, to send an image:
resp, err := cli.Upload(context.Background(), yourImageBytes, whatsappprotocol.MediaImage) // handle error imageMsg := &waProto.ImageMessage{ Caption: proto.String("Hello, world!"), Mimetype: proto.String("image/png"), // replace this with the actual mime type // you can also optionally add other fields like ContextInfo and JpegThumbnail here Url: &resp.URL, DirectPath: &resp.DirectPath, MediaKey: resp.MediaKey, FileEncSha256: resp.FileEncSHA256, FileSha256: resp.FileSha256, FileLength: &resp.FileLength, } _, err = cli.SendMessage(context.Background(), targetJID, &waProto.Message{ ImageMessage: imageMsg, }) // handle error again
The same applies to the other message types like DocumentMessage, just replace the struct type and Message field name.
type DangerousInfoQuery ¶
type DangerousInfoQuery = infoQuery
type DangerousInfoQueryType ¶
type DangerousInfoQueryType = infoQueryType
type DangerousInternalClient ¶
type DangerousInternalClient struct {
// contains filtered or unexported fields
}
func (*DangerousInternalClient) CancelResponse ¶
func (int *DangerousInternalClient) CancelResponse(reqID string, ch chan *waBinary.Node)
func (*DangerousInternalClient) GetServerPreKeyCount ¶
func (int *DangerousInternalClient) GetServerPreKeyCount() (int, error)
func (*DangerousInternalClient) QueryMediaConn ¶
func (int *DangerousInternalClient) QueryMediaConn() (*MediaConn, error)
func (*DangerousInternalClient) RefreshMediaConn ¶
func (int *DangerousInternalClient) RefreshMediaConn(force bool) (*MediaConn, error)
func (*DangerousInternalClient) RequestAppStateKeys ¶
func (int *DangerousInternalClient) RequestAppStateKeys(ctx context.Context, keyIDs [][]byte)
func (*DangerousInternalClient) SendIQ ¶
func (int *DangerousInternalClient) SendIQ(query DangerousInfoQuery) (*waBinary.Node, error)
func (*DangerousInternalClient) SendIQAsync ¶
func (int *DangerousInternalClient) SendIQAsync(query DangerousInfoQuery) (<-chan *waBinary.Node, error)
func (*DangerousInternalClient) SendNode ¶
func (int *DangerousInternalClient) SendNode(node waBinary.Node) error
func (*DangerousInternalClient) SendRetryReceipt ¶
func (int *DangerousInternalClient) SendRetryReceipt(node *waBinary.Node, info *types.MessageInfo, forceIncludeIdentity bool)
func (*DangerousInternalClient) WaitResponse ¶
func (int *DangerousInternalClient) WaitResponse(reqID string) chan *waBinary.Node
type DisconnectedError ¶
DisconnectedError is returned if the websocket disconnects before an info query or other request gets a response.
func (*DisconnectedError) Error ¶
func (err *DisconnectedError) Error() string
func (*DisconnectedError) Is ¶
func (err *DisconnectedError) Is(other error) bool
type DownloadHTTPError ¶
func (DownloadHTTPError) Error ¶
func (dhe DownloadHTTPError) Error() string
func (DownloadHTTPError) Is ¶
func (dhe DownloadHTTPError) Is(other error) bool
type DownloadableMessage ¶
type DownloadableMessage interface { proto.Message GetDirectPath() string GetMediaKey() []byte GetFileSha256() []byte GetFileEncSha256() []byte }
DownloadableMessage represents a protobuf message that contains attachment info.
All of the downloadable messages inside a Message struct implement this interface (ImageMessage, VideoMessage, AudioMessage, DocumentMessage, StickerMessage).
type DownloadableThumbnail ¶
type DownloadableThumbnail interface { proto.Message GetThumbnailDirectPath() string GetThumbnailSha256() []byte GetThumbnailEncSha256() []byte GetMediaKey() []byte }
DownloadableThumbnail represents a protobuf message that contains a thumbnail attachment.
This is primarily meant for link preview thumbnails in ExtendedTextMessage.
type ElementMissingError ¶
ElementMissingError is returned by various functions that parse XML elements when a required element is missing.
func (*ElementMissingError) Error ¶
func (eme *ElementMissingError) Error() string
type EventHandler ¶
type EventHandler func(evt interface{})
EventHandler is a function that can handle events from WhatsApp.
type GetProfilePictureParams ¶
type MediaConn ¶
type MediaConn struct { Auth string AuthTTL int TTL int MaxBuckets int FetchedAt time.Time Hosts []MediaConnHost }
MediaConn contains a list of WhatsApp servers from which attachments can be downloaded from.
type MediaConnHost ¶
type MediaConnHost struct {
Hostname string
}
MediaConnHost represents a single host to download media from.
type MediaType ¶
type MediaType string
MediaType represents a type of uploaded file on WhatsApp. The value is the key which is used as a part of generating the encryption keys.
const ( MediaImage MediaType = "WhatsApp Image Keys" MediaVideo MediaType = "WhatsApp Video Keys" MediaAudio MediaType = "WhatsApp Audio Keys" MediaDocument MediaType = "WhatsApp Document Keys" MediaHistory MediaType = "WhatsApp History Keys" MediaAppState MediaType = "WhatsApp App State Keys" MediaLinkThumbnail MediaType = "WhatsApp Link Thumbnail Keys" )
The known media types
func GetMediaType ¶
func GetMediaType(msg DownloadableMessage) MediaType
GetMediaType returns the MediaType value corresponding to the given protobuf message.
type MessageDebugTimings ¶
type MsgSecretType ¶
type MsgSecretType string
const ( EncSecretPollVote MsgSecretType = "Poll Vote" EncSecretReaction MsgSecretType = "Enc Reaction" )
type PairClientType ¶
type PairClientType int
PairClientType is the type of client to use with PairCode. The type is automatically filled based on store.DeviceProps.PlatformType (which is what QR login uses).
const ( PairClientUnknown PairClientType = iota PairClientChrome PairClientEdge PairClientFirefox PairClientIE PairClientOpera PairClientSafari PairClientElectron PairClientUWP PairClientOtherWebClient )
type PairDatabaseError ¶
PairDatabaseError is included in an events.PairError if the pairing failed due to being unable to save the credentials to the device store.
func (*PairDatabaseError) Error ¶
func (err *PairDatabaseError) Error() string
func (*PairDatabaseError) Unwrap ¶
func (err *PairDatabaseError) Unwrap() error
type PairProtoError ¶
PairProtoError is included in an events.PairError if the pairing failed due to a protobuf error.
func (*PairProtoError) Error ¶
func (err *PairProtoError) Error() string
func (*PairProtoError) Unwrap ¶
func (err *PairProtoError) Unwrap() error
type ParticipantChange ¶
type ParticipantChange string
const ( ParticipantChangeAdd ParticipantChange = "add" ParticipantChangeRemove ParticipantChange = "remove" ParticipantChangePromote ParticipantChange = "promote" ParticipantChangeDemote ParticipantChange = "demote" )
type QRChannelItem ¶
type QRChannelItem struct { // The type of event, "code" for new QR codes (see Code field) and "error" for pairing errors (see Error) field. // For non-code/error events, you can just compare the whole item to the event variables (like QRChannelSuccess). Event string // If the item is a pair error, then this field contains the error message. Error error // If the item is a new code, then this field contains the raw data. Code string // The timeout after which the next code will be sent down the channel. Timeout time.Duration }
type RecentMessage ¶
RecentMessage contains the info needed to re-send a message when another device fails to decrypt it.
type ReqCreateGroup ¶
type ReqCreateGroup struct { // Group names are limited to 25 characters. A longer group name will cause a 406 not acceptable error. Name string // You don't need to include your own JID in the participants array, the WhatsApp servers will add it implicitly. Participants []types.JID // A create key can be provided to deduplicate the group create notification that will be triggered // when the group is created. If provided, the JoinedGroup event will contain the same key. CreateKey types.MessageID // Set IsParent to true to create a community instead of a normal group. // When creating a community, the linked announcement group will be created automatically by the server. types.GroupParent // Set LinkedParentJID to create a group inside a community. types.GroupLinkedParent }
ReqCreateGroup contains the request data for CreateGroup.
type SendRequestExtra ¶
type SendRequestExtra struct { // The message ID to use when sending. If this is not provided, a random message ID will be generated ID types.MessageID // Should the message be sent as a peer message (protocol messages to your own devices, e.g. app state key requests) Peer bool }
SendRequestExtra contains the optional parameters for SendMessage.
By default, optional parameters don't have to be provided at all, e.g.
cli.SendMessage(ctx, to, message)
When providing optional parameters, add a single instance of this struct as the last parameter:
cli.SendMessage(ctx, to, message, whatsappprotocol.SendRequestExtra{...})
Trying to add multiple extra parameters will return an error.
type SendResponse ¶
type SendResponse struct { // The message timestamp returned by the server Timestamp time.Time // The ID of the sent message ID types.MessageID // Message handling duration, used for debugging DebugTimings MessageDebugTimings }
type UploadResponse ¶
type UploadResponse struct { URL string `json:"url"` DirectPath string `json:"direct_path"` MediaKey []byte `json:"-"` FileEncSHA256 []byte `json:"-"` FileSHA256 []byte `json:"-"` FileLength uint64 `json:"-"` }
UploadResponse contains the data from the attachment upload, which can be put into a message to send the attachment.
Source Files ¶
- appstate.go
- broadcast.go
- call.go
- client.go
- connectionevents.go
- download.go
- errors.go
- group.go
- handshake.go
- internals.go
- keepalive.go
- mediaconn.go
- mediaretry.go
- message.go
- msgsecret.go
- notification.go
- pair-code.go
- pair.go
- prekeys.go
- presence.go
- privacysettings.go
- qrchan.go
- receipt.go
- request.go
- retry.go
- send.go
- update.go
- upload.go
- user.go
Directories ¶
Path | Synopsis |
---|---|
Package appstate implements encoding and decoding WhatsApp's app state patches.
|
Package appstate implements encoding and decoding WhatsApp's app state patches. |
lthash
Package lthash implements a summation based hash algorithm that maintains the integrity of a piece of data over a series of mutations.
|
Package lthash implements a summation based hash algorithm that maintains the integrity of a piece of data over a series of mutations. |
Package binary implements encoding and decoding documents in WhatsApp's binary XML format.
|
Package binary implements encoding and decoding documents in WhatsApp's binary XML format. |
proto
Package proto contains the compiled protobuf structs from WhatsApp's protobuf schema.
|
Package proto contains the compiled protobuf structs from WhatsApp's protobuf schema. |
token
Package token contains maps of predefined tokens that WhatsApp's binary XML encoding uses to save bytes when sending commonly used strings.
|
Package token contains maps of predefined tokens that WhatsApp's binary XML encoding uses to save bytes when sending commonly used strings. |
Package socket implements a subset of the Noise protocol framework on top of websockets as used by WhatsApp.
|
Package socket implements a subset of the Noise protocol framework on top of websockets as used by WhatsApp. |
Package store contains interfaces for storing data needed for WhatsApp multidevice.
|
Package store contains interfaces for storing data needed for WhatsApp multidevice. |
sqlstore
Package sqlstore contains an SQL-backed implementation of the interfaces in the store package.
|
Package sqlstore contains an SQL-backed implementation of the interfaces in the store package. |
Package types contains various structs and other types used by whatsappprotocol.
|
Package types contains various structs and other types used by whatsappprotocol. |
events
Package events contains all the events that whatsappprotocol.Client emits to functions registered with AddEventHandler.
|
Package events contains all the events that whatsappprotocol.Client emits to functions registered with AddEventHandler. |
util
|
|
cbcutil
CBC describes a block cipher mode.
|
CBC describes a block cipher mode. |
hkdfutil
Package hkdfutil contains a simple wrapper for golang.org/x/crypto/hkdf that reads a specified number of bytes.
|
Package hkdfutil contains a simple wrapper for golang.org/x/crypto/hkdf that reads a specified number of bytes. |
keys
Package keys contains a utility struct for elliptic curve keypairs.
|
Package keys contains a utility struct for elliptic curve keypairs. |
log
Package waLog contains a simple logger interface used by the other whatsappprotocol packages.
|
Package waLog contains a simple logger interface used by the other whatsappprotocol packages. |