@areumtecnologia/wasockets 1.1.5 → 1.1.6

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
@@ -30,6 +30,12 @@ O **WaSockets** foi criado para resolver problemas crônicos de estabilidade e a
30
30
  | **Mídia** | Correções de Miniaturas e Status | Correção de falhas na extração de miniaturas (`extractImageThumb`), na geração de previews de links, no envio de mídias em álbuns/vídeos e na publicação de mídias em status de grupos. |
31
31
  | **Newsletters (Canais)** | Consulta de Canais Inscritos | Adicionado o método exclusivo `newsletterSubscribed` que lista todos os canais de transmissão (Newsletters) aos quais o usuário está inscrito. |
32
32
  | **Mensagens de Negócios** | Botões Interativos & PIX | Suporte avançado para layouts de botões interativos, botões Cards (com imagem/vídeo), suporte para botões PIX estáticos e fluxos completos de checkout e pagamento (`review_and_pay`). |
33
+ | **Recursos Web (v1.1.6)** | Fixação com Duração | Fixação de mensagens com durações configuráveis (24h, 7d, 30d) usando `pinInChatMessage` e `messageAddOnDurationInSecs`. |
34
+ | **Recursos Web (v1.1.6)** | Reação a Status | Método nativo `reactToStatus` para curtir e reagir diretamente a publicações de Status. |
35
+ | **Recursos Web (v1.1.6)** | Eventos & RSVP | Criação, cancelamento (`cancelEvent`) e confirmação de presença (`sendEventResponse`) em eventos de grupo. |
36
+ | **Recursos Web (v1.1.6)** | Favoritos & Notas de Contato | Sincronização via App State de chats favoritos (`updateFavorite`) e anotações internas de contatos (`updateChatNote`). |
37
+ | **Recursos Web (v1.1.6)** | Chamadas Web (Web Calling) | Sinalização completa de chamadas no navegador (`offerCall`, `acceptCall`, `terminateCall`, `rejectCall`). |
38
+ | **Recursos Web (v1.1.6)** | Resolução Reversa de LID | Consulta reversa USync para obter o número de telefone a partir de um identificador `@lid` (`getPnUser`). |
33
39
 
34
40
  ---
35
41
 
@@ -52,8 +58,14 @@ O **WaSockets** foi criado para resolver problemas crônicos de estabilidade e a
52
58
  - [Botões Interativos](#botões-interativos)
53
59
  - [Botão de Pagamento PIX](#botão-de-pagamento-pix)
54
60
  - [Fluxos de Checkout (PAY)](#fluxos-de-checkout-pay)
55
- - [Menção em Status](#menção-em-status)
61
+ - [Menção em Status e Reações](#menção-em-status-e-reações)
62
+ - [Comentários em Canais e Comunidades](#comentários-em-canais-e-comunidades)
56
63
  - [Modificando Mensagens e Chats](#-modificando-mensagens-e-chats)
64
+ - [Fixação de Mensagens com Duração](#fixação-de-mensagens-com-duração)
65
+ - [Favoritos e Anotações de Contato](#favoritos-e-anotações-de-contato)
66
+ - [Eventos em Grupos e RSVP](#-eventos-em-grupos-e-rsvp)
67
+ - [Chamadas Web (Web Calling)](#-chamadas-web-web-calling)
68
+ - [Resolução de Usuários (LID e Telefone)](#-resolução-de-usuários-lid-e-telefone)
57
69
  - [Gerenciamento de Grupos](#-gerenciamento-de-grupos)
58
70
  - [Newsletters (Canais)](#-newsletters-canais)
59
71
  - [Configurações de Privacidade](#-configurações-de-privacidade)
@@ -508,6 +520,22 @@ await sock.sendStatusMentions(
508
520
  },
509
521
  contatosParaMencionar
510
522
  )
523
+
524
+ // Curtir / Reagir diretamente a uma publicação de Status (Stories)
525
+ await sock.reactToStatus(statusMsgKey, '❤️')
526
+ ```
527
+
528
+ ### Comentários em Canais e Comunidades
529
+
530
+ Envie respostas e comentários estruturados em postagens de canais (Newsletters) ou mensagens de aviso em comunidades:
531
+
532
+ ```javascript
533
+ await sock.sendMessage(jid, {
534
+ comment: {
535
+ targetMessageKey: mensagemOriginal.key,
536
+ message: { text: 'Excelente atualização! 👏' }
537
+ }
538
+ })
511
539
  ```
512
540
 
513
541
  ---
@@ -537,10 +565,114 @@ await sock.chatModify({ mute: null }, jid)
537
565
  // Marcar chat como Não Lido
538
566
  await sock.chatModify({ markRead: false, lastMessages: [msg] }, jid)
539
567
 
540
- // Fixar chat na lista
568
+ // Fixar chat na lista de conversas
541
569
  await sock.chatModify({ pin: true }, jid)
542
570
  ```
543
571
 
572
+ ### Fixação de Mensagens com Duração
573
+
574
+ Fixe mensagens em conversas individuais ou grupos escolhendo a duração desejada:
575
+
576
+ ```javascript
577
+ // Fixar mensagem por 7 dias (604800 segundos)
578
+ // Durações padrão: 86400 (24h), 604800 (7 dias), 2592000 (30 dias)
579
+ await sock.sendMessage(jid, {
580
+ pin: {
581
+ key: msg.key,
582
+ type: 1, // 1: Fixar, 2: Desafixar
583
+ time: 604800
584
+ }
585
+ })
586
+
587
+ // Desafixar mensagem
588
+ await sock.sendMessage(jid, {
589
+ pin: {
590
+ key: msg.key,
591
+ type: 2
592
+ }
593
+ })
594
+ ```
595
+
596
+ ### Favoritos e Anotações de Contato
597
+
598
+ Organize suas conversas sincronizando com o WhatsApp Web e celular:
599
+
600
+ ```javascript
601
+ // Marcar conversa como Favorita
602
+ await sock.updateFavorite(jid, true)
603
+
604
+ // Desmarcar conversa como Favorita
605
+ await sock.updateFavorite(jid, false)
606
+
607
+ // Adicionar ou atualizar nota interna no contato (Chat Note)
608
+ await sock.updateChatNote(jid, 'Cliente solicitou proposta para 50 atendentes.')
609
+
610
+ // Remover nota interna do contato
611
+ await sock.removeChatNote(jid)
612
+ ```
613
+
614
+ ---
615
+
616
+ ## 📅 Eventos em Grupos e RSVP
617
+
618
+ Crie eventos colaborativos, responda confirmações de presença e cancele eventos:
619
+
620
+ ```javascript
621
+ // 1. Criar um novo evento no grupo
622
+ const evento = await sock.sendMessage(grupoJid, {
623
+ event: {
624
+ name: 'Reunião de Planejamento de Sprints',
625
+ description: 'Alinhamento estratégico dos projetos da Áreum Tecnologia.',
626
+ startTime: Math.floor(Date.now() / 1000) + 86400, // Amanhã
627
+ extraGuestsAllowed: true
628
+ }
629
+ })
630
+
631
+ // 2. Responder ao Evento (RSVP)
632
+ // Tipos de resposta: 'GOING' (1), 'NOT_GOING' (2), 'MAYBE' (3)
633
+ await sock.sendEventResponse(grupoJid, evento.key, 'GOING', 1 /* +1 convidado extra */)
634
+
635
+ // 3. Cancelar o Evento
636
+ await sock.cancelEvent(grupoJid, evento.key)
637
+ ```
638
+
639
+ ---
640
+
641
+ ## 📞 Chamadas Web (Web Calling)
642
+
643
+ Gerencie o ciclo completo de sinalização de chamadas de voz e vídeo:
644
+
645
+ ```javascript
646
+ // Iniciar uma chamada (áudio ou vídeo)
647
+ const chamada = await sock.offerCall('5511999999999@s.whatsapp.net', false /* isVideo */)
648
+ console.log('ID da chamada:', chamada.id)
649
+
650
+ // Atender uma chamada recebida
651
+ await sock.acceptCall(callId, callerJid)
652
+
653
+ // Encerrar uma chamada em andamento
654
+ await sock.terminateCall(callId, callerJid)
655
+
656
+ // Rejeitar uma chamada recebida
657
+ await sock.rejectCall(callId, callerJid)
658
+ ```
659
+
660
+ ---
661
+
662
+ ## 🔍 Resolução de Usuários (LID e Telefone)
663
+
664
+ Com a evolução da privacidade no WhatsApp, participantes de grupos e canais frequentemente utilizam identificadores `@lid`. O **WaSockets** permite resolução bidirecional:
665
+
666
+ ```javascript
667
+ // Obter o identificador LID a partir de um número de telefone (PN)
668
+ const [lidUser] = await sock.getLidUser('5511999999999@s.whatsapp.net')
669
+ console.log('LID do usuário:', lidUser)
670
+
671
+ // Obter o número de telefone a partir de um @lid recebido em grupo
672
+ const [pnUser] = await sock.getPnUser('12345678901234@lid')
673
+ console.log('Número de telefone real:', pnUser?.phone_number)
674
+ ```
675
+
544
676
  ---
545
677
 
546
678
  ## 🏷️ Etiquetas de Negócio (Labels - WhatsApp Business)
@@ -30,6 +30,7 @@ const {
30
30
  const { makeMutex } = require("../Utils/make-mutex")
31
31
  const {
32
32
  isPnUser,
33
+ isLidUser,
33
34
  getBinaryNodeChild,
34
35
  getBinaryNodeChildren,
35
36
  jidDecode,
@@ -257,6 +258,39 @@ const makeChatsSocket = (config) => {
257
258
  }
258
259
  }
259
260
 
261
+ const getPnUser = async (jid) => {
262
+ if (!jid) {
263
+ throw new Boom('Please input a jid user')
264
+ }
265
+
266
+ if (!isLidUser(jid)) {
267
+ throw new Boom('Invalid JID: Not a LID user!')
268
+ }
269
+
270
+ const targetJid = jidNormalizedUser(jid)
271
+
272
+ const usyncQuery = new USyncQuery()
273
+ usyncQuery.protocols.push({
274
+ name: 'phone_number',
275
+ getQueryElement: () => ({
276
+ tag: 'phone_number',
277
+ attrs: {},
278
+ content: undefined
279
+ }),
280
+ getUserElement: () => null,
281
+ parser: (node) => node.attrs.phone_number || node.attrs.val
282
+ })
283
+ usyncQuery.users.push({
284
+ id: targetJid
285
+ })
286
+
287
+ const result = await suki.executeUSyncQuery(usyncQuery)
288
+
289
+ if (result) {
290
+ return result.list
291
+ }
292
+ }
293
+
260
294
  const fetchStatus = async (...jids) => {
261
295
  const usyncQuery = new USyncQuery().withStatusProtocol()
262
296
 
@@ -914,6 +948,39 @@ const makeChatsSocket = (config) => {
914
948
  }, jid)
915
949
  }
916
950
 
951
+ /**
952
+ * Set or unset a chat as Favorite
953
+ */
954
+ const updateFavorite = (jid, favorite = true) => {
955
+ return chatModify({
956
+ favorite
957
+ }, jid)
958
+ }
959
+
960
+ /**
961
+ * Add or update a note for a chat (contact notes)
962
+ */
963
+ const updateChatNote = (jid, text) => {
964
+ return chatModify({
965
+ note: {
966
+ text,
967
+ deleted: false,
968
+ createdAt: Date.now()
969
+ }
970
+ }, jid)
971
+ }
972
+
973
+ /**
974
+ * Remove note for a chat
975
+ */
976
+ const removeChatNote = (jid) => {
977
+ return chatModify({
978
+ note: {
979
+ deleted: true
980
+ }
981
+ }, jid)
982
+ }
983
+
917
984
  /**
918
985
  * Adds label
919
986
  */
@@ -1257,6 +1324,10 @@ const makeChatsSocket = (config) => {
1257
1324
  appStatePatchMutex,
1258
1325
  notificationMutex,
1259
1326
  getLidUser,
1327
+ getPnUser,
1328
+ updateFavorite,
1329
+ updateChatNote,
1330
+ removeChatNote,
1260
1331
  fetchBlocklist,
1261
1332
  fetchStatus,
1262
1333
  fetchDisappearingDuration,
@@ -302,6 +302,46 @@ const makeMessagesRecvSocket = (config) => {
302
302
  await query(stanza)
303
303
  }
304
304
 
305
+ const acceptCall = async (callId, callFrom) => {
306
+ const stanza = ({
307
+ tag: 'call',
308
+ attrs: {
309
+ from: authState.creds.me.id,
310
+ to: callFrom,
311
+ },
312
+ content: [{
313
+ tag: 'accept',
314
+ attrs: {
315
+ 'call-id': callId,
316
+ 'call-creator': callFrom,
317
+ },
318
+ content: undefined,
319
+ }],
320
+ })
321
+
322
+ await query(stanza)
323
+ }
324
+
325
+ const terminateCall = async (callId, callFrom) => {
326
+ const stanza = ({
327
+ tag: 'call',
328
+ attrs: {
329
+ from: authState.creds.me.id,
330
+ to: callFrom,
331
+ },
332
+ content: [{
333
+ tag: 'terminate',
334
+ attrs: {
335
+ 'call-id': callId,
336
+ 'call-creator': callFrom,
337
+ },
338
+ content: undefined,
339
+ }],
340
+ })
341
+
342
+ await query(stanza)
343
+ }
344
+
305
345
  const sendRetryRequest = async (node, forceIncludeKeys = false) => {
306
346
  const { fullMessage } = decodeMessageNode(node, authState.creds.me.id, authState.creds.me.lid || '')
307
347
  const { key: msgKey } = fullMessage
@@ -1633,6 +1673,8 @@ const makeMessagesRecvSocket = (config) => {
1633
1673
  sendRetryRequest,
1634
1674
  offerCall,
1635
1675
  rejectCall,
1676
+ acceptCall,
1677
+ terminateCall,
1636
1678
  fetchMessageHistory,
1637
1679
  requestPlaceholderResend,
1638
1680
  messageRetryManager
@@ -736,7 +736,7 @@ const makeMessagesSocket = (config) => {
736
736
  return
737
737
  }
738
738
 
739
- if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage) {
739
+ if (messages.pinInChatMessage || messages.keepInChatMessage || message.reactionMessage || message.protocolMessage?.editedMessage || message.encEventResponseMessage || message.commentMessage) {
740
740
  extraAttrs['decrypt-fail'] = 'hide'
741
741
  }
742
742
 
@@ -1140,9 +1140,15 @@ const makeMessagesSocket = (config) => {
1140
1140
  else if (message.reactionMessage) {
1141
1141
  return 'reaction'
1142
1142
  }
1143
- else if (message.eventMessage) {
1143
+ else if (message.eventMessage || message.eventResponseMessage || message.encEventResponseMessage) {
1144
1144
  return 'event'
1145
1145
  }
1146
+ else if (message.pinInChatMessage) {
1147
+ return 'pin'
1148
+ }
1149
+ else if (message.commentMessage) {
1150
+ return 'comment'
1151
+ }
1146
1152
  else if (getMediaType(message)) {
1147
1153
  return 'media'
1148
1154
  }
@@ -1609,6 +1615,61 @@ const makeMessagesSocket = (config) => {
1609
1615
 
1610
1616
  return msg
1611
1617
  },
1618
+ reactToStatus: async (key, reaction = '❤️') => {
1619
+ const author = key.participant || key.remoteJid
1620
+ const targetJid = STORIES_JID
1621
+ return relayMessage(
1622
+ targetJid,
1623
+ {
1624
+ reactionMessage: {
1625
+ key,
1626
+ text: reaction,
1627
+ senderTimestampMs: Date.now()
1628
+ }
1629
+ },
1630
+ {
1631
+ messageId: generateMessageID(authState.creds.me.id),
1632
+ statusJidList: [jidNormalizedUser(author)],
1633
+ additionalNodes: [
1634
+ {
1635
+ tag: 'meta',
1636
+ attrs: {},
1637
+ content: [
1638
+ {
1639
+ tag: 'target',
1640
+ attrs: {
1641
+ chat: targetJid,
1642
+ author: jidNormalizedUser(author)
1643
+ }
1644
+ }
1645
+ ]
1646
+ }
1647
+ ]
1648
+ }
1649
+ )
1650
+ },
1651
+ sendEventResponse: async (jid, eventCreationMessageKey, response, extraGuestCount = 0) => {
1652
+ const responseType = typeof response === 'string'
1653
+ ? (proto.Message.EventResponseMessage?.EventResponseType?.[response] ?? 1)
1654
+ : response
1655
+
1656
+ return sendMessage(jid, {
1657
+ eventResponse: {
1658
+ eventKey: eventCreationMessageKey,
1659
+ response: responseType,
1660
+ extraGuestCount,
1661
+ timestampMs: Date.now()
1662
+ }
1663
+ })
1664
+ },
1665
+ cancelEvent: async (jid, eventKey) => {
1666
+ return sendMessage(jid, {
1667
+ event: {
1668
+ isCanceled: true
1669
+ },
1670
+ edit: eventKey
1671
+ })
1672
+ },
1612
1673
  sendMessage: async (jid, content, options = {}) => {
1613
1674
  const userJid = authState.creds.me.id
1614
1675
  const additionalAttributes = {}
@@ -527,6 +527,38 @@ const chatModificationToAppPatch = (mod, jid) => {
527
527
  }
528
528
  }
529
529
 
530
+ else if ('favorite' in mod) {
531
+ patch = {
532
+ syncAction: {
533
+ favoritesAction: {
534
+ favorites: mod.favorite ? [{ id: jid }] : []
535
+ }
536
+ },
537
+ index: ['favorites', jid],
538
+ type: 'regular_low',
539
+ apiVersion: 1,
540
+ operation: mod.favorite ? OP.SET : OP.REMOVE
541
+ }
542
+ }
543
+
544
+ else if ('note' in mod) {
545
+ patch = {
546
+ syncAction: {
547
+ noteEditAction: {
548
+ type: 1,
549
+ chatJid: jid,
550
+ createdAt: mod.note?.createdAt || Date.now(),
551
+ deleted: !!mod.note?.deleted,
552
+ unstructuredContent: mod.note?.text || ''
553
+ }
554
+ },
555
+ index: ['note_edit', jid],
556
+ type: 'regular_low',
557
+ apiVersion: 1,
558
+ operation: mod.note?.deleted ? OP.REMOVE : OP.SET
559
+ }
560
+ }
561
+
530
562
  else if ('star' in mod) {
531
563
  const key = mod.star.messages[0]
532
564
  patch = {
@@ -814,6 +846,25 @@ const processSyncAction = (syncAction, ev, me, initialSyncOpts, logger) => {
814
846
  }
815
847
  }
816
848
 
849
+ else if (action?.favoritesAction || type === 'favorites') {
850
+ const isFavorite = (action?.favoritesAction?.favorites?.length || 0) > 0
851
+ ev.emit('chats.update', [{
852
+ id,
853
+ favorite: isFavorite,
854
+ conditional: getChatUpdateConditional(id, undefined)
855
+ }])
856
+ }
857
+
858
+ else if (action?.noteEditAction || type === 'note_edit') {
859
+ ev.emit('chats.update', [{
860
+ id: action?.noteEditAction?.chatJid || id,
861
+ notes: action?.noteEditAction?.deleted ? null : {
862
+ text: action?.noteEditAction?.unstructuredContent,
863
+ createdAt: toNumber(action?.noteEditAction?.createdAt)
864
+ }
865
+ }])
866
+ }
867
+
817
868
  else if (action?.labelEditAction) {
818
869
  const { name, color, deleted, predefinedId } = action.labelEditAction
819
870
  ev.emit('labels.edit', {
@@ -622,6 +622,40 @@ const generateWAMessageContent = async (message, options) => {
622
622
  m.keepInChatMessage.key = message.keep.key
623
623
  m.keepInChatMessage.keepType = message.keep?.type || 1
624
624
  m.keepInChatMessage.timestampMs = message.keep?.time || Date.now()
625
+ } else if (hasNonNullishProperty(message, "pin")) {
626
+ m.pinInChatMessage = {
627
+ key: message.pin.key,
628
+ type: typeof message.pin.type === 'number' ? message.pin.type : 1,
629
+ senderTimestampMs: message.pin.senderTimestampMs || Date.now()
630
+ }
631
+
632
+ if (message.pin.time || message.pin.duration) {
633
+ m.messageContextInfo = m.messageContextInfo || {}
634
+ m.messageContextInfo.messageAddOnDurationInSecs = message.pin.time || message.pin.duration
635
+ }
636
+ } else if (hasNonNullishProperty(message, "comment")) {
637
+ m.commentMessage = {
638
+ targetMessageKey: message.comment.targetMessageKey,
639
+ message: await generateWAMessageContent(message.comment.message, options)
640
+ }
641
+ } else if (hasNonNullishProperty(message, "eventResponse")) {
642
+ const responseType = typeof message.eventResponse.response === 'string'
643
+ ? (WAProto.Message.EventResponseMessage?.EventResponseType?.[message.eventResponse.response] || 1)
644
+ : (message.eventResponse.response || 1)
645
+
646
+ m.eventResponseMessage = {
647
+ response: responseType,
648
+ timestampMs: message.eventResponse.timestampMs || Date.now(),
649
+ extraGuestCount: message.eventResponse.extraGuestCount || 0
650
+ }
651
+
652
+ if (message.eventResponse.eventKey) {
653
+ m.messageContextInfo = m.messageContextInfo || {}
654
+ m.messageContextInfo.messageAssociation = {
655
+ associationType: 1,
656
+ parentMessageKey: message.eventResponse.eventKey
657
+ }
658
+ }
625
659
  } else if (hasNonNullishProperty(message, "call")) {
626
660
  m.scheduledCallCreationMessage = {}
627
661
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@areumtecnologia/wasockets",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "WhatsApp API Modification By areumtecnologia",
5
5
  "keywords": [
6
6
  "whatsapp",