@systemzero/baileys 1.0.5 → 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.
Files changed (206) hide show
  1. package/README.md +629 -658
  2. package/lib/Defaults/index.js +2 -2
  3. package/lib/Socket/chats.js +12 -1
  4. package/lib/Socket/groups.js +13 -4
  5. package/lib/Socket/messages-recv.js +45 -6
  6. package/lib/Socket/messages-recv.js.bak +1273 -0
  7. package/lib/Socket/messages-send.js +1 -0
  8. package/lib/Socket/socket.js +4 -4
  9. package/lib/Utils/bad-mac-handler.js +158 -0
  10. package/lib/Utils/generics.js +2 -1
  11. package/lib/Utils/get-best-version.js +41 -0
  12. package/lib/Utils/get-name.d.ts +1 -0
  13. package/lib/Utils/get-name.js +54 -0
  14. package/lib/Utils/group-status-detection.js +113 -0
  15. package/lib/Utils/index.js +8 -0
  16. package/lib/Utils/logger.js +4 -1
  17. package/lib/Utils/messages-media.js +38 -0
  18. package/lib/Utils/messages.js +113 -7
  19. package/lib/Utils/payment-detection.js +212 -0
  20. package/lib/Utils/payment-guard.d.ts +15 -0
  21. package/lib/Utils/payment-guard.js +142 -0
  22. package/lib/Utils/resolve-lid-phone.js +30 -0
  23. package/lib/Utils/scheduling.js +138 -0
  24. package/lib/Utils/validate-connection.js +11 -5
  25. package/lib/WABinary/jid-utils.js +245 -1
  26. package/package.json +1 -1
  27. package/lib/Defaults/index.d.ts.map +0 -1
  28. package/lib/Defaults/index.js.map +0 -1
  29. package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
  30. package/lib/Signal/Group/ciphertext-message.js.map +0 -1
  31. package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
  32. package/lib/Signal/Group/group-session-builder.js.map +0 -1
  33. package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
  34. package/lib/Signal/Group/group_cipher.js.map +0 -1
  35. package/lib/Signal/Group/index.d.ts.map +0 -1
  36. package/lib/Signal/Group/index.js.map +0 -1
  37. package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
  38. package/lib/Signal/Group/keyhelper.js.map +0 -1
  39. package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
  40. package/lib/Signal/Group/sender-chain-key.js.map +0 -1
  41. package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
  42. package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
  43. package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
  44. package/lib/Signal/Group/sender-key-message.js.map +0 -1
  45. package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
  46. package/lib/Signal/Group/sender-key-name.js.map +0 -1
  47. package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
  48. package/lib/Signal/Group/sender-key-record.js.map +0 -1
  49. package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
  50. package/lib/Signal/Group/sender-key-state.js.map +0 -1
  51. package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
  52. package/lib/Signal/Group/sender-message-key.js.map +0 -1
  53. package/lib/Signal/libsignal.d.ts.map +0 -1
  54. package/lib/Signal/libsignal.js.map +0 -1
  55. package/lib/Signal/lid-mapping.d.ts.map +0 -1
  56. package/lib/Signal/lid-mapping.js.map +0 -1
  57. package/lib/Socket/Client/index.d.ts.map +0 -1
  58. package/lib/Socket/Client/index.js.map +0 -1
  59. package/lib/Socket/Client/types.d.ts.map +0 -1
  60. package/lib/Socket/Client/types.js.map +0 -1
  61. package/lib/Socket/Client/websocket.d.ts.map +0 -1
  62. package/lib/Socket/Client/websocket.js.map +0 -1
  63. package/lib/Socket/business.d.ts.map +0 -1
  64. package/lib/Socket/business.js.map +0 -1
  65. package/lib/Socket/chats.d.ts.map +0 -1
  66. package/lib/Socket/chats.js.map +0 -1
  67. package/lib/Socket/communities.d.ts.map +0 -1
  68. package/lib/Socket/communities.js.map +0 -1
  69. package/lib/Socket/groups.d.ts.map +0 -1
  70. package/lib/Socket/groups.js.map +0 -1
  71. package/lib/Socket/index.d.ts.map +0 -1
  72. package/lib/Socket/index.js.map +0 -1
  73. package/lib/Socket/messages-recv.d.ts.map +0 -1
  74. package/lib/Socket/messages-recv.js.map +0 -1
  75. package/lib/Socket/messages-send.d.ts.map +0 -1
  76. package/lib/Socket/messages-send.js.map +0 -1
  77. package/lib/Socket/mex.d.ts.map +0 -1
  78. package/lib/Socket/mex.js.map +0 -1
  79. package/lib/Socket/newsletter.d.ts.map +0 -1
  80. package/lib/Socket/newsletter.js.map +0 -1
  81. package/lib/Socket/socket.d.ts.map +0 -1
  82. package/lib/Socket/socket.js.map +0 -1
  83. package/lib/Types/Auth.d.ts.map +0 -1
  84. package/lib/Types/Auth.js.map +0 -1
  85. package/lib/Types/Bussines.d.ts.map +0 -1
  86. package/lib/Types/Bussines.js.map +0 -1
  87. package/lib/Types/Call.d.ts.map +0 -1
  88. package/lib/Types/Call.js.map +0 -1
  89. package/lib/Types/Chat.d.ts.map +0 -1
  90. package/lib/Types/Chat.js.map +0 -1
  91. package/lib/Types/Contact.d.ts.map +0 -1
  92. package/lib/Types/Contact.js.map +0 -1
  93. package/lib/Types/Events.d.ts.map +0 -1
  94. package/lib/Types/Events.js.map +0 -1
  95. package/lib/Types/GroupMetadata.d.ts.map +0 -1
  96. package/lib/Types/GroupMetadata.js.map +0 -1
  97. package/lib/Types/Label.d.ts.map +0 -1
  98. package/lib/Types/Label.js.map +0 -1
  99. package/lib/Types/LabelAssociation.d.ts.map +0 -1
  100. package/lib/Types/LabelAssociation.js.map +0 -1
  101. package/lib/Types/Message.d.ts.map +0 -1
  102. package/lib/Types/Message.js.map +0 -1
  103. package/lib/Types/Newsletter.d.ts.map +0 -1
  104. package/lib/Types/Newsletter.js.map +0 -1
  105. package/lib/Types/Product.d.ts.map +0 -1
  106. package/lib/Types/Product.js.map +0 -1
  107. package/lib/Types/Signal.d.ts.map +0 -1
  108. package/lib/Types/Signal.js.map +0 -1
  109. package/lib/Types/Socket.d.ts.map +0 -1
  110. package/lib/Types/Socket.js.map +0 -1
  111. package/lib/Types/State.d.ts.map +0 -1
  112. package/lib/Types/State.js.map +0 -1
  113. package/lib/Types/USync.d.ts.map +0 -1
  114. package/lib/Types/USync.js.map +0 -1
  115. package/lib/Types/index.d.ts.map +0 -1
  116. package/lib/Types/index.js.map +0 -1
  117. package/lib/Utils/auth-utils.d.ts.map +0 -1
  118. package/lib/Utils/auth-utils.js.map +0 -1
  119. package/lib/Utils/browser-utils.d.ts.map +0 -1
  120. package/lib/Utils/browser-utils.js.map +0 -1
  121. package/lib/Utils/business.d.ts.map +0 -1
  122. package/lib/Utils/business.js.map +0 -1
  123. package/lib/Utils/chat-utils.d.ts.map +0 -1
  124. package/lib/Utils/chat-utils.js.map +0 -1
  125. package/lib/Utils/crypto.d.ts.map +0 -1
  126. package/lib/Utils/crypto.js.map +0 -1
  127. package/lib/Utils/decode-wa-message.d.ts.map +0 -1
  128. package/lib/Utils/decode-wa-message.js.map +0 -1
  129. package/lib/Utils/event-buffer.d.ts.map +0 -1
  130. package/lib/Utils/event-buffer.js.map +0 -1
  131. package/lib/Utils/generics.d.ts.map +0 -1
  132. package/lib/Utils/generics.js.map +0 -1
  133. package/lib/Utils/history.d.ts.map +0 -1
  134. package/lib/Utils/history.js.map +0 -1
  135. package/lib/Utils/index.d.ts.map +0 -1
  136. package/lib/Utils/index.js.map +0 -1
  137. package/lib/Utils/link-preview.d.ts.map +0 -1
  138. package/lib/Utils/link-preview.js.map +0 -1
  139. package/lib/Utils/logger.d.ts.map +0 -1
  140. package/lib/Utils/logger.js.map +0 -1
  141. package/lib/Utils/lt-hash.d.ts.map +0 -1
  142. package/lib/Utils/lt-hash.js.map +0 -1
  143. package/lib/Utils/make-mutex.d.ts.map +0 -1
  144. package/lib/Utils/make-mutex.js.map +0 -1
  145. package/lib/Utils/message-retry-manager.d.ts.map +0 -1
  146. package/lib/Utils/message-retry-manager.js.map +0 -1
  147. package/lib/Utils/messages-media.d.ts.map +0 -1
  148. package/lib/Utils/messages-media.js.map +0 -1
  149. package/lib/Utils/messages.d.ts.map +0 -1
  150. package/lib/Utils/messages.js.map +0 -1
  151. package/lib/Utils/noise-handler.d.ts.map +0 -1
  152. package/lib/Utils/noise-handler.js.map +0 -1
  153. package/lib/Utils/pre-key-manager.d.ts.map +0 -1
  154. package/lib/Utils/pre-key-manager.js.map +0 -1
  155. package/lib/Utils/process-message.d.ts.map +0 -1
  156. package/lib/Utils/process-message.js.map +0 -1
  157. package/lib/Utils/signal.d.ts.map +0 -1
  158. package/lib/Utils/signal.js.map +0 -1
  159. package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
  160. package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
  161. package/lib/Utils/validate-connection.d.ts.map +0 -1
  162. package/lib/Utils/validate-connection.js.map +0 -1
  163. package/lib/WABinary/constants.d.ts.map +0 -1
  164. package/lib/WABinary/constants.js.map +0 -1
  165. package/lib/WABinary/decode.d.ts.map +0 -1
  166. package/lib/WABinary/decode.js.map +0 -1
  167. package/lib/WABinary/encode.d.ts.map +0 -1
  168. package/lib/WABinary/encode.js.map +0 -1
  169. package/lib/WABinary/generic-utils.d.ts.map +0 -1
  170. package/lib/WABinary/generic-utils.js.map +0 -1
  171. package/lib/WABinary/index.d.ts.map +0 -1
  172. package/lib/WABinary/index.js.map +0 -1
  173. package/lib/WABinary/jid-utils.d.ts.map +0 -1
  174. package/lib/WABinary/jid-utils.js.map +0 -1
  175. package/lib/WABinary/types.d.ts.map +0 -1
  176. package/lib/WABinary/types.js.map +0 -1
  177. package/lib/WAM/BinaryInfo.d.ts.map +0 -1
  178. package/lib/WAM/BinaryInfo.js.map +0 -1
  179. package/lib/WAM/constants.d.ts.map +0 -1
  180. package/lib/WAM/constants.js.map +0 -1
  181. package/lib/WAM/encode.d.ts.map +0 -1
  182. package/lib/WAM/encode.js.map +0 -1
  183. package/lib/WAM/index.d.ts.map +0 -1
  184. package/lib/WAM/index.js.map +0 -1
  185. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
  186. package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
  187. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
  188. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
  189. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
  190. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
  191. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
  192. package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
  193. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
  194. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
  195. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
  196. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
  197. package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
  198. package/lib/WAUSync/Protocols/index.js.map +0 -1
  199. package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
  200. package/lib/WAUSync/USyncQuery.js.map +0 -1
  201. package/lib/WAUSync/USyncUser.d.ts.map +0 -1
  202. package/lib/WAUSync/USyncUser.js.map +0 -1
  203. package/lib/WAUSync/index.d.ts.map +0 -1
  204. package/lib/WAUSync/index.js.map +0 -1
  205. package/lib/index.d.ts.map +0 -1
  206. package/lib/index.js.map +0 -1
@@ -8,7 +8,7 @@ import { WAMessageStatus, WAProto, ButtonHeaderType, ButtonType, CarouselCardTyp
8
8
  import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser, isLidUser, isPnUser } from '../WABinary/index.js';
9
9
  import { sha256 } from './crypto.js';
10
10
  import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
11
- import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, getStream, toBuffer, getImageProcessingLibrary } from './messages-media.js';
11
+ import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, getStream, toBuffer, getImageProcessingLibrary, transcodeAudioToOpus } from './messages-media.js';
12
12
  const MIMETYPE_MAP = {
13
13
  image: 'image/jpeg',
14
14
  video: 'video/mp4',
@@ -77,9 +77,32 @@ export const prepareWAMessageMedia = async (message, options) => {
77
77
  }
78
78
  if (!uploadData.mimetype) {
79
79
  uploadData.mimetype = MIMETYPE_MAP[mediaType];
80
- } else if (mediaType === 'audio' && uploadData.mimetype === 'audio/ogg; codecs=opus') {
81
- uploadData.mimetype = 'audio/mpeg';
82
80
  }
81
+ // ptt: true → transcodifica de verdade pra opus/ogg mono 16kHz.
82
+ // Antes, isso só trocava o RÓTULO do mimetype sem mudar os bytes reais,
83
+ // o que fazia o WhatsApp mostrar "algo errado com o arquivo de áudio"
84
+ // pra qualquer entrada que não já fosse opus puro.
85
+ //
86
+ // IMPORTANTE: não confia no `mimetype` que o CALLER declarou (a pessoa
87
+ // pode escrever 'audio/ogg' mesmo passando um mp3/m4a de origem) — só
88
+ // confia na extensão real do arquivo/URL de origem.
89
+ let pttTranscodedPath;
90
+ if (mediaType === 'audio' && uploadData.ptt === true) {
91
+ const mediaUrl = typeof uploadData.media === 'string'
92
+ ? uploadData.media
93
+ : uploadData.media?.url?.toString?.() || '';
94
+ const sourceLooksLikeOpus = /\.(opus|ogg)(\?|$)/i.test(mediaUrl);
95
+
96
+ if (!sourceLooksLikeOpus) {
97
+ const sourceForTranscode = Buffer.isBuffer(uploadData.media)
98
+ ? uploadData.media
99
+ : (typeof uploadData.media === 'string' ? uploadData.media : mediaUrl);
100
+ pttTranscodedPath = await transcodeAudioToOpus(sourceForTranscode);
101
+ uploadData.media = { url: pttTranscodedPath };
102
+ }
103
+ uploadData.mimetype = 'audio/ogg; codecs=opus';
104
+ }
105
+
83
106
  if (cacheableKey) {
84
107
  const mediaBuff = await options.mediaCache.get(cacheableKey);
85
108
  if (mediaBuff) {
@@ -118,6 +141,10 @@ if (!uploadData.mimetype) {
118
141
  if (obj.stickerMessage) {
119
142
  obj.stickerMessage.stickerSentTs = Date.now();
120
143
  }
144
+ // lottie: stickerSentTs também no stickerMessage interno
145
+ if (obj.lottieStickerMessage?.message?.stickerMessage) {
146
+ obj.lottieStickerMessage.message.stickerMessage.stickerSentTs = Date.now();
147
+ }
121
148
  if (cacheableKey) {
122
149
  logger?.debug({ cacheableKey }, 'set cache');
123
150
  await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
@@ -126,10 +153,10 @@ if (!uploadData.mimetype) {
126
153
  }
127
154
  const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
128
155
  const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
129
- const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
156
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
130
157
  const requiresPlaybackSpeed = mediaType === 'audio' && uploadData.ptt === true;
131
158
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
132
- const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
159
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
133
160
  const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
134
161
  logger,
135
162
  saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
@@ -188,6 +215,9 @@ if (!uploadData.mimetype) {
188
215
  if (originalFilePath) {
189
216
  await fs.unlink(originalFilePath);
190
217
  }
218
+ if (pttTranscodedPath) {
219
+ await fs.unlink(pttTranscodedPath);
220
+ }
191
221
  logger?.debug('removed tmp files');
192
222
  }
193
223
  catch (error) {
@@ -472,6 +502,70 @@ const prepareNativeFlowButtons = (message) => {
472
502
  } else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
473
503
  return { name: 'single_select', buttonParamsJson: JSON.stringify({ title: buttonText || '📋 Select', sections: button.sections, icon: buttonIcon }) };
474
504
  }
505
+ // ── Botões estendidos (vanzxy) ──────────────────────────────────────
506
+ else if (hasOptionalProperty(button, 'reminder') && !!button.reminder) {
507
+ return { name: 'cta_reminder', buttonParamsJson: JSON.stringify({ display_text: buttonText || '⏰ Remind me', id: button.id || button.reminder, icon: buttonIcon }) };
508
+ }
509
+ else if (hasOptionalProperty(button, 'cancelReminder') && !!button.cancelReminder) {
510
+ return { name: 'cta_cancel_reminder', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🔕 Cancel reminder', id: button.id || button.cancelReminder, icon: buttonIcon }) };
511
+ }
512
+ else if (hasOptionalProperty(button, 'address') && !!button.address) {
513
+ return { name: 'address_message', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📍 Send address', id: button.id || 'address_message' }) };
514
+ }
515
+ else if (hasOptionalProperty(button, 'location') && button.location === true) {
516
+ return { name: 'send_location', buttonParamsJson: JSON.stringify({}) };
517
+ }
518
+ else if (hasOptionalProperty(button, 'catalog') && !!button.catalog) {
519
+ return { name: 'catalog_message', buttonParamsJson: JSON.stringify({ business_phone_number: button.catalog.bizJid || button.catalog, display_text: buttonText || '🛍️ View catalog', id: button.id || 'catalog_message' }) };
520
+ }
521
+ else if (hasOptionalProperty(button, 'products') && !!button.products) {
522
+ return { name: 'mpm', buttonParamsJson: JSON.stringify({ business_phone_number: button.bizJid || button.products.bizJid, product_ids: Array.isArray(button.products) ? button.products : button.products.ids }) };
523
+ }
524
+ else if (hasOptionalProperty(button, 'otp') && !!button.otp) {
525
+ return { name: 'otp_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Copy code', code: button.otp }) };
526
+ }
527
+ else if (hasOptionalProperty(button, 'oneTapOtp') && !!button.oneTapOtp) {
528
+ return { name: 'authentication_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Autofill', code: button.oneTapOtp }) };
529
+ }
530
+ else if (hasOptionalProperty(button, 'phoneNumber') && !!button.phoneNumber) {
531
+ return { name: 'call_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📞 Call', phone_number: button.phoneNumber }) };
532
+ }
533
+ else if (hasOptionalProperty(button, 'urlBtn') && !!button.urlBtn) {
534
+ return { name: 'url_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🌐 Open', url: button.urlBtn }) };
535
+ }
536
+ else if (hasOptionalProperty(button, 'card') && !!button.card) {
537
+ return { name: 'card_message', buttonParamsJson: JSON.stringify(button.card) };
538
+ }
539
+ else if (hasOptionalProperty(button, 'orderDetails') && !!button.orderDetails) {
540
+ return { name: 'order_details', buttonParamsJson: JSON.stringify(button.orderDetails) };
541
+ }
542
+ else if (hasOptionalProperty(button, 'orderStatus') && !!button.orderStatus) {
543
+ return { name: 'order_status', buttonParamsJson: JSON.stringify(button.orderStatus) };
544
+ }
545
+ else if (hasOptionalProperty(button, 'trackOrder') && !!button.trackOrder) {
546
+ return { name: 'track_order', buttonParamsJson: JSON.stringify({ id: button.id || button.trackOrder, display_text: buttonText || '🚚 Track order' }) };
547
+ }
548
+ else if (hasOptionalProperty(button, 'reorder') && !!button.reorder) {
549
+ return { name: 'reorder', buttonParamsJson: JSON.stringify({ id: button.id || button.reorder, display_text: buttonText || '🔁 Reorder' }) };
550
+ }
551
+ else if (hasOptionalProperty(button, 'cancelOrder') && !!button.cancelOrder) {
552
+ return { name: 'cancel_order', buttonParamsJson: JSON.stringify({ id: button.id || button.cancelOrder, display_text: buttonText || '❌ Cancel order' }) };
553
+ }
554
+ else if (hasOptionalProperty(button, 'clearChat') && button.clearChat === true) {
555
+ return { name: 'clear_chat', buttonParamsJson: JSON.stringify({}) };
556
+ }
557
+ else if (hasOptionalProperty(button, 'screen') && !!button.screen) {
558
+ return { name: 'navigateToScreen', buttonParamsJson: JSON.stringify({ screen_name: button.screen, data: button.data || {} }) };
559
+ }
560
+ else if (hasOptionalProperty(button, 'flow') && !!button.flow) {
561
+ return { name: 'flow_action', buttonParamsJson: JSON.stringify({ flow_message_version: button.flow.version || '3', flow_id: button.flow.id, flow_cta: buttonText || button.flow.cta || 'Continue', flow_action: button.flow.action || 'navigate', flow_action_payload: button.flow.actionPayload || { screen: button.flow.screen || 'WELCOME', data: button.flow.data || {} } }) };
562
+ }
563
+ else if (hasOptionalProperty(button, 'voiceCall') && !!button.voiceCall) {
564
+ return { name: 'voice_call', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📞 Voice call', id: button.id || button.voiceCall }) };
565
+ }
566
+ else if (hasOptionalProperty(button, 'videoCall') && !!button.videoCall) {
567
+ return { name: 'video_call_button', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🎥 Video call', id: button.id || button.videoCall }) };
568
+ }
475
569
  return button;
476
570
  }),
477
571
  messageParamsJson: JSON.stringify(messageParamsJson)
@@ -936,7 +1030,12 @@ export const generateWAMessageContent = async (message, options) => {
936
1030
  const key = m[messageType];
937
1031
  if (key && 'contextInfo' in key && !!key.contextInfo) key.contextInfo.isGroupStatus = message.groupStatus;
938
1032
  else if (key) key.contextInfo = { isGroupStatus: message.groupStatus };
939
- m = { groupStatusMessageV2: { message: m } };
1033
+ // Fix: audio PTT usa groupStatusMessage (v1) V2 causa tela preta em versões antigas do WA
1034
+ if (messageType === 'audioMessage') {
1035
+ m = { groupStatusMessage: { message: m } };
1036
+ } else {
1037
+ m = { groupStatusMessageV2: { message: m } };
1038
+ }
940
1039
  delete message.groupStatus;
941
1040
  }
942
1041
  // ── spoiler ───────────────────────────────────────────────────────────────
@@ -961,6 +1060,12 @@ export const generateWAMessageContent = async (message, options) => {
961
1060
  }
962
1061
  // ── isLottie ──────────────────────────────────────────────────────────────
963
1062
  if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
1063
+ // marca o stickerMessage interno com isLottie: true para o WA renderizar como premium
1064
+ if (m.stickerMessage) {
1065
+ m.stickerMessage.isLottie = true;
1066
+ m.stickerMessage.isAnimated = m.stickerMessage.isAnimated ?? false;
1067
+ m.stickerMessage.isAiSticker = false;
1068
+ }
964
1069
  m = { lottieStickerMessage: { message: m } };
965
1070
  }
966
1071
  // ── viewOnceV2 / viewOnceV2Extension ──────────────────────────────────────
@@ -1094,7 +1199,8 @@ export const normalizeMessageContent = (content) => {
1094
1199
  message?.documentWithCaptionMessage ||
1095
1200
  message?.viewOnceMessageV2 ||
1096
1201
  message?.viewOnceMessageV2Extension ||
1097
- message?.editedMessage);
1202
+ message?.editedMessage ||
1203
+ message?.lottieStickerMessage);
1098
1204
  }
1099
1205
  };
1100
1206
  export const extractMessageContent = (content) => {
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Detecção de mensagens de pagamento — incluindo cobranças STEALTH
3
+ * (ocultas para admins, indecifráveis pelo bot).
4
+ *
5
+ * @author Dev Gui (original), integrado na lib para System Zero. By Josué </>
6
+ */
7
+
8
+ const MAX_PAYMENT_SCAN_DEPTH = 8;
9
+
10
+ const PAYMENT_MESSAGE_KEYS = new Set([
11
+ 'paymentInviteMessage',
12
+ 'requestPaymentMessage',
13
+ 'sendPaymentMessage',
14
+ 'cancelPaymentRequestMessage',
15
+ 'declinePaymentRequestMessage',
16
+ ]);
17
+
18
+ function canScanObject(value) {
19
+ return value && typeof value === 'object' && !(value instanceof ArrayBuffer) && !ArrayBuffer.isView(value);
20
+ }
21
+
22
+ function hasPaymentMessageKey(value, depth = 0, seenObjects = new WeakSet()) {
23
+ if (!canScanObject(value) || depth > MAX_PAYMENT_SCAN_DEPTH || seenObjects.has(value)) {
24
+ return false;
25
+ }
26
+
27
+ seenObjects.add(value);
28
+
29
+ for (const [key, childValue] of Object.entries(value)) {
30
+ if (key === 'quotedMessage') continue;
31
+
32
+ if (PAYMENT_MESSAGE_KEYS.has(key) && canScanObject(childValue)) {
33
+ return true;
34
+ }
35
+
36
+ if (hasPaymentMessageKey(childValue, depth + 1, seenObjects)) {
37
+ return true;
38
+ }
39
+ }
40
+
41
+ return false;
42
+ }
43
+
44
+ /** Verifica se a webMessage contém uma mensagem de pagamento — visível ou stealth. */
45
+ export function hasPaymentMessage(webMessage) {
46
+ return hasPaymentMessageKey(webMessage?.message);
47
+ }
48
+
49
+ function findQuotedPaymentContext(value, depth = 0, seenObjects = new WeakSet()) {
50
+ if (!canScanObject(value) || depth > MAX_PAYMENT_SCAN_DEPTH || seenObjects.has(value)) {
51
+ return null;
52
+ }
53
+
54
+ seenObjects.add(value);
55
+
56
+ const contextInfo = value.contextInfo;
57
+
58
+ if (
59
+ canScanObject(contextInfo) &&
60
+ typeof contextInfo.participant === 'string' &&
61
+ canScanObject(contextInfo.quotedMessage) &&
62
+ hasPaymentMessageKey(contextInfo.quotedMessage)
63
+ ) {
64
+ return {
65
+ participant: contextInfo.participant,
66
+ stanzaId: typeof contextInfo.stanzaId === 'string' ? contextInfo.stanzaId : undefined,
67
+ };
68
+ }
69
+
70
+ for (const [key, childValue] of Object.entries(value)) {
71
+ if (key === 'quotedMessage') continue;
72
+ const found = findQuotedPaymentContext(childValue, depth + 1, seenObjects);
73
+ if (found) return found;
74
+ }
75
+
76
+ return null;
77
+ }
78
+
79
+ /**
80
+ * Lê a marcação (quoted) de uma mensagem de pagamento — inclusive as enviadas
81
+ * de forma oculta para admins — e devolve o autor original e o id da
82
+ * mensagem citada. Ignora citações aninhadas para não atribuir o pagamento errado.
83
+ */
84
+ export function getQuotedPaymentContext(webMessage) {
85
+ return findQuotedPaymentContext(webMessage?.message);
86
+ }
87
+
88
+ // ─── Registro de envelopes (corroboração anti-forja) ──────────────────────
89
+
90
+ const ENVELOPE_TTL_MS = 60 * 60 * 1000; // 1 hora
91
+ const MAX_ENTRIES_PER_GROUP = 1000;
92
+
93
+ // registry: Map<groupJid, Map<msgId, {participant, contentState, stealth, ts}>>
94
+ const registry = new Map();
95
+
96
+ const NON_CONTENT_KEYS = new Set(['messageContextInfo', 'senderKeyDistributionMessage']);
97
+
98
+ function hasReadableContent(message) {
99
+ if (!message || typeof message !== 'object') return false;
100
+ return Object.keys(message).some((key) => !NON_CONTENT_KEYS.has(key));
101
+ }
102
+
103
+ function pruneGroup(groupMap) {
104
+ const now = Date.now();
105
+
106
+ for (const [id, entry] of groupMap) {
107
+ if (now - entry.ts > ENVELOPE_TTL_MS) {
108
+ groupMap.delete(id);
109
+ }
110
+ }
111
+
112
+ while (groupMap.size > MAX_ENTRIES_PER_GROUP) {
113
+ const oldestKey = groupMap.keys().next().value;
114
+ if (oldestKey === undefined) break;
115
+ groupMap.delete(oldestKey);
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Registra o envelope de uma mensagem de grupo. `isPayment` é calculado pelo
121
+ * chamador (que já roda a detecção de pagamento) para evitar import cíclico.
122
+ */
123
+ export function recordMessageEnvelope(webMessage, isPayment) {
124
+ const remoteJid = webMessage?.key?.remoteJid;
125
+ const id = webMessage?.key?.id;
126
+ const participant = webMessage?.key?.participant;
127
+
128
+ if (!remoteJid?.endsWith('@g.us') || !id || !participant) return;
129
+
130
+ let groupMap = registry.get(remoteJid);
131
+ if (!groupMap) {
132
+ groupMap = new Map();
133
+ registry.set(remoteJid, groupMap);
134
+ }
135
+
136
+ const contentState = isPayment ? 'payment' : hasReadableContent(webMessage?.message) ? 'other' : 'unreadable';
137
+
138
+ groupMap.set(id, {
139
+ participant,
140
+ contentState,
141
+ stealth: Boolean(webMessage?.stealthMeta),
142
+ ts: Date.now(),
143
+ });
144
+
145
+ pruneGroup(groupMap);
146
+ }
147
+
148
+ /**
149
+ * Decide se uma marcação de pagamento é confiável o bastante para punir o autor.
150
+ *
151
+ * - corroborated: o bot recebeu a mensagem original, do MESMO autor, e ela era
152
+ * pagamento OU foi indecifrável (consistente com o truque stealth). Pode punir.
153
+ * - contradicted: o bot recebeu a mensagem, mas de OUTRO autor, ou ela era
154
+ * conteúdo legível que não era pagamento. É forja. Nunca punir.
155
+ * - nenhum dos dois: o bot não viu a mensagem original. Sem corroboração, não pune.
156
+ */
157
+ export function verifyQuotedAuthor({ groupJid, stanzaId, participant }) {
158
+ if (!stanzaId) return { corroborated: false, contradicted: false };
159
+
160
+ const entry = registry.get(groupJid)?.get(stanzaId);
161
+ if (!entry) return { corroborated: false, contradicted: false };
162
+
163
+ if (entry.participant !== participant) {
164
+ return { corroborated: false, contradicted: true };
165
+ }
166
+
167
+ if (entry.contentState === 'other') {
168
+ return { corroborated: false, contradicted: true };
169
+ }
170
+
171
+ return { corroborated: true, contradicted: false };
172
+ }
173
+
174
+ /** Apenas para testes: limpa o registro. */
175
+ export function __clearEnvelopeRegistry() {
176
+ registry.clear();
177
+ }
178
+
179
+ // ─── Extração de valor monetário ───────────────────────────────────────────
180
+
181
+ /**
182
+ * Extrai o valor monetário de um requestPaymentMessage / sendPaymentMessage.
183
+ * Retorna { value, currencyCode } ou null se não encontrar.
184
+ */
185
+ export function extractPaymentAmount(webMessage) {
186
+ try {
187
+ const msg =
188
+ webMessage?.message?.requestPaymentMessage ||
189
+ webMessage?.message?.sendPaymentMessage ||
190
+ webMessage?.message?.paymentInviteMessage;
191
+
192
+ if (!msg) return null;
193
+
194
+ if (msg.amount && msg.amount.value !== undefined) {
195
+ const raw = typeof msg.amount.value === 'object' ? msg.amount.value.low ?? 0 : Number(msg.amount.value);
196
+ const offset = msg.amount.offset || 100;
197
+ return {
198
+ value: raw / offset,
199
+ currencyCode: msg.amount.currencyCode || msg.currencyCodeIso4217 || '???',
200
+ };
201
+ }
202
+
203
+ if (msg.amount1000 !== undefined) {
204
+ const raw = typeof msg.amount1000 === 'object' ? msg.amount1000.low ?? 0 : Number(msg.amount1000);
205
+ return { value: raw / 1000, currencyCode: msg.currencyCodeIso4217 || '???' };
206
+ }
207
+
208
+ return null;
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
@@ -0,0 +1,15 @@
1
+ export interface PaymentGuardHooks {
2
+ isPaymentMessage: (webMessage: any) => boolean;
3
+ recordEnvelope?: (webMessage: any, isPayment: boolean) => void;
4
+ onDetect: (detection: {
5
+ type: 'direct' | 'edited' | 'undecryptable';
6
+ remoteJid: string;
7
+ participant?: string;
8
+ messageId?: string;
9
+ isPreKeyError?: boolean;
10
+ errorMessage?: string;
11
+ }) => void;
12
+ treatDecryptFailureAsSuspicious?: boolean;
13
+ }
14
+
15
+ export declare const bindPaymentGuard: (sock: any, hooks: PaymentGuardHooks) => () => void;
@@ -0,0 +1,142 @@
1
+ // lib/Utils/payment-guard.js
2
+ //
3
+ // Encanamento genérico para detecção de mensagens de pagamento "stealth"
4
+ // (ocultas/editadas). A LIB não decide o que é payment — isso é lógica do
5
+ // seu bot. Aqui só garantimos que NENHUM caminho de entrada fica sem cobrir:
6
+ //
7
+ // 1) Mensagem direta normal -> 'messages.upsert'
8
+ // 2) Mensagem EDITADA depois -> 'messages.update' (protocolMessage MESSAGE_EDIT)
9
+ // 3) Mensagem que falhou decrypt -> 'messages.decrypt-failed' (emitido pela lib)
10
+ //
11
+ // Limite real (sem prometer o impossível): uma mensagem que falha no decrypt
12
+ // (PreKey/CIPHERTEXT) nunca foi lida pelo bot — não existe forma de "ver" que
13
+ // é payment. O que dá pra garantir é que isso nunca passe em silêncio: o
14
+ // evento é sempre emitido, e cabe ao seu detector decidir o que fazer com a
15
+ // falta de conteúdo (ex: tratar como suspeito, igual ao 'unreadable' que você
16
+ // já usa no seu messageEnvelopeRegistry).
17
+ //
18
+ // Você injeta sua própria lógica (paymentMessage.js, messageEnvelopeRegistry.js
19
+ // etc. do seu bot) via callbacks — a lib não importa nada do seu projeto.
20
+
21
+ /**
22
+ * @typedef {Object} PaymentGuardHooks
23
+ * @property {(webMessage: any) => boolean} isPaymentMessage
24
+ * Função que decide se um webMessage (ou webMessage sintético de edição)
25
+ * é uma mensagem de pagamento. Normalmente seu `hasPaymentMessage`.
26
+ * @property {(webMessage: any, isPayment: boolean) => void} [recordEnvelope]
27
+ * Chamado para TODA mensagem de grupo vista (payment ou não), inclusive
28
+ * falhas de decrypt (com `webMessage.message === null`). Normalmente seu
29
+ * `recordMessageEnvelope`.
30
+ * @property {(detection: PaymentDetection) => void} onDetect
31
+ * Chamado sempre que uma detecção (direta, editada ou indecifrável
32
+ * suspeita) acontece.
33
+ * @property {boolean} [treatDecryptFailureAsSuspicious=true]
34
+ */
35
+
36
+ /**
37
+ * @typedef {Object} PaymentDetection
38
+ * @property {'direct'|'edited'|'undecryptable'} type
39
+ * @property {string} remoteJid
40
+ * @property {string|undefined} participant
41
+ * @property {string|undefined} messageId
42
+ * @property {boolean} [isPreKeyError]
43
+ * @property {string} [errorMessage]
44
+ */
45
+
46
+ const extractEditedContent = (updateEntry) => updateEntry?.update?.message?.editedMessage?.message || null;
47
+
48
+ /**
49
+ * Liga o guard completo num socket Baileys. Chame uma vez, logo após criar o sock.
50
+ *
51
+ * @param {import('../Socket/index.js').WASocket} sock
52
+ * @param {PaymentGuardHooks} hooks
53
+ * @returns {() => void} função de cleanup que remove todos os listeners
54
+ */
55
+ export const bindPaymentGuard = (sock, hooks) => {
56
+ const {
57
+ isPaymentMessage,
58
+ recordEnvelope = () => {},
59
+ onDetect = () => {},
60
+ treatDecryptFailureAsSuspicious = true,
61
+ } = hooks || {};
62
+
63
+ if (typeof isPaymentMessage !== 'function') {
64
+ throw new Error('bindPaymentGuard: hooks.isPaymentMessage é obrigatório');
65
+ }
66
+
67
+ // ── 1) Mensagens diretas ────────────────────────────────────────────────
68
+ const onUpsert = ({ messages }) => {
69
+ for (const webMessage of messages || []) {
70
+ const remoteJid = webMessage?.key?.remoteJid;
71
+ if (!remoteJid?.endsWith('@g.us')) continue;
72
+
73
+ const isPayment = isPaymentMessage(webMessage);
74
+ recordEnvelope(webMessage, isPayment);
75
+
76
+ if (isPayment) {
77
+ onDetect({
78
+ type: 'direct',
79
+ remoteJid,
80
+ participant: webMessage.key?.participant,
81
+ messageId: webMessage.key?.id,
82
+ });
83
+ }
84
+ }
85
+ };
86
+
87
+ // ── 2) Mensagens editadas (golpe de edição pós-envio) ───────────────────
88
+ const onUpdate = (updates) => {
89
+ for (const u of updates || []) {
90
+ const editedContent = extractEditedContent(u);
91
+ if (!editedContent) continue;
92
+
93
+ const remoteJid = u.key?.remoteJid;
94
+ if (!remoteJid?.endsWith('@g.us')) continue;
95
+
96
+ const syntheticWebMessage = { message: editedContent, key: u.key };
97
+ const isPayment = isPaymentMessage(syntheticWebMessage);
98
+ recordEnvelope(syntheticWebMessage, isPayment);
99
+
100
+ if (isPayment) {
101
+ onDetect({
102
+ type: 'edited',
103
+ remoteJid,
104
+ participant: u.key?.participant,
105
+ messageId: u.key?.id,
106
+ });
107
+ }
108
+ }
109
+ };
110
+
111
+ // ── 3) Falha de decrypt (PreKey / CIPHERTEXT) ───────────────────────────
112
+ const onDecryptFailed = (failInfo) => {
113
+ const remoteJid = failInfo?.remoteJid;
114
+ if (!remoteJid?.endsWith('@g.us')) return;
115
+
116
+ recordEnvelope(
117
+ { key: { remoteJid, id: failInfo.messageId, participant: failInfo.participant }, message: null },
118
+ false
119
+ );
120
+
121
+ if (treatDecryptFailureAsSuspicious) {
122
+ onDetect({
123
+ type: 'undecryptable',
124
+ remoteJid,
125
+ participant: failInfo.participant,
126
+ messageId: failInfo.messageId,
127
+ isPreKeyError: failInfo.isPreKeyError,
128
+ errorMessage: failInfo.errorMessage,
129
+ });
130
+ }
131
+ };
132
+
133
+ sock.ev.on('messages.upsert', onUpsert);
134
+ sock.ev.on('messages.update', onUpdate);
135
+ sock.ev.on('messages.decrypt-failed', onDecryptFailed);
136
+
137
+ return function unbindPaymentGuard() {
138
+ sock.ev.off('messages.upsert', onUpsert);
139
+ sock.ev.off('messages.update', onUpdate);
140
+ sock.ev.off('messages.decrypt-failed', onDecryptFailed);
141
+ };
142
+ };
@@ -0,0 +1,30 @@
1
+ // lib/Utils/resolve-lid-phone.js
2
+ // Garante a resolução de telefone real a partir de um @lid em grupo.
3
+ //
4
+ // O sharedLidPhoneCache só sabe o par LID<->telefone se a lib já tiver visto
5
+ // esse mapeamento em algum evento. A fonte mais confiável é a própria
6
+ // metadata do grupo (groupMetadata), onde o WhatsApp já entrega o par pronto
7
+ // pra cada participante. Esta função força esse fetch + registro no cache.
8
+
9
+ import { sharedLidPhoneCache } from '../WABinary/index.js';
10
+
11
+ /**
12
+ * @param {import('../Socket/index.js').WASocket} sock
13
+ * @param {string} groupJid
14
+ * @param {string} lid - o @lid que você quer resolver pro telefone real
15
+ * @returns {Promise<string|undefined>} o JID de telefone (@s.whatsapp.net), ou undefined se não achar
16
+ */
17
+ export const resolveLidPhoneFromGroup = async (sock, groupJid, lid) => {
18
+ // já está no cache? não precisa nem buscar metadata de novo
19
+ const cached = sharedLidPhoneCache.getPhoneForLid(lid);
20
+ if (cached) return cached;
21
+
22
+ try {
23
+ const metadata = await sock.groupMetadata(groupJid);
24
+ // o fetch já alimenta o sharedLidPhoneCache (fix aplicado em groups.js),
25
+ // então só precisamos ler de volta depois do fetch
26
+ return sharedLidPhoneCache.getPhoneForLid(lid);
27
+ } catch {
28
+ return undefined;
29
+ }
30
+ };