@badzz88/baileys 8.5.4 → 8.5.5
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 +1 -1
- package/WAProto/WAProto.proto +75 -1256
- package/WAProto/index.d.ts +19729 -36122
- package/WAProto/index.js +132193 -178538
- package/engine-requirements.js +15 -7
- package/lib/Defaults/index.js +14 -22
- package/lib/Signal/Group/group_cipher.js +5 -5
- package/lib/Signal/Group/keyhelper.js +4 -4
- package/lib/Signal/Group/sender-chain-key.js +2 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +1 -1
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-record.js +1 -1
- package/lib/Signal/Group/sender-message-key.js +5 -5
- package/lib/Signal/libsignal.js +28 -39
- package/lib/Signal/lid-mapping.js +8 -28
- package/lib/Socket/aigroups.js +5 -5
- package/lib/Socket/business.js +11 -3
- package/lib/Socket/chats.js +319 -668
- package/lib/Socket/communities.js +89 -85
- package/lib/Socket/graphql.js +228 -435
- package/lib/Socket/groups.js +211 -132
- package/lib/Socket/index.js +7 -9
- package/lib/Socket/interactive-handler.js +60 -117
- package/lib/Socket/interop.js +175 -56
- package/lib/Socket/luxu.js +387 -0
- package/lib/Socket/managed-account.js +48 -79
- package/lib/Socket/messages-recv.js +340 -898
- package/lib/Socket/messages-send.js +160 -462
- package/lib/Socket/mex.js +53 -40
- package/lib/Socket/newsletter.js +295 -291
- package/lib/Socket/privacy.js +265 -77
- package/lib/Socket/registration.js +157 -164
- package/lib/Socket/socket.js +199 -181
- package/lib/Socket/text-router.js +83 -0
- package/lib/Socket/username.js +227 -144
- package/lib/Store/keyed-db.js +108 -0
- package/lib/Store/make-cache-manager-store.js +2 -2
- package/lib/Store/make-in-memory-store.js +141 -25
- package/lib/Types/Auth.js +3 -3
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Mex.js +112 -0
- package/lib/Types/Newsletter.js +65 -96
- package/lib/Types/State.js +4 -4
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +57 -52
- package/lib/Utils/browser-utils.js +1 -1
- package/lib/Utils/business.js +6 -2
- package/lib/Utils/chat-utils.js +38 -102
- package/lib/Utils/command-loader.d.ts +31 -0
- package/lib/Utils/command-loader.js +100 -0
- package/lib/Utils/companion-reg-client-utils.js +42 -0
- package/lib/Utils/crypto.js +84 -54
- package/lib/Utils/curve25519-js.js +275 -0
- package/lib/Utils/decode-wa-message.js +83 -316
- package/lib/Utils/event-buffer.js +61 -37
- package/lib/Utils/generics.js +103 -157
- package/lib/Utils/group-history.js +12 -3
- package/lib/Utils/history.js +15 -15
- package/lib/Utils/index.js +5 -1
- package/lib/Utils/link-preview.js +8 -3
- package/lib/Utils/lt-hash.js +35 -2
- package/lib/Utils/make-mutex.js +1 -1
- package/lib/Utils/message-retry-manager.js +92 -27
- package/lib/Utils/messages-media.js +130 -159
- package/lib/Utils/messages.js +566 -144
- package/lib/Utils/meta-ai-msmsg.js +202 -62
- package/lib/Utils/native-bridge.js +82 -0
- package/lib/Utils/noise-handler.js +21 -19
- package/lib/Utils/offline-node-processor.js +8 -3
- package/lib/Utils/pre-key-manager.js +23 -13
- package/lib/Utils/process-message.js +106 -197
- package/lib/Utils/reporting-utils.js +2 -2
- package/lib/Utils/session-pool.d.ts +16 -0
- package/lib/Utils/session-pool.js +94 -0
- package/lib/Utils/signal.js +63 -28
- package/lib/Utils/stanza-ack.js +12 -39
- package/lib/Utils/sticker.d.ts +17 -0
- package/lib/Utils/sticker.js +145 -0
- package/lib/Utils/sync-action-utils.js +7 -4
- package/lib/Utils/tc-token-utils.js +25 -20
- package/lib/Utils/use-multi-file-auth-state.js +14 -30
- package/lib/Utils/validate-connection.js +19 -59
- package/lib/Utils/view-once-cache.d.ts +9 -0
- package/lib/Utils/view-once-cache.js +79 -0
- package/lib/WABinary/decode.js +16 -50
- package/lib/WABinary/encode.js +224 -54
- package/lib/WABinary/generic-utils.js +97 -7
- package/lib/WABinary/jid-utils.js +14 -27
- package/lib/WAM/encode.js +7 -7
- package/lib/WAUSync/Protocols/USyncBusinessProtocol.js +1 -58
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +1 -7
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -13
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +4 -1
- package/lib/WAUSync/Protocols/USyncFeatureProtocol.js +7 -20
- package/lib/WAUSync/Protocols/USyncPictureProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncSidelistProtocol.js +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +2 -7
- package/lib/WAUSync/Protocols/USyncTextStatusProtocol.js +1 -1
- package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +1 -2
- package/lib/WAUSync/Protocols/index.js +0 -1
- package/lib/WAUSync/USyncQuery.js +6 -77
- package/lib/WAUSync/USyncUser.js +8 -16
- package/lib/WAUSync/index.js +0 -1
- package/lib/antiban.js +854 -608
- package/lib/index.js +1 -1
- package/package.json +20 -16
package/lib/Utils/messages.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
Object.defineProperty(exports, '__esModule', { value: true })
|
|
3
|
-
exports.
|
|
3
|
+
exports.assertMediaContent =
|
|
4
|
+
exports.downloadMediaMessage =
|
|
5
|
+
exports.aggregateMessageKeysNotFromMe =
|
|
6
|
+
exports.updateMessageWithEventResponse =
|
|
7
|
+
exports.updateMessageWithPollUpdate =
|
|
8
|
+
exports.updateMessageWithReaction =
|
|
9
|
+
exports.updateMessageWithReceipt =
|
|
10
|
+
exports.getDevice =
|
|
11
|
+
exports.extractMessageContent =
|
|
12
|
+
exports.normalizeMessageContent =
|
|
13
|
+
exports.getContentType =
|
|
14
|
+
exports.generateWAMessage =
|
|
4
15
|
exports.generateWAMessageFromContent =
|
|
5
16
|
exports.generateWAMessageContent =
|
|
6
17
|
exports.hasNonNullishProperty =
|
|
@@ -10,10 +21,12 @@ exports.generateWAMessage =
|
|
|
10
21
|
exports.generateLinkPreviewIfRequired =
|
|
11
22
|
exports.extractUrlFromText =
|
|
12
23
|
void 0
|
|
24
|
+
exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage
|
|
25
|
+
exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
|
|
13
26
|
const boom_1 = require('@hapi/boom')
|
|
14
27
|
const crypto_1 = require('crypto')
|
|
15
28
|
const fs_1 = require('fs')
|
|
16
|
-
const
|
|
29
|
+
const index_js_1 = require('../../WAProto/index.js')
|
|
17
30
|
const WAProto_1 = require('../../WAProto/index.js')
|
|
18
31
|
const Defaults_1 = require('../Defaults')
|
|
19
32
|
const Types_1 = require('../Types')
|
|
@@ -23,12 +36,6 @@ const generics_1 = require('./generics')
|
|
|
23
36
|
const messages_media_1 = require('./messages-media')
|
|
24
37
|
const reporting_utils_1 = require('./reporting-utils')
|
|
25
38
|
const jid_display_normalization_1 = require('./jid-display-normalization')
|
|
26
|
-
const message_inspect_1 = require('./message-inspect')
|
|
27
|
-
Object.assign(exports, message_inspect_1)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const groupInviteThumbnailCache = new lru_cache_1.LRUCache({ max: 100, ttl: 5 * 60 * 1000 })
|
|
32
39
|
const MIMETYPE_MAP = {
|
|
33
40
|
image: 'image/jpeg',
|
|
34
41
|
video: 'video/mp4',
|
|
@@ -45,14 +52,14 @@ const MessageTypeProto = {
|
|
|
45
52
|
document: WAProto_1.proto.Message.DocumentMessage
|
|
46
53
|
}
|
|
47
54
|
|
|
48
|
-
|
|
55
|
+
// Input payloads can carry protobuf media message keys (e.g. imageMessage).
|
|
49
56
|
const MEDIA_MESSAGE_TYPE_ALIASES = {
|
|
50
57
|
imageMessage: 'image',
|
|
51
58
|
videoMessage: 'video',
|
|
52
59
|
audioMessage: 'audio',
|
|
53
60
|
documentMessage: 'document',
|
|
54
61
|
stickerMessage: 'sticker',
|
|
55
|
-
|
|
62
|
+
// PTV = push-to-video (video note) payload.
|
|
56
63
|
ptvMessage: 'video'
|
|
57
64
|
}
|
|
58
65
|
const MEDIA_MESSAGE_TYPE_ALIAS_KEYS = Object.keys(MEDIA_MESSAGE_TYPE_ALIASES)
|
|
@@ -189,7 +196,11 @@ const tokenizeCode = code => {
|
|
|
189
196
|
return merged
|
|
190
197
|
}
|
|
191
198
|
|
|
192
|
-
|
|
199
|
+
/**
|
|
200
|
+
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
201
|
+
* @param text eg. hello https://google.com
|
|
202
|
+
* @returns the URL, eg. https://google.com
|
|
203
|
+
*/
|
|
193
204
|
const extractUrlFromText = text => text.match(Defaults_1.URL_REGEX)?.[0]
|
|
194
205
|
exports.extractUrlFromText = extractUrlFromText
|
|
195
206
|
const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
@@ -199,7 +210,7 @@ const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
|
199
210
|
const urlInfo = await getUrlInfo(url)
|
|
200
211
|
return urlInfo
|
|
201
212
|
} catch (error) {
|
|
202
|
-
|
|
213
|
+
// ignore if fails
|
|
203
214
|
logger?.warn({ trace: error.stack }, 'url generation failed')
|
|
204
215
|
}
|
|
205
216
|
}
|
|
@@ -220,9 +231,6 @@ const assertColor = async color => {
|
|
|
220
231
|
}
|
|
221
232
|
const prepareWAMessageMedia = async (message, options) => {
|
|
222
233
|
const logger = options.logger
|
|
223
|
-
|
|
224
|
-
const mediaAbProps = options.mediaAbProps || null
|
|
225
|
-
const getMediaProp = propName => (0, messages_media_1.getMediaProp)(mediaAbProps, propName)
|
|
226
234
|
let mediaType
|
|
227
235
|
for (const key of Defaults_1.MEDIA_KEYS) {
|
|
228
236
|
if (key in message) {
|
|
@@ -237,20 +245,7 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
237
245
|
media: message[mediaType]
|
|
238
246
|
}
|
|
239
247
|
delete uploadData[mediaType]
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (mediaType === 'video' && uploadData.mimetype) {
|
|
245
|
-
const isHevc = uploadData.mimetype.includes('hevc') || uploadData.mimetype.includes('x-matroska')
|
|
246
|
-
if (isHevc && !getMediaProp('hevc_video_dual_upload')) {
|
|
247
|
-
|
|
248
|
-
uploadData.mimetype = MIMETYPE_MAP['video']
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
248
|
+
// check if cacheable + generate cache key
|
|
254
249
|
const cacheableKey =
|
|
255
250
|
typeof uploadData.media === 'object' &&
|
|
256
251
|
'url' in uploadData.media &&
|
|
@@ -267,7 +262,7 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
267
262
|
const mediaBuff = await options.mediaCache.get(cacheableKey)
|
|
268
263
|
if (mediaBuff) {
|
|
269
264
|
logger?.debug({ cacheableKey }, 'got media cache hit')
|
|
270
|
-
const obj =
|
|
265
|
+
const obj = index_js_1.proto.Message.decode(mediaBuff)
|
|
271
266
|
const key = `${mediaType}Message`
|
|
272
267
|
Object.assign(obj[key], { ...uploadData, media: undefined })
|
|
273
268
|
return obj
|
|
@@ -289,7 +284,7 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
289
284
|
})
|
|
290
285
|
await fs_1.promises.unlink(filePath)
|
|
291
286
|
const obj = WAProto_1.proto.Message.fromObject({
|
|
292
|
-
|
|
287
|
+
// todo: add more support here
|
|
293
288
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
294
289
|
url: mediaUrl,
|
|
295
290
|
directPath,
|
|
@@ -380,27 +375,6 @@ const prepareWAMessageMedia = async (message, options) => {
|
|
|
380
375
|
logger?.warn('failed to remove tmp file')
|
|
381
376
|
}
|
|
382
377
|
})
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const hdImageEnabled = mediaType === 'image' && getMediaProp('hd_image_dual_upload') && !!uploadData.hd
|
|
387
|
-
const hdVideoEnabled = mediaType === 'video' && getMediaProp('hd_video_dual_upload') && !!uploadData.hd
|
|
388
|
-
if (hdImageEnabled || hdVideoEnabled) {
|
|
389
|
-
uploadData.mediaHd = true
|
|
390
|
-
logger?.debug({ mediaType }, 'HD dual upload enabled, setting mediaHd flag')
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
if (mediaType === 'image' && uploadData.motionPhoto != null) {
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
if (typeof uploadData.motionPhoto === 'boolean') {
|
|
399
|
-
uploadData.motionPhoto = uploadData.motionPhoto ? 1 : 0
|
|
400
|
-
}
|
|
401
|
-
logger?.debug('motion photo field set on image message')
|
|
402
|
-
}
|
|
403
|
-
|
|
404
378
|
const obj = WAProto_1.proto.Message.fromObject({
|
|
405
379
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
406
380
|
url: mediaUrl,
|
|
@@ -440,15 +414,19 @@ const prepareDisappearingMessageSettingContent = ephemeralExpiration => {
|
|
|
440
414
|
return WAProto_1.proto.Message.fromObject(content)
|
|
441
415
|
}
|
|
442
416
|
exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent
|
|
443
|
-
|
|
417
|
+
/**
|
|
418
|
+
* Generate forwarded message content like WA does
|
|
419
|
+
* @param message the message to forward
|
|
420
|
+
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
421
|
+
*/
|
|
444
422
|
const generateForwardMessageContent = (message, forceForward) => {
|
|
445
423
|
let content = message.message
|
|
446
424
|
if (!content) {
|
|
447
425
|
throw new boom_1.Boom('no content in message', { statusCode: 400 })
|
|
448
426
|
}
|
|
449
|
-
|
|
427
|
+
// hacky copy
|
|
450
428
|
content = (0, exports.normalizeMessageContent)(content)
|
|
451
|
-
content =
|
|
429
|
+
content = index_js_1.proto.Message.decode(index_js_1.proto.Message.encode(content).finish())
|
|
452
430
|
let key = Object.keys(content)[0]
|
|
453
431
|
let score = content?.[key]?.contextInfo?.forwardingScore || 0
|
|
454
432
|
score += message.key.fromMe && !forceForward ? 0 : 1
|
|
@@ -676,19 +654,15 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
676
654
|
m.groupInviteMessage.caption = message.groupInvite.text
|
|
677
655
|
m.groupInviteMessage.groupJid = message.groupInvite.jid
|
|
678
656
|
m.groupInviteMessage.groupName = message.groupInvite.subject
|
|
657
|
+
//TODO: use built-in interface and get disappearing mode info etc.
|
|
658
|
+
//TODO: cache / use store!?
|
|
679
659
|
if (options.getProfilePicUrl) {
|
|
680
|
-
const
|
|
681
|
-
if (
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher })
|
|
687
|
-
if (resp.ok) {
|
|
688
|
-
const buf = Buffer.from(await resp.arrayBuffer())
|
|
689
|
-
m.groupInviteMessage.jpegThumbnail = buf
|
|
690
|
-
groupInviteThumbnailCache.set(message.groupInvite.jid, buf)
|
|
691
|
-
}
|
|
660
|
+
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview')
|
|
661
|
+
if (pfpUrl) {
|
|
662
|
+
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher })
|
|
663
|
+
if (resp.ok) {
|
|
664
|
+
const buf = Buffer.from(await resp.arrayBuffer())
|
|
665
|
+
m.groupInviteMessage.jpegThumbnail = buf
|
|
692
666
|
}
|
|
693
667
|
}
|
|
694
668
|
}
|
|
@@ -712,7 +686,7 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
712
686
|
m.buttonsResponseMessage = {
|
|
713
687
|
selectedButtonId: message.buttonReply.id,
|
|
714
688
|
selectedDisplayText: message.buttonReply.displayText,
|
|
715
|
-
type:
|
|
689
|
+
type: index_js_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
716
690
|
}
|
|
717
691
|
break
|
|
718
692
|
case 'interactive':
|
|
@@ -762,7 +736,7 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
762
736
|
(message.event.call === 'audio' ? Defaults_1.CALL_AUDIO_PREFIX : Defaults_1.CALL_VIDEO_PREFIX) + token
|
|
763
737
|
}
|
|
764
738
|
m.messageContextInfo = {
|
|
765
|
-
|
|
739
|
+
// encKey
|
|
766
740
|
messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32)
|
|
767
741
|
}
|
|
768
742
|
m.eventMessage.name = message.event.name
|
|
@@ -806,26 +780,21 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
806
780
|
...(message.poll.hideParticipantName !== undefined
|
|
807
781
|
? { hideParticipantName: !!message.poll.hideParticipantName }
|
|
808
782
|
: {}),
|
|
809
|
-
...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {})
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
...((0, messages_media_1.getMediaProp)(options.mediaAbProps, 'media_poll') && message.poll.mediaPoll != null
|
|
813
|
-
? { mediaPoll: !!message.poll.mediaPoll }
|
|
814
|
-
: {})
|
|
783
|
+
...(message.poll.allowAddOption !== undefined ? { allowAddOption: !!message.poll.allowAddOption } : {})
|
|
815
784
|
}
|
|
816
785
|
if (message.poll.version === 6 || message.poll.v6) {
|
|
817
786
|
m.pollCreationMessageV6 = pollCreationMessage
|
|
818
787
|
} else if (message.poll.version === 5 || message.poll.v5) {
|
|
819
788
|
m.pollCreationMessageV5 = pollCreationMessage
|
|
820
789
|
} else if (message.poll.toAnnouncementGroup) {
|
|
821
|
-
|
|
790
|
+
// poll v2 is for community announcement groups (single select and multiple)
|
|
822
791
|
m.pollCreationMessageV2 = pollCreationMessage
|
|
823
792
|
} else {
|
|
824
793
|
if (message.poll.selectableCount === 1) {
|
|
825
|
-
|
|
794
|
+
//poll v3 is for single select polls
|
|
826
795
|
m.pollCreationMessageV3 = pollCreationMessage
|
|
827
796
|
} else {
|
|
828
|
-
|
|
797
|
+
// poll for multiple choice polls
|
|
829
798
|
m.pollCreationMessage = pollCreationMessage
|
|
830
799
|
}
|
|
831
800
|
}
|
|
@@ -954,13 +923,13 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
954
923
|
}
|
|
955
924
|
} else if ((0, exports.hasNonNullishProperty)(message, 'sharePhoneNumber')) {
|
|
956
925
|
m.protocolMessage = {
|
|
957
|
-
type:
|
|
926
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
958
927
|
}
|
|
959
928
|
} else if ((0, exports.hasNonNullishProperty)(message, 'requestPhoneNumber')) {
|
|
960
929
|
m.requestPhoneNumberMessage = {}
|
|
961
930
|
} else if ((0, exports.hasNonNullishProperty)(message, 'limitSharing')) {
|
|
962
931
|
m.protocolMessage = {
|
|
963
|
-
type:
|
|
932
|
+
type: index_js_1.proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
964
933
|
limitSharing: {
|
|
965
934
|
sharingLimited: message.limitSharing === true,
|
|
966
935
|
trigger: 1,
|
|
@@ -1017,7 +986,7 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
1017
986
|
} else if ('interactiveMessage' in message) {
|
|
1018
987
|
m = { interactiveMessage: message.interactiveMessage }
|
|
1019
988
|
} else if ('richResponse' in message) {
|
|
1020
|
-
|
|
989
|
+
// handled in richResponse block below
|
|
1021
990
|
} else if ('groupStatusMessage' in message) {
|
|
1022
991
|
m = { groupStatusMessage: WAProto_1.proto.Message.GroupStatusMessage.fromObject(message.groupStatusMessage) }
|
|
1023
992
|
} else if (hasCaptionWithoutMedia && !hasCaptionContainer) {
|
|
@@ -1063,7 +1032,7 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
1063
1032
|
m = { interactiveMessage }
|
|
1064
1033
|
} else if ('templateButtons' in message && !!message.templateButtons) {
|
|
1065
1034
|
const msg = {
|
|
1066
|
-
hydratedButtons: message.templateButtons
|
|
1035
|
+
hydratedButtons: message.hasOwnProperty('templateButtons') ? message.templateButtons : message.templateButtons
|
|
1067
1036
|
}
|
|
1068
1037
|
if ('text' in message) {
|
|
1069
1038
|
msg.hydratedContentText = message.text
|
|
@@ -1120,42 +1089,73 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
1120
1089
|
}
|
|
1121
1090
|
}
|
|
1122
1091
|
if ('interactiveButtons' in message && !!message.interactiveButtons) {
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1092
|
+
const nativeFlow = WAProto_1.proto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
|
|
1093
|
+
buttons: message.interactiveButtons,
|
|
1094
|
+
messageParamsJson: message.messageParams ?? "{}"
|
|
1095
|
+
});
|
|
1096
|
+
let interactiveMessage = { nativeFlowMessage: nativeFlow };
|
|
1097
|
+
if ('text' in message) {
|
|
1098
|
+
interactiveMessage.body = { text: message.text };
|
|
1099
|
+
} else if ('caption' in message) {
|
|
1100
|
+
interactiveMessage.body = { text: message.caption };
|
|
1101
|
+
}
|
|
1102
|
+
if ('title' in message && !!message.title) {
|
|
1103
|
+
interactiveMessage.header = {
|
|
1104
|
+
title: message.title,
|
|
1105
|
+
subtitle: message.subtitle || "",
|
|
1106
|
+
hasMediaAttachment: false
|
|
1107
|
+
};
|
|
1108
|
+
let media = null;
|
|
1109
|
+
if ('image' in message && !!message.image) {
|
|
1110
|
+
media = await prepareWAMessageMedia({ image: message.image }, options);
|
|
1111
|
+
interactiveMessage.header.imageMessage = media.imageMessage;
|
|
1112
|
+
if (message.image.caption) {
|
|
1113
|
+
interactiveMessage.header.imageMessage.caption = message.image.caption;
|
|
1114
|
+
}
|
|
1115
|
+
} else if ('video' in message && !!message.video) {
|
|
1116
|
+
media = await prepareWAMessageMedia({ video: message.video }, options);
|
|
1117
|
+
interactiveMessage.header.videoMessage = media.videoMessage;
|
|
1118
|
+
if (message.video.caption) {
|
|
1119
|
+
interactiveMessage.header.videoMessage.caption = message.video.caption;
|
|
1120
|
+
}
|
|
1121
|
+
} else if ('gif' in message && !!message.gif) {
|
|
1122
|
+
media = await prepareWAMessageMedia({ video: message.gif }, options);
|
|
1123
|
+
interactiveMessage.header.videoMessage = media.videoMessage;
|
|
1124
|
+
interactiveMessage.header.videoMessage.gifPlayback = true;
|
|
1125
|
+
if (message.gif.caption) {
|
|
1126
|
+
interactiveMessage.header.videoMessage.caption = message.gif.caption;
|
|
1127
|
+
}
|
|
1128
|
+
} else if ('document' in message && !!message.document) {
|
|
1129
|
+
media = await prepareWAMessageMedia({ document: message.document }, options);
|
|
1130
|
+
interactiveMessage.header.documentMessage = media.documentMessage;
|
|
1131
|
+
let docuR = interactiveMessage.header.documentMessage;
|
|
1132
|
+
docuR.fileName = message.document.fileName || "Document";
|
|
1133
|
+
docuR.mimetype = message.document.mimetype || docuR.mimetype;
|
|
1134
|
+
if (message.document.caption) docuR.caption = message.document.caption;
|
|
1135
|
+
if (message.document.jpegThumbnail) docuR.jpegThumbnail = message.document.jpegThumbnail;
|
|
1136
|
+
} else if ('location' in message && !!message.location) {
|
|
1137
|
+
let mLoc = message.location;
|
|
1138
|
+
interactiveMessage.header.locationMessage = {
|
|
1139
|
+
degreesLongitude: mLoc.longitude || 0,
|
|
1140
|
+
degreesLatitude: mLoc.latitude || 0,
|
|
1141
|
+
name: mLoc.name || null,
|
|
1142
|
+
address: mLoc.address || null,
|
|
1143
|
+
url: mLoc.url || null
|
|
1144
|
+
};
|
|
1145
|
+
media = true;
|
|
1146
|
+
} else if ('thumbnail' in message && !!message.thumbnail) {
|
|
1147
|
+
interactiveMessage.header.jpegThumbnail = message.thumbnail;
|
|
1148
|
+
}
|
|
1149
|
+
if (media || ('thumbnail' in message && !!message.thumbnail)) {
|
|
1150
|
+
interactiveMessage.header.hasMediaAttachment = true;
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
if ('footer' in message && !!message.footer) {
|
|
1154
|
+
interactiveMessage.footer = { text: message.footer };
|
|
1155
|
+
}
|
|
1156
|
+
applyContextInfoAndMentions(interactiveMessage, message);
|
|
1157
|
+
m = { interactiveMessage };
|
|
1158
|
+
}
|
|
1159
1159
|
if ('shop' in message && !!message.shop) {
|
|
1160
1160
|
const interactiveMessage = {
|
|
1161
1161
|
shopStorefrontMessage: WAProto_1.proto.Message.InteractiveMessage.ShopMessage.fromObject({
|
|
@@ -1232,12 +1232,22 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
1232
1232
|
}
|
|
1233
1233
|
})
|
|
1234
1234
|
}
|
|
1235
|
-
if (table
|
|
1235
|
+
if (table) {
|
|
1236
|
+
const isPlainArrayTable = Array.isArray(table)
|
|
1237
|
+
const rows = isPlainArrayTable ? table : table.rows
|
|
1238
|
+
if (!Array.isArray(rows)) {
|
|
1239
|
+
throw new boom_1.Boom(
|
|
1240
|
+
'richResponse.table must be an array of rows (e.g. [["h1","h2"],["a","b"]]) or { rows: [...] }',
|
|
1241
|
+
{ statusCode: 400 }
|
|
1242
|
+
)
|
|
1243
|
+
}
|
|
1236
1244
|
sections.push({
|
|
1237
1245
|
view_model: {
|
|
1238
1246
|
primitive: {
|
|
1239
|
-
rows:
|
|
1240
|
-
cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells
|
|
1247
|
+
rows: rows.map((row, idx) => ({
|
|
1248
|
+
cells: Array.isArray(row) ? row.map(c => ({ text: String(c) })) : row.cells,
|
|
1249
|
+
// when a plain 2D array is passed, the first row is treated as the table header
|
|
1250
|
+
isHeading: Array.isArray(row) ? isPlainArrayTable && idx === 0 : row.isHeading
|
|
1241
1251
|
})),
|
|
1242
1252
|
__typename: 'GenAITableUXPrimitive'
|
|
1243
1253
|
},
|
|
@@ -1868,10 +1878,9 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
1868
1878
|
...existing,
|
|
1869
1879
|
botThreadInfo: {
|
|
1870
1880
|
clientInfo: {
|
|
1871
|
-
type:
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
: (bt.type ?? threadTypeEnum.UNKNOWN),
|
|
1881
|
+
type: typeof bt.type === 'string'
|
|
1882
|
+
? (threadTypeMap[bt.type.toLowerCase()] ?? threadTypeEnum.UNKNOWN)
|
|
1883
|
+
: (bt.type ?? threadTypeEnum.UNKNOWN),
|
|
1875
1884
|
sourceChatJid: bt.sourceChatJid || ''
|
|
1876
1885
|
}
|
|
1877
1886
|
}
|
|
@@ -2272,7 +2281,7 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
2272
2281
|
m.messageContextInfo = {
|
|
2273
2282
|
...(m.messageContextInfo || {}),
|
|
2274
2283
|
messageAddOnDurationInSecs: message.afterReadDuration,
|
|
2275
|
-
messageAddOnExpiryType:
|
|
2284
|
+
messageAddOnExpiryType: index_js_1.proto.MessageContextInfo.MessageAddonExpiryType.DEPENDENT_ON_PARENT
|
|
2276
2285
|
}
|
|
2277
2286
|
}
|
|
2278
2287
|
if ('raw' in message && !!message.raw) {
|
|
@@ -2401,8 +2410,8 @@ const generateWAMessageContent = async (message, options) => {
|
|
|
2401
2410
|
}
|
|
2402
2411
|
exports.generateWAMessageContent = generateWAMessageContent
|
|
2403
2412
|
const generateWAMessageFromContent = (jid, message, options) => {
|
|
2404
|
-
|
|
2405
|
-
|
|
2413
|
+
// set timestamp to now
|
|
2414
|
+
// if not specified
|
|
2406
2415
|
if (!options.timestamp) {
|
|
2407
2416
|
options.timestamp = new Date()
|
|
2408
2417
|
}
|
|
@@ -2412,12 +2421,12 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
2412
2421
|
const { quoted, userJid } = options
|
|
2413
2422
|
if (quoted && !(0, WABinary_1.isJidNewsletter)(jid)) {
|
|
2414
2423
|
const participant = quoted.key.fromMe
|
|
2415
|
-
? userJid
|
|
2424
|
+
? userJid // TODO: Add support for LIDs
|
|
2416
2425
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid
|
|
2417
2426
|
let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message)
|
|
2418
2427
|
const msgType = (0, exports.getContentType)(quotedMsg)
|
|
2419
|
-
|
|
2420
|
-
quotedMsg =
|
|
2428
|
+
// strip any redundant properties
|
|
2429
|
+
quotedMsg = index_js_1.proto.Message.create({ [msgType]: quotedMsg[msgType] })
|
|
2421
2430
|
const quotedContent = quotedMsg[msgType]
|
|
2422
2431
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
2423
2432
|
delete quotedContent.contextInfo
|
|
@@ -2426,53 +2435,62 @@ const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
2426
2435
|
contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant)
|
|
2427
2436
|
contextInfo.stanzaId = quoted.key.id
|
|
2428
2437
|
contextInfo.quotedMessage = quotedMsg
|
|
2429
|
-
|
|
2430
|
-
|
|
2438
|
+
// if a participant is quoted, then it must be a group
|
|
2439
|
+
// hence, remoteJid of group must also be entered
|
|
2431
2440
|
if (jid !== quoted.key.remoteJid) {
|
|
2432
2441
|
contextInfo.remoteJid = quoted.key.remoteJid
|
|
2433
2442
|
}
|
|
2434
2443
|
if (contextInfo && innerMessage[key]) {
|
|
2435
|
-
|
|
2444
|
+
/* @ts-ignore */
|
|
2436
2445
|
innerMessage[key].contextInfo = contextInfo
|
|
2437
2446
|
}
|
|
2438
2447
|
}
|
|
2439
2448
|
if (
|
|
2440
|
-
|
|
2449
|
+
// if we want to send a disappearing message
|
|
2441
2450
|
!!options.ephemeralExpiration &&
|
|
2442
|
-
|
|
2451
|
+
// and it's not a protocol message -- delete, toggle disappear message
|
|
2443
2452
|
key !== 'protocolMessage' &&
|
|
2444
|
-
|
|
2453
|
+
// already not converted to disappearing message
|
|
2445
2454
|
key !== 'ephemeralMessage' &&
|
|
2446
|
-
|
|
2455
|
+
// newsletters don't support ephemeral messages
|
|
2447
2456
|
!(0, WABinary_1.isJidNewsletter)(jid)
|
|
2448
2457
|
) {
|
|
2449
|
-
|
|
2458
|
+
/* @ts-ignore */
|
|
2450
2459
|
innerMessage[key].contextInfo = {
|
|
2451
2460
|
...(innerMessage[key].contextInfo || {}),
|
|
2452
2461
|
expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL
|
|
2453
|
-
|
|
2462
|
+
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
2454
2463
|
}
|
|
2455
2464
|
}
|
|
2456
2465
|
message = WAProto_1.proto.Message.create(message)
|
|
2466
|
+
const iosBros = options?.browser?.[0] === 'iOS' || options?.browser?.[1] === 'Safari'
|
|
2467
|
+
const androidBros = options?.browser?.[0] === 'Android'
|
|
2468
|
+
const pickedMessageId =
|
|
2469
|
+
options?.messageId ||
|
|
2470
|
+
(iosBros
|
|
2471
|
+
? (0, generics_1.generateIOSMessageID)()
|
|
2472
|
+
: androidBros
|
|
2473
|
+
? (0, generics_1.generateAndroMessageID)()
|
|
2474
|
+
: (0, generics_1.generateMessageIDV2)(userJid))
|
|
2457
2475
|
const messageJSON = {
|
|
2458
2476
|
key: {
|
|
2459
2477
|
remoteJid: jid,
|
|
2460
2478
|
fromMe: true,
|
|
2461
|
-
id:
|
|
2479
|
+
id: pickedMessageId
|
|
2462
2480
|
},
|
|
2463
2481
|
message: message,
|
|
2464
2482
|
messageTimestamp: timestamp,
|
|
2465
2483
|
messageStubParameters: [],
|
|
2466
|
-
participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined,
|
|
2484
|
+
participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined, // TODO: Add support for LIDs
|
|
2467
2485
|
status: Types_1.WAMessageStatus.PENDING
|
|
2468
2486
|
}
|
|
2469
2487
|
return WAProto_1.proto.WebMessageInfo.fromObject(messageJSON)
|
|
2470
2488
|
}
|
|
2471
2489
|
exports.generateWAMessageFromContent = generateWAMessageFromContent
|
|
2472
2490
|
const generateWAMessage = async (jid, content, options) => {
|
|
2473
|
-
|
|
2491
|
+
// ensure msg ID is with every log
|
|
2474
2492
|
options.logger = options?.logger?.child({ msgId: options.messageId })
|
|
2475
|
-
|
|
2493
|
+
// Pass jid in the options to generateWAMessageContent
|
|
2476
2494
|
return (0, exports.generateWAMessageFromContent)(
|
|
2477
2495
|
jid,
|
|
2478
2496
|
await (0, exports.generateWAMessageContent)(content, { ...options, jid }),
|
|
@@ -2480,3 +2498,407 @@ const generateWAMessage = async (jid, content, options) => {
|
|
|
2480
2498
|
)
|
|
2481
2499
|
}
|
|
2482
2500
|
exports.generateWAMessage = generateWAMessage
|
|
2501
|
+
/** Get the key to access the true type of content */
|
|
2502
|
+
const getContentType = content => {
|
|
2503
|
+
if (content) {
|
|
2504
|
+
const keys = Object.keys(content)
|
|
2505
|
+
const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage')
|
|
2506
|
+
return key
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
exports.getContentType = getContentType
|
|
2510
|
+
/**
|
|
2511
|
+
* Normalizes ephemeral, view once messages to regular message content
|
|
2512
|
+
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
2513
|
+
* @param content
|
|
2514
|
+
* @returns
|
|
2515
|
+
*/
|
|
2516
|
+
const normalizeMessageContent = content => {
|
|
2517
|
+
if (!content) {
|
|
2518
|
+
return undefined
|
|
2519
|
+
}
|
|
2520
|
+
// set max iterations to prevent an infinite loop
|
|
2521
|
+
for (let i = 0; i < 5; i++) {
|
|
2522
|
+
const inner = getFutureProofMessage(content)
|
|
2523
|
+
if (!inner) {
|
|
2524
|
+
break
|
|
2525
|
+
}
|
|
2526
|
+
content = inner.message
|
|
2527
|
+
}
|
|
2528
|
+
return content
|
|
2529
|
+
function getFutureProofMessage(message) {
|
|
2530
|
+
return (
|
|
2531
|
+
message.ephemeralMessage ||
|
|
2532
|
+
message.viewOnceMessage ||
|
|
2533
|
+
message.documentWithCaptionMessage ||
|
|
2534
|
+
message.viewOnceMessageV2 ||
|
|
2535
|
+
message.viewOnceMessageV2Extension ||
|
|
2536
|
+
message.editedMessage ||
|
|
2537
|
+
message.groupMentionedMessage ||
|
|
2538
|
+
message.botInvokeMessage ||
|
|
2539
|
+
message.lottieStickerMessage ||
|
|
2540
|
+
message.eventCoverImage ||
|
|
2541
|
+
message.statusMentionMessage ||
|
|
2542
|
+
message.pollCreationOptionImageMessage ||
|
|
2543
|
+
message.associatedChildMessage ||
|
|
2544
|
+
message.groupStatusMentionMessage ||
|
|
2545
|
+
message.pollCreationMessageV4 ||
|
|
2546
|
+
message.pollCreationMessageV5 ||
|
|
2547
|
+
message.statusAddYours ||
|
|
2548
|
+
message.groupStatusMessage ||
|
|
2549
|
+
message.limitSharingMessage ||
|
|
2550
|
+
message.botTaskMessage ||
|
|
2551
|
+
message.questionMessage ||
|
|
2552
|
+
message.groupStatusMessageV2 ||
|
|
2553
|
+
message.botForwardedMessage ||
|
|
2554
|
+
message.questionReplyMessage ||
|
|
2555
|
+
message.newsletterAdminProfileMessage ||
|
|
2556
|
+
message.newsletterAdminProfileMessageV2 ||
|
|
2557
|
+
message.newsletterAdminProfileStatusMessage ||
|
|
2558
|
+
message.spoilerMessage ||
|
|
2559
|
+
message.groupStatusV3Message ||
|
|
2560
|
+
message.pollCreationMessageV6
|
|
2561
|
+
)
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
exports.normalizeMessageContent = normalizeMessageContent
|
|
2565
|
+
/**
|
|
2566
|
+
* Extract the true message content from a message
|
|
2567
|
+
* Eg. extracts the inner message from a disappearing message/view once message
|
|
2568
|
+
*/
|
|
2569
|
+
const extractMessageContent = content => {
|
|
2570
|
+
const extractFromTemplateMessage = msg => {
|
|
2571
|
+
if (msg.imageMessage) {
|
|
2572
|
+
return { imageMessage: msg.imageMessage }
|
|
2573
|
+
} else if (msg.documentMessage) {
|
|
2574
|
+
return { documentMessage: msg.documentMessage }
|
|
2575
|
+
} else if (msg.videoMessage) {
|
|
2576
|
+
return { videoMessage: msg.videoMessage }
|
|
2577
|
+
} else if (msg.locationMessage) {
|
|
2578
|
+
return { locationMessage: msg.locationMessage }
|
|
2579
|
+
} else {
|
|
2580
|
+
return {
|
|
2581
|
+
conversation:
|
|
2582
|
+
'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
content = (0, exports.normalizeMessageContent)(content)
|
|
2587
|
+
if (content?.buttonsMessage) {
|
|
2588
|
+
return extractFromTemplateMessage(content.buttonsMessage)
|
|
2589
|
+
}
|
|
2590
|
+
if (content?.templateMessage?.hydratedFourRowTemplate) {
|
|
2591
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate)
|
|
2592
|
+
}
|
|
2593
|
+
if (content?.templateMessage?.hydratedTemplate) {
|
|
2594
|
+
return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate)
|
|
2595
|
+
}
|
|
2596
|
+
if (content?.templateMessage?.fourRowTemplate) {
|
|
2597
|
+
return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate)
|
|
2598
|
+
}
|
|
2599
|
+
return content
|
|
2600
|
+
}
|
|
2601
|
+
exports.extractMessageContent = extractMessageContent
|
|
2602
|
+
/**
|
|
2603
|
+
* Returns the device predicted by message ID
|
|
2604
|
+
*/
|
|
2605
|
+
const getDevice = id =>
|
|
2606
|
+
/^3A.{18}$/.test(id)
|
|
2607
|
+
? 'ios'
|
|
2608
|
+
: /^3E.{20}$/.test(id)
|
|
2609
|
+
? 'web'
|
|
2610
|
+
: /^(.{21}|.{32})$/.test(id)
|
|
2611
|
+
? 'android'
|
|
2612
|
+
: /^(3F|.{18}$)/.test(id)
|
|
2613
|
+
? 'desktop'
|
|
2614
|
+
: 'api/baileys'
|
|
2615
|
+
exports.getDevice = getDevice
|
|
2616
|
+
/** Upserts a receipt in the message */
|
|
2617
|
+
const updateMessageWithReceipt = (msg, receipt) => {
|
|
2618
|
+
msg.userReceipt = msg.userReceipt || []
|
|
2619
|
+
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
|
|
2620
|
+
if (recp) {
|
|
2621
|
+
Object.assign(recp, receipt)
|
|
2622
|
+
} else {
|
|
2623
|
+
msg.userReceipt.push(receipt)
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
exports.updateMessageWithReceipt = updateMessageWithReceipt
|
|
2627
|
+
/** Update the message with a new reaction */
|
|
2628
|
+
const updateMessageWithReaction = (msg, reaction) => {
|
|
2629
|
+
const authorID = (0, generics_1.getKeyAuthor)(reaction.key)
|
|
2630
|
+
const reactions = (msg.reactions || []).filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID)
|
|
2631
|
+
reaction.text = reaction.text || ''
|
|
2632
|
+
reactions.push(reaction)
|
|
2633
|
+
msg.reactions = reactions
|
|
2634
|
+
}
|
|
2635
|
+
exports.updateMessageWithReaction = updateMessageWithReaction
|
|
2636
|
+
/** Update the message with a new poll update */
|
|
2637
|
+
const updateMessageWithPollUpdate = (msg, update) => {
|
|
2638
|
+
const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey)
|
|
2639
|
+
const reactions = (msg.pollUpdates || []).filter(
|
|
2640
|
+
r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID
|
|
2641
|
+
)
|
|
2642
|
+
if (update.vote?.selectedOptions?.length) {
|
|
2643
|
+
reactions.push(update)
|
|
2644
|
+
}
|
|
2645
|
+
msg.pollUpdates = reactions
|
|
2646
|
+
}
|
|
2647
|
+
exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate
|
|
2648
|
+
/** Update the message with a new event response */
|
|
2649
|
+
const updateMessageWithEventResponse = (msg, update) => {
|
|
2650
|
+
const authorID = (0, generics_1.getKeyAuthor)(update.eventResponseMessageKey)
|
|
2651
|
+
const responses = (msg.eventResponses || []).filter(
|
|
2652
|
+
r => (0, generics_1.getKeyAuthor)(r.eventResponseMessageKey) !== authorID
|
|
2653
|
+
)
|
|
2654
|
+
responses.push(update)
|
|
2655
|
+
msg.eventResponses = responses
|
|
2656
|
+
}
|
|
2657
|
+
exports.updateMessageWithEventResponse = updateMessageWithEventResponse
|
|
2658
|
+
/**
|
|
2659
|
+
* Aggregates all poll updates in a poll.
|
|
2660
|
+
* @param msg the poll creation message
|
|
2661
|
+
* @param meId your jid
|
|
2662
|
+
* @returns A list of options & their voters
|
|
2663
|
+
*/
|
|
2664
|
+
function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
2665
|
+
const opts =
|
|
2666
|
+
message?.pollCreationMessage?.options ||
|
|
2667
|
+
message?.pollCreationMessageV2?.options ||
|
|
2668
|
+
message?.pollCreationMessageV3?.options ||
|
|
2669
|
+
message?.pollCreationMessageV5?.options ||
|
|
2670
|
+
message?.pollCreationMessageV6?.options ||
|
|
2671
|
+
message?.pollCreationMessageV4?.message?.pollCreationMessage?.options ||
|
|
2672
|
+
message?.pollCreationMessageV4?.message?.pollCreationMessageV3?.options ||
|
|
2673
|
+
[]
|
|
2674
|
+
const voteHashMap = opts.reduce((acc, opt) => {
|
|
2675
|
+
const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString()
|
|
2676
|
+
acc[hash] = {
|
|
2677
|
+
name: opt.optionName || '',
|
|
2678
|
+
voters: []
|
|
2679
|
+
}
|
|
2680
|
+
return acc
|
|
2681
|
+
}, {})
|
|
2682
|
+
for (const update of pollUpdates || []) {
|
|
2683
|
+
const { vote } = update
|
|
2684
|
+
if (!vote) {
|
|
2685
|
+
continue
|
|
2686
|
+
}
|
|
2687
|
+
for (const option of vote.selectedOptions || []) {
|
|
2688
|
+
const hash = option.toString()
|
|
2689
|
+
let data = voteHashMap[hash]
|
|
2690
|
+
if (!data) {
|
|
2691
|
+
voteHashMap[hash] = {
|
|
2692
|
+
name: 'Unknown',
|
|
2693
|
+
voters: []
|
|
2694
|
+
}
|
|
2695
|
+
data = voteHashMap[hash]
|
|
2696
|
+
}
|
|
2697
|
+
voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId))
|
|
2698
|
+
}
|
|
2699
|
+
}
|
|
2700
|
+
return Object.values(voteHashMap)
|
|
2701
|
+
}
|
|
2702
|
+
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
2703
|
+
const aggregateMessageKeysNotFromMe = keys => {
|
|
2704
|
+
const keyMap = {}
|
|
2705
|
+
for (const { remoteJid, id, participant, fromMe, sts } of keys) {
|
|
2706
|
+
if (!fromMe) {
|
|
2707
|
+
const uqKey = `${remoteJid}:${participant || ''}`
|
|
2708
|
+
if (!keyMap[uqKey]) {
|
|
2709
|
+
keyMap[uqKey] = {
|
|
2710
|
+
jid: remoteJid,
|
|
2711
|
+
participant: participant,
|
|
2712
|
+
messageIds: [],
|
|
2713
|
+
// sts is per-message; kept only for single-message interop receipts
|
|
2714
|
+
sts: sts
|
|
2715
|
+
}
|
|
2716
|
+
} else if (keyMap[uqKey].sts !== sts) {
|
|
2717
|
+
// Multiple messages with different sts — can't aggregate a single sts
|
|
2718
|
+
keyMap[uqKey].sts = undefined
|
|
2719
|
+
}
|
|
2720
|
+
keyMap[uqKey].messageIds.push(id)
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
return Object.values(keyMap)
|
|
2724
|
+
}
|
|
2725
|
+
exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe
|
|
2726
|
+
|
|
2727
|
+
/**
|
|
2728
|
+
* Check if a WebMessageInfo has a scheduled reveal time (ConditionalRevealMessage)
|
|
2729
|
+
*/
|
|
2730
|
+
const isScheduledMessage = msg => !!msg?.scheduledMessageMetadata?.scheduledTime
|
|
2731
|
+
exports.isScheduledMessage = isScheduledMessage
|
|
2732
|
+
|
|
2733
|
+
/**
|
|
2734
|
+
* Get scheduled reveal time of a message as a Date, or null
|
|
2735
|
+
*/
|
|
2736
|
+
const getScheduledMessageTime = msg => {
|
|
2737
|
+
const t = msg?.scheduledMessageMetadata?.scheduledTime
|
|
2738
|
+
if (!t) return null
|
|
2739
|
+
return new Date(Number(t) * 1000)
|
|
2740
|
+
}
|
|
2741
|
+
exports.getScheduledMessageTime = getScheduledMessageTime
|
|
2742
|
+
|
|
2743
|
+
/**
|
|
2744
|
+
* Extract PaymentInfo from a WebMessageInfo (the payment status field, not the message content)
|
|
2745
|
+
*/
|
|
2746
|
+
const getMessagePaymentInfo = msg => msg?.paymentInfo || msg?.quotedPaymentInfo || null
|
|
2747
|
+
exports.getMessagePaymentInfo = getMessagePaymentInfo
|
|
2748
|
+
|
|
2749
|
+
/**
|
|
2750
|
+
* Get all comment metadata from a WebMessageInfo
|
|
2751
|
+
*/
|
|
2752
|
+
const getMessageCommentMetadata = msg => msg?.commentMetadata || null
|
|
2753
|
+
exports.getMessageCommentMetadata = getMessageCommentMetadata
|
|
2754
|
+
|
|
2755
|
+
/**
|
|
2756
|
+
* Get all message add-ons (reactions, poll updates, pins) from a WebMessageInfo
|
|
2757
|
+
*/
|
|
2758
|
+
const getMessageAddOns = msg => msg?.messageAddOns || []
|
|
2759
|
+
exports.getMessageAddOns = getMessageAddOns
|
|
2760
|
+
|
|
2761
|
+
/**
|
|
2762
|
+
* Get the quiz correct answer from a poll creation message, if it's a quiz
|
|
2763
|
+
*/
|
|
2764
|
+
const getPollCorrectAnswer = pollMsg => {
|
|
2765
|
+
const poll =
|
|
2766
|
+
pollMsg?.pollCreationMessage ||
|
|
2767
|
+
pollMsg?.pollCreationMessageV2 ||
|
|
2768
|
+
pollMsg?.pollCreationMessageV3 ||
|
|
2769
|
+
pollMsg?.pollCreationMessageV5 ||
|
|
2770
|
+
pollMsg?.pollCreationMessageV6
|
|
2771
|
+
if (!poll) return null
|
|
2772
|
+
const isQuiz = poll.pollType === WAProto_1.proto.Message.PollType?.QUIZ || poll.pollType === 1
|
|
2773
|
+
return isQuiz ? poll.correctAnswer?.optionName || null : null
|
|
2774
|
+
}
|
|
2775
|
+
exports.getPollCorrectAnswer = getPollCorrectAnswer
|
|
2776
|
+
|
|
2777
|
+
/**
|
|
2778
|
+
* Aggregates all event responses in an event message.
|
|
2779
|
+
* @param msg the event creation message
|
|
2780
|
+
* @param meLid your lid
|
|
2781
|
+
* @returns A list of response types & their responders
|
|
2782
|
+
*/
|
|
2783
|
+
function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
|
|
2784
|
+
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE']
|
|
2785
|
+
const responseMap = {}
|
|
2786
|
+
|
|
2787
|
+
for (const type of responseTypes) {
|
|
2788
|
+
responseMap[type] = {
|
|
2789
|
+
response: type,
|
|
2790
|
+
responders: []
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
for (const update of eventResponses) {
|
|
2794
|
+
const { response } = update.response || {}
|
|
2795
|
+
const responseType = index_js_1.proto.Message.EventResponseMessage.EventResponseType[response]
|
|
2796
|
+
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
2797
|
+
responseMap[responseType].responders.push(generics_1.getKeyAuthor(update.eventResponseMessageKey, meLid))
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
return Object.values(responseMap)
|
|
2802
|
+
}
|
|
2803
|
+
|
|
2804
|
+
exports.getAggregateResponsesInEventMessage = getAggregateResponsesInEventMessage
|
|
2805
|
+
|
|
2806
|
+
const REUPLOAD_REQUIRED_STATUS = [410, 404]
|
|
2807
|
+
/**
|
|
2808
|
+
* Downloads the given message. Throws an error if it's not a media message
|
|
2809
|
+
*/
|
|
2810
|
+
const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
2811
|
+
const result = await downloadMsg().catch(async error => {
|
|
2812
|
+
if (
|
|
2813
|
+
ctx &&
|
|
2814
|
+
typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
|
|
2815
|
+
REUPLOAD_REQUIRED_STATUS.includes(error.status)
|
|
2816
|
+
) {
|
|
2817
|
+
ctx.logger.info({ key: message.key }, 'sending reupload media request...')
|
|
2818
|
+
// request reupload
|
|
2819
|
+
message = await ctx.reuploadRequest(message)
|
|
2820
|
+
const result = await downloadMsg()
|
|
2821
|
+
return result
|
|
2822
|
+
}
|
|
2823
|
+
throw error
|
|
2824
|
+
})
|
|
2825
|
+
return result
|
|
2826
|
+
async function downloadMsg() {
|
|
2827
|
+
const mContent = (0, exports.extractMessageContent)(message.message)
|
|
2828
|
+
if (!mContent) {
|
|
2829
|
+
throw new boom_1.Boom('No message present', { statusCode: 400, data: message })
|
|
2830
|
+
}
|
|
2831
|
+
const contentType = (0, exports.getContentType)(mContent)
|
|
2832
|
+
let mediaType = contentType?.replace('Message', '')
|
|
2833
|
+
const media = mContent[contentType]
|
|
2834
|
+
if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
|
|
2835
|
+
throw new boom_1.Boom(`"${contentType}" message is not a media message`)
|
|
2836
|
+
}
|
|
2837
|
+
let download
|
|
2838
|
+
if ('thumbnailDirectPath' in media && !('url' in media)) {
|
|
2839
|
+
download = {
|
|
2840
|
+
directPath: media.thumbnailDirectPath,
|
|
2841
|
+
mediaKey: media.mediaKey
|
|
2842
|
+
}
|
|
2843
|
+
mediaType = 'thumbnail-link'
|
|
2844
|
+
} else {
|
|
2845
|
+
download = media
|
|
2846
|
+
}
|
|
2847
|
+
const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options)
|
|
2848
|
+
if (type === 'buffer') {
|
|
2849
|
+
const bufferArray = []
|
|
2850
|
+
for await (const chunk of stream) {
|
|
2851
|
+
bufferArray.push(chunk)
|
|
2852
|
+
}
|
|
2853
|
+
return Buffer.concat(bufferArray)
|
|
2854
|
+
}
|
|
2855
|
+
return stream
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
exports.downloadMediaMessage = downloadMediaMessage
|
|
2859
|
+
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
2860
|
+
const assertMediaContent = content => {
|
|
2861
|
+
content = (0, exports.extractMessageContent)(content)
|
|
2862
|
+
const mediaContent =
|
|
2863
|
+
content?.documentMessage ||
|
|
2864
|
+
content?.imageMessage ||
|
|
2865
|
+
content?.videoMessage ||
|
|
2866
|
+
content?.audioMessage ||
|
|
2867
|
+
content?.stickerMessage
|
|
2868
|
+
if (!mediaContent) {
|
|
2869
|
+
throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content })
|
|
2870
|
+
}
|
|
2871
|
+
return mediaContent
|
|
2872
|
+
}
|
|
2873
|
+
exports.assertMediaContent = assertMediaContent
|
|
2874
|
+
/**
|
|
2875
|
+
* Normalizes a bare user id to @s.whatsapp.net. Does not convert LID↔PN; use lidMapping / PN in key.remoteJidAlt when needed.
|
|
2876
|
+
*/
|
|
2877
|
+
const toJid = id => {
|
|
2878
|
+
if (!id) return ''
|
|
2879
|
+
if (id.includes('@')) return id
|
|
2880
|
+
return `${id}@s.whatsapp.net`
|
|
2881
|
+
}
|
|
2882
|
+
exports.toJid = toJid
|
|
2883
|
+
/**
|
|
2884
|
+
* Returns the peer LID JID when the key is LID-primary (decode sets remoteJid/participant to @lid when WA sends LID).
|
|
2885
|
+
*/
|
|
2886
|
+
const getSenderLid = message => {
|
|
2887
|
+
const k = message.key
|
|
2888
|
+
if (!k) {
|
|
2889
|
+
return { jid: '', lid: '' }
|
|
2890
|
+
}
|
|
2891
|
+
const jid = k.participant || k.remoteJid || ''
|
|
2892
|
+
if (jid.endsWith('@lid') || jid.endsWith('@hosted.lid')) {
|
|
2893
|
+
return { jid, lid: jid }
|
|
2894
|
+
}
|
|
2895
|
+
if (k.lid && typeof k.lid === 'string') {
|
|
2896
|
+
const lid = k.lid.includes('@') ? k.lid : (0, WABinary_1.jidEncode)(k.lid, 'lid')
|
|
2897
|
+
return { jid, lid }
|
|
2898
|
+
}
|
|
2899
|
+
if (k.participantLid && (0, WABinary_1.isLidUser)(k.participantLid)) {
|
|
2900
|
+
return { jid, lid: k.participantLid }
|
|
2901
|
+
}
|
|
2902
|
+
return { jid, lid: '' }
|
|
2903
|
+
}
|
|
2904
|
+
exports.getSenderLid = getSenderLid
|