@areumtecnologia/wasockets 2.3.0 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -9
- package/lib/Client/Context/MessageContext.js +109 -42
- package/lib/Client/Normalizers/EventNormalizer.js +18 -1
- package/lib/Client/Normalizers/JidNormalizer.js +5 -2
- package/lib/Client/Normalizers/MessageNormalizer.js +311 -23
- package/lib/Client/index.d.ts +87 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -224,18 +224,33 @@ client.on(Events.LABELS_UPDATE, (labels) => { /* Criação ou associação de et
|
|
|
224
224
|
Ao escutar `Events.MESSAGE_RECEIVED`, você recebe um objeto `ctx` que já contém todos os dados mastigados e métodos de ação vinculados diretamente àquela mensagem:
|
|
225
225
|
|
|
226
226
|
#### Propriedades de `ctx`:
|
|
227
|
-
- `ctx.id`: ID único da mensagem.
|
|
227
|
+
- `ctx.id`: ID único da mensagem (se for mensagem editada, preserva o ID original rastreável).
|
|
228
228
|
- `ctx.chatId`: JID do chat (usuário, grupo ou canal).
|
|
229
|
-
- `ctx.
|
|
230
|
-
- `ctx.
|
|
231
|
-
- `ctx.
|
|
229
|
+
- `ctx.fromMe` / `ctx.isFromMe`: Booleano se a mensagem foi enviada pelo próprio bot.
|
|
230
|
+
- `ctx.from`: JID de origem (remetente em recebidas, ou JID do bot em enviadas).
|
|
231
|
+
- `ctx.to`: JID de destino (bot em recebidas, ou JID do chat/destinatário em enviadas).
|
|
232
|
+
- `ctx.fromPushName` / `ctx.senderName`: Nome de exibição formatado ou pushName ('Você' se `fromMe`).
|
|
233
|
+
- `ctx.fromNumber` / `ctx.senderNumber`: Apenas os números de telefone do remetente (sem sufixos ou `@s.whatsapp.net`).
|
|
234
|
+
- `ctx.sender`: JID de quem enviou a mensagem no chat.
|
|
232
235
|
- `ctx.isGroup`: Booleano indicando se é grupo (`@g.us`).
|
|
233
236
|
- `ctx.isChannel`: Booleano indicando se é newsletter/canal (`@newsletter`).
|
|
234
|
-
- `ctx.
|
|
235
|
-
- `ctx.body`: Texto principal da mensagem (já
|
|
236
|
-
- `ctx.type`: Tipo normalizado (`text`, `image`, `video`, `
|
|
237
|
-
- `ctx.
|
|
238
|
-
- `ctx.
|
|
237
|
+
- `ctx.isBroadcast`: Booleano se é lista de transmissão ou status.
|
|
238
|
+
- `ctx.body`: Texto principal da mensagem (já desempacotado recursivamente de `viewOnce`, `ephemeral`, botões, listas, etc.).
|
|
239
|
+
- `ctx.type`: Tipo normalizado e amigável (28 tipos identificados: `text`, `image`, `video`, `voice`, `document`, `sticker`, `native_flow_response`, `poll_creation`, `event`, etc.).
|
|
240
|
+
- `ctx.isEdited`: Booleano indicando se a mensagem é uma edição de mensagem prévia (`protocolMessage.editedMessage`).
|
|
241
|
+
- `ctx.editTimestamp`: Instância de `Date` informando quando a mensagem foi editada (ou `null`).
|
|
242
|
+
- `ctx.interactiveReply`: Resposta estruturada de botões, listas e fluxos nativos (`{ id, text, params, flowName, type }`).
|
|
243
|
+
- `ctx.contacts`: Array com contatos e vCards parseados (`[{ name, number, org, vcard }]`), com suporte a `waid` e fallback `TEL`.
|
|
244
|
+
- `ctx.location`: Coordenadas e endereço (`{ latitude, longitude, name, address, url, isLive }`).
|
|
245
|
+
- `ctx.reaction`: Detalhes de reação (`{ emoji, reactedMessageId, targetKey, isRemoval, senderTimestamp }`).
|
|
246
|
+
- `ctx.poll`: Dados completos de enquetes (`{ name, options, selectableOptionsCount, allowsMultipleAnswers }`).
|
|
247
|
+
- `ctx.mediaDetails`: Metadados da mídia (`{ mimetype, fileName, caption, duration, isPtt, isGif, isViewOnce }`).
|
|
248
|
+
- `ctx.media`: Instância de `MediaObject` para conversão rápida para buffer, base64, salvamento em disco ou Sharp.
|
|
249
|
+
- `ctx.isMentioningMe`: Booleano indicando se o bot foi mencionado diretamente no texto (suporta Phone JID e LID JID).
|
|
250
|
+
- `ctx.mentions` / `ctx.mentionedJids`: Array de JIDs mencionados na mensagem.
|
|
251
|
+
- `ctx.isForwarded` & `ctx.forwardingScore`: Rastreamento de encaminhamento.
|
|
252
|
+
- `ctx.isReply`: Booleano indicando se esta mensagem está citando outra.
|
|
253
|
+
- `ctx.quoted`: Instância recursiva de `MessageContext` da mensagem citada/respondida (com controle de profundidade).
|
|
239
254
|
- `ctx.raw`: O nó bruto original do WhatsApp/Baileys para casos de baixo nível.
|
|
240
255
|
|
|
241
256
|
#### Ações Disponíveis em `ctx`:
|
|
@@ -10,58 +10,125 @@ class MessageContext {
|
|
|
10
10
|
/**
|
|
11
11
|
* @param {import('../WaClient').WaClient} client
|
|
12
12
|
* @param {Object} rawMessage - proto.IWebMessageInfo
|
|
13
|
+
* @param {number} [depth=0] - Recursion depth guard for quoted messages
|
|
13
14
|
*/
|
|
14
|
-
constructor(client, rawMessage) {
|
|
15
|
+
constructor(client, rawMessage, depth = 0) {
|
|
15
16
|
this.client = client
|
|
16
|
-
this.raw = rawMessage
|
|
17
|
+
this.raw = rawMessage || {}
|
|
18
|
+
|
|
19
|
+
// 1. Detection of Edited Message (protocolMessage.editedMessage)
|
|
20
|
+
const editedMsg = rawMessage?.message?.protocolMessage?.editedMessage
|
|
21
|
+
this.isEdited = Boolean(editedMsg)
|
|
22
|
+
|
|
23
|
+
const effectiveKey = (this.isEdited && rawMessage.message.protocolMessage.key)
|
|
24
|
+
? { ...rawMessage.key, ...rawMessage.message.protocolMessage.key }
|
|
25
|
+
: (rawMessage?.key || {})
|
|
26
|
+
|
|
27
|
+
const effectiveMessage = editedMsg || rawMessage?.message || null
|
|
28
|
+
|
|
29
|
+
// 2. Identity and Routing
|
|
30
|
+
this.id = effectiveKey.id || rawMessage?.key?.id || ''
|
|
31
|
+
this.chatId = effectiveKey.remoteJid || rawMessage?.key?.remoteJid || ''
|
|
32
|
+
this.fromMe = Boolean(effectiveKey.fromMe)
|
|
33
|
+
this.isFromMe = this.fromMe
|
|
17
34
|
|
|
18
|
-
const key = rawMessage.key || {}
|
|
19
|
-
this.id = key.id || ''
|
|
20
|
-
this.chatId = key.remoteJid || ''
|
|
21
|
-
this.isFromMe = Boolean(key.fromMe)
|
|
22
35
|
this.isGroup = JidNormalizer.isGroup(this.chatId)
|
|
23
36
|
this.isChannel = JidNormalizer.isNewsletter(this.chatId)
|
|
24
37
|
this.isBroadcast = JidNormalizer.isBroadcast(this.chatId)
|
|
25
38
|
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
// Bot JID resolution
|
|
40
|
+
const sockUser = client?.sock?.user || {}
|
|
41
|
+
const clientJid = sockUser.id ? JidNormalizer.toJid(sockUser.id) : ''
|
|
42
|
+
const clientLid = sockUser.lid ? JidNormalizer.toJid(sockUser.lid) : ''
|
|
43
|
+
|
|
44
|
+
// Origin and Destination (from / to)
|
|
45
|
+
if (this.fromMe) {
|
|
46
|
+
this.from = clientJid || this.chatId
|
|
47
|
+
this.to = this.chatId
|
|
48
|
+
this.fromPushName = sockUser.name || sockUser.notify || 'Você'
|
|
49
|
+
} else {
|
|
50
|
+
this.from = this.isGroup
|
|
51
|
+
? (effectiveKey.participant || rawMessage?.participant || this.chatId)
|
|
52
|
+
: this.chatId
|
|
53
|
+
this.to = clientJid || this.chatId
|
|
54
|
+
this.fromPushName = rawMessage?.pushName || JidNormalizer.toPhoneNumber(this.from) || 'Desconhecido'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.sender = this.from
|
|
58
|
+
this.fromNumber = JidNormalizer.toPhoneNumber(this.from)
|
|
59
|
+
this.senderNumber = this.fromNumber
|
|
60
|
+
this.senderName = this.fromPushName
|
|
61
|
+
|
|
62
|
+
// Timestamps
|
|
63
|
+
const rawTs = Number(rawMessage?.messageTimestamp || Math.floor(Date.now() / 1000))
|
|
64
|
+
this.timestamp = new Date(rawTs > 10000000000 ? rawTs : rawTs * 1000)
|
|
65
|
+
this.editTimestamp = this.isEdited
|
|
66
|
+
? new Date(Number(rawMessage.message.protocolMessage.timestampMs || Date.now()))
|
|
67
|
+
: null
|
|
68
|
+
|
|
69
|
+
// 3. Content Decoupling & Typed Parsers
|
|
70
|
+
this.type = MessageNormalizer.extractType(effectiveMessage)
|
|
71
|
+
this.body = MessageNormalizer.extractBody(effectiveMessage)
|
|
72
|
+
this.interactiveReply = MessageNormalizer.extractInteractiveReply(effectiveMessage)
|
|
73
|
+
this.contacts = MessageNormalizer.extractContacts(effectiveMessage)
|
|
74
|
+
this.location = MessageNormalizer.extractLocation(effectiveMessage)
|
|
75
|
+
this.reaction = MessageNormalizer.extractReaction(effectiveMessage)
|
|
76
|
+
this.poll = MessageNormalizer.extractPollCreation(effectiveMessage, rawMessage)
|
|
77
|
+
|
|
78
|
+
// 4. Media Normalization
|
|
79
|
+
this.mediaNode = MessageNormalizer.extractMediaNode(effectiveMessage)
|
|
38
80
|
this.hasMedia = Boolean(this.mediaNode)
|
|
39
81
|
this.media = this.hasMedia ? new MediaObject(this.mediaNode, this.type) : null
|
|
40
|
-
this.
|
|
41
|
-
|
|
42
|
-
// Context
|
|
43
|
-
this.contextInfo = MessageNormalizer.extractContextInfo(
|
|
44
|
-
this.
|
|
82
|
+
this.mediaDetails = MessageNormalizer.extractMediaDetails(effectiveMessage)
|
|
83
|
+
|
|
84
|
+
// 5. Context Info, Mentions & Forwarding
|
|
85
|
+
this.contextInfo = MessageNormalizer.extractContextInfo(effectiveMessage)
|
|
86
|
+
this.mentions = this.contextInfo?.mentionedJid || []
|
|
87
|
+
this.mentionedJids = this.mentions
|
|
88
|
+
this.isForwarded = Boolean(this.contextInfo?.isForwarded)
|
|
89
|
+
this.forwardingScore = Number(this.contextInfo?.forwardingScore || 0)
|
|
90
|
+
|
|
91
|
+
// Checks if the bot was directly mentioned (by PN or LID)
|
|
92
|
+
this.isMentioningMe = Boolean(
|
|
93
|
+
(clientJid && this.mentions.includes(clientJid)) ||
|
|
94
|
+
(clientLid && this.mentions.includes(clientLid))
|
|
95
|
+
)
|
|
45
96
|
|
|
46
|
-
// Quoted
|
|
97
|
+
// 6. Quoted Message Parsing (Recursion with depth limit 1)
|
|
98
|
+
this.isReply = Boolean(this.contextInfo?.quotedMessage)
|
|
47
99
|
this.quoted = null
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
this.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
100
|
+
|
|
101
|
+
if (this.isReply && this.contextInfo?.quotedMessage && depth === 0) {
|
|
102
|
+
const quotedParticipant = this.contextInfo.participant || this.chatId
|
|
103
|
+
const quotedRaw = {
|
|
104
|
+
key: {
|
|
105
|
+
remoteJid: this.chatId,
|
|
106
|
+
id: this.contextInfo.stanzaId,
|
|
107
|
+
fromMe: clientJid && JidNormalizer.toJid(quotedParticipant) === clientJid,
|
|
108
|
+
participant: quotedParticipant
|
|
109
|
+
},
|
|
110
|
+
message: this.contextInfo.quotedMessage,
|
|
111
|
+
pushName: this.contextInfo.pushName
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
this.quoted = new MessageContext(this.client, quotedRaw, depth + 1)
|
|
115
|
+
} catch {
|
|
116
|
+
this.quoted = {
|
|
117
|
+
id: this.contextInfo.stanzaId,
|
|
118
|
+
sender: quotedParticipant,
|
|
119
|
+
senderNumber: JidNormalizer.toPhoneNumber(quotedParticipant),
|
|
120
|
+
type: MessageNormalizer.extractType(this.contextInfo.quotedMessage),
|
|
121
|
+
body: MessageNormalizer.extractBody(this.contextInfo.quotedMessage),
|
|
122
|
+
raw: this.contextInfo.quotedMessage
|
|
123
|
+
}
|
|
57
124
|
}
|
|
58
125
|
}
|
|
59
126
|
}
|
|
60
127
|
|
|
61
128
|
/**
|
|
62
129
|
* Responde citando esta mensagem automaticamente
|
|
63
|
-
* @param {string|Object} content - Texto ou
|
|
64
|
-
* @param {Object} [options={}]
|
|
130
|
+
* @param {string|Object} content - Texto ou payload de mensagem
|
|
131
|
+
* @param {Object} [options={}]
|
|
65
132
|
*/
|
|
66
133
|
async reply(content, options = {}) {
|
|
67
134
|
const payload = typeof content === 'string' ? { text: content } : content
|
|
@@ -72,8 +139,8 @@ class MessageContext {
|
|
|
72
139
|
}
|
|
73
140
|
|
|
74
141
|
/**
|
|
75
|
-
* Reage à mensagem com um emoji
|
|
76
|
-
* @param {string} emoji
|
|
142
|
+
* Reage à mensagem com um emoji (ou string vazia para remover)
|
|
143
|
+
* @param {string} emoji
|
|
77
144
|
*/
|
|
78
145
|
async react(emoji) {
|
|
79
146
|
return this.client.sendMessage(this.chatId, {
|
|
@@ -104,13 +171,13 @@ class MessageContext {
|
|
|
104
171
|
* Marca esta mensagem como lida (ticks azuis)
|
|
105
172
|
*/
|
|
106
173
|
async markRead() {
|
|
107
|
-
if (!this.client
|
|
174
|
+
if (!this.client?.sock?.readMessages) return
|
|
108
175
|
return this.client.sock.readMessages([this.raw.key])
|
|
109
176
|
}
|
|
110
177
|
|
|
111
178
|
/**
|
|
112
179
|
* Encaminha esta mensagem para outro destinatário
|
|
113
|
-
* @param {string} targetJid
|
|
180
|
+
* @param {string} targetJid
|
|
114
181
|
* @param {Object} [options={}]
|
|
115
182
|
*/
|
|
116
183
|
async forward(targetJid, options = {}) {
|
|
@@ -122,10 +189,10 @@ class MessageContext {
|
|
|
122
189
|
|
|
123
190
|
/**
|
|
124
191
|
* Fixa a mensagem no chat com duração configurável
|
|
125
|
-
* @param {number} [durationInSeconds=86400]
|
|
192
|
+
* @param {number} [durationInSeconds=86400]
|
|
126
193
|
*/
|
|
127
194
|
async pin(durationInSeconds = 86400) {
|
|
128
|
-
if (!this.client
|
|
195
|
+
if (!this.client?.sock?.pinInChatMessage) {
|
|
129
196
|
throw new Error('Método pinInChatMessage indisponível no socket.')
|
|
130
197
|
}
|
|
131
198
|
return this.client.sock.pinInChatMessage(this.chatId, this.raw.key, 1, durationInSeconds)
|
|
@@ -135,7 +202,7 @@ class MessageContext {
|
|
|
135
202
|
* Desafixa a mensagem do topo da conversa
|
|
136
203
|
*/
|
|
137
204
|
async unpin() {
|
|
138
|
-
if (!this.client
|
|
205
|
+
if (!this.client?.sock?.pinInChatMessage) {
|
|
139
206
|
throw new Error('Método pinInChatMessage indisponível no socket.')
|
|
140
207
|
}
|
|
141
208
|
return this.client.sock.pinInChatMessage(this.chatId, this.raw.key, 2)
|
|
@@ -20,9 +20,17 @@ class EventNormalizer {
|
|
|
20
20
|
if (!Array.isArray(messages)) return
|
|
21
21
|
|
|
22
22
|
for (const rawMsg of messages) {
|
|
23
|
-
// Ignore stub or protocol messages if empty
|
|
23
|
+
// Ignore stub or protocol messages if completely empty
|
|
24
24
|
if (!rawMsg.message && !rawMsg.messageStubType) continue
|
|
25
25
|
|
|
26
|
+
// Handle protocol revoke messages explicitly
|
|
27
|
+
const isRevoke = rawMsg.message?.protocolMessage?.type === 0
|
|
28
|
+
if (isRevoke) {
|
|
29
|
+
client.emit(Events.MESSAGE_DELETE, {
|
|
30
|
+
keys: [rawMsg.message.protocolMessage.key || rawMsg.key]
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
26
34
|
const ctx = new MessageContext(client, rawMsg)
|
|
27
35
|
|
|
28
36
|
if (ctx.isFromMe) {
|
|
@@ -30,6 +38,15 @@ class EventNormalizer {
|
|
|
30
38
|
} else {
|
|
31
39
|
client.emit(Events.MESSAGE_RECEIVED, ctx, rawMsg)
|
|
32
40
|
}
|
|
41
|
+
|
|
42
|
+
// If message is an edit, also notify on MESSAGE_UPDATE
|
|
43
|
+
if (ctx.isEdited) {
|
|
44
|
+
client.emit(Events.MESSAGE_UPDATE, [{
|
|
45
|
+
key: ctx.raw.key,
|
|
46
|
+
update: { message: ctx.raw.message },
|
|
47
|
+
ctx
|
|
48
|
+
}])
|
|
49
|
+
}
|
|
33
50
|
}
|
|
34
51
|
})
|
|
35
52
|
|
|
@@ -4,7 +4,8 @@ Object.defineProperty(exports, "__esModule", { value: true })
|
|
|
4
4
|
|
|
5
5
|
class JidNormalizer {
|
|
6
6
|
/**
|
|
7
|
-
* Sanitizes and normalizes any phone number or JID string into a canonical JID
|
|
7
|
+
* Sanitizes and normalizes any phone number or JID string into a canonical JID,
|
|
8
|
+
* stripping device specifiers (e.g., ":1@s.whatsapp.net" -> "@s.whatsapp.net")
|
|
8
9
|
* @param {string} input - Phone number (e.g., "+55 (11) 99999-9999") or existing JID
|
|
9
10
|
* @returns {string} Normalized JID (e.g., "5511999999999@s.whatsapp.net")
|
|
10
11
|
*/
|
|
@@ -13,7 +14,9 @@ class JidNormalizer {
|
|
|
13
14
|
const cleaned = input.trim()
|
|
14
15
|
|
|
15
16
|
if (cleaned.includes('@')) {
|
|
16
|
-
|
|
17
|
+
const [userPart, server] = cleaned.split('@')
|
|
18
|
+
const cleanUser = userPart.split(':')[0]
|
|
19
|
+
return `${cleanUser}@${server}`
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
const digits = cleaned.replace(/\D/g, '')
|
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true })
|
|
|
4
4
|
|
|
5
5
|
class MessageNormalizer {
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Recursively unwraps all container wrappers (ephemeral, viewOnce, associatedChild, etc.)
|
|
8
8
|
* @param {Object} rawMessageContent - raw message node from proto
|
|
9
9
|
* @returns {Object} Unwrapped message object
|
|
10
10
|
*/
|
|
@@ -12,81 +12,347 @@ class MessageNormalizer {
|
|
|
12
12
|
if (!rawMessageContent) return null
|
|
13
13
|
|
|
14
14
|
let msg = rawMessageContent
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
msg.
|
|
28
|
-
|
|
15
|
+
let unwrapped = true
|
|
16
|
+
|
|
17
|
+
// Loop to peel away multiple layers of wrappers if nested
|
|
18
|
+
while (unwrapped && msg) {
|
|
19
|
+
unwrapped = false
|
|
20
|
+
|
|
21
|
+
if (msg.ephemeralMessage?.message) {
|
|
22
|
+
msg = msg.ephemeralMessage.message
|
|
23
|
+
unwrapped = true
|
|
24
|
+
continue
|
|
25
|
+
}
|
|
26
|
+
if (msg.viewOnceMessage?.message) {
|
|
27
|
+
msg = msg.viewOnceMessage.message
|
|
28
|
+
unwrapped = true
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
if (msg.viewOnceMessageV2?.message) {
|
|
32
|
+
msg = msg.viewOnceMessageV2.message
|
|
33
|
+
unwrapped = true
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
if (msg.viewOnceMessageV2Extension?.message) {
|
|
37
|
+
msg = msg.viewOnceMessageV2Extension.message
|
|
38
|
+
unwrapped = true
|
|
39
|
+
continue
|
|
40
|
+
}
|
|
41
|
+
if (msg.documentWithCaptionMessage?.message) {
|
|
42
|
+
msg = msg.documentWithCaptionMessage.message
|
|
43
|
+
unwrapped = true
|
|
44
|
+
continue
|
|
45
|
+
}
|
|
46
|
+
if (msg.associatedChildMessage?.message) {
|
|
47
|
+
msg = msg.associatedChildMessage.message
|
|
48
|
+
unwrapped = true
|
|
49
|
+
continue
|
|
50
|
+
}
|
|
29
51
|
}
|
|
30
52
|
|
|
31
53
|
return msg
|
|
32
54
|
}
|
|
33
55
|
|
|
34
56
|
/**
|
|
35
|
-
* Identifies the primary content type of the message
|
|
36
|
-
* @param {Object} messageContent -
|
|
37
|
-
* @returns {string} Normalized type
|
|
57
|
+
* Identifies the primary friendly content type of the message (28 distinct types)
|
|
58
|
+
* @param {Object} messageContent - Raw or unwrapped message content
|
|
59
|
+
* @returns {string} Normalized friendly type
|
|
38
60
|
*/
|
|
39
61
|
static extractType(messageContent) {
|
|
40
62
|
const msg = this.unwrapMessage(messageContent)
|
|
41
63
|
if (!msg) return 'unknown'
|
|
42
64
|
|
|
65
|
+
// 1. Text & Media Basics
|
|
43
66
|
if (msg.conversation || msg.extendedTextMessage) return 'text'
|
|
44
67
|
if (msg.imageMessage) return 'image'
|
|
68
|
+
if (msg.albumMessage) return 'album'
|
|
45
69
|
if (msg.videoMessage) return msg.videoMessage.gifPlayback ? 'gif' : 'video'
|
|
46
70
|
if (msg.audioMessage) return msg.audioMessage.ptt ? 'voice' : 'audio'
|
|
47
71
|
if (msg.documentMessage) return 'document'
|
|
48
72
|
if (msg.stickerMessage) return 'sticker'
|
|
73
|
+
if (msg.locationMessage) return 'location'
|
|
74
|
+
if (msg.liveLocationMessage) return 'live_location'
|
|
49
75
|
if (msg.contactMessage || msg.contactsArrayMessage) return 'contact'
|
|
50
|
-
|
|
51
|
-
|
|
76
|
+
|
|
77
|
+
// 2. Polls & Events
|
|
78
|
+
if (msg.pollCreationMessage || msg.pollCreationMessageV2 || msg.pollCreationMessageV3) return 'poll_creation'
|
|
79
|
+
if (msg.pollUpdateMessage) return 'poll_update'
|
|
80
|
+
if (msg.eventMessage) return 'event'
|
|
81
|
+
if (msg.eventResponseMessage || msg.encEventResponseMessage) return 'event_response'
|
|
82
|
+
|
|
83
|
+
// 3. Reactions & Pins
|
|
52
84
|
if (msg.reactionMessage) return 'reaction'
|
|
53
|
-
if (msg.
|
|
54
|
-
if (msg.
|
|
85
|
+
if (msg.pinInChatMessage) return 'pin_in_chat'
|
|
86
|
+
if (msg.unpinInChatMessage) return 'unpin_in_chat'
|
|
87
|
+
|
|
88
|
+
// 4. Replies from Buttons, Lists & Questions
|
|
89
|
+
if (msg.buttonsResponseMessage) return 'button_reply'
|
|
55
90
|
if (msg.listResponseMessage) return 'list_reply'
|
|
91
|
+
if (msg.templateButtonReplyMessage) return 'template_button_reply'
|
|
92
|
+
if (msg.questionReplyMessage) return 'question_reply'
|
|
93
|
+
|
|
94
|
+
// 5. Specific Interactive Flow Buttons
|
|
95
|
+
const firstBtnName = msg.interactiveMessage?.interactiveButtons?.[0]?.name
|
|
96
|
+
if (firstBtnName) {
|
|
97
|
+
switch (firstBtnName) {
|
|
98
|
+
case 'review_and_pay': return 'review_and_pay'
|
|
99
|
+
case 'review_order': return 'review_order'
|
|
100
|
+
case 'quick_reply': return 'quick_reply'
|
|
101
|
+
case 'cta_url': return 'cta_url'
|
|
102
|
+
case 'cta_copy': return 'cta_copy'
|
|
103
|
+
case 'cta_call': return 'cta_call'
|
|
104
|
+
case 'voice_call': return 'voice_call'
|
|
105
|
+
case 'payment_info': return 'payment_info'
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 6. Native Flow Responses
|
|
110
|
+
if (msg.interactiveResponseMessage?.nativeFlowResponseMessage) {
|
|
111
|
+
const flowName = msg.interactiveResponseMessage.nativeFlowResponseMessage.name
|
|
112
|
+
if (flowName === 'menu_options') return 'native_flow_menu_options_response'
|
|
113
|
+
return 'native_flow_response'
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 7. Interactive Containers
|
|
117
|
+
if (msg.interactiveMessage?.interactiveButtons) return 'interactive_buttons'
|
|
118
|
+
if (msg.interactiveMessage?.interactiveList) return 'interactive_list'
|
|
119
|
+
if (msg.interactiveResponseMessage) return 'interactive_response'
|
|
120
|
+
if (msg.interactiveMessage) return 'interactive_message'
|
|
121
|
+
|
|
122
|
+
// 8. Protocol
|
|
56
123
|
if (msg.protocolMessage) return 'protocol'
|
|
57
124
|
|
|
125
|
+
// Fallback to first non-metadata proto key
|
|
58
126
|
const keys = Object.keys(msg).filter(k => k !== 'messageContextInfo' && k !== 'senderKeyDistributionMessage')
|
|
59
127
|
return keys[0] ? keys[0].replace('Message', '').toLowerCase() : 'unknown'
|
|
60
128
|
}
|
|
61
129
|
|
|
62
130
|
/**
|
|
63
131
|
* Extracts readable text content across diverse message structures
|
|
64
|
-
* @param {Object} messageContent -
|
|
132
|
+
* @param {Object} messageContent - Raw or unwrapped message content
|
|
65
133
|
* @returns {string} Text body
|
|
66
134
|
*/
|
|
67
135
|
static extractBody(messageContent) {
|
|
68
136
|
const msg = this.unwrapMessage(messageContent)
|
|
69
137
|
if (!msg) return ''
|
|
70
138
|
|
|
139
|
+
// Direct Text
|
|
71
140
|
if (msg.conversation) return msg.conversation
|
|
72
141
|
if (msg.extendedTextMessage?.text) return msg.extendedTextMessage.text
|
|
142
|
+
|
|
143
|
+
// Media Captions
|
|
73
144
|
if (msg.imageMessage?.caption) return msg.imageMessage.caption
|
|
74
145
|
if (msg.videoMessage?.caption) return msg.videoMessage.caption
|
|
75
146
|
if (msg.documentMessage?.caption) return msg.documentMessage.caption
|
|
147
|
+
|
|
148
|
+
// Button / List selections
|
|
76
149
|
if (msg.buttonsResponseMessage?.selectedDisplayText) return msg.buttonsResponseMessage.selectedDisplayText
|
|
150
|
+
if (msg.buttonsResponseMessage?.selectedButtonId) return msg.buttonsResponseMessage.selectedButtonId
|
|
77
151
|
if (msg.listResponseMessage?.title) return msg.listResponseMessage.title
|
|
152
|
+
if (msg.templateButtonReplyMessage?.selectedDisplayText) return msg.templateButtonReplyMessage.selectedDisplayText
|
|
78
153
|
if (msg.templateButtonReplyMessage?.selectedId) return msg.templateButtonReplyMessage.selectedId
|
|
154
|
+
|
|
155
|
+
// Interactive / Native Flows
|
|
156
|
+
if (msg.interactiveResponseMessage?.body?.text) return msg.interactiveResponseMessage.body.text
|
|
79
157
|
if (msg.interactiveResponseMessage?.nativeFlowResponseMessage?.paramsJson) {
|
|
80
158
|
return msg.interactiveResponseMessage.nativeFlowResponseMessage.paramsJson
|
|
81
159
|
}
|
|
160
|
+
if (msg.interactiveMessage?.body?.text) return msg.interactiveMessage.body.text
|
|
161
|
+
|
|
162
|
+
// Polls, Reactions, Questions & Locations
|
|
82
163
|
if (msg.pollCreationMessage?.name) return msg.pollCreationMessage.name
|
|
83
164
|
if (msg.pollCreationMessageV2?.name) return msg.pollCreationMessageV2.name
|
|
84
165
|
if (msg.pollCreationMessageV3?.name) return msg.pollCreationMessageV3.name
|
|
85
166
|
if (msg.reactionMessage?.text) return msg.reactionMessage.text
|
|
167
|
+
if (msg.questionReplyMessage?.message?.extendedTextMessage?.text) return msg.questionReplyMessage.message.extendedTextMessage.text
|
|
168
|
+
if (msg.questionReplyMessage?.answer) return msg.questionReplyMessage.answer
|
|
169
|
+
if (msg.locationMessage?.name || msg.locationMessage?.address) {
|
|
170
|
+
return [msg.locationMessage.name, msg.locationMessage.address].filter(Boolean).join(' - ')
|
|
171
|
+
}
|
|
86
172
|
|
|
87
173
|
return ''
|
|
88
174
|
}
|
|
89
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Extracts and safely parses interactive button/list responses
|
|
178
|
+
* @param {Object} messageContent - Unwrapped message content
|
|
179
|
+
* @returns {Object|null} { id, text, params, flowName, type }
|
|
180
|
+
*/
|
|
181
|
+
static extractInteractiveReply(messageContent) {
|
|
182
|
+
const msg = this.unwrapMessage(messageContent)
|
|
183
|
+
if (!msg) return null
|
|
184
|
+
|
|
185
|
+
// 1. Classic Button Response
|
|
186
|
+
if (msg.buttonsResponseMessage) {
|
|
187
|
+
return {
|
|
188
|
+
id: msg.buttonsResponseMessage.selectedButtonId || '',
|
|
189
|
+
text: msg.buttonsResponseMessage.selectedDisplayText || '',
|
|
190
|
+
type: 'button'
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 2. List Dropdown Selection
|
|
195
|
+
if (msg.listResponseMessage) {
|
|
196
|
+
const row = msg.listResponseMessage.singleSelectReply
|
|
197
|
+
return {
|
|
198
|
+
id: row?.selectedRowId || '',
|
|
199
|
+
text: msg.listResponseMessage.title || '',
|
|
200
|
+
description: row?.description || '',
|
|
201
|
+
type: 'list'
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 3. Native Flow Interactive Response
|
|
206
|
+
if (msg.interactiveResponseMessage?.nativeFlowResponseMessage) {
|
|
207
|
+
const flow = msg.interactiveResponseMessage.nativeFlowResponseMessage
|
|
208
|
+
let params = {}
|
|
209
|
+
if (flow.paramsJson) {
|
|
210
|
+
try {
|
|
211
|
+
params = JSON.parse(flow.paramsJson)
|
|
212
|
+
} catch {
|
|
213
|
+
params = { raw: flow.paramsJson }
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const id = params?.id || params?.selected_id || params?.row_id || flow.name || ''
|
|
218
|
+
return {
|
|
219
|
+
id,
|
|
220
|
+
text: flow.paramsJson || '',
|
|
221
|
+
params,
|
|
222
|
+
flowName: flow.name || '',
|
|
223
|
+
type: 'native_flow'
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 4. Template Button Reply
|
|
228
|
+
if (msg.templateButtonReplyMessage) {
|
|
229
|
+
return {
|
|
230
|
+
id: msg.templateButtonReplyMessage.selectedId || '',
|
|
231
|
+
text: msg.templateButtonReplyMessage.selectedDisplayText || '',
|
|
232
|
+
type: 'template_button'
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// 5. Question Reply (Channels)
|
|
237
|
+
if (msg.questionReplyMessage) {
|
|
238
|
+
return {
|
|
239
|
+
id: msg.questionReplyMessage.selectedButtonId || '',
|
|
240
|
+
text: msg.questionReplyMessage.selectedDisplayText || msg.questionReplyMessage.answer || '',
|
|
241
|
+
type: 'question_reply'
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return null
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Extracts and parses contacts from vCards
|
|
250
|
+
* @param {Object} messageContent - Unwrapped message content
|
|
251
|
+
* @returns {Array<{ name: string, number: string, org?: string, vcard: string }>}
|
|
252
|
+
*/
|
|
253
|
+
static extractContacts(messageContent) {
|
|
254
|
+
const msg = this.unwrapMessage(messageContent)
|
|
255
|
+
if (!msg) return []
|
|
256
|
+
|
|
257
|
+
const parseVcard = (vcard = '') => {
|
|
258
|
+
const nameMatch = vcard.match(/FN:(.+)/i)
|
|
259
|
+
// Tenta waid= primeiro, se não encontrar pega TEL
|
|
260
|
+
const waidMatch = vcard.match(/waid=(\d+)/i)
|
|
261
|
+
const telMatch = vcard.match(/TEL[^:]*:([+\d\s().-]+)/i)
|
|
262
|
+
const orgMatch = vcard.match(/ORG:([^;\n\r]+)/i)
|
|
263
|
+
|
|
264
|
+
let number = ''
|
|
265
|
+
if (waidMatch) {
|
|
266
|
+
number = waidMatch[1].replace(/\D/g, '')
|
|
267
|
+
} else if (telMatch) {
|
|
268
|
+
number = telMatch[1].replace(/\D/g, '')
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
name: (nameMatch ? nameMatch[1] : '').replace(/\\/g, '').trim(),
|
|
273
|
+
number,
|
|
274
|
+
org: orgMatch ? orgMatch[1].trim() : undefined,
|
|
275
|
+
vcard
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (msg.contactMessage) {
|
|
280
|
+
return [parseVcard(msg.contactMessage.vcard)]
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (msg.contactsArrayMessage?.contacts) {
|
|
284
|
+
return msg.contactsArrayMessage.contacts.map(c => parseVcard(c.vcard))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return []
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Extracts coordinates and metadata from location messages
|
|
292
|
+
* @param {Object} messageContent - Unwrapped message content
|
|
293
|
+
* @returns {Object|null}
|
|
294
|
+
*/
|
|
295
|
+
static extractLocation(messageContent) {
|
|
296
|
+
const msg = this.unwrapMessage(messageContent)
|
|
297
|
+
if (!msg) return null
|
|
298
|
+
|
|
299
|
+
const loc = msg.locationMessage || msg.liveLocationMessage
|
|
300
|
+
if (!loc) return null
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
latitude: Number(loc.degreesLatitude || 0),
|
|
304
|
+
longitude: Number(loc.degreesLongitude || 0),
|
|
305
|
+
name: loc.name || '',
|
|
306
|
+
address: loc.address || '',
|
|
307
|
+
url: loc.url || '',
|
|
308
|
+
accuracyInMeters: loc.accuracyInMeters ? Number(loc.accuracyInMeters) : undefined,
|
|
309
|
+
speedInMps: loc.speedInMps ? Number(loc.speedInMps) : undefined,
|
|
310
|
+
isLive: Boolean(msg.liveLocationMessage)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Extracts reaction details
|
|
316
|
+
* @param {Object} messageContent - Unwrapped message content
|
|
317
|
+
* @returns {Object|null}
|
|
318
|
+
*/
|
|
319
|
+
static extractReaction(messageContent) {
|
|
320
|
+
const msg = this.unwrapMessage(messageContent)
|
|
321
|
+
if (!msg?.reactionMessage) return null
|
|
322
|
+
|
|
323
|
+
const reaction = msg.reactionMessage
|
|
324
|
+
return {
|
|
325
|
+
emoji: reaction.text || '',
|
|
326
|
+
reactedMessageId: reaction.key?.id || '',
|
|
327
|
+
targetKey: reaction.key || null,
|
|
328
|
+
isRemoval: !reaction.text,
|
|
329
|
+
senderTimestamp: reaction.senderTimestampMs ? new Date(Number(reaction.senderTimestampMs)) : new Date()
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Extracts poll creation structure
|
|
335
|
+
* @param {Object} messageContent - Unwrapped message content
|
|
336
|
+
* @param {Object} [rawMessage]
|
|
337
|
+
* @returns {Object|null}
|
|
338
|
+
*/
|
|
339
|
+
static extractPollCreation(messageContent, rawMessage) {
|
|
340
|
+
const msg = this.unwrapMessage(messageContent)
|
|
341
|
+
const poll = msg?.pollCreationMessage || msg?.pollCreationMessageV2 || msg?.pollCreationMessageV3
|
|
342
|
+
if (!poll) return null
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
id: rawMessage?.key?.id || '',
|
|
346
|
+
name: poll.name || '',
|
|
347
|
+
selectableOptionsCount: Number(poll.selectableOptionsCount || 1),
|
|
348
|
+
allowsMultipleAnswers: Number(poll.selectableOptionsCount || 1) > 1,
|
|
349
|
+
options: (poll.options || []).map(opt => ({
|
|
350
|
+
name: opt.optionName || '',
|
|
351
|
+
hash: opt.optionHash ? Buffer.from(opt.optionHash).toString('hex') : null
|
|
352
|
+
}))
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
90
356
|
/**
|
|
91
357
|
* Extracts media node if present
|
|
92
358
|
* @param {Object} messageContent - Unwrapped message content
|
|
@@ -106,6 +372,26 @@ class MessageNormalizer {
|
|
|
106
372
|
)
|
|
107
373
|
}
|
|
108
374
|
|
|
375
|
+
/**
|
|
376
|
+
* Extracts structured media details
|
|
377
|
+
* @param {Object} messageContent - Unwrapped message content
|
|
378
|
+
* @returns {Object|null}
|
|
379
|
+
*/
|
|
380
|
+
static extractMediaDetails(messageContent) {
|
|
381
|
+
const node = this.extractMediaNode(messageContent)
|
|
382
|
+
if (!node) return null
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
mimetype: node.mimetype || '',
|
|
386
|
+
fileName: node.fileName || '',
|
|
387
|
+
caption: node.caption || '',
|
|
388
|
+
duration: node.seconds ? Number(node.seconds) : null,
|
|
389
|
+
isPtt: Boolean(node.ptt),
|
|
390
|
+
isGif: Boolean(node.gifPlayback),
|
|
391
|
+
isViewOnce: Boolean(node.viewOnce)
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
109
395
|
/**
|
|
110
396
|
* Extracts quoted message context info
|
|
111
397
|
* @param {Object} messageContent - Unwrapped message content
|
|
@@ -125,6 +411,8 @@ class MessageNormalizer {
|
|
|
125
411
|
msg.buttonsResponseMessage?.contextInfo ||
|
|
126
412
|
msg.listResponseMessage?.contextInfo ||
|
|
127
413
|
msg.interactiveResponseMessage?.contextInfo ||
|
|
414
|
+
msg.templateButtonReplyMessage?.contextInfo ||
|
|
415
|
+
msg.messageContextInfo ||
|
|
128
416
|
null
|
|
129
417
|
)
|
|
130
418
|
}
|
package/lib/Client/index.d.ts
CHANGED
|
@@ -92,13 +92,66 @@ export declare class CallContext {
|
|
|
92
92
|
playAudio(audioPathOrBuffer: string | Buffer): Promise<any>
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
export interface InteractiveReplyData {
|
|
96
|
+
id: string
|
|
97
|
+
text: string
|
|
98
|
+
params?: any
|
|
99
|
+
flowName?: string
|
|
100
|
+
description?: string
|
|
101
|
+
type: 'button' | 'list' | 'native_flow' | 'template_button' | 'question_reply' | string
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ContactData {
|
|
105
|
+
name: string
|
|
106
|
+
number: string
|
|
107
|
+
org?: string
|
|
108
|
+
vcard: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface LocationData {
|
|
112
|
+
latitude: number
|
|
113
|
+
longitude: number
|
|
114
|
+
name: string
|
|
115
|
+
address: string
|
|
116
|
+
url: string
|
|
117
|
+
accuracyInMeters?: number
|
|
118
|
+
speedInMps?: number
|
|
119
|
+
isLive: boolean
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface ReactionData {
|
|
123
|
+
emoji: string
|
|
124
|
+
reactedMessageId: string
|
|
125
|
+
targetKey: any
|
|
126
|
+
isRemoval: boolean
|
|
127
|
+
senderTimestamp: Date
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export interface PollData {
|
|
131
|
+
id: string
|
|
132
|
+
name: string
|
|
133
|
+
selectableOptionsCount: number
|
|
134
|
+
allowsMultipleAnswers: boolean
|
|
135
|
+
options: Array<{ name: string; hash: string | null }>
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface MediaDetailsData {
|
|
139
|
+
mimetype: string
|
|
140
|
+
fileName: string
|
|
141
|
+
caption: string
|
|
142
|
+
duration: number | null
|
|
143
|
+
isPtt: boolean
|
|
144
|
+
isGif: boolean
|
|
145
|
+
isViewOnce: boolean
|
|
146
|
+
}
|
|
147
|
+
|
|
95
148
|
export interface QuotedMessageContext {
|
|
96
149
|
id: string
|
|
97
150
|
sender: string
|
|
98
151
|
senderNumber: string
|
|
99
152
|
type: string
|
|
100
153
|
body: string
|
|
101
|
-
raw: proto.IMessage
|
|
154
|
+
raw: proto.IMessage | any
|
|
102
155
|
}
|
|
103
156
|
|
|
104
157
|
export declare class MessageContext {
|
|
@@ -106,24 +159,48 @@ export declare class MessageContext {
|
|
|
106
159
|
raw: proto.IWebMessageInfo
|
|
107
160
|
id: string
|
|
108
161
|
chatId: string
|
|
162
|
+
fromMe: boolean
|
|
109
163
|
isFromMe: boolean
|
|
110
164
|
isGroup: boolean
|
|
111
165
|
isChannel: boolean
|
|
112
166
|
isBroadcast: boolean
|
|
167
|
+
|
|
168
|
+
from: string
|
|
169
|
+
to: string
|
|
170
|
+
fromPushName: string
|
|
171
|
+
fromNumber: string
|
|
113
172
|
sender: string
|
|
114
173
|
senderNumber: string
|
|
115
174
|
senderName: string
|
|
175
|
+
|
|
176
|
+
timestamp: Date
|
|
177
|
+
isEdited: boolean
|
|
178
|
+
editTimestamp: Date | null
|
|
179
|
+
|
|
116
180
|
type: string
|
|
117
181
|
body: string
|
|
182
|
+
interactiveReply: InteractiveReplyData | null
|
|
183
|
+
contacts: ContactData[]
|
|
184
|
+
location: LocationData | null
|
|
185
|
+
reaction: ReactionData | null
|
|
186
|
+
poll: PollData | null
|
|
187
|
+
|
|
118
188
|
mediaNode: any | null
|
|
119
189
|
hasMedia: boolean
|
|
120
190
|
media: MediaObject | null
|
|
121
|
-
|
|
191
|
+
mediaDetails: MediaDetailsData | null
|
|
192
|
+
|
|
122
193
|
contextInfo: proto.IContextInfo | null
|
|
194
|
+
mentions: string[]
|
|
123
195
|
mentionedJids: string[]
|
|
124
|
-
|
|
196
|
+
isForwarded: boolean
|
|
197
|
+
forwardingScore: number
|
|
198
|
+
isMentioningMe: boolean
|
|
199
|
+
|
|
200
|
+
isReply: boolean
|
|
201
|
+
quoted: MessageContext | QuotedMessageContext | null
|
|
125
202
|
|
|
126
|
-
constructor(client: WaClient, rawMessage: proto.IWebMessageInfo)
|
|
203
|
+
constructor(client: WaClient, rawMessage: proto.IWebMessageInfo, depth?: number)
|
|
127
204
|
|
|
128
205
|
reply(content: string | AnyMessageContent, options?: MiscMessageGenerationOptions): Promise<proto.WebMessageInfo | undefined>
|
|
129
206
|
react(emoji: string): Promise<proto.WebMessageInfo | undefined>
|
|
@@ -397,7 +474,13 @@ export declare class MessageNormalizer {
|
|
|
397
474
|
static unwrapMessage(rawMessageContent: any): any
|
|
398
475
|
static extractType(messageContent: any): string
|
|
399
476
|
static extractBody(messageContent: any): string
|
|
477
|
+
static extractInteractiveReply(messageContent: any): InteractiveReplyData | null
|
|
478
|
+
static extractContacts(messageContent: any): ContactData[]
|
|
479
|
+
static extractLocation(messageContent: any): LocationData | null
|
|
480
|
+
static extractReaction(messageContent: any): ReactionData | null
|
|
481
|
+
static extractPollCreation(messageContent: any, rawMessage?: any): PollData | null
|
|
400
482
|
static extractMediaNode(messageContent: any): any | null
|
|
483
|
+
static extractMediaDetails(messageContent: any): MediaDetailsData | null
|
|
401
484
|
static extractContextInfo(messageContent: any): proto.IContextInfo | null
|
|
402
485
|
}
|
|
403
486
|
|