@systemzero/baileys 1.0.6 → 1.0.7
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 +371 -305
- package/lib/Defaults/index.js +2 -2
- package/lib/Socket/messages-recv.js +9 -4
- package/lib/Socket/socket.js +4 -4
- package/lib/Utils/generics.js +2 -1
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/messages.js +1 -1
- package/lib/Utils/scheduling.js +138 -0
- package/lib/Utils/validate-connection.js +11 -5
- package/package.json +2 -3
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# @systemzero/baileys
|
|
6
6
|
|
|
7
|
-
**v1.0.
|
|
7
|
+
**v1.0.7** · Fork avançado do Baileys com suporte nativo a todos os tipos de mensagens do WhatsApp
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/@systemzero/baileys)
|
|
10
10
|
[](LICENSE)
|
|
@@ -27,7 +27,7 @@ npm i sharp # conversão de imagem para sticker pack
|
|
|
27
27
|
npm i @napi-rs/image # alternativa ao sharp
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
Requer `ffmpeg` instalado no sistema (com suporte a `libopus`) para conversão automática de áudio PTT
|
|
30
|
+
Requer `ffmpeg` instalado no sistema (com suporte a `libopus`) para conversão automática de áudio PTT. Confirme com `ffmpeg -encoders | grep opus`.
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
@@ -44,19 +44,21 @@ Requer `ffmpeg` instalado no sistema (com suporte a `libopus`) para conversão a
|
|
|
44
44
|
9. [LID / JID — Sistema avançado](#9-lid--jid--sistema-avançado)
|
|
45
45
|
10. [Username (@usuario)](#10-username-usuario)
|
|
46
46
|
11. [MessageBuilder — Button, ButtonV2, Carousel, AIRich](#11-messagebuilder--button-buttonv2-carousel-airich)
|
|
47
|
-
12. [Botões estendidos
|
|
47
|
+
12. [Botões estendidos](#12-botões-estendidos)
|
|
48
48
|
13. [Grupos](#13-grupos)
|
|
49
49
|
14. [Perfil e privacidade](#14-perfil-e-privacidade)
|
|
50
50
|
15. [Eventos do socket](#15-eventos-do-socket)
|
|
51
51
|
16. [Utilitários](#16-utilitários)
|
|
52
52
|
17. [Fixar / Desfixar mensagens](#17-fixar--desfixar-mensagens)
|
|
53
53
|
18. [Resolução de nomes (getName)](#18-resolução-de-nomes-getname)
|
|
54
|
-
19. [PTT real com qualquer formato
|
|
55
|
-
20. [Guard de pagamento stealth
|
|
56
|
-
21. [Bad MAC Handler
|
|
54
|
+
19. [PTT real com qualquer formato](#19-ptt-real-com-qualquer-formato)
|
|
55
|
+
20. [Guard de pagamento stealth](#20-guard-de-pagamento-stealth-anti-fraude)
|
|
56
|
+
21. [Bad MAC Handler](#21-bad-mac-handler--sessões-signal)
|
|
57
57
|
22. [Resolução LID → telefone via grupo](#22-resolução-lid--telefone-via-grupo)
|
|
58
58
|
23. [Versão do WhatsApp sempre atualizada](#23-versão-do-whatsapp-sempre-atualizada)
|
|
59
|
-
24. [WhatsApp Flows — Formulários
|
|
59
|
+
24. [WhatsApp Flows — Formulários](#24-whatsapp-flows--formulários-interativos)
|
|
60
|
+
25. [Mensagens agendadas](#25-mensagens-agendadas)
|
|
61
|
+
26. [Status em grupo (GroupStatusMessageV2)](#26-status-em-grupo-groupstatusmessagev2)
|
|
60
62
|
|
|
61
63
|
---
|
|
62
64
|
|
|
@@ -68,11 +70,11 @@ const {
|
|
|
68
70
|
useMultiFileAuthState,
|
|
69
71
|
DisconnectReason,
|
|
70
72
|
Browsers,
|
|
71
|
-
|
|
73
|
+
getBestWaVersion
|
|
72
74
|
} = require('@systemzero/baileys')
|
|
73
75
|
|
|
74
76
|
const { state, saveCreds } = await useMultiFileAuthState('./session')
|
|
75
|
-
const { version } = await
|
|
77
|
+
const { version, isLatest, source } = await getBestWaVersion()
|
|
76
78
|
|
|
77
79
|
const sock = makeWASocket({
|
|
78
80
|
version,
|
|
@@ -91,21 +93,25 @@ sock.ev.on('creds.update', saveCreds)
|
|
|
91
93
|
sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
|
|
92
94
|
if (connection === 'close') {
|
|
93
95
|
const code = lastDisconnect?.error?.output?.statusCode
|
|
94
|
-
if (code
|
|
96
|
+
if (code === DisconnectReason.loggedOut) {
|
|
97
|
+
process.exit()
|
|
98
|
+
} else if (code === DisconnectReason.forbidden) {
|
|
99
|
+
console.log('Sessão revogada pelo WhatsApp (403). Apague a sessão e re-pareie.')
|
|
100
|
+
process.exit()
|
|
101
|
+
} else {
|
|
102
|
+
startBot()
|
|
103
|
+
}
|
|
95
104
|
}
|
|
96
105
|
})
|
|
97
106
|
```
|
|
98
107
|
|
|
99
|
-
>
|
|
108
|
+
> Use `getBestWaVersion()` em vez de `fetchLatestWaWebVersion()` — ela tenta as duas fontes e só aceita resultado se buscou fresco de verdade, sem cair silenciosamente num fallback desatualizado que pode causar erros 403.
|
|
100
109
|
|
|
101
110
|
### Pairing Code
|
|
102
111
|
|
|
103
112
|
```js
|
|
104
|
-
// Automático
|
|
105
113
|
const code = await sock.requestPairingCode('5511999999999')
|
|
106
|
-
|
|
107
|
-
// Personalizado — exatamente 8 caracteres
|
|
108
|
-
const code = await sock.requestPairingCode('5511999999999', 'MYBOT001')
|
|
114
|
+
const code = await sock.requestPairingCode('5511999999999', 'MYBOT001') // personalizado — 8 chars
|
|
109
115
|
```
|
|
110
116
|
|
|
111
117
|
### Store em memória
|
|
@@ -134,47 +140,23 @@ store.bind(sock.ev)
|
|
|
134
140
|
Todos os campos de mídia aceitam `Buffer`, `{ url: 'https://...' }` ou `{ stream }`.
|
|
135
141
|
|
|
136
142
|
```js
|
|
137
|
-
// Texto
|
|
138
143
|
await sock.sendMessage(jid, { text: 'Olá!' })
|
|
139
|
-
|
|
140
|
-
// Texto com menção
|
|
141
|
-
await sock.sendMessage(jid, {
|
|
142
|
-
text: '@5511...!',
|
|
143
|
-
mentions: ['5511999999999@s.whatsapp.net']
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
// Imagem
|
|
144
|
+
await sock.sendMessage(jid, { text: '@5511...!', mentions: ['5511999999999@s.whatsapp.net'] })
|
|
147
145
|
await sock.sendMessage(jid, { image: { url: 'https://...' }, caption: 'Legenda' })
|
|
148
|
-
|
|
149
|
-
// Vídeo
|
|
150
146
|
await sock.sendMessage(jid, { video: buffer, caption: 'Legenda', gifPlayback: false })
|
|
151
|
-
|
|
152
|
-
// Áudio normal (música)
|
|
153
147
|
await sock.sendMessage(jid, { audio: buffer, mimetype: 'audio/mpeg', ptt: false })
|
|
154
|
-
|
|
155
|
-
// Nota de voz (PTT) — ver seção 19 para conversão automática de qualquer formato
|
|
156
|
-
await sock.sendMessage(jid, { audio: buffer, ptt: true })
|
|
157
|
-
|
|
158
|
-
// Documento
|
|
148
|
+
await sock.sendMessage(jid, { audio: buffer, ptt: true }) // PTT — converte automaticamente
|
|
159
149
|
await sock.sendMessage(jid, { document: buffer, mimetype: 'application/pdf', fileName: 'doc.pdf' })
|
|
160
|
-
|
|
161
|
-
// Figurinha
|
|
162
150
|
await sock.sendMessage(jid, { sticker: buffer })
|
|
163
|
-
|
|
164
|
-
// Album (mínimo 2 itens)
|
|
165
151
|
await sock.sendMessage(jid, {
|
|
166
152
|
album: [{ image: buffer1 }, { image: buffer2 }, { video: bufferVideo }]
|
|
167
153
|
})
|
|
168
|
-
|
|
169
|
-
// Contato
|
|
170
154
|
await sock.sendMessage(jid, {
|
|
171
155
|
contacts: {
|
|
172
156
|
displayName: 'João',
|
|
173
157
|
contacts: [{ vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:João\nTEL:+5511999999999\nEND:VCARD' }]
|
|
174
158
|
}
|
|
175
159
|
})
|
|
176
|
-
|
|
177
|
-
// Localização
|
|
178
160
|
await sock.sendMessage(jid, {
|
|
179
161
|
location: { degreesLatitude: -23.55, degreesLongitude: -46.63, name: 'São Paulo' }
|
|
180
162
|
})
|
|
@@ -185,38 +167,16 @@ await sock.sendMessage(jid, {
|
|
|
185
167
|
## 3. Mensagens especiais
|
|
186
168
|
|
|
187
169
|
```js
|
|
188
|
-
// Reação
|
|
189
170
|
await sock.sendMessage(jid, { react: { text: '❤️', key: message.key } })
|
|
190
|
-
|
|
191
|
-
// Deletar
|
|
192
171
|
await sock.sendMessage(jid, { delete: message.key })
|
|
193
|
-
|
|
194
|
-
// Editar
|
|
195
172
|
await sock.sendMessage(jid, { edit: message.key, text: 'Texto editado' })
|
|
196
|
-
|
|
197
|
-
// Fixar — ver seção 17 para a versão completa
|
|
198
|
-
await sock.sendMessage(jid, { pin: message.key, type: 1 })
|
|
199
|
-
|
|
200
|
-
// View Once
|
|
201
173
|
await sock.sendMessage(jid, { image: buffer, viewOnce: true })
|
|
202
174
|
await sock.sendMessage(jid, { image: buffer, viewOnceV2: true })
|
|
203
|
-
|
|
204
|
-
// Spoiler
|
|
205
175
|
await sock.sendMessage(jid, { image: buffer, caption: '!', spoiler: true })
|
|
206
|
-
|
|
207
|
-
// Status de grupo
|
|
208
176
|
await sock.sendMessage(jid, { image: buffer, groupStatus: true })
|
|
209
|
-
|
|
210
|
-
// Efêmera
|
|
211
177
|
await sock.sendMessage(jid, { image: buffer, ephemeral: true })
|
|
212
|
-
|
|
213
|
-
// Lottie sticker
|
|
214
178
|
await sock.sendMessage(jid, { sticker: buffer, isLottie: true })
|
|
215
|
-
|
|
216
|
-
// Mention All
|
|
217
179
|
await sock.sendMessage(jid, { text: '@all', mentionAll: true })
|
|
218
|
-
|
|
219
|
-
// External Ad Reply
|
|
220
180
|
await sock.sendMessage(jid, {
|
|
221
181
|
text: 'Confira!',
|
|
222
182
|
externalAdReply: {
|
|
@@ -226,21 +186,15 @@ await sock.sendMessage(jid, {
|
|
|
226
186
|
renderLargerThumbnail: true
|
|
227
187
|
}
|
|
228
188
|
})
|
|
229
|
-
|
|
230
|
-
// Enquete
|
|
231
189
|
await sock.sendMessage(jid, {
|
|
232
190
|
poll: { name: 'Pergunta?', values: ['A', 'B', 'C'], selectableCount: 1 }
|
|
233
191
|
})
|
|
234
|
-
|
|
235
|
-
// Encaminhar
|
|
236
192
|
const { generateForwardMessageContent, generateWAMessageFromContent } = require('@systemzero/baileys')
|
|
237
193
|
const fwd = generateForwardMessageContent(message, false)
|
|
238
194
|
const msg = generateWAMessageFromContent(jid, fwd, { quoted: m })
|
|
239
195
|
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
240
196
|
```
|
|
241
197
|
|
|
242
|
-
> ⚠️ **Removido desta versão do README:** preview de link customizado (`linkPreview` manual) e áudio embutido em rodapé de card interativo (`audioFooter`). Os dois existem no proto do WhatsApp, mas em testes reais (Android e iOS) **não renderizaram corretamente** — o `audioFooter` não mostrou o player de áudio, e o `linkPreview` manual caiu num formato de card genérico em vez do esperado. Não são recursos confiáveis hoje.
|
|
243
|
-
|
|
244
198
|
---
|
|
245
199
|
|
|
246
200
|
## 4. Botões e interativos
|
|
@@ -264,40 +218,24 @@ const msg = generateWAMessageFromContent(jid, {
|
|
|
264
218
|
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
265
219
|
```
|
|
266
220
|
|
|
267
|
-
### Tipos de botão (testados e funcionais)
|
|
268
|
-
|
|
269
221
|
```js
|
|
270
|
-
// Resposta rápida
|
|
271
222
|
{ name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: 'OK', id: 'ok' }) }
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
{ name: '
|
|
275
|
-
|
|
276
|
-
// Copiar
|
|
277
|
-
{ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copiar', copy_code: 'CODIGO' }) }
|
|
278
|
-
|
|
279
|
-
// Ligar
|
|
280
|
-
{ name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: 'Ligar', phone_number: '5511999999999' }) }
|
|
281
|
-
|
|
282
|
-
// Lista dropdown
|
|
223
|
+
{ name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Abrir', url: 'https://systemzone.store' }) }
|
|
224
|
+
{ name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: 'Copiar', copy_code: 'CODIGO' }) }
|
|
225
|
+
{ name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: 'Ligar', phone_number: '5511999999999' }) }
|
|
283
226
|
{
|
|
284
227
|
name: 'single_select',
|
|
285
228
|
buttonParamsJson: JSON.stringify({
|
|
286
229
|
title: 'Escolher',
|
|
287
|
-
sections: [{ title: 'Cat', rows: [
|
|
288
|
-
{ header: 'Op1', title: 'Opção 1', description: 'Desc', id: 'op1' }
|
|
289
|
-
]}]
|
|
230
|
+
sections: [{ title: 'Cat', rows: [{ header: 'Op1', title: 'Opção 1', description: 'Desc', id: 'op1' }] }]
|
|
290
231
|
})
|
|
291
232
|
}
|
|
292
233
|
```
|
|
293
234
|
|
|
294
|
-
### Header com imagem
|
|
235
|
+
### Header com imagem + nativeFlow
|
|
295
236
|
|
|
296
237
|
```js
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// Forma simplificada e já testada: usar `image` + `caption` direto no sendMessage,
|
|
300
|
-
// junto com nativeFlow — a lib monta o header automaticamente.
|
|
238
|
+
// Use `caption`, não `text` — `text` faz a lib pular o bloco do header
|
|
301
239
|
await sock.sendMessage(jid, {
|
|
302
240
|
image: { url: 'https://...' },
|
|
303
241
|
caption: 'Texto do card',
|
|
@@ -305,9 +243,19 @@ await sock.sendMessage(jid, {
|
|
|
305
243
|
})
|
|
306
244
|
```
|
|
307
245
|
|
|
308
|
-
|
|
246
|
+
### Carousel
|
|
309
247
|
|
|
310
|
-
|
|
248
|
+
```js
|
|
249
|
+
await sock.sendMessage(jid, {
|
|
250
|
+
cards: [
|
|
251
|
+
{ image: buffer1, caption: 'Card 1', nativeFlow: [{ text: 'Ver', id: 'c1' }] },
|
|
252
|
+
{ image: buffer2, caption: 'Card 2', nativeFlow: [{ text: 'Ver', id: 'c2' }] },
|
|
253
|
+
],
|
|
254
|
+
footer: 'Rodapé'
|
|
255
|
+
})
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Template Buttons / Sections
|
|
311
259
|
|
|
312
260
|
```js
|
|
313
261
|
await sock.sendMessage(jid, {
|
|
@@ -318,16 +266,10 @@ await sock.sendMessage(jid, {
|
|
|
318
266
|
{ text: 'Ligar', call: '5511999999999' },
|
|
319
267
|
]
|
|
320
268
|
})
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
### Sections
|
|
324
269
|
|
|
325
|
-
```js
|
|
326
270
|
await sock.sendMessage(jid, {
|
|
327
271
|
text: 'Selecione', buttonText: 'Ver opções',
|
|
328
|
-
sections: [{ title: 'Cat', rows: [
|
|
329
|
-
{ title: 'Item 1', description: 'Desc', id: 'i1' }
|
|
330
|
-
]}]
|
|
272
|
+
sections: [{ title: 'Cat', rows: [{ title: 'Item 1', description: 'Desc', id: 'i1' }] }]
|
|
331
273
|
})
|
|
332
274
|
```
|
|
333
275
|
|
|
@@ -340,37 +282,30 @@ await sock.sendMessage(jid, {
|
|
|
340
282
|
cover: bufferWebP,
|
|
341
283
|
stickers: [
|
|
342
284
|
{ data: bufferWebP, emojis: ['😂'] },
|
|
343
|
-
{ data: bufferAnima, emojis: ['🔥'] },
|
|
344
|
-
{ data: bufferPng, emojis: ['✨'] },
|
|
285
|
+
{ data: bufferAnima, emojis: ['🔥'] },
|
|
286
|
+
{ data: bufferPng, emojis: ['✨'] },
|
|
345
287
|
],
|
|
346
|
-
name:
|
|
347
|
-
publisher:
|
|
348
|
-
description: 'Descrição'
|
|
288
|
+
name: 'Nome do Pack',
|
|
289
|
+
publisher: 'Autor',
|
|
349
290
|
})
|
|
350
291
|
```
|
|
351
292
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
Máximo 60 figurinhas, cada uma até 1MB. Requer `sharp` ou `@napi-rs/image` para PNG.
|
|
293
|
+
`cover` e cada `stickers[].data` esperam **Buffer**. Máximo 60 figurinhas, até 1MB cada. Requer `sharp` ou `@napi-rs/image` para PNG.
|
|
355
294
|
|
|
356
295
|
---
|
|
357
296
|
|
|
358
297
|
## 6. Canais Newsletter
|
|
359
298
|
|
|
360
299
|
```js
|
|
361
|
-
// Texto
|
|
362
300
|
await sock.sendMessage('120363...@newsletter', { text: 'Novidade!' })
|
|
363
301
|
|
|
364
|
-
// Mídia — sempre via generateWAMessage + relayMessage
|
|
365
302
|
const { generateWAMessage, generateMessageIDV2 } = require('@systemzero/baileys')
|
|
366
|
-
|
|
367
303
|
const fullMsg = await generateWAMessage(canalJid, { image: buffer, caption: 'Legenda' }, {
|
|
368
304
|
upload: sock.waUploadToServer, userJid: sock.user.id,
|
|
369
305
|
messageId: generateMessageIDV2(sock.user.id),
|
|
370
306
|
})
|
|
371
307
|
await sock.relayMessage(canalJid, fullMsg.message, { messageId: fullMsg.key.id })
|
|
372
308
|
|
|
373
|
-
// Gerenciar
|
|
374
309
|
const canal = await sock.newsletterCreate('Nome', 'Descrição')
|
|
375
310
|
await sock.newsletterFollow(jid)
|
|
376
311
|
await sock.newsletterUnfollow(jid)
|
|
@@ -392,14 +327,11 @@ const { subscribers } = await sock.newsletterSubscribers(jid)
|
|
|
392
327
|
## 7. Enquetes com decrypt
|
|
393
328
|
|
|
394
329
|
```js
|
|
395
|
-
// Criar
|
|
396
330
|
await sock.sendMessage(jid, {
|
|
397
331
|
poll: { name: 'Pergunta?', values: ['A', 'B', 'C'], selectableCount: 1 }
|
|
398
332
|
})
|
|
399
333
|
|
|
400
|
-
// Receber votos — no connect.js após saveCreds:
|
|
401
334
|
const { getAggregateVotesInPollMessage } = require('@systemzero/baileys/lib/Utils/messages.js')
|
|
402
|
-
|
|
403
335
|
sock.ev.on('messages.update', async (updates) => {
|
|
404
336
|
for (const { key, update } of updates) {
|
|
405
337
|
if (!update.pollUpdates) continue
|
|
@@ -418,25 +350,13 @@ sock.ev.on('messages.update', async (updates) => {
|
|
|
418
350
|
## 8. AI Rich
|
|
419
351
|
|
|
420
352
|
```js
|
|
421
|
-
// Texto com hyperlink
|
|
422
353
|
await sock.sendRich(jid, [
|
|
423
|
-
sock.makeText('Acesse [nosso site](https://systemzone.store).')
|
|
424
|
-
|
|
354
|
+
sock.makeText('Acesse [nosso site](https://systemzone.store).'),
|
|
355
|
+
sock.makeCode('bash', 'npm i @systemzero/baileys'),
|
|
356
|
+
sock.makeTable([['Nome', 'Status'], ['Botões', '✅']]),
|
|
357
|
+
sock.makeList(['Item 1', 'Item 2']),
|
|
358
|
+
], quotedMsg, ['RICH_RESPONSE_CODE', 'RICH_RESPONSE_TABLE'])
|
|
425
359
|
|
|
426
|
-
// Código
|
|
427
|
-
await sock.sendRich(jid, [
|
|
428
|
-
sock.makeCode('bash', 'npm i @systemzero/baileys')
|
|
429
|
-
], null, ['RICH_RESPONSE_CODE'])
|
|
430
|
-
|
|
431
|
-
// Tabela
|
|
432
|
-
await sock.sendRich(jid, [
|
|
433
|
-
sock.makeTable([['Nome', 'Status'], ['Botões', '✅'], ['Canais', '✅']])
|
|
434
|
-
], null, ['RICH_RESPONSE_TABLE'])
|
|
435
|
-
|
|
436
|
-
// Lista
|
|
437
|
-
await sock.sendRich(jid, [sock.makeList(['Item 1', 'Item 2'])])
|
|
438
|
-
|
|
439
|
-
// Atalhos
|
|
440
360
|
await sock.sendRichText(jid, 'Texto com [link](https://systemzone.store)', quotedMsg)
|
|
441
361
|
await sock.sendRichCode(jid, 'Título', 'javascript', 'const x = 1', quotedMsg)
|
|
442
362
|
await sock.sendRichList(jid, 'Lista', ['A', 'B'], quotedMsg)
|
|
@@ -447,10 +367,10 @@ await sock.sendRichList(jid, 'Lista', ['A', 'B'], quotedMsg)
|
|
|
447
367
|
## 9. LID / JID — Sistema avançado
|
|
448
368
|
|
|
449
369
|
O WhatsApp usa dois tipos de identificador:
|
|
450
|
-
- **JID** (`@s.whatsapp.net`) —
|
|
451
|
-
- **LID** (`@lid`) — identificador opaco
|
|
370
|
+
- **JID** (`@s.whatsapp.net`) — baseado em número de telefone
|
|
371
|
+
- **LID** (`@lid`) — identificador opaco sem número
|
|
452
372
|
|
|
453
|
-
A partir da v1.0.6, a
|
|
373
|
+
A partir da v1.0.6, a lib resolve automaticamente LID↔JID via `sharedLidPhoneCache`, inclusive ao buscar metadata de grupo (`groupMetadata`).
|
|
454
374
|
|
|
455
375
|
```js
|
|
456
376
|
const {
|
|
@@ -458,29 +378,21 @@ const {
|
|
|
458
378
|
getSenderInfo, sharedLidPhoneCache, isLidUser, isPnUser,
|
|
459
379
|
getBotJid, setBotMap
|
|
460
380
|
} = require('@systemzero/baileys')
|
|
461
|
-
```
|
|
462
381
|
|
|
463
|
-
```js
|
|
464
382
|
const jid = lidToJid('123456@lid')
|
|
465
383
|
const { jid, lid } = resolveAll('5511999999999@s.whatsapp.net')
|
|
466
|
-
|
|
467
|
-
normalizeJid('123456@lid') // → resolve via cache
|
|
468
|
-
const { isValid, error } = validateJid('5511999999999@s.whatsapp.net')
|
|
469
|
-
const { jid, lid, isGroup, isValid } = getSenderInfo(message)
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
### Cache bidirecional
|
|
384
|
+
const { jid, lid, isGroup } = getSenderInfo(message)
|
|
473
385
|
|
|
474
|
-
|
|
386
|
+
normalizeJid('5511999999999') // → '5511999999999@s.whatsapp.net'
|
|
387
|
+
normalizeJid('123456@lid') // → resolve via cache
|
|
475
388
|
|
|
476
|
-
|
|
477
|
-
sharedLidPhoneCache.set('123456@lid', '5511999999999@s.whatsapp.net') // manual, raro
|
|
389
|
+
sharedLidPhoneCache.set('123456@lid', '5511999999999@s.whatsapp.net')
|
|
478
390
|
const jid = sharedLidPhoneCache.getPhoneForLid('123456@lid')
|
|
479
391
|
const lid = sharedLidPhoneCache.getLidForPhone('5511999999999@s.whatsapp.net')
|
|
480
|
-
console.log('
|
|
392
|
+
console.log('Cache size:', sharedLidPhoneCache.size)
|
|
481
393
|
```
|
|
482
394
|
|
|
483
|
-
> ⚠️
|
|
395
|
+
> ⚠️ Identidades do próprio bot (`sock.user.id`/`.lid`) vêm com sufixo de dispositivo (`numero:N@dominio`). Pra comparar com `participants`, remova só o `:N` preservando o domínio: `jid.replace(/:\d+(?=@)/, '')` — **não use** `split(':')[0]`.
|
|
484
396
|
|
|
485
397
|
---
|
|
486
398
|
|
|
@@ -489,9 +401,7 @@ console.log('Entradas no cache:', sharedLidPhoneCache.size)
|
|
|
489
401
|
```js
|
|
490
402
|
const { resolveUsername, isUsername } = require('@systemzero/baileys')
|
|
491
403
|
|
|
492
|
-
isUsername('@josue')
|
|
493
|
-
isUsername('josue') // false
|
|
494
|
-
|
|
404
|
+
isUsername('@josue') // true
|
|
495
405
|
const jid = await resolveUsername('@josue', sock.onWhatsApp.bind(sock))
|
|
496
406
|
if (jid) await sock.sendMessage(jid, { text: 'Olá!' })
|
|
497
407
|
```
|
|
@@ -502,34 +412,28 @@ if (jid) await sock.sendMessage(jid, { text: 'Olá!' })
|
|
|
502
412
|
|
|
503
413
|
```js
|
|
504
414
|
const { Button, ButtonV2, Carousel, AIRich } = require('@systemzero/baileys/lib/MB.cjs')
|
|
415
|
+
// O socket vai sempre no construtor: new ButtonV2(sock)
|
|
505
416
|
```
|
|
506
417
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
### ButtonV2 — botões clássicos
|
|
418
|
+
### ButtonV2
|
|
510
419
|
|
|
511
420
|
```js
|
|
512
421
|
const msg = new ButtonV2(sock)
|
|
513
|
-
msg.setTitle('Título')
|
|
514
|
-
msg.setBody('Corpo')
|
|
515
|
-
msg.setFooter('Rodapé')
|
|
422
|
+
msg.setTitle('Título'); msg.setBody('Corpo'); msg.setFooter('Rodapé')
|
|
516
423
|
msg.setThumbnail('https://exemplo.com/imagem.jpg')
|
|
517
424
|
msg.addButton('✅ Opção 1', 'opcao_1')
|
|
518
425
|
msg.addButton('❌ Opção 2', 'opcao_2')
|
|
519
426
|
await msg.send(jid, { quoted: m })
|
|
427
|
+
// clique chega como m.body === 'opcao_1'
|
|
520
428
|
```
|
|
521
429
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
### Button — native flow avançado
|
|
430
|
+
### Button (native flow)
|
|
525
431
|
|
|
526
432
|
```js
|
|
527
433
|
const msg = new Button(sock)
|
|
528
|
-
msg.setTitle('Título')
|
|
529
|
-
msg.setBody('Corpo')
|
|
530
434
|
msg.addReply('✅ Confirmar', 'confirmar')
|
|
531
|
-
msg.addUrl('🔗 Abrir
|
|
532
|
-
msg.addCopy('📋 Copiar
|
|
435
|
+
msg.addUrl('🔗 Abrir', 'https://systemzone.store')
|
|
436
|
+
msg.addCopy('📋 Copiar', 'PROMO2025')
|
|
533
437
|
msg.addCall('📞 Ligar', '5511999999999')
|
|
534
438
|
await msg.send(jid, { quoted: m })
|
|
535
439
|
```
|
|
@@ -538,9 +442,9 @@ await msg.send(jid, { quoted: m })
|
|
|
538
442
|
|
|
539
443
|
```js
|
|
540
444
|
const msg = new Carousel(sock)
|
|
541
|
-
msg.setBody('Escolha
|
|
542
|
-
msg.card(c => c.image('https://...').title('Card 1').text('
|
|
543
|
-
msg.card(c => c.image('https://...').title('Card 2').text('
|
|
445
|
+
msg.setBody('Escolha:')
|
|
446
|
+
msg.card(c => c.image('https://...').title('Card 1').text('Desc').button('Ver', 'c1'))
|
|
447
|
+
msg.card(c => c.image('https://...').title('Card 2').text('Desc').button('Ver', 'c2'))
|
|
544
448
|
await msg.send(jid, { quoted: m })
|
|
545
449
|
```
|
|
546
450
|
|
|
@@ -549,27 +453,25 @@ await msg.send(jid, { quoted: m })
|
|
|
549
453
|
```js
|
|
550
454
|
const msg = new AIRich(sock)
|
|
551
455
|
msg.addText('Acesse [System Zero](https://systemzone.store).')
|
|
552
|
-
msg.addCode('javascript', `
|
|
553
|
-
msg.addTable([['Comando', '
|
|
456
|
+
msg.addCode('javascript', `require('@systemzero/baileys')`)
|
|
457
|
+
msg.addTable([['Comando', 'Desc'], ['!menu', 'Menu principal']])
|
|
554
458
|
await msg.send(jid, { quoted: m })
|
|
555
459
|
```
|
|
556
460
|
|
|
557
461
|
---
|
|
558
462
|
|
|
559
|
-
## 12. Botões estendidos
|
|
560
|
-
|
|
561
|
-
> ⚠️ **Não verificado nesta versão do README.** Os tipos abaixo (`catalog`, `products`, `otp`, `orderDetails`, `flow`, etc) exigem conta **WhatsApp Business API verificada** com catálogo/pagamentos configurados — nenhum deles foi testado numa conta pessoal real. Os tipos mais simples (`reminder`, `address`, `location`, `phoneNumber`, `urlBtn`, `clearChat`) têm chance maior de funcionar em qualquer conta, mas também não foram confirmados em teste real. Trate como documentação do proto, não como garantia de funcionamento. Para formulários reais e testados, veja a seção 24.
|
|
463
|
+
## 12. Botões estendidos
|
|
562
464
|
|
|
563
465
|
```js
|
|
466
|
+
await sock.sendMessage(jid, { text: 'Mensagem', nativeFlow: [ /* botões */ ] })
|
|
467
|
+
|
|
564
468
|
{ text: '⏰ Me lembre', reminder: 'lembrete_id' }
|
|
565
469
|
{ text: '🔕 Cancelar', cancelReminder: 'lembrete_id' }
|
|
566
|
-
{ text: '📍
|
|
470
|
+
{ text: '📍 Endereço', address: true }
|
|
567
471
|
{ text: '📡 Localização', location: true }
|
|
568
|
-
{ text: '🛍️
|
|
569
|
-
{ text: '📦 Produtos', products: ['prod_id_1'], bizJid: '5511...@s.whatsapp.net' }
|
|
472
|
+
{ text: '🛍️ Catálogo', catalog: '5511999999999@s.whatsapp.net' }
|
|
570
473
|
{ text: 'Copiar código', otp: '123456' }
|
|
571
474
|
{ text: '📞 Ligar', phoneNumber: '5511999999999' }
|
|
572
|
-
{ text: '📋 Ver pedido', orderDetails: { order_id: '123', token: 'abc' } }
|
|
573
475
|
{ text: '🗑️ Limpar chat', clearChat: true }
|
|
574
476
|
{ text: '🔗 Abrir', urlBtn: 'https://systemzone.store' }
|
|
575
477
|
```
|
|
@@ -579,7 +481,7 @@ await msg.send(jid, { quoted: m })
|
|
|
579
481
|
```js
|
|
580
482
|
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
581
483
|
const msg = messages[0]
|
|
582
|
-
if (msg.isSystemNotification) return
|
|
484
|
+
if (msg.isSystemNotification) return
|
|
583
485
|
})
|
|
584
486
|
```
|
|
585
487
|
|
|
@@ -588,9 +490,8 @@ sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
|
588
490
|
## 13. Grupos
|
|
589
491
|
|
|
590
492
|
```js
|
|
591
|
-
const grupo = await sock.groupCreate('Nome', ['5511...@s.whatsapp.net'])
|
|
592
493
|
const meta = await sock.groupMetadata(jid)
|
|
593
|
-
|
|
494
|
+
const grupo = await sock.groupCreate('Nome', ['5511...@s.whatsapp.net'])
|
|
594
495
|
await sock.groupParticipantsUpdate(jid, ['5511...@s.whatsapp.net'], 'add')
|
|
595
496
|
await sock.groupParticipantsUpdate(jid, ['5511...@s.whatsapp.net'], 'remove')
|
|
596
497
|
await sock.groupParticipantsUpdate(jid, ['5511...@s.whatsapp.net'], 'promote')
|
|
@@ -598,12 +499,10 @@ await sock.groupParticipantsUpdate(jid, ['5511...@s.whatsapp.net'], 'demote')
|
|
|
598
499
|
await sock.groupLeave(jid)
|
|
599
500
|
await sock.groupUpdateSubject(jid, 'Novo Nome')
|
|
600
501
|
await sock.groupUpdateDescription(jid, 'Nova descrição')
|
|
601
|
-
|
|
602
502
|
const code = await sock.groupInviteCode(jid)
|
|
603
503
|
await sock.groupRevokeInvite(jid)
|
|
604
504
|
await sock.groupAcceptInvite('CODIGO')
|
|
605
505
|
await sock.groupToggleEphemeral(jid, 86400)
|
|
606
|
-
|
|
607
506
|
const grupos = await sock.groupFetchAllParticipating()
|
|
608
507
|
```
|
|
609
508
|
|
|
@@ -615,22 +514,16 @@ const grupos = await sock.groupFetchAllParticipating()
|
|
|
615
514
|
const url = await sock.profilePictureUrl(jid, 'image')
|
|
616
515
|
await sock.updateProfilePicture(jid, buffer)
|
|
617
516
|
await sock.updateProfileStatus('🤖 Bot ativo')
|
|
618
|
-
|
|
619
517
|
const [result] = await sock.onWhatsApp('5511999999999')
|
|
620
|
-
|
|
621
518
|
await sock.updateBlockStatus(jid, 'block')
|
|
622
519
|
await sock.updateBlockStatus(jid, 'unblock')
|
|
623
|
-
|
|
624
520
|
await sock.sendPresenceUpdate('composing', jid)
|
|
625
521
|
await sock.sendPresenceUpdate('available', jid)
|
|
626
522
|
await sock.presenceSubscribe(jid)
|
|
627
|
-
|
|
628
523
|
await sock.readMessages([message.key])
|
|
629
|
-
|
|
630
524
|
await sock.chatModify({ archive: true }, jid)
|
|
631
525
|
await sock.chatModify({ pin: true }, jid)
|
|
632
526
|
await sock.chatModify({ mute: 8 * 60 * 60 * 1000 }, jid)
|
|
633
|
-
|
|
634
527
|
await sock.logout()
|
|
635
528
|
```
|
|
636
529
|
|
|
@@ -650,7 +543,6 @@ sock.ev.on('group-participants.update', ({ id, participants, action }) => {})
|
|
|
650
543
|
sock.ev.on('contacts.update', (contacts) => {})
|
|
651
544
|
sock.ev.on('connection.update', ({ connection, lastDisconnect, qr }) => {})
|
|
652
545
|
sock.ev.on('creds.update', saveCreds)
|
|
653
|
-
|
|
654
546
|
sock.ev.on('call', (calls) => {
|
|
655
547
|
for (const call of calls) {
|
|
656
548
|
if (call.status === 'offer') sock.rejectCall(call.id, call.from)
|
|
@@ -663,79 +555,57 @@ sock.ev.on('call', (calls) => {
|
|
|
663
555
|
## 16. Utilitários
|
|
664
556
|
|
|
665
557
|
```js
|
|
666
|
-
const { downloadMediaMessage
|
|
558
|
+
const { downloadMediaMessage } = require('@systemzero/baileys')
|
|
667
559
|
const buffer = await downloadMediaMessage(message, 'buffer', {})
|
|
668
560
|
|
|
669
561
|
const { generateWAMessage, generateWAMessageFromContent, generateMessageIDV2 } = require('@systemzero/baileys')
|
|
562
|
+
const { jidNormalizedUser, isJidGroup, isJidNewsletter, isLidUser, isPnUser } = require('@systemzero/baileys')
|
|
670
563
|
|
|
671
|
-
|
|
672
|
-
jidNormalizedUser, isJidGroup, isJidNewsletter,
|
|
673
|
-
isLidUser, isPnUser, isUsername,
|
|
674
|
-
normalizeJid, resolveAll, getSenderInfo,
|
|
675
|
-
jidEncode, jidDecode
|
|
676
|
-
} = require('@systemzero/baileys')
|
|
677
|
-
|
|
678
|
-
// Detectar dispositivo — analisa o FORMATO DO ID DA MENSAGEM, não o JID
|
|
564
|
+
// Detectar dispositivo — analisa o ID DA MENSAGEM, não o JID
|
|
679
565
|
const { getDevice } = require('@systemzero/baileys')
|
|
680
566
|
const device = getDevice(message.key.id) // 'android' | 'ios' | 'web' | 'unknown'
|
|
681
567
|
```
|
|
682
568
|
|
|
683
|
-
> `getDevice` só funciona em cima de um `message.key.id` real — não dá pra usar pra "descobrir o dispositivo" de alguém sem ter uma mensagem recente dela pra inspecionar.
|
|
684
|
-
|
|
685
569
|
---
|
|
686
570
|
|
|
687
571
|
## 17. Fixar / Desfixar mensagens
|
|
688
572
|
|
|
689
573
|
```js
|
|
690
|
-
|
|
691
|
-
await sock.sendMessage(jid, { pin: quotedMsg.key, type:
|
|
692
|
-
|
|
693
|
-
// Desfixar (type: 2 = UNPIN_FOR_ALL)
|
|
694
|
-
await sock.sendMessage(jid, { pin: quotedMsg.key, type: 2 })
|
|
574
|
+
await sock.sendMessage(jid, { pin: quotedMsg.key, type: 1 }) // PIN_FOR_ALL
|
|
575
|
+
await sock.sendMessage(jid, { pin: quotedMsg.key, type: 2 }) // UNPIN_FOR_ALL
|
|
695
576
|
```
|
|
696
577
|
|
|
697
|
-
`quotedMsg.key` precisa conter `remoteJid`, `fromMe`, `id` e (em grupos) `participant`.
|
|
698
|
-
|
|
699
578
|
---
|
|
700
579
|
|
|
701
580
|
## 18. Resolução de nomes (getName)
|
|
702
581
|
|
|
703
582
|
```js
|
|
704
583
|
const { getName } = require('@systemzero/baileys')
|
|
705
|
-
const nome = getName(msg, contactStore)
|
|
584
|
+
const nome = getName(msg, contactStore)
|
|
585
|
+
// Prioridade: contato salvo → verifiedName → pushName → @lid → telefone → "Usuário desconhecido"
|
|
706
586
|
```
|
|
707
587
|
|
|
708
|
-
Ordem de prioridade: contato salvo localmente → `verifiedName` → `pushName`/`notify` → resolução `@lid` via cache → número formatado → `"Usuário desconhecido"`. Nunca retorna vazio.
|
|
709
|
-
|
|
710
588
|
---
|
|
711
589
|
|
|
712
|
-
## 19. PTT real com qualquer formato
|
|
713
|
-
|
|
714
|
-
Antes da correção, `ptt: true` só **etiquetava** o mimetype como opus sem converter os bytes de verdade — qualquer entrada que não já fosse opus puro dava "algo errado com o arquivo de áudio" no WhatsApp. Agora a lib transcodifica de verdade via `ffmpeg` quando necessário.
|
|
590
|
+
## 19. PTT real com qualquer formato
|
|
715
591
|
|
|
716
592
|
```js
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
ptt: true
|
|
720
|
-
})
|
|
593
|
+
// Qualquer formato de entrada — a lib converte via ffmpeg automaticamente
|
|
594
|
+
await sock.sendMessage(jid, { audio: { url: 'https://musica.mp3' }, ptt: true })
|
|
721
595
|
```
|
|
722
596
|
|
|
723
|
-
A lib detecta
|
|
724
|
-
|
|
725
|
-
> Requer `ffmpeg` com `libopus` no `PATH` do servidor.
|
|
597
|
+
A lib detecta a extensão real da fonte (não o `mimetype` declarado) e converte pra mono/16kHz opus quando necessário. Requer `ffmpeg` com `libopus`.
|
|
726
598
|
|
|
727
599
|
---
|
|
728
600
|
|
|
729
601
|
## 20. Guard de pagamento stealth (anti-fraude)
|
|
730
602
|
|
|
731
|
-
A lib emite o evento `messages.decrypt-failed` sempre que uma mensagem de grupo falha na descriptografia (PreKey/CIPHERTEXT) — cobre o golpe de cobrança "invisível": mensagem normal enviada, depois **editada** pra conteúdo de pagamento.
|
|
732
|
-
|
|
733
603
|
```js
|
|
734
604
|
const { bindPaymentGuard } = require('@systemzero/baileys')
|
|
735
605
|
|
|
736
606
|
bindPaymentGuard(sock, {
|
|
737
|
-
isPaymentMessage: (webMessage) => { /* sua lógica
|
|
738
|
-
recordEnvelope: (webMessage, isPayment) => { /* seu registro
|
|
607
|
+
isPaymentMessage: (webMessage) => { /* sua lógica */ },
|
|
608
|
+
recordEnvelope: (webMessage, isPayment) => { /* seu registro */ },
|
|
739
609
|
treatDecryptFailureAsSuspicious: true,
|
|
740
610
|
onDetect: (detection) => {
|
|
741
611
|
// detection.type: 'direct' | 'edited' | 'undecryptable'
|
|
@@ -745,29 +615,23 @@ bindPaymentGuard(sock, {
|
|
|
745
615
|
|
|
746
616
|
| Caminho | Evento | Garantia |
|
|
747
617
|
|---|---|---|
|
|
748
|
-
| Mensagem direta | `messages.upsert` | Roda
|
|
749
|
-
| Mensagem editada | `messages.update` | Extrai `editedMessage` e roda
|
|
750
|
-
| Falha de decrypt | `messages.decrypt-failed` | Nunca ignora
|
|
751
|
-
|
|
752
|
-
> Falha de decrypt nunca foi lida pelo bot — não existe forma de confirmar que era pagamento. O guard garante que isso nunca passe sem nenhum aviso, não que vai "adivinhar" o conteúdo.
|
|
618
|
+
| Mensagem direta | `messages.upsert` | Roda detector no conteúdo |
|
|
619
|
+
| Mensagem editada | `messages.update` | Extrai `editedMessage` e roda detector |
|
|
620
|
+
| Falha de decrypt | `messages.decrypt-failed` | Nunca ignora silenciosamente |
|
|
753
621
|
|
|
754
622
|
---
|
|
755
623
|
|
|
756
624
|
## 21. Bad MAC Handler — sessões Signal
|
|
757
625
|
|
|
758
626
|
```js
|
|
759
|
-
const {
|
|
627
|
+
const { badMacHandler } = require('@systemzero/baileys')
|
|
760
628
|
|
|
761
629
|
if (badMacHandler.isBadMacError(error)) {
|
|
762
|
-
badMacHandler.handleError(error, '
|
|
630
|
+
badMacHandler.handleError(error, 'contexto')
|
|
763
631
|
}
|
|
764
|
-
|
|
765
|
-
// limpa sessões Signal problemáticas, preservando creds.json
|
|
766
|
-
badMacHandler.clearProblematicSessionFiles()
|
|
632
|
+
badMacHandler.clearProblematicSessionFiles() // preserva creds.json
|
|
767
633
|
```
|
|
768
634
|
|
|
769
|
-
Erros "Bad MAC" são esperados ocasionalmente em qualquer cliente Signal Protocol (sessão desincronizada do outro lado) — esse handler conta, dá reset automático após um intervalo, e remove só arquivos de sessão por par, nunca as credenciais principais.
|
|
770
|
-
|
|
771
635
|
---
|
|
772
636
|
|
|
773
637
|
## 22. Resolução LID → telefone via grupo
|
|
@@ -777,7 +641,7 @@ const { resolveLidPhoneFromGroup } = require('@systemzero/baileys')
|
|
|
777
641
|
const telefone = await resolveLidPhoneFromGroup(sock, groupJid, lid)
|
|
778
642
|
```
|
|
779
643
|
|
|
780
|
-
Força
|
|
644
|
+
Força resolução de `@lid` → telefone via `groupMetadata` — útil quando o cache ainda não tem aquele par.
|
|
781
645
|
|
|
782
646
|
---
|
|
783
647
|
|
|
@@ -786,17 +650,14 @@ Força a resolução de um `@lid` pro telefone real buscando a metadata do grupo
|
|
|
786
650
|
```js
|
|
787
651
|
const { getBestWaVersion } = require('@systemzero/baileys')
|
|
788
652
|
const { version, isLatest, source } = await getBestWaVersion()
|
|
653
|
+
// tenta web.whatsapp.com → GitHub → fallback fixo (avisando explicitamente)
|
|
789
654
|
```
|
|
790
655
|
|
|
791
|
-
Tenta `web.whatsapp.com`, depois GitHub, e só aceita um resultado se `isLatest === true` de verdade. Diferente de `fetchLatestWaWebVersion()`/`fetchLatestBaileysVersion()` isoladas, que sempre retornam algo (mesmo em falha, caindo num valor fixo antigo) sem deixar isso óbvio.
|
|
792
|
-
|
|
793
656
|
---
|
|
794
657
|
|
|
795
658
|
## 24. WhatsApp Flows — Formulários interativos
|
|
796
659
|
|
|
797
|
-
>
|
|
798
|
-
|
|
799
|
-
### Enviando o formulário
|
|
660
|
+
> Requer `flow_id` aprovado no Meta Business Manager.
|
|
800
661
|
|
|
801
662
|
```js
|
|
802
663
|
const { generateWAMessageFromContent } = require('@systemzero/baileys')
|
|
@@ -804,32 +665,22 @@ const { generateWAMessageFromContent } = require('@systemzero/baileys')
|
|
|
804
665
|
const formMsg = generateWAMessageFromContent(jid, {
|
|
805
666
|
viewOnceMessage: {
|
|
806
667
|
message: {
|
|
807
|
-
messageContextInfo: {
|
|
808
|
-
deviceListMetadata: {},
|
|
809
|
-
deviceListMetadataVersion: 2
|
|
810
|
-
},
|
|
668
|
+
messageContextInfo: { deviceListMetadata: {}, deviceListMetadataVersion: 2 },
|
|
811
669
|
interactiveMessage: {
|
|
812
|
-
body: { text: '
|
|
670
|
+
body: { text: 'Preencha seus dados.' },
|
|
813
671
|
nativeFlowMessage: {
|
|
814
672
|
buttons: [{
|
|
815
673
|
name: 'galaxy_message',
|
|
816
674
|
buttonParamsJson: JSON.stringify({
|
|
817
675
|
flow_message_version: '4',
|
|
818
|
-
flow_id: '
|
|
676
|
+
flow_id: 'SEU_FLOW_ID',
|
|
819
677
|
flow_action_payload: {
|
|
820
678
|
screen: 'contact_details',
|
|
821
|
-
data: {
|
|
822
|
-
full_name_visible: true,
|
|
823
|
-
phone_number_visible: true,
|
|
824
|
-
email_visible: true,
|
|
825
|
-
offer_name: 'Olá, seja bem-vindo',
|
|
826
|
-
offer_description: 'Sistema de teste'
|
|
827
|
-
}
|
|
679
|
+
data: { full_name_visible: true, email_visible: true }
|
|
828
680
|
},
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
flow_token: 'T0ZGRVJfU0lHTlVQ'
|
|
681
|
+
flow_cta: '__localize:FLOWS_SIGN_UP_BUTTON_TITLE',
|
|
682
|
+
flow_action: 'navigate',
|
|
683
|
+
flow_token: 'T0ZGRVJfU0lHTlVQ'
|
|
833
684
|
})
|
|
834
685
|
}],
|
|
835
686
|
messageParamsJson: '{}'
|
|
@@ -844,70 +695,285 @@ await sock.relayMessage(jid, formMsg.message, { messageId: formMsg.key.id })
|
|
|
844
695
|
|
|
845
696
|
### Recebendo a resposta
|
|
846
697
|
|
|
847
|
-
A resposta do formulário chega como `interactiveResponseMessage`. O `nativeFlowResponseMessage.paramsJson` traz os dados preenchidos dentro de `wa_flow_response_params.response_message` (uma string JSON aninhada — precisa de `JSON.parse` duplo).
|
|
848
|
-
|
|
849
698
|
```js
|
|
850
699
|
sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
851
700
|
const m = messages[0]
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
if (nfr?.name === 'galaxy_message' && nfr?.paramsJson) {
|
|
856
|
-
const parsed = JSON.parse(nfr.paramsJson)
|
|
857
|
-
if (!parsed.wa_flow_response_params) return
|
|
858
|
-
|
|
859
|
-
const flowParams = parsed.wa_flow_response_params
|
|
860
|
-
const flowId = flowParams.flow_id || ''
|
|
861
|
-
const rawResponse = flowParams.response_message || ''
|
|
862
|
-
|
|
863
|
-
let screens = []
|
|
864
|
-
try { screens = JSON.parse(rawResponse).screens || [] } catch {}
|
|
865
|
-
|
|
866
|
-
// extrai só os campos preenchidos
|
|
867
|
-
const campos = {}
|
|
868
|
-
for (const screen of screens) {
|
|
869
|
-
for (const comp of (screen.components || [])) {
|
|
870
|
-
if (comp.name && comp.value !== undefined && comp.value !== '') {
|
|
871
|
-
campos[comp.name] = comp.value
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
701
|
+
const nfr = m.message?.interactiveResponseMessage?.nativeFlowResponseMessage
|
|
702
|
+
if (nfr?.name !== 'galaxy_message' || !nfr?.paramsJson) return
|
|
875
703
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
704
|
+
const parsed = JSON.parse(nfr.paramsJson)
|
|
705
|
+
if (!parsed.wa_flow_response_params) return
|
|
706
|
+
|
|
707
|
+
const flowId = parsed.wa_flow_response_params.flow_id
|
|
708
|
+
const screens = JSON.parse(parsed.wa_flow_response_params.response_message).screens || []
|
|
709
|
+
|
|
710
|
+
const campos = {}
|
|
711
|
+
for (const screen of screens) {
|
|
712
|
+
for (const comp of (screen.components || [])) {
|
|
713
|
+
if (comp.name && comp.value !== undefined && comp.value !== '')
|
|
714
|
+
campos[comp.name] = comp.value
|
|
884
715
|
}
|
|
885
716
|
}
|
|
717
|
+
|
|
718
|
+
if (flowId === 'SEU_FLOW_ID') {
|
|
719
|
+
let resposta = 'Formulário recebido!\n\n'
|
|
720
|
+
if (campos.full_name) resposta += `Nome: ${campos.full_name}\n`
|
|
721
|
+
if (campos.email) resposta += `Email: ${campos.email}\n`
|
|
722
|
+
await sock.sendMessage(m.key.remoteJid, { text: resposta }, { quoted: m })
|
|
723
|
+
}
|
|
886
724
|
})
|
|
887
725
|
```
|
|
888
726
|
|
|
889
727
|
---
|
|
890
728
|
|
|
729
|
+
## 25. Mensagens agendadas
|
|
730
|
+
|
|
731
|
+
Sistema de agendamento em memória. Auto-inicia o timer quando há mensagens na fila, auto-para quando a fila esvazia.
|
|
732
|
+
|
|
733
|
+
```js
|
|
734
|
+
const { createMessageScheduler } = require('@systemzero/baileys')
|
|
735
|
+
|
|
736
|
+
const scheduler = createMessageScheduler(
|
|
737
|
+
(jid, content) => sock.sendMessage(jid, content),
|
|
738
|
+
{
|
|
739
|
+
onSent: (s, msg) => console.log(`Enviado pra ${s.jid} — ID: ${s.id}`),
|
|
740
|
+
onFailed: (s, err) => console.error(`Falhou: ${err.message}`)
|
|
741
|
+
}
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
// Agendar para data/hora específica
|
|
745
|
+
const entry = scheduler.schedule(
|
|
746
|
+
'5511999999999@s.whatsapp.net',
|
|
747
|
+
{ text: 'Feliz Aniversário! 🎂' },
|
|
748
|
+
new Date('2026-12-25T09:00:00')
|
|
749
|
+
)
|
|
750
|
+
console.log('Agendado com ID:', entry.id)
|
|
751
|
+
|
|
752
|
+
// Agendar com delay (30 minutos)
|
|
753
|
+
scheduler.scheduleDelay(jid, { text: 'Lembrete!' }, 30 * 60 * 1000)
|
|
754
|
+
|
|
755
|
+
// Agendar qualquer tipo de mensagem
|
|
756
|
+
scheduler.schedule(groupJid, {
|
|
757
|
+
image: { url: './promo.jpg' },
|
|
758
|
+
caption: 'Promoção de fim de semana!'
|
|
759
|
+
}, new Date('2026-12-20T08:00:00'))
|
|
760
|
+
|
|
761
|
+
// Cancelar um agendamento
|
|
762
|
+
scheduler.cancel(entry.id)
|
|
763
|
+
|
|
764
|
+
// Cancelar todos de um JID
|
|
765
|
+
scheduler.cancelForJid(jid)
|
|
766
|
+
|
|
767
|
+
// Ver pendentes
|
|
768
|
+
const pendentes = scheduler.getPending()
|
|
769
|
+
|
|
770
|
+
// Parar/retomar
|
|
771
|
+
scheduler.stop()
|
|
772
|
+
scheduler.start()
|
|
773
|
+
|
|
774
|
+
// Limpar tudo
|
|
775
|
+
scheduler.clearAll()
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
**Objeto retornado:**
|
|
779
|
+
```js
|
|
780
|
+
{
|
|
781
|
+
id: 'sched_1782000000000_a3f2b1',
|
|
782
|
+
jid: '5511...@s.whatsapp.net',
|
|
783
|
+
content: { text: '...' },
|
|
784
|
+
scheduledTime: Date,
|
|
785
|
+
createdAt: Date,
|
|
786
|
+
status: 'pending' | 'sent' | 'failed' | 'cancelled',
|
|
787
|
+
messageId: '...', // preenchido após envio
|
|
788
|
+
error: '...' // preenchido em caso de falha
|
|
789
|
+
}
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
---
|
|
793
|
+
|
|
794
|
+
## 26. Status em grupo (GroupStatusMessageV2)
|
|
795
|
+
|
|
796
|
+
Posta conteúdo como status dentro de um grupo — texto, imagem, vídeo ou áudio. Suporta modo **Close Friends** (apenas amigos próximos), usando o mesmo mecanismo do status nativo do WhatsApp.
|
|
797
|
+
|
|
798
|
+
```js
|
|
799
|
+
const { generateWAMessageFromContent, prepareWAMessageMedia, downloadMediaMessage } = require('@systemzero/baileys')
|
|
800
|
+
const crypto = require('crypto')
|
|
801
|
+
|
|
802
|
+
// ── Texto para todos do grupo ────────────────────────────────────────────────
|
|
803
|
+
const messageSecret = crypto.randomBytes(32)
|
|
804
|
+
|
|
805
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
806
|
+
messageContextInfo: { messageSecret },
|
|
807
|
+
groupStatusMessageV2: {
|
|
808
|
+
message: {
|
|
809
|
+
extendedTextMessage: {
|
|
810
|
+
text: 'Olá, grupo! 👋',
|
|
811
|
+
contextInfo: { isGroupStatus: true }
|
|
812
|
+
},
|
|
813
|
+
messageContextInfo: { messageSecret }
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
}, {})
|
|
817
|
+
|
|
818
|
+
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
819
|
+
```
|
|
820
|
+
|
|
821
|
+
```js
|
|
822
|
+
// ── Texto apenas para Amigos Próximos (Close Friends) ────────────────────────
|
|
823
|
+
const messageSecret = crypto.randomBytes(32)
|
|
824
|
+
|
|
825
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
826
|
+
messageContextInfo: { messageSecret },
|
|
827
|
+
groupStatusMessageV2: {
|
|
828
|
+
message: {
|
|
829
|
+
extendedTextMessage: {
|
|
830
|
+
text: 'Só pra você que é chegado 🤫',
|
|
831
|
+
contextInfo: {
|
|
832
|
+
isGroupStatus: true,
|
|
833
|
+
statusAudienceMetadata: { audienceType: 1 } // 1 = Close Friends
|
|
834
|
+
}
|
|
835
|
+
},
|
|
836
|
+
messageContextInfo: { messageSecret }
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
}, {})
|
|
840
|
+
|
|
841
|
+
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
```js
|
|
845
|
+
// ── Imagem para todos do grupo ───────────────────────────────────────────────
|
|
846
|
+
const messageSecret = crypto.randomBytes(32)
|
|
847
|
+
|
|
848
|
+
const prep = await prepareWAMessageMedia(
|
|
849
|
+
{ image: buffer },
|
|
850
|
+
{ upload: sock.waUploadToServer }
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
854
|
+
messageContextInfo: { messageSecret },
|
|
855
|
+
groupStatusMessageV2: {
|
|
856
|
+
message: {
|
|
857
|
+
imageMessage: {
|
|
858
|
+
...prep.imageMessage,
|
|
859
|
+
caption: 'Legenda da imagem',
|
|
860
|
+
contextInfo: { isGroupStatus: true }
|
|
861
|
+
},
|
|
862
|
+
messageContextInfo: { messageSecret }
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}, {})
|
|
866
|
+
|
|
867
|
+
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
```js
|
|
871
|
+
// ── Imagem apenas para Amigos Próximos ───────────────────────────────────────
|
|
872
|
+
const messageSecret = crypto.randomBytes(32)
|
|
873
|
+
|
|
874
|
+
const prep = await prepareWAMessageMedia(
|
|
875
|
+
{ image: buffer },
|
|
876
|
+
{ upload: sock.waUploadToServer }
|
|
877
|
+
)
|
|
878
|
+
|
|
879
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
880
|
+
messageContextInfo: { messageSecret },
|
|
881
|
+
groupStatusMessageV2: {
|
|
882
|
+
message: {
|
|
883
|
+
imageMessage: {
|
|
884
|
+
...prep.imageMessage,
|
|
885
|
+
caption: 'Só pra chegados 🤫',
|
|
886
|
+
contextInfo: {
|
|
887
|
+
isGroupStatus: true,
|
|
888
|
+
statusAudienceMetadata: { audienceType: 1 }
|
|
889
|
+
}
|
|
890
|
+
},
|
|
891
|
+
messageContextInfo: { messageSecret }
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
}, {})
|
|
895
|
+
|
|
896
|
+
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
```js
|
|
900
|
+
// ── Vídeo ─────────────────────────────────────────────────────────────────────
|
|
901
|
+
const messageSecret = crypto.randomBytes(32)
|
|
902
|
+
|
|
903
|
+
const prep = await prepareWAMessageMedia(
|
|
904
|
+
{ video: buffer },
|
|
905
|
+
{ upload: sock.waUploadToServer }
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
909
|
+
messageContextInfo: { messageSecret },
|
|
910
|
+
groupStatusMessageV2: {
|
|
911
|
+
message: {
|
|
912
|
+
videoMessage: {
|
|
913
|
+
...prep.videoMessage,
|
|
914
|
+
caption: 'Legenda do vídeo',
|
|
915
|
+
contextInfo: { isGroupStatus: true }
|
|
916
|
+
},
|
|
917
|
+
messageContextInfo: { messageSecret }
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}, {})
|
|
921
|
+
|
|
922
|
+
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
```js
|
|
926
|
+
// ── Áudio ─────────────────────────────────────────────────────────────────────
|
|
927
|
+
const messageSecret = crypto.randomBytes(32)
|
|
928
|
+
|
|
929
|
+
const prep = await prepareWAMessageMedia(
|
|
930
|
+
{ audio: buffer, mimetype: 'audio/mp4' },
|
|
931
|
+
{ upload: sock.waUploadToServer }
|
|
932
|
+
)
|
|
933
|
+
|
|
934
|
+
const msg = generateWAMessageFromContent(jid, {
|
|
935
|
+
messageContextInfo: { messageSecret },
|
|
936
|
+
groupStatusMessageV2: {
|
|
937
|
+
message: {
|
|
938
|
+
audioMessage: {
|
|
939
|
+
...prep.audioMessage,
|
|
940
|
+
contextInfo: { isGroupStatus: true }
|
|
941
|
+
},
|
|
942
|
+
messageContextInfo: { messageSecret }
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}, {})
|
|
946
|
+
|
|
947
|
+
await sock.relayMessage(jid, msg.message, { messageId: msg.key.id })
|
|
948
|
+
```
|
|
949
|
+
|
|
950
|
+
**Pontos importantes:**
|
|
951
|
+
- `messageSecret` deve ser um `Buffer` de 32 bytes gerado com `crypto.randomBytes(32)` — cada postagem precisa de um novo secret
|
|
952
|
+
- `audienceType: 1` ativa o modo Close Friends; omitir `statusAudienceMetadata` posta para todos do grupo
|
|
953
|
+
- Para repostar uma mensagem citada (quoted), baixe a mídia com `downloadMediaMessage` antes de fazer o `prepareWAMessageMedia`
|
|
954
|
+
- O `groupStatus: true` no `sendMessage` simples (seção 3) existe como atalho, mas para controle de Close Friends e repost de mídias, use o `groupStatusMessageV2` direto como mostrado acima
|
|
955
|
+
|
|
956
|
+
---
|
|
957
|
+
|
|
891
958
|
## Changelog
|
|
892
959
|
|
|
960
|
+
### v1.0.7
|
|
961
|
+
- **Fix PTT real** — `ptt: true` converte via `ffmpeg` antes do upload; corrigido bug `Cannot use 'in' operator to search for 'stream'` (caminho do arquivo transcrito era passado como string crua em vez de `{ url }`)
|
|
962
|
+
- **Fix 403 / conexão** — `lidDbMigrated` no login agora reflete o estado real das credenciais; companion version usa a versão real do WA; `"forbidden"` mapeado corretamente pra `DisconnectReason.forbidden`
|
|
963
|
+
- **Mensagens agendadas** — `MessageScheduler` / `createMessageScheduler` portado de innovatorssoft/baileys
|
|
964
|
+
- **PreKey recovery melhorado** — upload de 30 chaves (era 5), delay de 2500ms (era 1000ms), parâmetro `force` pra ignorar `MIN_UPLOAD_INTERVAL` em recuperação de erro
|
|
965
|
+
- **Fix groupMetadata → sharedLidPhoneCache** — pares LID↔telefone agora registrados automaticamente ao buscar metadata de grupo
|
|
966
|
+
- Guard de pagamento stealth, Bad MAC Handler, getName, getBestWaVersion, resolveLidPhoneFromGroup, WhatsApp Flows
|
|
967
|
+
|
|
893
968
|
### v1.0.6
|
|
894
|
-
- **Fix PTT real** — `ptt: true` agora transcodifica via `ffmpeg` quando necessário, em vez de só etiquetar o mimetype
|
|
895
|
-
- **Fix groupMetadata → sharedLidPhoneCache** — pares LID↔telefone da lista de participantes de grupo agora são registrados no cache (antes eram descartados)
|
|
896
|
-
- **Fix cleanId de identidade própria** — comparação de `sock.user.id`/`sock.user.lid` com sufixo de dispositivo (`:N`) agora preserva o domínio
|
|
897
|
-
- **Guard de pagamento stealth** — `bindPaymentGuard`, evento `messages.decrypt-failed`
|
|
898
|
-
- **Bad MAC Handler** — `BadMacHandler`, `badMacHandler`
|
|
899
|
-
- **getName** — resolução de nome em cascata, nunca vazia
|
|
900
|
-
- **getBestWaVersion** — busca de versão que não falha em silêncio
|
|
901
|
-
- **resolveLidPhoneFromGroup** — força resolução via metadata de grupo
|
|
902
|
-
- **WhatsApp Flows** — suporte documentado a formulários interativos (`nativeFlowResponseMessage`)
|
|
903
|
-
- **isSystemNotification** — flag em mensagens de notificação de sistema
|
|
904
969
|
- Sistema LID/JID avançado com cache bidirecional (`sharedLidPhoneCache`)
|
|
905
970
|
- `lidToJid`, `resolveJid`, `resolveAll`, `normalizeJid`, `validateJid`, `getSenderInfo`
|
|
906
971
|
- Suporte a `@username` — `isUsername`, `resolveUsername`
|
|
972
|
+
- Botões estendidos (20+ tipos via `nativeFlow`)
|
|
973
|
+
- Fix canal newsletter, fix groupStatus audio, `isSystemNotification`
|
|
907
974
|
|
|
908
975
|
### v1.0.5
|
|
909
|
-
- Sticker Pack nativo com
|
|
910
|
-
- Fix canal newsletter (`extraAttrs` no `plaintext`)
|
|
976
|
+
- Sticker Pack nativo com animadas e PNG
|
|
911
977
|
- Album, Spoiler, ViewOnce V2, Ephemeral, Lottie, Evento
|
|
912
978
|
- Native Flow, Carousel, Template Buttons
|
|
913
979
|
- AI Rich (makeText, makeCode, makeTable, makeList, sendRich)
|
|
@@ -918,7 +984,7 @@ sock.ev.on('messages.upsert', async ({ messages }) => {
|
|
|
918
984
|
|
|
919
985
|
<div align="center">
|
|
920
986
|
|
|
921
|
-
**@systemzero/baileys v1.0.
|
|
987
|
+
**@systemzero/baileys v1.0.7**
|
|
922
988
|
|
|
923
989
|
Desenvolvido por [Josué </>](https://t.me/blackhzx) · [Canal WhatsApp](https://whatsapp.com/channel/0029VaqUb9aGk1FxqeKKOd2i) · [systemzone.store](https://systemzone.store)
|
|
924
990
|
|