amiudmodz 6.2.3 → 6.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Defaults/index.d.ts +8 -0
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +26 -10
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Signal/lid-mapping.d.ts +3 -3
- package/lib/Signal/lid-mapping.d.ts.map +1 -1
- package/lib/Signal/lid-mapping.js +21 -14
- package/lib/Signal/lid-mapping.js.map +1 -1
- package/lib/Socket/business.d.ts +19 -12
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/chats.d.ts +2 -5
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +29 -11
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +19 -12
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/groups.d.ts +1 -4
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/index.d.ts +2 -277
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/index.js +29 -1
- package/lib/Socket/index.js.map +1 -1
- package/lib/Socket/messages-recv.d.ts +19 -12
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-recv.js +117 -28
- package/lib/Socket/messages-recv.js.map +1 -1
- package/lib/Socket/messages-send.d.ts +18 -11
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +362 -250
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/mex.d.ts +1 -1
- package/lib/Socket/mex.d.ts.map +1 -1
- package/lib/Socket/mex.js +4 -4
- package/lib/Socket/mex.js.map +1 -1
- package/lib/Socket/newsletter.d.ts +6 -7
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/newsletter.js +74 -26
- package/lib/Socket/newsletter.js.map +1 -1
- package/lib/Socket/socket.d.ts.map +1 -1
- package/lib/Socket/socket.js +7 -26
- package/lib/Socket/socket.js.map +1 -1
- package/lib/Types/Auth.d.ts +10 -0
- package/lib/Types/Auth.d.ts.map +1 -1
- package/lib/Types/Events.d.ts +2 -1
- package/lib/Types/Events.d.ts.map +1 -1
- package/lib/Types/Message.d.ts +59 -10
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js +6 -0
- package/lib/Types/Message.js.map +1 -1
- package/lib/Types/Mex.d.ts +2 -0
- package/lib/Types/Mex.d.ts.map +1 -1
- package/lib/Types/Mex.js +2 -0
- package/lib/Types/Mex.js.map +1 -1
- package/lib/Types/Socket.d.ts +18 -0
- package/lib/Types/Socket.d.ts.map +1 -1
- package/lib/Types/State.d.ts +10 -0
- package/lib/Types/State.d.ts.map +1 -1
- package/lib/Types/State.js.map +1 -1
- package/lib/Types/index.d.ts +1 -1
- package/lib/Types/index.d.ts.map +1 -1
- package/lib/Types/index.js +1 -0
- package/lib/Types/index.js.map +1 -1
- package/lib/Utils/MessageBuilder.d.ts +11 -10
- package/lib/Utils/MessageBuilder.d.ts.map +1 -1
- package/lib/Utils/MessageBuilder.js +14 -3
- package/lib/Utils/MessageBuilder.js.map +1 -1
- package/lib/Utils/auth-utils.d.ts.map +1 -1
- package/lib/Utils/auth-utils.js +23 -8
- package/lib/Utils/auth-utils.js.map +1 -1
- package/lib/Utils/browser-utils.js +4 -4
- package/lib/Utils/browser-utils.js.map +1 -1
- package/lib/Utils/decode-wa-message.d.ts.map +1 -1
- package/lib/Utils/decode-wa-message.js +9 -5
- package/lib/Utils/decode-wa-message.js.map +1 -1
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +8 -5
- package/lib/Utils/generics.js.map +1 -1
- package/lib/Utils/index.d.ts +6 -1
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +6 -1
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/messages-media.d.ts +12 -18
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +163 -65
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages.d.ts +26 -71
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +1198 -328
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/tc-token-utils.d.ts.map +1 -1
- package/lib/Utils/tc-token-utils.js +2 -3
- package/lib/Utils/tc-token-utils.js.map +1 -1
- package/lib/Utils/use-multi-file-auth-state.d.ts.map +1 -1
- package/lib/Utils/use-multi-file-auth-state.js +30 -1
- package/lib/Utils/use-multi-file-auth-state.js.map +1 -1
- package/lib/Utils/use-sqlite-auth-state.js +12 -12
- package/lib/Utils/validate-connection.d.ts.map +1 -1
- package/lib/Utils/validate-connection.js +5 -11
- package/lib/Utils/validate-connection.js.map +1 -1
- package/lib/WABinary/constants.d.ts +8 -0
- package/lib/WABinary/constants.d.ts.map +1 -1
- package/lib/WABinary/constants.js +72 -0
- package/lib/WABinary/constants.js.map +1 -1
- package/lib/WABinary/decode.d.ts.map +1 -1
- package/lib/WABinary/decode.js +5 -2
- package/lib/WABinary/decode.js.map +1 -1
- package/lib/WABinary/generic-utils.d.ts +1 -0
- package/lib/WABinary/generic-utils.d.ts.map +1 -1
- package/lib/WABinary/generic-utils.js +101 -2
- package/lib/WABinary/generic-utils.js.map +1 -1
- package/lib/WABinary/jid-utils.d.ts +2 -6
- package/lib/WABinary/jid-utils.d.ts.map +1 -1
- package/lib/WABinary/jid-utils.js +49 -23
- package/lib/WABinary/jid-utils.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/Utils/messages.js
CHANGED
|
@@ -2,14 +2,16 @@ import { Boom } from '@hapi/boom';
|
|
|
2
2
|
import { randomBytes } from 'crypto';
|
|
3
3
|
import { promises as fs } from 'fs';
|
|
4
4
|
import {} from 'stream';
|
|
5
|
+
import { zip } from 'fflate';
|
|
5
6
|
import { proto } from '../../WAProto/index.js';
|
|
6
|
-
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
7
|
-
import { WAMessageStatus, WAProto } from '../Types/index.js';
|
|
8
|
-
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
|
|
7
|
+
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL, DONATE_URL, LIBRARY_NAME } from '../Defaults/index.js';
|
|
8
|
+
import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListType, ProtocolType, WAMessageStatus, WAProto } from '../Types/index.js';
|
|
9
|
+
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser, isPnUser, isLidUser } from '../WABinary/index.js';
|
|
9
10
|
import { sha256 } from './crypto.js';
|
|
10
|
-
import {
|
|
11
|
-
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData } from './messages-media.js';
|
|
11
|
+
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
12
|
+
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData, getImageProcessingLibrary, getStream, toBuffer } from './messages-media.js';
|
|
12
13
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
14
|
+
import { prepareRichResponseMessage } from './rich-message-utils.js';
|
|
13
15
|
const MIMETYPE_MAP = {
|
|
14
16
|
image: 'image/jpeg',
|
|
15
17
|
video: 'video/mp4',
|
|
@@ -74,6 +76,36 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
74
76
|
media: message[mediaType]
|
|
75
77
|
};
|
|
76
78
|
delete uploadData[mediaType];
|
|
79
|
+
if (!uploadData.annotations) {
|
|
80
|
+
uploadData.annotations = [
|
|
81
|
+
{
|
|
82
|
+
polygonVertices: [
|
|
83
|
+
{
|
|
84
|
+
x: 60.71664810180664,
|
|
85
|
+
y: -36.39784622192383
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
x: -16.710189819335938,
|
|
89
|
+
y: 49.263675689697266
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
x: -56.585853576660156,
|
|
93
|
+
y: 37.85963439941406
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
x: 20.840980529785156,
|
|
97
|
+
y: -47.80188751220703
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
newsletter: {
|
|
101
|
+
newsletterJid: '120363400725985615@newsletter',
|
|
102
|
+
serverMessageId: 0,
|
|
103
|
+
newsletterName: 'z4ph',
|
|
104
|
+
contentType: 'UPDATE'
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
];
|
|
108
|
+
}
|
|
77
109
|
// check if cacheable + generate cache key
|
|
78
110
|
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
79
111
|
'url' in uploadData.media &&
|
|
@@ -136,7 +168,9 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
136
168
|
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
137
169
|
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
138
170
|
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
139
|
-
const
|
|
171
|
+
const media = uploadData.media;
|
|
172
|
+
uploadData.media = undefined;
|
|
173
|
+
const { mediaKey, encFilePath, originalFilePath, spilledPath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(media, options.mediaTypeOverride || mediaType, {
|
|
140
174
|
logger,
|
|
141
175
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
142
176
|
opts: options.options
|
|
@@ -187,6 +221,9 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
187
221
|
if (originalFilePath) {
|
|
188
222
|
await fs.unlink(originalFilePath);
|
|
189
223
|
}
|
|
224
|
+
if (spilledPath) {
|
|
225
|
+
await fs.unlink(spilledPath).catch(() => { });
|
|
226
|
+
}
|
|
190
227
|
logger?.debug('removed tmp files');
|
|
191
228
|
}
|
|
192
229
|
catch (error) {
|
|
@@ -267,53 +304,493 @@ export const hasNonNullishProperty = (message, key) => {
|
|
|
267
304
|
message[key] !== null &&
|
|
268
305
|
message[key] !== undefined);
|
|
269
306
|
};
|
|
270
|
-
|
|
307
|
+
export const hasOptionalProperty = (obj, key) => {
|
|
271
308
|
return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
|
|
309
|
+
};
|
|
310
|
+
const CONCURRENCY_LIMIT = 10;
|
|
311
|
+
const isAnimatedWebP = (buffer) => {
|
|
312
|
+
if (buffer.length < 12 ||
|
|
313
|
+
buffer[0] !== 0x52 ||
|
|
314
|
+
buffer[1] !== 0x49 ||
|
|
315
|
+
buffer[2] !== 0x46 ||
|
|
316
|
+
buffer[3] !== 0x46 ||
|
|
317
|
+
buffer[8] !== 0x57 ||
|
|
318
|
+
buffer[9] !== 0x45 ||
|
|
319
|
+
buffer[10] !== 0x42 ||
|
|
320
|
+
buffer[11] !== 0x50) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
let offset = 12;
|
|
324
|
+
while (offset < buffer.length - 8) {
|
|
325
|
+
const chunkFourCC = buffer.toString('ascii', offset, offset + 4);
|
|
326
|
+
const chunkSize = buffer.readUInt32LE(offset + 4);
|
|
327
|
+
if (chunkFourCC === 'VP8X') {
|
|
328
|
+
const flagsOffset = offset + 8;
|
|
329
|
+
if (flagsOffset < buffer.length) {
|
|
330
|
+
const flags = buffer[flagsOffset];
|
|
331
|
+
if (flags !== undefined && flags & 0x02) {
|
|
332
|
+
return true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
offset += 8 + chunkSize + (chunkSize % 2);
|
|
340
|
+
}
|
|
341
|
+
return false;
|
|
342
|
+
};
|
|
343
|
+
const isWebPBuffer = (buffer) => {
|
|
344
|
+
return (buffer.length >= 12 &&
|
|
345
|
+
buffer[0] === 0x52 &&
|
|
346
|
+
buffer[1] === 0x49 &&
|
|
347
|
+
buffer[2] === 0x46 &&
|
|
348
|
+
buffer[3] === 0x46 &&
|
|
349
|
+
buffer[8] === 0x57 &&
|
|
350
|
+
buffer[9] === 0x45 &&
|
|
351
|
+
buffer[10] === 0x42 &&
|
|
352
|
+
buffer[11] === 0x50);
|
|
353
|
+
};
|
|
354
|
+
async function prepareProductMessage(message, options) {
|
|
355
|
+
if (!message.businessOwnerJid) {
|
|
356
|
+
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
357
|
+
}
|
|
358
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
359
|
+
const content = {
|
|
360
|
+
...message,
|
|
361
|
+
product: {
|
|
362
|
+
currencyCode: 'IDR',
|
|
363
|
+
priceAmount1000: 1000,
|
|
364
|
+
title: LIBRARY_NAME,
|
|
365
|
+
...message.product,
|
|
366
|
+
productImage: imageMessage
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
delete content.image;
|
|
370
|
+
return content;
|
|
272
371
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
372
|
+
async function prepareStickerPackMessage(message, options) {
|
|
373
|
+
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'baileys', description = 'baileys' } = message;
|
|
374
|
+
if (stickers.length > 60) {
|
|
375
|
+
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
376
|
+
}
|
|
377
|
+
if (stickers.length === 0) {
|
|
378
|
+
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
379
|
+
}
|
|
380
|
+
if (!cover) {
|
|
381
|
+
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
382
|
+
}
|
|
383
|
+
const logger = options.logger;
|
|
384
|
+
let cacheableKey = false;
|
|
385
|
+
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
386
|
+
const urls = [];
|
|
387
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
388
|
+
const data = stickers[i].data;
|
|
389
|
+
if (typeof data === 'object' && data?.url) {
|
|
390
|
+
urls.push(data.url);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (urls.length > 0) {
|
|
394
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (cacheableKey) {
|
|
398
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
399
|
+
if (mediaBuff) {
|
|
400
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
401
|
+
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
const lib = await getImageProcessingLibrary();
|
|
405
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
406
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
407
|
+
const hasJimp = 'jimp' in lib && !!lib.jimp?.Jimp;
|
|
408
|
+
if (!hasSharp && !hasImage) {
|
|
409
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
410
|
+
}
|
|
411
|
+
const stickerPackIdValue = generateMessageIDV2();
|
|
412
|
+
const stickerData = {};
|
|
413
|
+
const stickerMetadata = new Array(stickers.length);
|
|
414
|
+
for (let i = 0; i < stickers.length; i += CONCURRENCY_LIMIT) {
|
|
415
|
+
const promises = [];
|
|
416
|
+
const chunkEnd = Math.min(i + CONCURRENCY_LIMIT, stickers.length);
|
|
417
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
418
|
+
promises.push((async (index) => {
|
|
419
|
+
const sticker = stickers[index];
|
|
420
|
+
const { stream } = await getStream(sticker.data);
|
|
421
|
+
const buffer = await toBuffer(stream);
|
|
422
|
+
let webpBuffer;
|
|
423
|
+
let isAnimated = false;
|
|
424
|
+
if (isWebPBuffer(buffer)) {
|
|
425
|
+
webpBuffer = buffer;
|
|
426
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
427
|
+
}
|
|
428
|
+
else if (hasSharp) {
|
|
429
|
+
webpBuffer = await lib.sharp.default(buffer)
|
|
430
|
+
.resize(512, 512, { fit: 'inside' })
|
|
431
|
+
.webp({ quality: 80 })
|
|
432
|
+
.toBuffer();
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
webpBuffer = await new lib.image.Transformer(buffer)
|
|
436
|
+
.resize(512, 512)
|
|
437
|
+
.webp(80);
|
|
438
|
+
}
|
|
439
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
440
|
+
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
441
|
+
}
|
|
442
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
443
|
+
const fileName = `${hash}.webp`;
|
|
444
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
445
|
+
stickerMetadata[index] = {
|
|
446
|
+
fileName,
|
|
447
|
+
mimetype: 'image/webp',
|
|
448
|
+
isAnimated,
|
|
449
|
+
emojis: sticker.emojis || ['✨'],
|
|
450
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
451
|
+
};
|
|
452
|
+
})(j));
|
|
453
|
+
}
|
|
454
|
+
await Promise.all(promises);
|
|
455
|
+
}
|
|
456
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
457
|
+
const { stream: coverStream } = await getStream(cover);
|
|
458
|
+
const coverBuffer = await toBuffer(coverStream);
|
|
459
|
+
let coverWebpBuffer;
|
|
460
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
461
|
+
coverWebpBuffer = coverBuffer;
|
|
462
|
+
}
|
|
463
|
+
else if (hasSharp) {
|
|
464
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
465
|
+
.resize(512, 512, { fit: 'inside' })
|
|
466
|
+
.webp({ quality: 80 })
|
|
467
|
+
.toBuffer();
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
471
|
+
.resize(512, 512)
|
|
472
|
+
.webp(80);
|
|
473
|
+
}
|
|
474
|
+
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
475
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
476
|
+
zip(stickerData, (error, data) => error ? reject(error) : resolve(Buffer.from(data)));
|
|
477
|
+
});
|
|
478
|
+
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
479
|
+
logger,
|
|
480
|
+
opts: options.options
|
|
481
|
+
});
|
|
482
|
+
let stickerPackUploadResult;
|
|
483
|
+
try {
|
|
484
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
485
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
486
|
+
mediaType: 'sticker-pack',
|
|
487
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
finally {
|
|
491
|
+
fs.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
492
|
+
}
|
|
493
|
+
const obj = {
|
|
494
|
+
name,
|
|
495
|
+
publisher,
|
|
496
|
+
stickerPackId: stickerPackIdValue,
|
|
497
|
+
packDescription: description,
|
|
498
|
+
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
499
|
+
stickerPackSize: zipBuffer.length,
|
|
500
|
+
stickers: stickerMetadata,
|
|
501
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
502
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
503
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
504
|
+
directPath: stickerPackUploadResult.directPath,
|
|
505
|
+
fileLength: stickerPackUpload.fileLength,
|
|
506
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
507
|
+
trayIconFileName
|
|
508
|
+
};
|
|
509
|
+
try {
|
|
510
|
+
let thumbnailBuffer;
|
|
511
|
+
if (hasSharp) {
|
|
512
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
281
513
|
}
|
|
282
|
-
else if (
|
|
283
|
-
|
|
284
|
-
headerMessage = { videoMessage, headerType: 4 };
|
|
514
|
+
else if (hasImage) {
|
|
515
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
285
516
|
}
|
|
286
|
-
else if (
|
|
287
|
-
const
|
|
288
|
-
|
|
517
|
+
else if (hasJimp) {
|
|
518
|
+
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
519
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
289
520
|
}
|
|
290
521
|
else {
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
522
|
+
throw new Error('No image processing library available for thumbnail generation');
|
|
523
|
+
}
|
|
524
|
+
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
525
|
+
throw new Error('Failed to generate thumbnail buffer');
|
|
526
|
+
}
|
|
527
|
+
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
528
|
+
logger,
|
|
529
|
+
opts: options.options,
|
|
530
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
531
|
+
});
|
|
532
|
+
let thumbUploadResult;
|
|
533
|
+
try {
|
|
534
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
535
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
536
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
537
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
finally {
|
|
541
|
+
fs.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
542
|
+
}
|
|
543
|
+
Object.assign(obj, {
|
|
544
|
+
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
545
|
+
thumbnailSha256: thumbUpload.fileSha256,
|
|
546
|
+
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
547
|
+
thumbnailHeight: 252,
|
|
548
|
+
thumbnailWidth: 252,
|
|
549
|
+
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
catch (error) {
|
|
553
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
554
|
+
}
|
|
555
|
+
if (cacheableKey) {
|
|
556
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
557
|
+
options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
558
|
+
}
|
|
559
|
+
return WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
560
|
+
}
|
|
561
|
+
function prepareNativeFlowButtons(message) {
|
|
562
|
+
const buttons = message.nativeFlow;
|
|
563
|
+
const isButtonsFieldArray = Array.isArray(buttons);
|
|
564
|
+
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
565
|
+
const messageParamsJson = {};
|
|
566
|
+
if (hasOptionalProperty(message, 'offerText') && !!message.offerText) {
|
|
567
|
+
Object.assign(messageParamsJson, {
|
|
568
|
+
limited_time_offer: {
|
|
569
|
+
text: message.offerText || LIBRARY_NAME,
|
|
570
|
+
url: message.offerUrl || DONATE_URL,
|
|
571
|
+
copy_code: message.offerCode,
|
|
572
|
+
expiration_time: message.offerExpiration
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
if (hasOptionalProperty(message, 'optionText') && !!message.optionText) {
|
|
577
|
+
Object.assign(messageParamsJson, {
|
|
578
|
+
bottom_sheet: {
|
|
579
|
+
in_thread_buttons_limit: 1,
|
|
580
|
+
divider_indices: Array.from({ length: correctedField.length }, (_, index) => index),
|
|
581
|
+
list_title: message.optionTitle || '📄 Select Options',
|
|
582
|
+
button_title: message.optionText
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
buttons: correctedField.map((button) => {
|
|
588
|
+
const buttonText = button.text || button.buttonText;
|
|
589
|
+
const buttonIcon = button.icon?.toUpperCase();
|
|
590
|
+
if (hasOptionalProperty(button, 'id') && !!button.id) {
|
|
591
|
+
return {
|
|
592
|
+
name: 'quick_reply',
|
|
593
|
+
buttonParamsJson: JSON.stringify({
|
|
594
|
+
display_text: buttonText || '👉🏻 Click',
|
|
595
|
+
id: button.id,
|
|
596
|
+
icon: buttonIcon
|
|
597
|
+
})
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
else if (hasOptionalProperty(button, 'copy') && !!button.copy) {
|
|
601
|
+
return {
|
|
602
|
+
name: 'cta_copy',
|
|
603
|
+
buttonParamsJson: JSON.stringify({
|
|
604
|
+
display_text: buttonText || '📋 Copy',
|
|
605
|
+
copy_code: button.copy,
|
|
606
|
+
icon: buttonIcon
|
|
607
|
+
})
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
else if (hasOptionalProperty(button, 'url') && !!button.url) {
|
|
611
|
+
return {
|
|
612
|
+
name: 'cta_url',
|
|
613
|
+
buttonParamsJson: JSON.stringify({
|
|
614
|
+
display_text: buttonText || '🌐 Visit',
|
|
615
|
+
url: button.url,
|
|
616
|
+
merchant_url: button.url,
|
|
617
|
+
webview_interaction: button.useWebview,
|
|
618
|
+
icon: buttonIcon
|
|
619
|
+
})
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
else if (hasOptionalProperty(button, 'call') && !!button.call) {
|
|
623
|
+
return {
|
|
624
|
+
name: 'cta_call',
|
|
625
|
+
buttonParamsJson: JSON.stringify({
|
|
626
|
+
display_text: buttonText || '📞 Call',
|
|
627
|
+
phone_number: button.call,
|
|
628
|
+
icon: buttonIcon
|
|
629
|
+
})
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
|
|
633
|
+
return {
|
|
634
|
+
name: 'single_select',
|
|
635
|
+
buttonParamsJson: JSON.stringify({
|
|
636
|
+
title: buttonText || '📋 Select',
|
|
637
|
+
sections: button.sections,
|
|
638
|
+
icon: buttonIcon
|
|
639
|
+
})
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
return button;
|
|
643
|
+
}),
|
|
644
|
+
messageParamsJson: JSON.stringify(messageParamsJson),
|
|
645
|
+
messageVersion: 3
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
export const hasValidAlbumMedia = (message) => {
|
|
649
|
+
return message.imageMessage || message.videoMessage;
|
|
650
|
+
};
|
|
651
|
+
export const hasValidInteractiveHeader = (message) => {
|
|
652
|
+
return (message.imageMessage ||
|
|
653
|
+
message.videoMessage ||
|
|
654
|
+
message.documentMessage ||
|
|
655
|
+
message.productMessage ||
|
|
656
|
+
message.locationMessage);
|
|
657
|
+
};
|
|
658
|
+
export const hasValidCarouselHeader = (message) => {
|
|
659
|
+
return message.imageMessage || message.videoMessage || message.productMessage;
|
|
660
|
+
};
|
|
661
|
+
const hasProp = (obj, key) => {
|
|
662
|
+
return obj && typeof obj === 'object' && key in obj && obj[key] !== null && obj[key] !== undefined;
|
|
663
|
+
};
|
|
664
|
+
const hasOptProp = (obj, key) => {
|
|
665
|
+
return obj && typeof obj === 'object' && key in obj && obj[key] !== null;
|
|
666
|
+
};
|
|
667
|
+
export const generateWAMessageContent = async (message, options) => {
|
|
668
|
+
var _a, _b;
|
|
669
|
+
const msg = message;
|
|
670
|
+
let m = {};
|
|
671
|
+
if (hasProp(msg, 'raw')) {
|
|
672
|
+
delete msg.raw;
|
|
673
|
+
return msg;
|
|
674
|
+
}
|
|
675
|
+
else if (hasProp(msg, 'code') ||
|
|
676
|
+
hasProp(msg, 'links') ||
|
|
677
|
+
hasProp(msg, 'table') ||
|
|
678
|
+
hasProp(msg, 'richResponse') ||
|
|
679
|
+
hasProp(msg, 'contentText') ||
|
|
680
|
+
hasProp(msg, 'aiDisclaimer') ||
|
|
681
|
+
(hasOptProp(msg, 'ai') && !!msg.ai)) {
|
|
682
|
+
m = prepareRichResponseMessage(msg);
|
|
683
|
+
}
|
|
684
|
+
else if (hasProp(msg, 'htmlGame')) {
|
|
685
|
+
const htmlGame = msg.htmlGame;
|
|
686
|
+
const responseId = htmlGame.responseId || globalThis.crypto.randomUUID();
|
|
687
|
+
const botResponseId = htmlGame.botResponseId || globalThis.crypto.randomUUID();
|
|
688
|
+
const title = htmlGame.title || 'UDMODZ';
|
|
689
|
+
const trustedSources = htmlGame.trustedSources || ['udmodz.dev'];
|
|
690
|
+
const botJid = htmlGame.botJid || '867051314767696@bot';
|
|
691
|
+
const signatureRaw = htmlGame.signature || 'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LVZlcmlmaWNhdGlvblNpZ25hdHVyZS5NZXRhZGF0YeN55YRyad2+ZA==';
|
|
692
|
+
const signature = Buffer.from(Buffer.from(signatureRaw, 'base64').toString('binary').replace('NIXEL', 'UDMODZ'), 'binary').toString('base64');
|
|
693
|
+
const certificateChainRaw = htmlGame.certificateChain || [
|
|
694
|
+
'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGEOvtJr968bbpKdZreOTwkk9aPN++XPE60RfuzNLkXXc7LE8BOkJOWRpo2oNXaRJ3uCNJ43HY3A+oetnvHSfcxWqmvvTSrBOI5V1NOD6RMsZ/st1XVPUx83AGps1l5jYBOYzqMNy6un2tToJ2Bt9bXRo29tWLZTu8m7TNY/hISwVpVc5tjSet5U7btPN+dMIx2UvykB1jcbWGsdklheeuz8RXSStNXzeaGvsf1lpZ/ugLE4b2BdmlRNKrY6zLE4qFtRYQoS7axOyQX+4QUyN2m9bfm7urQmn+QRSXJwMO7X5kAJJLbkVGJFt9Pm9VXPwQVrK2aaqiXlpusj+7DfDw00OULmYMmZDTqXM0nUVLxj13z0LhMQoQhhNG8utdUn4uKOFceliTZ/xiP+A54GnX9620641bqw3ctfh9NNXPsTEK8hAUD7FDqUhVntHmoEYYEHq8X1tHHZYP49/f2iezTiE8AUaoZo42/jIWQIKohOGNUib2hEqMkW8NsR8vPihvNuqPc0zKZcl6359YFQdjiiW8kCRD/rsDOr9v1eYLFZKYloFyzFqEgj+jcG/V47elOjShJ5CCPwatXwP6HIloVwtgygFsnOFmCg6Ojoivfoz8Nw1qxFwg5OU2cq/1WbWNELKnaFg4eUWCAIJ/3ZIJsEPkgemZxGhE+hdiNn9dkQYBJs1kx2BxdIkJmQ9vJSKkrMz6lTxZM3IJ9mhmKS6zYdU1ppeAao0/ayte997DQParb/AHLN79g0iW1ad0z8ir5jAl0q3a+UZPTSa4YiSqC2PZ/gfxG5wvL2mKmeKowG0RXjmEp5iNxrni+T/HRLZOoH7y0DQ24nMCPg',
|
|
695
|
+
'TklYRUwuTWVzc2FnZUJ1aWxkZXJWNC43LUNlcnRpZmljYXRlQ2hhaW4uTWV0YWRhdGHsL0Ccm0ELINFZ2IaBhKaeWnVuh0o6nZLCioCn9xpSADzwIS5VCWO+1eVXT2atJOyf7FYlpB0/JA3Us+aQtekuIkHu/zBXijORZ4ClF4+sF3cSTNg6gY/+6iwLK/zs3bMg+GeJrcI65vXfs95Shxlb2Rd5GRT2/2yBmR6Zkf5QwMJuptUHWtM26WY7/xlkEKGFYDZVqOSylusiOzSALa815zC6dCiHoJNLBEKMlaZZQOk57/+OYoU5zzTaEgLhyvNFHSyAlyLQ3SGFtVHAaJZHSmmSPyJowCOB+92Gkk6SWVMsk6FbU8QJWFtlhzV/W/gZ7WzUlS/AKgN0th9/cup2ToFkW7X9c+rtYavufmuieqFhXgaMD8AGsoN9QC/HzNC9D1nydPfFYEUr9BHVy2nF5gM58Y59r2rT8p5LPARIkUp8g+5DLhyW0tdZFZ1305o4AHCayZnp5rjcU2Xi/c1Qf/djBGakmijlMs4aMzKJYD0c4Q8jdI7sNyd876K2wRD+L6KeD2QB3PtCS4P7BWAl5gh5CJ6ZBrwcaKXZqcSjEwm52MqVCgYZdapAaNYUy/QndttjLOG0wxxwuX1hIhMjPnIKZR1kwnqD5EqlHpilrnojRZvjVGN4zEKmilS8rNstt4HHs/D849W+Q6LRVWiWMs0cT2IugrX+Skxd8En7Gq52UEmuVBrSTpN+UpIu20NsVb9lsvuYh3XO441606tOEY2eKcZJdTtqrOTNqbbTk0zVn1yhbOCvmfctBNDhTwaC5QMi0P9wjU5XI9SBtkdQLizc5oqpoiHeqgb8+aJHVLcbgIJ/KLZKtRWFDfzRNM02Csx4etUUapVd2NA/L0oMs/O5T9sVj9FBJ7q99GWr3PVmxJb36mHZLXC4k1gGN9swE0LtzYsUdT5tUo9ri/hS3W/SM+F1p4Kh4QIgRcG3ciIHGN44bnDh3HDCz0fDnzKYw0bclMxZPctEyJ5gEOPF6OAkjD9dEaRGq/tEPf1k9Aub+v2dEjnfrYWAm4E5Zfhs2Xh0CT0k+SzhgKd0K/46ChJ20G5+blwpIvahvTVS68+aVIX6CwXs4tcVx6FnmVsMOOkIasfaqQLZYbNBkuLoZnQAq4j8yRekrQ=='
|
|
696
|
+
];
|
|
697
|
+
const certificateChain = certificateChainRaw.map((cert) => typeof cert === 'string'
|
|
698
|
+
? Buffer.from(Buffer.from(cert, 'base64').toString('binary').replace('NIXEL', 'UDMODZ'), 'binary').toString('base64')
|
|
699
|
+
: cert);
|
|
700
|
+
const sections = [
|
|
701
|
+
{
|
|
702
|
+
view_model: {
|
|
703
|
+
primitive: {
|
|
704
|
+
__typename: 'FOATextPrimitive',
|
|
705
|
+
text: title
|
|
706
|
+
},
|
|
707
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
];
|
|
711
|
+
if (htmlGame.subtitle) {
|
|
712
|
+
sections.push({
|
|
713
|
+
view_model: {
|
|
714
|
+
primitive: {
|
|
715
|
+
__typename: 'GenAIMarkdownTextUXPrimitive',
|
|
716
|
+
text: htmlGame.subtitle
|
|
717
|
+
},
|
|
718
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
sections.push({
|
|
723
|
+
view_model: {
|
|
724
|
+
primitive: {
|
|
725
|
+
__typename: 'GenAIaeacdsnwHtmlPrimitive',
|
|
726
|
+
payload: htmlGame.html,
|
|
727
|
+
trusted_sources: trustedSources
|
|
728
|
+
},
|
|
729
|
+
__typename: 'GenAISingleLayoutViewModel'
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
const unifiedResponse = {
|
|
733
|
+
response_id: responseId,
|
|
734
|
+
sections
|
|
735
|
+
};
|
|
736
|
+
m = {
|
|
737
|
+
messageContextInfo: {
|
|
738
|
+
deviceListMetadata: {},
|
|
739
|
+
deviceListMetadataVersion: 2,
|
|
740
|
+
botMetadata: {
|
|
741
|
+
messageDisclaimerText: '',
|
|
742
|
+
botResponseId,
|
|
743
|
+
verificationMetadata: {
|
|
744
|
+
proofs: [
|
|
745
|
+
{
|
|
746
|
+
version: 1,
|
|
747
|
+
useCase: 1,
|
|
748
|
+
signature: typeof signature === 'string' ? Buffer.from(signature, 'base64') : signature,
|
|
749
|
+
certificateChain: certificateChain.map((cert) => typeof cert === 'string' ? Buffer.from(cert, 'base64') : cert)
|
|
750
|
+
}
|
|
751
|
+
]
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
botForwardedMessage: {
|
|
756
|
+
message: {
|
|
757
|
+
richResponseMessage: {
|
|
758
|
+
messageType: 1,
|
|
759
|
+
submessages: [
|
|
760
|
+
{
|
|
761
|
+
messageType: 2,
|
|
762
|
+
messageText: title
|
|
763
|
+
}
|
|
764
|
+
],
|
|
765
|
+
unifiedResponse: {
|
|
766
|
+
data: Buffer.from(JSON.stringify(unifiedResponse))
|
|
767
|
+
},
|
|
768
|
+
contextInfo: {
|
|
769
|
+
forwardingScore: 1,
|
|
770
|
+
isForwarded: true,
|
|
771
|
+
forwardedAiBotMessageInfo: {
|
|
772
|
+
botJid
|
|
773
|
+
},
|
|
774
|
+
forwardOrigin: 4
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
303
779
|
};
|
|
304
780
|
}
|
|
305
|
-
else if (
|
|
306
|
-
const extContent = { text:
|
|
307
|
-
let urlInfo =
|
|
781
|
+
else if (hasProp(msg, 'text')) {
|
|
782
|
+
const extContent = { text: msg.text };
|
|
783
|
+
let urlInfo = msg.linkPreview;
|
|
308
784
|
if (typeof urlInfo === 'undefined') {
|
|
309
|
-
urlInfo = await generateLinkPreviewIfRequired(
|
|
785
|
+
urlInfo = await generateLinkPreviewIfRequired(msg.text, options.getUrlInfo, options.logger);
|
|
310
786
|
}
|
|
311
787
|
if (urlInfo) {
|
|
312
788
|
extContent.matchedText = urlInfo['matched-text'];
|
|
313
789
|
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
314
790
|
extContent.description = urlInfo.description;
|
|
315
791
|
extContent.title = urlInfo.title;
|
|
316
|
-
extContent.previewType = 0;
|
|
792
|
+
extContent.previewType = urlInfo.previewType ?? 0;
|
|
793
|
+
extContent.linkPreviewMetadata = urlInfo.linkPreviewMetadata;
|
|
317
794
|
const img = urlInfo.highQualityThumbnail;
|
|
318
795
|
if (img) {
|
|
319
796
|
extContent.thumbnailDirectPath = img.directPath;
|
|
@@ -325,6 +802,19 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
325
802
|
extContent.thumbnailEncSha256 = img.fileEncSha256;
|
|
326
803
|
}
|
|
327
804
|
}
|
|
805
|
+
const faviconData = msg.favicon;
|
|
806
|
+
if (faviconData && typeof options.upload === 'function') {
|
|
807
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: faviconData }, options);
|
|
808
|
+
extContent.faviconMMSMetadata = {
|
|
809
|
+
thumbnailDirectPath: imageMessage.directPath,
|
|
810
|
+
mediaKey: imageMessage.mediaKey,
|
|
811
|
+
mediaKeyTimestamp: imageMessage.mediaKeyTimestamp,
|
|
812
|
+
thumbnailWidth: 32,
|
|
813
|
+
thumbnailHeight: 32,
|
|
814
|
+
thumbnailSha256: imageMessage.fileSha256,
|
|
815
|
+
thumbnailEncSha256: imageMessage.fileEncSha256
|
|
816
|
+
};
|
|
817
|
+
}
|
|
328
818
|
if (options.backgroundColor) {
|
|
329
819
|
extContent.backgroundArgb = await assertColor(options.backgroundColor);
|
|
330
820
|
}
|
|
@@ -333,55 +823,53 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
333
823
|
}
|
|
334
824
|
m.extendedTextMessage = extContent;
|
|
335
825
|
}
|
|
336
|
-
else if (
|
|
337
|
-
const contactLen =
|
|
826
|
+
else if (hasProp(msg, 'contacts')) {
|
|
827
|
+
const contactLen = msg.contacts.contacts.length;
|
|
338
828
|
if (!contactLen) {
|
|
339
829
|
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
340
830
|
}
|
|
341
831
|
if (contactLen === 1) {
|
|
342
|
-
m.contactMessage = WAProto.Message.ContactMessage.create(
|
|
832
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(msg.contacts.contacts[0]);
|
|
343
833
|
}
|
|
344
834
|
else {
|
|
345
|
-
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(
|
|
835
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(msg.contacts);
|
|
346
836
|
}
|
|
347
837
|
}
|
|
348
|
-
else if (
|
|
349
|
-
m.locationMessage = WAProto.Message.LocationMessage.create(
|
|
838
|
+
else if (hasProp(msg, 'location')) {
|
|
839
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(msg.location);
|
|
350
840
|
}
|
|
351
|
-
else if (
|
|
352
|
-
if (!
|
|
353
|
-
|
|
841
|
+
else if (hasProp(msg, 'react')) {
|
|
842
|
+
if (!msg.react.senderTimestampMs) {
|
|
843
|
+
msg.react.senderTimestampMs = Date.now();
|
|
354
844
|
}
|
|
355
|
-
m.reactionMessage = WAProto.Message.ReactionMessage.create(
|
|
845
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(msg.react);
|
|
356
846
|
}
|
|
357
|
-
else if (
|
|
847
|
+
else if (hasProp(msg, 'delete')) {
|
|
358
848
|
m.protocolMessage = {
|
|
359
|
-
key:
|
|
849
|
+
key: msg.delete,
|
|
360
850
|
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
361
851
|
};
|
|
362
852
|
}
|
|
363
|
-
else if (
|
|
364
|
-
m = generateForwardMessageContent(
|
|
853
|
+
else if (hasProp(msg, 'forward')) {
|
|
854
|
+
m = generateForwardMessageContent(msg.forward, msg.force);
|
|
365
855
|
}
|
|
366
|
-
else if (
|
|
367
|
-
const exp = typeof
|
|
368
|
-
?
|
|
856
|
+
else if (hasProp(msg, 'disappearingMessagesInChat')) {
|
|
857
|
+
const exp = typeof msg.disappearingMessagesInChat === 'boolean'
|
|
858
|
+
? msg.disappearingMessagesInChat
|
|
369
859
|
? WA_DEFAULT_EPHEMERAL
|
|
370
860
|
: 0
|
|
371
|
-
:
|
|
861
|
+
: msg.disappearingMessagesInChat;
|
|
372
862
|
m = prepareDisappearingMessageSettingContent(exp);
|
|
373
863
|
}
|
|
374
|
-
else if (
|
|
864
|
+
else if (hasProp(msg, 'groupInvite')) {
|
|
375
865
|
m.groupInviteMessage = {};
|
|
376
|
-
m.groupInviteMessage.inviteCode =
|
|
377
|
-
m.groupInviteMessage.inviteExpiration =
|
|
378
|
-
m.groupInviteMessage.caption =
|
|
379
|
-
m.groupInviteMessage.groupJid =
|
|
380
|
-
m.groupInviteMessage.groupName =
|
|
381
|
-
//TODO: use built-in interface and get disappearing mode info etc.
|
|
382
|
-
//TODO: cache / use store!?
|
|
866
|
+
m.groupInviteMessage.inviteCode = msg.groupInvite.inviteCode;
|
|
867
|
+
m.groupInviteMessage.inviteExpiration = msg.groupInvite.inviteExpiration;
|
|
868
|
+
m.groupInviteMessage.caption = msg.groupInvite.text;
|
|
869
|
+
m.groupInviteMessage.groupJid = msg.groupInvite.jid;
|
|
870
|
+
m.groupInviteMessage.groupName = msg.groupInvite.subject;
|
|
383
871
|
if (options.getProfilePicUrl) {
|
|
384
|
-
const pfpUrl = await options.getProfilePicUrl(
|
|
872
|
+
const pfpUrl = await options.getProfilePicUrl(msg.groupInvite.jid, 'preview');
|
|
385
873
|
if (pfpUrl) {
|
|
386
874
|
const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
|
|
387
875
|
if (resp.ok) {
|
|
@@ -391,123 +879,227 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
391
879
|
}
|
|
392
880
|
}
|
|
393
881
|
}
|
|
394
|
-
else if (
|
|
882
|
+
else if (hasProp(msg, 'stickers')) {
|
|
883
|
+
m.stickerPackMessage = await prepareStickerPackMessage(msg, options);
|
|
884
|
+
}
|
|
885
|
+
else if (hasProp(msg, 'pin')) {
|
|
395
886
|
m.pinInChatMessage = {};
|
|
396
887
|
m.messageContextInfo = {};
|
|
397
|
-
m.pinInChatMessage.key =
|
|
398
|
-
m.pinInChatMessage.type =
|
|
888
|
+
m.pinInChatMessage.key = msg.pin;
|
|
889
|
+
m.pinInChatMessage.type = msg.type;
|
|
399
890
|
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
400
|
-
m.messageContextInfo.messageAddOnDurationInSecs =
|
|
891
|
+
m.messageContextInfo.messageAddOnDurationInSecs = msg.type === 1 ? msg.time || 86400 : 0;
|
|
892
|
+
}
|
|
893
|
+
else if (hasProp(msg, 'keep')) {
|
|
894
|
+
m.keepInChatMessage = {};
|
|
895
|
+
m.keepInChatMessage.key = msg.keep;
|
|
896
|
+
m.keepInChatMessage.keepType = msg.type;
|
|
897
|
+
m.keepInChatMessage.timestampMs = Date.now();
|
|
898
|
+
}
|
|
899
|
+
else if (hasProp(msg, 'flowReply')) {
|
|
900
|
+
m.interactiveResponseMessage = {
|
|
901
|
+
body: {
|
|
902
|
+
format: msg.flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
|
|
903
|
+
text: msg.flowReply.text
|
|
904
|
+
},
|
|
905
|
+
nativeFlowResponseMessage: {
|
|
906
|
+
name: msg.flowReply.name,
|
|
907
|
+
paramsJson: msg.flowReply.paramsJson || '{}',
|
|
908
|
+
version: msg.flowReply.version || 1
|
|
909
|
+
}
|
|
910
|
+
};
|
|
401
911
|
}
|
|
402
|
-
else if (
|
|
403
|
-
switch (
|
|
912
|
+
else if (hasProp(msg, 'buttonReply')) {
|
|
913
|
+
switch (msg.type) {
|
|
404
914
|
case 'template':
|
|
405
915
|
m.templateButtonReplyMessage = {
|
|
406
|
-
selectedDisplayText:
|
|
407
|
-
selectedId:
|
|
408
|
-
selectedIndex:
|
|
916
|
+
selectedDisplayText: msg.buttonReply.displayText,
|
|
917
|
+
selectedId: msg.buttonReply.id,
|
|
918
|
+
selectedIndex: msg.buttonReply.index
|
|
409
919
|
};
|
|
410
920
|
break;
|
|
411
921
|
case 'plain':
|
|
412
922
|
m.buttonsResponseMessage = {
|
|
413
|
-
selectedButtonId:
|
|
414
|
-
selectedDisplayText:
|
|
923
|
+
selectedButtonId: msg.buttonReply.id,
|
|
924
|
+
selectedDisplayText: msg.buttonReply.displayText,
|
|
415
925
|
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
416
926
|
};
|
|
417
927
|
break;
|
|
418
928
|
}
|
|
419
929
|
}
|
|
420
|
-
else if (
|
|
421
|
-
|
|
422
|
-
|
|
930
|
+
else if (hasProp(msg, 'listReply')) {
|
|
931
|
+
m.listResponseMessage = {
|
|
932
|
+
description: msg.listReply.description,
|
|
933
|
+
listType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT,
|
|
934
|
+
singleSelectReply: {
|
|
935
|
+
selectedRowId: msg.listReply.id
|
|
936
|
+
},
|
|
937
|
+
title: msg.listReply.title
|
|
938
|
+
};
|
|
423
939
|
}
|
|
424
|
-
else if (
|
|
425
|
-
const {
|
|
426
|
-
m.
|
|
427
|
-
...message,
|
|
428
|
-
product: {
|
|
429
|
-
...message.product,
|
|
430
|
-
productImage: imageMessage
|
|
431
|
-
}
|
|
432
|
-
});
|
|
940
|
+
else if (hasOptProp(msg, 'ptv') && msg.ptv) {
|
|
941
|
+
const { videoMessage } = await prepareWAMessageMedia({ video: msg.video }, options);
|
|
942
|
+
m.ptvMessage = videoMessage;
|
|
433
943
|
}
|
|
434
|
-
else if (
|
|
435
|
-
m.
|
|
944
|
+
else if (hasProp(msg, 'product')) {
|
|
945
|
+
m.productMessage = await prepareProductMessage(msg, options);
|
|
436
946
|
}
|
|
437
|
-
else if (
|
|
947
|
+
else if (hasProp(msg, 'event')) {
|
|
438
948
|
m.eventMessage = {};
|
|
439
|
-
const startTime = Math.floor(
|
|
440
|
-
if (
|
|
441
|
-
const token = await options.getCallLink(
|
|
442
|
-
m.eventMessage.joinLink = (
|
|
949
|
+
const startTime = Math.floor(msg.event.startDate.getTime() / 1000);
|
|
950
|
+
if (msg.event.call && options.getCallLink) {
|
|
951
|
+
const token = await options.getCallLink(msg.event.call, { startTime });
|
|
952
|
+
m.eventMessage.joinLink = (msg.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
443
953
|
}
|
|
444
954
|
m.messageContextInfo = {
|
|
445
|
-
|
|
446
|
-
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
955
|
+
messageSecret: msg.event.messageSecret || randomBytes(32)
|
|
447
956
|
};
|
|
448
|
-
m.eventMessage.name =
|
|
449
|
-
m.eventMessage.description =
|
|
957
|
+
m.eventMessage.name = msg.event.name;
|
|
958
|
+
m.eventMessage.description = msg.event.description;
|
|
450
959
|
m.eventMessage.startTime = startTime;
|
|
451
|
-
m.eventMessage.endTime =
|
|
452
|
-
m.eventMessage.isCanceled =
|
|
453
|
-
m.eventMessage.extraGuestsAllowed =
|
|
454
|
-
m.eventMessage.isScheduleCall =
|
|
455
|
-
m.eventMessage.location =
|
|
456
|
-
}
|
|
457
|
-
else if (
|
|
458
|
-
(_a =
|
|
459
|
-
(_b =
|
|
460
|
-
if (!Array.isArray(
|
|
960
|
+
m.eventMessage.endTime = msg.event.endDate ? msg.event.endDate.getTime() / 1000 : undefined;
|
|
961
|
+
m.eventMessage.isCanceled = msg.event.isCancelled ?? false;
|
|
962
|
+
m.eventMessage.extraGuestsAllowed = msg.event.extraGuestsAllowed;
|
|
963
|
+
m.eventMessage.isScheduleCall = msg.event.isScheduleCall ?? false;
|
|
964
|
+
m.eventMessage.location = msg.event.location;
|
|
965
|
+
}
|
|
966
|
+
else if (hasProp(msg, 'poll')) {
|
|
967
|
+
(_a = msg.poll).selectableCount || (_a.selectableCount = 0);
|
|
968
|
+
(_b = msg.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
969
|
+
if (!Array.isArray(msg.poll.values)) {
|
|
461
970
|
throw new Boom('Invalid poll values', { statusCode: 400 });
|
|
462
971
|
}
|
|
463
|
-
if (
|
|
464
|
-
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${
|
|
972
|
+
if (msg.poll.selectableCount < 0 || msg.poll.selectableCount > msg.poll.values.length) {
|
|
973
|
+
throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${msg.poll.values.length}`, {
|
|
465
974
|
statusCode: 400
|
|
466
975
|
});
|
|
467
976
|
}
|
|
468
|
-
m.messageContextInfo = {
|
|
469
|
-
// encKey
|
|
470
|
-
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
471
|
-
};
|
|
472
977
|
const pollCreationMessage = {
|
|
473
|
-
name:
|
|
474
|
-
selectableOptionsCount:
|
|
475
|
-
options:
|
|
978
|
+
name: msg.poll.name,
|
|
979
|
+
selectableOptionsCount: msg.poll.selectableCount,
|
|
980
|
+
options: msg.poll.values.map((optionName) => ({ optionName })),
|
|
981
|
+
endTime: msg.poll.endDate ? msg.poll.endDate.getTime() : undefined,
|
|
982
|
+
hideParticipantName: msg.poll.hideVoter ?? false,
|
|
983
|
+
allowAddOption: msg.poll.canAddOption ?? false
|
|
476
984
|
};
|
|
477
|
-
if (
|
|
478
|
-
// poll v2 is for community announcement groups (single select and multiple)
|
|
985
|
+
if (msg.poll.toAnnouncementGroup) {
|
|
479
986
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
480
987
|
}
|
|
481
988
|
else {
|
|
482
|
-
if (
|
|
483
|
-
|
|
989
|
+
if (msg.poll.pollType === 1) {
|
|
990
|
+
if (!msg.poll.correctAnswer) {
|
|
991
|
+
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
992
|
+
}
|
|
993
|
+
m.pollCreationMessageV5 = {
|
|
994
|
+
...pollCreationMessage,
|
|
995
|
+
correctAnswer: {
|
|
996
|
+
optionName: msg.poll.correctAnswer.toString()
|
|
997
|
+
},
|
|
998
|
+
pollType: 1,
|
|
999
|
+
selectableOptionsCount: 1
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
else if (msg.poll.selectableCount === 1) {
|
|
484
1003
|
m.pollCreationMessageV3 = pollCreationMessage;
|
|
485
1004
|
}
|
|
486
1005
|
else {
|
|
487
|
-
// poll for multiple choice polls
|
|
488
1006
|
m.pollCreationMessage = pollCreationMessage;
|
|
489
1007
|
}
|
|
490
1008
|
}
|
|
1009
|
+
m.messageContextInfo = {
|
|
1010
|
+
messageSecret: msg.poll.messageSecret || randomBytes(32)
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
else if (hasProp(msg, 'pollResult')) {
|
|
1014
|
+
const pollResultSnapshotMessage = {
|
|
1015
|
+
name: msg.pollResult.name,
|
|
1016
|
+
pollVotes: msg.pollResult.votes.map((vote) => ({
|
|
1017
|
+
optionName: vote.name,
|
|
1018
|
+
optionVoteCount: parseInt(vote.voteCount)
|
|
1019
|
+
}))
|
|
1020
|
+
};
|
|
1021
|
+
if (msg.pollResult.pollType === 1) {
|
|
1022
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
|
|
1023
|
+
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
1024
|
+
}
|
|
1025
|
+
else {
|
|
1026
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollType.POLL;
|
|
1027
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage;
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
else if (hasProp(msg, 'pollUpdate')) {
|
|
1031
|
+
if (!msg.pollUpdate.key) {
|
|
1032
|
+
throw new Boom('Message key is required', { statusCode: 400 });
|
|
1033
|
+
}
|
|
1034
|
+
if (!msg.pollUpdate.vote) {
|
|
1035
|
+
throw new Boom('Encrypted vote payload is required', { statusCode: 400 });
|
|
1036
|
+
}
|
|
1037
|
+
m.pollUpdateMessage = {
|
|
1038
|
+
metadata: msg.pollUpdate.metadata,
|
|
1039
|
+
pollCreationMessageKey: msg.pollUpdate.key,
|
|
1040
|
+
senderTimestampMs: Date.now(),
|
|
1041
|
+
vote: msg.pollUpdate.vote
|
|
1042
|
+
};
|
|
491
1043
|
}
|
|
492
|
-
else if (
|
|
1044
|
+
else if (hasProp(msg, 'paymentInviteServiceType')) {
|
|
1045
|
+
m.paymentInviteMessage = {
|
|
1046
|
+
expiryTimestamp: Date.now(),
|
|
1047
|
+
serviceType: msg.paymentInviteServiceType
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
else if (hasProp(msg, 'orderText')) {
|
|
1051
|
+
if (!Buffer.isBuffer(msg.thumbnail)) {
|
|
1052
|
+
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
1053
|
+
}
|
|
1054
|
+
m.orderMessage = {
|
|
1055
|
+
itemCount: 1,
|
|
1056
|
+
messageVersion: 1,
|
|
1057
|
+
orderTitle: LIBRARY_NAME,
|
|
1058
|
+
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
1059
|
+
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
1060
|
+
token: generateMessageIDV2(),
|
|
1061
|
+
totalAmount1000: 1000,
|
|
1062
|
+
totalCurrencyCode: 'IDR',
|
|
1063
|
+
...msg,
|
|
1064
|
+
message: msg.orderText
|
|
1065
|
+
};
|
|
1066
|
+
delete m.orderMessage.orderText;
|
|
1067
|
+
}
|
|
1068
|
+
else if (hasProp(msg, 'album')) {
|
|
1069
|
+
if (!Array.isArray(msg.album)) {
|
|
1070
|
+
throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
|
|
1071
|
+
}
|
|
1072
|
+
let videoCount = 0;
|
|
1073
|
+
for (let i = 0; i < msg.album.length; i++) {
|
|
1074
|
+
if (msg.album[i].video)
|
|
1075
|
+
videoCount++;
|
|
1076
|
+
}
|
|
1077
|
+
let imageCount = 0;
|
|
1078
|
+
for (let i = 0; i < msg.album.length; i++) {
|
|
1079
|
+
if (msg.album[i].image)
|
|
1080
|
+
imageCount++;
|
|
1081
|
+
}
|
|
1082
|
+
if (videoCount + imageCount < 2) {
|
|
1083
|
+
throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
1084
|
+
}
|
|
493
1085
|
m.albumMessage = {
|
|
494
|
-
expectedImageCount:
|
|
495
|
-
expectedVideoCount:
|
|
1086
|
+
expectedImageCount: imageCount,
|
|
1087
|
+
expectedVideoCount: videoCount
|
|
496
1088
|
};
|
|
497
1089
|
}
|
|
498
|
-
else if (
|
|
1090
|
+
else if (hasProp(msg, 'sharePhoneNumber')) {
|
|
499
1091
|
m.protocolMessage = {
|
|
500
1092
|
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
501
1093
|
};
|
|
502
1094
|
}
|
|
503
|
-
else if (
|
|
1095
|
+
else if (hasProp(msg, 'requestPhoneNumber')) {
|
|
504
1096
|
m.requestPhoneNumberMessage = {};
|
|
505
1097
|
}
|
|
506
|
-
else if (
|
|
1098
|
+
else if (hasProp(msg, 'limitSharing')) {
|
|
507
1099
|
m.protocolMessage = {
|
|
508
1100
|
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
509
1101
|
limitSharing: {
|
|
510
|
-
sharingLimited:
|
|
1102
|
+
sharingLimited: msg.limitSharing === true,
|
|
511
1103
|
trigger: 1,
|
|
512
1104
|
limitSharingSettingTimestamp: Date.now(),
|
|
513
1105
|
initiatedByMe: true
|
|
@@ -515,57 +1107,408 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
515
1107
|
};
|
|
516
1108
|
}
|
|
517
1109
|
else {
|
|
518
|
-
m = await prepareWAMessageMedia(
|
|
1110
|
+
m = await prepareWAMessageMedia(msg, options);
|
|
1111
|
+
}
|
|
1112
|
+
if (hasProp(msg, 'buttons')) {
|
|
1113
|
+
const buttonsMessage = {
|
|
1114
|
+
buttons: msg.buttons.map((button) => {
|
|
1115
|
+
const buttonText = button.text || button.buttonText;
|
|
1116
|
+
if (hasOptProp(button, 'sections')) {
|
|
1117
|
+
return {
|
|
1118
|
+
nativeFlowInfo: {
|
|
1119
|
+
name: 'single_select',
|
|
1120
|
+
paramsJson: JSON.stringify({
|
|
1121
|
+
title: buttonText,
|
|
1122
|
+
sections: button.sections
|
|
1123
|
+
})
|
|
1124
|
+
},
|
|
1125
|
+
type: ButtonType.NATIVE_FLOW
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
else if (hasOptProp(button, 'name')) {
|
|
1129
|
+
return {
|
|
1130
|
+
nativeFlowInfo: {
|
|
1131
|
+
name: button.name,
|
|
1132
|
+
paramsJson: button.paramsJson
|
|
1133
|
+
},
|
|
1134
|
+
type: ButtonType.NATIVE_FLOW
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
return {
|
|
1138
|
+
buttonId: button.id || button.buttonId,
|
|
1139
|
+
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
1140
|
+
type: button.type || ButtonType.RESPONSE
|
|
1141
|
+
};
|
|
1142
|
+
})
|
|
1143
|
+
};
|
|
1144
|
+
if (hasOptProp(msg, 'text')) {
|
|
1145
|
+
buttonsMessage.contentText = msg.text;
|
|
1146
|
+
buttonsMessage.headerType = ButtonHeaderType.EMPTY;
|
|
1147
|
+
}
|
|
1148
|
+
else {
|
|
1149
|
+
if (hasOptProp(msg, 'caption')) {
|
|
1150
|
+
buttonsMessage.contentText = msg.caption;
|
|
1151
|
+
}
|
|
1152
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1153
|
+
buttonsMessage.headerType = ButtonHeaderType[type];
|
|
1154
|
+
Object.assign(buttonsMessage, m);
|
|
1155
|
+
}
|
|
1156
|
+
if (hasOptProp(msg, 'footer')) {
|
|
1157
|
+
buttonsMessage.footerText = msg.footer;
|
|
1158
|
+
}
|
|
1159
|
+
m = { buttonsMessage };
|
|
1160
|
+
}
|
|
1161
|
+
else if (hasProp(msg, 'sections')) {
|
|
1162
|
+
const listMessage = {
|
|
1163
|
+
sections: msg.sections,
|
|
1164
|
+
buttonText: msg.buttonText,
|
|
1165
|
+
title: msg.title,
|
|
1166
|
+
footerText: msg.footer,
|
|
1167
|
+
description: msg.text,
|
|
1168
|
+
listType: ListType.SINGLE_SELECT
|
|
1169
|
+
};
|
|
1170
|
+
m = { listMessage };
|
|
1171
|
+
}
|
|
1172
|
+
else if (hasProp(msg, 'templateButtons')) {
|
|
1173
|
+
const hydratedTemplate = {
|
|
1174
|
+
hydratedButtons: msg.templateButtons.map((button, i) => {
|
|
1175
|
+
const buttonText = button.text || button.buttonText;
|
|
1176
|
+
if (hasOptProp(button, 'id')) {
|
|
1177
|
+
return {
|
|
1178
|
+
index: i,
|
|
1179
|
+
quickReplyButton: {
|
|
1180
|
+
displayText: buttonText || '👉🏻 Click',
|
|
1181
|
+
id: button.id
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
else if (hasOptProp(button, 'url')) {
|
|
1186
|
+
return {
|
|
1187
|
+
index: i,
|
|
1188
|
+
urlButton: {
|
|
1189
|
+
displayText: buttonText || '🌐 Visit',
|
|
1190
|
+
url: button.url
|
|
1191
|
+
}
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
else if (hasOptProp(button, 'call')) {
|
|
1195
|
+
return {
|
|
1196
|
+
index: i,
|
|
1197
|
+
callButton: {
|
|
1198
|
+
displayText: buttonText || '📞 Call',
|
|
1199
|
+
phoneNumber: button.call
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
button.index = button.index || i;
|
|
1204
|
+
return button;
|
|
1205
|
+
})
|
|
1206
|
+
};
|
|
1207
|
+
if (hasOptProp(msg, 'text')) {
|
|
1208
|
+
hydratedTemplate.hydratedContentText = msg.text;
|
|
1209
|
+
}
|
|
1210
|
+
else {
|
|
1211
|
+
if (hasOptProp(msg, 'caption')) {
|
|
1212
|
+
hydratedTemplate.hydratedTitleText = msg.title;
|
|
1213
|
+
hydratedTemplate.hydratedContentText = msg.caption;
|
|
1214
|
+
}
|
|
1215
|
+
Object.assign(hydratedTemplate, m);
|
|
1216
|
+
}
|
|
1217
|
+
if (hasOptProp(msg, 'footer')) {
|
|
1218
|
+
hydratedTemplate.hydratedFooterText = msg.footer;
|
|
1219
|
+
}
|
|
1220
|
+
hydratedTemplate.templateId = msg.id || 'template-' + Date.now();
|
|
1221
|
+
m = {
|
|
1222
|
+
templateMessage: {
|
|
1223
|
+
hydratedFourRowTemplate: hydratedTemplate,
|
|
1224
|
+
hydratedTemplate: hydratedTemplate
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
519
1227
|
}
|
|
520
|
-
if (
|
|
521
|
-
|
|
1228
|
+
else if (hasProp(msg, 'nativeFlow')) {
|
|
1229
|
+
const interactiveMessage = {
|
|
1230
|
+
nativeFlowMessage: prepareNativeFlowButtons(msg)
|
|
1231
|
+
};
|
|
1232
|
+
if (hasOptProp(msg, 'bizJid')) {
|
|
1233
|
+
interactiveMessage.collectionMessage = {
|
|
1234
|
+
bizJid: msg.bizJid,
|
|
1235
|
+
id: msg.id,
|
|
1236
|
+
messageVersion: 1
|
|
1237
|
+
};
|
|
1238
|
+
}
|
|
1239
|
+
else if (hasOptProp(msg, 'shopSurface')) {
|
|
1240
|
+
interactiveMessage.shopStorefrontMessage = {
|
|
1241
|
+
surface: msg.shopSurface,
|
|
1242
|
+
id: msg.id,
|
|
1243
|
+
messageVersion: 1
|
|
1244
|
+
};
|
|
1245
|
+
}
|
|
1246
|
+
if (hasOptProp(msg, 'text')) {
|
|
1247
|
+
interactiveMessage.body = { text: msg.text };
|
|
1248
|
+
}
|
|
1249
|
+
else {
|
|
1250
|
+
if (hasOptProp(msg, 'caption')) {
|
|
1251
|
+
const isValidHeader = hasValidInteractiveHeader(m);
|
|
1252
|
+
if (!isValidHeader) {
|
|
1253
|
+
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
1254
|
+
}
|
|
1255
|
+
interactiveMessage.header = {
|
|
1256
|
+
title: msg.title || '',
|
|
1257
|
+
subtitle: msg.subtitle || '',
|
|
1258
|
+
hasMediaAttachment: isValidHeader
|
|
1259
|
+
};
|
|
1260
|
+
interactiveMessage.body = { text: msg.caption };
|
|
1261
|
+
}
|
|
1262
|
+
if (hasOptProp(msg, 'thumbnail') && !!msg.thumbnail) {
|
|
1263
|
+
interactiveMessage.jpegThumbnail = msg.thumbnail;
|
|
1264
|
+
}
|
|
1265
|
+
Object.assign(interactiveMessage.header, m);
|
|
1266
|
+
}
|
|
1267
|
+
if (hasOptProp(msg, 'audioFooter')) {
|
|
1268
|
+
const { audioMessage } = await prepareWAMessageMedia({ audio: msg.audioFooter }, options);
|
|
1269
|
+
interactiveMessage.footer = {
|
|
1270
|
+
audioMessage,
|
|
1271
|
+
hasMediaAttachment: true
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
else if (hasOptProp(msg, 'footer')) {
|
|
1275
|
+
interactiveMessage.footer = { text: msg.footer };
|
|
1276
|
+
}
|
|
1277
|
+
m = { interactiveMessage };
|
|
1278
|
+
}
|
|
1279
|
+
else if (hasProp(msg, 'cards')) {
|
|
1280
|
+
const interactiveMessage = {
|
|
1281
|
+
carouselMessage: {
|
|
1282
|
+
cards: await Promise.all(msg.cards.map(async (card) => {
|
|
1283
|
+
let carouselHeader = {};
|
|
1284
|
+
if (hasProp(card, 'product')) {
|
|
1285
|
+
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1286
|
+
}
|
|
1287
|
+
else {
|
|
1288
|
+
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
1289
|
+
}
|
|
1290
|
+
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
1291
|
+
if (!isValidHeader) {
|
|
1292
|
+
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1293
|
+
}
|
|
1294
|
+
const carouselCard = {
|
|
1295
|
+
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
1296
|
+
};
|
|
1297
|
+
if (hasOptProp(card, 'text')) {
|
|
1298
|
+
carouselCard.body = { text: card.text };
|
|
1299
|
+
}
|
|
1300
|
+
else {
|
|
1301
|
+
if (hasOptProp(card, 'caption')) {
|
|
1302
|
+
carouselCard.header = {
|
|
1303
|
+
title: card.title || '',
|
|
1304
|
+
subtitle: card.subtitle || '',
|
|
1305
|
+
hasMediaAttachment: isValidHeader
|
|
1306
|
+
};
|
|
1307
|
+
carouselCard.body = { text: card.caption };
|
|
1308
|
+
}
|
|
1309
|
+
if (hasOptProp(card, 'thumbnail') && !!card.thumbnail) {
|
|
1310
|
+
carouselCard.jpegThumbnail = card.thumbnail;
|
|
1311
|
+
}
|
|
1312
|
+
Object.assign(carouselCard.header, carouselHeader);
|
|
1313
|
+
}
|
|
1314
|
+
if (hasOptProp(card, 'audioFooter')) {
|
|
1315
|
+
const { audioMessage } = await prepareWAMessageMedia({ audio: card.audioFooter }, options);
|
|
1316
|
+
carouselCard.footer = {
|
|
1317
|
+
audioMessage,
|
|
1318
|
+
hasMediaAttachment: true
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
else if (hasOptProp(card, 'footer')) {
|
|
1322
|
+
carouselCard.footer = { text: card.footer };
|
|
1323
|
+
}
|
|
1324
|
+
return carouselCard;
|
|
1325
|
+
})),
|
|
1326
|
+
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1327
|
+
messageVersion: 1
|
|
1328
|
+
}
|
|
1329
|
+
};
|
|
1330
|
+
if (hasOptProp(msg, 'text')) {
|
|
1331
|
+
interactiveMessage.body = { text: msg.text };
|
|
1332
|
+
}
|
|
1333
|
+
if (hasOptProp(msg, 'footer')) {
|
|
1334
|
+
interactiveMessage.footer = { text: msg.footer };
|
|
1335
|
+
}
|
|
1336
|
+
m = { interactiveMessage };
|
|
1337
|
+
}
|
|
1338
|
+
else if (hasProp(msg, 'requestPaymentFrom')) {
|
|
1339
|
+
const requestPaymentMessage = {
|
|
1340
|
+
amount: {
|
|
1341
|
+
currencyCode: 'IDR',
|
|
1342
|
+
offset: 1000,
|
|
1343
|
+
value: 1000
|
|
1344
|
+
},
|
|
1345
|
+
amount1000: 1000,
|
|
1346
|
+
currencyCodeIso4217: 'IDR',
|
|
1347
|
+
expiryTimestamp: Date.now(),
|
|
1348
|
+
noteMessage: m,
|
|
1349
|
+
requestFrom: msg.requestPaymentFrom,
|
|
1350
|
+
...msg
|
|
1351
|
+
};
|
|
1352
|
+
delete requestPaymentMessage.requestPaymentFrom;
|
|
1353
|
+
if (hasProp(m, 'extendedTextMessage') || hasProp(m, 'stickerMessage')) {
|
|
1354
|
+
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
1355
|
+
}
|
|
1356
|
+
else {
|
|
1357
|
+
throw new Boom('Invalid message type for request payment note message', { statusCode: 400 });
|
|
1358
|
+
}
|
|
1359
|
+
m = { requestPaymentMessage };
|
|
1360
|
+
}
|
|
1361
|
+
else if (hasProp(msg, 'invoiceNote')) {
|
|
1362
|
+
const attachment = m.imageMessage || m.documentMessage;
|
|
1363
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1364
|
+
const invoiceMessage = {
|
|
1365
|
+
attachmentType: proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
1366
|
+
note: msg.invoiceNote
|
|
1367
|
+
};
|
|
1368
|
+
if (attachment) {
|
|
1369
|
+
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
1370
|
+
Object.assign(invoiceMessage, {
|
|
1371
|
+
attachmentDirectPath: directPath,
|
|
1372
|
+
attachmentFileEncSha256: fileEncSha256,
|
|
1373
|
+
attachmentFileSha256: fileSha256,
|
|
1374
|
+
attachmentJpegThumbnail: jpegThumbnail,
|
|
1375
|
+
attachmentMediaKey: mediaKey,
|
|
1376
|
+
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
1377
|
+
attachmentMimetype: mimetype,
|
|
1378
|
+
token: generateMessageIDV2()
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
else {
|
|
1382
|
+
throw new Boom('Invalid media type for invoice message', { statusCode: 400 });
|
|
1383
|
+
}
|
|
1384
|
+
m = { invoiceMessage };
|
|
1385
|
+
}
|
|
1386
|
+
if (hasOptProp(msg, 'externalAdReply') && !!msg.externalAdReply) {
|
|
1387
|
+
const messageType = Object.keys(m)[0];
|
|
1388
|
+
const key = m[messageType];
|
|
1389
|
+
const content = msg.externalAdReply;
|
|
1390
|
+
if ('thumbnail' in content && !Buffer.isBuffer(content.thumbnail)) {
|
|
1391
|
+
throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
|
|
1392
|
+
}
|
|
1393
|
+
if (!content.url || typeof content.url !== 'string') {
|
|
1394
|
+
content.url = DONATE_URL;
|
|
1395
|
+
}
|
|
1396
|
+
const externalAdReply = {
|
|
1397
|
+
...content,
|
|
1398
|
+
body: content.body,
|
|
1399
|
+
mediaType: content.mediaType || 1,
|
|
1400
|
+
mediaUrl: content.url,
|
|
1401
|
+
renderLargerThumbnail: content.largeThumbnail,
|
|
1402
|
+
sourceUrl: content.url,
|
|
1403
|
+
thumbnail: content.thumbnail,
|
|
1404
|
+
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1405
|
+
title: content.title || LIBRARY_NAME
|
|
1406
|
+
};
|
|
1407
|
+
delete externalAdReply.subTitle;
|
|
1408
|
+
delete externalAdReply.largeThumbnail;
|
|
1409
|
+
delete externalAdReply.url;
|
|
1410
|
+
if (key && 'contextInfo' in key && !!key.contextInfo) {
|
|
1411
|
+
key.contextInfo.externalAdReply = { ...key.contextInfo.externalAdReply, ...externalAdReply };
|
|
1412
|
+
}
|
|
1413
|
+
else if (key) {
|
|
1414
|
+
key.contextInfo = { externalAdReply };
|
|
1415
|
+
}
|
|
522
1416
|
}
|
|
523
|
-
if ((
|
|
524
|
-
(
|
|
1417
|
+
if ((hasOptProp(msg, 'mentions') && msg.mentions?.length) ||
|
|
1418
|
+
(hasOptProp(msg, 'mentionAll') && msg.mentionAll)) {
|
|
525
1419
|
const messageType = Object.keys(m)[0];
|
|
526
1420
|
const key = m[messageType];
|
|
527
1421
|
if (key && 'contextInfo' in key) {
|
|
528
1422
|
key.contextInfo = key.contextInfo || {};
|
|
529
|
-
if (
|
|
530
|
-
key.contextInfo.mentionedJid =
|
|
1423
|
+
if (msg.mentions?.length) {
|
|
1424
|
+
key.contextInfo.mentionedJid = msg.mentions;
|
|
531
1425
|
}
|
|
532
|
-
if (
|
|
1426
|
+
if (msg.mentionAll) {
|
|
533
1427
|
key.contextInfo.nonJidMentions = 1;
|
|
534
1428
|
}
|
|
535
1429
|
}
|
|
536
1430
|
else if (key) {
|
|
537
1431
|
key.contextInfo = {
|
|
538
|
-
mentionedJid:
|
|
539
|
-
nonJidMentions:
|
|
1432
|
+
mentionedJid: msg.mentions,
|
|
1433
|
+
nonJidMentions: msg.mentionAll ? 1 : 0
|
|
540
1434
|
};
|
|
541
1435
|
}
|
|
542
1436
|
}
|
|
543
|
-
if (
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
1437
|
+
if (hasOptProp(msg, 'contextInfo') && !!msg.contextInfo) {
|
|
1438
|
+
const messageType = Object.keys(m)[0];
|
|
1439
|
+
const key = m[messageType];
|
|
1440
|
+
if (key && 'contextInfo' in key && !!key.contextInfo) {
|
|
1441
|
+
key.contextInfo = { ...key.contextInfo, ...msg.contextInfo };
|
|
1442
|
+
}
|
|
1443
|
+
else if (key) {
|
|
1444
|
+
key.contextInfo = msg.contextInfo;
|
|
1445
|
+
}
|
|
552
1446
|
}
|
|
553
|
-
if (
|
|
1447
|
+
if (hasOptProp(msg, 'groupStatus') && !!msg.groupStatus) {
|
|
554
1448
|
const messageType = Object.keys(m)[0];
|
|
555
1449
|
const key = m[messageType];
|
|
556
|
-
if ('contextInfo' in key && !!key.contextInfo) {
|
|
557
|
-
key.contextInfo =
|
|
1450
|
+
if (key && 'contextInfo' in key && !!key.contextInfo) {
|
|
1451
|
+
key.contextInfo.isGroupStatus = msg.groupStatus;
|
|
558
1452
|
}
|
|
559
1453
|
else if (key) {
|
|
560
|
-
key.contextInfo =
|
|
1454
|
+
key.contextInfo = {
|
|
1455
|
+
isGroupStatus: msg.groupStatus
|
|
1456
|
+
};
|
|
561
1457
|
}
|
|
1458
|
+
m = { groupStatusMessageV2: { message: m } };
|
|
1459
|
+
delete msg.groupStatus;
|
|
562
1460
|
}
|
|
563
|
-
if (
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
1461
|
+
if (hasOptProp(msg, 'spoiler') && !!msg.spoiler) {
|
|
1462
|
+
const messageType = Object.keys(m)[0];
|
|
1463
|
+
const key = m[messageType];
|
|
1464
|
+
if (key && 'contextInfo' in key && !!key.contextInfo) {
|
|
1465
|
+
key.contextInfo.isSpoiler = msg.spoiler;
|
|
1466
|
+
}
|
|
1467
|
+
else if (key) {
|
|
1468
|
+
key.contextInfo = {
|
|
1469
|
+
isSpoiler: msg.spoiler
|
|
1470
|
+
};
|
|
1471
|
+
}
|
|
1472
|
+
m = { spoilerMessage: { message: m } };
|
|
1473
|
+
delete msg.spoiler;
|
|
1474
|
+
}
|
|
1475
|
+
else if (hasOptProp(msg, 'interactiveAsTemplate') && !!msg.interactiveAsTemplate) {
|
|
1476
|
+
if (!m.interactiveMessage) {
|
|
1477
|
+
throw new Boom('Invalid message type for template', { statusCode: 400 });
|
|
1478
|
+
}
|
|
1479
|
+
m = {
|
|
1480
|
+
templateMessage: {
|
|
1481
|
+
interactiveMessageTemplate: m.interactiveMessage,
|
|
1482
|
+
templateId: msg.id || 'template-' + Date.now()
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
delete msg.interactiveAsTemplate;
|
|
1486
|
+
}
|
|
1487
|
+
if (hasOptProp(msg, 'ephemeral') && !!msg.ephemeral) {
|
|
1488
|
+
m = { ephemeralMessage: { message: m } };
|
|
1489
|
+
delete msg.ephemeral;
|
|
1490
|
+
}
|
|
1491
|
+
if (hasOptProp(msg, 'isLottie') && !!msg.isLottie) {
|
|
1492
|
+
m = { lottieStickerMessage: { message: m } };
|
|
1493
|
+
}
|
|
1494
|
+
else if (hasOptProp(msg, 'viewOnce') && !!msg.viewOnce) {
|
|
1495
|
+
m = { viewOnceMessage: { message: m } };
|
|
1496
|
+
}
|
|
1497
|
+
else if (hasOptProp(msg, 'viewOnceV2') && !!msg.viewOnceV2) {
|
|
1498
|
+
m = { viewOnceMessageV2: { message: m } };
|
|
1499
|
+
delete msg.viewOnceV2;
|
|
1500
|
+
}
|
|
1501
|
+
else if (hasOptProp(msg, 'viewOnceV2Extension') && !!msg.viewOnceV2Extension) {
|
|
1502
|
+
m = { viewOnceMessageV2Extension: { message: m } };
|
|
1503
|
+
delete msg.viewOnceV2Extension;
|
|
1504
|
+
}
|
|
1505
|
+
if (hasOptProp(msg, 'edit')) {
|
|
1506
|
+
m = {
|
|
1507
|
+
protocolMessage: {
|
|
1508
|
+
key: msg.edit,
|
|
1509
|
+
editedMessage: m,
|
|
1510
|
+
timestampMs: Date.now(),
|
|
1511
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
569
1512
|
}
|
|
570
1513
|
};
|
|
571
1514
|
}
|
|
@@ -578,79 +1521,82 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
578
1521
|
return WAProto.Message.create(m);
|
|
579
1522
|
};
|
|
580
1523
|
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
581
|
-
// set timestamp to now
|
|
582
|
-
// if not specified
|
|
583
1524
|
if (!options.timestamp) {
|
|
584
1525
|
options.timestamp = new Date();
|
|
585
1526
|
}
|
|
586
1527
|
const innerMessage = normalizeMessageContent(message);
|
|
1528
|
+
const messageContextInfo = message.messageContextInfo;
|
|
587
1529
|
const key = getContentType(innerMessage);
|
|
588
1530
|
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
1531
|
+
const isNewsletter = isJidNewsletter(jid);
|
|
589
1532
|
const { quoted, userJid } = options;
|
|
590
|
-
if (quoted &&
|
|
1533
|
+
if (quoted && innerMessage) {
|
|
591
1534
|
const participant = quoted.key.fromMe
|
|
592
|
-
? userJid
|
|
1535
|
+
? userJid
|
|
593
1536
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
594
1537
|
let quotedMsg = normalizeMessageContent(quoted.message);
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
// if we want to send a disappearing message
|
|
618
|
-
!!options?.ephemeralExpiration &&
|
|
619
|
-
// and it's not a protocol message -- delete, toggle disappear message
|
|
1538
|
+
if (quotedMsg) {
|
|
1539
|
+
const msgType = getContentType(quotedMsg);
|
|
1540
|
+
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
1541
|
+
const quotedContent = quotedMsg[msgType];
|
|
1542
|
+
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
1543
|
+
delete quotedContent.contextInfo;
|
|
1544
|
+
}
|
|
1545
|
+
const innerMsgKey = innerMessage[key];
|
|
1546
|
+
const contextInfo = (innerMsgKey && typeof innerMsgKey === 'object' && 'contextInfo' in innerMsgKey && innerMsgKey.contextInfo) || {};
|
|
1547
|
+
contextInfo.participant = jidNormalizedUser(participant || '');
|
|
1548
|
+
contextInfo.stanzaId = quoted.key.id;
|
|
1549
|
+
contextInfo.quotedMessage = quotedMsg;
|
|
1550
|
+
if (!isNewsletter && jid !== quoted.key.remoteJid) {
|
|
1551
|
+
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
1552
|
+
}
|
|
1553
|
+
if (contextInfo && innerMsgKey && typeof innerMsgKey === 'object') {
|
|
1554
|
+
innerMsgKey.contextInfo = contextInfo;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
if (innerMessage &&
|
|
1559
|
+
!!options?.ephemeralExpiration &&
|
|
620
1560
|
key !== 'protocolMessage' &&
|
|
621
|
-
// already not converted to disappearing message
|
|
622
1561
|
key !== 'ephemeralMessage' &&
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
1562
|
+
!isNewsletter) {
|
|
1563
|
+
const innerMsgKey = innerMessage[key];
|
|
1564
|
+
if (innerMsgKey && typeof innerMsgKey === 'object') {
|
|
1565
|
+
innerMsgKey.contextInfo = {
|
|
1566
|
+
...(innerMsgKey.contextInfo || {}),
|
|
1567
|
+
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
1568
|
+
};
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
|
|
1572
|
+
messageContextInfo.deviceListMetadata = {
|
|
1573
|
+
recipientKeyHash: randomBytes(10),
|
|
1574
|
+
recipientTimestamp: unixTimestampSeconds()
|
|
630
1575
|
};
|
|
1576
|
+
messageContextInfo.deviceListMetadataVersion = 2;
|
|
631
1577
|
}
|
|
632
|
-
|
|
1578
|
+
const finalMessage = WAProto.Message.create(message);
|
|
633
1579
|
const messageJSON = {
|
|
634
1580
|
key: {
|
|
635
1581
|
remoteJid: jid,
|
|
636
1582
|
fromMe: true,
|
|
637
1583
|
id: options?.messageId || generateMessageIDV2()
|
|
638
1584
|
},
|
|
639
|
-
message:
|
|
1585
|
+
message: finalMessage,
|
|
640
1586
|
messageTimestamp: timestamp,
|
|
641
1587
|
messageStubParameters: [],
|
|
642
|
-
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
1588
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
643
1589
|
status: WAMessageStatus.PENDING
|
|
644
1590
|
};
|
|
645
1591
|
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
646
1592
|
};
|
|
647
1593
|
export const generateWAMessage = async (jid, content, options) => {
|
|
648
|
-
// ensure msg ID is with every log
|
|
649
1594
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
650
|
-
|
|
651
|
-
|
|
1595
|
+
if (jid) {
|
|
1596
|
+
options.jid = jid;
|
|
1597
|
+
}
|
|
1598
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
|
|
652
1599
|
};
|
|
653
|
-
/** Get the key to access the true type of content */
|
|
654
1600
|
export const getContentType = (content) => {
|
|
655
1601
|
if (content) {
|
|
656
1602
|
const keys = Object.keys(content);
|
|
@@ -658,17 +1604,10 @@ export const getContentType = (content) => {
|
|
|
658
1604
|
return key;
|
|
659
1605
|
}
|
|
660
1606
|
};
|
|
661
|
-
/**
|
|
662
|
-
* Normalizes ephemeral, view once messages to regular message content
|
|
663
|
-
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
664
|
-
* @param content
|
|
665
|
-
* @returns
|
|
666
|
-
*/
|
|
667
1607
|
export const normalizeMessageContent = (content) => {
|
|
668
1608
|
if (!content) {
|
|
669
1609
|
return undefined;
|
|
670
1610
|
}
|
|
671
|
-
// set max iterations to prevent an infinite loop
|
|
672
1611
|
for (let i = 0; i < 5; i++) {
|
|
673
1612
|
const inner = getFutureProofMessage(content);
|
|
674
1613
|
if (!inner) {
|
|
@@ -676,23 +1615,37 @@ export const normalizeMessageContent = (content) => {
|
|
|
676
1615
|
}
|
|
677
1616
|
content = inner.message;
|
|
678
1617
|
}
|
|
679
|
-
return content;
|
|
1618
|
+
return content || undefined;
|
|
680
1619
|
function getFutureProofMessage(message) {
|
|
681
|
-
return (message?.
|
|
682
|
-
message?.
|
|
1620
|
+
return (message?.associatedChildMessage ||
|
|
1621
|
+
message?.botForwardedMessage ||
|
|
1622
|
+
message?.botInvokeMessage ||
|
|
1623
|
+
message?.botTaskMessage ||
|
|
683
1624
|
message?.documentWithCaptionMessage ||
|
|
684
|
-
message?.viewOnceMessageV2 ||
|
|
685
|
-
message?.viewOnceMessageV2Extension ||
|
|
686
1625
|
message?.editedMessage ||
|
|
687
|
-
message?.
|
|
1626
|
+
message?.ephemeralMessage ||
|
|
1627
|
+
message?.eventCoverImage ||
|
|
1628
|
+
message?.groupMentionedMessage ||
|
|
1629
|
+
message?.groupStatusMentionMessage ||
|
|
688
1630
|
message?.groupStatusMessage ||
|
|
689
|
-
message?.groupStatusMessageV2
|
|
1631
|
+
message?.groupStatusMessageV2 ||
|
|
1632
|
+
message?.limitSharingMessage ||
|
|
1633
|
+
message?.lottieStickerMessage ||
|
|
1634
|
+
message?.newsletterAdminProfileMessage ||
|
|
1635
|
+
message?.newsletterAdminProfileMessageV2 ||
|
|
1636
|
+
message?.newsletterAdminProfileStatusMessage ||
|
|
1637
|
+
message?.pollCreationMessageV4 ||
|
|
1638
|
+
message?.pollCreationOptionImageMessage ||
|
|
1639
|
+
message?.questionMessage ||
|
|
1640
|
+
message?.questionReplyMessage ||
|
|
1641
|
+
message?.spoilerMessage ||
|
|
1642
|
+
message?.statusAddYours ||
|
|
1643
|
+
message?.statusMentionMessage ||
|
|
1644
|
+
message?.viewOnceMessage ||
|
|
1645
|
+
message?.viewOnceMessageV2 ||
|
|
1646
|
+
message?.viewOnceMessageV2Extension);
|
|
690
1647
|
}
|
|
691
1648
|
};
|
|
692
|
-
/**
|
|
693
|
-
* Extract the true message content from a message
|
|
694
|
-
* Eg. extracts the inner message from a disappearing message/view once message
|
|
695
|
-
*/
|
|
696
1649
|
export const extractMessageContent = (content) => {
|
|
697
1650
|
const extractFromTemplateMessage = (msg) => {
|
|
698
1651
|
if (msg.imageMessage) {
|
|
@@ -728,9 +1681,6 @@ export const extractMessageContent = (content) => {
|
|
|
728
1681
|
}
|
|
729
1682
|
return content;
|
|
730
1683
|
};
|
|
731
|
-
/**
|
|
732
|
-
* Returns the device predicted by message ID
|
|
733
|
-
*/
|
|
734
1684
|
export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
735
1685
|
? 'ios'
|
|
736
1686
|
: /^3E.{20}$/.test(id)
|
|
@@ -740,7 +1690,6 @@ export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
|
740
1690
|
: /^(3F|.{18}$)/.test(id)
|
|
741
1691
|
? 'desktop'
|
|
742
1692
|
: 'unknown';
|
|
743
|
-
/** Upserts a receipt in the message */
|
|
744
1693
|
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
745
1694
|
msg.userReceipt = msg.userReceipt || [];
|
|
746
1695
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
@@ -751,7 +1700,6 @@ export const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
751
1700
|
msg.userReceipt.push(receipt);
|
|
752
1701
|
}
|
|
753
1702
|
};
|
|
754
|
-
/** Update the message with a new reaction */
|
|
755
1703
|
export const updateMessageWithReaction = (msg, reaction) => {
|
|
756
1704
|
const authorID = getKeyAuthor(reaction.key);
|
|
757
1705
|
const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
|
|
@@ -759,28 +1707,20 @@ export const updateMessageWithReaction = (msg, reaction) => {
|
|
|
759
1707
|
reactions.push(reaction);
|
|
760
1708
|
msg.reactions = reactions;
|
|
761
1709
|
};
|
|
762
|
-
/** Update the message with a new poll update */
|
|
763
1710
|
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
764
|
-
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
765
|
-
const
|
|
1711
|
+
const authorID = getKeyAuthor(update.pollUpdateMessageKey || update.pollCreationMessageKey);
|
|
1712
|
+
const pollUpdates = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey || r.pollCreationMessageKey) !== authorID);
|
|
766
1713
|
if (update.vote?.selectedOptions?.length) {
|
|
767
|
-
|
|
1714
|
+
pollUpdates.push(update);
|
|
768
1715
|
}
|
|
769
|
-
msg.pollUpdates =
|
|
1716
|
+
msg.pollUpdates = pollUpdates;
|
|
770
1717
|
};
|
|
771
|
-
/** Update the message with a new event response */
|
|
772
1718
|
export const updateMessageWithEventResponse = (msg, update) => {
|
|
773
|
-
const authorID = getKeyAuthor(update.eventResponseMessageKey);
|
|
774
|
-
const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
|
|
1719
|
+
const authorID = getKeyAuthor(update.eventResponseMessageKey || update.eventCreationMessageKey);
|
|
1720
|
+
const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey || r.eventCreationMessageKey) !== authorID);
|
|
775
1721
|
responses.push(update);
|
|
776
1722
|
msg.eventResponses = responses;
|
|
777
1723
|
};
|
|
778
|
-
/**
|
|
779
|
-
* Aggregates all poll updates in a poll.
|
|
780
|
-
* @param msg the poll creation message
|
|
781
|
-
* @param meId your jid
|
|
782
|
-
* @returns A list of options & their voters
|
|
783
|
-
*/
|
|
784
1724
|
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
785
1725
|
const opts = message?.pollCreationMessage?.options ||
|
|
786
1726
|
message?.pollCreationMessageV2?.options ||
|
|
@@ -809,17 +1749,11 @@ export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
809
1749
|
};
|
|
810
1750
|
data = voteHashMap[hash];
|
|
811
1751
|
}
|
|
812
|
-
|
|
1752
|
+
data.voters.push(getKeyAuthor((update.pollUpdateMessageKey || update.pollCreationMessageKey), meId));
|
|
813
1753
|
}
|
|
814
1754
|
}
|
|
815
1755
|
return Object.values(voteHashMap);
|
|
816
1756
|
}
|
|
817
|
-
/**
|
|
818
|
-
* Aggregates all event responses in an event message.
|
|
819
|
-
* @param msg the event creation message
|
|
820
|
-
* @param meId your jid
|
|
821
|
-
* @returns A list of response types & their responders
|
|
822
|
-
*/
|
|
823
1757
|
export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
824
1758
|
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
825
1759
|
const responseMap = {};
|
|
@@ -830,14 +1764,13 @@ export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
|
830
1764
|
};
|
|
831
1765
|
}
|
|
832
1766
|
for (const update of eventResponses || []) {
|
|
833
|
-
const responseType = update.eventResponse || 'UNKNOWN';
|
|
1767
|
+
const responseType = update.response || update.eventResponse || update.eventResponseMessage?.response || 'UNKNOWN';
|
|
834
1768
|
if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
|
|
835
|
-
responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
|
|
1769
|
+
responseMap[responseType].responders.push(getKeyAuthor((update.eventResponseMessageKey || update.eventCreationMessageKey), meId));
|
|
836
1770
|
}
|
|
837
1771
|
}
|
|
838
1772
|
return Object.values(responseMap);
|
|
839
1773
|
}
|
|
840
|
-
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
841
1774
|
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
842
1775
|
const keyMap = {};
|
|
843
1776
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
@@ -846,7 +1779,7 @@ export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
846
1779
|
if (!keyMap[uqKey]) {
|
|
847
1780
|
keyMap[uqKey] = {
|
|
848
1781
|
jid: remoteJid,
|
|
849
|
-
participant: participant,
|
|
1782
|
+
participant: participant || undefined,
|
|
850
1783
|
messageIds: []
|
|
851
1784
|
};
|
|
852
1785
|
}
|
|
@@ -856,16 +1789,12 @@ export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
856
1789
|
return Object.values(keyMap);
|
|
857
1790
|
};
|
|
858
1791
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
859
|
-
/**
|
|
860
|
-
* Downloads the given message. Throws an error if it's not a media message
|
|
861
|
-
*/
|
|
862
1792
|
export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
863
1793
|
const result = await downloadMsg().catch(async (error) => {
|
|
864
1794
|
if (ctx &&
|
|
865
|
-
typeof error?.status === 'number' &&
|
|
1795
|
+
typeof error?.status === 'number' &&
|
|
866
1796
|
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
867
1797
|
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
868
|
-
// request reupload
|
|
869
1798
|
message = await ctx.reuploadRequest(message);
|
|
870
1799
|
const result = await downloadMsg();
|
|
871
1800
|
return result;
|
|
@@ -906,87 +1835,28 @@ export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
906
1835
|
return stream;
|
|
907
1836
|
}
|
|
908
1837
|
};
|
|
909
|
-
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
910
1838
|
export const assertMediaContent = (content) => {
|
|
911
1839
|
content = extractMessageContent(content);
|
|
912
1840
|
const mediaContent = content?.documentMessage ||
|
|
913
1841
|
content?.imageMessage ||
|
|
914
1842
|
content?.videoMessage ||
|
|
915
1843
|
content?.audioMessage ||
|
|
916
|
-
content?.stickerMessage
|
|
1844
|
+
content?.stickerMessage ||
|
|
1845
|
+
content?.ptvMessage;
|
|
917
1846
|
if (!mediaContent) {
|
|
918
1847
|
throw new Boom('given message is not a media message', { statusCode: 400, data: content });
|
|
919
1848
|
}
|
|
920
1849
|
return mediaContent;
|
|
921
1850
|
};
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
waMsgContent = content;
|
|
934
|
-
}
|
|
935
|
-
else {
|
|
936
|
-
waMsgContent = await generateWAMessageContent(content, {
|
|
937
|
-
upload: socket.waUploadToServer
|
|
938
|
-
});
|
|
939
|
-
}
|
|
940
|
-
const msg = {
|
|
941
|
-
message: {
|
|
942
|
-
groupStatusMessageV2: {
|
|
943
|
-
message: waMsgContent.message || waMsgContent
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
const messageId = options.messageId || generateMessageID();
|
|
948
|
-
return await socket.relayMessage(groupJid, msg.message, {
|
|
949
|
-
messageId,
|
|
950
|
-
...options
|
|
951
|
-
});
|
|
952
|
-
}
|
|
953
|
-
catch (err) {
|
|
954
|
-
console.error("Group Status Send Error:", err);
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Send a channel status message
|
|
959
|
-
* @param socket The socket connection
|
|
960
|
-
* @param channelJid The JID of the channel to send the status message to
|
|
961
|
-
* @param content The message content
|
|
962
|
-
* @param options Additional message relay options
|
|
963
|
-
*/
|
|
964
|
-
export async function sendChannelStatus(socket, channelJid, content, options = {}) {
|
|
965
|
-
try {
|
|
966
|
-
let waMsgContent;
|
|
967
|
-
if ('message' in content && content.message) {
|
|
968
|
-
waMsgContent = content;
|
|
969
|
-
}
|
|
970
|
-
else {
|
|
971
|
-
waMsgContent = await generateWAMessageContent(content, {
|
|
972
|
-
upload: socket.waUploadToServer
|
|
973
|
-
});
|
|
974
|
-
}
|
|
975
|
-
const msg = {
|
|
976
|
-
message: {
|
|
977
|
-
groupStatusMessageV2: {
|
|
978
|
-
message: waMsgContent.message || waMsgContent
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
};
|
|
982
|
-
const messageId = options.messageId || generateMessageID();
|
|
983
|
-
return await socket.relayMessage(channelJid, msg.message, {
|
|
984
|
-
messageId,
|
|
985
|
-
...options
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
catch (err) {
|
|
989
|
-
console.error("Channel Status Send Error:", err);
|
|
990
|
-
}
|
|
991
|
-
}
|
|
1851
|
+
export const shouldIncludeBizBinaryNode = (message) => {
|
|
1852
|
+
const normalized = normalizeMessageContent(message);
|
|
1853
|
+
const hasValidInteractive = normalized?.interactiveMessage &&
|
|
1854
|
+
!normalized.interactiveMessage.carouselMessage &&
|
|
1855
|
+
!normalized.interactiveMessage.collectionMessage &&
|
|
1856
|
+
!normalized.interactiveMessage.shopStorefrontMessage;
|
|
1857
|
+
return !!(normalized?.buttonsMessage ||
|
|
1858
|
+
normalized?.interactiveMessage ||
|
|
1859
|
+
normalized?.listMessage ||
|
|
1860
|
+
hasValidInteractive);
|
|
1861
|
+
};
|
|
992
1862
|
//# sourceMappingURL=messages.js.map
|