2022-01-30 23:27:37 +00:00
|
|
|
// 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 whatsmeow
|
|
|
|
|
|
|
|
import (
|
2023-01-28 21:57:53 +00:00
|
|
|
"fmt"
|
2022-01-30 23:27:37 +00:00
|
|
|
|
|
|
|
waBinary "go.mau.fi/whatsmeow/binary"
|
|
|
|
"go.mau.fi/whatsmeow/types"
|
|
|
|
"go.mau.fi/whatsmeow/types/events"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (cli *Client) handleChatState(node *waBinary.Node) {
|
2022-06-24 22:36:16 +00:00
|
|
|
source, err := cli.parseMessageSource(node, true)
|
2022-01-30 23:27:37 +00:00
|
|
|
if err != nil {
|
|
|
|
cli.Log.Warnf("Failed to parse chat state update: %v", err)
|
|
|
|
} else if len(node.GetChildren()) != 1 {
|
|
|
|
cli.Log.Warnf("Failed to parse chat state update: unexpected number of children in element (%d)", len(node.GetChildren()))
|
|
|
|
} else {
|
|
|
|
child := node.GetChildren()[0]
|
|
|
|
presence := types.ChatPresence(child.Tag)
|
2022-03-12 22:02:04 +00:00
|
|
|
if presence != types.ChatPresenceComposing && presence != types.ChatPresencePaused {
|
2022-01-30 23:27:37 +00:00
|
|
|
cli.Log.Warnf("Unrecognized chat presence state %s", child.Tag)
|
|
|
|
}
|
2022-03-12 22:02:04 +00:00
|
|
|
media := types.ChatPresenceMedia(child.AttrGetter().OptionalString("media"))
|
2022-01-30 23:27:37 +00:00
|
|
|
cli.dispatchEvent(&events.ChatPresence{
|
|
|
|
MessageSource: source,
|
|
|
|
State: presence,
|
2022-03-12 22:02:04 +00:00
|
|
|
Media: media,
|
2022-01-30 23:27:37 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (cli *Client) handlePresence(node *waBinary.Node) {
|
|
|
|
var evt events.Presence
|
|
|
|
ag := node.AttrGetter()
|
|
|
|
evt.From = ag.JID("from")
|
|
|
|
presenceType := ag.OptionalString("type")
|
|
|
|
if presenceType == "unavailable" {
|
|
|
|
evt.Unavailable = true
|
|
|
|
} else if presenceType != "" {
|
|
|
|
cli.Log.Debugf("Unrecognized presence type '%s' in presence event from %s", presenceType, evt.From)
|
|
|
|
}
|
|
|
|
lastSeen := ag.OptionalString("last")
|
|
|
|
if lastSeen != "" && lastSeen != "deny" {
|
2022-06-11 21:07:42 +00:00
|
|
|
evt.LastSeen = ag.UnixTime("last")
|
2022-01-30 23:27:37 +00:00
|
|
|
}
|
|
|
|
if !ag.OK() {
|
|
|
|
cli.Log.Warnf("Error parsing presence event: %+v", ag.Errors)
|
|
|
|
} else {
|
|
|
|
cli.dispatchEvent(&evt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 (cli *Client) SendPresence(state types.Presence) error {
|
|
|
|
if len(cli.Store.PushName) == 0 {
|
|
|
|
return ErrNoPushName
|
|
|
|
}
|
2022-03-12 22:02:04 +00:00
|
|
|
if state == types.PresenceAvailable {
|
2024-05-23 21:44:31 +00:00
|
|
|
cli.sendActiveReceipts.CompareAndSwap(0, 1)
|
2022-03-12 22:02:04 +00:00
|
|
|
} else {
|
2024-05-23 21:44:31 +00:00
|
|
|
cli.sendActiveReceipts.CompareAndSwap(1, 0)
|
2022-03-12 22:02:04 +00:00
|
|
|
}
|
2022-01-30 23:27:37 +00:00
|
|
|
return cli.sendNode(waBinary.Node{
|
|
|
|
Tag: "presence",
|
|
|
|
Attrs: waBinary.Attrs{
|
|
|
|
"name": cli.Store.PushName,
|
|
|
|
"type": string(state),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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:
|
2022-11-26 23:42:16 +00:00
|
|
|
//
|
|
|
|
// cli.SendPresence(types.PresenceAvailable)
|
2022-01-30 23:27:37 +00:00
|
|
|
func (cli *Client) SubscribePresence(jid types.JID) error {
|
2023-01-28 21:57:53 +00:00
|
|
|
privacyToken, err := cli.Store.PrivacyTokens.GetPrivacyToken(jid)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("failed to get privacy token: %w", err)
|
|
|
|
} else if privacyToken == nil {
|
|
|
|
if cli.ErrorOnSubscribePresenceWithoutToken {
|
|
|
|
return fmt.Errorf("%w for %v", ErrNoPrivacyToken, jid.ToNonAD())
|
|
|
|
} else {
|
|
|
|
cli.Log.Debugf("Trying to subscribe to presence of %s without privacy token", jid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
req := waBinary.Node{
|
2022-01-30 23:27:37 +00:00
|
|
|
Tag: "presence",
|
|
|
|
Attrs: waBinary.Attrs{
|
|
|
|
"type": "subscribe",
|
|
|
|
"to": jid,
|
|
|
|
},
|
2023-01-28 21:57:53 +00:00
|
|
|
}
|
|
|
|
if privacyToken != nil {
|
|
|
|
req.Content = []waBinary.Node{{
|
|
|
|
Tag: "tctoken",
|
|
|
|
Content: privacyToken.Token,
|
|
|
|
}}
|
|
|
|
}
|
|
|
|
return cli.sendNode(req)
|
2022-01-30 23:27:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// SendChatPresence updates the user's typing status in a specific chat.
|
2022-03-12 22:02:04 +00:00
|
|
|
//
|
|
|
|
// The media parameter can be set to indicate the user is recording media (like a voice message) rather than typing a text message.
|
|
|
|
func (cli *Client) SendChatPresence(jid types.JID, state types.ChatPresence, media types.ChatPresenceMedia) error {
|
2023-01-28 21:57:53 +00:00
|
|
|
ownID := cli.getOwnID()
|
|
|
|
if ownID.IsEmpty() {
|
|
|
|
return ErrNotLoggedIn
|
|
|
|
}
|
2022-03-12 22:02:04 +00:00
|
|
|
content := []waBinary.Node{{Tag: string(state)}}
|
|
|
|
if state == types.ChatPresenceComposing && len(media) > 0 {
|
|
|
|
content[0].Attrs = waBinary.Attrs{
|
|
|
|
"media": string(media),
|
|
|
|
}
|
|
|
|
}
|
2022-01-30 23:27:37 +00:00
|
|
|
return cli.sendNode(waBinary.Node{
|
|
|
|
Tag: "chatstate",
|
|
|
|
Attrs: waBinary.Attrs{
|
2023-01-28 21:57:53 +00:00
|
|
|
"from": ownID,
|
2022-01-30 23:27:37 +00:00
|
|
|
"to": jid,
|
|
|
|
},
|
2022-03-12 22:02:04 +00:00
|
|
|
Content: content,
|
2022-01-30 23:27:37 +00:00
|
|
|
})
|
|
|
|
}
|