@neykoor/baileys 7.0.4-rc15 → 7.0.7-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.d.ts +0 -4
- package/lib/Defaults/index.d.ts.map +1 -1
- package/lib/Defaults/index.js +7 -11
- 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 +35 -0
- package/lib/Socket/business.d.ts.map +1 -1
- package/lib/Socket/chats.d.ts +3 -1
- package/lib/Socket/chats.d.ts.map +1 -1
- package/lib/Socket/chats.js +32 -3
- package/lib/Socket/chats.js.map +1 -1
- package/lib/Socket/communities.d.ts +35 -0
- package/lib/Socket/communities.d.ts.map +1 -1
- package/lib/Socket/groups.d.ts +2 -0
- 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 +35 -0
- package/lib/Socket/index.d.ts.map +1 -1
- package/lib/Socket/messages-recv.d.ts +35 -0
- package/lib/Socket/messages-recv.d.ts.map +1 -1
- package/lib/Socket/messages-send.d.ts +36 -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 +2 -0
- 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.d.ts +0 -8
- package/lib/Utils/generics.d.ts.map +1 -1
- package/lib/Utils/generics.js +2 -15
- package/lib/Utils/generics.js.map +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-composer.d.ts +2 -2
- package/lib/Utils/message-composer.d.ts.map +1 -1
- package/lib/Utils/message-composer.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 +290 -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 { ButtonHeaderType, ButtonType, 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,253 @@ 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 buttonsMessage = {
|
|
498
|
+
buttons: message.buttons.map((button) => {
|
|
499
|
+
const buttonText = button.text || button.buttonText;
|
|
500
|
+
if (button.nativeFlowInfo) {
|
|
501
|
+
return {
|
|
502
|
+
buttonId: button.id || button.buttonId,
|
|
503
|
+
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
504
|
+
nativeFlowInfo: button.nativeFlowInfo,
|
|
505
|
+
type: ButtonType.NATIVE_FLOW
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
else if (button.sections) {
|
|
509
|
+
return {
|
|
510
|
+
nativeFlowInfo: {
|
|
511
|
+
name: 'single_select',
|
|
512
|
+
paramsJson: JSON.stringify({ title: buttonText, sections: button.sections })
|
|
513
|
+
},
|
|
514
|
+
type: ButtonType.NATIVE_FLOW
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
else if (button.name) {
|
|
518
|
+
return {
|
|
519
|
+
nativeFlowInfo: { name: button.name, paramsJson: button.paramsJson },
|
|
520
|
+
type: ButtonType.NATIVE_FLOW
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
buttonId: button.id || button.buttonId,
|
|
525
|
+
buttonText: typeof buttonText === 'string' ? { displayText: buttonText } : buttonText,
|
|
526
|
+
type: button.type || ButtonType.RESPONSE
|
|
527
|
+
};
|
|
528
|
+
})
|
|
529
|
+
};
|
|
530
|
+
if (message.text) {
|
|
531
|
+
buttonsMessage.contentText = message.text;
|
|
532
|
+
buttonsMessage.headerType = ButtonHeaderType.EMPTY;
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
const media = await prepareWAMessageMedia(message, options);
|
|
536
|
+
if (message.caption) {
|
|
537
|
+
buttonsMessage.contentText = message.caption;
|
|
538
|
+
}
|
|
539
|
+
const mediaKey = Object.keys(media)[0];
|
|
540
|
+
if (!mediaKey) {
|
|
541
|
+
throw new Boom('buttons message needs either "text" or a media field (image/video/document) as header', {
|
|
542
|
+
statusCode: 400
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
const type = mediaKey.replace('Message', '').toUpperCase();
|
|
546
|
+
buttonsMessage.headerType = ButtonHeaderType[type];
|
|
547
|
+
Object.assign(buttonsMessage, media);
|
|
548
|
+
}
|
|
549
|
+
if (message.footer) {
|
|
550
|
+
buttonsMessage.footerText = message.footer;
|
|
551
|
+
}
|
|
552
|
+
m.buttonsMessage = buttonsMessage;
|
|
553
|
+
}
|
|
554
|
+
else if (hasNonNullishProperty(message, 'sections')) {
|
|
555
|
+
m.listMessage = {
|
|
556
|
+
sections: message.sections,
|
|
557
|
+
buttonText: message.buttonText,
|
|
558
|
+
title: message.title,
|
|
559
|
+
footerText: message.footer,
|
|
560
|
+
description: message.description,
|
|
561
|
+
listType: ListType.SINGLE_SELECT
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
else if (hasNonNullishProperty(message, 'templateButtons')) {
|
|
565
|
+
const hydratedTemplate = {
|
|
566
|
+
hydratedButtons: message.templateButtons.map((button, i) => {
|
|
567
|
+
const buttonText = button.text || button.buttonText;
|
|
568
|
+
if (button.id) {
|
|
569
|
+
return { index: i, quickReplyButton: { displayText: buttonText || 'Click', id: button.id } };
|
|
570
|
+
}
|
|
571
|
+
else if (button.url) {
|
|
572
|
+
return { index: i, urlButton: { displayText: buttonText || 'Visit', url: button.url } };
|
|
573
|
+
}
|
|
574
|
+
else if (button.call) {
|
|
575
|
+
return { index: i, callButton: { displayText: buttonText || 'Call', phoneNumber: button.call } };
|
|
576
|
+
}
|
|
577
|
+
button.index = button.index || i;
|
|
578
|
+
return button;
|
|
579
|
+
})
|
|
580
|
+
};
|
|
581
|
+
if (message.text) {
|
|
582
|
+
hydratedTemplate.hydratedContentText = message.text;
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
const media = await prepareWAMessageMedia(message, options);
|
|
586
|
+
if (message.caption) {
|
|
587
|
+
hydratedTemplate.hydratedTitleText = message.title;
|
|
588
|
+
hydratedTemplate.hydratedContentText = message.caption;
|
|
589
|
+
}
|
|
590
|
+
Object.assign(hydratedTemplate, media);
|
|
591
|
+
}
|
|
592
|
+
if (message.footer) {
|
|
593
|
+
hydratedTemplate.hydratedFooterText = message.footer;
|
|
594
|
+
}
|
|
595
|
+
hydratedTemplate.templateId = message.id || `template-${Date.now()}`;
|
|
596
|
+
m.templateMessage = {
|
|
597
|
+
hydratedFourRowTemplate: hydratedTemplate,
|
|
598
|
+
hydratedTemplate
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
else if (hasNonNullishProperty(message, 'nativeFlow')) {
|
|
602
|
+
const interactiveMessage = {
|
|
603
|
+
nativeFlowMessage: prepareNativeFlowButtons(message)
|
|
604
|
+
};
|
|
605
|
+
if (message.text) {
|
|
606
|
+
interactiveMessage.body = { text: message.text };
|
|
607
|
+
}
|
|
608
|
+
else {
|
|
609
|
+
const media = await prepareWAMessageMedia(message, options);
|
|
610
|
+
if (message.caption) {
|
|
611
|
+
const isValidHeader = hasValidInteractiveHeader(media);
|
|
612
|
+
if (!isValidHeader) {
|
|
613
|
+
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
614
|
+
}
|
|
615
|
+
interactiveMessage.header = {
|
|
616
|
+
title: message.title || '',
|
|
617
|
+
subtitle: message.subtitle || '',
|
|
618
|
+
hasMediaAttachment: isValidHeader
|
|
619
|
+
};
|
|
620
|
+
interactiveMessage.body = { text: message.caption };
|
|
621
|
+
}
|
|
622
|
+
Object.assign(interactiveMessage.header || (interactiveMessage.header = {}), media);
|
|
623
|
+
}
|
|
624
|
+
if (message.audioFooter) {
|
|
625
|
+
const { audioMessage } = await prepareWAMessageMedia({ audio: message.audioFooter }, options);
|
|
626
|
+
interactiveMessage.footer = { audioMessage, hasMediaAttachment: true };
|
|
627
|
+
}
|
|
628
|
+
else if (message.footer) {
|
|
629
|
+
interactiveMessage.footer = { text: message.footer };
|
|
630
|
+
}
|
|
631
|
+
m.interactiveMessage = interactiveMessage;
|
|
632
|
+
}
|
|
633
|
+
else if (hasNonNullishProperty(message, 'cards')) {
|
|
634
|
+
const interactiveMessage = {
|
|
635
|
+
carouselMessage: {
|
|
636
|
+
cards: await Promise.all(message.cards.map(async (card) => {
|
|
637
|
+
const carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
638
|
+
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
639
|
+
if (!isValidHeader) {
|
|
640
|
+
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
641
|
+
}
|
|
642
|
+
const carouselCard = {
|
|
643
|
+
nativeFlowMessage: prepareNativeFlowButtons({ nativeFlow: card.nativeFlow || [] })
|
|
644
|
+
};
|
|
645
|
+
if (card.text) {
|
|
646
|
+
carouselCard.body = { text: card.text };
|
|
647
|
+
}
|
|
648
|
+
else if (card.caption) {
|
|
649
|
+
carouselCard.header = {
|
|
650
|
+
title: card.title || '',
|
|
651
|
+
subtitle: card.subtitle || '',
|
|
652
|
+
hasMediaAttachment: isValidHeader,
|
|
653
|
+
...carouselHeader
|
|
654
|
+
};
|
|
655
|
+
carouselCard.body = { text: card.caption };
|
|
656
|
+
}
|
|
657
|
+
if (card.audioFooter) {
|
|
658
|
+
const { audioMessage } = await prepareWAMessageMedia({ audio: card.audioFooter }, options);
|
|
659
|
+
carouselCard.footer = { audioMessage, hasMediaAttachment: true };
|
|
660
|
+
}
|
|
661
|
+
else if (card.footer) {
|
|
662
|
+
carouselCard.footer = { text: card.footer };
|
|
663
|
+
}
|
|
664
|
+
if (card.contextInfo) {
|
|
665
|
+
carouselCard.contextInfo = card.contextInfo;
|
|
666
|
+
}
|
|
667
|
+
return carouselCard;
|
|
668
|
+
})),
|
|
669
|
+
carouselCardType: CarouselCardType.UNKNOWN,
|
|
670
|
+
messageVersion: 1
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
if (message.footer) {
|
|
674
|
+
interactiveMessage.footer = { text: message.footer };
|
|
675
|
+
}
|
|
676
|
+
m.interactiveMessage = interactiveMessage;
|
|
677
|
+
}
|
|
678
|
+
else if (hasNonNullishProperty(message, 'text')) {
|
|
435
679
|
const extContent = { text: message.text };
|
|
436
680
|
let urlInfo = message.linkPreview;
|
|
437
681
|
if (typeof urlInfo === 'undefined') {
|
|
@@ -507,8 +751,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
507
751
|
m.groupInviteMessage.caption = message.groupInvite.text;
|
|
508
752
|
m.groupInviteMessage.groupJid = message.groupInvite.jid;
|
|
509
753
|
m.groupInviteMessage.groupName = message.groupInvite.subject;
|
|
510
|
-
//TODO: use built-in interface and get disappearing mode info etc.
|
|
511
|
-
//TODO: cache / use store!?
|
|
512
754
|
if (options.getProfilePicUrl) {
|
|
513
755
|
const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
|
|
514
756
|
if (pfpUrl) {
|
|
@@ -553,7 +795,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
553
795
|
m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
|
|
554
796
|
}
|
|
555
797
|
m.messageContextInfo = {
|
|
556
|
-
// encKey
|
|
557
798
|
messageSecret: message.event.messageSecret || randomBytes(32)
|
|
558
799
|
};
|
|
559
800
|
m.eventMessage.name = message.event.name;
|
|
@@ -565,6 +806,24 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
565
806
|
m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
|
|
566
807
|
m.eventMessage.location = message.event.location;
|
|
567
808
|
}
|
|
809
|
+
else if (hasNonNullishProperty(message, 'buttonReply')) {
|
|
810
|
+
switch (message.type) {
|
|
811
|
+
case 'template':
|
|
812
|
+
m.templateButtonReplyMessage = {
|
|
813
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
814
|
+
selectedId: message.buttonReply.id,
|
|
815
|
+
selectedIndex: message.buttonReply.index
|
|
816
|
+
};
|
|
817
|
+
break;
|
|
818
|
+
case 'plain':
|
|
819
|
+
m.buttonsResponseMessage = {
|
|
820
|
+
selectedButtonId: message.buttonReply.id,
|
|
821
|
+
selectedDisplayText: message.buttonReply.displayText,
|
|
822
|
+
type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
|
|
823
|
+
};
|
|
824
|
+
break;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
568
827
|
else if (hasNonNullishProperty(message, 'poll')) {
|
|
569
828
|
(_a = message.poll).selectableCount || (_a.selectableCount = 0);
|
|
570
829
|
(_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
|
|
@@ -577,7 +836,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
577
836
|
});
|
|
578
837
|
}
|
|
579
838
|
m.messageContextInfo = {
|
|
580
|
-
// encKey
|
|
581
839
|
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
582
840
|
};
|
|
583
841
|
const pollCreationMessage = {
|
|
@@ -586,16 +844,13 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
586
844
|
options: message.poll.values.map(optionName => ({ optionName }))
|
|
587
845
|
};
|
|
588
846
|
if (message.poll.toAnnouncementGroup) {
|
|
589
|
-
// poll v2 is for community announcement groups (single select and multiple)
|
|
590
847
|
m.pollCreationMessageV2 = pollCreationMessage;
|
|
591
848
|
}
|
|
592
849
|
else {
|
|
593
850
|
if (message.poll.selectableCount === 1) {
|
|
594
|
-
//poll v3 is for single select polls
|
|
595
851
|
m.pollCreationMessageV3 = pollCreationMessage;
|
|
596
852
|
}
|
|
597
853
|
else {
|
|
598
|
-
// poll for multiple choice polls
|
|
599
854
|
m.pollCreationMessage = pollCreationMessage;
|
|
600
855
|
}
|
|
601
856
|
}
|
|
@@ -705,8 +960,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
705
960
|
return WAProto.Message.create(m);
|
|
706
961
|
};
|
|
707
962
|
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
708
|
-
// set timestamp to now
|
|
709
|
-
// if not specified
|
|
710
963
|
if (!options.timestamp) {
|
|
711
964
|
options.timestamp = new Date();
|
|
712
965
|
}
|
|
@@ -716,11 +969,10 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
716
969
|
const { quoted, userJid } = options;
|
|
717
970
|
if (quoted && !isJidNewsletter(jid)) {
|
|
718
971
|
const participant = quoted.key.fromMe
|
|
719
|
-
? userJid
|
|
972
|
+
? userJid
|
|
720
973
|
: quoted.participant || quoted.key.participant || quoted.key.remoteJid;
|
|
721
974
|
let quotedMsg = normalizeMessageContent(quoted.message);
|
|
722
975
|
const msgType = getContentType(quotedMsg);
|
|
723
|
-
// strip any redundant properties
|
|
724
976
|
quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
|
|
725
977
|
const quotedContent = quotedMsg[msgType];
|
|
726
978
|
if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
|
|
@@ -730,30 +982,23 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
730
982
|
contextInfo.participant = jidNormalizedUser(participant);
|
|
731
983
|
contextInfo.stanzaId = quoted.key.id;
|
|
732
984
|
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
985
|
if (jid !== quoted.key.remoteJid) {
|
|
736
986
|
contextInfo.remoteJid = quoted.key.remoteJid;
|
|
737
987
|
}
|
|
738
988
|
if (contextInfo && innerMessage[key]) {
|
|
739
|
-
|
|
989
|
+
;
|
|
740
990
|
innerMessage[key].contextInfo = contextInfo;
|
|
741
991
|
}
|
|
742
992
|
}
|
|
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
|
|
993
|
+
if (!!options?.ephemeralExpiration &&
|
|
747
994
|
key !== 'protocolMessage' &&
|
|
748
|
-
// already not converted to disappearing message
|
|
749
995
|
key !== 'ephemeralMessage' &&
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
...(
|
|
755
|
-
expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
|
|
756
|
-
//ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
|
|
996
|
+
!isJidNewsletter(jid) &&
|
|
997
|
+
innerMessage[key]) {
|
|
998
|
+
const target = innerMessage[key];
|
|
999
|
+
target.contextInfo = {
|
|
1000
|
+
...(target.contextInfo || {}),
|
|
1001
|
+
expiration: Number(options.ephemeralExpiration) || WA_DEFAULT_EPHEMERAL
|
|
757
1002
|
};
|
|
758
1003
|
}
|
|
759
1004
|
message = WAProto.Message.create(message);
|
|
@@ -766,18 +1011,15 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
766
1011
|
message: message,
|
|
767
1012
|
messageTimestamp: timestamp,
|
|
768
1013
|
messageStubParameters: [],
|
|
769
|
-
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
1014
|
+
participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
|
|
770
1015
|
status: WAMessageStatus.PENDING
|
|
771
1016
|
};
|
|
772
1017
|
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
773
1018
|
};
|
|
774
1019
|
export const generateWAMessage = async (jid, content, options) => {
|
|
775
|
-
// ensure msg ID is with every log
|
|
776
1020
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
777
|
-
// Pass jid in the options to generateWAMessageContent
|
|
778
1021
|
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { ...options, jid }), options);
|
|
779
1022
|
};
|
|
780
|
-
/** Get the key to access the true type of content */
|
|
781
1023
|
export const getContentType = (content) => {
|
|
782
1024
|
if (content) {
|
|
783
1025
|
const keys = Object.keys(content);
|
|
@@ -785,17 +1027,10 @@ export const getContentType = (content) => {
|
|
|
785
1027
|
return key;
|
|
786
1028
|
}
|
|
787
1029
|
};
|
|
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
1030
|
export const normalizeMessageContent = (content) => {
|
|
795
1031
|
if (!content) {
|
|
796
1032
|
return undefined;
|
|
797
1033
|
}
|
|
798
|
-
// set max iterations to prevent an infinite loop
|
|
799
1034
|
for (let i = 0; i < 5; i++) {
|
|
800
1035
|
const inner = getFutureProofMessage(content);
|
|
801
1036
|
if (!inner) {
|
|
@@ -817,10 +1052,6 @@ export const normalizeMessageContent = (content) => {
|
|
|
817
1052
|
message?.lottieStickerMessage);
|
|
818
1053
|
}
|
|
819
1054
|
};
|
|
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
1055
|
export const extractMessageContent = (content) => {
|
|
825
1056
|
const extractFromTemplateMessage = (msg) => {
|
|
826
1057
|
if (msg.imageMessage) {
|
|
@@ -856,9 +1087,6 @@ export const extractMessageContent = (content) => {
|
|
|
856
1087
|
}
|
|
857
1088
|
return content;
|
|
858
1089
|
};
|
|
859
|
-
/**
|
|
860
|
-
* Returns the device predicted by message ID
|
|
861
|
-
*/
|
|
862
1090
|
export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
863
1091
|
? 'ios'
|
|
864
1092
|
: /^3E.{20}$/.test(id)
|
|
@@ -868,7 +1096,6 @@ export const getDevice = (id) => /^3A.{18}$/.test(id)
|
|
|
868
1096
|
: /^(3F|.{18}$)/.test(id)
|
|
869
1097
|
? 'desktop'
|
|
870
1098
|
: 'unknown';
|
|
871
|
-
/** Upserts a receipt in the message */
|
|
872
1099
|
export const updateMessageWithReceipt = (msg, receipt) => {
|
|
873
1100
|
msg.userReceipt = msg.userReceipt || [];
|
|
874
1101
|
const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
|
|
@@ -879,7 +1106,6 @@ export const updateMessageWithReceipt = (msg, receipt) => {
|
|
|
879
1106
|
msg.userReceipt.push(receipt);
|
|
880
1107
|
}
|
|
881
1108
|
};
|
|
882
|
-
/** Update the message with a new reaction */
|
|
883
1109
|
export const updateMessageWithReaction = (msg, reaction) => {
|
|
884
1110
|
const authorID = getKeyAuthor(reaction.key);
|
|
885
1111
|
const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
|
|
@@ -887,7 +1113,6 @@ export const updateMessageWithReaction = (msg, reaction) => {
|
|
|
887
1113
|
reactions.push(reaction);
|
|
888
1114
|
msg.reactions = reactions;
|
|
889
1115
|
};
|
|
890
|
-
/** Update the message with a new poll update */
|
|
891
1116
|
export const updateMessageWithPollUpdate = (msg, update) => {
|
|
892
1117
|
const authorID = getKeyAuthor(update.pollUpdateMessageKey);
|
|
893
1118
|
const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
|
|
@@ -896,19 +1121,12 @@ export const updateMessageWithPollUpdate = (msg, update) => {
|
|
|
896
1121
|
}
|
|
897
1122
|
msg.pollUpdates = reactions;
|
|
898
1123
|
};
|
|
899
|
-
/** Update the message with a new event response */
|
|
900
1124
|
export const updateMessageWithEventResponse = (msg, update) => {
|
|
901
1125
|
const authorID = getKeyAuthor(update.eventResponseMessageKey);
|
|
902
1126
|
const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
|
|
903
1127
|
responses.push(update);
|
|
904
1128
|
msg.eventResponses = responses;
|
|
905
1129
|
};
|
|
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
1130
|
export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
913
1131
|
const opts = message?.pollCreationMessage?.options ||
|
|
914
1132
|
message?.pollCreationMessageV2?.options ||
|
|
@@ -942,12 +1160,6 @@ export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
|
|
|
942
1160
|
}
|
|
943
1161
|
return Object.values(voteHashMap);
|
|
944
1162
|
}
|
|
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
1163
|
export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
952
1164
|
const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
|
|
953
1165
|
const responseMap = {};
|
|
@@ -965,7 +1177,6 @@ export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
|
|
|
965
1177
|
}
|
|
966
1178
|
return Object.values(responseMap);
|
|
967
1179
|
}
|
|
968
|
-
/** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
|
|
969
1180
|
export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
970
1181
|
const keyMap = {};
|
|
971
1182
|
for (const { remoteJid, id, participant, fromMe } of keys) {
|
|
@@ -984,16 +1195,12 @@ export const aggregateMessageKeysNotFromMe = (keys) => {
|
|
|
984
1195
|
return Object.values(keyMap);
|
|
985
1196
|
};
|
|
986
1197
|
const REUPLOAD_REQUIRED_STATUS = [410, 404];
|
|
987
|
-
/**
|
|
988
|
-
* Downloads the given message. Throws an error if it's not a media message
|
|
989
|
-
*/
|
|
990
1198
|
export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
991
1199
|
const result = await downloadMsg().catch(async (error) => {
|
|
992
1200
|
if (ctx &&
|
|
993
|
-
typeof error?.status === 'number' &&
|
|
1201
|
+
typeof error?.status === 'number' &&
|
|
994
1202
|
REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
|
|
995
1203
|
ctx.logger.info({ key: message.key }, 'sending reupload media request...');
|
|
996
|
-
// request reupload
|
|
997
1204
|
message = await ctx.reuploadRequest(message);
|
|
998
1205
|
const result = await downloadMsg();
|
|
999
1206
|
return result;
|
|
@@ -1034,7 +1241,6 @@ export const downloadMediaMessage = async (message, type, options, ctx) => {
|
|
|
1034
1241
|
return stream;
|
|
1035
1242
|
}
|
|
1036
1243
|
};
|
|
1037
|
-
/** Checks whether the given message is a media message; if it is returns the inner content */
|
|
1038
1244
|
export const assertMediaContent = (content) => {
|
|
1039
1245
|
content = extractMessageContent(content);
|
|
1040
1246
|
const mediaContent = content?.documentMessage ||
|