@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.
- package/README.md +629 -658
- package/lib/Defaults/index.js +2 -2
- package/lib/Socket/chats.js +12 -1
- package/lib/Socket/groups.js +13 -4
- package/lib/Socket/messages-recv.js +45 -6
- package/lib/Socket/messages-recv.js.bak +1273 -0
- package/lib/Socket/messages-send.js +1 -0
- package/lib/Socket/socket.js +4 -4
- package/lib/Utils/bad-mac-handler.js +158 -0
- package/lib/Utils/generics.js +2 -1
- package/lib/Utils/get-best-version.js +41 -0
- package/lib/Utils/get-name.d.ts +1 -0
- package/lib/Utils/get-name.js +54 -0
- package/lib/Utils/group-status-detection.js +113 -0
- package/lib/Utils/index.js +8 -0
- package/lib/Utils/logger.js +4 -1
- package/lib/Utils/messages-media.js +38 -0
- package/lib/Utils/messages.js +113 -7
- package/lib/Utils/payment-detection.js +212 -0
- package/lib/Utils/payment-guard.d.ts +15 -0
- package/lib/Utils/payment-guard.js +142 -0
- package/lib/Utils/resolve-lid-phone.js +30 -0
- package/lib/Utils/scheduling.js +138 -0
- package/lib/Utils/validate-connection.js +11 -5
- package/lib/WABinary/jid-utils.js +245 -1
- package/package.json +1 -1
- package/lib/Defaults/index.d.ts.map +0 -1
- package/lib/Defaults/index.js.map +0 -1
- package/lib/Signal/Group/ciphertext-message.d.ts.map +0 -1
- package/lib/Signal/Group/ciphertext-message.js.map +0 -1
- package/lib/Signal/Group/group-session-builder.d.ts.map +0 -1
- package/lib/Signal/Group/group-session-builder.js.map +0 -1
- package/lib/Signal/Group/group_cipher.d.ts.map +0 -1
- package/lib/Signal/Group/group_cipher.js.map +0 -1
- package/lib/Signal/Group/index.d.ts.map +0 -1
- package/lib/Signal/Group/index.js.map +0 -1
- package/lib/Signal/Group/keyhelper.d.ts.map +0 -1
- package/lib/Signal/Group/keyhelper.js.map +0 -1
- package/lib/Signal/Group/sender-chain-key.d.ts.map +0 -1
- package/lib/Signal/Group/sender-chain-key.js.map +0 -1
- package/lib/Signal/Group/sender-key-distribution-message.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-distribution-message.js.map +0 -1
- package/lib/Signal/Group/sender-key-message.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-message.js.map +0 -1
- package/lib/Signal/Group/sender-key-name.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-name.js.map +0 -1
- package/lib/Signal/Group/sender-key-record.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-record.js.map +0 -1
- package/lib/Signal/Group/sender-key-state.d.ts.map +0 -1
- package/lib/Signal/Group/sender-key-state.js.map +0 -1
- package/lib/Signal/Group/sender-message-key.d.ts.map +0 -1
- package/lib/Signal/Group/sender-message-key.js.map +0 -1
- package/lib/Signal/libsignal.d.ts.map +0 -1
- package/lib/Signal/libsignal.js.map +0 -1
- package/lib/Signal/lid-mapping.d.ts.map +0 -1
- package/lib/Signal/lid-mapping.js.map +0 -1
- package/lib/Socket/Client/index.d.ts.map +0 -1
- package/lib/Socket/Client/index.js.map +0 -1
- package/lib/Socket/Client/types.d.ts.map +0 -1
- package/lib/Socket/Client/types.js.map +0 -1
- package/lib/Socket/Client/websocket.d.ts.map +0 -1
- package/lib/Socket/Client/websocket.js.map +0 -1
- package/lib/Socket/business.d.ts.map +0 -1
- package/lib/Socket/business.js.map +0 -1
- package/lib/Socket/chats.d.ts.map +0 -1
- package/lib/Socket/chats.js.map +0 -1
- package/lib/Socket/communities.d.ts.map +0 -1
- package/lib/Socket/communities.js.map +0 -1
- package/lib/Socket/groups.d.ts.map +0 -1
- package/lib/Socket/groups.js.map +0 -1
- package/lib/Socket/index.d.ts.map +0 -1
- package/lib/Socket/index.js.map +0 -1
- package/lib/Socket/messages-recv.d.ts.map +0 -1
- package/lib/Socket/messages-recv.js.map +0 -1
- package/lib/Socket/messages-send.d.ts.map +0 -1
- package/lib/Socket/messages-send.js.map +0 -1
- package/lib/Socket/mex.d.ts.map +0 -1
- package/lib/Socket/mex.js.map +0 -1
- package/lib/Socket/newsletter.d.ts.map +0 -1
- package/lib/Socket/newsletter.js.map +0 -1
- package/lib/Socket/socket.d.ts.map +0 -1
- package/lib/Socket/socket.js.map +0 -1
- package/lib/Types/Auth.d.ts.map +0 -1
- package/lib/Types/Auth.js.map +0 -1
- package/lib/Types/Bussines.d.ts.map +0 -1
- package/lib/Types/Bussines.js.map +0 -1
- package/lib/Types/Call.d.ts.map +0 -1
- package/lib/Types/Call.js.map +0 -1
- package/lib/Types/Chat.d.ts.map +0 -1
- package/lib/Types/Chat.js.map +0 -1
- package/lib/Types/Contact.d.ts.map +0 -1
- package/lib/Types/Contact.js.map +0 -1
- package/lib/Types/Events.d.ts.map +0 -1
- package/lib/Types/Events.js.map +0 -1
- package/lib/Types/GroupMetadata.d.ts.map +0 -1
- package/lib/Types/GroupMetadata.js.map +0 -1
- package/lib/Types/Label.d.ts.map +0 -1
- package/lib/Types/Label.js.map +0 -1
- package/lib/Types/LabelAssociation.d.ts.map +0 -1
- package/lib/Types/LabelAssociation.js.map +0 -1
- package/lib/Types/Message.d.ts.map +0 -1
- package/lib/Types/Message.js.map +0 -1
- package/lib/Types/Newsletter.d.ts.map +0 -1
- package/lib/Types/Newsletter.js.map +0 -1
- package/lib/Types/Product.d.ts.map +0 -1
- package/lib/Types/Product.js.map +0 -1
- package/lib/Types/Signal.d.ts.map +0 -1
- package/lib/Types/Signal.js.map +0 -1
- package/lib/Types/Socket.d.ts.map +0 -1
- package/lib/Types/Socket.js.map +0 -1
- package/lib/Types/State.d.ts.map +0 -1
- package/lib/Types/State.js.map +0 -1
- package/lib/Types/USync.d.ts.map +0 -1
- package/lib/Types/USync.js.map +0 -1
- package/lib/Types/index.d.ts.map +0 -1
- package/lib/Types/index.js.map +0 -1
- package/lib/Utils/auth-utils.d.ts.map +0 -1
- package/lib/Utils/auth-utils.js.map +0 -1
- package/lib/Utils/browser-utils.d.ts.map +0 -1
- package/lib/Utils/browser-utils.js.map +0 -1
- package/lib/Utils/business.d.ts.map +0 -1
- package/lib/Utils/business.js.map +0 -1
- package/lib/Utils/chat-utils.d.ts.map +0 -1
- package/lib/Utils/chat-utils.js.map +0 -1
- package/lib/Utils/crypto.d.ts.map +0 -1
- package/lib/Utils/crypto.js.map +0 -1
- package/lib/Utils/decode-wa-message.d.ts.map +0 -1
- package/lib/Utils/decode-wa-message.js.map +0 -1
- package/lib/Utils/event-buffer.d.ts.map +0 -1
- package/lib/Utils/event-buffer.js.map +0 -1
- package/lib/Utils/generics.d.ts.map +0 -1
- package/lib/Utils/generics.js.map +0 -1
- package/lib/Utils/history.d.ts.map +0 -1
- package/lib/Utils/history.js.map +0 -1
- package/lib/Utils/index.d.ts.map +0 -1
- package/lib/Utils/index.js.map +0 -1
- package/lib/Utils/link-preview.d.ts.map +0 -1
- package/lib/Utils/link-preview.js.map +0 -1
- package/lib/Utils/logger.d.ts.map +0 -1
- package/lib/Utils/logger.js.map +0 -1
- package/lib/Utils/lt-hash.d.ts.map +0 -1
- package/lib/Utils/lt-hash.js.map +0 -1
- package/lib/Utils/make-mutex.d.ts.map +0 -1
- package/lib/Utils/make-mutex.js.map +0 -1
- package/lib/Utils/message-retry-manager.d.ts.map +0 -1
- package/lib/Utils/message-retry-manager.js.map +0 -1
- package/lib/Utils/messages-media.d.ts.map +0 -1
- package/lib/Utils/messages-media.js.map +0 -1
- package/lib/Utils/messages.d.ts.map +0 -1
- package/lib/Utils/messages.js.map +0 -1
- package/lib/Utils/noise-handler.d.ts.map +0 -1
- package/lib/Utils/noise-handler.js.map +0 -1
- package/lib/Utils/pre-key-manager.d.ts.map +0 -1
- package/lib/Utils/pre-key-manager.js.map +0 -1
- package/lib/Utils/process-message.d.ts.map +0 -1
- package/lib/Utils/process-message.js.map +0 -1
- package/lib/Utils/signal.d.ts.map +0 -1
- package/lib/Utils/signal.js.map +0 -1
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +0 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +0 -1
- package/lib/Utils/validate-connection.d.ts.map +0 -1
- package/lib/Utils/validate-connection.js.map +0 -1
- package/lib/WABinary/constants.d.ts.map +0 -1
- package/lib/WABinary/constants.js.map +0 -1
- package/lib/WABinary/decode.d.ts.map +0 -1
- package/lib/WABinary/decode.js.map +0 -1
- package/lib/WABinary/encode.d.ts.map +0 -1
- package/lib/WABinary/encode.js.map +0 -1
- package/lib/WABinary/generic-utils.d.ts.map +0 -1
- package/lib/WABinary/generic-utils.js.map +0 -1
- package/lib/WABinary/index.d.ts.map +0 -1
- package/lib/WABinary/index.js.map +0 -1
- package/lib/WABinary/jid-utils.d.ts.map +0 -1
- package/lib/WABinary/jid-utils.js.map +0 -1
- package/lib/WABinary/types.d.ts.map +0 -1
- package/lib/WABinary/types.js.map +0 -1
- package/lib/WAM/BinaryInfo.d.ts.map +0 -1
- package/lib/WAM/BinaryInfo.js.map +0 -1
- package/lib/WAM/constants.d.ts.map +0 -1
- package/lib/WAM/constants.js.map +0 -1
- package/lib/WAM/encode.d.ts.map +0 -1
- package/lib/WAM/encode.js.map +0 -1
- package/lib/WAM/index.d.ts.map +0 -1
- package/lib/WAM/index.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncContactProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js.map +0 -1
- package/lib/WAUSync/Protocols/index.d.ts.map +0 -1
- package/lib/WAUSync/Protocols/index.js.map +0 -1
- package/lib/WAUSync/USyncQuery.d.ts.map +0 -1
- package/lib/WAUSync/USyncQuery.js.map +0 -1
- package/lib/WAUSync/USyncUser.d.ts.map +0 -1
- package/lib/WAUSync/USyncUser.js.map +0 -1
- package/lib/WAUSync/index.d.ts.map +0 -1
- package/lib/WAUSync/index.js.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js.map +0 -1
package/lib/Utils/messages.js
CHANGED
|
@@ -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
|
-
|
|
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
|
+
};
|