@nuiisweety/baileys 0.1.16 → 0.1.17
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 +390 -833
- package/README.md.bak +1 -1
- package/lib/Socket/index.js +14 -22
- package/lib/Socket/index.js.bak +4 -4
- package/lib/Socket/messages-send.js +1 -0
- package/lib/Socket/messages-send.js.bak +5 -2
- package/lib/Utils/messages.js +13 -0
- package/lib/Utils/messages.js.bak +921 -309
- package/package.json +1 -1
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { Boom } from '@hapi/boom';
|
|
2
2
|
import { randomBytes } from 'crypto';
|
|
3
|
+
import { zip } from 'fflate';
|
|
3
4
|
import { promises as fs } from 'fs';
|
|
4
5
|
import {} from 'stream';
|
|
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, DONATE_URL, LIBRARY_NAME, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
8
|
+
import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListType, ProtocolType, WAMessageStatus, WAProto } from '../Types/index.js';
|
|
9
|
+
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, isLidUser, isPnUser, jidNormalizedUser } from '../WABinary/index.js';
|
|
9
10
|
import { sha256 } from './crypto.js';
|
|
10
11
|
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
11
|
-
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData } from './messages-media.js';
|
|
12
|
+
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
13
|
+
import { prepareRichResponseMessage } from './rich-message-utils.js';
|
|
12
14
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
15
|
+
const CONCURRENCY_LIMIT = 10;
|
|
13
16
|
const MIMETYPE_MAP = {
|
|
14
17
|
image: 'image/jpeg',
|
|
15
18
|
video: 'video/mp4',
|
|
@@ -261,19 +264,322 @@ export const generateForwardMessageContent = (message, forceForward) => {
|
|
|
261
264
|
return content;
|
|
262
265
|
};
|
|
263
266
|
export const hasNonNullishProperty = (message, key) => {
|
|
264
|
-
return
|
|
265
|
-
message
|
|
267
|
+
return message != null &&
|
|
268
|
+
typeof message === 'object' &&
|
|
266
269
|
key in message &&
|
|
267
|
-
message[key]
|
|
268
|
-
|
|
270
|
+
message[key] != null;
|
|
271
|
+
};
|
|
272
|
+
export const hasOptionalProperty = (obj, key) => {
|
|
273
|
+
return obj != null &&
|
|
274
|
+
typeof obj === 'object' &&
|
|
275
|
+
key in obj &&
|
|
276
|
+
obj[key] != null;
|
|
277
|
+
};
|
|
278
|
+
// Validate album message media to avoid bug
|
|
279
|
+
export const hasValidAlbumMedia = (message) => {
|
|
280
|
+
return message.imageMessage ||
|
|
281
|
+
message.videoMessage;
|
|
282
|
+
};
|
|
283
|
+
export const hasValidInteractiveHeader = (message) => {
|
|
284
|
+
return message.imageMessage ||
|
|
285
|
+
message.videoMessage ||
|
|
286
|
+
message.documentMessage ||
|
|
287
|
+
message.productMessage ||
|
|
288
|
+
message.locationMessage;
|
|
289
|
+
};
|
|
290
|
+
// Validate carousel cards header to avoid bug
|
|
291
|
+
export const hasValidCarouselHeader = (message) => {
|
|
292
|
+
return message.imageMessage ||
|
|
293
|
+
message.videoMessage ||
|
|
294
|
+
message.productMessage;
|
|
295
|
+
};
|
|
296
|
+
// Extract product message into a standalone function so it can also be reused as the header for interactive messages
|
|
297
|
+
const prepareProductMessage = async (message, options) => {
|
|
298
|
+
if (!message.businessOwnerJid) {
|
|
299
|
+
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
300
|
+
}
|
|
301
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
302
|
+
const content = {
|
|
303
|
+
...message,
|
|
304
|
+
product: {
|
|
305
|
+
currencyCode: 'IDR',
|
|
306
|
+
priceAmount1000: 1000,
|
|
307
|
+
title: LIBRARY_NAME,
|
|
308
|
+
...message.product,
|
|
309
|
+
productImage: imageMessage
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
delete content.image;
|
|
313
|
+
return content;
|
|
314
|
+
};
|
|
315
|
+
const prepareStickerPackMessage = async (message, options) => {
|
|
316
|
+
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: nuiisweety', description = '🏷️ nuiisweety/baileys' } = message;
|
|
317
|
+
if (stickers.length > 60) {
|
|
318
|
+
throw new Boom('Sticker pack exceeds the maximum limit of 60 stickers', { statusCode: 400 });
|
|
319
|
+
}
|
|
320
|
+
if (stickers.length === 0) {
|
|
321
|
+
throw new Boom('Sticker pack must contain at least one sticker', { statusCode: 400 });
|
|
322
|
+
}
|
|
323
|
+
if (!cover) {
|
|
324
|
+
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
325
|
+
}
|
|
326
|
+
const logger = options.logger;
|
|
327
|
+
let cacheableKey = false;
|
|
328
|
+
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
329
|
+
const urls = [];
|
|
330
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
331
|
+
const data = stickers[i].data;
|
|
332
|
+
if (typeof data === 'object' && data?.url) {
|
|
333
|
+
urls.push(data.url);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
if (urls.length > 0) {
|
|
337
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
if (cacheableKey) {
|
|
341
|
+
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
342
|
+
if (mediaBuff) {
|
|
343
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
344
|
+
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const lib = await getImageProcessingLibrary();
|
|
348
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
349
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
350
|
+
const hasJimp = 'jimp' in lib && !!lib.jimp?.Jimp;
|
|
351
|
+
if (!hasSharp && !hasImage) {
|
|
352
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
353
|
+
}
|
|
354
|
+
const stickerPackIdValue = generateMessageIDV2();
|
|
355
|
+
const stickerData = {};
|
|
356
|
+
const stickerMetadata = new Array(stickers.length);
|
|
357
|
+
for (let i = 0; i < stickers.length; i += CONCURRENCY_LIMIT) {
|
|
358
|
+
const promises = [];
|
|
359
|
+
const chunkEnd = Math.min(i + CONCURRENCY_LIMIT, stickers.length);
|
|
360
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
361
|
+
promises.push((async (index) => {
|
|
362
|
+
const sticker = stickers[index];
|
|
363
|
+
const { stream } = await getStream(sticker.data);
|
|
364
|
+
const buffer = await toBuffer(stream);
|
|
365
|
+
let webpBuffer;
|
|
366
|
+
let isAnimated = false;
|
|
367
|
+
if (isWebPBuffer(buffer)) {
|
|
368
|
+
webpBuffer = buffer;
|
|
369
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
370
|
+
}
|
|
371
|
+
else if (hasSharp) {
|
|
372
|
+
webpBuffer = await lib.sharp.default(buffer)
|
|
373
|
+
.resize(512, 512, { fit: 'inside' })
|
|
374
|
+
.webp({ quality: 80 })
|
|
375
|
+
.toBuffer();
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
webpBuffer = await new lib.image.Transformer(buffer)
|
|
379
|
+
.resize(512, 512)
|
|
380
|
+
.webp(80);
|
|
381
|
+
}
|
|
382
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
383
|
+
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
384
|
+
}
|
|
385
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
386
|
+
const fileName = `${hash}.webp`;
|
|
387
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
388
|
+
stickerMetadata[index] = {
|
|
389
|
+
fileName,
|
|
390
|
+
mimetype: 'image/webp',
|
|
391
|
+
isAnimated,
|
|
392
|
+
emojis: sticker.emojis || ['✨'],
|
|
393
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
394
|
+
};
|
|
395
|
+
})(j));
|
|
396
|
+
}
|
|
397
|
+
await Promise.all(promises);
|
|
398
|
+
}
|
|
399
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
400
|
+
const { stream: coverStream } = await getStream(cover);
|
|
401
|
+
const coverBuffer = await toBuffer(coverStream);
|
|
402
|
+
let coverWebpBuffer;
|
|
403
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
404
|
+
coverWebpBuffer = coverBuffer;
|
|
405
|
+
}
|
|
406
|
+
else if (hasSharp) {
|
|
407
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
408
|
+
.resize(512, 512, { fit: 'inside' })
|
|
409
|
+
.webp({ quality: 80 })
|
|
410
|
+
.toBuffer();
|
|
411
|
+
}
|
|
412
|
+
else {
|
|
413
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
414
|
+
.resize(512, 512)
|
|
415
|
+
.webp(80);
|
|
416
|
+
}
|
|
417
|
+
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
418
|
+
const zipBuffer = await new Promise((resolve, reject) => {
|
|
419
|
+
zip(stickerData, (error, data) => error ? reject(error) : resolve(Buffer.from(data)));
|
|
420
|
+
});
|
|
421
|
+
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
422
|
+
logger,
|
|
423
|
+
opts: options.options
|
|
424
|
+
});
|
|
425
|
+
let stickerPackUploadResult;
|
|
426
|
+
try {
|
|
427
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
428
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
429
|
+
mediaType: 'sticker-pack',
|
|
430
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
finally {
|
|
434
|
+
fs.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
435
|
+
}
|
|
436
|
+
const obj = {
|
|
437
|
+
name,
|
|
438
|
+
publisher,
|
|
439
|
+
stickerPackId: stickerPackIdValue,
|
|
440
|
+
packDescription: description,
|
|
441
|
+
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
442
|
+
stickerPackSize: zipBuffer.length,
|
|
443
|
+
stickers: stickerMetadata,
|
|
444
|
+
fileSha256: stickerPackUpload.fileSha256,
|
|
445
|
+
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
446
|
+
mediaKey: stickerPackUpload.mediaKey,
|
|
447
|
+
directPath: stickerPackUploadResult.directPath,
|
|
448
|
+
fileLength: stickerPackUpload.fileLength,
|
|
449
|
+
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
450
|
+
trayIconFileName
|
|
451
|
+
};
|
|
452
|
+
try {
|
|
453
|
+
let thumbnailBuffer;
|
|
454
|
+
if (hasSharp) {
|
|
455
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
456
|
+
}
|
|
457
|
+
else if (hasImage) {
|
|
458
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
459
|
+
}
|
|
460
|
+
else if (hasJimp) {
|
|
461
|
+
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
462
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
463
|
+
}
|
|
464
|
+
else {
|
|
465
|
+
throw new Error('No image processing library available for thumbnail generation');
|
|
466
|
+
}
|
|
467
|
+
if (!thumbnailBuffer || thumbnailBuffer.length === 0) {
|
|
468
|
+
throw new Error('Failed to generate thumbnail buffer');
|
|
469
|
+
}
|
|
470
|
+
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
471
|
+
logger,
|
|
472
|
+
opts: options.options,
|
|
473
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
474
|
+
});
|
|
475
|
+
let thumbUploadResult;
|
|
476
|
+
try {
|
|
477
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
478
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
479
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
480
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
finally {
|
|
484
|
+
fs.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
485
|
+
}
|
|
486
|
+
Object.assign(obj, {
|
|
487
|
+
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
488
|
+
thumbnailSha256: thumbUpload.fileSha256,
|
|
489
|
+
thumbnailEncSha256: thumbUpload.fileEncSha256,
|
|
490
|
+
thumbnailHeight: 252,
|
|
491
|
+
thumbnailWidth: 252,
|
|
492
|
+
imageDataHash: sha256(thumbnailBuffer).toString('base64')
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
497
|
+
}
|
|
498
|
+
if (cacheableKey) {
|
|
499
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
500
|
+
options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
501
|
+
}
|
|
502
|
+
return WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
503
|
+
};
|
|
504
|
+
const prepareNativeFlowButtons = (message) => {
|
|
505
|
+
const buttons = message.nativeFlow;
|
|
506
|
+
const isButtonsFieldArray = Array.isArray(buttons);
|
|
507
|
+
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
508
|
+
const messageParamsJson = {};
|
|
509
|
+
if (hasOptionalProperty(message, 'offerText') && !!message.offerText) {
|
|
510
|
+
Object.assign(messageParamsJson, {
|
|
511
|
+
limited_time_offer: {
|
|
512
|
+
text: message.offerText || LIBRARY_NAME,
|
|
513
|
+
url: message.offerUrl || DONATE_URL,
|
|
514
|
+
copy_code: message.offerCode,
|
|
515
|
+
expiration_time: message.offerExpiration
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
if (hasOptionalProperty(message, 'optionText') && !!message.optionText) {
|
|
520
|
+
Object.assign(messageParamsJson, {
|
|
521
|
+
bottom_sheet: {
|
|
522
|
+
in_thread_buttons_limit: 1,
|
|
523
|
+
divider_indices: Array.from({ length: correctedField.length }, (_, index) => index),
|
|
524
|
+
list_title: message.optionTitle || '📄 Select Options',
|
|
525
|
+
button_title: message.optionText
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
buttons: correctedField.map(button => {
|
|
531
|
+
const buttonText = button.text || button.buttonText;
|
|
532
|
+
const buttonIcon = button.icon?.toUpperCase();
|
|
533
|
+
if (hasOptionalProperty(button, 'id') && !!button.id) {
|
|
534
|
+
return { name: 'quick_reply', buttonParamsJson: JSON.stringify({ display_text: buttonText || '👉🏻 Click', id: button.id, icon: buttonIcon }) };
|
|
535
|
+
}
|
|
536
|
+
else if (hasOptionalProperty(button, 'copy') && !!button.copy) {
|
|
537
|
+
return { name: 'cta_copy', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📋 Copy', copy_code: button.copy, icon: buttonIcon }) };
|
|
538
|
+
}
|
|
539
|
+
else if (hasOptionalProperty(button, 'url') && !!button.url) {
|
|
540
|
+
return { name: 'cta_url', buttonParamsJson: JSON.stringify({ display_text: buttonText || '🌐 Visit', url: button.url, merchant_url: button.url, webview_interaction: button.useWebview, icon: buttonIcon }) };
|
|
541
|
+
}
|
|
542
|
+
else if (hasOptionalProperty(button, 'call') && !!button.call) {
|
|
543
|
+
return { name: 'cta_call', buttonParamsJson: JSON.stringify({ display_text: buttonText || '📞 Call', phone_number: button.call, icon: buttonIcon }) };
|
|
544
|
+
}
|
|
545
|
+
else if (hasOptionalProperty(button, 'sections') && !!button.sections) {
|
|
546
|
+
return { name: 'single_select', buttonParamsJson: JSON.stringify({ title: buttonText || '📋 Select', sections: button.sections, icon: buttonIcon }) };
|
|
547
|
+
}
|
|
548
|
+
return button;
|
|
549
|
+
}),
|
|
550
|
+
messageParamsJson: JSON.stringify(messageParamsJson),
|
|
551
|
+
messageVersion: 3
|
|
552
|
+
};
|
|
269
553
|
};
|
|
270
|
-
function hasOptionalProperty(obj, key) {
|
|
271
|
-
return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
|
|
272
|
-
}
|
|
273
554
|
export const generateWAMessageContent = async (message, options) => {
|
|
274
555
|
var _a, _b;
|
|
275
556
|
let m = {};
|
|
276
|
-
if (hasNonNullishProperty(message, '
|
|
557
|
+
if (hasNonNullishProperty(message, 'raw')) {
|
|
558
|
+
delete message.raw;
|
|
559
|
+
return message;
|
|
560
|
+
}
|
|
561
|
+
else if (hasNonNullishProperty(message, 'code') ||
|
|
562
|
+
hasNonNullishProperty(message, 'links') ||
|
|
563
|
+
hasNonNullishProperty(message, 'table') ||
|
|
564
|
+
hasNonNullishProperty(message, 'richResponse') ||
|
|
565
|
+
hasNonNullishProperty(message, 'latex') ||
|
|
566
|
+
hasNonNullishProperty(message, 'gridImage') ||
|
|
567
|
+
hasNonNullishProperty(message, 'inlineImage') ||
|
|
568
|
+
hasNonNullishProperty(message, 'dynamic') ||
|
|
569
|
+
hasNonNullishProperty(message, 'map') ||
|
|
570
|
+
hasNonNullishProperty(message, 'contentItems') ||
|
|
571
|
+
// tipe baru (nama prefixed 'rich' agar tidak konflik dengan media biasa)
|
|
572
|
+
hasNonNullishProperty(message, 'richImage') ||
|
|
573
|
+
hasNonNullishProperty(message, 'richVideo') ||
|
|
574
|
+
hasNonNullishProperty(message, 'reels') ||
|
|
575
|
+
hasNonNullishProperty(message, 'source') ||
|
|
576
|
+
hasNonNullishProperty(message, 'richProduct') ||
|
|
577
|
+
hasNonNullishProperty(message, 'richPost') ||
|
|
578
|
+
hasNonNullishProperty(message, 'tip') ||
|
|
579
|
+
hasNonNullishProperty(message, 'suggest')) {
|
|
580
|
+
m = prepareRichResponseMessage(message);
|
|
581
|
+
}
|
|
582
|
+
else if (hasNonNullishProperty(message, 'text')) {
|
|
277
583
|
const extContent = { text: message.text };
|
|
278
584
|
let urlInfo = message.linkPreview;
|
|
279
585
|
if (typeof urlInfo === 'undefined') {
|
|
@@ -362,6 +668,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
362
668
|
}
|
|
363
669
|
}
|
|
364
670
|
}
|
|
671
|
+
else if (hasNonNullishProperty(message, 'stickers')) {
|
|
672
|
+
m.stickerPackMessage = await prepareStickerPackMessage(message, options);
|
|
673
|
+
}
|
|
365
674
|
else if (hasNonNullishProperty(message, 'pin')) {
|
|
366
675
|
m.pinInChatMessage = {};
|
|
367
676
|
m.messageContextInfo = {};
|
|
@@ -370,6 +679,25 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
370
679
|
m.pinInChatMessage.senderTimestampMs = Date.now();
|
|
371
680
|
m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
|
|
372
681
|
}
|
|
682
|
+
else if (hasNonNullishProperty(message, 'keep')) {
|
|
683
|
+
m.keepInChatMessage = {};
|
|
684
|
+
m.keepInChatMessage.key = message.keep;
|
|
685
|
+
m.keepInChatMessage.keepType = message.type;
|
|
686
|
+
m.keepInChatMessage.timestampMs = Date.now();
|
|
687
|
+
}
|
|
688
|
+
else if (hasNonNullishProperty(message, 'flowReply')) {
|
|
689
|
+
m.interactiveResponseMessage = {
|
|
690
|
+
body: {
|
|
691
|
+
format: message.flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
|
|
692
|
+
text: message.flowReply.text
|
|
693
|
+
},
|
|
694
|
+
nativeFlowResponseMessage: {
|
|
695
|
+
name: message.flowReply.name,
|
|
696
|
+
paramsJson: message.flowReply.paramsJson || '{}',
|
|
697
|
+
version: message.flowReply.version || 1
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
}
|
|
373
701
|
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
374
702
|
switch (message.type) {
|
|
375
703
|
case 'template':
|
|
@@ -393,17 +721,32 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
393
721
|
m.ptvMessage = videoMessage;
|
|
394
722
|
}
|
|
395
723
|
else if (hasNonNullishProperty(message, 'product')) {
|
|
396
|
-
|
|
397
|
-
|
|
724
|
+
if (!message.businessOwnerJid) {
|
|
725
|
+
throw new Boom('"businessOwnerJid" is missing from the content', { statusCode: 400 });
|
|
726
|
+
}
|
|
727
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
728
|
+
const content = {
|
|
398
729
|
...message,
|
|
399
730
|
product: {
|
|
731
|
+
currencyCode: 'IDR',
|
|
732
|
+
priceAmount1000: 1000,
|
|
733
|
+
title: LIBRARY_NAME,
|
|
400
734
|
...message.product,
|
|
401
735
|
productImage: imageMessage
|
|
402
736
|
}
|
|
403
|
-
}
|
|
737
|
+
};
|
|
738
|
+
delete content.image;
|
|
739
|
+
m.productMessage = WAProto.Message.ProductMessage.create(content);
|
|
404
740
|
}
|
|
405
741
|
else if (hasNonNullishProperty(message, 'listReply')) {
|
|
406
|
-
m.listResponseMessage = {
|
|
742
|
+
m.listResponseMessage = {
|
|
743
|
+
description: message.listReply.description,
|
|
744
|
+
listType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT,
|
|
745
|
+
singleSelectReply: {
|
|
746
|
+
selectedRowId: message.listReply.id
|
|
747
|
+
},
|
|
748
|
+
title: message.listReply.title
|
|
749
|
+
};
|
|
407
750
|
}
|
|
408
751
|
else if (hasNonNullishProperty(message, 'event')) {
|
|
409
752
|
m.eventMessage = {};
|
|
@@ -436,21 +779,35 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
436
779
|
statusCode: 400
|
|
437
780
|
});
|
|
438
781
|
}
|
|
439
|
-
m.messageContextInfo = {
|
|
440
|
-
// encKey
|
|
441
|
-
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
442
|
-
};
|
|
443
782
|
const pollCreationMessage = {
|
|
444
783
|
name: message.poll.name,
|
|
445
784
|
selectableOptionsCount: message.poll.selectableCount,
|
|
446
|
-
options: message.poll.values.map(optionName => ({ optionName }))
|
|
785
|
+
options: message.poll.values.map(optionName => ({ optionName })),
|
|
786
|
+
endTime: message.poll.endDate ? message.poll.endDate.getTime() : undefined,
|
|
787
|
+
hideParticipantName: message.poll.hideVoter ?? false,
|
|
788
|
+
allowAddOption: message.poll.canAddOption ?? false
|
|
447
789
|
};
|
|
448
790
|
if (message.poll.toAnnouncementGroup) {
|
|
449
791
|
// poll v2 is for community announcement groups (single select and multiple)
|
|
450
792
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
451
793
|
}
|
|
452
794
|
else {
|
|
453
|
-
|
|
795
|
+
// Add quiz message support
|
|
796
|
+
if (message.poll.pollType === 1) {
|
|
797
|
+
if (!message.poll.correctAnswer) {
|
|
798
|
+
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
799
|
+
}
|
|
800
|
+
m.pollCreationMessageV5 = {
|
|
801
|
+
// quiz for newsletter only
|
|
802
|
+
...pollCreationMessage,
|
|
803
|
+
correctAnswer: {
|
|
804
|
+
optionName: message.poll.correctAnswer.toString()
|
|
805
|
+
},
|
|
806
|
+
pollType: 1,
|
|
807
|
+
selectableOptionsCount: 1
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
else if (message.poll.selectableCount === 1) {
|
|
454
811
|
//poll v3 is for single select polls
|
|
455
812
|
m.pollCreationMessageV3 = pollCreationMessage;
|
|
456
813
|
}
|
|
@@ -459,11 +816,88 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
459
816
|
m.pollCreationMessage = pollCreationMessage;
|
|
460
817
|
}
|
|
461
818
|
}
|
|
819
|
+
m.messageContextInfo = {
|
|
820
|
+
// encKey
|
|
821
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
else if (hasNonNullishProperty(message, 'pollResult')) {
|
|
825
|
+
const pollResultSnapshotMessage = {
|
|
826
|
+
name: message.pollResult.name,
|
|
827
|
+
pollVotes: message.pollResult.votes.map(vote => ({
|
|
828
|
+
optionName: vote.name,
|
|
829
|
+
optionVoteCount: parseInt(vote.voteCount)
|
|
830
|
+
}))
|
|
831
|
+
};
|
|
832
|
+
if (message.pollResult.pollType === 1) {
|
|
833
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
|
|
834
|
+
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
pollResultSnapshotMessage.pollType = proto.Message.PollType.POLL;
|
|
838
|
+
m.pollResultSnapshotMessage = pollResultSnapshotMessage;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
else if (hasNonNullishProperty(message, 'pollUpdate')) {
|
|
842
|
+
if (!message.pollUpdate.key) {
|
|
843
|
+
throw new Boom('Message key is required', { statusCode: 400 });
|
|
844
|
+
}
|
|
845
|
+
if (!message.pollUpdate.vote) {
|
|
846
|
+
throw new Boom('Encrypted vote payload is required', { statusCode: 400 });
|
|
847
|
+
}
|
|
848
|
+
m.pollUpdateMessage = {
|
|
849
|
+
metadata: message.pollUpdate.metadata,
|
|
850
|
+
pollCreationMessageKey: message.pollUpdate.key,
|
|
851
|
+
senderTimestampMs: Date.now(),
|
|
852
|
+
vote: message.pollUpdate.vote
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
|
|
856
|
+
m.paymentInviteMessage = {
|
|
857
|
+
expiryTimestamp: Date.now(),
|
|
858
|
+
serviceType: message.paymentInviteServiceType
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
else if (hasNonNullishProperty(message, 'orderText')) {
|
|
862
|
+
if (!Buffer.isBuffer(message.thumbnail)) {
|
|
863
|
+
throw new Boom('Must provide thumbnail buffer in order message', { statusCode: 400 });
|
|
864
|
+
}
|
|
865
|
+
m.orderMessage = {
|
|
866
|
+
itemCount: 1,
|
|
867
|
+
messageVersion: 1,
|
|
868
|
+
orderTitle: LIBRARY_NAME,
|
|
869
|
+
status: proto.Message.OrderMessage.OrderStatus.INQUIRY,
|
|
870
|
+
surface: proto.Message.OrderMessage.OrderSurface.CATALOG,
|
|
871
|
+
token: generateMessageIDV2(),
|
|
872
|
+
totalAmount1000: 1000,
|
|
873
|
+
totalCurrencyCode: 'IDR',
|
|
874
|
+
...message,
|
|
875
|
+
message: message.orderText
|
|
876
|
+
};
|
|
877
|
+
delete m.orderMessage.orderText;
|
|
462
878
|
}
|
|
463
879
|
else if (hasNonNullishProperty(message, 'album')) {
|
|
880
|
+
if (!Array.isArray(message.album)) {
|
|
881
|
+
throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
|
|
882
|
+
}
|
|
883
|
+
let videoCount = 0;
|
|
884
|
+
for (let i = 0; i < message.album.length; i++) {
|
|
885
|
+
if (message.album[i].video)
|
|
886
|
+
videoCount++;
|
|
887
|
+
}
|
|
888
|
+
;
|
|
889
|
+
let imageCount = 0;
|
|
890
|
+
for (let i = 0; i < message.album.length; i++) {
|
|
891
|
+
if (message.album[i].image)
|
|
892
|
+
imageCount++;
|
|
893
|
+
}
|
|
894
|
+
;
|
|
895
|
+
if ((videoCount + imageCount) < 2) {
|
|
896
|
+
throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
897
|
+
}
|
|
464
898
|
m.albumMessage = {
|
|
465
|
-
expectedImageCount:
|
|
466
|
-
expectedVideoCount:
|
|
899
|
+
expectedImageCount: imageCount,
|
|
900
|
+
expectedVideoCount: videoCount
|
|
467
901
|
};
|
|
468
902
|
}
|
|
469
903
|
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
@@ -485,287 +919,336 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
485
919
|
}
|
|
486
920
|
};
|
|
487
921
|
}
|
|
488
|
-
else if ('
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
922
|
+
else if (hasNonNullishProperty(message, 'buttonsMessage')) {
|
|
923
|
+
// Direct buttonsMessage passthrough — supports headerType 6 (locationMessage), etc.
|
|
924
|
+
const btnMsg = { ...message.buttonsMessage };
|
|
925
|
+
if (btnMsg.locationMessage?.jpegThumbnail && !Buffer.isBuffer(btnMsg.locationMessage.jpegThumbnail)) {
|
|
926
|
+
const lib = await getImageProcessingLibrary();
|
|
927
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
928
|
+
if (hasSharp) {
|
|
929
|
+
const rawBuf = typeof btnMsg.locationMessage.jpegThumbnail === 'string'
|
|
930
|
+
? (await fs.readFile(btnMsg.locationMessage.jpegThumbnail))
|
|
931
|
+
: btnMsg.locationMessage.jpegThumbnail;
|
|
932
|
+
btnMsg.locationMessage.jpegThumbnail = await lib.sharp.default(rawBuf)
|
|
933
|
+
.resize(300, 300, { fit: 'inside', withoutEnlargement: true })
|
|
934
|
+
.jpeg({ quality: 80 })
|
|
935
|
+
.toBuffer();
|
|
500
936
|
}
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
else if ('paymentInvite' in message) {
|
|
504
|
-
m.paymentInviteMessage = {
|
|
505
|
-
serviceType: message.paymentInvite?.type,
|
|
506
|
-
expiryTimestamp: message.paymentInvite?.expiry
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
else if ('order' in message) {
|
|
510
|
-
m.orderMessage = WAProto.Message.OrderMessage.fromObject({
|
|
511
|
-
orderId: message.order.id,
|
|
512
|
-
thumbnail: message.order.thumbnail,
|
|
513
|
-
itemCount: message.order.itemCount,
|
|
514
|
-
status: message.order.status,
|
|
515
|
-
surface: message.order.surface,
|
|
516
|
-
orderTitle: message.order.title,
|
|
517
|
-
message: message.order.text,
|
|
518
|
-
sellerJid: message.order.seller,
|
|
519
|
-
token: message.order.token,
|
|
520
|
-
totalAmount1000: message.order.amount,
|
|
521
|
-
totalCurrencyCode: message.order.currency
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
else if ('inviteAdmin' in message) {
|
|
525
|
-
m.newsletterAdminInviteMessage = {};
|
|
526
|
-
m.newsletterAdminInviteMessage.inviteExpiration = message.inviteAdmin?.inviteExpiration;
|
|
527
|
-
m.newsletterAdminInviteMessage.caption = message.inviteAdmin?.text;
|
|
528
|
-
m.newsletterAdminInviteMessage.newsletterJid = message.inviteAdmin?.jid;
|
|
529
|
-
m.newsletterAdminInviteMessage.newsletterName = message.inviteAdmin?.subject;
|
|
530
|
-
m.newsletterAdminInviteMessage.jpegThumbnail = message.inviteAdmin?.thumbnail;
|
|
531
|
-
}
|
|
532
|
-
else if ('requestPayment' in message) {
|
|
533
|
-
const _rp = message.requestPayment;
|
|
534
|
-
const sticker = _rp?.sticker
|
|
535
|
-
? await prepareWAMessageMedia({ sticker: _rp.sticker }, options)
|
|
536
|
-
: null;
|
|
537
|
-
let notes = {};
|
|
538
|
-
if (_rp?.sticker) {
|
|
539
|
-
notes = {
|
|
540
|
-
stickerMessage: {
|
|
541
|
-
...sticker?.stickerMessage,
|
|
542
|
-
contextInfo: _rp?.contextInfo
|
|
543
|
-
}
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
else if (_rp?.note) {
|
|
547
|
-
notes = {
|
|
548
|
-
extendedTextMessage: {
|
|
549
|
-
text: _rp.note,
|
|
550
|
-
contextInfo: _rp?.contextInfo
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
937
|
}
|
|
554
|
-
|
|
555
|
-
throw new Boom('Invalid media type', { statusCode: 400 });
|
|
556
|
-
}
|
|
557
|
-
m.requestPaymentMessage = WAProto.Message.RequestPaymentMessage.fromObject({
|
|
558
|
-
expiryTimestamp: _rp?.expiry,
|
|
559
|
-
amount1000: _rp?.amount,
|
|
560
|
-
currencyCodeIso4217: _rp?.currency,
|
|
561
|
-
requestFrom: _rp?.from,
|
|
562
|
-
noteMessage: { ...notes },
|
|
563
|
-
background: _rp?.background ?? null
|
|
564
|
-
});
|
|
938
|
+
m = { buttonsMessage: btnMsg };
|
|
565
939
|
}
|
|
566
940
|
else {
|
|
567
941
|
m = await prepareWAMessageMedia(message, options);
|
|
568
942
|
}
|
|
569
|
-
if ('buttons'
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
943
|
+
if (hasNonNullishProperty(message, 'buttons')) {
|
|
944
|
+
const buttonsMessage = {
|
|
945
|
+
buttons: message.buttons.map(button => {
|
|
946
|
+
const buttonText = button.text || button.buttonText;
|
|
947
|
+
if (hasOptionalProperty(button, 'sections')) {
|
|
948
|
+
return {
|
|
949
|
+
nativeFlowInfo: {
|
|
950
|
+
name: 'single_select',
|
|
951
|
+
paramsJson: JSON.stringify({
|
|
952
|
+
title: buttonText,
|
|
953
|
+
sections: button.sections
|
|
954
|
+
})
|
|
955
|
+
},
|
|
956
|
+
type: ButtonType.NATIVE_FLOW
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
else if (hasOptionalProperty(button, 'name')) {
|
|
960
|
+
return {
|
|
961
|
+
nativeFlowInfo: {
|
|
962
|
+
name: button.name,
|
|
963
|
+
paramsJson: button.paramsJson
|
|
964
|
+
},
|
|
965
|
+
type: ButtonType.NATIVE_FLOW
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
buttonId: button.id || button.buttonId,
|
|
970
|
+
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
971
|
+
type: button.type || ButtonType.RESPONSE
|
|
972
|
+
};
|
|
580
973
|
})
|
|
581
974
|
};
|
|
582
|
-
if ('text'
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
else if ('caption' in message) {
|
|
586
|
-
interactiveMessage.body = { text: message.caption };
|
|
587
|
-
interactiveMessage.header = { hasMediaAttachment: true };
|
|
588
|
-
Object.assign(interactiveMessage.header, m);
|
|
589
|
-
}
|
|
590
|
-
if ('title' in message && !!message.title) {
|
|
591
|
-
interactiveMessage.header = interactiveMessage.header || {};
|
|
592
|
-
interactiveMessage.header.title = message.title;
|
|
593
|
-
interactiveMessage.header.hasMediaAttachment = false;
|
|
594
|
-
}
|
|
595
|
-
if ('footer' in message && !!message.footer) {
|
|
596
|
-
interactiveMessage.footer = { text: message.footer };
|
|
975
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
976
|
+
buttonsMessage.contentText = message.text;
|
|
977
|
+
buttonsMessage.headerType = ButtonHeaderType.EMPTY;
|
|
597
978
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
if ('mentions' in message && !!message.mentions) {
|
|
602
|
-
interactiveMessage.contextInfo = { mentionedJid: message.mentions };
|
|
603
|
-
}
|
|
604
|
-
m = { interactiveMessage };
|
|
605
|
-
}
|
|
606
|
-
else if ('templateButtons' in message && !!message.templateButtons) {
|
|
607
|
-
const nativeButtons = message.templateButtons.map(b => {
|
|
608
|
-
if (b.quickReplyButton) {
|
|
609
|
-
return {
|
|
610
|
-
name: 'quick_reply',
|
|
611
|
-
buttonParamsJson: JSON.stringify({
|
|
612
|
-
display_text: b.quickReplyButton.displayText || '',
|
|
613
|
-
id: b.quickReplyButton.id || ''
|
|
614
|
-
})
|
|
615
|
-
};
|
|
616
|
-
}
|
|
617
|
-
if (b.urlButton) {
|
|
618
|
-
return {
|
|
619
|
-
name: 'cta_url',
|
|
620
|
-
buttonParamsJson: JSON.stringify({
|
|
621
|
-
display_text: b.urlButton.displayText || '',
|
|
622
|
-
url: b.urlButton.url || '',
|
|
623
|
-
merchant_url: b.urlButton.url || ''
|
|
624
|
-
})
|
|
625
|
-
};
|
|
626
|
-
}
|
|
627
|
-
if (b.callButton) {
|
|
628
|
-
return {
|
|
629
|
-
name: 'cta_call',
|
|
630
|
-
buttonParamsJson: JSON.stringify({
|
|
631
|
-
display_text: b.callButton.displayText || '',
|
|
632
|
-
phone_number: b.callButton.phoneNumber || ''
|
|
633
|
-
})
|
|
634
|
-
};
|
|
979
|
+
else {
|
|
980
|
+
if (hasOptionalProperty(message, 'caption')) {
|
|
981
|
+
buttonsMessage.contentText = message.caption;
|
|
635
982
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
983
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
984
|
+
buttonsMessage.headerType = ButtonHeaderType[type];
|
|
985
|
+
Object.assign(buttonsMessage, m);
|
|
986
|
+
}
|
|
987
|
+
if (hasOptionalProperty(message, 'footer')) {
|
|
988
|
+
buttonsMessage.footerText = message.footer;
|
|
989
|
+
}
|
|
990
|
+
m = { buttonsMessage };
|
|
991
|
+
}
|
|
992
|
+
else if (hasNonNullishProperty(message, 'sections')) {
|
|
993
|
+
const listMessage = {
|
|
994
|
+
sections: message.sections,
|
|
995
|
+
buttonText: message.buttonText,
|
|
996
|
+
title: message.title,
|
|
997
|
+
footerText: message.footer,
|
|
998
|
+
description: message.text,
|
|
999
|
+
listType: ListType.SINGLE_SELECT
|
|
1000
|
+
};
|
|
1001
|
+
m = { listMessage };
|
|
1002
|
+
}
|
|
1003
|
+
else if (hasNonNullishProperty(message, 'templateButtons')) {
|
|
1004
|
+
const hydratedTemplate = {
|
|
1005
|
+
hydratedButtons: message.templateButtons.map((button, i) => {
|
|
1006
|
+
const buttonText = button.text || button.buttonText;
|
|
1007
|
+
if (hasOptionalProperty(button, 'id')) {
|
|
1008
|
+
return {
|
|
1009
|
+
index: i,
|
|
1010
|
+
quickReplyButton: {
|
|
1011
|
+
displayText: buttonText || '👉🏻 Click',
|
|
1012
|
+
id: button.id
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
else if (hasOptionalProperty(button, 'url')) {
|
|
1017
|
+
return {
|
|
1018
|
+
index: i,
|
|
1019
|
+
urlButton: {
|
|
1020
|
+
displayText: buttonText || '🌐 Visit',
|
|
1021
|
+
url: button.url
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
else if (hasOptionalProperty(button, 'call')) {
|
|
1026
|
+
return {
|
|
1027
|
+
index: i,
|
|
1028
|
+
callButton: {
|
|
1029
|
+
displayText: buttonText || '📞 Call',
|
|
1030
|
+
phoneNumber: button.call
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
button.index = button.index || i;
|
|
1035
|
+
return button;
|
|
644
1036
|
})
|
|
645
1037
|
};
|
|
646
|
-
if ('text'
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
else if ('caption' in message) {
|
|
650
|
-
interactiveMessage.body = { text: message.caption };
|
|
651
|
-
interactiveMessage.header = { hasMediaAttachment: true };
|
|
652
|
-
Object.assign(interactiveMessage.header, m);
|
|
1038
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
1039
|
+
hydratedTemplate.hydratedContentText = message.text;
|
|
653
1040
|
}
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1041
|
+
else {
|
|
1042
|
+
if (hasOptionalProperty(message, 'caption')) {
|
|
1043
|
+
hydratedTemplate.hydratedTitleText = message.title;
|
|
1044
|
+
hydratedTemplate.hydratedContentText = message.caption;
|
|
1045
|
+
}
|
|
1046
|
+
;
|
|
1047
|
+
Object.assign(hydratedTemplate, m);
|
|
659
1048
|
}
|
|
660
|
-
if ('
|
|
661
|
-
|
|
1049
|
+
if (hasOptionalProperty(message, 'footer')) {
|
|
1050
|
+
hydratedTemplate.hydratedFooterText = message.footer;
|
|
662
1051
|
}
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
name: 'single_select',
|
|
670
|
-
buttonParamsJson: JSON.stringify({
|
|
671
|
-
title: message.buttonText || 'Lihat Pilihan',
|
|
672
|
-
sections: message.sections.map(s => ({
|
|
673
|
-
title: s.title || '',
|
|
674
|
-
highlight_label: s.highlight_label || '',
|
|
675
|
-
rows: (s.rows || []).map(r => ({
|
|
676
|
-
header: r.title || '',
|
|
677
|
-
title: r.description || '',
|
|
678
|
-
id: r.rowId || r.id || ''
|
|
679
|
-
}))
|
|
680
|
-
}))
|
|
681
|
-
})
|
|
682
|
-
}]
|
|
683
|
-
})
|
|
1052
|
+
hydratedTemplate.templateId = message.id || 'template-' + Date.now();
|
|
1053
|
+
m = {
|
|
1054
|
+
templateMessage: {
|
|
1055
|
+
hydratedFourRowTemplate: hydratedTemplate,
|
|
1056
|
+
hydratedTemplate: hydratedTemplate
|
|
1057
|
+
}
|
|
684
1058
|
};
|
|
685
|
-
if (message.text) interactiveMessage.body = { text: message.text };
|
|
686
|
-
if (message.footer) interactiveMessage.footer = { text: message.footer };
|
|
687
|
-
if (message.title) interactiveMessage.header = { title: message.title, hasMediaAttachment: false };
|
|
688
|
-
if (message.contextInfo) interactiveMessage.contextInfo = message.contextInfo;
|
|
689
|
-
if (message.mentions) interactiveMessage.contextInfo = { mentionedJid: message.mentions };
|
|
690
|
-
m = { interactiveMessage };
|
|
691
1059
|
}
|
|
692
|
-
if ('
|
|
1060
|
+
else if (hasNonNullishProperty(message, 'nativeFlow')) {
|
|
693
1061
|
const interactiveMessage = {
|
|
694
|
-
nativeFlowMessage:
|
|
695
|
-
buttons: message.interactiveButtons
|
|
696
|
-
})
|
|
1062
|
+
nativeFlowMessage: prepareNativeFlowButtons(message)
|
|
697
1063
|
};
|
|
698
|
-
if ('
|
|
699
|
-
interactiveMessage.
|
|
1064
|
+
if (hasOptionalProperty(message, 'bizJid')) {
|
|
1065
|
+
interactiveMessage.collectionMessage = {
|
|
1066
|
+
bizJid: message.bizJid,
|
|
1067
|
+
id: message.id,
|
|
1068
|
+
messageVersion: 1
|
|
1069
|
+
};
|
|
700
1070
|
}
|
|
701
|
-
else if ('
|
|
702
|
-
interactiveMessage.
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
hasMediaAttachment: message?.media ?? false
|
|
1071
|
+
else if (hasOptionalProperty(message, 'shopSurface')) {
|
|
1072
|
+
interactiveMessage.shopStorefrontMessage = {
|
|
1073
|
+
surface: message.shopSurface,
|
|
1074
|
+
id: message.id,
|
|
1075
|
+
messageVersion: 1
|
|
707
1076
|
};
|
|
708
|
-
Object.assign(interactiveMessage.header, m);
|
|
709
1077
|
}
|
|
710
|
-
if ('
|
|
711
|
-
interactiveMessage.
|
|
1078
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
1079
|
+
interactiveMessage.body = { text: message.text };
|
|
712
1080
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
1081
|
+
else {
|
|
1082
|
+
if (hasOptionalProperty(message, 'caption')) {
|
|
1083
|
+
const isValidHeader = hasValidInteractiveHeader(m);
|
|
1084
|
+
if (!isValidHeader) {
|
|
1085
|
+
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
1086
|
+
}
|
|
1087
|
+
interactiveMessage.header = {
|
|
1088
|
+
title: message.title || '',
|
|
1089
|
+
subtitle: message.subtitle || '',
|
|
1090
|
+
hasMediaAttachment: isValidHeader
|
|
1091
|
+
};
|
|
1092
|
+
interactiveMessage.body = { text: message.caption };
|
|
1093
|
+
}
|
|
1094
|
+
if (hasOptionalProperty(message, 'thumbnail') && !!message.thumbnail) {
|
|
1095
|
+
interactiveMessage.jpegThumbnail = message.thumbnail;
|
|
1096
|
+
}
|
|
719
1097
|
Object.assign(interactiveMessage.header, m);
|
|
720
1098
|
}
|
|
721
|
-
if ('
|
|
722
|
-
|
|
1099
|
+
if (hasOptionalProperty(message, 'audioFooter')) {
|
|
1100
|
+
const { audioMessage } = await prepareWAMessageMedia({
|
|
1101
|
+
audio: message.audioFooter
|
|
1102
|
+
}, options);
|
|
1103
|
+
interactiveMessage.footer = {
|
|
1104
|
+
audioMessage,
|
|
1105
|
+
hasMediaAttachment: true
|
|
1106
|
+
};
|
|
723
1107
|
}
|
|
724
|
-
if ('
|
|
725
|
-
interactiveMessage.
|
|
1108
|
+
else if (hasOptionalProperty(message, 'footer')) {
|
|
1109
|
+
interactiveMessage.footer = { text: message.footer };
|
|
726
1110
|
}
|
|
727
1111
|
m = { interactiveMessage };
|
|
728
1112
|
}
|
|
729
|
-
if ('
|
|
1113
|
+
else if (hasNonNullishProperty(message, 'cards')) {
|
|
730
1114
|
const interactiveMessage = {
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
1115
|
+
carouselMessage: {
|
|
1116
|
+
cards: await Promise.all(message.cards.map(async (card) => {
|
|
1117
|
+
let carouselHeader = {};
|
|
1118
|
+
if (hasNonNullishProperty(card, 'product')) {
|
|
1119
|
+
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1120
|
+
}
|
|
1121
|
+
else {
|
|
1122
|
+
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
1123
|
+
}
|
|
1124
|
+
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
1125
|
+
if (!isValidHeader) {
|
|
1126
|
+
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1127
|
+
}
|
|
1128
|
+
const carouselCard = {
|
|
1129
|
+
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
1130
|
+
};
|
|
1131
|
+
if (hasOptionalProperty(card, 'text')) {
|
|
1132
|
+
carouselCard.body = { text: card.text };
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
if (hasOptionalProperty(card, 'caption')) {
|
|
1136
|
+
carouselCard.header = {
|
|
1137
|
+
title: card.title || '',
|
|
1138
|
+
subtitle: card.subtitle || '',
|
|
1139
|
+
hasMediaAttachment: isValidHeader
|
|
1140
|
+
};
|
|
1141
|
+
carouselCard.body = { text: card.caption };
|
|
1142
|
+
}
|
|
1143
|
+
if (hasOptionalProperty(card, 'thumbnail') && !!card.thumbnail) {
|
|
1144
|
+
carouselCard.jpegThumbnail = card.thumbnail;
|
|
1145
|
+
}
|
|
1146
|
+
Object.assign(carouselCard.header, carouselHeader);
|
|
1147
|
+
}
|
|
1148
|
+
if (hasOptionalProperty(card, 'audioFooter')) {
|
|
1149
|
+
const { audioMessage } = await prepareWAMessageMedia({
|
|
1150
|
+
audio: card.audioFooter
|
|
1151
|
+
}, options);
|
|
1152
|
+
carouselCard.footer = {
|
|
1153
|
+
audioMessage,
|
|
1154
|
+
hasMediaAttachment: true
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
else if (hasOptionalProperty(card, 'footer')) {
|
|
1158
|
+
carouselCard.footer = { text: card.footer };
|
|
1159
|
+
}
|
|
1160
|
+
return carouselCard;
|
|
1161
|
+
})),
|
|
1162
|
+
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1163
|
+
messageVersion: 1
|
|
1164
|
+
}
|
|
735
1165
|
};
|
|
736
|
-
if ('text'
|
|
1166
|
+
if (hasOptionalProperty(message, 'text')) {
|
|
737
1167
|
interactiveMessage.body = { text: message.text };
|
|
738
1168
|
}
|
|
739
|
-
|
|
740
|
-
interactiveMessage.body = { text: message.caption };
|
|
741
|
-
interactiveMessage.header = {
|
|
742
|
-
title: message.title,
|
|
743
|
-
subtitle: message.subtitle,
|
|
744
|
-
hasMediaAttachment: message?.media ?? false
|
|
745
|
-
};
|
|
746
|
-
Object.assign(interactiveMessage.header, m);
|
|
747
|
-
}
|
|
748
|
-
if ('footer' in message && !!message.footer) {
|
|
1169
|
+
if (hasOptionalProperty(message, 'footer')) {
|
|
749
1170
|
interactiveMessage.footer = { text: message.footer };
|
|
750
1171
|
}
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
1172
|
+
m = { interactiveMessage };
|
|
1173
|
+
}
|
|
1174
|
+
else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
|
|
1175
|
+
const requestPaymentMessage = {
|
|
1176
|
+
amount: {
|
|
1177
|
+
currencyCode: 'IDR',
|
|
1178
|
+
offset: 1000,
|
|
1179
|
+
value: 1000
|
|
1180
|
+
},
|
|
1181
|
+
amount1000: 1000,
|
|
1182
|
+
currencyCodeIso4217: 'IDR',
|
|
1183
|
+
expiryTimestamp: Date.now(),
|
|
1184
|
+
noteMessage: m,
|
|
1185
|
+
requestFrom: message.requestPaymentFrom,
|
|
1186
|
+
...message
|
|
1187
|
+
};
|
|
1188
|
+
delete requestPaymentMessage.requestPaymentFrom;
|
|
1189
|
+
if (hasNonNullishProperty(m, 'extendedTextMessage') || hasNonNullishProperty(m, 'stickerMessage')) {
|
|
1190
|
+
Object.assign(requestPaymentMessage.noteMessage, m);
|
|
758
1191
|
}
|
|
759
|
-
|
|
760
|
-
|
|
1192
|
+
else {
|
|
1193
|
+
throw new Boom('Invalid message type for request payment note message', { statusCode: 400 });
|
|
761
1194
|
}
|
|
762
|
-
|
|
763
|
-
|
|
1195
|
+
m = { requestPaymentMessage };
|
|
1196
|
+
}
|
|
1197
|
+
else if (hasNonNullishProperty(message, 'invoiceNote')) {
|
|
1198
|
+
const attachment = m.imageMessage || m.documentMessage;
|
|
1199
|
+
const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
|
|
1200
|
+
const invoiceMessage = {
|
|
1201
|
+
attachmentType: proto.Message.InvoiceMessage.AttachmentType[type === 'DOCUMENT' ? 'PDF' : 'IMAGE'],
|
|
1202
|
+
note: message.invoiceNote
|
|
1203
|
+
};
|
|
1204
|
+
if (attachment) {
|
|
1205
|
+
const { directPath, fileEncSha256, fileSha256, jpegThumbnail = undefined, mediaKey, mediaKeyTimestamp, mimetype } = attachment;
|
|
1206
|
+
Object.assign(invoiceMessage, {
|
|
1207
|
+
attachmentDirectPath: directPath,
|
|
1208
|
+
attachmentFileEncSha256: fileEncSha256,
|
|
1209
|
+
attachmentFileSha256: fileSha256,
|
|
1210
|
+
attachmentJpegThumbnail: jpegThumbnail,
|
|
1211
|
+
attachmentMediaKey: mediaKey,
|
|
1212
|
+
attachmentMediaKeyTimestamp: mediaKeyTimestamp,
|
|
1213
|
+
attachmentMimetype: mimetype,
|
|
1214
|
+
token: generateMessageIDV2()
|
|
1215
|
+
});
|
|
764
1216
|
}
|
|
765
|
-
|
|
1217
|
+
else {
|
|
1218
|
+
throw new Boom('Invalid media type for invoice message', { statusCode: 400 });
|
|
1219
|
+
}
|
|
1220
|
+
m = { invoiceMessage };
|
|
766
1221
|
}
|
|
767
|
-
if (hasOptionalProperty(message, '
|
|
768
|
-
|
|
1222
|
+
if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
|
|
1223
|
+
const messageType = Object.keys(m)[0];
|
|
1224
|
+
const key = m[messageType];
|
|
1225
|
+
const content = message.externalAdReply;
|
|
1226
|
+
if ('thumbnail' in content && !Buffer.isBuffer(content.thumbnail)) {
|
|
1227
|
+
throw new Boom('Thumbnail must in buffer type', { statusCode: 400 });
|
|
1228
|
+
}
|
|
1229
|
+
if (!content.url || typeof content.url !== 'string') {
|
|
1230
|
+
content.url = DONATE_URL;
|
|
1231
|
+
}
|
|
1232
|
+
const externalAdReply = {
|
|
1233
|
+
...content,
|
|
1234
|
+
body: content.body,
|
|
1235
|
+
mediaType: content.mediaType || 1,
|
|
1236
|
+
mediaUrl: content.url,
|
|
1237
|
+
renderLargerThumbnail: content.largeThumbnail,
|
|
1238
|
+
sourceUrl: content.url,
|
|
1239
|
+
thumbnail: content.thumbnail,
|
|
1240
|
+
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1241
|
+
title: content.title || LIBRARY_NAME
|
|
1242
|
+
};
|
|
1243
|
+
delete externalAdReply.subTitle;
|
|
1244
|
+
delete externalAdReply.largeThumbnail;
|
|
1245
|
+
delete externalAdReply.url;
|
|
1246
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1247
|
+
key.contextInfo.externalAdReply = { ...key.contextInfo.externalAdReply, ...externalAdReply };
|
|
1248
|
+
}
|
|
1249
|
+
else if (key) {
|
|
1250
|
+
key.contextInfo = { externalAdReply };
|
|
1251
|
+
}
|
|
769
1252
|
}
|
|
770
1253
|
if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
|
|
771
1254
|
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
|
|
@@ -787,16 +1270,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
787
1270
|
};
|
|
788
1271
|
}
|
|
789
1272
|
}
|
|
790
|
-
if (hasOptionalProperty(message, 'edit')) {
|
|
791
|
-
m = {
|
|
792
|
-
protocolMessage: {
|
|
793
|
-
key: message.edit,
|
|
794
|
-
editedMessage: m,
|
|
795
|
-
timestampMs: Date.now(),
|
|
796
|
-
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
}
|
|
800
1273
|
if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
|
|
801
1274
|
const messageType = Object.keys(m)[0];
|
|
802
1275
|
const key = m[messageType];
|
|
@@ -807,12 +1280,71 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
807
1280
|
key.contextInfo = message.contextInfo;
|
|
808
1281
|
}
|
|
809
1282
|
}
|
|
810
|
-
if (hasOptionalProperty(message, '
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1283
|
+
if (hasOptionalProperty(message, 'groupStatus') && !!message.groupStatus) {
|
|
1284
|
+
const messageType = Object.keys(m)[0];
|
|
1285
|
+
const key = m[messageType];
|
|
1286
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1287
|
+
key.contextInfo.isGroupStatus = message.groupStatus;
|
|
1288
|
+
}
|
|
1289
|
+
else if (key) {
|
|
1290
|
+
key.contextInfo = {
|
|
1291
|
+
isGroupStatus: message.groupStatus
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
m = { groupStatusMessageV2: { message: m } };
|
|
1295
|
+
delete message.groupStatus;
|
|
1296
|
+
}
|
|
1297
|
+
if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
|
|
1298
|
+
const messageType = Object.keys(m)[0];
|
|
1299
|
+
const key = m[messageType];
|
|
1300
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1301
|
+
key.contextInfo.isSpoiler = message.spoiler;
|
|
1302
|
+
}
|
|
1303
|
+
else if (key) {
|
|
1304
|
+
key.contextInfo = {
|
|
1305
|
+
isSpoiler: message.spoiler
|
|
1306
|
+
};
|
|
1307
|
+
}
|
|
1308
|
+
m = { spoilerMessage: { message: m } };
|
|
1309
|
+
delete message.spoiler;
|
|
1310
|
+
}
|
|
1311
|
+
else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
1312
|
+
if (!m.interactiveMessage) {
|
|
1313
|
+
throw new Boom('Invalid message type for template', { statusCode: 400 });
|
|
1314
|
+
}
|
|
1315
|
+
m = {
|
|
1316
|
+
templateMessage: {
|
|
1317
|
+
interactiveMessageTemplate: m.interactiveMessage,
|
|
1318
|
+
templateId: message.id || 'template-' + Date.now()
|
|
1319
|
+
}
|
|
1320
|
+
};
|
|
1321
|
+
delete message.interactiveAsTemplate;
|
|
1322
|
+
}
|
|
1323
|
+
if (hasOptionalProperty(message, 'ephemeral') && !!message.ephemeral) {
|
|
1324
|
+
m = { ephemeralMessage: { message: m } };
|
|
1325
|
+
delete message.ephemeral;
|
|
1326
|
+
}
|
|
1327
|
+
if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
|
|
1328
|
+
m = { lottieStickerMessage: { message: m } };
|
|
1329
|
+
}
|
|
1330
|
+
else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1331
|
+
m = { viewOnceMessage: { message: m } };
|
|
1332
|
+
}
|
|
1333
|
+
else if (hasOptionalProperty(message, 'viewOnceV2') && !!message.viewOnceV2) {
|
|
1334
|
+
m = { viewOnceMessageV2: { message: m } };
|
|
1335
|
+
delete message.viewOnceV2;
|
|
1336
|
+
}
|
|
1337
|
+
else if (hasOptionalProperty(message, 'viewOnceV2Extension') && !!message.viewOnceV2Extension) {
|
|
1338
|
+
m = { viewOnceMessageV2Extension: { message: m } };
|
|
1339
|
+
delete message.viewOnceV2Extension;
|
|
1340
|
+
}
|
|
1341
|
+
if (hasOptionalProperty(message, 'edit')) {
|
|
1342
|
+
m = {
|
|
1343
|
+
protocolMessage: {
|
|
1344
|
+
key: message.edit,
|
|
1345
|
+
editedMessage: m,
|
|
1346
|
+
timestampMs: Date.now(),
|
|
1347
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
816
1348
|
}
|
|
817
1349
|
};
|
|
818
1350
|
}
|
|
@@ -831,10 +1363,12 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
831
1363
|
options.timestamp = new Date();
|
|
832
1364
|
}
|
|
833
1365
|
const innerMessage = normalizeMessageContent(message);
|
|
1366
|
+
const messageContextInfo = message.messageContextInfo;
|
|
834
1367
|
const key = getContentType(innerMessage);
|
|
835
1368
|
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
1369
|
+
const isNewsletter = isJidNewsletter(jid);
|
|
836
1370
|
const { quoted, userJid } = options;
|
|
837
|
-
if (quoted
|
|
1371
|
+
if (quoted) {
|
|
838
1372
|
const participant = quoted.key.fromMe
|
|
839
1373
|
? userJid // TODO: Add support for LIDs
|
|
840
1374
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
@@ -852,7 +1386,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
852
1386
|
contextInfo.quotedMessage = quotedMsg;
|
|
853
1387
|
// if a participant is quoted, then it must be a group
|
|
854
1388
|
// hence, remoteJid of group must also be entered
|
|
855
|
-
if (jid !== quoted.key.remoteJid) {
|
|
1389
|
+
if (!isNewsletter && jid !== quoted.key.remoteJid) {
|
|
856
1390
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
857
1391
|
}
|
|
858
1392
|
if (contextInfo && innerMessage[key]) {
|
|
@@ -868,7 +1402,7 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
868
1402
|
// already not converted to disappearing message
|
|
869
1403
|
key !== 'ephemeralMessage' &&
|
|
870
1404
|
// newsletters don't support ephemeral messages
|
|
871
|
-
!
|
|
1405
|
+
!isNewsletter) {
|
|
872
1406
|
/* @ts-ignore */
|
|
873
1407
|
innerMessage[key].contextInfo = {
|
|
874
1408
|
...(innerMessage[key].contextInfo || {}),
|
|
@@ -876,6 +1410,13 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
876
1410
|
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
877
1411
|
};
|
|
878
1412
|
}
|
|
1413
|
+
if (messageContextInfo?.messageSecret && (isPnUser(jid) || isLidUser(jid))) {
|
|
1414
|
+
messageContextInfo.deviceListMetadata = {
|
|
1415
|
+
recipientKeyHash: randomBytes(10),
|
|
1416
|
+
recipientTimestamp: unixTimestampSeconds()
|
|
1417
|
+
};
|
|
1418
|
+
messageContextInfo.deviceListMetadataVersion = 2;
|
|
1419
|
+
}
|
|
879
1420
|
message = WAProto.Message.create(message);
|
|
880
1421
|
const messageJSON = {
|
|
881
1422
|
key: {
|
|
@@ -895,7 +1436,10 @@ export const generateWAMessage = async (jid, content, options) => {
|
|
|
895
1436
|
// ensure msg ID is with every log
|
|
896
1437
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
897
1438
|
// Pass jid in the options to generateWAMessageContent
|
|
898
|
-
|
|
1439
|
+
if (jid) {
|
|
1440
|
+
options.jid = jid;
|
|
1441
|
+
}
|
|
1442
|
+
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
|
|
899
1443
|
};
|
|
900
1444
|
/** Get the key to access the true type of content */
|
|
901
1445
|
export const getContentType = (content) => {
|
|
@@ -905,12 +1449,6 @@ export const getContentType = (content) => {
|
|
|
905
1449
|
return key;
|
|
906
1450
|
}
|
|
907
1451
|
};
|
|
908
|
-
/**
|
|
909
|
-
* Normalizes ephemeral, view once messages to regular message content
|
|
910
|
-
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
911
|
-
* @param content
|
|
912
|
-
* @returns
|
|
913
|
-
*/
|
|
914
1452
|
export const normalizeMessageContent = (content) => {
|
|
915
1453
|
if (!content) {
|
|
916
1454
|
return undefined;
|
|
@@ -925,29 +1463,33 @@ export const normalizeMessageContent = (content) => {
|
|
|
925
1463
|
}
|
|
926
1464
|
return content;
|
|
927
1465
|
function getFutureProofMessage(message) {
|
|
928
|
-
return (message?.
|
|
929
|
-
message?.
|
|
1466
|
+
return (message?.associatedChildMessage ||
|
|
1467
|
+
message?.botForwardedMessage ||
|
|
1468
|
+
message?.botInvokeMessage ||
|
|
1469
|
+
message?.botTaskMessage ||
|
|
930
1470
|
message?.documentWithCaptionMessage ||
|
|
931
|
-
message?.viewOnceMessageV2 ||
|
|
932
|
-
message?.viewOnceMessageV2Extension ||
|
|
933
1471
|
message?.editedMessage ||
|
|
934
|
-
message?.
|
|
935
|
-
message?.botInvokeMessage ||
|
|
936
|
-
message?.lottieStickerMessage ||
|
|
1472
|
+
message?.ephemeralMessage ||
|
|
937
1473
|
message?.eventCoverImage ||
|
|
938
|
-
message?.
|
|
939
|
-
message?.pollCreationOptionImageMessage ||
|
|
940
|
-
message?.associatedChildMessage ||
|
|
1474
|
+
message?.groupMentionedMessage ||
|
|
941
1475
|
message?.groupStatusMentionMessage ||
|
|
942
|
-
message?.pollCreationMessageV4 ||
|
|
943
|
-
message?.pollCreationMessageV5 ||
|
|
944
|
-
message?.statusAddYours ||
|
|
945
1476
|
message?.groupStatusMessage ||
|
|
1477
|
+
message?.groupStatusMessageV2 ||
|
|
946
1478
|
message?.limitSharingMessage ||
|
|
947
|
-
message?.
|
|
1479
|
+
message?.lottieStickerMessage ||
|
|
1480
|
+
message?.newsletterAdminProfileMessage ||
|
|
1481
|
+
message?.newsletterAdminProfileMessageV2 ||
|
|
1482
|
+
message?.newsletterAdminProfileStatusMessage ||
|
|
1483
|
+
message?.pollCreationMessageV4 ||
|
|
1484
|
+
message?.pollCreationOptionImageMessage ||
|
|
948
1485
|
message?.questionMessage ||
|
|
949
|
-
message?.
|
|
950
|
-
message?.
|
|
1486
|
+
message?.questionReplyMessage ||
|
|
1487
|
+
message?.spoilerMessage ||
|
|
1488
|
+
message?.statusAddYours ||
|
|
1489
|
+
message?.statusMentionMessage ||
|
|
1490
|
+
message?.viewOnceMessage ||
|
|
1491
|
+
message?.viewOnceMessageV2 ||
|
|
1492
|
+
message?.viewOnceMessageV2Extension);
|
|
951
1493
|
}
|
|
952
1494
|
};
|
|
953
1495
|
/**
|
|
@@ -1180,4 +1722,74 @@ export const assertMediaContent = (content) => {
|
|
|
1180
1722
|
}
|
|
1181
1723
|
return mediaContent;
|
|
1182
1724
|
};
|
|
1183
|
-
|
|
1725
|
+
/**
|
|
1726
|
+
* Checks if a WebP buffer is animated by looking for VP8X chunk with animation flag
|
|
1727
|
+
* or ANIM/ANMF chunks
|
|
1728
|
+
*/
|
|
1729
|
+
const isAnimatedWebP = (buffer) => {
|
|
1730
|
+
// WebP must start with RIFF....WEBP
|
|
1731
|
+
if (buffer.length < 12 ||
|
|
1732
|
+
buffer[0] !== 0x52 ||
|
|
1733
|
+
buffer[1] !== 0x49 ||
|
|
1734
|
+
buffer[2] !== 0x46 ||
|
|
1735
|
+
buffer[3] !== 0x46 ||
|
|
1736
|
+
buffer[8] !== 0x57 ||
|
|
1737
|
+
buffer[9] !== 0x45 ||
|
|
1738
|
+
buffer[10] !== 0x42 ||
|
|
1739
|
+
buffer[11] !== 0x50) {
|
|
1740
|
+
return false;
|
|
1741
|
+
}
|
|
1742
|
+
;
|
|
1743
|
+
// Parse chunks starting after RIFF header (12 bytes)
|
|
1744
|
+
let offset = 12;
|
|
1745
|
+
while (offset < buffer.length - 8) {
|
|
1746
|
+
const chunkFourCC = buffer.toString('ascii', offset, offset + 4);
|
|
1747
|
+
const chunkSize = buffer.readUInt32LE(offset + 4);
|
|
1748
|
+
if (chunkFourCC === 'VP8X') {
|
|
1749
|
+
// VP8X extended header, check animation flag (bit 1 at offset+8)
|
|
1750
|
+
const flagsOffset = offset + 8;
|
|
1751
|
+
if (flagsOffset < buffer.length) {
|
|
1752
|
+
const flags = buffer[flagsOffset];
|
|
1753
|
+
if (flags & 0x02) {
|
|
1754
|
+
return true;
|
|
1755
|
+
}
|
|
1756
|
+
;
|
|
1757
|
+
}
|
|
1758
|
+
;
|
|
1759
|
+
}
|
|
1760
|
+
else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
1761
|
+
// ANIM or ANMF chunks indicate animation
|
|
1762
|
+
return true;
|
|
1763
|
+
}
|
|
1764
|
+
;
|
|
1765
|
+
// Move to next chunk (chunk size + 8 bytes header, padded to even)
|
|
1766
|
+
offset += 8 + chunkSize + (chunkSize % 2);
|
|
1767
|
+
}
|
|
1768
|
+
;
|
|
1769
|
+
return false;
|
|
1770
|
+
};
|
|
1771
|
+
/**
|
|
1772
|
+
* Checks if a buffer is a WebP file
|
|
1773
|
+
*/
|
|
1774
|
+
const isWebPBuffer = (buffer) => {
|
|
1775
|
+
return (buffer.length >= 12 &&
|
|
1776
|
+
buffer[0] === 0x52 &&
|
|
1777
|
+
buffer[1] === 0x49 &&
|
|
1778
|
+
buffer[2] === 0x46 &&
|
|
1779
|
+
buffer[3] === 0x46 &&
|
|
1780
|
+
buffer[8] === 0x57 &&
|
|
1781
|
+
buffer[9] === 0x45 &&
|
|
1782
|
+
buffer[10] === 0x42 &&
|
|
1783
|
+
buffer[11] === 0x50);
|
|
1784
|
+
};
|
|
1785
|
+
export const shouldIncludeBizBinaryNode = (message) => {
|
|
1786
|
+
const hasValidInteractive = message.interactiveMessage &&
|
|
1787
|
+
!message.interactiveMessage.carouselMessage &&
|
|
1788
|
+
!message.interactiveMessage.collectionMessage &&
|
|
1789
|
+
!message.interactiveMessage.shopStorefrontMessage;
|
|
1790
|
+
return (message.buttonsMessage ||
|
|
1791
|
+
message.interactiveMessage ||
|
|
1792
|
+
message.listMessage ||
|
|
1793
|
+
hasValidInteractive);
|
|
1794
|
+
};
|
|
1795
|
+
//# sourceMappingURL=messages.js.map
|