baileyz 1.0.6 → 1.0.8

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.
@@ -1,1480 +1,547 @@
1
- "use strict"
2
-
3
- Object.defineProperty(exports, "__esModule", { value: true })
4
-
5
- const { Boom } = require("@hapi/boom")
6
- const { randomBytes } = require("crypto")
7
- const { promises } = require("fs")
8
- const { proto } = require("../../WAProto")
9
- const {
10
- URL_REGEX,
11
- WA_DEFAULT_EPHEMERAL
12
- } = require("../Defaults/constants")
13
- const { MEDIA_KEYS } = require("../Defaults/media")
14
- const {
15
- WAProto,
16
- WAMessageStatus
17
- } = require("../Types")
18
- const {
19
- isJidGroup,
20
- isJidNewsletter,
21
- isJidStatusBroadcast,
22
- jidNormalizedUser
23
- } = require("../WABinary")
24
- const { sha256 } = require("./crypto")
25
- const {
26
- generateMessageID,
27
- getKeyAuthor,
28
- unixTimestampSeconds
29
- } = require("./generics")
30
- const {
31
- downloadContentFromMessage,
32
- encryptedStream,
33
- generateThumbnail,
34
- getAudioDuration,
35
- getAudioWaveform,
36
- getRawMediaUploadData,
37
- getStream,
38
- toBuffer
39
- } = require("./messages-media")
40
-
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.assertMediaContent = exports.downloadMediaMessage = exports.aggregateMessageKeysNotFromMe = exports.getAggregateVotesInPollMessage = exports.updateMessageWithPollUpdate = exports.updateMessageWithReaction = exports.updateMessageWithReceipt = exports.getDevice = exports.extractMessageContent = exports.normalizeMessageContent = exports.getContentType = exports.generateWAMessage = exports.generateWAMessageFromContent = exports.generateWAMessageContent = exports.generateForwardMessageContent = exports.prepareDisappearingMessageSettingContent = exports.prepareWAMessageMedia = exports.generateLinkPreviewIfRequired = exports.extractUrlFromText = void 0;
7
+ const boom_1 = require("@hapi/boom");
8
+ const axios_1 = __importDefault(require("axios"));
9
+ const crypto_1 = require("crypto");
10
+ const fs_1 = require("fs");
11
+ const WAProto_1 = require("../../WAProto");
12
+ const Defaults_1 = require("../Defaults");
13
+ const Types_1 = require("../Types");
14
+ const WABinary_1 = require("../WABinary");
15
+ const crypto_2 = require("./crypto");
16
+ const generics_1 = require("./generics");
17
+ const messages_media_1 = require("./messages-media");
41
18
  const MIMETYPE_MAP = {
42
19
  image: 'image/jpeg',
43
20
  video: 'video/mp4',
44
21
  document: 'application/pdf',
45
- audio: 'audio/ogg codecs=opus',
22
+ audio: 'audio/ogg; codecs=opus',
46
23
  sticker: 'image/webp',
47
- 'product-catalog-image': 'image/jpeg'
48
- }
49
-
24
+ 'product-catalog-image': 'image/jpeg',
25
+ };
50
26
  const MessageTypeProto = {
51
- 'image': WAProto.Message.ImageMessage,
52
- 'video': WAProto.Message.VideoMessage,
53
- 'audio': WAProto.Message.AudioMessage,
54
- 'sticker': WAProto.Message.StickerMessage,
55
- 'document': WAProto.Message.DocumentMessage
56
- }
57
-
27
+ 'image': Types_1.WAProto.Message.ImageMessage,
28
+ 'video': Types_1.WAProto.Message.VideoMessage,
29
+ 'audio': Types_1.WAProto.Message.AudioMessage,
30
+ 'sticker': Types_1.WAProto.Message.StickerMessage,
31
+ 'document': Types_1.WAProto.Message.DocumentMessage,
32
+ };
33
+ const ButtonType = WAProto_1.proto.Message.ButtonsMessage.HeaderType;
58
34
  /**
59
35
  * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
60
36
  * @param text eg. hello https://google.com
61
37
  * @returns the URL, eg. https://google.com
62
38
  */
63
- const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0]
64
-
39
+ const extractUrlFromText = (text) => { var _a; return (_a = text.match(Defaults_1.URL_REGEX)) === null || _a === void 0 ? void 0 : _a[0]; };
40
+ exports.extractUrlFromText = extractUrlFromText;
65
41
  const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
66
- const url = extractUrlFromText(text)
67
-
42
+ const url = (0, exports.extractUrlFromText)(text);
68
43
  if (!!getUrlInfo && url) {
69
44
  try {
70
- const urlInfo = await getUrlInfo(url)
71
- return urlInfo
45
+ const urlInfo = await getUrlInfo(url);
46
+ return urlInfo;
72
47
  }
73
-
74
- catch (error) {
75
- logger?.warn({ trace: error.stack }, 'url generation failed')
48
+ catch (error) { // ignore if fails
49
+ logger === null || logger === void 0 ? void 0 : logger.warn({ trace: error.stack }, 'url generation failed');
76
50
  }
77
51
  }
78
- }
79
-
52
+ };
53
+ exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired;
80
54
  const assertColor = async (color) => {
81
- let assertedColor
82
-
55
+ let assertedColor;
83
56
  if (typeof color === 'number') {
84
- assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1
57
+ assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1;
85
58
  }
86
-
87
59
  else {
88
- let hex = color.trim().replace('#', '')
60
+ let hex = color.trim().replace('#', '');
89
61
  if (hex.length <= 6) {
90
- hex = 'FF' + hex.padStart(6, '0')
62
+ hex = 'FF' + hex.padStart(6, '0');
91
63
  }
92
- assertedColor = parseInt(hex, 16)
93
- return assertedColor
64
+ assertedColor = parseInt(hex, 16);
65
+ return assertedColor;
94
66
  }
95
- }
96
-
67
+ };
97
68
  const prepareWAMessageMedia = async (message, options) => {
98
- const logger = options.logger
99
-
100
- let mediaType
101
-
102
- for (const key of MEDIA_KEYS) {
69
+ const logger = options.logger;
70
+ let mediaType;
71
+ for (const key of Defaults_1.MEDIA_KEYS) {
103
72
  if (key in message) {
104
- mediaType = key
73
+ mediaType = key;
105
74
  }
106
75
  }
107
-
108
76
  if (!mediaType) {
109
- throw new Boom('Invalid media type', { statusCode: 400 })
77
+ throw new boom_1.Boom('Invalid media type', {
78
+ statusCode: 400
79
+ });
110
80
  }
111
81
 
112
- const uploadData = {
82
+ const uploadData = {
113
83
  ...message,
114
- ...(message.annotations ? {
115
- annotations: message.annotations
116
- } : {
117
- annotations: [
118
- {
119
- polygonVertices: [
120
- {
121
- x: 60.71664810180664,
122
- y: -36.39784622192383
123
- },
124
- {
125
- x: -16.710189819335938,
126
- y: 49.263675689697266
127
- },
128
- {
129
- x: -56.585853576660156,
130
- y: 37.85963439941406
131
- },
132
- {
133
- x: 20.840980529785156,
134
- y: -47.80188751220703
135
- }
136
- ],
137
- newsletter: {
138
- newsletterJid: "120363422113753558@newsletter",
139
- serverMessageId: 0,
140
- newsletterName: "MovaNest.",
141
- contentType: "UPDATE",
142
- }
143
- }
144
- ]
145
- }),
146
84
  media: message[mediaType]
147
85
  };
148
-
149
-
150
- delete uploadData[mediaType]
151
-
152
- // check if cacheable + generate cache key
86
+ delete uploadData[mediaType];
153
87
  const cacheableKey = typeof uploadData.media === 'object' &&
154
- 'url' in uploadData.media &&
88
+ ('url' in uploadData.media) &&
155
89
  !!uploadData.media.url &&
156
- !!options.mediaCache &&
157
- mediaType + ':' + uploadData.media.url.toString()
158
-
90
+ !!options.mediaCache && (
91
+ mediaType + ':' + uploadData.media.url.toString());
92
+
159
93
  if (mediaType === 'document' && !uploadData.fileName) {
160
- uploadData.fileName = 'file'
94
+ uploadData.fileName = 'file';
161
95
  }
162
-
96
+
163
97
  if (!uploadData.mimetype) {
164
- uploadData.mimetype = MIMETYPE_MAP[mediaType]
98
+ uploadData.mimetype = MIMETYPE_MAP[mediaType];
165
99
  }
166
100
 
167
101
  if (cacheableKey) {
168
- const mediaBuff = await options.mediaCache.get(cacheableKey)
169
-
102
+ const mediaBuff = options.mediaCache.get(cacheableKey);
170
103
  if (mediaBuff) {
171
- logger?.debug({ cacheableKey }, 'got media cache hit')
172
-
173
- const obj = proto.Message.decode(mediaBuff)
174
- const key = `${mediaType}Message`
175
-
176
- Object.assign(obj[key], { ...uploadData, media: undefined })
177
-
178
- return obj
104
+ logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'got media cache hit');
105
+ const obj = Types_1.WAProto.Message.decode(mediaBuff);
106
+ const key = `${mediaType}Message`;
107
+ Object.assign(obj[key], { ...uploadData, media: undefined });
108
+ return obj;
179
109
  }
180
110
  }
181
111
 
182
- const isNewsletter = !!options.jid && isJidNewsletter(options.jid)
112
+ const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
113
+ const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
114
+ (typeof uploadData['jpegThumbnail'] === 'undefined');
115
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
116
+ const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
117
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
183
118
 
184
- if (isNewsletter) {
185
- logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter')
186
- const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger)
187
- const fileSha256B64 = fileSha256.toString('base64');
188
- const { mediaUrl, directPath } = await options.upload(filePath, {
189
- fileEncSha256B64: fileSha256B64,
190
- mediaType: mediaType,
191
- timeoutMs: options.mediaUploadTimeoutMs
192
- })
193
-
194
- await promises.unlink(filePath)
195
-
196
- const obj = WAProto.Message.fromObject({
197
- // todo: add more support here
198
- [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
199
- url: mediaUrl,
200
- directPath,
201
- fileSha256,
202
- fileLength,
203
- ...uploadData,
204
- media: undefined
205
- })
206
- })
207
-
208
- if (uploadData.ptv) {
209
- obj.ptvMessage = obj.videoMessage
210
- delete obj.videoMessage
211
- }
212
-
213
- if (obj.stickerMessage) {
214
- obj.stickerMessage.stickerSentTs = Date.now()
215
- }
216
-
217
- if (cacheableKey) {
218
- logger?.debug({ cacheableKey }, 'set cache');
219
- await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish())
220
- }
119
+ const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath, opusConverted } = await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
120
+ logger,
121
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
122
+ opts: options.options,
123
+ isPtt: uploadData.ptt,
124
+ forceOpus: (mediaType === "audio" && uploadData.mimetype && uploadData.mimetype.includes('opus'))
125
+ });
221
126
 
222
- return obj
127
+ if (mediaType === 'audio' && opusConverted) {
128
+ uploadData.mimetype = 'audio/ogg; codecs=opus';
223
129
  }
224
130
 
225
- const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
226
- const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined'
227
- const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true
228
- const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
229
- const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
230
- const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
231
- logger,
232
- saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
233
- opts: options.options
234
- })
235
- const fileEncSha256B64 = fileEncSha256.toString('base64')
236
- const [{ mediaUrl, directPath }] = await Promise.all([
131
+ const fileEncSha256B64 = (options.newsletter ? fileSha256 : fileEncSha256 !== null && fileEncSha256 !== void 0 ? fileEncSha256 : fileSha256).toString('base64');
132
+
133
+ const [{ mediaUrl, directPath, handle }] = await Promise.all([
237
134
  (async () => {
238
- const result = await options.upload(encFilePath, {
239
- fileEncSha256B64,
240
- mediaType,
241
- timeoutMs: options.mediaUploadTimeoutMs
242
- })
243
- logger?.debug({ mediaType, cacheableKey }, 'uploaded media')
244
- return result
135
+ const result = await options.upload(encWriteStream, { fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs });
136
+ logger === null || logger === void 0 ? void 0 : logger.debug({ mediaType, cacheableKey }, 'uploaded media');
137
+ return result;
245
138
  })(),
246
139
  (async () => {
247
140
  try {
248
141
  if (requiresThumbnailComputation) {
249
- const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options)
250
-
251
- uploadData.jpegThumbnail = thumbnail
252
-
142
+ const { thumbnail, originalImageDimensions } = await (0, messages_media_1.generateThumbnail)(bodyPath, mediaType, options);
143
+ uploadData.jpegThumbnail = thumbnail;
253
144
  if (!uploadData.width && originalImageDimensions) {
254
- uploadData.width = originalImageDimensions.width
255
- uploadData.height = originalImageDimensions.height
256
- logger?.debug('set dimensions')
145
+ uploadData.width = originalImageDimensions.width;
146
+ uploadData.height = originalImageDimensions.height;
147
+ logger === null || logger === void 0 ? void 0 : logger.debug('set dimensions');
257
148
  }
258
- logger?.debug('generated thumbnail')
149
+ logger === null || logger === void 0 ? void 0 : logger.debug('generated thumbnail');
259
150
  }
260
-
261
151
  if (requiresDurationComputation) {
262
- uploadData.seconds = await getAudioDuration(originalFilePath)
263
- logger?.debug('computed audio duration')
152
+ uploadData.seconds = await (0, messages_media_1.getAudioDuration)(bodyPath);
153
+ logger === null || logger === void 0 ? void 0 : logger.debug('computed audio duration');
264
154
  }
265
-
266
155
  if (requiresWaveformProcessing) {
267
- uploadData.waveform = await getAudioWaveform(originalFilePath, logger)
268
- logger?.debug('processed waveform')
156
+ uploadData.waveform = await (0, messages_media_1.getAudioWaveform)(bodyPath, logger);
157
+ logger === null || logger === void 0 ? void 0 : logger.debug('processed waveform');
269
158
  }
270
-
271
159
  if (requiresAudioBackground) {
272
- uploadData.backgroundArgb = await assertColor(options.backgroundColor)
273
- logger?.debug('computed backgroundColor audio status')
160
+ uploadData.backgroundArgb = await assertColor(options.backgroundColor);
161
+ logger === null || logger === void 0 ? void 0 : logger.debug('computed backgroundColor audio status');
274
162
  }
275
163
  }
276
164
  catch (error) {
277
- logger?.warn({ trace: error.stack }, 'failed to obtain extra info')
165
+ logger === null || logger === void 0 ? void 0 : logger.warn({ trace: error.stack }, 'failed to obtain extra info');
278
166
  }
279
- })()
280
- ]).finally(async () => {
281
- try {
282
- await promises.unlink(encFilePath)
283
-
284
- if (originalFilePath) {
285
- await promises.unlink(originalFilePath)
286
- }
287
-
288
- logger?.debug('removed tmp files')
167
+ })(),
168
+ ])
169
+ .finally(async () => {
170
+ if (!Buffer.isBuffer(encWriteStream)) {
171
+ encWriteStream.destroy();
289
172
  }
290
- catch (error) {
291
- logger?.warn('failed to remove tmp file')
173
+
174
+ if (didSaveToTmpPath && bodyPath) {
175
+ await fs_1.promises.unlink(bodyPath);
176
+ logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp files');
292
177
  }
293
- })
178
+ });
294
179
 
295
- const obj = WAProto.Message.fromObject({
180
+ const obj = Types_1.WAProto.Message.fromObject({
296
181
  [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
297
- url: mediaUrl,
182
+ url: handle ? undefined : mediaUrl,
298
183
  directPath,
299
- mediaKey,
300
- fileEncSha256,
184
+ mediaKey: mediaKey,
185
+ fileEncSha256: fileEncSha256,
301
186
  fileSha256,
302
187
  fileLength,
303
- mediaKeyTimestamp: unixTimestampSeconds(),
188
+ mediaKeyTimestamp: handle ? undefined : (0, generics_1.unixTimestampSeconds)(),
304
189
  ...uploadData,
305
190
  media: undefined
306
191
  })
307
- })
192
+ });
308
193
 
309
194
  if (uploadData.ptv) {
310
- obj.ptvMessage = obj.videoMessage
311
- delete obj.videoMessage
195
+ obj.ptvMessage = obj.videoMessage;
196
+ delete obj.videoMessage;
312
197
  }
313
198
 
314
199
  if (cacheableKey) {
315
- logger?.debug({ cacheableKey }, 'set cache')
316
- await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish())
317
- }
318
-
319
- return obj
320
- }
321
-
322
- const prepareAlbumMessageContent = async (jid, albums, options) => {
323
- if (!Array.isArray(albums)) {
324
- throw new Error("albums must be an array containing media objects.")
325
- }
326
-
327
- if (albums.length === 0) {
328
- throw new Error("albums cannot be empty. At least one media item is required.")
200
+ logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'set cache');
201
+ options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish());
329
202
  }
330
203
 
331
- const validCount = albums.filter(m => ('image' in m) || ('video' in m)).length
332
-
333
- if (validCount === 0) {
334
- throw new Error("albums contains no valid media. Use 'image' or 'video' keys.")
335
- }
336
-
337
- let mediaHandle
338
- let mediaMsg
339
- const message = []
340
-
341
- const albumMsg = generateWAMessageFromContent(jid, {
342
- albumMessage: {
343
- expectedImageCount: albums.filter(item => 'image' in item).length,
344
- expectedVideoCount: albums.filter(item => 'video' in item).length
345
- }
346
- }, options)
347
-
348
- await options.suki.relayMessage(jid, albumMsg.message, {
349
- messageId: albumMsg.key.id
350
- })
351
-
352
- for (const media of albums) {
353
- let content = {}
354
- if ('image' in media) {
355
- content = { image: media.image }
356
- } else if ('video' in media) {
357
- content = { video: media.video }
358
- } else {
359
- continue
360
- }
361
-
362
- mediaMsg = await generateWAMessage(
363
- jid,
364
- {
365
- ...content,
366
- ...media
367
- },
368
- {
369
- userJid: options.userJid,
370
- upload: async (encFilePath, opts) => {
371
- const up = await options.suki.waUploadToServer(
372
- encFilePath,
373
- { ...opts, newsletter: isJidNewsletter(jid) }
374
- )
375
- mediaHandle = up.handle
376
- return up
377
- },
378
- ...options
379
- }
380
- )
381
-
382
- if (mediaMsg) {
383
- mediaMsg.message.messageContextInfo = {
384
- messageSecret: randomBytes(32),
385
- messageAssociation: {
386
- associationType: proto.MessageAssociation.AssociationType.MEDIA_ALBUM,
387
- parentMessageKey: albumMsg.key
388
- }
389
- }
390
- }
391
-
392
- message.push(mediaMsg)
393
- }
394
-
395
- return message
396
- }
397
-
398
- const prepareDisappearingMessageSettingContent = (expiration) => {
204
+ return obj;
205
+ };
206
+ exports.prepareWAMessageMedia = prepareWAMessageMedia;
207
+ const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
208
+ ephemeralExpiration = ephemeralExpiration || 0;
399
209
  const content = {
400
210
  ephemeralMessage: {
401
211
  message: {
402
212
  protocolMessage: {
403
- type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
404
- ephemeralExpiration: expiration ? expiration : 0
213
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
214
+ ephemeralExpiration
405
215
  }
406
216
  }
407
217
  }
408
- }
409
-
410
- return WAProto.Message.fromObject(content)
411
- }
412
-
218
+ };
219
+ return Types_1.WAProto.Message.fromObject(content);
220
+ };
221
+ exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
413
222
  /**
414
223
  * Generate forwarded message content like WA does
415
224
  * @param message the message to forward
416
225
  * @param options.forceForward will show the message as forwarded even if it is from you
417
226
  */
418
227
  const generateForwardMessageContent = (message, forceForward) => {
419
- let content = message.message
420
-
228
+ var _a;
229
+ let content = message.message;
421
230
  if (!content) {
422
- throw new Boom('no content in message', { statusCode: 400 })
231
+ throw new boom_1.Boom('no content in message', { statusCode: 400 });
423
232
  }
424
-
425
233
  // hacky copy
426
- content = normalizeMessageContent(content)
427
- content = proto.Message.decode(proto.Message.encode(content).finish())
428
-
429
- let key = Object.keys(content)[0]
430
- let score = content[key].contextInfo?.forwardingScore || 0
431
-
432
- if (forceForward) score += forceForward ? forceForward : 1
433
-
234
+ content = (0, exports.normalizeMessageContent)(content);
235
+ content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish());
236
+ let key = Object.keys(content)[0];
237
+ let score = ((_a = content[key].contextInfo) === null || _a === void 0 ? void 0 : _a.forwardingScore) || 0;
238
+ score += message.key.fromMe && !forceForward ? 0 : 1;
434
239
  if (key === 'conversation') {
435
- content.extendedTextMessage = { text: content[key] }
436
- delete content.conversation
437
- key = 'extendedTextMessage'
240
+ content.extendedTextMessage = { text: content[key] };
241
+ delete content.conversation;
242
+ key = 'extendedTextMessage';
438
243
  }
439
-
440
244
  if (score > 0) {
441
- content[key].contextInfo = { forwardingScore: score, isForwarded: true }
245
+ content[key].contextInfo = { forwardingScore: score, isForwarded: true };
442
246
  }
443
-
444
247
  else {
445
- content[key].contextInfo = {}
248
+ content[key].contextInfo = {};
446
249
  }
447
-
448
- return content
449
- }
450
-
451
- const hasNonNullishProperty = (message, key) => {
452
- return (
453
- typeof message === 'object' &&
454
- message !== null &&
455
- key in message &&
456
- message[key] !== null &&
457
- message[key] !== undefined
458
- )
459
- }
460
-
461
- function hasOptionalProperty(obj, key) {
462
- return (
463
- typeof obj === 'object' &&
464
- obj !== null &&
465
- key in obj &&
466
- obj[key] !== null
467
- )
468
- }
469
-
250
+ return content;
251
+ };
252
+ exports.generateForwardMessageContent = generateForwardMessageContent;
470
253
  const generateWAMessageContent = async (message, options) => {
471
- let m = {}
472
-
473
- if (hasNonNullishProperty(message, "text")) {
474
- const extContent = { text: message.text }
475
-
476
- let urlInfo = message.linkPreview
477
-
478
- if (typeof urlInfo === "undefined") {
479
- urlInfo = await generateLinkPreviewIfRequired(
480
- message.text,
481
- options.getUrlInfo,
482
- options.logger
483
- )
254
+ var _a;
255
+ var _b;
256
+ let m = {};
257
+ if ('text' in message) {
258
+ const extContent = { text: message.text };
259
+ let urlInfo = message.linkPreview;
260
+ if (typeof urlInfo === 'undefined') {
261
+ urlInfo = await (0, exports.generateLinkPreviewIfRequired)(message.text, options.getUrlInfo, options.logger);
484
262
  }
485
-
486
263
  if (urlInfo) {
487
- extContent.canonicalUrl = urlInfo["canonical-url"]
488
- extContent.matchedText = urlInfo["matched-text"]
489
- extContent.jpegThumbnail = urlInfo.jpegThumbnail
490
- extContent.description = urlInfo.description
491
- extContent.title = urlInfo.title
492
- extContent.previewType = 0
493
-
494
- const img = urlInfo.highQualityThumbnail
495
-
264
+ extContent.canonicalUrl = urlInfo['canonical-url'];
265
+ extContent.matchedText = urlInfo['matched-text'];
266
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail;
267
+ extContent.description = urlInfo.description;
268
+ extContent.title = urlInfo.title;
269
+ extContent.previewType = 0;
270
+ const img = urlInfo.highQualityThumbnail;
496
271
  if (img) {
497
- extContent.thumbnailDirectPath = img.directPath
498
- extContent.mediaKey = img.mediaKey
499
- extContent.mediaKeyTimestamp = img.mediaKeyTimestamp
500
- extContent.thumbnailWidth = img.width
501
- extContent.thumbnailHeight = img.height
502
- extContent.thumbnailSha256 = img.fileSha256
503
- extContent.thumbnailEncSha256 = img.fileEncSha256
272
+ extContent.thumbnailDirectPath = img.directPath;
273
+ extContent.mediaKey = img.mediaKey;
274
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
275
+ extContent.thumbnailWidth = img.width;
276
+ extContent.thumbnailHeight = img.height;
277
+ extContent.thumbnailSha256 = img.fileSha256;
278
+ extContent.thumbnailEncSha256 = img.fileEncSha256;
504
279
  }
505
280
  }
506
-
507
281
  if (options.backgroundColor) {
508
- extContent.backgroundArgb = await assertColor(
509
- options.backgroundColor
510
- )
282
+ extContent.backgroundArgb = await assertColor(options.backgroundColor);
511
283
  }
512
-
513
- if (options.textColor) {
514
- extContent.textArgb = await assertColor(options.textColor)
515
- }
516
-
517
284
  if (options.font) {
518
- extContent.font = options.font
285
+ extContent.font = options.font;
519
286
  }
520
-
521
- m.extendedTextMessage = extContent
522
- } else if (hasNonNullishProperty(message, "contacts")) {
523
- const contactLen = message.contacts.contacts.length
524
-
525
- let contactMessage
526
-
527
- if (!contactLen) {
528
- throw new Boom("require atleast 1 contact", { statusCode: 400 })
529
- }
530
-
531
- if (contactLen === 1) {
532
- contactMessage = {
533
- contactMessage: WAProto.Message.ContactMessage.fromObject(
534
- message.contacts.contacts[0]
535
- ),
536
- }
537
- } else {
538
- contactMessage = {
539
- contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(
540
- message.contacts
541
- ),
542
- }
543
- }
544
-
545
- m = contactMessage
546
- } else if (hasNonNullishProperty(message, "contacts")) {
547
- const contactLen = message.contacts.contacts.length
548
-
549
- let contactMessage
550
-
287
+ m.extendedTextMessage = extContent;
288
+ }
289
+ else if ('contacts' in message) {
290
+ const contactLen = message.contacts.contacts.length;
551
291
  if (!contactLen) {
552
- throw new Boom("require atleast 1 contact", { statusCode: 400 })
292
+ throw new boom_1.Boom('require atleast 1 contact', { statusCode: 400 });
553
293
  }
554
-
555
294
  if (contactLen === 1) {
556
- contactMessage = {
557
- contactMessage: WAProto.Message.ContactMessage.fromObject(
558
- message.contacts.contacts[0]
559
- ),
560
- }
561
- } else {
562
- contactMessage = {
563
- contactsArrayMessage: WAProto.Message.ContactsArrayMessage.fromObject(
564
- message.contacts
565
- ),
566
- }
295
+ m.contactMessage = Types_1.WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0]);
567
296
  }
568
-
569
- m = contactMessage
570
- } else if (hasNonNullishProperty(message, "location")) {
571
- let locationMessage
572
-
573
- if (message.live) {
574
- locationMessage = {
575
- liveLocationMessage: WAProto.Message.LiveLocationMessage.fromObject(
576
- message.location
577
- ),
578
- }
579
- } else {
580
- locationMessage = {
581
- locationMessage: WAProto.Message.LocationMessage.fromObject(
582
- message.location
583
- ),
584
- }
297
+ else {
298
+ m.contactsArrayMessage = Types_1.WAProto.Message.ContactsArrayMessage.fromObject(message.contacts);
585
299
  }
586
-
587
- m = locationMessage
588
- } else if (hasNonNullishProperty(message, "react")) {
300
+ }
301
+ else if ('location' in message) {
302
+ m.locationMessage = Types_1.WAProto.Message.LocationMessage.fromObject(message.location);
303
+ }
304
+ else if ('react' in message) {
589
305
  if (!message.react.senderTimestampMs) {
590
- message.react.senderTimestampMs = Date.now()
306
+ message.react.senderTimestampMs = Date.now();
591
307
  }
592
-
593
- m.reactionMessage = WAProto.Message.ReactionMessage.fromObject(
594
- message.react
595
- )
596
- } else if (hasNonNullishProperty(message, "delete")) {
308
+ m.reactionMessage = Types_1.WAProto.Message.ReactionMessage.fromObject(message.react);
309
+ }
310
+ else if ('delete' in message) {
597
311
  m.protocolMessage = {
598
312
  key: message.delete,
599
- type: WAProto.Message.ProtocolMessage.Type.REVOKE,
600
- }
601
- } else if (hasNonNullishProperty(message, "sharePhoneNumber")) {
602
- m.protocolMessage = {
603
- type: WAProto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER,
604
- }
605
- } else if (hasNonNullishProperty(message, "requestPhoneNumber")) {
606
- m.requestPhoneNumberMessage = {}
607
- } else if (hasNonNullishProperty(message, "forward")) {
608
- m = generateForwardMessageContent(message.forward, message.force)
609
- } else if (hasNonNullishProperty(message, "disappearingMessagesInChat")) {
610
- const exp =
611
- typeof message.disappearingMessagesInChat === "boolean"
612
- ? message.disappearingMessagesInChat
613
- ? WA_DEFAULT_EPHEMERAL
614
- : 0
615
- : message.disappearingMessagesInChat
616
- m = prepareDisappearingMessageSettingContent(exp)
617
- } else if (hasNonNullishProperty(message, "groupInvite")) {
618
- m.groupInviteMessage = {}
619
-
620
- m.groupInviteMessage.inviteCode = message.groupInvite.code
621
- m.groupInviteMessage.inviteExpiration = message.groupInvite.expiration
622
- m.groupInviteMessage.caption = message.groupInvite.caption
623
- m.groupInviteMessage.groupJid = message.groupInvite.jid
624
- m.groupInviteMessage.groupName = message.groupInvite.name
625
- m.groupInviteMessage.contextInfo = message.contextInfo
626
-
627
- if (options.getProfilePicUrl) {
628
- const pfpUrl = await options.getProfilePicUrl(
629
- message.groupInvite.jid
630
- )
631
- const { thumbnail } = await generateThumbnail(pfpUrl, "image")
632
- m.groupInviteMessage.jpegThumbnail = thumbnail
633
- }
634
- } else if (hasNonNullishProperty(message, "adminInvite")) {
635
- m.newsletterAdminInviteMessage = {}
636
-
637
- m.newsletterAdminInviteMessage.newsletterJid = message.adminInvite.jid
638
- m.newsletterAdminInviteMessage.newsletterName =
639
- message.adminInvite.name
640
- m.newsletterAdminInviteMessage.caption = message.adminInvite.caption
641
- m.newsletterAdminInviteMessage.inviteExpiration =
642
- message.adminInvite.expiration
643
- m.newsletterAdminInviteMessage.contextInfo = message.contextInfo
644
-
645
- if (options.getProfilePicUrl) {
646
- const pfpUrl = await options.getProfilePicUrl(
647
- message.adminInvite.jid
648
- )
649
- const { thumbnail } = await generateThumbnail(pfpUrl, "image")
650
- m.newsletterAdminInviteMessage.jpegThumbnail = thumbnail
651
- }
652
- } else if (hasNonNullishProperty(message, "keep")) {
653
- m.keepInChatMessage = {}
654
-
655
- m.keepInChatMessage.key = message.keep.key
656
- m.keepInChatMessage.keepType = message.keep?.type || 1
657
- m.keepInChatMessage.timestampMs = message.keep?.time || Date.now()
658
- } else if (hasNonNullishProperty(message, "call")) {
659
- m.scheduledCallCreationMessage = {}
660
-
661
- m.scheduledCallCreationMessage.scheduledTimestampMs =
662
- message.call?.time || Date.now()
663
- m.scheduledCallCreationMessage.callType = message.call?.type || 1
664
- m.scheduledCallCreationMessage.title =
665
- message.call?.name || "Call Creation"
666
- } else if (hasNonNullishProperty(message, "paymentInvite")) {
667
- m.messageContextInfo = {}
668
- m.paymentInviteMessage = {}
669
-
670
- m.paymentInviteMessage.expiryTimestamp =
671
- message.paymentInvite?.expiry || 0
672
- m.paymentInviteMessage.serviceType = message.paymentInvite?.type || 2
673
- } else if (hasNonNullishProperty(message, "ptv")) {
674
- const { videoMessage } = await prepareWAMessageMedia(
675
- { video: message.video },
676
- options
677
- )
678
-
679
- m.ptvMessage = videoMessage
680
- } else if (hasNonNullishProperty(message, "order")) {
681
- m.orderMessage = WAProto.Message.OrderMessage.fromObject(message.order)
682
- } else if (hasNonNullishProperty(message, "product")) {
683
- const { imageMessage } = await prepareWAMessageMedia(
684
- { image: message.product.productImage },
685
- options
686
- )
687
-
688
- m.productMessage = WAProto.Message.ProductMessage.fromObject({
689
- ...message,
690
- product: {
691
- ...message.product,
692
- productImage: imageMessage,
693
- },
694
- })
695
- } else if (hasNonNullishProperty(message, "album")) {
696
- const imageMessages = message.album.filter((item) => "image" in item)
697
- const videoMessages = message.album.filter((item) => "video" in item)
698
-
699
- m.albumMessage = WAProto.Message.AlbumMessage.fromObject({
700
- expectedImageCount: imageMessages.length,
701
- expectedVideoCount: videoMessages.length,
702
- })
703
- } else if (hasNonNullishProperty(message, "event")) {
704
- m.eventMessage = WAProto.Message.EventMessage.fromObject(message.event)
705
-
706
- if (!message.event.startTime) {
707
- m.eventMessage.startTime = unixTimestampSeconds() + 86400
708
- }
709
-
710
- if (options.getCallLink && message.event.call) {
711
- const link = await options.getCallLink(
712
- message.event.call,
713
- m.eventMessage.startTime
714
- )
715
- m.eventMessage.joinLink = link
716
- }
717
- } else if (hasNonNullishProperty(message, "pollResult")) {
718
- if (!Array.isArray(message.pollResult.values)) {
719
- throw new Boom("Invalid pollResult values", { statusCode: 400 })
720
- }
721
-
722
- const pollResultSnapshotMessage = {
723
- name: message.pollResult.name,
724
- pollVotes: message.pollResult.values.map(
725
- ([optionName, optionVoteCount]) => ({
726
- optionName,
727
- optionVoteCount,
728
- })
729
- ),
730
- }
731
-
732
- m.pollResultSnapshotMessage = pollResultSnapshotMessage
733
- } else if (hasNonNullishProperty(message, "poll")) {
734
- if (!Array.isArray(message.poll.values)) {
735
- throw new Boom("Invalid poll values", { statusCode: 400 })
736
- }
737
-
738
- if (
739
- message.poll.selectableCount < 0 ||
740
- message.poll.selectableCount > message.poll.values.length
741
- ) {
742
- throw new Boom(
743
- `poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`,
744
- { statusCode: 400 }
745
- )
746
- }
747
-
748
- const pollCreationMessage = {
749
- name: message.poll.name,
750
- selectableOptionsCount: message.poll?.selectableCount || 0,
751
- options: message.poll.values.map((optionName) => ({ optionName })),
752
- }
753
-
754
- if (message.poll?.toAnnouncementGroup) {
755
- m.pollCreationMessageV2 = pollCreationMessage
756
- } else {
757
- if (message.poll.selectableCount > 0) {
758
- m.pollCreationMessageV3 = pollCreationMessage
759
- } else {
760
- m.pollCreationMessage = pollCreationMessage
761
- }
762
- }
763
- } else if (hasNonNullishProperty(message, "payment")) {
764
- const requestPaymentMessage = {
765
- amount: {
766
- currencyCode: message.payment?.currency || "IDR",
767
- offset: message.payment?.offset || 0,
768
- value: message.payment?.amount || 999999999,
769
- },
770
- expiryTimestamp: message.payment?.expiry || 0,
771
- amount1000: message.payment?.amount || 999999999 * 1000,
772
- currencyCodeIso4217: message.payment?.currency || "IDR",
773
- requestFrom: message.payment?.from || "0@s.whatsapp.net",
774
- noteMessage: {
775
- extendedTextMessage: {
776
- text: message.payment?.note || "Notes",
777
- },
778
- },
779
- background: {
780
- placeholderArgb:
781
- message.payment?.image?.placeholderArgb || 4278190080,
782
- textArgb: message.payment?.image?.textArgb || 4294967295,
783
- subtextArgb: message.payment?.image?.subtextArgb || 4294967295,
784
- type: 1,
785
- },
786
- }
787
-
788
- m.requestPaymentMessage = requestPaymentMessage
789
- } else if (hasNonNullishProperty(message, "stickerPack")) {
790
- const {
791
- stickers,
792
- cover,
793
- name,
794
- publisher,
795
- packId,
796
- description,
797
- } = message.stickerPack
798
-
799
- const { zip } = require("fflate")
800
-
801
- const stickerData = {}
802
- const stickerPromises = stickers.map(async (s, i) => {
803
- const { stream } = await getStream(s.sticker)
804
- const buffer = await toBuffer(stream)
805
- const hash = sha256(buffer).toString("base64url")
806
- const fileName = `${i.toString().padStart(2, "0")}_${hash}.webp`
807
-
808
- stickerData[fileName] = [new Uint8Array(buffer), { level: 0 }]
809
-
810
- return {
811
- fileName,
812
- mimetype: "image/webp",
813
- isAnimated: s.isAnimated || false,
814
- isLottie: s.isLottie || false,
815
- emojis: s.emojis || [],
816
- accessibilityLabel: s.accessibilityLabel || "",
817
- }
818
- })
819
-
820
- const stickerMetadata = await Promise.all(stickerPromises)
821
-
822
- const zipBuffer = await new Promise((resolve, reject) => {
823
- zip(stickerData, (err, data) => {
824
- if (err) {
825
- reject(err)
826
- } else {
827
- resolve(Buffer.from(data))
828
- }
829
- })
830
- })
831
-
832
- const coverBuffer = await toBuffer((await getStream(cover)).stream)
833
-
834
- const [stickerPackUpload, coverUpload] = await Promise.all([
835
- encryptedStream(zipBuffer, "sticker-pack", {
836
- logger: options.logger,
837
- opts: options.options,
838
- }),
839
- prepareWAMessageMedia(
840
- { image: coverBuffer },
841
- { ...options, mediaTypeOverride: "image" }
842
- ),
843
- ])
844
-
845
- const stickerPackUploadResult = await options.upload(
846
- stickerPackUpload.encFilePath,
847
- {
848
- fileEncSha256B64: stickerPackUpload.fileEncSha256.toString(
849
- "base64"
850
- ),
851
- mediaType: "sticker-pack",
852
- timeoutMs: options.mediaUploadTimeoutMs,
853
- }
854
- )
855
-
856
- const coverImage = coverUpload.imageMessage
857
- const imageDataHash = sha256(coverBuffer).toString("base64")
858
- const stickerPackId = packId || generateMessageID()
859
-
860
- m.stickerPackMessage = {
861
- name,
862
- publisher,
863
- stickerPackId,
864
- packDescription: description,
865
- stickerPackOrigin:
866
- proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
867
- stickerPackSize: stickerPackUpload.fileLength,
868
- stickers: stickerMetadata,
869
- fileSha256: stickerPackUpload.fileSha256,
870
- fileEncSha256: stickerPackUpload.fileEncSha256,
871
- mediaKey: stickerPackUpload.mediaKey,
872
- directPath: stickerPackUploadResult.directPath,
873
- fileLength: stickerPackUpload.fileLength,
874
- mediaKeyTimestamp: unixTimestampSeconds(),
875
- trayIconFileName: `${stickerPackId}.png`,
876
- imageDataHash,
877
- thumbnailDirectPath: coverImage.directPath,
878
- thumbnailFileSha256: coverImage.fileSha256,
879
- thumbnailFileEncSha256: coverImage.fileEncSha256,
880
- thumbnailHeight: coverImage.height,
881
- thumbnailWidth: coverImage.width,
882
- }
883
- } else {
884
- m = await prepareWAMessageMedia(message, options)
313
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.REVOKE
314
+ };
885
315
  }
886
-
887
- if (hasNonNullishProperty(message, "buttonReply")) {
316
+ else if ('forward' in message) {
317
+ m = (0, exports.generateForwardMessageContent)(message.forward, message.force);
318
+ }
319
+ else if ('disappearingMessagesInChat' in message) {
320
+ const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
321
+ (message.disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
322
+ message.disappearingMessagesInChat;
323
+ m = (0, exports.prepareDisappearingMessageSettingContent)(exp);
324
+ }
325
+ else if ('buttonReply' in message) {
888
326
  switch (message.type) {
889
- case "list":
890
- m.listResponseMessage = {
891
- title: message.buttonReply.title,
892
- description: message.buttonReply.description,
893
- singleSelectReply: {
894
- selectedRowId: message.buttonReply.rowId,
895
- },
896
- lisType:
897
- proto.Message.ListResponseMessage.ListType
898
- .SINGLE_SELECT,
899
- }
900
- break
901
- case "template":
327
+ case 'template':
902
328
  m.templateButtonReplyMessage = {
903
329
  selectedDisplayText: message.buttonReply.displayText,
904
330
  selectedId: message.buttonReply.id,
905
331
  selectedIndex: message.buttonReply.index,
906
- }
907
- break
908
- case "plain":
332
+ };
333
+ break;
334
+ case 'plain':
909
335
  m.buttonsResponseMessage = {
910
336
  selectedButtonId: message.buttonReply.id,
911
337
  selectedDisplayText: message.buttonReply.displayText,
912
- type:
913
- proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT,
914
- }
915
- break
916
- case "interactive":
917
- m.interactiveResponseMessage = {
918
- body: {
919
- text: message.buttonReply.displayText,
920
- format:
921
- proto.Message.InteractiveResponseMessage.Body.Format
922
- .EXTENSIONS_1,
923
- },
924
- nativeFlowResponseMessage: {
925
- name: message.buttonReply.nativeFlows.name,
926
- paramsJson: message.buttonReply.nativeFlows.paramsJson,
927
- version: message.buttonReply.nativeFlows.version,
928
- },
929
- }
930
- break
931
- }
932
- } else if (hasNonNullishProperty(message, "sections")) {
933
- m.listMessage = {
934
- title: message.title,
935
- buttonText: message.buttonText,
936
- footerText: message.footer,
937
- description: message.text,
938
- sections: message.sections,
939
- listType: proto.Message.ListMessage.ListType.SINGLE_SELECT,
940
- }
941
- } else if (hasNonNullishProperty(message, "productList")) {
942
- const thumbnail = message.thumbnail
943
- ? await generateThumbnail(message.thumbnail, "image")
944
- : null
945
-
946
- m.listMessage = {
947
- title: message.title,
948
- buttonText: message.buttonText,
949
- footerText: message.footer,
950
- description: message.text,
951
- productListInfo: {
952
- productSections: message.productList,
953
- headerImage: {
954
- productId: message.productList[0].products[0].productId,
955
- jpegThumbnail: thumbnail?.thumbnail || null,
956
- },
957
- businessOwnerJid: message.businessOwnerJid,
958
- },
959
- listType: proto.Message.ListMessage.ListType.PRODUCT_LIST,
960
- }
961
- } else if (hasNonNullishProperty(message, "buttons")) {
962
- const buttonsMessage = {
963
- buttons: message.buttons.map((b) => ({
964
- ...b,
965
- type: proto.Message.ButtonsMessage.Button.Type.RESPONSE,
966
- })),
967
- }
968
-
969
- if (hasNonNullishProperty(message, "text")) {
970
- buttonsMessage.contentText = message.text
971
- buttonsMessage.headerType =
972
- proto.Message.ButtonsMessage.HeaderType.EMPTY
973
- } else {
974
- if (hasNonNullishProperty(message, "caption")) {
975
- buttonsMessage.contentText = message.caption
976
- }
977
-
978
- const type = Object.keys(m)[0].replace("Message", "").toUpperCase()
979
-
980
- buttonsMessage.headerType =
981
- proto.Message.ButtonsMessage.HeaderType[type]
982
-
983
- Object.assign(buttonsMessage, m)
984
- }
985
-
986
- if (hasNonNullishProperty(message, "title")) {
987
- buttonsMessage.text = message.title
988
- buttonsMessage.headerType =
989
- proto.Message.ButtonsMessage.HeaderType.TEXT
990
- }
991
-
992
- if (hasNonNullishProperty(message, "footer")) {
993
- buttonsMessage.footerText = message.footer
994
- }
995
-
996
- m = { buttonsMessage }
997
- } else if (hasNonNullishProperty(message, "templateButtons")) {
998
- const hydratedTemplate = {
999
- hydratedButtons: message.templateButtons,
1000
- }
1001
-
1002
- if (hasNonNullishProperty(message, "text")) {
1003
- hydratedTemplate.hydratedContentText = message.text
1004
- } else {
1005
- if (hasNonNullishProperty(message, "caption")) {
1006
- hydratedTemplate.hydratedContentText = message.caption
1007
- }
1008
-
1009
- Object.assign(msg, m)
1010
- }
1011
-
1012
- if (hasNonNullishProperty(message, "footer")) {
1013
- hydratedTemplate.hydratedFooterText = message.footer
1014
- }
1015
-
1016
- m = { templateMessage: { hydratedTemplate } }
1017
- } else if (hasNonNullishProperty(message, "interactiveButtons")) {
1018
- const interactiveMessage = {
1019
- nativeFlowMessage: {
1020
- buttons: message.interactiveButtons,
1021
- },
1022
- }
1023
-
1024
- if (hasNonNullishProperty(message, "text")) {
1025
- interactiveMessage.body = {
1026
- text: message.text,
1027
- }
1028
- }
1029
-
1030
- if (hasNonNullishProperty(message, "title")) {
1031
- interactiveMessage.header = {
1032
- title: message.title,
1033
- subtitle: null,
1034
- hasMediaAttachment: false,
1035
- }
1036
-
1037
- if (hasNonNullishProperty(message, "subtitle")) {
1038
- interactiveMessage.header.subtitle = message.subtitle
1039
- }
1040
- } else {
1041
- if (hasNonNullishProperty(message, "caption")) {
1042
- interactiveMessage.body = {
1043
- text: message.caption,
1044
- }
1045
-
1046
- interactiveMessage.header = {
1047
- title: null,
1048
- subtitle: null,
1049
- hasMediaAttachment: false,
1050
- ...Object.assign(interactiveMessage, m),
1051
- }
1052
-
1053
- if (hasNonNullishProperty(message, "title")) {
1054
- interactiveMessage.header.title = message.title
1055
- }
1056
-
1057
- if (hasNonNullishProperty(message, "subtitle")) {
1058
- interactiveMessage.header.subtitle = message.subtitle
1059
- }
1060
-
1061
- if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1062
- interactiveMessage.header.hasMediaAttachment = Boolean(
1063
- message.hasMediaAttachment
1064
- )
1065
- }
1066
- }
1067
- }
1068
-
1069
- if (hasNonNullishProperty(message, "footer")) {
1070
- interactiveMessage.footer = {
1071
- text: message.footer,
1072
- }
1073
- }
1074
-
1075
- m = { interactiveMessage }
1076
- } else if (hasNonNullishProperty(message, "shop")) {
1077
- const interactiveMessage = {
1078
- shopStorefrontMessage: {
1079
- surface: message.shop.surface,
1080
- id: message.shop.id,
1081
- },
1082
- }
1083
-
1084
- if (hasNonNullishProperty(message, "text")) {
1085
- interactiveMessage.body = {
1086
- text: message.text,
1087
- }
1088
- }
1089
-
1090
- if (hasNonNullishProperty(message, "title")) {
1091
- interactiveMessage.header = {
1092
- title: message.title,
1093
- subtitle: null,
1094
- hasMediaAttachment: false,
1095
- }
1096
-
1097
- if (hasNonNullishProperty(message, "subtitle")) {
1098
- interactiveMessage.header.subtitle = message.subtitle
1099
- }
1100
- } else {
1101
- if (hasNonNullishProperty(message, "caption")) {
1102
- interactiveMessage.body = {
1103
- text: message.caption,
1104
- }
1105
-
1106
- interactiveMessage.header = {
1107
- title: null,
1108
- subtitle: null,
1109
- hasMediaAttachment: false,
1110
- ...Object.assign(interactiveMessage, m),
1111
- }
1112
-
1113
- if (hasNonNullishProperty(message, "title")) {
1114
- interactiveMessage.header.title = message.title
1115
- }
1116
-
1117
- if (hasNonNullishProperty(message, "subtitle")) {
1118
- interactiveMessage.header.subtitle = message.subtitle
1119
- }
1120
-
1121
- if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1122
- interactiveMessage.header.hasMediaAttachment = Boolean(
1123
- message.hasMediaAttachment
1124
- )
1125
- }
1126
- }
338
+ type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT,
339
+ };
340
+ break;
1127
341
  }
1128
-
1129
- if (hasNonNullishProperty(message, "footer")) {
1130
- interactiveMessage.footer = {
1131
- text: message.footer,
342
+ }
343
+ else if ('product' in message) {
344
+ const { imageMessage } = await (0, exports.prepareWAMessageMedia)({ image: message.product.productImage }, options);
345
+ m.productMessage = Types_1.WAProto.Message.ProductMessage.fromObject({
346
+ ...message,
347
+ product: {
348
+ ...message.product,
349
+ productImage: imageMessage,
1132
350
  }
351
+ });
352
+ }
353
+ else if ('listReply' in message) {
354
+ m.listResponseMessage = { ...message.listReply };
355
+ }
356
+ else if ('poll' in message) {
357
+ (_b = message.poll).selectableCount || (_b.selectableCount = 0);
358
+ if (!Array.isArray(message.poll.values)) {
359
+ throw new boom_1.Boom('Invalid poll values', { statusCode: 400 });
1133
360
  }
1134
-
1135
- m = { interactiveMessage }
1136
- } else if (hasNonNullishProperty(message, "collection")) {
1137
- const interactiveMessage = {
1138
- collectionMessage: {
1139
- bizJid: message.collection.bizJid,
1140
- id: message.collection.id,
1141
- messageVersion: message?.collection?.version,
1142
- },
361
+ if (message.poll.selectableCount < 0
362
+ || message.poll.selectableCount > message.poll.values.length) {
363
+ throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, { statusCode: 400 });
1143
364
  }
1144
-
1145
- if (hasNonNullishProperty(message, "text")) {
1146
- interactiveMessage.body = {
1147
- text: message.text,
1148
- }
365
+ m.messageContextInfo = {
366
+ // encKey
367
+ messageSecret: message.poll.messageSecret || (0, crypto_1.randomBytes)(32),
368
+ };
369
+ m.pollCreationMessage = {
370
+ name: message.poll.name,
371
+ selectableOptionsCount: message.poll.selectableCount,
372
+ options: message.poll.values.map(optionName => ({ optionName })),
373
+ };
374
+ }
375
+ else if ('sharePhoneNumber' in message) {
376
+ m.protocolMessage = {
377
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
378
+ };
379
+ }
380
+ else if ('requestPhoneNumber' in message) {
381
+ m.requestPhoneNumberMessage = {};
382
+ }
383
+ else {
384
+ m = await (0, exports.prepareWAMessageMedia)(message, options);
385
+ }
386
+ if ('buttons' in message && !!message.buttons) {
387
+ const buttonsMessage = {
388
+ buttons: message.buttons.map(b => ({ ...b, type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
389
+ };
390
+ if ('text' in message) {
391
+ buttonsMessage.contentText = message.text;
392
+ buttonsMessage.headerType = ButtonType.EMPTY;
1149
393
  }
1150
-
1151
- if (hasNonNullishProperty(message, "title")) {
1152
- interactiveMessage.header = {
1153
- title: message.title,
1154
- subtitle: null,
1155
- hasMediaAttachment: false,
1156
- }
1157
-
1158
- if (hasNonNullishProperty(message, "subtitle")) {
1159
- interactiveMessage.header.subtitle = message.subtitle
1160
- }
1161
- } else {
1162
- if (hasNonNullishProperty(message, "caption")) {
1163
- interactiveMessage.body = {
1164
- text: message.caption,
1165
- }
1166
-
1167
- interactiveMessage.header = {
1168
- title: null,
1169
- subtitle: null,
1170
- hasMediaAttachment: false,
1171
- ...Object.assign(interactiveMessage, m),
1172
- }
1173
-
1174
- if (hasNonNullishProperty(message, "title")) {
1175
- interactiveMessage.header.title = message.title
1176
- }
1177
-
1178
- if (hasNonNullishProperty(message, "subtitle")) {
1179
- interactiveMessage.header.subtitle = message.subtitle
1180
- }
1181
-
1182
- if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1183
- interactiveMessage.header.hasMediaAttachment = Boolean(
1184
- message.hasMediaAttachment
1185
- )
1186
- }
394
+ else {
395
+ if ('caption' in message) {
396
+ buttonsMessage.contentText = message.caption;
1187
397
  }
398
+ const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
399
+ buttonsMessage.headerType = ButtonType[type];
400
+ Object.assign(buttonsMessage, m);
1188
401
  }
1189
-
1190
- if (hasNonNullishProperty(message, "footer")) {
1191
- interactiveMessage.footer = {
1192
- text: message.footer,
1193
- }
402
+ if ('footer' in message && !!message.footer) {
403
+ buttonsMessage.footerText = message.footer;
1194
404
  }
1195
-
1196
- m = { interactiveMessage }
1197
- } else if (hasNonNullishProperty(message, "cards")) {
1198
- const slides = await Promise.all(
1199
- message.cards.map(async (slide) => {
1200
- const {
1201
- image,
1202
- video,
1203
- product,
1204
- title,
1205
- body,
1206
- footer,
1207
- buttons,
1208
- } = slide
1209
-
1210
- let header
1211
-
1212
- if (product) {
1213
- const { imageMessage } = await prepareWAMessageMedia(
1214
- { image: product.productImage, ...options },
1215
- options
1216
- )
1217
- header = {
1218
- productMessage: {
1219
- product: {
1220
- ...product,
1221
- productImage: imageMessage,
1222
- },
1223
- ...slide,
1224
- },
1225
- }
1226
- } else if (image) {
1227
- header = await prepareWAMessageMedia(
1228
- { image: image, ...options },
1229
- options
1230
- )
1231
- } else if (video) {
1232
- header = await prepareWAMessageMedia(
1233
- { video: video, ...options },
1234
- options
1235
- )
1236
- }
1237
-
1238
- const msg = {
1239
- header: {
1240
- title,
1241
- hasMediaAttachment: true,
1242
- ...header,
1243
- },
1244
- body: {
1245
- text: body,
1246
- },
1247
- footer: {
1248
- text: footer,
1249
- },
1250
- nativeFlowMessage: {
1251
- buttons,
1252
- },
1253
- }
1254
-
1255
- return msg
1256
- })
1257
- )
1258
-
1259
- const interactiveMessage = {
1260
- carouselMessage: {
1261
- cards: slides,
1262
- },
405
+ m = { buttonsMessage };
406
+ }
407
+ else if ('templateButtons' in message && !!message.templateButtons) {
408
+ const msg = {
409
+ hydratedButtons: message.templateButtons
410
+ };
411
+ if ('text' in message) {
412
+ msg.hydratedContentText = message.text;
1263
413
  }
1264
-
1265
- if (hasNonNullishProperty(message, "text")) {
1266
- interactiveMessage.body = {
1267
- text: message.text,
414
+ else {
415
+ if ('caption' in message) {
416
+ msg.hydratedContentText = message.caption;
1268
417
  }
418
+ Object.assign(msg, m);
1269
419
  }
1270
-
1271
- if (hasNonNullishProperty(message, "title")) {
1272
- interactiveMessage.header = {
1273
- title: message.title,
1274
- subtitle: null,
1275
- hasMediaAttachment: false,
1276
- }
1277
-
1278
- if (hasNonNullishProperty(message, "subtitle")) {
1279
- interactiveMessage.header.subtitle = message.subtitle
1280
- }
1281
- } else {
1282
- if (hasNonNullishProperty(message, "caption")) {
1283
- interactiveMessage.body = {
1284
- text: message.caption,
1285
- }
1286
-
1287
- interactiveMessage.header = {
1288
- title: null,
1289
- subtitle: null,
1290
- hasMediaAttachment: false,
1291
- ...Object.assign(interactiveMessage, m),
1292
- }
1293
-
1294
- if (hasNonNullishProperty(message, "title")) {
1295
- interactiveMessage.header.title = message.title
1296
- }
1297
-
1298
- if (hasNonNullishProperty(message, "subtitle")) {
1299
- interactiveMessage.header.subtitle = message.subtitle
1300
- }
1301
-
1302
- if (hasNonNullishProperty(message, "hasMediaAttachment")) {
1303
- interactiveMessage.header.hasMediaAttachment = Boolean(
1304
- message.hasMediaAttachment
1305
- )
1306
- }
1307
- }
420
+ if ('footer' in message && !!message.footer) {
421
+ msg.hydratedFooterText = message.footer;
1308
422
  }
1309
-
1310
- if (hasNonNullishProperty(message, "footer")) {
1311
- interactiveMessage.footer = {
1312
- text: message.footer,
423
+ m = {
424
+ templateMessage: {
425
+ fourRowTemplate: msg,
426
+ hydratedTemplate: msg
1313
427
  }
1314
- }
1315
-
1316
- m = { interactiveMessage }
428
+ };
1317
429
  }
1318
-
1319
- if (hasOptionalProperty(message, "ephemeral")) {
1320
- m = { ephemeralMessage: { message: m } }
430
+ if ('sections' in message && !!message.sections) {
431
+ const listMessage = {
432
+ sections: message.sections,
433
+ buttonText: message.buttonText,
434
+ title: message.title,
435
+ footerText: message.footer,
436
+ description: message.text,
437
+ listType: WAProto_1.proto.Message.ListMessage.ListType.SINGLE_SELECT
438
+ };
439
+ m = { listMessage };
1321
440
  }
1322
-
1323
- if (hasOptionalProperty(message, "mentions") && message.mentions?.length) {
1324
- const messageType = Object.keys(m)[0]
1325
- const key = m[messageType]
1326
-
1327
- if ("contextInfo" in key && !!key.contextInfo) {
1328
- key.contextInfo.mentionedJid = message.mentions
1329
- } else if (key) {
1330
- key.contextInfo = {
1331
- mentionedJid: message.mentions,
1332
- }
1333
- }
441
+ if ('viewOnce' in message && !!message.viewOnce) {
442
+ m = { viewOnceMessage: { message: m } };
1334
443
  }
1335
-
1336
- if (hasOptionalProperty(message, "contextInfo") && !!message.contextInfo) {
1337
- const messageType = Object.keys(m)[0]
1338
- const key = m[messageType]
1339
-
1340
- if ("contextInfo" in key && !!key.contextInfo) {
1341
- key.contextInfo = { ...key.contextInfo, ...message.contextInfo }
1342
- } else if (key) {
1343
- key.contextInfo = message.contextInfo
1344
- }
444
+ if ('mentions' in message && ((_a = message.mentions) === null || _a === void 0 ? void 0 : _a.length)) {
445
+ const [messageType] = Object.keys(m);
446
+ m[messageType].contextInfo = m[messageType] || {};
447
+ m[messageType].contextInfo.mentionedJid = message.mentions;
1345
448
  }
1346
-
1347
- if (hasOptionalProperty(message, "edit")) {
449
+ if ('edit' in message) {
1348
450
  m = {
1349
451
  protocolMessage: {
1350
452
  key: message.edit,
1351
453
  editedMessage: m,
1352
454
  timestampMs: Date.now(),
1353
- type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT,
1354
- },
1355
- }
455
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
456
+ }
457
+ };
1356
458
  }
1357
-
1358
- return WAProto.Message.fromObject(m)
1359
- }
1360
-
459
+ if ('contextInfo' in message && !!message.contextInfo) {
460
+ const [messageType] = Object.keys(m);
461
+ m[messageType] = m[messageType] || {};
462
+ m[messageType].contextInfo = message.contextInfo;
463
+ }
464
+ return Types_1.WAProto.Message.fromObject(m);
465
+ };
466
+ exports.generateWAMessageContent = generateWAMessageContent;
1361
467
  const generateWAMessageFromContent = (jid, message, options) => {
468
+ // set timestamp to now
469
+ // if not specified
1362
470
  if (!options.timestamp) {
1363
- options.timestamp = new Date()
1364
- }
1365
-
1366
- const innerMessage = normalizeMessageContent(message)
1367
- const key = getContentType(innerMessage)
1368
- const timestamp = unixTimestampSeconds(options.timestamp)
1369
- const threadId = []
1370
- const { quoted, userJid } = options
1371
-
1372
- if (quoted && !isJidNewsletter(jid)) {
1373
- const participant = quoted.key.fromMe
1374
- ? userJid
1375
- : quoted.participant || quoted.key.participant || quoted.key.remoteJid
1376
-
1377
- let quotedMsg = normalizeMessageContent(quoted.message)
1378
- const msgType = getContentType(quotedMsg)
1379
-
1380
- quotedMsg = proto.Message.fromObject({ [msgType]: quotedMsg[msgType] })
1381
-
1382
- const quotedContent = quotedMsg[msgType]
1383
-
471
+ options.timestamp = new Date();
472
+ }
473
+ const innerMessage = (0, exports.normalizeMessageContent)(message);
474
+ const key = (0, exports.getContentType)(innerMessage);
475
+ const timestamp = (0, generics_1.unixTimestampSeconds)(options.timestamp);
476
+ const { quoted, userJid } = options;
477
+ if (quoted && !(0, WABinary_1.isJidNewsLetter)(jid)) {
478
+ const participant = quoted.key.fromMe ? userJid : (quoted.participant || quoted.key.participant || quoted.key.remoteJid);
479
+ let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message);
480
+ const msgType = (0, exports.getContentType)(quotedMsg);
481
+ // strip any redundant properties
482
+ quotedMsg = WAProto_1.proto.Message.fromObject({ [msgType]: quotedMsg[msgType] });
483
+ const quotedContent = quotedMsg[msgType];
1384
484
  if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
1385
- delete quotedContent.contextInfo
1386
- }
1387
-
1388
- let requestPayment
1389
-
1390
- if (key === 'requestPaymentMessage') {
1391
- if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage) {
1392
- requestPayment = innerMessage?.requestPaymentMessage?.noteMessage?.extendedTextMessage
1393
- } else if (innerMessage?.requestPaymentMessage && innerMessage?.requestPaymentMessage?.noteMessage?.stickerMessage) {
1394
- requestPayment = innerMessage.requestPaymentMessage?.noteMessage?.stickerMessage
1395
- }
1396
- }
1397
-
1398
- const contextInfo = (key === 'requestPaymentMessage' ? requestPayment?.contextInfo : innerMessage[key].contextInfo) || {}
1399
-
1400
- contextInfo.participant = jidNormalizedUser(participant)
1401
- contextInfo.stanzaId = quoted.key.id
1402
- contextInfo.quotedMessage = quotedMsg
1403
-
485
+ delete quotedContent.contextInfo;
486
+ }
487
+ const contextInfo = innerMessage[key].contextInfo || {};
488
+ contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant);
489
+ contextInfo.stanzaId = quoted.key.id;
490
+ contextInfo.quotedMessage = quotedMsg;
491
+ // if a participant is quoted, then it must be a group
492
+ // hence, remoteJid of group must also be entered
1404
493
  if (jid !== quoted.key.remoteJid) {
1405
- contextInfo.remoteJid = quoted.key.remoteJid
1406
- }
1407
-
1408
- if (contextInfo.quotedMessage) {
1409
- contextInfo.quotedType = 0
1410
- }
1411
-
1412
- if (contextInfo.quotedMessage && isJidGroup(jid)) {
1413
- threadId.push({
1414
- threadType: proto.ThreadID.ThreadType.VIEW_REPLIES,
1415
- threadKey: {
1416
- remoteJid: quoted?.key?.remoteJid,
1417
- fromMe: quoted?.key?.fromMe,
1418
- id: generateMessageID(),
1419
- ...(quoted?.key?.fromMe ? {} : { participant: quoted?.key?.participant })
1420
- }
1421
- })
1422
- }
1423
-
1424
- if (key === 'requestPaymentMessage' && requestPayment) {
1425
- requestPayment.contextInfo = contextInfo
1426
- } else {
1427
- innerMessage[key].contextInfo = contextInfo
494
+ contextInfo.remoteJid = quoted.key.remoteJid;
1428
495
  }
496
+ innerMessage[key].contextInfo = contextInfo;
1429
497
  }
1430
-
1431
- if (key !== 'protocolMessage' &&
498
+ if (
499
+ // if we want to send a disappearing message
500
+ !!(options === null || options === void 0 ? void 0 : options.ephemeralExpiration) &&
501
+ // and it's not a protocol message -- delete, toggle disappear message
502
+ key !== 'protocolMessage' &&
503
+ // already not converted to disappearing message
1432
504
  key !== 'ephemeralMessage' &&
1433
- !isJidNewsletter(jid)) {
1434
- message.messageContextInfo = {
1435
- threadId: threadId.length > 0 ? threadId : [],
1436
- messageSecret: randomBytes(32),
1437
- ...message.messageContextInfo
1438
- }
505
+ // newsletter not accept disappearing messages
506
+ !(0, WABinary_1.isJidNewsLetter)(jid)) {
1439
507
  innerMessage[key].contextInfo = {
1440
508
  ...(innerMessage[key].contextInfo || {}),
1441
- expiration: options.ephemeralExpiration ? options.ephemeralExpiration : 0
1442
- }
509
+ expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL,
510
+ //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
511
+ };
1443
512
  }
1444
-
1445
- message = WAProto.Message.fromObject(message)
1446
-
513
+ message = Types_1.WAProto.Message.fromObject(message);
1447
514
  const messageJSON = {
1448
515
  key: {
1449
516
  remoteJid: jid,
1450
517
  fromMe: true,
1451
- id: options?.messageId || generateMessageID()
518
+ id: (options === null || options === void 0 ? void 0 : options.messageId) || (0, generics_1.generateMessageID)(),
1452
519
  },
1453
520
  message: message,
1454
521
  messageTimestamp: timestamp,
1455
522
  messageStubParameters: [],
1456
- participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
1457
- status: WAMessageStatus.PENDING
1458
- }
1459
-
1460
- return WAProto.WebMessageInfo.fromObject(messageJSON)
1461
- }
1462
-
523
+ participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined,
524
+ status: Types_1.WAMessageStatus.PENDING
525
+ };
526
+ return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON);
527
+ };
528
+ exports.generateWAMessageFromContent = generateWAMessageFromContent;
1463
529
  const generateWAMessage = async (jid, content, options) => {
1464
- options.logger = options?.logger?.child({ msgId: options.messageId })
1465
-
1466
- return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { newsletter: isJidNewsletter(jid), ...options }), options)
1467
- }
1468
-
530
+ var _a;
531
+ // ensure msg ID is with every log
532
+ options.logger = (_a = options === null || options === void 0 ? void 0 : options.logger) === null || _a === void 0 ? void 0 : _a.child({ msgId: options.messageId });
533
+ return (0, exports.generateWAMessageFromContent)(jid, await (0, exports.generateWAMessageContent)(content, { newsletter: (0, WABinary_1.isJidNewsLetter)(jid), ...options }), options);
534
+ };
535
+ exports.generateWAMessage = generateWAMessage;
536
+ /** Get the key to access the true type of content */
1469
537
  const getContentType = (content) => {
1470
538
  if (content) {
1471
- const keys = Object.keys(content)
1472
- const key = keys.find(k => (k === 'conversation' || k.endsWith('Message') || k.endsWith('V2') || k.endsWith('V3') || k.endsWith('V4')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
1473
-
1474
- return key
539
+ const keys = Object.keys(content);
540
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
541
+ return key;
1475
542
  }
1476
- }
1477
-
543
+ };
544
+ exports.getContentType = getContentType;
1478
545
  /**
1479
546
  * Normalizes ephemeral, view once messages to regular message content
1480
547
  * Eg. image messages in ephemeral messages, in view once messages etc.
@@ -1483,171 +550,110 @@ const getContentType = (content) => {
1483
550
  */
1484
551
  const normalizeMessageContent = (content) => {
1485
552
  if (!content) {
1486
- return undefined
553
+ return undefined;
1487
554
  }
1488
-
555
+ // set max iterations to prevent an infinite loop
1489
556
  for (let i = 0; i < 5; i++) {
1490
- const inner = getFutureProofMessage(content)
557
+ const inner = getFutureProofMessage(content);
1491
558
  if (!inner) {
1492
- break
559
+ break;
1493
560
  }
1494
-
1495
- content = inner.message
561
+ content = inner.message;
1496
562
  }
1497
-
1498
- return content
1499
-
563
+ return content;
1500
564
  function getFutureProofMessage(message) {
1501
- return (
1502
- (message?.editedMessage)
1503
- || (message?.statusAddYours)
1504
- || (message?.botTaskMessage)
1505
- || (message?.eventCoverImage)
1506
- || (message?.questionMessage)
1507
- || (message?.viewOnceMessage)
1508
- || (message?.botInvokeMessage)
1509
- || (message?.ephemeralMessage)
1510
- || (message?.limitSharingMessage)
1511
- || (message?.viewOnceMessageV2)
1512
- || (message?.lottieStickerMessage)
1513
- || (message?.groupStatusMessage)
1514
- || (message?.questionReplyMessage)
1515
- || (message?.botForwardedMessage)
1516
- || (message?.statusMentionMessage)
1517
- || (message?.groupStatusMessageV2)
1518
- || (message?.pollCreationMessageV4)
1519
- || (message?.associatedChildMessage)
1520
- || (message?.groupMentionedMessage)
1521
- || (message?.groupStatusMentionMessage)
1522
- || (message?.viewOnceMessageV2Extension)
1523
- || (message?.documentWithCaptionMessage)
1524
- || (message?.pollCreationOptionImageMessage))
1525
- }
1526
- }
1527
-
565
+ return ((message === null || message === void 0 ? void 0 : message.ephemeralMessage)
566
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessage)
567
+ || (message === null || message === void 0 ? void 0 : message.documentWithCaptionMessage)
568
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2)
569
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension)
570
+ || (message === null || message === void 0 ? void 0 : message.editedMessage));
571
+ }
572
+ };
573
+ exports.normalizeMessageContent = normalizeMessageContent;
1528
574
  /**
1529
575
  * Extract the true message content from a message
1530
576
  * Eg. extracts the inner message from a disappearing message/view once message
1531
577
  */
1532
578
  const extractMessageContent = (content) => {
1533
- const extractFromButtonsMessage = (msg) => {
1534
- const header = typeof msg.header === 'object' && msg.header !== null
1535
-
1536
- if (header ? msg.header?.imageMessage : msg.imageMessage) {
1537
- return { imageMessage: header ? msg.header.imageMessage : msg.imageMessage }
579
+ var _a, _b, _c, _d, _e, _f;
580
+ const extractFromTemplateMessage = (msg) => {
581
+ if (msg.imageMessage) {
582
+ return { imageMessage: msg.imageMessage };
1538
583
  }
1539
-
1540
- else if (header ? msg.header?.documentMessage : msg.documentMessage) {
1541
- return { documentMessage: header ? msg.header.documentMessage : msg.documentMessage }
1542
- }
1543
-
1544
- else if (header ? msg.header?.videoMessage : msg.videoMessage) {
1545
- return { videoMessage: header ? msg.header.videoMessage: msg.videoMessage }
584
+ else if (msg.documentMessage) {
585
+ return { documentMessage: msg.documentMessage };
1546
586
  }
1547
-
1548
- else if (header ? msg.header?.locationMessage : msg.locationMessage) {
1549
- return { locationMessage: header ? msg.header.locationMessage : msg.locationMessage }
587
+ else if (msg.videoMessage) {
588
+ return { videoMessage: msg.videoMessage };
1550
589
  }
1551
-
1552
- else if (header ? msg.header?.productMessage : msg.productMessage) {
1553
- return { productMessage: header ? msg.header.productMessage : msg.productMessage }
590
+ else if (msg.locationMessage) {
591
+ return { locationMessage: msg.locationMessage };
1554
592
  }
1555
-
1556
593
  else {
1557
594
  return {
1558
595
  conversation: 'contentText' in msg
1559
596
  ? msg.contentText
1560
- : ('hydratedContentText' in msg ? msg.hydratedContentText : 'body' in msg ? msg.body.text : '')
1561
- }
597
+ : ('hydratedContentText' in msg ? msg.hydratedContentText : '')
598
+ };
1562
599
  }
600
+ };
601
+ content = (0, exports.normalizeMessageContent)(content);
602
+ if (content === null || content === void 0 ? void 0 : content.buttonsMessage) {
603
+ return extractFromTemplateMessage(content.buttonsMessage);
1563
604
  }
1564
-
1565
- content = normalizeMessageContent(content)
1566
-
1567
- if (content?.buttonsMessage) {
1568
- return extractFromButtonsMessage(content.buttonsMessage)
1569
- }
1570
-
1571
- if (content?.interactiveMessage) {
1572
- return extractFromButtonsMessage(content.interactiveMessage)
1573
- }
1574
-
1575
- if (content?.templateMessage?.interactiveMessageTemplate) {
1576
- return extractFromButtonsMessage(content?.templateMessage?.interactiveMessageTemplate)
605
+ if ((_a = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _a === void 0 ? void 0 : _a.hydratedFourRowTemplate) {
606
+ return extractFromTemplateMessage((_b = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _b === void 0 ? void 0 : _b.hydratedFourRowTemplate);
1577
607
  }
1578
-
1579
- if (content?.templateMessage?.hydratedFourRowTemplate) {
1580
- return extractFromButtonsMessage(content?.templateMessage?.hydratedFourRowTemplate)
608
+ if ((_c = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _c === void 0 ? void 0 : _c.hydratedTemplate) {
609
+ return extractFromTemplateMessage((_d = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _d === void 0 ? void 0 : _d.hydratedTemplate);
1581
610
  }
1582
-
1583
- if (content?.templateMessage?.hydratedTemplate) {
1584
- return extractFromButtonsMessage(content?.templateMessage?.hydratedTemplate)
611
+ if ((_e = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _e === void 0 ? void 0 : _e.fourRowTemplate) {
612
+ return extractFromTemplateMessage((_f = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _f === void 0 ? void 0 : _f.fourRowTemplate);
1585
613
  }
1586
-
1587
- if (content?.templateMessage?.fourRowTemplate) {
1588
- return extractFromButtonsMessage(content?.templateMessage?.fourRowTemplate)
1589
- }
1590
-
1591
- return content
1592
- }
1593
-
614
+ return content;
615
+ };
616
+ exports.extractMessageContent = extractMessageContent;
1594
617
  /**
1595
618
  * Returns the device predicted by message ID
1596
619
  */
1597
- const getDevice = (id) => /^3A.{18}$/.test(id) ? 'ios' :
1598
- /^3E.{20}$/.test(id) ? 'web' :
1599
- /^(.{21}|.{32})$/.test(id) ? 'android' :
1600
- /^(3F|.{18}$)/.test(id) ? 'desktop' :
1601
- 'baileys'
1602
-
620
+ const getDevice = (id) => /^3A.{18}$/.test(id) ? 'ios' : /^3E.{20}$/.test(id) ? 'web' : /^(.{21}|.{32})$/.test(id) ? 'android' : /^.{18}$/.test(id) ? 'desktop' : 'unknown';
621
+ exports.getDevice = getDevice;
1603
622
  /** Upserts a receipt in the message */
1604
623
  const updateMessageWithReceipt = (msg, receipt) => {
1605
- msg.userReceipt = msg.userReceipt || []
1606
- const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid)
1607
-
624
+ msg.userReceipt = msg.userReceipt || [];
625
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
1608
626
  if (recp) {
1609
- Object.assign(recp, receipt)
627
+ Object.assign(recp, receipt);
1610
628
  }
1611
-
1612
629
  else {
1613
- msg.userReceipt.push(receipt)
630
+ msg.userReceipt.push(receipt);
1614
631
  }
1615
- }
1616
-
632
+ };
633
+ exports.updateMessageWithReceipt = updateMessageWithReceipt;
1617
634
  /** Update the message with a new reaction */
1618
635
  const updateMessageWithReaction = (msg, reaction) => {
1619
- const authorID = getKeyAuthor(reaction.key)
636
+ const authorID = (0, generics_1.getKeyAuthor)(reaction.key);
1620
637
  const reactions = (msg.reactions || [])
1621
- .filter(r => getKeyAuthor(r.key) !== authorID)
1622
-
1623
- reaction.text = reaction.text || ''
1624
- reactions.push(reaction)
1625
- msg.reactions = reactions
1626
- }
1627
-
638
+ .filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID);
639
+ if (reaction.text) {
640
+ reactions.push(reaction);
641
+ }
642
+ msg.reactions = reactions;
643
+ };
644
+ exports.updateMessageWithReaction = updateMessageWithReaction;
1628
645
  /** Update the message with a new poll update */
1629
646
  const updateMessageWithPollUpdate = (msg, update) => {
1630
- const authorID = getKeyAuthor(update.pollUpdateMessageKey)
1631
- const votes = (msg.pollUpdates || [])
1632
- .filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID)
1633
-
1634
- if (update.vote?.selectedOptions?.length) {
1635
- votes.push(update)
1636
- }
1637
-
1638
- msg.pollUpdates = votes
1639
- }
1640
-
1641
- /** Update the message with a new event response*/
1642
- const updateMessageWithEventResponse = (msg, update) => {
1643
- const authorID = getKeyAuthor(update.eventResponseMessageKey)
1644
- const responses = (msg.eventResponses || [])
1645
- .filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID)
1646
-
1647
- responses.push(update)
1648
- msg.eventResponses = responses
1649
- }
1650
-
647
+ var _a, _b;
648
+ const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey);
649
+ const reactions = (msg.pollUpdates || [])
650
+ .filter(r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
651
+ if ((_b = (_a = update.vote) === null || _a === void 0 ? void 0 : _a.selectedOptions) === null || _b === void 0 ? void 0 : _b.length) {
652
+ reactions.push(update);
653
+ }
654
+ msg.pollUpdates = reactions;
655
+ };
656
+ exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate;
1651
657
  /**
1652
658
  * Aggregates all poll updates in a poll.
1653
659
  * @param msg the poll creation message
@@ -1655,204 +661,124 @@ const updateMessageWithEventResponse = (msg, update) => {
1655
661
  * @returns A list of options & their voters
1656
662
  */
1657
663
  function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
1658
- message = normalizeMessageContent(message)
1659
-
1660
- const opts = message?.pollCreationMessage?.options || message?.pollCreationMessageV2?.options || message?.pollCreationMessageV3?.options || []
1661
-
664
+ var _a, _b, _c;
665
+ const opts = ((_a = message === null || message === void 0 ? void 0 : message.pollCreationMessage) === null || _a === void 0 ? void 0 : _a.options) || ((_b = message === null || message === void 0 ? void 0 : message.pollCreationMessageV2) === null || _b === void 0 ? void 0 : _b.options) || ((_c = message === null || message === void 0 ? void 0 : message.pollCreationMessageV3) === null || _c === void 0 ? void 0 : _c.options) || [];
1662
666
  const voteHashMap = opts.reduce((acc, opt) => {
1663
- const hash = sha256(Buffer.from(opt.optionName || '')).toString()
667
+ const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString();
1664
668
  acc[hash] = {
1665
669
  name: opt.optionName || '',
1666
670
  voters: []
1667
- }
1668
-
1669
- return acc
1670
- }, {})
1671
-
671
+ };
672
+ return acc;
673
+ }, {});
1672
674
  for (const update of pollUpdates || []) {
1673
- const { vote } = update
1674
-
675
+ const { vote } = update;
1675
676
  if (!vote) {
1676
- continue
677
+ continue;
1677
678
  }
1678
-
1679
679
  for (const option of vote.selectedOptions || []) {
1680
- const hash = option.toString()
1681
- let data = voteHashMap[hash]
1682
-
680
+ const hash = option.toString();
681
+ let data = voteHashMap[hash];
1683
682
  if (!data) {
1684
683
  voteHashMap[hash] = {
1685
684
  name: 'Unknown',
1686
685
  voters: []
1687
- }
1688
-
1689
- data = voteHashMap[hash]
686
+ };
687
+ data = voteHashMap[hash];
1690
688
  }
1691
-
1692
- voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId))
689
+ voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId));
1693
690
  }
1694
691
  }
1695
-
1696
- return Object.values(voteHashMap)
1697
- }
1698
-
1699
- /**
1700
- * Aggregates all event responses in an event message.
1701
- * @param msg the event creation message
1702
- * @param meLid your lid
1703
- * @returns A list of response types & their responders
1704
- */
1705
- function getAggregateResponsesInEventMessage({ eventResponses }, meLid) {
1706
- const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE']
1707
- const responseMap = {}
1708
-
1709
- for (const type of responseTypes) {
1710
- responseMap[type] = {
1711
- response: type,
1712
- responders: []
1713
- }
1714
- }
1715
-
1716
- for (const update of eventResponses) {
1717
- const { response } = update.response || 0
1718
- const responseType = proto.Message.EventResponseMessage.EventResponseType[response]
1719
- if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
1720
- responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meLid))
1721
- }
1722
- }
1723
-
1724
- return Object.values(responseMap)
692
+ return Object.values(voteHashMap);
1725
693
  }
1726
-
694
+ exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage;
1727
695
  /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
1728
696
  const aggregateMessageKeysNotFromMe = (keys) => {
1729
- const keyMap = {}
1730
-
697
+ const keyMap = {};
1731
698
  for (const { remoteJid, id, participant, fromMe } of keys) {
1732
699
  if (!fromMe) {
1733
- const uqKey = `${remoteJid}:${participant || ''}`
1734
-
700
+ const uqKey = `${remoteJid}:${participant || ''}`;
1735
701
  if (!keyMap[uqKey]) {
1736
702
  keyMap[uqKey] = {
1737
703
  jid: remoteJid,
1738
704
  participant: participant,
1739
705
  messageIds: []
1740
- }
706
+ };
1741
707
  }
1742
-
1743
- keyMap[uqKey].messageIds.push(id)
708
+ keyMap[uqKey].messageIds.push(id);
1744
709
  }
1745
710
  }
1746
-
1747
- return Object.values(keyMap)
1748
- }
1749
-
1750
- const REUPLOAD_REQUIRED_STATUS = [410, 404]
1751
-
711
+ return Object.values(keyMap);
712
+ };
713
+ exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
714
+ const REUPLOAD_REQUIRED_STATUS = [410, 404];
1752
715
  /**
1753
716
  * Downloads the given message. Throws an error if it's not a media message
1754
717
  */
1755
718
  const downloadMediaMessage = async (message, type, options, ctx) => {
1756
- const result = await downloadMsg().catch(async (error) => {
1757
- if (ctx &&
1758
- typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
1759
- REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
1760
- ctx.logger.info({ key: message.key }, 'sending reupload media request...')
1761
-
1762
- // request reupload
1763
- message = await ctx.reuploadRequest(message)
1764
-
1765
- const result = await downloadMsg()
1766
- return result
1767
- }
1768
-
1769
- throw error
1770
- })
1771
-
1772
- return result
1773
-
719
+ const result = await downloadMsg()
720
+ .catch(async (error) => {
721
+ var _a;
722
+ if (ctx) {
723
+ if (axios_1.default.isAxiosError(error)) {
724
+ // check if the message requires a reupload
725
+ if (REUPLOAD_REQUIRED_STATUS.includes((_a = error.response) === null || _a === void 0 ? void 0 : _a.status)) {
726
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...');
727
+ // request reupload
728
+ message = await ctx.reuploadRequest(message);
729
+ const result = await downloadMsg();
730
+ return result;
731
+ }
732
+ }
733
+ }
734
+ throw error;
735
+ });
736
+ return result;
1774
737
  async function downloadMsg() {
1775
- const mContent = extractMessageContent(message.message)
1776
-
738
+ const mContent = (0, exports.extractMessageContent)(message.message);
1777
739
  if (!mContent) {
1778
- throw new Boom('No message present', { statusCode: 400, data: message })
740
+ throw new boom_1.Boom('No message present', { statusCode: 400, data: message });
1779
741
  }
1780
-
1781
- const contentType = getContentType(mContent)
1782
-
1783
- let mediaType = contentType?.replace('Message', '')
1784
-
1785
- const media = contentType === 'productMessage' ? mContent[contentType]?.product?.productImage : mContent[contentType]
1786
-
742
+ const contentType = (0, exports.getContentType)(mContent);
743
+ let mediaType = contentType === null || contentType === void 0 ? void 0 : contentType.replace('Message', '');
744
+ const media = mContent[contentType];
1787
745
  if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
1788
- throw new Boom(`"${contentType}" message is not a media message`)
746
+ throw new boom_1.Boom(`"${contentType}" message is not a media message`);
1789
747
  }
1790
-
1791
- let download
1792
-
748
+ let download;
1793
749
  if ('thumbnailDirectPath' in media && !('url' in media)) {
1794
750
  download = {
1795
751
  directPath: media.thumbnailDirectPath,
1796
752
  mediaKey: media.mediaKey
1797
- }
1798
- mediaType = 'thumbnail-link'
753
+ };
754
+ mediaType = 'thumbnail-link';
1799
755
  }
1800
756
  else {
1801
- download = media
757
+ download = media;
1802
758
  }
1803
-
1804
- const stream = await downloadContentFromMessage(download, mediaType, options)
1805
-
759
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options);
1806
760
  if (type === 'buffer') {
1807
- const bufferArray = []
761
+ const bufferArray = [];
1808
762
  for await (const chunk of stream) {
1809
763
  bufferArray.push(chunk);
1810
764
  }
1811
765
  return Buffer.concat(bufferArray);
1812
766
  }
1813
- return stream
767
+ return stream;
1814
768
  }
1815
- }
1816
-
769
+ };
770
+ exports.downloadMediaMessage = downloadMediaMessage;
1817
771
  /** Checks whether the given message is a media message; if it is returns the inner content */
1818
772
  const assertMediaContent = (content) => {
1819
- content = extractMessageContent(content)
1820
-
1821
- const mediaContent = content?.documentMessage ||
1822
- content?.imageMessage ||
1823
- content?.videoMessage ||
1824
- content?.audioMessage ||
1825
- content?.stickerMessage
1826
-
773
+ content = (0, exports.extractMessageContent)(content);
774
+ const mediaContent = (content === null || content === void 0 ? void 0 : content.documentMessage)
775
+ || (content === null || content === void 0 ? void 0 : content.imageMessage)
776
+ || (content === null || content === void 0 ? void 0 : content.videoMessage)
777
+ || (content === null || content === void 0 ? void 0 : content.audioMessage)
778
+ || (content === null || content === void 0 ? void 0 : content.stickerMessage);
1827
779
  if (!mediaContent) {
1828
- throw new Boom('given message is not a media message', { statusCode: 400, data: content });
780
+ throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content });
1829
781
  }
1830
-
1831
- return mediaContent
1832
- }
1833
-
1834
- module.exports = {
1835
- extractUrlFromText,
1836
- generateLinkPreviewIfRequired,
1837
- prepareWAMessageMedia,
1838
- prepareAlbumMessageContent,
1839
- prepareDisappearingMessageSettingContent,
1840
- generateForwardMessageContent,
1841
- generateWAMessageContent,
1842
- generateWAMessageFromContent,
1843
- generateWAMessage,
1844
- getContentType,
1845
- hasNonNullishProperty,
1846
- normalizeMessageContent,
1847
- extractMessageContent,
1848
- getDevice,
1849
- updateMessageWithReceipt,
1850
- updateMessageWithReaction,
1851
- updateMessageWithPollUpdate,
1852
- updateMessageWithEventResponse,
1853
- getAggregateVotesInPollMessage,
1854
- getAggregateResponsesInEventMessage,
1855
- aggregateMessageKeysNotFromMe,
1856
- downloadMediaMessage,
1857
- assertMediaContent
1858
- }
782
+ return mediaContent;
783
+ };
784
+ exports.assertMediaContent = assertMediaContent;