You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
matterbridge/vendor/go.mau.fi/whatsmeow/types/events/events.go

419 lines
16 KiB
Go

// Copyright (c) 2021 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
// Package events contains all the events that whatsmeow.Client emits to functions registered with AddEventHandler.
package events
import (
"fmt"
"time"
waBinary "go.mau.fi/whatsmeow/binary"
waProto "go.mau.fi/whatsmeow/binary/proto"
"go.mau.fi/whatsmeow/types"
)
// QR is emitted after connecting when there's no session data in the device store.
//
// The QR codes are available in the Codes slice. You should render the strings as QR codes one by
// one, switching to the next one whenever enough time has passed. WhatsApp web seems to show the
// first code for 60 seconds and all other codes for 20 seconds.
//
// When the QR code has been scanned and pairing is complete, PairSuccess will be emitted. If you
// run out of codes before scanning, the server will close the websocket, and you will have to
// reconnect to get more codes.
type QR struct {
Codes []string
}
// PairSuccess is emitted after the QR code has been scanned with the phone and the handshake has
// been completed. Note that this is generally followed by a websocket reconnection, so you should
// wait for the Connected before trying to send anything.
type PairSuccess struct {
ID types.JID
BusinessName string
Platform string
}
// PairError is emitted when a pair-success event is received from the server, but finishing the pairing locally fails.
type PairError struct {
ID types.JID
BusinessName string
Platform string
Error error
}
// QRScannedWithoutMultidevice is emitted when the pairing QR code is scanned, but the phone didn't have multidevice enabled.
// The same QR code can still be scanned after this event, which means the user can just be told to enable multidevice and re-scan the code.
type QRScannedWithoutMultidevice struct{}
// Connected is emitted when the client has successfully connected to the WhatsApp servers
// and is authenticated. The user who the client is authenticated as will be in the device store
// at this point, which is why this event doesn't contain any data.
type Connected struct{}
// KeepAliveTimeout is emitted when the keepalive ping request to WhatsApp web servers times out.
//
// Currently, there's no automatic handling for these, but it's expected that the TCP connection will
// either start working again or notice it's dead on its own eventually. Clients may use this event to
// decide to force a disconnect+reconnect faster.
type KeepAliveTimeout struct {
ErrorCount int
LastSuccess time.Time
}
// KeepAliveRestored is emitted if the keepalive pings start working again after some KeepAliveTimeout events.
// Note that if the websocket disconnects before the pings start working, this event will not be emitted.
type KeepAliveRestored struct{}
// LoggedOut is emitted when the client has been unpaired from the phone.
//
// This can happen while connected (stream:error messages) or right after connecting (connect failure messages).
//
// This will not be emitted when the logout is initiated by this client (using Client.LogOut()).
type LoggedOut struct {
// OnConnect is true if the event was triggered by a connect failure message.
// If it's false, the event was triggered by a stream:error message.
OnConnect bool
// If OnConnect is true, then this field contains the reason code.
Reason ConnectFailureReason
}
// StreamReplaced is emitted when the client is disconnected by another client connecting with the same keys.
//
// This can happen if you accidentally start another process with the same session
// or otherwise try to connect twice with the same session.
type StreamReplaced struct{}
// TempBanReason is an error code included in temp ban error events.
type TempBanReason int
const (
TempBanBlockedByUsers TempBanReason = 101
TempBanSentToTooManyPeople TempBanReason = 102
TempBanCreatedTooManyGroups TempBanReason = 103
TempBanSentTooManySameMessage TempBanReason = 104
TempBanBroadcastList TempBanReason = 106
)
var tempBanReasonMessage = map[TempBanReason]string{
TempBanBlockedByUsers: "too many people blocked you",
TempBanSentToTooManyPeople: "you sent too many messages to people who don't have you in their address books",
TempBanCreatedTooManyGroups: "you created too many groups with people who don't have you in their address books",
TempBanSentTooManySameMessage: "you sent the same message to too many people",
TempBanBroadcastList: "you sent too many messages to a broadcast list",
}
// String returns the reason code and a human-readable description of the ban reason.
func (tbr TempBanReason) String() string {
msg, ok := tempBanReasonMessage[tbr]
if !ok {
msg = "you may have violated the terms of service (unknown error)"
}
return fmt.Sprintf("%d: %s", int(tbr), msg)
}
// TemporaryBan is emitted when there's a connection failure with the ConnectFailureTempBanned reason code.
type TemporaryBan struct {
Code TempBanReason
Expire time.Time
}
func (tb *TemporaryBan) String() string {
if tb.Expire.IsZero() {
return fmt.Sprintf("You've been temporarily banned: %v", tb.Code)
}
return fmt.Sprintf("You've been temporarily banned: %v. The ban expires at %v", tb.Code, tb.Expire)
}
// ConnectFailureReason is an error code included in connection failure events.
type ConnectFailureReason int
const (
ConnectFailureLoggedOut ConnectFailureReason = 401
ConnectFailureTempBanned ConnectFailureReason = 402
ConnectFailureMainDeviceGone ConnectFailureReason = 403
ConnectFailureUnknownLogout ConnectFailureReason = 406
ConnectFailureClientOutdated ConnectFailureReason = 405
ConnectFailureBadUserAgent ConnectFailureReason = 409
// 400, 500 and 501 are also existing codes, but the meaning is unknown
)
var connectFailureReasonMessage = map[ConnectFailureReason]string{
ConnectFailureLoggedOut: "logged out from another device",
ConnectFailureTempBanned: "account temporarily banned",
ConnectFailureMainDeviceGone: "primary device was logged out", // seems to happen for both bans and switching phones
ConnectFailureUnknownLogout: "logged out for unknown reason",
ConnectFailureClientOutdated: "client is out of date",
ConnectFailureBadUserAgent: "client user agent was rejected",
}
// IsLoggedOut returns true if the client should delete session data due to this connect failure.
func (cfr ConnectFailureReason) IsLoggedOut() bool {
return cfr == ConnectFailureLoggedOut || cfr == ConnectFailureMainDeviceGone || cfr == ConnectFailureUnknownLogout
}
// String returns the reason code and a short human-readable description of the error.
func (cfr ConnectFailureReason) String() string {
msg, ok := connectFailureReasonMessage[cfr]
if !ok {
msg = "unknown error"
}
return fmt.Sprintf("%d: %s", int(cfr), msg)
}
// ConnectFailure is emitted when the WhatsApp server sends a <failure> node with an unknown reason.
//
// Known reasons are handled internally and emitted as different events (e.g. LoggedOut and TemporaryBan).
type ConnectFailure struct {
Reason ConnectFailureReason
Raw *waBinary.Node
}
// ClientOutdated is emitted when the WhatsApp server rejects the connection with the ConnectFailureClientOutdated code.
type ClientOutdated struct{}
// StreamError is emitted when the WhatsApp server sends a <stream:error> node with an unknown code.
//
// Known codes are handled internally and emitted as different events (e.g. LoggedOut).
type StreamError struct {
Code string
Raw *waBinary.Node
}
// Disconnected is emitted when the websocket is closed by the server.
type Disconnected struct{}
// HistorySync is emitted when the phone has sent a blob of historical messages.
type HistorySync struct {
Data *waProto.HistorySync
}
// UndecryptableMessage is emitted when receiving a new message that failed to decrypt.
//
// The library will automatically ask the sender to retry. If the sender resends the message,
// and it's decryptable, then it will be emitted as a normal Message event.
//
// The UndecryptableMessage event may also be repeated if the resent message is also undecryptable.
type UndecryptableMessage struct {
Info types.MessageInfo
// IsUnavailable is true if the recipient device didn't send a ciphertext to this device at all
// (as opposed to sending a ciphertext, but the ciphertext not being decryptable).
IsUnavailable bool
}
// Message is emitted when receiving a new message.
type Message struct {
Info types.MessageInfo // Information about the message like the chat and sender IDs
Message *waProto.Message // The actual message struct
IsEphemeral bool // True if the message was unwrapped from an EphemeralMessage
IsViewOnce bool // True if the message was unwrapped from a ViewOnceMessage or ViewOnceMessageV2
IsViewOnceV2 bool // True if the message was unwrapped from a ViewOnceMessage
IsDocumentWithCaption bool // True if the message was unwrapped from a DocumentWithCaptionMessage
// The raw message struct. This is the raw unmodified data, which means the actual message might
// be wrapped in DeviceSentMessage, EphemeralMessage or ViewOnceMessage.
RawMessage *waProto.Message
}
// UnwrapRaw fills the Message, IsEphemeral and IsViewOnce fields based on the raw message in the RawMessage field.
func (evt *Message) UnwrapRaw() *Message {
evt.Message = evt.RawMessage
if evt.Message.GetDeviceSentMessage().GetMessage() != nil {
evt.Info.DeviceSentMeta = &types.DeviceSentMeta{
DestinationJID: evt.Message.GetDeviceSentMessage().GetDestinationJid(),
Phash: evt.Message.GetDeviceSentMessage().GetPhash(),
}
evt.Message = evt.Message.GetDeviceSentMessage().GetMessage()
}
if evt.Message.GetEphemeralMessage().GetMessage() != nil {
evt.Message = evt.Message.GetEphemeralMessage().GetMessage()
evt.IsEphemeral = true
}
if evt.Message.GetViewOnceMessage().GetMessage() != nil {
evt.Message = evt.Message.GetViewOnceMessage().GetMessage()
evt.IsViewOnce = true
}
if evt.Message.GetViewOnceMessageV2().GetMessage() != nil {
evt.Message = evt.Message.GetViewOnceMessageV2().GetMessage()
evt.IsViewOnce = true
evt.IsViewOnceV2 = true
}
if evt.Message.GetDocumentWithCaptionMessage().GetMessage() != nil {
evt.Message = evt.Message.GetDocumentWithCaptionMessage().GetMessage()
evt.IsDocumentWithCaption = true
}
return evt
}
// ReceiptType represents the type of a Receipt event.
type ReceiptType string
const (
// ReceiptTypeDelivered means the message was delivered to the device (but the user might not have noticed).
ReceiptTypeDelivered ReceiptType = ""
// ReceiptTypeRetry means the message was delivered to the device, but decrypting the message failed.
ReceiptTypeRetry ReceiptType = "retry"
// ReceiptTypeRead means the user opened the chat and saw the message.
ReceiptTypeRead ReceiptType = "read"
// ReceiptTypeReadSelf means the current user read a message from a different device, and has read receipts disabled in privacy settings.
ReceiptTypeReadSelf ReceiptType = "read-self"
)
// GoString returns the name of the Go constant for the ReceiptType value.
func (rt ReceiptType) GoString() string {
switch rt {
case ReceiptTypeRead:
return "events.ReceiptTypeRead"
case ReceiptTypeReadSelf:
return "events.ReceiptTypeReadSelf"
case ReceiptTypeDelivered:
return "events.ReceiptTypeDelivered"
default:
return fmt.Sprintf("events.ReceiptType(%#v)", string(rt))
}
}
// Receipt is emitted when an outgoing message is delivered to or read by another user, or when another device reads an incoming message.
//
// N.B. WhatsApp on Android sends message IDs from newest message to oldest, but WhatsApp on iOS sends them in the opposite order (oldest first).
type Receipt struct {
types.MessageSource
MessageIDs []types.MessageID
Timestamp time.Time
Type ReceiptType
}
// ChatPresence is emitted when a chat state update (also known as typing notification) is received.
//
// Note that WhatsApp won't send you these updates unless you mark yourself as online:
// client.SendPresence(types.PresenceAvailable)
type ChatPresence struct {
types.MessageSource
State types.ChatPresence // The current state, either composing or paused
Media types.ChatPresenceMedia // When composing, the type of message
}
// Presence is emitted when a presence update is received.
//
// Note that WhatsApp only sends you presence updates for individual users after you subscribe to them:
// client.SubscribePresence(user JID)
type Presence struct {
// The user whose presence event this is
From types.JID
// True if the user is now offline
Unavailable bool
// The time when the user was last online. This may be the zero value if the user has hid their last seen time.
LastSeen time.Time
}
// JoinedGroup is emitted when you join or are added to a group.
type JoinedGroup struct {
Reason string // If the event was triggered by you using an invite link, this will be "invite".
Type string // "new" if it's a newly created group.
CreateKey types.MessageID // If you created the group, this is the same message ID you passed to CreateGroup.
types.GroupInfo
}
// GroupInfo is emitted when the metadata of a group changes.
type GroupInfo struct {
JID types.JID // The group ID in question
Notify string // Seems like a top-level type for the invite
Sender *types.JID // The user who made the change. Doesn't seem to be present when notify=invite
Timestamp time.Time // The time when the change occurred
Name *types.GroupName // Group name change
Topic *types.GroupTopic // Group topic (description) change
Locked *types.GroupLocked // Group locked status change (can only admins edit group info?)
Announce *types.GroupAnnounce // Group announce status change (can only admins send messages?)
Ephemeral *types.GroupEphemeral // Disappearing messages change
NewInviteLink *string // Group invite link change
PrevParticipantVersionID string
ParticipantVersionID string
JoinReason string // This will be "invite" if the user joined via invite link
Join []types.JID // Users who joined or were added the group
Leave []types.JID // Users who left or were removed from the group
Promote []types.JID // Users who were promoted to admins
Demote []types.JID // Users who were demoted to normal users
UnknownChanges []*waBinary.Node
}
// Picture is emitted when a user's profile picture or group's photo is changed.
//
// You can use Client.GetProfilePictureInfo to get the actual image URL after this event.
type Picture struct {
JID types.JID // The user or group ID where the picture was changed.
Author types.JID // The user who changed the picture.
Timestamp time.Time // The timestamp when the picture was changed.
Remove bool // True if the picture was removed.
PictureID string // The new picture ID if it was not removed.
}
// IdentityChange is emitted when another user changes their primary device.
type IdentityChange struct {
JID types.JID
Timestamp time.Time
// Implicit will be set to true if the event was triggered by an untrusted identity error,
// rather than an identity change notification from the server.
Implicit bool
}
// PrivacySettings is emitted when the user changes their privacy settings.
type PrivacySettings struct {
NewSettings types.PrivacySettings
GroupAddChanged bool
LastSeenChanged bool
StatusChanged bool
ProfileChanged bool
ReadReceiptsChanged bool
}
// OfflineSyncPreview is emitted right after connecting if the server is going to send events that the client missed during downtime.
type OfflineSyncPreview struct {
Total int
AppDataChanges int
Messages int
Notifications int
Receipts int
}
// OfflineSyncCompleted is emitted after the server has finished sending missed events.
type OfflineSyncCompleted struct {
Count int
}
type MediaRetryError struct {
Code int
}
// MediaRetry is emitted when the phone sends a response to a media retry request.
type MediaRetry struct {
Ciphertext []byte
IV []byte
// Sometimes there's an unencrypted media retry error. In these cases, Ciphertext and IV will be nil.
Error *MediaRetryError
Timestamp time.Time // The time of the response.
MessageID types.MessageID // The ID of the message.
ChatID types.JID // The chat ID where the message was sent.
SenderID types.JID // The user who sent the message. Only present in groups.
FromMe bool // Whether the message was sent by the current user or someone else.
}