@neykoor/baileys 7.0.5-rc15 → 7.0.8-rc15
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.js +6 -6
- package/lib/Defaults/index.js.map +1 -1
- package/lib/Signal/lid-mapping.d.ts.map +1 -1
- package/lib/Signal/lid-mapping.js.map +1 -1
- package/lib/Socket/business.d.ts +33 -0
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +9 -2
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +33 -0
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/groups.d.ts.map +1 -1
- package/lib/Socket/groups.js +7 -2
- package/lib/Socket/groups.js.map +1 -1
- package/lib/Socket/index.d.ts +33 -0
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/messages-recv.d.ts +33 -0
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-send.d.ts +34 -1
- package/lib/Socket/messages-send.d.ts.map +1 -1
- package/lib/Socket/messages-send.js +139 -65
- package/lib/Socket/messages-send.js.map +1 -1
- package/lib/Socket/newsletter.d.ts.map +1 -1
- package/lib/Socket/smgss.d.ts +203 -0
- package/lib/Socket/smgss.d.ts.map +1 -0
- package/lib/Socket/smgss.js +461 -0
- package/lib/Socket/smgss.js.map +1 -0
- package/lib/Store/make-in-memory-store.d.ts.map +1 -1
- package/lib/Store/make-in-memory-store.js +12 -10
- package/lib/Store/make-in-memory-store.js.map +1 -1
- package/lib/Types/Message.d.ts +94 -2
- package/lib/Types/Message.d.ts.map +1 -1
- package/lib/Types/Message.js +4 -0
- package/lib/Types/Message.js.map +1 -1
- package/lib/Utils/connection-watchdog.d.ts +30 -0
- package/lib/Utils/connection-watchdog.d.ts.map +1 -0
- package/lib/Utils/connection-watchdog.js +66 -0
- package/lib/Utils/connection-watchdog.js.map +1 -0
- package/lib/Utils/generics.js +1 -1
- package/lib/Utils/group-status-detection.d.ts +7 -0
- package/lib/Utils/group-status-detection.d.ts.map +1 -0
- package/lib/Utils/group-status-detection.js +72 -0
- package/lib/Utils/group-status-detection.js.map +1 -0
- package/lib/Utils/index.d.ts +3 -0
- package/lib/Utils/index.d.ts.map +1 -1
- package/lib/Utils/index.js +3 -0
- package/lib/Utils/index.js.map +1 -1
- package/lib/Utils/message-retry-manager.d.ts.map +1 -1
- package/lib/Utils/message-retry-manager.js.map +1 -1
- package/lib/Utils/messages-media.d.ts +1 -0
- package/lib/Utils/messages-media.d.ts.map +1 -1
- package/lib/Utils/messages-media.js +92 -0
- package/lib/Utils/messages-media.js.map +1 -1
- package/lib/Utils/messages-newsletter.d.ts +34 -0
- package/lib/Utils/messages-newsletter.d.ts.map +1 -1
- package/lib/Utils/messages-newsletter.js +67 -0
- package/lib/Utils/messages-newsletter.js.map +1 -1
- package/lib/Utils/messages.d.ts +3 -46
- package/lib/Utils/messages.d.ts.map +1 -1
- package/lib/Utils/messages.js +306 -84
- package/lib/Utils/messages.js.map +1 -1
- package/lib/Utils/scheduling.d.ts +46 -0
- package/lib/Utils/scheduling.d.ts.map +1 -0
- package/lib/Utils/scheduling.js +117 -0
- package/lib/Utils/scheduling.js.map +1 -0
- package/lib/Utils/signal.d.ts +0 -4
- package/lib/Utils/signal.d.ts.map +1 -1
- package/lib/Utils/signal.js +7 -14
- package/lib/Utils/signal.js.map +1 -1
- package/lib/Utils/use-sqlite-auth-state.d.ts +1 -0
- package/lib/Utils/use-sqlite-auth-state.d.ts.map +1 -1
- package/lib/Utils/use-sqlite-auth-state.js +18 -3
- package/lib/Utils/use-sqlite-auth-state.js.map +1 -1
- package/lib/Utils/validate-connection.d.ts.map +1 -1
- package/lib/Utils/validate-connection.js +1 -4
- package/lib/Utils/validate-connection.js.map +1 -1
- package/package.json +1 -1
package/lib/Utils/messages.js
CHANGED
|
@@ -6,11 +6,11 @@ import { join } from 'path';
|
|
|
6
6
|
import {} from 'stream';
|
|
7
7
|
import { proto } from '../../WAProto/index.js';
|
|
8
8
|
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
9
|
-
import { WAMessageStatus, WAProto } from '../Types/index.js';
|
|
9
|
+
import { CarouselCardType, ListType, WAMessageStatus, WAProto } from '../Types/index.js';
|
|
10
10
|
import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
|
|
11
11
|
import { sha256 } from './crypto.js';
|
|
12
12
|
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
13
|
-
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getMediaKeys, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
13
|
+
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getMediaKeys, getRawMediaUploadData, getStream, toBuffer, transcodeAudioToOpus } from './messages-media.js';
|
|
14
14
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
15
15
|
const MIMETYPE_MAP = {
|
|
16
16
|
image: 'image/jpeg',
|
|
@@ -27,11 +27,6 @@ const MessageTypeProto = {
|
|
|
27
27
|
sticker: WAProto.Message.StickerMessage,
|
|
28
28
|
document: WAProto.Message.DocumentMessage
|
|
29
29
|
};
|
|
30
|
-
/**
|
|
31
|
-
* Uses a regex to test whether the string contains a URL, and returns the URL if it does.
|
|
32
|
-
* @param text eg. hello https://google.com
|
|
33
|
-
* @returns the URL, eg. https://google.com
|
|
34
|
-
*/
|
|
35
30
|
export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
|
|
36
31
|
export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
|
|
37
32
|
const url = extractUrlFromText(text);
|
|
@@ -41,7 +36,6 @@ export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) =>
|
|
|
41
36
|
return urlInfo;
|
|
42
37
|
}
|
|
43
38
|
catch (error) {
|
|
44
|
-
// ignore if fails
|
|
45
39
|
logger?.warn({ trace: error.stack }, 'url generation failed');
|
|
46
40
|
}
|
|
47
41
|
}
|
|
@@ -232,7 +226,6 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
232
226
|
media: message[mediaType]
|
|
233
227
|
};
|
|
234
228
|
delete uploadData[mediaType];
|
|
235
|
-
// check if cacheable + generate cache key
|
|
236
229
|
const cacheableKey = typeof uploadData.media === 'object' &&
|
|
237
230
|
'url' in uploadData.media &&
|
|
238
231
|
!!uploadData.media.url &&
|
|
@@ -244,6 +237,18 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
244
237
|
if (!uploadData.mimetype) {
|
|
245
238
|
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
246
239
|
}
|
|
240
|
+
let pttTranscodedPath;
|
|
241
|
+
if (mediaType === 'audio' && uploadData.ptt === true) {
|
|
242
|
+
const media = uploadData.media;
|
|
243
|
+
const mediaUrl = typeof media === 'string' ? media : media?.url?.toString?.() || '';
|
|
244
|
+
const sourceLooksLikeOpus = /\.(opus|ogg)(\?|$)/i.test(mediaUrl);
|
|
245
|
+
if (!sourceLooksLikeOpus) {
|
|
246
|
+
const sourceForTranscode = Buffer.isBuffer(media) ? media : typeof media === 'string' ? media : mediaUrl;
|
|
247
|
+
pttTranscodedPath = await transcodeAudioToOpus(sourceForTranscode);
|
|
248
|
+
uploadData.media = { url: pttTranscodedPath };
|
|
249
|
+
}
|
|
250
|
+
uploadData.mimetype = 'audio/ogg; codecs=opus';
|
|
251
|
+
}
|
|
247
252
|
if (cacheableKey) {
|
|
248
253
|
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
249
254
|
if (mediaBuff) {
|
|
@@ -266,7 +271,6 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
266
271
|
});
|
|
267
272
|
await fs.unlink(filePath);
|
|
268
273
|
const obj = WAProto.Message.fromObject({
|
|
269
|
-
// todo: add more support here
|
|
270
274
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
271
275
|
url: mediaUrl,
|
|
272
276
|
directPath,
|
|
@@ -345,6 +349,9 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
345
349
|
if (originalFilePath) {
|
|
346
350
|
await fs.unlink(originalFilePath);
|
|
347
351
|
}
|
|
352
|
+
if (pttTranscodedPath) {
|
|
353
|
+
await fs.unlink(pttTranscodedPath);
|
|
354
|
+
}
|
|
348
355
|
logger?.debug('removed tmp files');
|
|
349
356
|
}
|
|
350
357
|
catch (error) {
|
|
@@ -388,17 +395,11 @@ export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) =>
|
|
|
388
395
|
};
|
|
389
396
|
return WAProto.Message.fromObject(content);
|
|
390
397
|
};
|
|
391
|
-
/**
|
|
392
|
-
* Generate forwarded message content like WA does
|
|
393
|
-
* @param message the message to forward
|
|
394
|
-
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
395
|
-
*/
|
|
396
398
|
export const generateForwardMessageContent = (message, forceForward) => {
|
|
397
399
|
let content = message.message;
|
|
398
400
|
if (!content) {
|
|
399
401
|
throw new Boom('no content in message', { statusCode: 400 });
|
|
400
402
|
}
|
|
401
|
-
// hacky copy
|
|
402
403
|
content = normalizeMessageContent(content);
|
|
403
404
|
content = proto.Message.decode(proto.Message.encode(content).finish());
|
|
404
405
|
let key = Object.keys(content)[0];
|
|
@@ -428,10 +429,269 @@ export const hasNonNullishProperty = (message, key) => {
|
|
|
428
429
|
function hasOptionalProperty(obj, key) {
|
|
429
430
|
return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
|
|
430
431
|
}
|
|
432
|
+
const prepareNativeFlowButtons = (message) => {
|
|
433
|
+
const buttons = message.nativeFlow;
|
|
434
|
+
const correctedField = Array.isArray(buttons) ? buttons : buttons.buttons;
|
|
435
|
+
return {
|
|
436
|
+
buttons: correctedField.map((button) => {
|
|
437
|
+
const buttonText = button.text || button.buttonText;
|
|
438
|
+
const icon = button.icon?.toUpperCase();
|
|
439
|
+
if (button.id) {
|
|
440
|
+
return {
|
|
441
|
+
name: 'quick_reply',
|
|
442
|
+
buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Click', id: button.id, icon })
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
else if (button.copy) {
|
|
446
|
+
return {
|
|
447
|
+
name: 'cta_copy',
|
|
448
|
+
buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Copy', copy_code: button.copy, icon })
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
else if (button.url) {
|
|
452
|
+
return {
|
|
453
|
+
name: 'cta_url',
|
|
454
|
+
buttonParamsJson: JSON.stringify({
|
|
455
|
+
display_text: buttonText || 'Visit',
|
|
456
|
+
url: button.url,
|
|
457
|
+
merchant_url: button.url,
|
|
458
|
+
webview_interaction: button.useWebview,
|
|
459
|
+
icon
|
|
460
|
+
})
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
else if (button.call) {
|
|
464
|
+
return {
|
|
465
|
+
name: 'cta_call',
|
|
466
|
+
buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Call', phone_number: button.call, icon })
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
else if (button.sections) {
|
|
470
|
+
return {
|
|
471
|
+
name: 'single_select',
|
|
472
|
+
buttonParamsJson: JSON.stringify({ title: buttonText || 'Select', sections: button.sections, icon })
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
return { name: button.name, buttonParamsJson: button.paramsJson };
|
|
476
|
+
})
|
|
477
|
+
};
|
|
478
|
+
};
|
|
479
|
+
export const hasValidInteractiveHeader = (message) => {
|
|
480
|
+
return !!(message.imageMessage ||
|
|
481
|
+
message.videoMessage ||
|
|
482
|
+
message.documentMessage ||
|
|
483
|
+
message.productMessage ||
|
|
484
|
+
message.locationMessage);
|
|
485
|
+
};
|
|
486
|
+
export const hasValidCarouselHeader = (message) => {
|
|
487
|
+
return !!(message.imageMessage || message.videoMessage || message.productMessage);
|
|
488
|
+
};
|
|
431
489
|
export const generateWAMessageContent = async (message, options) => {
|
|
432
490
|
var _a, _b;
|
|
433
491
|
let m = {};
|
|
434
|
-
if (hasNonNullishProperty(message, '
|
|
492
|
+
if (hasNonNullishProperty(message, 'raw')) {
|
|
493
|
+
delete message.raw;
|
|
494
|
+
return message;
|
|
495
|
+
}
|
|
496
|
+
else if (hasNonNullishProperty(message, 'buttons')) {
|
|
497
|
+
const nativeButtons = message.buttons.map((button) => {
|
|
498
|
+
if (button.nativeFlowInfo) {
|
|
499
|
+
return { name: button.nativeFlowInfo.name, buttonParamsJson: button.nativeFlowInfo.paramsJson };
|
|
500
|
+
}
|
|
501
|
+
if (button.name && (button.buttonParamsJson || button.paramsJson)) {
|
|
502
|
+
return { name: button.name, buttonParamsJson: button.buttonParamsJson || button.paramsJson };
|
|
503
|
+
}
|
|
504
|
+
const buttonText = button.text || (typeof button.buttonText === 'string' ? button.buttonText : button.buttonText?.displayText);
|
|
505
|
+
const id = button.id || button.buttonId || buttonText;
|
|
506
|
+
const icon = button.icon?.toUpperCase();
|
|
507
|
+
if (button.sections) {
|
|
508
|
+
return {
|
|
509
|
+
name: 'single_select',
|
|
510
|
+
buttonParamsJson: JSON.stringify({ title: buttonText || 'Select', sections: button.sections, icon })
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
else if (button.url) {
|
|
514
|
+
return {
|
|
515
|
+
name: 'cta_url',
|
|
516
|
+
buttonParamsJson: JSON.stringify({
|
|
517
|
+
display_text: buttonText || 'Visit',
|
|
518
|
+
url: button.url,
|
|
519
|
+
merchant_url: button.url,
|
|
520
|
+
webview_interaction: button.useWebview,
|
|
521
|
+
icon
|
|
522
|
+
})
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
else if (button.call) {
|
|
526
|
+
return {
|
|
527
|
+
name: 'cta_call',
|
|
528
|
+
buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Call', phone_number: button.call, icon })
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
else if (button.copy) {
|
|
532
|
+
return {
|
|
533
|
+
name: 'cta_copy',
|
|
534
|
+
buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Copy', copy_code: button.copy, icon })
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
name: 'quick_reply',
|
|
539
|
+
buttonParamsJson: JSON.stringify({ display_text: buttonText || 'Click', id, icon })
|
|
540
|
+
};
|
|
541
|
+
});
|
|
542
|
+
const interactiveMessage = {
|
|
543
|
+
nativeFlowMessage: { buttons: nativeButtons, messageParamsJson: '{}' }
|
|
544
|
+
};
|
|
545
|
+
if (message.text) {
|
|
546
|
+
interactiveMessage.body = { text: message.text };
|
|
547
|
+
interactiveMessage.header = { title: '', subtitle: '', hasMediaAttachment: false };
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
const media = await prepareWAMessageMedia(message, options);
|
|
551
|
+
const mediaKey = Object.keys(media)[0];
|
|
552
|
+
if (!mediaKey) {
|
|
553
|
+
throw new Boom('buttons message needs either "text" or a media field (image/video/document) as header', {
|
|
554
|
+
statusCode: 400
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
interactiveMessage.body = { text: message.caption || '' };
|
|
558
|
+
interactiveMessage.header = {
|
|
559
|
+
title: '',
|
|
560
|
+
subtitle: '',
|
|
561
|
+
hasMediaAttachment: true,
|
|
562
|
+
...media
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
if (message.footer) {
|
|
566
|
+
interactiveMessage.footer = { text: message.footer };
|
|
567
|
+
}
|
|
568
|
+
m.interactiveMessage = interactiveMessage;
|
|
569
|
+
}
|
|
570
|
+
else if (hasNonNullishProperty(message, 'sections')) {
|
|
571
|
+
m.listMessage = {
|
|
572
|
+
sections: message.sections,
|
|
573
|
+
buttonText: message.buttonText,
|
|
574
|
+
title: message.title,
|
|
575
|
+
footerText: message.footer,
|
|
576
|
+
description: message.description,
|
|
577
|
+
listType: ListType.SINGLE_SELECT
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
else if (hasNonNullishProperty(message, 'templateButtons')) {
|
|
581
|
+
const hydratedTemplate = {
|
|
582
|
+
hydratedButtons: message.templateButtons.map((button, i) => {
|
|
583
|
+
const buttonText = button.text || button.buttonText;
|
|
584
|
+
if (button.id) {
|
|
585
|
+
return { index: i, quickReplyButton: { displayText: buttonText || 'Click', id: button.id } };
|
|
586
|
+
}
|
|
587
|
+
else if (button.url) {
|
|
588
|
+
return { index: i, urlButton: { displayText: buttonText || 'Visit', url: button.url } };
|
|
589
|
+
}
|
|
590
|
+
else if (button.call) {
|
|
591
|
+
return { index: i, callButton: { displayText: buttonText || 'Call', phoneNumber: button.call } };
|
|
592
|
+
}
|
|
593
|
+
button.index = button.index || i;
|
|
594
|
+
return button;
|
|
595
|
+
})
|
|
596
|
+
};
|
|
597
|
+
if (message.text) {
|
|
598
|
+
hydratedTemplate.hydratedContentText = message.text;
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
const media = await prepareWAMessageMedia(message, options);
|
|
602
|
+
if (message.caption) {
|
|
603
|
+
hydratedTemplate.hydratedTitleText = message.title;
|
|
604
|
+
hydratedTemplate.hydratedContentText = message.caption;
|
|
605
|
+
}
|
|
606
|
+
Object.assign(hydratedTemplate, media);
|
|
607
|
+
}
|
|
608
|
+
if (message.footer) {
|
|
609
|
+
hydratedTemplate.hydratedFooterText = message.footer;
|
|
610
|
+
}
|
|
611
|
+
hydratedTemplate.templateId = message.id || `template-${Date.now()}`;
|
|
612
|
+
m.templateMessage = {
|
|
613
|
+
hydratedFourRowTemplate: hydratedTemplate,
|
|
614
|
+
hydratedTemplate
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
else if (hasNonNullishProperty(message, 'nativeFlow')) {
|
|
618
|
+
const interactiveMessage = {
|
|
619
|
+
nativeFlowMessage: prepareNativeFlowButtons(message)
|
|
620
|
+
};
|
|
621
|
+
if (message.text) {
|
|
622
|
+
interactiveMessage.body = { text: message.text };
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
const media = await prepareWAMessageMedia(message, options);
|
|
626
|
+
if (message.caption) {
|
|
627
|
+
const isValidHeader = hasValidInteractiveHeader(media);
|
|
628
|
+
if (!isValidHeader) {
|
|
629
|
+
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
630
|
+
}
|
|
631
|
+
interactiveMessage.header = {
|
|
632
|
+
title: message.title || '',
|
|
633
|
+
subtitle: message.subtitle || '',
|
|
634
|
+
hasMediaAttachment: isValidHeader
|
|
635
|
+
};
|
|
636
|
+
interactiveMessage.body = { text: message.caption };
|
|
637
|
+
}
|
|
638
|
+
Object.assign(interactiveMessage.header || (interactiveMessage.header = {}), media);
|
|
639
|
+
}
|
|
640
|
+
if (message.audioFooter) {
|
|
641
|
+
const { audioMessage } = await prepareWAMessageMedia({ audio: message.audioFooter }, options);
|
|
642
|
+
interactiveMessage.footer = { audioMessage, hasMediaAttachment: true };
|
|
643
|
+
}
|
|
644
|
+
else if (message.footer) {
|
|
645
|
+
interactiveMessage.footer = { text: message.footer };
|
|
646
|
+
}
|
|
647
|
+
m.interactiveMessage = interactiveMessage;
|
|
648
|
+
}
|
|
649
|
+
else if (hasNonNullishProperty(message, 'cards')) {
|
|
650
|
+
const interactiveMessage = {
|
|
651
|
+
carouselMessage: {
|
|
652
|
+
cards: await Promise.all(message.cards.map(async (card) => {
|
|
653
|
+
const carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
654
|
+
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
655
|
+
if (!isValidHeader) {
|
|
656
|
+
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
657
|
+
}
|
|
658
|
+
const carouselCard = {
|
|
659
|
+
nativeFlowMessage: prepareNativeFlowButtons({ nativeFlow: card.nativeFlow || [] })
|
|
660
|
+
};
|
|
661
|
+
if (card.text) {
|
|
662
|
+
carouselCard.body = { text: card.text };
|
|
663
|
+
}
|
|
664
|
+
else if (card.caption) {
|
|
665
|
+
carouselCard.header = {
|
|
666
|
+
title: card.title || '',
|
|
667
|
+
subtitle: card.subtitle || '',
|
|
668
|
+
hasMediaAttachment: isValidHeader,
|
|
669
|
+
...carouselHeader
|
|
670
|
+
};
|
|
671
|
+
carouselCard.body = { text: card.caption };
|
|
672
|
+
}
|
|
673
|
+
if (card.audioFooter) {
|
|
674
|
+
const { audioMessage } = await prepareWAMessageMedia({ audio: card.audioFooter }, options);
|
|
675
|
+
carouselCard.footer = { audioMessage, hasMediaAttachment: true };
|
|
676
|
+
}
|
|
677
|
+
else if (card.footer) {
|
|
678
|
+
carouselCard.footer = { text: card.footer };
|
|
679
|
+
}
|
|
680
|
+
if (card.contextInfo) {
|
|
681
|
+
carouselCard.contextInfo = card.contextInfo;
|
|
682
|
+
}
|
|
683
|
+
return carouselCard;
|
|
684
|
+
})),
|
|
685
|
+
carouselCardType: CarouselCardType.UNKNOWN,
|
|
686
|
+
messageVersion: 1
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
if (message.footer) {
|
|
690
|
+
interactiveMessage.footer = { text: message.footer };
|
|
691
|
+
}
|
|
692
|
+
m.interactiveMessage = interactiveMessage;
|
|
693
|
+
}
|
|
694
|
+
else if (hasNonNullishProperty(message, 'text')) {
|
|
435
695
|
const extContent = { text: message.text };
|
|
436
696
|
let urlInfo = message.linkPreview;
|
|
437
697
|
if (typeof urlInfo === 'undefined') {
|
|
@@ -507,8 +767,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
507
767
|
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
508
768
|
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
509
769
|
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
510
|
-
//TODO: use built-in interface and get disappearing mode info etc.
|
|
511
|
-
//TODO: cache / use store!?
|
|
512
770
|
if (options.getProfilePicUrl) {
|
|
513
771
|
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
514
772
|
if (pfpUrl) {
|
|
@@ -553,7 +811,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
553
811
|
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
554
812
|
}
|
|
555
813
|
m.messageContextInfo = {
|
|
556
|
-
// encKey
|
|
557
814
|
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
558
815
|
};
|
|
559
816
|
m.eventMessage.name = message.event.name;
|
|
@@ -565,6 +822,24 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
565
822
|
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
566
823
|
m.eventMessage.location = message.event.location;
|
|
567
824
|
}
|
|
825
|
+
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
826
|
+
switch (message.type) {
|
|
827
|
+
case 'template':
|
|
828
|
+
m.templateButtonReplyMessage = {
|
|
829
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
830
|
+
selectedId: message.buttonReply.id,
|
|
831
|
+
selectedIndex: message.buttonReply.index
|
|
832
|
+
};
|
|
833
|
+
break;
|
|
834
|
+
case 'plain':
|
|
835
|
+
m.buttonsResponseMessage = {
|
|
836
|
+
selectedButtonId: message.buttonReply.id,
|
|
837
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
838
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
839
|
+
};
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
568
843
|
else if (hasNonNullishProperty(message, 'poll')) {
|
|
569
844
|
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
570
845
|
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
@@ -577,7 +852,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
577
852
|
});
|
|
578
853
|
}
|
|
579
854
|
m.messageContextInfo = {
|
|
580
|
-
// encKey
|
|
581
855
|
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
582
856
|
};
|
|
583
857
|
const pollCreationMessage = {
|
|
@@ -586,16 +860,13 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
586
860
|
options: message.poll.values.map(optionName => ({ optionName }))
|
|
587
861
|
};
|
|
588
862
|
if (message.poll.toAnnouncementGroup) {
|
|
589
|
-
// poll v2 is for community announcement groups (single select and multiple)
|
|
590
863
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
591
864
|
}
|
|
592
865
|
else {
|
|
593
866
|
if (message.poll.selectableCount === 1) {
|
|
594
|
-
//poll v3 is for single select polls
|
|
595
867
|
m.pollCreationMessageV3 = pollCreationMessage;
|
|
596
868
|
}
|
|
597
869
|
else {
|
|
598
|
-
// poll for multiple choice polls
|
|
599
870
|
m.pollCreationMessage = pollCreationMessage;
|
|
600
871
|
}
|
|
601
872
|
}
|
|
@@ -705,8 +976,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
705
976
|
return WAProto.Message.create(m);
|
|
706
977
|
};
|
|
707
978
|
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
708
|
-
// set timestamp to now
|
|
709
|
-
// if not specified
|
|
710
979
|
if (!options.timestamp) {
|
|
711
980
|
options.timestamp = new Date();
|
|
712
981
|
}
|
|
@@ -716,11 +985,10 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
716
985
|
const { quoted, userJid } = options;
|
|
717
986
|
if (quoted && !isJidNewsletter(jid)) {
|
|
718
987
|
const participant = quoted.key.fromMe
|
|
719
|
-
? userJid
|
|
988
|
+
? userJid
|
|
720
989
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
721
990
|
let quotedMsg = normalizeMessageContent(quoted.message);
|
|
722
991
|
const msgType = getContentType(quotedMsg);
|
|
723
|
-
// strip any redundant properties
|
|
724
992
|
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
725
993
|
const quotedContent = quotedMsg[msgType];
|
|
726
994
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
@@ -730,30 +998,23 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
730
998
|
contextInfo.participant = jidNormalizedUser(participant);
|
|
731
999
|
contextInfo.stanzaId = quoted.key.id;
|
|
732
1000
|
contextInfo.quotedMessage = quotedMsg;
|
|
733
|
-
// if a participant is quoted, then it must be a group
|
|
734
|
-
// hence, remoteJid of group must also be entered
|
|
735
1001
|
if (jid !== quoted.key.remoteJid) {
|
|
736
1002
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
737
1003
|
}
|
|
738
1004
|
if (contextInfo && innerMessage[key]) {
|
|
739
|
-
|
|
1005
|
+
;
|
|
740
1006
|
innerMessage[key].contextInfo = contextInfo;
|
|
741
1007
|
}
|
|
742
1008
|
}
|
|
743
|
-
if (
|
|
744
|
-
// if we want to send a disappearing message
|
|
745
|
-
!!options?.ephemeralExpiration &&
|
|
746
|
-
// and it's not a protocol message -- delete, toggle disappear message
|
|
1009
|
+
if (!!options?.ephemeralExpiration &&
|
|
747
1010
|
key !== 'protocolMessage' &&
|
|
748
|
-
// already not converted to disappearing message
|
|
749
1011
|
key !== 'ephemeralMessage' &&
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
...(
|
|
755
|
-
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
756
|
-
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
1012
|
+
!isJidNewsletter(jid) &&
|
|
1013
|
+
innerMessage[key]) {
|
|
1014
|
+
const target = innerMessage[key];
|
|
1015
|
+
target.contextInfo = {
|
|
1016
|
+
...(target.contextInfo || {}),
|
|
1017
|
+
expiration: Number(options.ephemeralExpiration) || WA_DEFAULT_EPHEMERAL
|
|
757
1018
|
};
|
|
758
1019
|
}
|
|
759
1020
|
message = WAProto.Message.create(message);
|
|
@@ -766,18 +1027,15 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
766
1027
|
message: message,
|
|
767
1028
|
messageTimestamp: timestamp,
|
|
768
1029
|
messageStubParameters: [],
|
|
769
|
-
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
1030
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
770
1031
|
status: WAMessageStatus.PENDING
|
|
771
1032
|
};
|
|
772
1033
|
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
773
1034
|
};
|
|
774
1035
|
export const generateWAMessage = async (jid, content, options) => {
|
|
775
|
-
// ensure msg ID is with every log
|
|
776
1036
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
777
|
-
// Pass jid in the options to generateWAMessageContent
|
|
778
1037
|
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { ...options, jid }), options);
|
|
779
1038
|
};
|
|
780
|
-
/** Get the key to access the true type of content */
|
|
781
1039
|
export const getContentType = (content) => {
|
|
782
1040
|
if (content) {
|
|
783
1041
|
const keys = Object.keys(content);
|
|
@@ -785,17 +1043,10 @@ export const getContentType = (content) => {
|
|
|
785
1043
|
return key;
|
|
786
1044
|
}
|
|
787
1045
|
};
|
|
788
|
-
/**
|
|
789
|
-
* Normalizes ephemeral, view once messages to regular message content
|
|
790
|
-
* Eg. image messages in ephemeral messages, in view once messages etc.
|
|
791
|
-
* @param content
|
|
792
|
-
* @returns
|
|
793
|
-
*/
|
|
794
1046
|
export const normalizeMessageContent = (content) => {
|
|
795
1047
|
if (!content) {
|
|
796
1048
|
return undefined;
|
|
797
1049
|
}
|
|
798
|
-
// set max iterations to prevent an infinite loop
|
|
799
1050
|
for (let i = 0; i < 5; i++) {
|
|
800
1051
|
const inner = getFutureProofMessage(content);
|
|
801
1052
|
if (!inner) {
|
|
@@ -817,10 +1068,6 @@ export const normalizeMessageContent = (content) => {
|
|
|
817
1068
|
message?.lottieStickerMessage);
|
|
818
1069
|
}
|
|
819
1070
|
};
|
|
820
|
-
/**
|
|
821
|
-
* Extract the true message content from a message
|
|
822
|
-
* Eg. extracts the inner message from a disappearing message/view once message
|
|
823
|
-
*/
|
|
824
1071
|
export const extractMessageContent = (content) => {
|
|
825
1072
|
const extractFromTemplateMessage = (msg) => {
|
|
826
1073
|
if (msg.imageMessage) {
|
|
@@ -856,9 +1103,6 @@ export const extractMessageContent = (content) => {
|
|
|
856
1103
|
}
|
|
857
1104
|
return content;
|
|
858
1105
|
};
|
|
859
|
-
/**
|
|
860
|
-
* Returns the device predicted by message ID
|
|
861
|
-
*/
|
|
862
1106
|
export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
863
1107
|
? 'ios'
|
|
864
1108
|
: /^3E.{20}$/.test(id)
|
|
@@ -868,7 +1112,6 @@ export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
|
868
1112
|
: /^(3F|.{18}$)/.test(id)
|
|
869
1113
|
? 'desktop'
|
|
870
1114
|
: 'unknown';
|
|
871
|
-
/** Upserts a receipt in the message */
|
|
872
1115
|
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
873
1116
|
msg.userReceipt = msg.userReceipt || [];
|
|
874
1117
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
@@ -879,7 +1122,6 @@ export const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
879
1122
|
msg.userReceipt.push(receipt);
|
|
880
1123
|
}
|
|
881
1124
|
};
|
|
882
|
-
/** Update the message with a new reaction */
|
|
883
1125
|
export const updateMessageWithReaction = (msg, reaction) => {
|
|
884
1126
|
const authorID = getKeyAuthor(reaction.key);
|
|
885
1127
|
const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
|
|
@@ -887,7 +1129,6 @@ export const updateMessageWithReaction = (msg, reaction) => {
|
|
|
887
1129
|
reactions.push(reaction);
|
|
888
1130
|
msg.reactions = reactions;
|
|
889
1131
|
};
|
|
890
|
-
/** Update the message with a new poll update */
|
|
891
1132
|
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
892
1133
|
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
893
1134
|
const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
|
|
@@ -896,19 +1137,12 @@ export const updateMessageWithPollUpdate = (msg, update) => {
|
|
|
896
1137
|
}
|
|
897
1138
|
msg.pollUpdates = reactions;
|
|
898
1139
|
};
|
|
899
|
-
/** Update the message with a new event response */
|
|
900
1140
|
export const updateMessageWithEventResponse = (msg, update) => {
|
|
901
1141
|
const authorID = getKeyAuthor(update.eventResponseMessageKey);
|
|
902
1142
|
const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
|
|
903
1143
|
responses.push(update);
|
|
904
1144
|
msg.eventResponses = responses;
|
|
905
1145
|
};
|
|
906
|
-
/**
|
|
907
|
-
* Aggregates all poll updates in a poll.
|
|
908
|
-
* @param msg the poll creation message
|
|
909
|
-
* @param meId your jid
|
|
910
|
-
* @returns A list of options & their voters
|
|
911
|
-
*/
|
|
912
1146
|
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
913
1147
|
const opts = message?.pollCreationMessage?.options ||
|
|
914
1148
|
message?.pollCreationMessageV2?.options ||
|
|
@@ -942,12 +1176,6 @@ export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
942
1176
|
}
|
|
943
1177
|
return Object.values(voteHashMap);
|
|
944
1178
|
}
|
|
945
|
-
/**
|
|
946
|
-
* Aggregates all event responses in an event message.
|
|
947
|
-
* @param msg the event creation message
|
|
948
|
-
* @param meId your jid
|
|
949
|
-
* @returns A list of response types & their responders
|
|
950
|
-
*/
|
|
951
1179
|
export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
952
1180
|
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
953
1181
|
const responseMap = {};
|
|
@@ -965,7 +1193,6 @@ export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
|
965
1193
|
}
|
|
966
1194
|
return Object.values(responseMap);
|
|
967
1195
|
}
|
|
968
|
-
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
969
1196
|
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
970
1197
|
const keyMap = {};
|
|
971
1198
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
@@ -984,16 +1211,12 @@ export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
984
1211
|
return Object.values(keyMap);
|
|
985
1212
|
};
|
|
986
1213
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
987
|
-
/**
|
|
988
|
-
* Downloads the given message. Throws an error if it's not a media message
|
|
989
|
-
*/
|
|
990
1214
|
export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
991
1215
|
const result = await downloadMsg().catch(async (error) => {
|
|
992
1216
|
if (ctx &&
|
|
993
|
-
typeof error?.status === 'number' &&
|
|
1217
|
+
typeof error?.status === 'number' &&
|
|
994
1218
|
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
995
1219
|
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
996
|
-
// request reupload
|
|
997
1220
|
message = await ctx.reuploadRequest(message);
|
|
998
1221
|
const result = await downloadMsg();
|
|
999
1222
|
return result;
|
|
@@ -1034,7 +1257,6 @@ export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
1034
1257
|
return stream;
|
|
1035
1258
|
}
|
|
1036
1259
|
};
|
|
1037
|
-
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
1038
1260
|
export const assertMediaContent = (content) => {
|
|
1039
1261
|
content = extractMessageContent(content);
|
|
1040
1262
|
const mediaContent = content?.documentMessage ||
|