@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 CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  # @systemzero/baileys
6
6
 
7
- **v1.0.6** · Fork avançado do Baileys com suporte nativo a todos os tipos de mensagens do WhatsApp
7
+ **v1.0.7** · Fork avançado do Baileys com suporte nativo a todos os tipos de mensagens do WhatsApp
8
8
 
9
9
  [![NPM](https://img.shields.io/npm/v/@systemzero/baileys?color=7c3aed&style=flat-square&label=npm)](https://www.npmjs.com/package/@systemzero/baileys)
10
10
  [![License](https://img.shields.io/badge/license-MIT-6366f1?style=flat-square)](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 e para o sistema anti-fraude de pagamento processar mídia. Confirme com `ffmpeg -encoders | grep opus`.
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 (v1.0.6)](#12-botões-estendidos-v106)
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 de entrada](#19-ptt-real-com-qualquer-formato-de-entrada)
55
- 20. [Guard de pagamento stealth (anti-fraude)](#20-guard-de-pagamento-stealth-anti-fraude)
56
- 21. [Bad MAC Handler — sessões Signal](#21-bad-mac-handler--sessões-signal)
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 interativos](#24-whatsapp-flows--formulários-interativos)
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
- fetchLatestWaWebVersion
73
+ getBestWaVersion
72
74
  } = require('@systemzero/baileys')
73
75
 
74
76
  const { state, saveCreds } = await useMultiFileAuthState('./session')
75
- const { version } = await fetchLatestWaWebVersion()
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 !== DisconnectReason.loggedOut) startBot()
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
- > Veja a seção [23](#23-versão-do-whatsapp-sempre-atualizada) para um jeito mais robusto de buscar a versão, que não falha silenciosamente.
108
+ > Use `getBestWaVersion()` em vez de `fetchLatestWaWebVersion()` ela tenta as duas fontes e 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
- // Link
274
- { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: 'Abrir', url: 'https://systemzone.store' }) }
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 (funciona — confirmado em teste real)
235
+ ### Header com imagem + nativeFlow
295
236
 
296
237
  ```js
297
- const { generateWAMessage, generateMessageIDV2 } = require('@systemzero/baileys')
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
- > Use `caption`, não `text`, quando quiser header com imagem junto de `nativeFlow` — usar `text` faz a lib pular inteiro o bloco que monta o header.
246
+ ### Carousel
309
247
 
310
- ### Template Buttons
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: ['🔥'] }, // animada
344
- { data: bufferPng, emojis: ['✨'] }, // PNG converte auto
285
+ { data: bufferAnima, emojis: ['🔥'] },
286
+ { data: bufferPng, emojis: ['✨'] },
345
287
  ],
346
- name: 'Nome do Pack',
347
- publisher: 'Autor',
348
- description: 'Descrição'
288
+ name: 'Nome do Pack',
289
+ publisher: 'Autor',
349
290
  })
350
291
  ```
351
292
 
352
- > Tanto `cover` quanto cada item de `stickers[].data` esperam **Buffer**, não `{ url }`. Se você baixou as figurinhas de uma API, baixe o buffer primeiro (`axios.get(url, { responseType: 'arraybuffer' })`) antes de montar o objeto.
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
- ], quotedMsg)
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`) — formato baseado em número de telefone
451
- - **LID** (`@lid`) — identificador opaco, não contém o número de telefone
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 baileys resolve automaticamente LID↔JID em mensagens recebidas e na metadata de grupo (ver seção 22), via `sharedLidPhoneCache`.
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
- normalizeJid('5511999999999') // → '5511999999999@s.whatsapp.net'
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
- O cache é populado automaticamente ao processar mensagens **e** ao buscar metadata de grupo (`groupMetadata`) esse segundo ponto foi corrigido na v1.0.6, antes o par LID↔telefone que vem pronto na lista de participantes do grupo era descartado.
386
+ normalizeJid('5511999999999') // '5511999999999@s.whatsapp.net'
387
+ normalizeJid('123456@lid') // → resolve via cache
475
388
 
476
- ```js
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('Entradas no cache:', sharedLidPhoneCache.size)
392
+ console.log('Cache size:', sharedLidPhoneCache.size)
481
393
  ```
482
394
 
483
- > ⚠️ Atenção com sufixo de dispositivo: identidades do **próprio bot** (`sock.user.id` / `sock.user.lid`) costumam vir como `numero:N@dominio` (ex: `558892659041:10@s.whatsapp.net`). Pra comparar com a lista de `participants` (que não tem sufixo), remova só o `:N`, preservando o domínio: `jid.replace(/:\d+(?=@)/, '')` — **não use** `jid.split(':')[0]`, isso corta o domínio inteiro junto.
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') // true
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
- > O socket vai sempre no construtor: `new ButtonV2(sock)`
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
- Capturar clique: `m.body` chega com o id do botão.
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 site', 'https://systemzone.store')
532
- msg.addCopy('📋 Copiar código', 'PROMO2025')
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 um item:')
542
- msg.card(c => c.image('https://...').title('Card 1').text('Descrição').button('Ver', 'c1'))
543
- msg.card(c => c.image('https://...').title('Card 2').text('Descrição').button('Ver', 'c2'))
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', `const baileys = require('@systemzero/baileys')`)
553
- msg.addTable([['Comando', 'Descrição'], ['!menu', 'Abre o menu']])
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 (v1.0.6)
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: '📍 Enviar endereço', address: true }
470
+ { text: '📍 Endereço', address: true }
567
471
  { text: '📡 Localização', location: true }
568
- { text: '🛍️ Ver catálogo', catalog: '5511999999999@s.whatsapp.net' }
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 // ignora notificação de sistema
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, downloadContentFromMessage } = require('@systemzero/baileys')
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
- const {
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
- // Fixar (type: 1 = PIN_FOR_ALL)
691
- await sock.sendMessage(jid, { pin: quotedMsg.key, type: 1 })
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) // contactStore opcional: { [jid]: { name, notify, verifiedName } }
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 de entrada
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
- await sock.sendMessage(jid, {
718
- audio: { url: 'https://qualquer-formato.mp3' }, // mp3, m4a, wav, o que for
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 se a fonte é `.opus`/`.ogg` (pela extensão real, não pelo que você declarar em `mimetype`) e converte quando necessário, pra mono/16kHz a especificação que o player de voz do WhatsApp espera.
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 de detecção */ },
738
- recordEnvelope: (webMessage, isPayment) => { /* seu registro de corroboração */ },
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 seu detector no conteúdo normal |
749
- | Mensagem editada | `messages.update` | Extrai `editedMessage` e roda o mesmo detector |
750
- | Falha de decrypt | `messages.decrypt-failed` | Nunca ignora em silêncio, mesmo sem conteúdo |
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 { BadMacHandler, badMacHandler } = require('@systemzero/baileys')
627
+ const { badMacHandler } = require('@systemzero/baileys')
760
628
 
761
629
  if (badMacHandler.isBadMacError(error)) {
762
- badMacHandler.handleError(error, 'algum-contexto')
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 a resolução de um `@lid` pro telefone real buscando a metadata do grupo (que já entrega o par pronto por participante) — útil quando o cache ainda não tem aquele par.
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
- > ⚠️ WhatsApp Flows é um recurso de **WhatsApp Business API**, normalmente exigindo um `flow_id` aprovado pela Meta pra sua conta/app específico. O exemplo abaixo é o que está em uso real no System Zero — funciona pro `flow_id` configurado nessa conta, mas **não é garantido que o mesmo `flow_id` funcione pra qualquer outra conta**. Pra criar seu próprio Flow, você precisa cadastrar e aprovar ele no Meta Business Manager.
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: 'Formulário de teste\n\nPreencha seus dados.' },
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: 'SEU_FLOW_ID_AQUI',
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
- well_version: 'V700',
830
- flow_cta: '__localize:FLOWS_SIGN_UP_BUTTON_TITLE',
831
- flow_action: 'navigate',
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
- if (m.message?.interactiveResponseMessage) {
853
- const nfr = m.message.interactiveResponseMessage.nativeFlowResponseMessage
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
- // roteia por flow_id — adicione mais formulários aqui
877
- if (flowId === 'SEU_FLOW_ID_AQUI') {
878
- let resposta = 'Formulário recebido!\n\n'
879
- if (campos.full_name) resposta += `Nome: ${campos.full_name}\n`
880
- if (campos.phone_number) resposta += `Telefone: +${campos.phone_number}\n`
881
- if (campos.email) resposta += `Email: ${campos.email}\n`
882
- await sock.sendMessage(m.key.remoteJid, { text: resposta }, { quoted: m })
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 suporte a animadas e PNG
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.6**
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