Update dependencies and go1.18 (#1873)
* Update dependencies and go1.18 * Exclude unnecessary linters and update build to go1.18
This commit is contained in:
@@ -166,7 +166,7 @@ func (b *Builder) processV3(sessionRecord *record.Session,
|
||||
if message.PreKeyID() != nil && message.PreKeyID().Value != medium.MaxValue {
|
||||
return message.PreKeyID(), nil
|
||||
}
|
||||
return nil, nil
|
||||
return optional.NewEmptyUint32(), nil
|
||||
}
|
||||
|
||||
// ProcessBundle builds a new session from a PreKeyBundle retrieved
|
||||
|
||||
17
vendor/go.mau.fi/whatsmeow/appstate.go
vendored
17
vendor/go.mau.fi/whatsmeow/appstate.go
vendored
@@ -7,6 +7,7 @@
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -56,7 +57,7 @@ func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotS
|
||||
mutations, newState, err := cli.appStateProc.DecodePatches(patches, state, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, appstate.ErrKeyNotFound) {
|
||||
go cli.requestMissingAppStateKeys(patches)
|
||||
go cli.requestMissingAppStateKeys(context.TODO(), patches)
|
||||
}
|
||||
return fmt.Errorf("failed to decode app state %s patches: %w", name, err)
|
||||
}
|
||||
@@ -150,6 +151,9 @@ func (cli *Client) dispatchAppState(mutation appstate.Mutation, dispatchEvts boo
|
||||
if cli.Store.Contacts != nil {
|
||||
storeUpdateError = cli.Store.Contacts.PutContactName(jid, act.GetFirstName(), act.GetFullName())
|
||||
}
|
||||
case "deleteChat":
|
||||
act := mutation.Action.GetDeleteChatAction()
|
||||
eventToDispatch = &events.DeleteChat{JID: jid, Timestamp: ts, Action: act}
|
||||
case "star":
|
||||
if len(mutation.Index) < 5 {
|
||||
return
|
||||
@@ -234,7 +238,7 @@ func (cli *Client) fetchAppStatePatches(name appstate.WAPatchName, fromVersion u
|
||||
return appstate.ParsePatchList(resp, cli.downloadExternalAppStateBlob)
|
||||
}
|
||||
|
||||
func (cli *Client) requestMissingAppStateKeys(patches *appstate.PatchList) {
|
||||
func (cli *Client) requestMissingAppStateKeys(ctx context.Context, patches *appstate.PatchList) {
|
||||
cli.appStateKeyRequestsLock.Lock()
|
||||
rawKeyIDs := cli.appStateProc.GetMissingKeyIDs(patches)
|
||||
filteredKeyIDs := make([][]byte, 0, len(rawKeyIDs))
|
||||
@@ -248,10 +252,10 @@ func (cli *Client) requestMissingAppStateKeys(patches *appstate.PatchList) {
|
||||
}
|
||||
}
|
||||
cli.appStateKeyRequestsLock.Unlock()
|
||||
cli.requestAppStateKeys(filteredKeyIDs)
|
||||
cli.requestAppStateKeys(ctx, filteredKeyIDs)
|
||||
}
|
||||
|
||||
func (cli *Client) requestAppStateKeys(rawKeyIDs [][]byte) {
|
||||
func (cli *Client) requestAppStateKeys(ctx context.Context, rawKeyIDs [][]byte) {
|
||||
keyIDs := make([]*waProto.AppStateSyncKeyId, len(rawKeyIDs))
|
||||
debugKeyIDs := make([]string, len(rawKeyIDs))
|
||||
for i, keyID := range rawKeyIDs {
|
||||
@@ -266,8 +270,11 @@ func (cli *Client) requestAppStateKeys(rawKeyIDs [][]byte) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if cli.Store.ID == nil {
|
||||
return
|
||||
}
|
||||
cli.Log.Infof("Sending key request for app state keys %+v", debugKeyIDs)
|
||||
_, err := cli.SendMessage(cli.Store.ID.ToNonAD(), "", msg)
|
||||
_, err := cli.SendMessage(ctx, cli.Store.ID.ToNonAD(), "", msg)
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to send app state key request: %v", err)
|
||||
}
|
||||
|
||||
4
vendor/go.mau.fi/whatsmeow/appstate/hash.go
vendored
4
vendor/go.mau.fi/whatsmeow/appstate/hash.go
vendored
@@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
type Mutation struct {
|
||||
Operation waProto.SyncdMutation_SyncdMutationSyncdOperation
|
||||
Operation waProto.SyncdMutation_SyncdOperation
|
||||
Action *waProto.SyncActionValue
|
||||
Index []string
|
||||
IndexMAC []byte
|
||||
@@ -89,7 +89,7 @@ func generatePatchMAC(patch *waProto.SyncdPatch, name WAPatchName, key []byte) [
|
||||
return concatAndHMAC(sha256.New, key, dataToHash...)
|
||||
}
|
||||
|
||||
func generateContentMAC(operation waProto.SyncdMutation_SyncdMutationSyncdOperation, data, keyID, key []byte) []byte {
|
||||
func generateContentMAC(operation waProto.SyncdMutation_SyncdOperation, data, keyID, key []byte) []byte {
|
||||
operationBytes := []byte{byte(operation) + 1}
|
||||
keyDataLength := uint64ToBytes(uint64(len(keyID) + 1))
|
||||
return concatAndHMAC(sha512.New, key, operationBytes, keyID, data, keyDataLength)[:32]
|
||||
|
||||
16599
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.go
vendored
16599
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.go
vendored
File diff suppressed because it is too large
Load Diff
BIN
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.raw
vendored
BIN
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.raw
vendored
Binary file not shown.
1541
vendor/go.mau.fi/whatsmeow/binary/proto/def.proto
vendored
1541
vendor/go.mau.fi/whatsmeow/binary/proto/def.proto
vendored
File diff suppressed because it is too large
Load Diff
12
vendor/go.mau.fi/whatsmeow/errors.go
vendored
12
vendor/go.mau.fi/whatsmeow/errors.go
vendored
@@ -15,10 +15,11 @@ import (
|
||||
|
||||
// Miscellaneous errors
|
||||
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")
|
||||
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")
|
||||
|
||||
@@ -32,6 +33,9 @@ var (
|
||||
// ErrProfilePictureUnauthorized is returned by GetProfilePictureInfo when trying to get the profile picture of a user
|
||||
// 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 is returned by GetGroupInviteLink if you don't have the permission to get the link (status code 401).
|
||||
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).
|
||||
|
||||
59
vendor/go.mau.fi/whatsmeow/group.go
vendored
59
vendor/go.mau.fi/whatsmeow/group.go
vendored
@@ -7,6 +7,7 @@
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -18,8 +19,9 @@ import (
|
||||
|
||||
const InviteLinkPrefix = "https://chat.whatsapp.com/"
|
||||
|
||||
func (cli *Client) sendGroupIQ(iqType infoQueryType, jid types.JID, content waBinary.Node) (*waBinary.Node, error) {
|
||||
func (cli *Client) sendGroupIQ(ctx context.Context, iqType infoQueryType, jid types.JID, content waBinary.Node) (*waBinary.Node, error) {
|
||||
return cli.sendIQ(infoQuery{
|
||||
Context: ctx,
|
||||
Namespace: "w:g2",
|
||||
Type: iqType,
|
||||
To: jid,
|
||||
@@ -30,7 +32,12 @@ func (cli *Client) sendGroupIQ(iqType infoQueryType, jid types.JID, content waBi
|
||||
// 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 (cli *Client) CreateGroup(name string, participants []types.JID) (*types.GroupInfo, error) {
|
||||
//
|
||||
// Group names are limited to 25 characters. A longer group name will cause a 406 not acceptable error.
|
||||
//
|
||||
// Optionally, 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.
|
||||
func (cli *Client) CreateGroup(name string, participants []types.JID, createKey types.MessageID) (*types.GroupInfo, error) {
|
||||
participantNodes := make([]waBinary.Node, len(participants))
|
||||
for i, participant := range participants {
|
||||
participantNodes[i] = waBinary.Node{
|
||||
@@ -38,8 +45,12 @@ func (cli *Client) CreateGroup(name string, participants []types.JID) (*types.Gr
|
||||
Attrs: waBinary.Attrs{"jid": participant},
|
||||
}
|
||||
}
|
||||
key := GenerateMessageID()
|
||||
resp, err := cli.sendGroupIQ(iqSet, types.GroupServerJID, waBinary.Node{
|
||||
if createKey == "" {
|
||||
createKey = GenerateMessageID()
|
||||
}
|
||||
// WhatsApp web doesn't seem to include the static prefix for these
|
||||
key := strings.TrimPrefix(createKey, "3EB0")
|
||||
resp, err := cli.sendGroupIQ(context.TODO(), iqSet, types.GroupServerJID, waBinary.Node{
|
||||
Tag: "create",
|
||||
Attrs: waBinary.Attrs{
|
||||
"subject": name,
|
||||
@@ -59,7 +70,7 @@ func (cli *Client) CreateGroup(name string, participants []types.JID) (*types.Gr
|
||||
|
||||
// LeaveGroup leaves the specified group on WhatsApp.
|
||||
func (cli *Client) LeaveGroup(jid types.JID) error {
|
||||
_, err := cli.sendGroupIQ(iqSet, types.GroupServerJID, waBinary.Node{
|
||||
_, err := cli.sendGroupIQ(context.TODO(), iqSet, types.GroupServerJID, waBinary.Node{
|
||||
Tag: "leave",
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "group",
|
||||
@@ -141,7 +152,7 @@ func (cli *Client) SetGroupPhoto(jid types.JID, avatar []byte) (string, error) {
|
||||
|
||||
// SetGroupName updates the name (subject) of the given group on WhatsApp.
|
||||
func (cli *Client) SetGroupName(jid types.JID, name string) error {
|
||||
_, err := cli.sendGroupIQ(iqSet, jid, waBinary.Node{
|
||||
_, err := cli.sendGroupIQ(context.TODO(), iqSet, jid, waBinary.Node{
|
||||
Tag: "subject",
|
||||
Content: []byte(name),
|
||||
})
|
||||
@@ -164,7 +175,7 @@ func (cli *Client) SetGroupTopic(jid types.JID, previousID, newID, topic string)
|
||||
if newID == "" {
|
||||
newID = GenerateMessageID()
|
||||
}
|
||||
_, err := cli.sendGroupIQ(iqSet, jid, waBinary.Node{
|
||||
_, err := cli.sendGroupIQ(context.TODO(), iqSet, jid, waBinary.Node{
|
||||
Tag: "description",
|
||||
Attrs: waBinary.Attrs{
|
||||
"prev": previousID,
|
||||
@@ -184,7 +195,7 @@ func (cli *Client) SetGroupLocked(jid types.JID, locked bool) error {
|
||||
if !locked {
|
||||
tag = "unlocked"
|
||||
}
|
||||
_, err := cli.sendGroupIQ(iqSet, jid, waBinary.Node{Tag: tag})
|
||||
_, err := cli.sendGroupIQ(context.TODO(), iqSet, jid, waBinary.Node{Tag: tag})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -194,7 +205,7 @@ func (cli *Client) SetGroupAnnounce(jid types.JID, announce bool) error {
|
||||
if !announce {
|
||||
tag = "not_announcement"
|
||||
}
|
||||
_, err := cli.sendGroupIQ(iqSet, jid, waBinary.Node{Tag: tag})
|
||||
_, err := cli.sendGroupIQ(context.TODO(), iqSet, jid, waBinary.Node{Tag: tag})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -206,7 +217,7 @@ func (cli *Client) GetGroupInviteLink(jid types.JID, reset bool) (string, error)
|
||||
if reset {
|
||||
iqType = iqSet
|
||||
}
|
||||
resp, err := cli.sendGroupIQ(iqType, jid, waBinary.Node{Tag: "invite"})
|
||||
resp, err := cli.sendGroupIQ(context.TODO(), iqType, jid, waBinary.Node{Tag: "invite"})
|
||||
if errors.Is(err, ErrIQNotAuthorized) {
|
||||
return "", wrapIQError(ErrGroupInviteLinkUnauthorized, err)
|
||||
} else if errors.Is(err, ErrIQNotFound) {
|
||||
@@ -227,7 +238,7 @@ func (cli *Client) GetGroupInviteLink(jid types.JID, reset bool) (string, error)
|
||||
//
|
||||
// Note that this is specifically for invite messages, not invite links. Use GetGroupInfoFromLink for resolving chat.whatsapp.com links.
|
||||
func (cli *Client) GetGroupInfoFromInvite(jid, inviter types.JID, code string, expiration int64) (*types.GroupInfo, error) {
|
||||
resp, err := cli.sendGroupIQ(iqGet, jid, waBinary.Node{
|
||||
resp, err := cli.sendGroupIQ(context.TODO(), iqGet, jid, waBinary.Node{
|
||||
Tag: "query",
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "add_request",
|
||||
@@ -252,7 +263,7 @@ func (cli *Client) GetGroupInfoFromInvite(jid, inviter types.JID, code string, e
|
||||
//
|
||||
// Note that this is specifically for invite messages, not invite links. Use JoinGroupWithLink for joining with chat.whatsapp.com links.
|
||||
func (cli *Client) JoinGroupWithInvite(jid, inviter types.JID, code string, expiration int64) error {
|
||||
_, err := cli.sendGroupIQ(iqSet, jid, waBinary.Node{
|
||||
_, err := cli.sendGroupIQ(context.TODO(), iqSet, jid, waBinary.Node{
|
||||
Tag: "accept",
|
||||
Attrs: waBinary.Attrs{
|
||||
"code": code,
|
||||
@@ -267,7 +278,7 @@ func (cli *Client) JoinGroupWithInvite(jid, inviter types.JID, code string, expi
|
||||
// This will not cause the user to join the group.
|
||||
func (cli *Client) GetGroupInfoFromLink(code string) (*types.GroupInfo, error) {
|
||||
code = strings.TrimPrefix(code, InviteLinkPrefix)
|
||||
resp, err := cli.sendGroupIQ(iqGet, types.GroupServerJID, waBinary.Node{
|
||||
resp, err := cli.sendGroupIQ(context.TODO(), iqGet, types.GroupServerJID, waBinary.Node{
|
||||
Tag: "invite",
|
||||
Attrs: waBinary.Attrs{"code": code},
|
||||
})
|
||||
@@ -288,7 +299,7 @@ func (cli *Client) GetGroupInfoFromLink(code string) (*types.GroupInfo, error) {
|
||||
// JoinGroupWithLink joins the group using the given invite link.
|
||||
func (cli *Client) JoinGroupWithLink(code string) (types.JID, error) {
|
||||
code = strings.TrimPrefix(code, InviteLinkPrefix)
|
||||
resp, err := cli.sendGroupIQ(iqSet, types.GroupServerJID, waBinary.Node{
|
||||
resp, err := cli.sendGroupIQ(context.TODO(), iqSet, types.GroupServerJID, waBinary.Node{
|
||||
Tag: "invite",
|
||||
Attrs: waBinary.Attrs{"code": code},
|
||||
})
|
||||
@@ -308,7 +319,7 @@ func (cli *Client) JoinGroupWithLink(code string) (types.JID, error) {
|
||||
|
||||
// GetJoinedGroups returns the list of groups the user is participating in.
|
||||
func (cli *Client) GetJoinedGroups() ([]*types.GroupInfo, error) {
|
||||
resp, err := cli.sendGroupIQ(iqGet, types.GroupServerJID, waBinary.Node{
|
||||
resp, err := cli.sendGroupIQ(context.TODO(), iqGet, types.GroupServerJID, waBinary.Node{
|
||||
Tag: "participating",
|
||||
Content: []waBinary.Node{
|
||||
{Tag: "participants"},
|
||||
@@ -340,11 +351,11 @@ func (cli *Client) GetJoinedGroups() ([]*types.GroupInfo, error) {
|
||||
|
||||
// GetGroupInfo requests basic info about a group chat from the WhatsApp servers.
|
||||
func (cli *Client) GetGroupInfo(jid types.JID) (*types.GroupInfo, error) {
|
||||
return cli.getGroupInfo(jid, true)
|
||||
return cli.getGroupInfo(context.TODO(), jid, true)
|
||||
}
|
||||
|
||||
func (cli *Client) getGroupInfo(jid types.JID, lockParticipantCache bool) (*types.GroupInfo, error) {
|
||||
res, err := cli.sendGroupIQ(iqGet, jid, waBinary.Node{
|
||||
func (cli *Client) getGroupInfo(ctx context.Context, jid types.JID, lockParticipantCache bool) (*types.GroupInfo, error) {
|
||||
res, err := cli.sendGroupIQ(ctx, iqGet, jid, waBinary.Node{
|
||||
Tag: "query",
|
||||
Attrs: waBinary.Attrs{"request": "interactive"},
|
||||
})
|
||||
@@ -376,11 +387,11 @@ func (cli *Client) getGroupInfo(jid types.JID, lockParticipantCache bool) (*type
|
||||
return groupInfo, nil
|
||||
}
|
||||
|
||||
func (cli *Client) getGroupMembers(jid types.JID) ([]types.JID, error) {
|
||||
func (cli *Client) getGroupMembers(ctx context.Context, jid types.JID) ([]types.JID, error) {
|
||||
cli.groupParticipantsCacheLock.Lock()
|
||||
defer cli.groupParticipantsCacheLock.Unlock()
|
||||
if _, ok := cli.groupParticipantsCache[jid]; !ok {
|
||||
_, err := cli.getGroupInfo(jid, false)
|
||||
_, err := cli.getGroupInfo(ctx, jid, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -431,6 +442,9 @@ func (cli *Client) parseGroupNode(groupNode *waBinary.Node) (*types.GroupInfo, e
|
||||
case "ephemeral":
|
||||
group.IsEphemeral = true
|
||||
group.DisappearingTimer = uint32(childAG.Uint64("expiration"))
|
||||
case "member_add_mode":
|
||||
modeBytes, _ := child.Content.([]byte)
|
||||
group.MemberAddMode = types.GroupMemberAddMode(modeBytes)
|
||||
default:
|
||||
cli.Log.Debugf("Unknown element in group node %s: %s", group.JID.String(), child.XMLString())
|
||||
}
|
||||
@@ -461,7 +475,10 @@ func (cli *Client) parseGroupCreate(node *waBinary.Node) (*events.JoinedGroup, e
|
||||
return nil, fmt.Errorf("group create notification didn't contain group info")
|
||||
}
|
||||
var evt events.JoinedGroup
|
||||
evt.Reason = node.AttrGetter().OptionalString("reason")
|
||||
ag := node.AttrGetter()
|
||||
evt.Reason = ag.OptionalString("reason")
|
||||
evt.CreateKey = ag.OptionalString("key")
|
||||
evt.Type = ag.OptionalString("type")
|
||||
info, err := cli.parseGroupNode(&groupNode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse group info in create notification: %w", err)
|
||||
|
||||
6
vendor/go.mau.fi/whatsmeow/handshake.go
vendored
6
vendor/go.mau.fi/whatsmeow/handshake.go
vendored
@@ -26,7 +26,7 @@ func (cli *Client) doHandshake(fs *socket.FrameSocket, ephemeralKP keys.KeyPair)
|
||||
nh.Start(socket.NoiseStartPattern, fs.Header)
|
||||
nh.Authenticate(ephemeralKP.Pub[:])
|
||||
data, err := proto.Marshal(&waProto.HandshakeMessage{
|
||||
ClientHello: &waProto.ClientHello{
|
||||
ClientHello: &waProto.HandshakeClientHello{
|
||||
Ephemeral: ephemeralKP.Pub[:],
|
||||
},
|
||||
})
|
||||
@@ -87,7 +87,7 @@ func (cli *Client) doHandshake(fs *socket.FrameSocket, ephemeralKP keys.KeyPair)
|
||||
if certDetailsRaw == nil || certSignature == nil {
|
||||
return fmt.Errorf("missing parts of noise certificate")
|
||||
}
|
||||
var certDetails waProto.NoiseCertificateDetails
|
||||
var certDetails waProto.NoiseCertificate_Details
|
||||
err = proto.Unmarshal(certDetailsRaw, &certDetails)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal noise certificate details: %w", err)
|
||||
@@ -107,7 +107,7 @@ func (cli *Client) doHandshake(fs *socket.FrameSocket, ephemeralKP keys.KeyPair)
|
||||
}
|
||||
encryptedClientFinishPayload := nh.Encrypt(clientFinishPayloadBytes)
|
||||
data, err = proto.Marshal(&waProto.HandshakeMessage{
|
||||
ClientFinish: &waProto.ClientFinish{
|
||||
ClientFinish: &waProto.HandshakeClientFinish{
|
||||
Static: encryptedPubkey,
|
||||
Payload: encryptedClientFinishPayload,
|
||||
},
|
||||
|
||||
10
vendor/go.mau.fi/whatsmeow/internals.go
vendored
10
vendor/go.mau.fi/whatsmeow/internals.go
vendored
@@ -6,7 +6,11 @@
|
||||
|
||||
package whatsmeow
|
||||
|
||||
import waBinary "go.mau.fi/whatsmeow/binary"
|
||||
import (
|
||||
"context"
|
||||
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
)
|
||||
|
||||
type DangerousInternalClient struct {
|
||||
c *Client
|
||||
@@ -54,8 +58,8 @@ func (int *DangerousInternalClient) GetServerPreKeyCount() (int, error) {
|
||||
return int.c.getServerPreKeyCount()
|
||||
}
|
||||
|
||||
func (int *DangerousInternalClient) RequestAppStateKeys(keyIDs [][]byte) {
|
||||
int.c.requestAppStateKeys(keyIDs)
|
||||
func (int *DangerousInternalClient) RequestAppStateKeys(ctx context.Context, keyIDs [][]byte) {
|
||||
int.c.requestAppStateKeys(ctx, keyIDs)
|
||||
}
|
||||
|
||||
func (int *DangerousInternalClient) SendRetryReceipt(node *waBinary.Node, forceIncludeIdentity bool) {
|
||||
|
||||
14
vendor/go.mau.fi/whatsmeow/message.go
vendored
14
vendor/go.mau.fi/whatsmeow/message.go
vendored
@@ -51,6 +51,11 @@ func (cli *Client) handleEncryptedMessage(node *waBinary.Node) {
|
||||
}
|
||||
|
||||
func (cli *Client) parseMessageSource(node *waBinary.Node, requireParticipant bool) (source types.MessageSource, err error) {
|
||||
clientID := cli.Store.ID
|
||||
if clientID == nil {
|
||||
err = ErrNotLoggedIn
|
||||
return
|
||||
}
|
||||
ag := node.AttrGetter()
|
||||
from := ag.JID("from")
|
||||
if from.Server == types.GroupServer || from.Server == types.BroadcastServer {
|
||||
@@ -61,13 +66,13 @@ func (cli *Client) parseMessageSource(node *waBinary.Node, requireParticipant bo
|
||||
} else {
|
||||
source.Sender = ag.OptionalJIDOrEmpty("participant")
|
||||
}
|
||||
if source.Sender.User == cli.Store.ID.User {
|
||||
if source.Sender.User == clientID.User {
|
||||
source.IsFromMe = true
|
||||
}
|
||||
if from.Server == types.BroadcastServer {
|
||||
source.BroadcastListOwner = ag.OptionalJIDOrEmpty("recipient")
|
||||
}
|
||||
} else if from.User == cli.Store.ID.User {
|
||||
} else if from.User == clientID.User {
|
||||
source.IsFromMe = true
|
||||
source.Sender = from
|
||||
recipient := ag.OptionalJID("recipient")
|
||||
@@ -395,7 +400,8 @@ func (cli *Client) handleDecryptedMessage(info *types.MessageInfo, msg *waProto.
|
||||
}
|
||||
|
||||
func (cli *Client) sendProtocolMessageReceipt(id, msgType string) {
|
||||
if len(id) == 0 || cli.Store.ID == nil {
|
||||
clientID := cli.Store.ID
|
||||
if len(id) == 0 || clientID == nil {
|
||||
return
|
||||
}
|
||||
err := cli.sendNode(waBinary.Node{
|
||||
@@ -403,7 +409,7 @@ func (cli *Client) sendProtocolMessageReceipt(id, msgType string) {
|
||||
Attrs: waBinary.Attrs{
|
||||
"id": id,
|
||||
"type": msgType,
|
||||
"to": types.NewJID(cli.Store.ID.User, types.LegacyUserServer),
|
||||
"to": types.NewJID(clientID.User, types.LegacyUserServer),
|
||||
},
|
||||
Content: nil,
|
||||
})
|
||||
|
||||
4
vendor/go.mau.fi/whatsmeow/prekeys.go
vendored
4
vendor/go.mau.fi/whatsmeow/prekeys.go
vendored
@@ -7,6 +7,7 @@
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -93,7 +94,7 @@ type preKeyResp struct {
|
||||
err error
|
||||
}
|
||||
|
||||
func (cli *Client) fetchPreKeys(users []types.JID) (map[types.JID]preKeyResp, error) {
|
||||
func (cli *Client) fetchPreKeys(ctx context.Context, users []types.JID) (map[types.JID]preKeyResp, error) {
|
||||
requests := make([]waBinary.Node, len(users))
|
||||
for i, user := range users {
|
||||
requests[i].Tag = "user"
|
||||
@@ -103,6 +104,7 @@ func (cli *Client) fetchPreKeys(users []types.JID) (map[types.JID]preKeyResp, er
|
||||
}
|
||||
}
|
||||
resp, err := cli.sendIQ(infoQuery{
|
||||
Context: ctx,
|
||||
Namespace: "encrypt",
|
||||
Type: "get",
|
||||
To: types.ServerJID,
|
||||
|
||||
22
vendor/go.mau.fi/whatsmeow/request.go
vendored
22
vendor/go.mau.fi/whatsmeow/request.go
vendored
@@ -201,17 +201,17 @@ func (cli *Client) retryFrame(reqType, id string, data []byte, origResp *waBinar
|
||||
return nil, err
|
||||
}
|
||||
var resp *waBinary.Node
|
||||
if ctx != nil && timeout > 0 {
|
||||
select {
|
||||
case resp = <-respChan:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-time.After(timeout):
|
||||
// FIXME this error isn't technically correct (but works for now - the ctx and timeout params are only used from sendIQ)
|
||||
return nil, ErrIQTimedOut
|
||||
}
|
||||
} else {
|
||||
resp = <-respChan
|
||||
timeoutChan := make(<-chan time.Time, 1)
|
||||
if timeout > 0 {
|
||||
timeoutChan = time.After(timeout)
|
||||
}
|
||||
select {
|
||||
case resp = <-respChan:
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-timeoutChan:
|
||||
// FIXME this error isn't technically correct (but works for now - the timeout param is only used from sendIQ)
|
||||
return nil, ErrIQTimedOut
|
||||
}
|
||||
if isDisconnectNode(resp) {
|
||||
cli.Log.Debugf("Retrying %s %s was interrupted by websocket disconnection (%v), not retrying anymore", reqType, id, resp.XMLString())
|
||||
|
||||
3
vendor/go.mau.fi/whatsmeow/retry.go
vendored
3
vendor/go.mau.fi/whatsmeow/retry.go
vendored
@@ -7,6 +7,7 @@
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -151,7 +152,7 @@ func (cli *Client) handleRetryReceipt(receipt *events.Receipt, node *waBinary.No
|
||||
} else if reason, recreate := cli.shouldRecreateSession(retryCount, receipt.Sender); recreate {
|
||||
cli.Log.Debugf("Fetching prekeys for %s for handling retry receipt with no prekey bundle because %s", receipt.Sender, reason)
|
||||
var keys map[types.JID]preKeyResp
|
||||
keys, err = cli.fetchPreKeys([]types.JID{receipt.Sender})
|
||||
keys, err = cli.fetchPreKeys(context.TODO(), []types.JID{receipt.Sender})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
131
vendor/go.mau.fi/whatsmeow/send.go
vendored
131
vendor/go.mau.fi/whatsmeow/send.go
vendored
@@ -7,6 +7,7 @@
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
@@ -34,7 +35,7 @@ import (
|
||||
// GenerateMessageID generates a random string that can be used as a message ID on WhatsApp.
|
||||
//
|
||||
// msgID := whatsmeow.GenerateMessageID()
|
||||
// cli.SendMessage(targetJID, msgID, &waProto.Message{...})
|
||||
// cli.SendMessage(context.Background(), targetJID, msgID, &waProto.Message{...})
|
||||
func GenerateMessageID() types.MessageID {
|
||||
id := make([]byte, 8)
|
||||
_, err := rand.Read(id)
|
||||
@@ -45,6 +46,31 @@ func GenerateMessageID() types.MessageID {
|
||||
return "3EB0" + strings.ToUpper(hex.EncodeToString(id))
|
||||
}
|
||||
|
||||
type MessageDebugTimings struct {
|
||||
Queue time.Duration
|
||||
|
||||
Marshal time.Duration
|
||||
GetParticipants time.Duration
|
||||
GetDevices time.Duration
|
||||
GroupEncrypt time.Duration
|
||||
PeerEncrypt time.Duration
|
||||
|
||||
Send time.Duration
|
||||
Resp time.Duration
|
||||
Retry time.Duration
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// SendMessage sends the given message.
|
||||
//
|
||||
// If the message ID is not provided, a random message ID will be generated.
|
||||
@@ -54,7 +80,7 @@ func GenerateMessageID() types.MessageID {
|
||||
//
|
||||
// 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(targetJID, "", &waProto.Message{
|
||||
// cli.SendMessage(context.Background(), targetJID, "", &waProto.Message{
|
||||
// Conversation: proto.String("Hello, World!"),
|
||||
// })
|
||||
//
|
||||
@@ -65,18 +91,22 @@ func GenerateMessageID() types.MessageID {
|
||||
//
|
||||
// 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.
|
||||
func (cli *Client) SendMessage(to types.JID, id types.MessageID, message *waProto.Message) (time.Time, error) {
|
||||
func (cli *Client) SendMessage(ctx context.Context, to types.JID, id types.MessageID, message *waProto.Message) (resp SendResponse, err error) {
|
||||
isPeerMessage := to.User == cli.Store.ID.User
|
||||
if to.AD && !isPeerMessage {
|
||||
return time.Time{}, ErrRecipientADJID
|
||||
err = ErrRecipientADJID
|
||||
return
|
||||
}
|
||||
|
||||
if len(id) == 0 {
|
||||
id = GenerateMessageID()
|
||||
}
|
||||
resp.ID = id
|
||||
|
||||
start := time.Now()
|
||||
// Sending multiple messages at a time can cause weird issues and makes it harder to retry safely
|
||||
cli.messageSendLock.Lock()
|
||||
resp.DebugTimings.Queue = time.Since(start)
|
||||
defer cli.messageSendLock.Unlock()
|
||||
|
||||
respChan := cli.waitResponse(id)
|
||||
@@ -84,34 +114,43 @@ func (cli *Client) SendMessage(to types.JID, id types.MessageID, message *waProt
|
||||
if !isPeerMessage {
|
||||
cli.addRecentMessage(to, id, message)
|
||||
}
|
||||
var err error
|
||||
var phash string
|
||||
var data []byte
|
||||
switch to.Server {
|
||||
case types.GroupServer, types.BroadcastServer:
|
||||
phash, data, err = cli.sendGroup(to, id, message)
|
||||
phash, data, err = cli.sendGroup(ctx, to, id, message, &resp.DebugTimings)
|
||||
case types.DefaultUserServer:
|
||||
if isPeerMessage {
|
||||
data, err = cli.sendPeerMessage(to, id, message)
|
||||
data, err = cli.sendPeerMessage(to, id, message, &resp.DebugTimings)
|
||||
} else {
|
||||
data, err = cli.sendDM(to, id, message)
|
||||
data, err = cli.sendDM(ctx, to, id, message, &resp.DebugTimings)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("%w %s", ErrUnknownServer, to.Server)
|
||||
}
|
||||
start = time.Now()
|
||||
if err != nil {
|
||||
cli.cancelResponse(id, respChan)
|
||||
return time.Time{}, err
|
||||
return
|
||||
}
|
||||
resp := <-respChan
|
||||
if isDisconnectNode(resp) {
|
||||
resp, err = cli.retryFrame("message send", id, data, resp, nil, 0)
|
||||
var respNode *waBinary.Node
|
||||
select {
|
||||
case respNode = <-respChan:
|
||||
case <-ctx.Done():
|
||||
err = ctx.Err()
|
||||
return
|
||||
}
|
||||
resp.DebugTimings.Resp = time.Since(start)
|
||||
if isDisconnectNode(respNode) {
|
||||
start = time.Now()
|
||||
respNode, err = cli.retryFrame("message send", id, data, respNode, ctx, 0)
|
||||
resp.DebugTimings.Retry = time.Since(start)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
return
|
||||
}
|
||||
}
|
||||
ag := resp.AttrGetter()
|
||||
ts := ag.UnixTime("t")
|
||||
ag := respNode.AttrGetter()
|
||||
resp.Timestamp = ag.UnixTime("t")
|
||||
expectedPHash := ag.OptionalString("phash")
|
||||
if len(expectedPHash) > 0 && phash != expectedPHash {
|
||||
cli.Log.Warnf("Server returned different participant list hash when sending to %s. Some devices may not have received the message.", to)
|
||||
@@ -120,7 +159,7 @@ func (cli *Client) SendMessage(to types.JID, id types.MessageID, message *waProt
|
||||
delete(cli.groupParticipantsCache, to)
|
||||
cli.groupParticipantsCacheLock.Unlock()
|
||||
}
|
||||
return ts, nil
|
||||
return
|
||||
}
|
||||
|
||||
// RevokeMessage deletes the given message from everyone in the chat.
|
||||
@@ -128,8 +167,8 @@ func (cli *Client) SendMessage(to types.JID, id types.MessageID, message *waProt
|
||||
//
|
||||
// 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 (cli *Client) RevokeMessage(chat types.JID, id types.MessageID) (time.Time, error) {
|
||||
return cli.SendMessage(chat, cli.generateRequestID(), &waProto.Message{
|
||||
func (cli *Client) RevokeMessage(chat types.JID, id types.MessageID) (SendResponse, error) {
|
||||
return cli.SendMessage(context.TODO(), chat, "", &waProto.Message{
|
||||
ProtocolMessage: &waProto.ProtocolMessage{
|
||||
Type: waProto.ProtocolMessage_REVOKE.Enum(),
|
||||
Key: &waProto.MessageKey{
|
||||
@@ -175,7 +214,7 @@ func ParseDisappearingTimerString(val string) (time.Duration, bool) {
|
||||
func (cli *Client) SetDisappearingTimer(chat types.JID, timer time.Duration) (err error) {
|
||||
switch chat.Server {
|
||||
case types.DefaultUserServer:
|
||||
_, err = cli.SendMessage(chat, "", &waProto.Message{
|
||||
_, err = cli.SendMessage(context.TODO(), chat, "", &waProto.Message{
|
||||
ProtocolMessage: &waProto.ProtocolMessage{
|
||||
Type: waProto.ProtocolMessage_EPHEMERAL_SETTING.Enum(),
|
||||
EphemeralExpiration: proto.Uint32(uint32(timer.Seconds())),
|
||||
@@ -183,9 +222,9 @@ func (cli *Client) SetDisappearingTimer(chat types.JID, timer time.Duration) (er
|
||||
})
|
||||
case types.GroupServer:
|
||||
if timer == 0 {
|
||||
_, err = cli.sendGroupIQ(iqSet, chat, waBinary.Node{Tag: "not_ephemeral"})
|
||||
_, err = cli.sendGroupIQ(context.TODO(), iqSet, chat, waBinary.Node{Tag: "not_ephemeral"})
|
||||
} else {
|
||||
_, err = cli.sendGroupIQ(iqSet, chat, waBinary.Node{
|
||||
_, err = cli.sendGroupIQ(context.TODO(), iqSet, chat, waBinary.Node{
|
||||
Tag: "ephemeral",
|
||||
Attrs: waBinary.Attrs{
|
||||
"expiration": strconv.Itoa(int(timer.Seconds())),
|
||||
@@ -212,26 +251,31 @@ func participantListHashV2(participants []types.JID) string {
|
||||
return fmt.Sprintf("2:%s", base64.RawStdEncoding.EncodeToString(hash[:6]))
|
||||
}
|
||||
|
||||
func (cli *Client) sendGroup(to types.JID, id types.MessageID, message *waProto.Message) (string, []byte, error) {
|
||||
func (cli *Client) sendGroup(ctx context.Context, to types.JID, id types.MessageID, message *waProto.Message, timings *MessageDebugTimings) (string, []byte, error) {
|
||||
var participants []types.JID
|
||||
var err error
|
||||
start := time.Now()
|
||||
if to.Server == types.GroupServer {
|
||||
participants, err = cli.getGroupMembers(to)
|
||||
participants, err = cli.getGroupMembers(ctx, to)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to get group members: %w", err)
|
||||
}
|
||||
} else {
|
||||
// TODO use context
|
||||
participants, err = cli.getBroadcastListParticipants(to)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to get broadcast list members: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
timings.GetParticipants = time.Since(start)
|
||||
start = time.Now()
|
||||
plaintext, _, err := marshalMessage(to, message)
|
||||
timings.Marshal = time.Since(start)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
start = time.Now()
|
||||
builder := groups.NewGroupSessionBuilder(cli.Store, pbSerializer)
|
||||
senderKeyName := protocol.NewSenderKeyName(to.String(), cli.Store.ID.SignalAddress())
|
||||
signalSKDMessage, err := builder.Create(senderKeyName)
|
||||
@@ -255,8 +299,9 @@ func (cli *Client) sendGroup(to types.JID, id types.MessageID, message *waProto.
|
||||
return "", nil, fmt.Errorf("failed to encrypt group message to send %s to %s: %w", id, to, err)
|
||||
}
|
||||
ciphertext := encrypted.SignedSerialize()
|
||||
timings.GroupEncrypt = time.Since(start)
|
||||
|
||||
node, allDevices, err := cli.prepareMessageNode(to, id, message, participants, skdPlaintext, nil)
|
||||
node, allDevices, err := cli.prepareMessageNode(ctx, to, id, message, participants, skdPlaintext, nil, timings)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
@@ -269,36 +314,44 @@ func (cli *Client) sendGroup(to types.JID, id types.MessageID, message *waProto.
|
||||
Attrs: waBinary.Attrs{"v": "2", "type": "skmsg"},
|
||||
})
|
||||
|
||||
start = time.Now()
|
||||
data, err := cli.sendNodeAndGetData(*node)
|
||||
timings.Send = time.Since(start)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("failed to send message node: %w", err)
|
||||
}
|
||||
return phash, data, nil
|
||||
}
|
||||
|
||||
func (cli *Client) sendPeerMessage(to types.JID, id types.MessageID, message *waProto.Message) ([]byte, error) {
|
||||
node, err := cli.preparePeerMessageNode(to, id, message)
|
||||
func (cli *Client) sendPeerMessage(to types.JID, id types.MessageID, message *waProto.Message, timings *MessageDebugTimings) ([]byte, error) {
|
||||
node, err := cli.preparePeerMessageNode(to, id, message, timings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start := time.Now()
|
||||
data, err := cli.sendNodeAndGetData(*node)
|
||||
timings.Send = time.Since(start)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send message node: %w", err)
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (cli *Client) sendDM(to types.JID, id types.MessageID, message *waProto.Message) ([]byte, error) {
|
||||
func (cli *Client) sendDM(ctx context.Context, to types.JID, id types.MessageID, message *waProto.Message, timings *MessageDebugTimings) ([]byte, error) {
|
||||
start := time.Now()
|
||||
messagePlaintext, deviceSentMessagePlaintext, err := marshalMessage(to, message)
|
||||
timings.Marshal = time.Since(start)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
node, _, err := cli.prepareMessageNode(to, id, message, []types.JID{to, cli.Store.ID.ToNonAD()}, messagePlaintext, deviceSentMessagePlaintext)
|
||||
node, _, err := cli.prepareMessageNode(ctx, to, id, message, []types.JID{to, cli.Store.ID.ToNonAD()}, messagePlaintext, deviceSentMessagePlaintext, timings)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start = time.Now()
|
||||
data, err := cli.sendNodeAndGetData(*node)
|
||||
timings.Send = time.Since(start)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to send message node: %w", err)
|
||||
}
|
||||
@@ -336,7 +389,7 @@ func getEditAttribute(msg *waProto.Message) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (cli *Client) preparePeerMessageNode(to types.JID, id types.MessageID, message *waProto.Message) (*waBinary.Node, error) {
|
||||
func (cli *Client) preparePeerMessageNode(to types.JID, id types.MessageID, message *waProto.Message, timings *MessageDebugTimings) (*waBinary.Node, error) {
|
||||
attrs := waBinary.Attrs{
|
||||
"id": id,
|
||||
"type": "text",
|
||||
@@ -346,12 +399,16 @@ func (cli *Client) preparePeerMessageNode(to types.JID, id types.MessageID, mess
|
||||
if message.GetProtocolMessage().GetType() == waProto.ProtocolMessage_APP_STATE_SYNC_KEY_REQUEST {
|
||||
attrs["push_priority"] = "high"
|
||||
}
|
||||
start := time.Now()
|
||||
plaintext, err := proto.Marshal(message)
|
||||
timings.Marshal = time.Since(start)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to marshal message: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
start = time.Now()
|
||||
encrypted, isPreKey, err := cli.encryptMessageForDevice(plaintext, to, nil)
|
||||
timings.PeerEncrypt = time.Since(start)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt peer message for %s: %v", to, err)
|
||||
}
|
||||
@@ -366,8 +423,10 @@ func (cli *Client) preparePeerMessageNode(to types.JID, id types.MessageID, mess
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (cli *Client) prepareMessageNode(to types.JID, id types.MessageID, message *waProto.Message, participants []types.JID, plaintext, dsmPlaintext []byte) (*waBinary.Node, []types.JID, error) {
|
||||
allDevices, err := cli.GetUserDevices(participants)
|
||||
func (cli *Client) prepareMessageNode(ctx context.Context, to types.JID, id types.MessageID, message *waProto.Message, participants []types.JID, plaintext, dsmPlaintext []byte, timings *MessageDebugTimings) (*waBinary.Node, []types.JID, error) {
|
||||
start := time.Now()
|
||||
allDevices, err := cli.GetUserDevicesContext(ctx, participants)
|
||||
timings.GetDevices = time.Since(start)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to get device list: %w", err)
|
||||
}
|
||||
@@ -381,7 +440,9 @@ func (cli *Client) prepareMessageNode(to types.JID, id types.MessageID, message
|
||||
attrs["edit"] = editAttr
|
||||
}
|
||||
|
||||
participantNodes, includeIdentity := cli.encryptMessageForDevices(allDevices, id, plaintext, dsmPlaintext)
|
||||
start = time.Now()
|
||||
participantNodes, includeIdentity := cli.encryptMessageForDevices(ctx, allDevices, id, plaintext, dsmPlaintext)
|
||||
timings.PeerEncrypt = time.Since(start)
|
||||
content := []waBinary.Node{{
|
||||
Tag: "participants",
|
||||
Content: participantNodes,
|
||||
@@ -430,7 +491,7 @@ func (cli *Client) makeDeviceIdentityNode() waBinary.Node {
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) encryptMessageForDevices(allDevices []types.JID, id string, msgPlaintext, dsmPlaintext []byte) ([]waBinary.Node, bool) {
|
||||
func (cli *Client) encryptMessageForDevices(ctx context.Context, allDevices []types.JID, id string, msgPlaintext, dsmPlaintext []byte) ([]waBinary.Node, bool) {
|
||||
includeIdentity := false
|
||||
participantNodes := make([]waBinary.Node, 0, len(allDevices))
|
||||
var retryDevices []types.JID
|
||||
@@ -456,7 +517,7 @@ func (cli *Client) encryptMessageForDevices(allDevices []types.JID, id string, m
|
||||
}
|
||||
}
|
||||
if len(retryDevices) > 0 {
|
||||
bundles, err := cli.fetchPreKeys(retryDevices)
|
||||
bundles, err := cli.fetchPreKeys(ctx, retryDevices)
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to fetch prekeys for %v to retry encryption: %v", retryDevices, err)
|
||||
} else {
|
||||
|
||||
@@ -65,8 +65,8 @@ func (vc WAVersionContainer) Hash() [16]byte {
|
||||
return md5.Sum([]byte(vc.String()))
|
||||
}
|
||||
|
||||
func (vc WAVersionContainer) ProtoAppVersion() *waProto.AppVersion {
|
||||
return &waProto.AppVersion{
|
||||
func (vc WAVersionContainer) ProtoAppVersion() *waProto.ClientPayload_UserAgent_AppVersion {
|
||||
return &waProto.ClientPayload_UserAgent_AppVersion{
|
||||
Primary: &vc[0],
|
||||
Secondary: &vc[1],
|
||||
Tertiary: &vc[2],
|
||||
@@ -74,7 +74,7 @@ func (vc WAVersionContainer) ProtoAppVersion() *waProto.AppVersion {
|
||||
}
|
||||
|
||||
// waVersion is the WhatsApp web client version
|
||||
var waVersion = WAVersionContainer{2, 2222, 11}
|
||||
var waVersion = WAVersionContainer{2, 2228, 12}
|
||||
|
||||
// waVersionHash is the md5 hash of a dot-separated waVersion
|
||||
var waVersionHash [16]byte
|
||||
@@ -101,9 +101,9 @@ func SetWAVersion(version WAVersionContainer) {
|
||||
}
|
||||
|
||||
var BaseClientPayload = &waProto.ClientPayload{
|
||||
UserAgent: &waProto.UserAgent{
|
||||
Platform: waProto.UserAgent_WEB.Enum(),
|
||||
ReleaseChannel: waProto.UserAgent_RELEASE.Enum(),
|
||||
UserAgent: &waProto.ClientPayload_UserAgent{
|
||||
Platform: waProto.ClientPayload_UserAgent_WEB.Enum(),
|
||||
ReleaseChannel: waProto.ClientPayload_UserAgent_RELEASE.Enum(),
|
||||
AppVersion: waVersion.ProtoAppVersion(),
|
||||
Mcc: proto.String("000"),
|
||||
Mnc: proto.String("000"),
|
||||
@@ -115,19 +115,16 @@ var BaseClientPayload = &waProto.ClientPayload{
|
||||
LocaleLanguageIso6391: proto.String("en"),
|
||||
LocaleCountryIso31661Alpha2: proto.String("en"),
|
||||
},
|
||||
WebInfo: &waProto.WebInfo{
|
||||
WebSubPlatform: waProto.WebInfo_WEB_BROWSER.Enum(),
|
||||
WebInfo: &waProto.ClientPayload_WebInfo{
|
||||
WebSubPlatform: waProto.ClientPayload_WebInfo_WEB_BROWSER.Enum(),
|
||||
},
|
||||
ConnectType: waProto.ClientPayload_WIFI_UNKNOWN.Enum(),
|
||||
ConnectReason: waProto.ClientPayload_USER_ACTIVATED.Enum(),
|
||||
}
|
||||
|
||||
// Deprecated: renamed to DeviceProps
|
||||
var CompanionProps = DeviceProps
|
||||
|
||||
var DeviceProps = &waProto.DeviceProps{
|
||||
Os: proto.String("whatsmeow"),
|
||||
Version: &waProto.AppVersion{
|
||||
Version: &waProto.DeviceProps_AppVersion{
|
||||
Primary: proto.Uint32(0),
|
||||
Secondary: proto.Uint32(1),
|
||||
Tertiary: proto.Uint32(0),
|
||||
@@ -152,7 +149,7 @@ func (device *Device) getRegistrationPayload() *waProto.ClientPayload {
|
||||
preKeyID := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(preKeyID, device.SignedPreKey.KeyID)
|
||||
deviceProps, _ := proto.Marshal(DeviceProps)
|
||||
payload.DevicePairingData = &waProto.DevicePairingRegistrationData{
|
||||
payload.DevicePairingData = &waProto.ClientPayload_DevicePairingRegistrationData{
|
||||
ERegid: regID,
|
||||
EKeytype: []byte{ecc.DjbType},
|
||||
EIdent: device.IdentityKey.Pub[:],
|
||||
|
||||
@@ -92,6 +92,14 @@ type MarkChatAsRead struct {
|
||||
Action *waProto.MarkChatAsReadAction // Whether the chat was marked as read or unread, and info about the most recent messages.
|
||||
}
|
||||
|
||||
// DeleteChat is emitted when a chat is deleted on another device.
|
||||
type DeleteChat struct {
|
||||
JID types.JID // The chat which was deleted.
|
||||
Timestamp time.Time // The time when the deletion happened.
|
||||
|
||||
Action *waProto.DeleteChatAction // Information about the deletion.
|
||||
}
|
||||
|
||||
// PushNameSetting is emitted when the user's push name is changed from another device.
|
||||
type PushNameSetting struct {
|
||||
Timestamp time.Time // The time when the push name was changed.
|
||||
|
||||
@@ -213,8 +213,10 @@ 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
|
||||
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.
|
||||
@@ -239,6 +241,15 @@ func (evt *Message) UnwrapRaw() *Message {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -305,7 +316,9 @@ type Presence struct {
|
||||
|
||||
// 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"
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
8
vendor/go.mau.fi/whatsmeow/types/group.go
vendored
8
vendor/go.mau.fi/whatsmeow/types/group.go
vendored
@@ -10,6 +10,12 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type GroupMemberAddMode string
|
||||
|
||||
const (
|
||||
GroupMemberAddModeAdmin GroupMemberAddMode = "admin_add"
|
||||
)
|
||||
|
||||
// GroupInfo contains basic information about a group chat on WhatsApp.
|
||||
type GroupInfo struct {
|
||||
JID JID
|
||||
@@ -25,6 +31,8 @@ type GroupInfo struct {
|
||||
|
||||
ParticipantVersionID string
|
||||
Participants []GroupParticipant
|
||||
|
||||
MemberAddMode GroupMemberAddMode
|
||||
}
|
||||
|
||||
// GroupName contains the name of a group along with metadata of who set it and when.
|
||||
|
||||
2
vendor/go.mau.fi/whatsmeow/types/user.go
vendored
2
vendor/go.mau.fi/whatsmeow/types/user.go
vendored
@@ -15,7 +15,7 @@ import (
|
||||
// VerifiedName contains verified WhatsApp business details.
|
||||
type VerifiedName struct {
|
||||
Certificate *waProto.VerifiedNameCertificate
|
||||
Details *waProto.VerifiedNameDetails
|
||||
Details *waProto.VerifiedNameCertificate_Details
|
||||
}
|
||||
|
||||
// UserInfo contains info about a WhatsApp user.
|
||||
|
||||
2
vendor/go.mau.fi/whatsmeow/upload.go
vendored
2
vendor/go.mau.fi/whatsmeow/upload.go
vendored
@@ -53,7 +53,7 @@ type UploadResponse struct {
|
||||
// FileSha256: resp.FileSha256,
|
||||
// FileLength: &resp.FileLength,
|
||||
// }
|
||||
// _, err = cli.SendMessage(targetJID, "", &waProto.Message{
|
||||
// _, err = cli.SendMessage(context.Background(), targetJID, "", &waProto.Message{
|
||||
// ImageMessage: imageMsg,
|
||||
// })
|
||||
// // handle error again
|
||||
|
||||
47
vendor/go.mau.fi/whatsmeow/user.go
vendored
47
vendor/go.mau.fi/whatsmeow/user.go
vendored
@@ -7,6 +7,7 @@
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
@@ -70,6 +71,23 @@ func (cli *Client) ResolveBusinessMessageLink(code string) (*types.BusinessMessa
|
||||
return &target, ag.Error()
|
||||
}
|
||||
|
||||
// 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 (cli *Client) SetStatusMessage(msg string) error {
|
||||
_, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "status",
|
||||
Type: iqSet,
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "status",
|
||||
Content: msg,
|
||||
}},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// IsOnWhatsApp checks if the given phone numbers are registered on WhatsApp.
|
||||
// The phone numbers should be in international format, including the `+` prefix.
|
||||
func (cli *Client) IsOnWhatsApp(phones []string) ([]types.IsOnWhatsAppResponse, error) {
|
||||
@@ -77,7 +95,7 @@ func (cli *Client) IsOnWhatsApp(phones []string) ([]types.IsOnWhatsAppResponse,
|
||||
for i := range jids {
|
||||
jids[i] = types.NewJID(phones[i], types.LegacyUserServer)
|
||||
}
|
||||
list, err := cli.usync(jids, "query", "interactive", []waBinary.Node{
|
||||
list, err := cli.usync(context.TODO(), jids, "query", "interactive", []waBinary.Node{
|
||||
{Tag: "business", Content: []waBinary.Node{{Tag: "verified_name"}}},
|
||||
{Tag: "contact"},
|
||||
})
|
||||
@@ -108,7 +126,7 @@ func (cli *Client) IsOnWhatsApp(phones []string) ([]types.IsOnWhatsAppResponse,
|
||||
|
||||
// GetUserInfo gets basic user info (avatar, status, verified business name, device list).
|
||||
func (cli *Client) GetUserInfo(jids []types.JID) (map[types.JID]types.UserInfo, error) {
|
||||
list, err := cli.usync(jids, "full", "background", []waBinary.Node{
|
||||
list, err := cli.usync(context.TODO(), jids, "full", "background", []waBinary.Node{
|
||||
{Tag: "business", Content: []waBinary.Node{{Tag: "verified_name"}}},
|
||||
{Tag: "status"},
|
||||
{Tag: "picture"},
|
||||
@@ -144,6 +162,10 @@ func (cli *Client) GetUserInfo(jids []types.JID) (map[types.JID]types.UserInfo,
|
||||
// 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 (cli *Client) GetUserDevices(jids []types.JID) ([]types.JID, error) {
|
||||
return cli.GetUserDevicesContext(context.Background(), jids)
|
||||
}
|
||||
|
||||
func (cli *Client) GetUserDevicesContext(ctx context.Context, jids []types.JID) ([]types.JID, error) {
|
||||
cli.userDevicesCacheLock.Lock()
|
||||
defer cli.userDevicesCacheLock.Unlock()
|
||||
|
||||
@@ -160,7 +182,7 @@ func (cli *Client) GetUserDevices(jids []types.JID) ([]types.JID, error) {
|
||||
return devices, nil
|
||||
}
|
||||
|
||||
list, err := cli.usync(jidsToSync, "query", "message", []waBinary.Node{
|
||||
list, err := cli.usync(ctx, jidsToSync, "query", "message", []waBinary.Node{
|
||||
{Tag: "devices", Attrs: waBinary.Attrs{"version": "2"}},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -181,8 +203,10 @@ func (cli *Client) GetUserDevices(jids []types.JID) ([]types.JID, 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 (cli *Client) GetProfilePictureInfo(jid types.JID, preview bool) (*types.ProfilePictureInfo, error) {
|
||||
//
|
||||
// Optionally, you can pass the last known profile picture ID.
|
||||
// If the profile picture hasn't changed, this will return nil with no error.
|
||||
func (cli *Client) GetProfilePictureInfo(jid types.JID, preview bool, existingID string) (*types.ProfilePictureInfo, error) {
|
||||
attrs := waBinary.Attrs{
|
||||
"query": "url",
|
||||
}
|
||||
@@ -191,6 +215,9 @@ func (cli *Client) GetProfilePictureInfo(jid types.JID, preview bool) (*types.Pr
|
||||
} else {
|
||||
attrs["type"] = "image"
|
||||
}
|
||||
if existingID != "" {
|
||||
attrs["id"] = existingID
|
||||
}
|
||||
resp, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "w:profile:picture",
|
||||
Type: "get",
|
||||
@@ -203,12 +230,15 @@ func (cli *Client) GetProfilePictureInfo(jid types.JID, preview bool) (*types.Pr
|
||||
if errors.Is(err, ErrIQNotAuthorized) {
|
||||
return nil, wrapIQError(ErrProfilePictureUnauthorized, err)
|
||||
} else if errors.Is(err, ErrIQNotFound) {
|
||||
return nil, nil
|
||||
return nil, wrapIQError(ErrProfilePictureNotSet, err)
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
picture, ok := resp.GetOptionalChildByTag("picture")
|
||||
if !ok {
|
||||
if existingID != "" {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, &ElementMissingError{Tag: "picture", In: "response to profile picture query"}
|
||||
}
|
||||
var info types.ProfilePictureInfo
|
||||
@@ -302,7 +332,7 @@ func parseVerifiedNameContent(verifiedNameNode waBinary.Node) (*types.VerifiedNa
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var certDetails waProto.VerifiedNameDetails
|
||||
var certDetails waProto.VerifiedNameCertificate_Details
|
||||
err = proto.Unmarshal(cert.GetDetails(), &certDetails)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -330,7 +360,7 @@ func parseDeviceList(user string, deviceNode waBinary.Node) []types.JID {
|
||||
return devices
|
||||
}
|
||||
|
||||
func (cli *Client) usync(jids []types.JID, mode, context string, query []waBinary.Node) (*waBinary.Node, error) {
|
||||
func (cli *Client) usync(ctx context.Context, jids []types.JID, mode, context string, query []waBinary.Node) (*waBinary.Node, error) {
|
||||
userList := make([]waBinary.Node, len(jids))
|
||||
for i, jid := range jids {
|
||||
userList[i].Tag = "user"
|
||||
@@ -350,6 +380,7 @@ func (cli *Client) usync(jids []types.JID, mode, context string, query []waBinar
|
||||
}
|
||||
}
|
||||
resp, err := cli.sendIQ(infoQuery{
|
||||
Context: ctx,
|
||||
Namespace: "usync",
|
||||
Type: "get",
|
||||
To: types.ServerJID,
|
||||
|
||||
Reference in New Issue
Block a user