Documentation ¶
Overview ¶
Package whatsmeow implements a client for interacting with the WhatsApp web multidevice API.
Index ¶
- Constants
- Variables
- func GenerateMessageID() types.MessageID
- type Client
- func (cli *Client) AddEventHandler(handler EventHandler) uint32
- func (cli *Client) Connect() error
- func (cli *Client) CreateGroup(name string, participants []types.JID) (*types.GroupInfo, error)
- func (cli *Client) Disconnect()
- func (cli *Client) Download(msg DownloadableMessage) (data []byte, err error)
- func (cli *Client) DownloadAny(msg *waProto.Message) (data []byte, err error)
- func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) 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) GetPrivacySettings() (settings types.PrivacySettings)
- func (cli *Client) GetProfilePictureInfo(jid types.JID, preview bool) (*types.ProfilePictureInfo, error)
- func (cli *Client) GetQRChannel(ctx context.Context) (<-chan QRChannelItem, error)
- func (cli *Client) GetUserDevices(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) Logout() error
- func (cli *Client) MarkRead(ids []types.MessageID, timestamp time.Time, chat, sender types.JID) error
- func (cli *Client) Reconnect() error
- func (cli *Client) RemoveEventHandler(id uint32) bool
- func (cli *Client) RemoveEventHandlers()
- func (cli *Client) ResolveBusinessMessageLink(code string) (*types.BusinessMessageLinkTarget, error)
- func (cli *Client) RevokeMessage(chat types.JID, id types.MessageID) (time.Time, error)
- func (cli *Client) SendChatPresence(state types.ChatPresence, jid types.JID) error
- func (cli *Client) SendMessage(to types.JID, id types.MessageID, message *waProto.Message) (time.Time, error)
- func (cli *Client) SendPresence(state types.Presence) error
- 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) SetGroupTopic(jid types.JID, previousID, newID, topic string) error
- func (cli *Client) SetPassive(passive bool) error
- func (cli *Client) SetProxyDialer(dialer proxy.Dialer)
- func (cli *Client) SubscribePresence(jid types.JID) error
- func (cli *Client) TryFetchPrivacySettings(ignoreCache bool) (*types.PrivacySettings, 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)
- type DownloadableMessage
- type ElementMissingError
- type EventHandler
- type IQError
- type MediaConn
- type MediaConnHost
- type MediaType
- type ParticipantChange
- type QRChannelItem
- type RecentMessage
- type UploadResponse
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 BusinessMessageLinkDirectPrefix = "https://api.whatsapp.com/message/"
const BusinessMessageLinkPrefix = "https://wa.me/message/"
const InviteLinkPrefix = "https://chat.whatsapp.com/"
const NoiseHandshakeResponseTimeout = 5 * time.Second
Variables ¶
var ( ErrNoSession = errors.New("can't encrypt message for device: no signal session established") ErrIQTimedOut = errors.New("info query timed out") ErrIQDisconnected = errors.New("socket disconnected before info query returned response") ErrNotConnected = errors.New("socket not connected") ErrNotLoggedIn = errors.New("the store doesn't contain a device JID") ErrAlreadyConnected = errors.New("socket 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") )
Miscellaneous errors
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") 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") )
var ( ErrBroadcastListUnsupported = errors.New("sending to broadcast lists is not yet supported") ErrUnknownServer = errors.New("can't send message to unknown server") ErrRecipientADJID = errors.New("message recipient must be normal (non-AD) JID") ErrSendDisconnected = errors.New("socket disconnected before message send returned response") )
Some errors that Client.SendMessage can return
var ( ErrMediaDownloadFailedWith404 = errors.New("download failed with status code 404") ErrMediaDownloadFailedWith410 = errors.New("download failed with status code 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 ( ErrIQNotAuthorized error = &IQError{Code: 401, Text: "not-authorized"} ErrIQForbidden error = &IQError{Code: 403, Text: "forbidden"} ErrIQNotFound error = &IQError{Code: 404, Text: "item-not-found"} ErrIQNotAcceptable error = &IQError{Code: 406, Text: "not-acceptable"} ErrIQGone error = &IQError{Code: 410, Text: "gone"} )
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 )
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"} // QRChannelScannedWithoutMultidevice is emitted from GetQRChannel if events.QRScannedWithoutMultidevice is received. QRChannelScannedWithoutMultidevice = QRChannelItem{Event: "err-scanned-without-multidevice"} )
Functions ¶
func GenerateMessageID ¶
GenerateMessageID generates a random string that can be used as a message ID on WhatsApp.
Types ¶
type Client ¶
type Client struct { Store *store.Device //Log waLog.Logger //recvLog waLog.Logger //sendLog waLog.Logger ProxyDialer proxy.Dialer EnableAutoReconnect bool LastSuccessfulConnect time.Time AutoReconnectErrors int Registered bool // EmitAppStateEventsOnFullSync can be set to true if you want to get app state events emitted // even when re-syncing the whole state. EmitAppStateEventsOnFullSync 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(to types.JID, id types.MessageID) *waProto.Message HistorySyncID int32 StartupTime int64 // 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 := whatsmeow.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.Order: 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 *whatsmeow.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) 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 ¶
CreateGroup creates a group on WhatsApp with the given name and participants.
You don't need to include your own JID in the participants array, the WhatsApp servers will add it implicitly.
func (*Client) Disconnect ¶
func (cli *Client) Disconnect()
Disconnect disconnects from the WhatsApp web websocket.
func (*Client) Download ¶
func (cli *Client) Download(msg DownloadableMessage) (data []byte, err error)
Download downloads the attachment from the given protobuf message.
func (*Client) DownloadAny ¶
DownloadAny loops through the downloadable parts of the given message and downloads the first non-nil item.
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) 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) 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, preview bool) (*types.ProfilePictureInfo, error)
GetProfilePictureInfo gets the URL where you can download a WhatsApp user's profile picture or group's photo. If the user or group doesn't have a profile picture, this returns nil with no 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 string, either "success", "timeout" or "err-already-have-id", depending on the result of the pairing. The channel will be closed immediately after one of those.
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) 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) 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.device.Delete() manually.
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.
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) RevokeMessage ¶
RevokeMessage deletes the given message from everyone in the chat. You can only revoke your own messages, and if the message is too old, then other users will ignore the deletion.
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.
func (*Client) SendChatPresence ¶
SendChatPresence updates the user's typing status in a specific chat.
func (*Client) SendMessage ¶
func (cli *Client) SendMessage(to types.JID, id types.MessageID, message *waProto.Message) (time.Time, error)
SendMessage sends the given message.
If the message ID is not provided, a random message ID will be generated.
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.
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) 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) 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 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, whatsmeow will automatically do SetPassive(false) after connecting.
func (*Client) SetProxyDialer ¶
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.
type DownloadableMessage ¶
type DownloadableMessage interface { proto.Message GetDirectPath() string GetMediaKey() []byte GetFileSha256() []byte GetFileEncSha256() []byte }
DownloadableMessage represents a protobuf message that contains attachment info.
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 interface {
Handle(evt interface{})
}
EventHandler is a function that can handle events from WhatsApp.
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" )
The known media types
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. // 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.
Source Files ¶
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 whatsmeow.
|
Package types contains various structs and other types used by whatsmeow. |
events
Package events contains all the events that whatsmeow.Client emits to functions registered with AddEventHandler.
|
Package events contains all the events that whatsmeow.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 whatsmeow packages.
|
Package waLog contains a simple logger interface used by the other whatsmeow packages. |