@gqb333/based 2.7.83 → 3.0.0

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.
@@ -16,7 +16,6 @@ const WABinary_1 = require("../WABinary");
16
16
  const crypto_2 = require("./crypto");
17
17
  const generics_1 = require("./generics");
18
18
  const messages_media_1 = require("./messages-media");
19
-
20
19
  const MIMETYPE_MAP = {
21
20
  image: 'image/jpeg',
22
21
  video: 'video/mp4',
@@ -25,7 +24,6 @@ const MIMETYPE_MAP = {
25
24
  sticker: 'image/webp',
26
25
  'product-catalog-image': 'image/jpeg',
27
26
  };
28
-
29
27
  const MessageTypeProto = {
30
28
  'image': Types_1.WAProto.Message.ImageMessage,
31
29
  'video': Types_1.WAProto.Message.VideoMessage,
@@ -33,16 +31,14 @@ const MessageTypeProto = {
33
31
  'sticker': Types_1.WAProto.Message.StickerMessage,
34
32
  'document': Types_1.WAProto.Message.DocumentMessage,
35
33
  };
36
-
37
34
  const ButtonType = WAProto_1.proto.Message.ButtonsMessage.HeaderType;
38
-
39
- const mediaCache = new Map();
40
- const CACHE_MAX_SIZE = 100;
41
- const CACHE_TTL = 3600000;
42
-
35
+ /**
36
+ * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
37
+ * @param text eg. hello https://google.com
38
+ * @returns the URL, eg. https://google.com
39
+ */
43
40
  const extractUrlFromText = (text) => { var _a; return (_a = text.match(Defaults_1.URL_REGEX)) === null || _a === void 0 ? void 0 : _a[0]; };
44
41
  exports.extractUrlFromText = extractUrlFromText;
45
-
46
42
  const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
47
43
  const url = (0, exports.extractUrlFromText)(text);
48
44
  if (!!getUrlInfo && url) {
@@ -50,13 +46,12 @@ const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
50
46
  const urlInfo = await getUrlInfo(url);
51
47
  return urlInfo;
52
48
  }
53
- catch (error) {
49
+ catch (error) { // ignore if fails
54
50
  logger === null || logger === void 0 ? void 0 : logger.warn({ trace: error.stack }, 'url generation failed');
55
51
  }
56
52
  }
57
53
  };
58
54
  exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired;
59
-
60
55
  const assertColor = async (color) => {
61
56
  let assertedColor;
62
57
  if (typeof color === 'number') {
@@ -72,88 +67,61 @@ const assertColor = async (color) => {
72
67
  return assertedColor;
73
68
  }
74
69
  };
75
-
76
- const cleanMediaCache = () => {
77
- if (mediaCache.size > CACHE_MAX_SIZE) {
78
- const entriesToDelete = mediaCache.size - CACHE_MAX_SIZE;
79
- const keys = Array.from(mediaCache.keys()).slice(0, entriesToDelete);
80
- keys.forEach(key => mediaCache.delete(key));
81
- }
82
- };
83
-
84
70
  const prepareWAMessageMedia = async (message, options) => {
85
71
  const logger = options.logger;
86
72
  let mediaType;
87
-
88
73
  for (const key of Defaults_1.MEDIA_KEYS) {
89
74
  if (key in message) {
90
75
  mediaType = key;
91
- break;
92
76
  }
93
77
  }
94
-
95
78
  if (!mediaType) {
96
79
  throw new boom_1.Boom('Invalid media type', { statusCode: 400 });
97
80
  }
98
-
99
81
  const uploadData = {
100
82
  ...message,
101
83
  media: message[mediaType]
102
84
  };
103
85
  delete uploadData[mediaType];
104
-
86
+ // check if cacheable + generate cache key
105
87
  const cacheableKey = typeof uploadData.media === 'object' &&
106
88
  ('url' in uploadData.media) &&
107
89
  !!uploadData.media.url &&
108
90
  !!options.mediaCache && (
109
- mediaType + ':' + uploadData.media.url.toString());
110
-
91
+ // generate the key
92
+ mediaType + ':' + uploadData.media.url.toString());
111
93
  if (mediaType === 'document' && !uploadData.fileName) {
112
94
  uploadData.fileName = 'file';
113
95
  }
114
-
115
96
  if (!uploadData.mimetype) {
116
97
  uploadData.mimetype = MIMETYPE_MAP[mediaType];
117
98
  }
118
-
99
+ // check for cache hit
119
100
  if (cacheableKey) {
120
- const cached = mediaCache.get(cacheableKey);
121
- if (cached && (Date.now() - cached.timestamp < CACHE_TTL)) {
122
- logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'media cache hit');
123
- const obj = Types_1.WAProto.Message.decode(cached.data);
101
+ const mediaBuff = options.mediaCache.get(cacheableKey);
102
+ if (mediaBuff) {
103
+ logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'got media cache hit');
104
+ const obj = Types_1.WAProto.Message.decode(mediaBuff);
124
105
  const key = `${mediaType}Message`;
125
106
  Object.assign(obj[key], { ...uploadData, media: undefined });
126
107
  return obj;
127
108
  }
128
109
  }
129
-
130
110
  const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
131
111
  const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
132
112
  (typeof uploadData['jpegThumbnail'] === 'undefined');
133
113
  const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
134
114
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
135
115
  const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
136
-
137
- const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath } =
138
- await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(
139
- uploadData.media,
140
- options.mediaTypeOverride || mediaType,
141
- {
142
- logger,
143
- saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
144
- opts: options.options
145
- }
146
- );
147
-
116
+ const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath, } = await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
117
+ logger,
118
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
119
+ opts: options.options
120
+ });
148
121
  const fileEncSha256B64 = (options.newsletter ? fileSha256 : fileEncSha256 !== null && fileEncSha256 !== void 0 ? fileEncSha256 : fileSha256).toString('base64');
149
-
150
122
  const [{ mediaUrl, directPath, handle }] = await Promise.all([
151
123
  (async () => {
152
- const result = await options.upload(encWriteStream, {
153
- fileEncSha256B64,
154
- mediaType,
155
- timeoutMs: options.mediaUploadTimeoutMs || 60000
156
- });
124
+ const result = await options.upload(encWriteStream, { fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs });
157
125
  logger === null || logger === void 0 ? void 0 : logger.debug({ mediaType, cacheableKey }, 'uploaded media');
158
126
  return result;
159
127
  })(),
@@ -186,60 +154,160 @@ const prepareWAMessageMedia = async (message, options) => {
186
154
  logger === null || logger === void 0 ? void 0 : logger.warn({ trace: error.stack }, 'failed to obtain extra info');
187
155
  }
188
156
  })(),
189
- ]).finally(async () => {
157
+ ])
158
+ .finally(async () => {
190
159
  if (!Buffer.isBuffer(encWriteStream)) {
191
160
  encWriteStream.destroy();
192
161
  }
162
+ // remove tmp files
193
163
  if (didSaveToTmpPath && bodyPath) {
194
164
  try {
195
165
  await fs_1.promises.access(bodyPath);
196
166
  await fs_1.promises.unlink(bodyPath);
167
+ logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp file');
168
+ }
169
+ catch (error) {
170
+ logger === null || logger === void 0 ? void 0 : logger.warn('failed to remove tmp file');
197
171
  }
198
- catch { }
199
172
  }
200
173
  });
201
-
202
- const message = MessageTypeProto[mediaType].fromObject({
203
- url: mediaUrl,
204
- directPath,
205
- mediaKey,
206
- fileEncSha256: options.newsletter ? fileSha256 : fileEncSha256 !== null && fileEncSha256 !== void 0 ? fileEncSha256 : fileSha256,
207
- fileSha256: fileSha256,
208
- fileLength: fileLength,
209
- mediaKeyTimestamp: (0, generics_1.unixTimestampSeconds)(),
210
- handle,
211
- ...uploadData,
212
- media: undefined
213
- });
214
-
215
174
  const obj = Types_1.WAProto.Message.fromObject({
216
- [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject(message)
175
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
176
+ url: handle ? undefined : mediaUrl,
177
+ directPath,
178
+ mediaKey: mediaKey,
179
+ fileEncSha256: fileEncSha256,
180
+ fileSha256,
181
+ fileLength,
182
+ mediaKeyTimestamp: handle ? undefined : (0, generics_1.unixTimestampSeconds)(),
183
+ ...uploadData,
184
+ media: undefined
185
+ })
217
186
  });
218
-
187
+ if (uploadData.ptv) {
188
+ obj.ptvMessage = obj.videoMessage;
189
+ delete obj.videoMessage;
190
+ }
219
191
  if (cacheableKey) {
220
- const encoded = Types_1.WAProto.Message.encode(obj).finish();
221
- mediaCache.set(cacheableKey, {
222
- data: encoded,
223
- timestamp: Date.now()
224
- });
225
- cleanMediaCache();
192
+ logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'set cache');
193
+ options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish());
226
194
  }
227
-
228
195
  return obj;
229
196
  };
230
197
  exports.prepareWAMessageMedia = prepareWAMessageMedia;
231
-
198
+ const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
199
+ ephemeralExpiration = ephemeralExpiration || 0;
200
+ const content = {
201
+ ephemeralMessage: {
202
+ message: {
203
+ protocolMessage: {
204
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
205
+ ephemeralExpiration
206
+ }
207
+ }
208
+ }
209
+ };
210
+ return Types_1.WAProto.Message.fromObject(content);
211
+ };
212
+ exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
213
+ /**
214
+ * Generate forwarded message content like WA does
215
+ * @param message the message to forward
216
+ * @param options.forceForward will show the message as forwarded even if it is from you
217
+ */
218
+ const generateForwardMessageContent = (message, forceForward) => {
219
+ var _a;
220
+ let content = message.message;
221
+ if (!content) {
222
+ throw new boom_1.Boom('no content in message', { statusCode: 400 });
223
+ }
224
+ // hacky copy
225
+ content = (0, exports.normalizeMessageContent)(content);
226
+ content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish());
227
+ let key = Object.keys(content)[0];
228
+ let score = ((_a = content[key].contextInfo) === null || _a === void 0 ? void 0 : _a.forwardingScore) || 0;
229
+ score += message.key.fromMe && !forceForward ? 0 : 1;
230
+ if (key === 'conversation') {
231
+ content.extendedTextMessage = { text: content[key] };
232
+ delete content.conversation;
233
+ key = 'extendedTextMessage';
234
+ }
235
+ if (score > 0) {
236
+ content[key].contextInfo = { forwardingScore: score, isForwarded: true };
237
+ }
238
+ else {
239
+ content[key].contextInfo = {};
240
+ }
241
+ return content;
242
+ };
243
+ exports.generateForwardMessageContent = generateForwardMessageContent;
232
244
  const generateWAMessageContent = async (message, options) => {
245
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
246
+ var _p, _q;
247
+
248
+ // Cross-platform externalAdReply thumbnail handling
249
+ const fixupExternalAdReplyThumb = async (externalAdReply) => {
250
+ const thumbUrl = externalAdReply.originalImageUrl || externalAdReply.thumbnailUrl;
251
+ const currentThumb = externalAdReply.thumbnail;
252
+ const currentThumbLen = currentThumb && typeof currentThumb.length === 'number' ? currentThumb.length : 0;
253
+ if (thumbUrl && (!currentThumb || currentThumbLen < 2000)) {
254
+ try {
255
+ const stream = await (0, messages_media_1.getHttpStream)(thumbUrl, {
256
+ timeout: 8000,
257
+ headers: {
258
+ 'User-Agent': 'WhatsApp/2.23.20.15 iOS/16.0 Device/iPhone'
259
+ }
260
+ });
261
+ const { buffer } = await (0, messages_media_1.extractImageThumb)(stream, 512, 80);
262
+ externalAdReply.thumbnail = buffer;
263
+ }
264
+ catch (error) {
265
+ options.logger?.warn('Failed to generate externalAdReply thumbnail for cross-platform compatibility:', error.message);
266
+ }
267
+ }
268
+ if (externalAdReply.renderLargerThumbnail === undefined) {
269
+ externalAdReply.renderLargerThumbnail = true;
270
+ }
271
+ return externalAdReply;
272
+ };
273
+ if (message.contextInfo?.externalAdReply) {
274
+ message.contextInfo.externalAdReply = await fixupExternalAdReplyThumb(message.contextInfo.externalAdReply);
275
+ }
276
+ if (message.externalAdReply) {
277
+ message.externalAdReply = await fixupExternalAdReplyThumb(message.externalAdReply);
278
+ }
279
+
233
280
  let m = {};
234
-
235
281
  if ('text' in message) {
236
- const extContent = await (0, exports.generateLinkPreviewIfRequired)(message.text, options.getUrlInfo, options.logger);
237
- m.extendedTextMessage = { text: message.text };
238
- if (extContent) {
239
- m.extendedTextMessage.contextInfo = {
240
- externalAdReply: extContent
241
- };
282
+ const extContent = { text: message.text };
283
+ let urlInfo = message.linkPreview;
284
+ if (urlInfo === true) {
285
+ urlInfo = await (0, exports.generateLinkPreviewIfRequired)(message.text, options.getUrlInfo, options.logger);
286
+ }
287
+ if (urlInfo && typeof urlInfo === 'object') {
288
+ extContent.matchedText = urlInfo['matched-text'];
289
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail;
290
+ extContent.description = urlInfo.description;
291
+ extContent.title = urlInfo.title;
292
+ extContent.previewType = 0;
293
+ const img = urlInfo.highQualityThumbnail;
294
+ if (img) {
295
+ extContent.thumbnailDirectPath = img.directPath;
296
+ extContent.mediaKey = img.mediaKey;
297
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
298
+ extContent.thumbnailWidth = img.width;
299
+ extContent.thumbnailHeight = img.height;
300
+ extContent.thumbnailSha256 = img.fileSha256;
301
+ extContent.thumbnailEncSha256 = img.fileEncSha256;
302
+ }
303
+ }
304
+ if (options.backgroundColor) {
305
+ extContent.backgroundArgb = await assertColor(options.backgroundColor);
242
306
  }
307
+ if (options.font) {
308
+ extContent.font = options.font;
309
+ }
310
+ m.extendedTextMessage = extContent;
243
311
  }
244
312
  else if ('contacts' in message) {
245
313
  const contactLen = message.contacts.contacts.length;
@@ -256,7 +324,13 @@ const generateWAMessageContent = async (message, options) => {
256
324
  else if ('location' in message) {
257
325
  m.locationMessage = Types_1.WAProto.Message.LocationMessage.fromObject(message.location);
258
326
  }
327
+ else if ('liveLocation' in message) {
328
+ m.liveLocationMessage = Types_1.WAProto.Message.LiveLocationMessage.fromObject(message.liveLocation);
329
+ }
259
330
  else if ('react' in message) {
331
+ if (!message.react.senderTimestampMs) {
332
+ message.react.senderTimestampMs = Date.now();
333
+ }
260
334
  m.reactionMessage = Types_1.WAProto.Message.ReactionMessage.fromObject(message.react);
261
335
  }
262
336
  else if ('delete' in message) {
@@ -265,66 +339,948 @@ const generateWAMessageContent = async (message, options) => {
265
339
  type: Types_1.WAProto.Message.ProtocolMessage.Type.REVOKE
266
340
  };
267
341
  }
268
- else if ('forward' in message || 'copy' in message) {
269
- const content = (0, exports.normalizeMessageContent)(message.forward || message.copy);
270
- if (!content) {
271
- throw new boom_1.Boom('no content present in forwarded/copied message', { statusCode: 400 });
342
+ else if ('forward' in message) {
343
+ m = (0, exports.generateForwardMessageContent)(message.forward, message.force);
344
+ }
345
+ else if ('disappearingMessagesInChat' in message) {
346
+ const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
347
+ (message.disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
348
+ message.disappearingMessagesInChat;
349
+ m = (0, exports.prepareDisappearingMessageSettingContent)(exp);
350
+ }
351
+ else if ('groupInvite' in message) {
352
+ m.groupInviteMessage = {};
353
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
354
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
355
+ m.groupInviteMessage.caption = message.groupInvite.text;
356
+ m.groupInviteMessage.groupJid = message.groupInvite.jid;
357
+ m.groupInviteMessage.groupName = message.groupInvite.subject;
358
+
359
+ // Get group metadata to obtain disappearing mode and other group info
360
+ if (options.groupMetadata) {
361
+ try {
362
+ const groupMetadata = await options.groupMetadata(message.groupInvite.jid);
363
+ if (groupMetadata) {
364
+ // Add disappearing mode info if available
365
+ if (groupMetadata.ephemeralDuration !== undefined) {
366
+ m.groupInviteMessage.ephemeralDuration = groupMetadata.ephemeralDuration;
367
+ }
368
+ // Add group subject from metadata if not provided in message
369
+ if (!m.groupInviteMessage.groupName && groupMetadata.subject) {
370
+ m.groupInviteMessage.groupName = groupMetadata.subject;
371
+ }
372
+ // Add group participant count
373
+ if (groupMetadata.participants) {
374
+ m.groupInviteMessage.groupSize = groupMetadata.participants.length;
375
+ }
376
+ }
377
+ } catch (error) {
378
+ options.logger?.debug({ error, jid: message.groupInvite.jid }, 'Failed to fetch group metadata for invite');
379
+ }
272
380
  }
273
- let key = Object.keys(content)[0];
274
- let score = content[key].contextInfo?.forwardingScore || 0;
275
- score += message.force ? 0 : 1;
276
- if (key === 'conversation') {
277
- m.extendedTextMessage = {
278
- text: content[key]
381
+
382
+ // Handle profile picture with caching
383
+ if (options.getProfilePicUrl) {
384
+ const cacheKey = `group_pfp:${message.groupInvite.jid}`;
385
+
386
+ // Check cache first if available
387
+ if (options.cache && options.cache.get) {
388
+ const cachedPfp = options.cache.get(cacheKey);
389
+ if (cachedPfp) {
390
+ m.groupInviteMessage.jpegThumbnail = cachedPfp;
391
+ }
392
+ }
393
+
394
+ // Fetch if not in cache
395
+ if (!m.groupInviteMessage.jpegThumbnail) {
396
+ try {
397
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
398
+ if (pfpUrl) {
399
+ const resp = await axios_1.default.get(pfpUrl, {
400
+ responseType: 'arraybuffer',
401
+ timeout: 5000,
402
+ headers: {
403
+ 'User-Agent': 'WhatsApp/2.23.20.15 iOS/16.0 Device/iPhone'
404
+ }
405
+ });
406
+ if (resp.status === 200) {
407
+ m.groupInviteMessage.jpegThumbnail = resp.data;
408
+
409
+ // Cache the result if cache is available
410
+ if (options.cache && options.cache.set) {
411
+ options.cache.set(cacheKey, resp.data, 3600); // Cache for 1 hour
412
+ }
413
+ }
414
+ }
415
+ } catch (error) {
416
+ options.logger?.debug({ error, jid: message.groupInvite.jid }, 'Failed to fetch group profile picture');
417
+ }
418
+ }
419
+ }
420
+ }
421
+ else if ('pin' in message) {
422
+ const pinData = typeof message.pin === 'object' ? message.pin : { key: message.pin };
423
+ // Map type: 1 = PIN_FOR_ALL, 2 = UNPIN_FOR_ALL
424
+ const pinType = pinData.type !== undefined ? pinData.type : (message.type !== undefined ? message.type : WAProto_1.proto.Message.PinInChatMessage.Type.PIN_FOR_ALL);
425
+ m.pinInChatMessage = {
426
+ key: pinData.key,
427
+ type: pinType,
428
+ senderTimestampMs: Date.now()
429
+ };
430
+ // Add messageContextInfo only for PIN (type 1), not for UNPIN (type 2)
431
+ if (pinType === WAProto_1.proto.Message.PinInChatMessage.Type.PIN_FOR_ALL) {
432
+ m.messageContextInfo = {
433
+ messageAddOnDurationInSecs: pinData.time || message.time || 86400, // Default 24 hours
434
+ messageAddOnExpiryType: WAProto_1.proto.MessageContextInfo.MessageAddonExpiryType.STATIC
279
435
  };
280
- key = 'extendedTextMessage';
436
+ }
437
+ }
438
+ else if ('keep' in message) {
439
+ m.keepInChatMessage = {};
440
+ m.keepInChatMessage.key = message.keep;
441
+ m.keepInChatMessage.keepType = message.type;
442
+ m.keepInChatMessage.timestampMs = Date.now();
443
+ }
444
+ else if ('call' in message) {
445
+ const call = message.call;
446
+ if (call && typeof call === 'object' && (
447
+ 'callKey' in call
448
+ || 'conversionSource' in call
449
+ || 'conversionData' in call
450
+ || 'conversionDelaySeconds' in call
451
+ || 'ctwaSignals' in call
452
+ || 'ctwaPayload' in call
453
+ || 'nativeFlowCallButtonPayload' in call
454
+ || 'deeplinkPayload' in call
455
+ )) {
456
+ m.call = WAProto_1.proto.Message.Call.fromObject(call);
281
457
  }
282
458
  else {
283
- m = { [key]: content[key] };
459
+ m = {
460
+ scheduledCallCreationMessage: {
461
+ scheduledTimestampMs: (_a = call === null || call === void 0 ? void 0 : call.time) !== null && _a !== void 0 ? _a : Date.now(),
462
+ callType: (_b = call === null || call === void 0 ? void 0 : call.type) !== null && _b !== void 0 ? _b : 1,
463
+ title: call === null || call === void 0 ? void 0 : call.title
464
+ }
465
+ };
284
466
  }
285
- if (score > 0) {
286
- m[key].contextInfo = { ...(m[key].contextInfo || {}), forwardingScore: score, isForwarded: true };
467
+ }
468
+ else if ('paymentInvite' in message) {
469
+ m.paymentInviteMessage = {
470
+ serviceType: message.paymentInvite.type,
471
+ expiryTimestamp: message.paymentInvite.expiry
472
+ };
473
+ }
474
+ else if ('buttonReply' in message) {
475
+ switch (message.type) {
476
+ case 'template':
477
+ m.templateButtonReplyMessage = {
478
+ selectedDisplayText: message.buttonReply.displayText,
479
+ selectedId: message.buttonReply.id,
480
+ selectedIndex: message.buttonReply.index,
481
+ selectedCarouselCardIndex: message.buttonReply.carouselCardIndex
482
+ };
483
+ break;
484
+ case 'plain':
485
+ m.buttonsResponseMessage = {
486
+ selectedButtonId: message.buttonReply.id,
487
+ selectedDisplayText: message.buttonReply.displayText,
488
+ type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT,
489
+ };
490
+ break;
491
+ }
492
+ }
493
+ else if ('ptv' in message && message.ptv) {
494
+ const { videoMessage } = await (0, exports.prepareWAMessageMedia)({ video: message.video }, options);
495
+ m.ptvMessage = videoMessage;
496
+ }
497
+ else if ('product' in message) {
498
+ const { imageMessage } = await (0, exports.prepareWAMessageMedia)({ image: message.product.productImage }, options);
499
+ m.productMessage = Types_1.WAProto.Message.ProductMessage.fromObject({
500
+ ...message,
501
+ product: {
502
+ ...message.product,
503
+ productImage: imageMessage,
504
+ }
505
+ });
506
+ }
507
+ else if ('order' in message) {
508
+ m.orderMessage = Types_1.WAProto.Message.OrderMessage.fromObject({
509
+ orderId: message.order.id,
510
+ thumbnail: message.order.thumbnail,
511
+ itemCount: message.order.itemCount,
512
+ status: message.order.status,
513
+ surface: message.order.surface,
514
+ orderTitle: message.order.title,
515
+ message: message.order.text,
516
+ sellerJid: message.order.seller,
517
+ token: message.order.token,
518
+ totalAmount1000: message.order.amount,
519
+ totalCurrencyCode: message.order.currency
520
+ });
521
+ }
522
+ else if ('listReply' in message) {
523
+ m.listResponseMessage = { ...message.listReply };
524
+ }
525
+ else if ('poll' in message) {
526
+ (_p = message.poll).selectableCount || (_p.selectableCount = 0);
527
+ (_q = message.poll).toAnnouncementGroup || (_q.toAnnouncementGroup = false);
528
+ if (!Array.isArray(message.poll.values)) {
529
+ throw new boom_1.Boom('Invalid poll values', { statusCode: 400 });
530
+ }
531
+ if (message.poll.selectableCount < 0
532
+ || message.poll.selectableCount > message.poll.values.length) {
533
+ throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, { statusCode: 400 });
534
+ }
535
+ m.messageContextInfo = {
536
+ // encKey
537
+ messageSecret: message.poll.messageSecret || (0, crypto_1.randomBytes)(32),
538
+ };
539
+ const pollCreationMessage = {
540
+ name: message.poll.name,
541
+ selectableOptionsCount: message.poll.selectableCount,
542
+ options: message.poll.values.map(optionName => ({ optionName })),
543
+ };
544
+ if (message.poll.toAnnouncementGroup) {
545
+ // poll v2 is for community announcement groups (single select and multiple)
546
+ m.pollCreationMessageV2 = pollCreationMessage;
287
547
  }
288
548
  else {
289
- m[key].contextInfo = m[key].contextInfo || {};
549
+ if (message.poll.selectableCount === 1) {
550
+ // poll v3 is for single select polls
551
+ m.pollCreationMessageV3 = pollCreationMessage;
552
+ }
553
+ else {
554
+ // poll for multiple choice polls
555
+ m.pollCreationMessage = pollCreationMessage;
556
+ }
290
557
  }
291
558
  }
292
- else if ('listMessage' in message) {
293
- m = { listMessage: Types_1.WAProto.Message.ListMessage.fromObject(message.listMessage) };
559
+ else if ('pollResultSnapshotV3' in message && !!message.pollResultSnapshotV3) {
560
+ m.pollResultSnapshotMessageV3 = {
561
+ pollCreationMessageKey: message.pollResultSnapshotV3.pollCreationMessageKey,
562
+ pollResult: message.pollResultSnapshotV3.pollResult,
563
+ selectedOptions: message.pollResultSnapshotV3.selectedOptions,
564
+ contextInfo: message.pollResultSnapshotV3.contextInfo,
565
+ pollType: message.pollResultSnapshotV3.pollType
566
+ };
294
567
  }
295
- else if ('disappearingMessagesInChat' in message) {
296
- const exp = typeof message.disappearingMessagesInChat === 'boolean' && message.disappearingMessagesInChat ?
297
- Defaults_1.WA_DEFAULT_EPHEMERAL : 0;
298
- m.protocolMessage = {
299
- ephemeralExpiration: exp,
300
- type: Types_1.WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
301
- ephemeralSettingTimestamp: (0, generics_1.unixTimestampSeconds)()
568
+ else if ('pollV4' in message) {
569
+ const pollCreationMessage = {
570
+ name: message.pollV4.name,
571
+ selectableOptionsCount: message.pollV4.selectableCount,
572
+ options: message.pollV4.values.map(optionName => ({ optionName })),
573
+ pollType: message.pollV4.pollType
302
574
  };
575
+ m.pollCreationMessageV4 = pollCreationMessage;
303
576
  }
304
- else if ('buttonReply' in message) {
305
- m.templateButtonReplyMessage = {
306
- selectedId: message.buttonReply.id,
307
- selectedDisplayText: message.buttonReply.displayText
577
+ else if ('pollV5' in message) {
578
+ const pollCreationMessage = {
579
+ name: message.pollV5.name,
580
+ selectableOptionsCount: message.pollV5.selectableCount,
581
+ options: message.pollV5.values.map(optionName => ({ optionName })),
582
+ pollType: message.pollV5.pollType
308
583
  };
584
+ m.pollCreationMessageV5 = pollCreationMessage;
309
585
  }
310
- else if ('listReply' in message) {
311
- m.listResponseMessage = Types_1.WAProto.Message.ListResponseMessage.fromObject(message.listReply);
586
+ else if ('event' in message) {
587
+ m.messageContextInfo = {
588
+ messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32),
589
+ };
590
+ m.eventMessage = { ...message.event };
312
591
  }
313
- else if ('poll' in message) {
314
- m.pollCreationMessage = Types_1.WAProto.Message.PollCreationMessage.fromObject(message.poll);
315
- if (!m.pollCreationMessage.selectableOptionsCount) {
316
- m.pollCreationMessage.selectableOptionsCount = 0;
592
+ else if ('comment' in message) {
593
+ m.commentMessage = {
594
+ message: message.comment.message,
595
+ targetMessageKey: message.comment.targetMessageKey
596
+ };
597
+ }
598
+ else if ('question' in message) {
599
+ m.questionMessage = {
600
+ text: message.question.text,
601
+ contextInfo: message.question.contextInfo
602
+ };
603
+ }
604
+ else if ('questionResponse' in message) {
605
+ m.questionResponseMessage = {
606
+ key: message.questionResponse.key,
607
+ text: message.questionResponse.text
608
+ };
609
+ }
610
+ else if ('statusQuestionAnswer' in message) {
611
+ m.statusQuestionAnswerMessage = {
612
+ key: message.statusQuestionAnswer.key,
613
+ text: message.statusQuestionAnswer.text
614
+ };
615
+ }
616
+ else if ('statusQuoted' in message) {
617
+ m.statusQuotedMessage = {
618
+ type: message.statusQuoted.type,
619
+ text: message.statusQuoted.text,
620
+ thumbnail: message.statusQuoted.thumbnail,
621
+ originalStatusId: message.statusQuoted.originalStatusId
622
+ };
623
+ }
624
+ else if ('statusStickerInteraction' in message) {
625
+ m.statusStickerInteractionMessage = {
626
+ key: message.statusStickerInteraction.key,
627
+ stickerKey: message.statusStickerInteraction.stickerKey,
628
+ type: message.statusStickerInteraction.type
629
+ };
630
+ }
631
+ else if ('richResponse' in message) {
632
+ m.richResponseMessage = WAProto_1.proto.AIRichResponseMessage.fromObject({
633
+ messageType: message.richResponse.messageType !== undefined ? message.richResponse.messageType : 1, // AI_RICH_RESPONSE_TYPE_STANDARD
634
+ submessages: message.richResponse.submessages || [],
635
+ unifiedResponse: message.richResponse.unifiedResponse,
636
+ contextInfo: message.richResponse.contextInfo
637
+ });
638
+ }
639
+ else if ('eventResponse' in message && !!message.eventResponse) {
640
+ m.eventResponseMessage = {
641
+ response: message.eventResponse.response, // GOING = 1, NOT_GOING = 2, MAYBE = 3
642
+ timestampMs: message.eventResponse.timestampMs || Date.now(),
643
+ extraGuestCount: message.eventResponse.extraGuestCount
644
+ };
645
+ }
646
+ else if ('statusMention' in message && !!message.statusMention) {
647
+ m.statusMentionMessage = {
648
+ quotedStatus: message.statusMention.quotedStatus
649
+ };
650
+ }
651
+ else if ('groupStatus' in message && !!message.groupStatus) {
652
+ m.groupStatusMessage = message.groupStatus.message;
653
+ }
654
+ else if ('botTask' in message && !!message.botTask) {
655
+ m.botTaskMessage = message.botTask.message;
656
+ }
657
+ else if ('limitSharing' in message && !!message.limitSharing) {
658
+ m.limitSharingMessage = message.limitSharing.message;
659
+ }
660
+ else if ('statusAddYours' in message && !!message.statusAddYours) {
661
+ m.statusAddYours = message.statusAddYours.message;
662
+ }
663
+ else if ('botForwarded' in message && !!message.botForwarded) {
664
+ m.botForwardedMessage = message.botForwarded.message;
665
+ }
666
+ else if ('eventCoverImage' in message && !!message.eventCoverImage) {
667
+ m.eventCoverImage = message.eventCoverImage.message;
668
+ }
669
+ else if ('stickerPack' in message && !!message.stickerPack) {
670
+ const pack = message.stickerPack;
671
+ const stickerPackMessage = {
672
+ name: pack.name,
673
+ publisher: pack.publisher,
674
+ packDescription: pack.description,
675
+ stickerPackId: pack.stickerPackId || (0, crypto_1.randomBytes)(16).toString('hex'),
676
+ stickerPackOrigin: pack.origin || 2 // USER_CREATED = 2
677
+ };
678
+ // Process cover if provided
679
+ if (pack.cover) {
680
+ const coverMedia = await (0, exports.prepareWAMessageMedia)({ image: pack.cover }, options);
681
+ stickerPackMessage.thumbnailDirectPath = coverMedia.imageMessage.directPath;
682
+ stickerPackMessage.thumbnailSha256 = coverMedia.imageMessage.thumbnailSha256;
683
+ stickerPackMessage.thumbnailEncSha256 = coverMedia.imageMessage.thumbnailEncSha256;
684
+ stickerPackMessage.thumbnailHeight = coverMedia.imageMessage.height;
685
+ stickerPackMessage.thumbnailWidth = coverMedia.imageMessage.width;
686
+ }
687
+ // Process stickers
688
+ if (pack.stickers && pack.stickers.length > 0) {
689
+ const processedStickers = await Promise.all(pack.stickers.map(async (sticker) => {
690
+ const stickerMedia = await (0, exports.prepareWAMessageMedia)({ sticker: sticker.sticker }, options);
691
+ return {
692
+ fileName: sticker.fileName || `sticker_${Date.now()}.webp`,
693
+ isAnimated: sticker.isAnimated || false,
694
+ emojis: sticker.emojis || [],
695
+ accessibilityLabel: sticker.accessibilityLabel,
696
+ isLottie: sticker.isLottie || false,
697
+ mimetype: sticker.mimetype || stickerMedia.stickerMessage.mimetype
698
+ };
699
+ }));
700
+ stickerPackMessage.stickers = processedStickers;
701
+ stickerPackMessage.stickerPackSize = processedStickers.length;
702
+ }
703
+ if (pack.caption) {
704
+ stickerPackMessage.caption = pack.caption;
705
+ }
706
+ m.stickerPackMessage = stickerPackMessage;
707
+ }
708
+ else if ('interactiveResponse' in message && !!message.interactiveResponse) {
709
+ const response = message.interactiveResponse;
710
+ const interactiveResponseMessage = {
711
+ body: {
712
+ text: response.body?.text || '',
713
+ format: response.body?.format || 0 // DEFAULT = 0
714
+ }
715
+ };
716
+ if (response.nativeFlowResponse) {
717
+ interactiveResponseMessage.nativeFlowResponseMessage = {
718
+ name: response.nativeFlowResponse.name,
719
+ paramsJson: response.nativeFlowResponse.paramsJson,
720
+ version: response.nativeFlowResponse.version || 1
721
+ };
722
+ }
723
+ if (response.contextInfo) {
724
+ interactiveResponseMessage.contextInfo = response.contextInfo;
725
+ }
726
+ m.interactiveResponseMessage = interactiveResponseMessage;
727
+ }
728
+ else if ('bCall' in message && !!message.bCall) {
729
+ m.bcallMessage = {
730
+ sessionId: message.bCall.sessionId,
731
+ mediaType: message.bCall.mediaType || 0, // UNKNOWN = 0, AUDIO = 1, VIDEO = 2
732
+ masterKey: message.bCall.masterKey,
733
+ caption: message.bCall.caption
734
+ };
735
+ }
736
+ else if ('callLog' in message && !!message.callLog) {
737
+ m.callLogMesssage = {
738
+ isVideo: message.callLog.isVideo || false,
739
+ callOutcome: message.callLog.callOutcome || 0, // CONNECTED = 0
740
+ durationSecs: message.callLog.durationSecs,
741
+ callType: message.callLog.callType || 0, // REGULAR = 0
742
+ participants: message.callLog.participants || []
743
+ };
744
+ }
745
+ else if ('encComment' in message && !!message.encComment) {
746
+ m.encCommentMessage = {
747
+ targetMessageKey: message.encComment.targetMessageKey,
748
+ encPayload: message.encComment.encPayload,
749
+ encIv: message.encComment.encIv
750
+ };
751
+ }
752
+ else if ('encEventResponse' in message && !!message.encEventResponse) {
753
+ m.encEventResponseMessage = {
754
+ eventCreationMessageKey: message.encEventResponse.eventCreationMessageKey,
755
+ encPayload: message.encEventResponse.encPayload,
756
+ encIv: message.encEventResponse.encIv
757
+ };
758
+ }
759
+ else if ('messageHistoryBundle' in message && !!message.messageHistoryBundle) {
760
+ const bundle = message.messageHistoryBundle;
761
+ const bundleMedia = bundle.media ? await (0, exports.prepareWAMessageMedia)({ document: bundle.media }, options) : null;
762
+ m.messageHistoryBundle = {
763
+ mimetype: bundle.mimetype || 'application/octet-stream',
764
+ fileSha256: bundleMedia?.documentMessage?.fileSha256,
765
+ mediaKey: bundleMedia?.documentMessage?.mediaKey,
766
+ fileEncSha256: bundleMedia?.documentMessage?.fileEncSha256,
767
+ directPath: bundleMedia?.documentMessage?.directPath,
768
+ mediaKeyTimestamp: bundleMedia?.documentMessage?.mediaKeyTimestamp,
769
+ contextInfo: bundle.contextInfo,
770
+ messageHistoryMetadata: bundle.messageHistoryMetadata
771
+ };
772
+ }
773
+ else if ('messageHistoryNotice' in message && !!message.messageHistoryNotice) {
774
+ m.messageHistoryNotice = {
775
+ contextInfo: message.messageHistoryNotice.contextInfo,
776
+ messageHistoryMetadata: message.messageHistoryNotice.messageHistoryMetadata
777
+ };
778
+ }
779
+ else if ('inviteFollower' in message && !!message.inviteFollower) {
780
+ m.newsletterFollowerInviteMessageV2 = {
781
+ newsletterJid: message.inviteFollower.newsletterJid,
782
+ newsletterName: message.inviteFollower.newsletterName,
783
+ jpegThumbnail: message.inviteFollower.thumbnail,
784
+ caption: message.inviteFollower.caption,
785
+ contextInfo: message.inviteFollower.contextInfo
786
+ };
787
+ }
788
+ else if ('placeholder' in message && !!message.placeholder) {
789
+ m.placeholderMessage = {
790
+ type: message.placeholder.type || 0 // MASK_LINKED_DEVICES = 0
791
+ };
792
+ }
793
+ else if ('secretEncrypted' in message && !!message.secretEncrypted) {
794
+ m.secretEncryptedMessage = {
795
+ targetMessageKey: message.secretEncrypted.targetMessageKey,
796
+ encPayload: message.secretEncrypted.encPayload,
797
+ encIv: message.secretEncrypted.encIv,
798
+ secretEncType: message.secretEncrypted.secretEncType || 0 // UNKNOWN = 0, EVENT_EDIT = 1, MESSAGE_EDIT = 2
799
+ };
800
+ }
801
+ else if ('statusNotification' in message && !!message.statusNotification) {
802
+ m.statusNotificationMessage = {
803
+ responseMessageKey: message.statusNotification.responseMessageKey,
804
+ originalMessageKey: message.statusNotification.originalMessageKey,
805
+ type: message.statusNotification.type || 0 // UNKNOWN = 0, STATUS_ADD_YOURS = 1, STATUS_RESHARE = 2, STATUS_QUESTION_ANSWER_RESHARE = 3
806
+ };
807
+ }
808
+ else if ('stickerSyncRMR' in message && !!message.stickerSyncRMR) {
809
+ m.stickerSyncRmrMessage = {
810
+ filehash: message.stickerSyncRMR.filehash || [],
811
+ rmrSource: message.stickerSyncRMR.rmrSource,
812
+ requestTimestamp: message.stickerSyncRMR.requestTimestamp || Date.now()
813
+ };
814
+ }
815
+ else if ('inviteAdmin' in message) {
816
+ m.newsletterAdminInviteMessage = {};
817
+ m.newsletterAdminInviteMessage.inviteExpiration = message.inviteAdmin.inviteExpiration;
818
+ m.newsletterAdminInviteMessage.caption = message.inviteAdmin.text;
819
+ m.newsletterAdminInviteMessage.newsletterJid = message.inviteAdmin.jid;
820
+ m.newsletterAdminInviteMessage.newsletterName = message.inviteAdmin.subject;
821
+ m.newsletterAdminInviteMessage.jpegThumbnail = message.inviteAdmin.thumbnail;
822
+ }
823
+ else if ('requestPayment' in message) {
824
+ const reqPayment = message.requestPayment;
825
+ const sticker = reqPayment.sticker ?
826
+ await (0, exports.prepareWAMessageMedia)({ sticker: reqPayment.sticker }, options)
827
+ : null;
828
+ let notes = {};
829
+ if (reqPayment.sticker) {
830
+ notes = {
831
+ stickerMessage: {
832
+ ...sticker.stickerMessage,
833
+ contextInfo: reqPayment.contextInfo
834
+ }
835
+ };
836
+ }
837
+ else if (reqPayment.note) {
838
+ notes = {
839
+ extendedTextMessage: {
840
+ text: reqPayment.note,
841
+ contextInfo: reqPayment.contextInfo,
842
+ }
843
+ };
317
844
  }
845
+ else {
846
+ throw new boom_1.Boom('Invalid media type', { statusCode: 400 });
847
+ }
848
+ m.requestPaymentMessage = Types_1.WAProto.Message.RequestPaymentMessage.fromObject({
849
+ expiryTimestamp: reqPayment.expiryTimestamp || reqPayment.expiry,
850
+ amount1000: reqPayment.amount1000 || reqPayment.amount,
851
+ currencyCodeIso4217: reqPayment.currencyCodeIso4217 || reqPayment.currency,
852
+ requestFrom: reqPayment.requestFrom || reqPayment.from,
853
+ noteMessage: { ...notes },
854
+ background: reqPayment.background,
855
+ // Aggiungi altri parametri se disponibili
856
+ ...reqPayment
857
+ });
858
+
859
+ // Pix adaptation for Brazilian payments
860
+ if (reqPayment.currencyCodeIso4217 === 'BRL' && reqPayment.pixKey) {
861
+ // Embed Pix key in note for dynamic requests
862
+ if (!m.requestPaymentMessage.noteMessage.extendedTextMessage) {
863
+ m.requestPaymentMessage.noteMessage = { extendedTextMessage: { text: '' } };
864
+ }
865
+ m.requestPaymentMessage.noteMessage.extendedTextMessage.text += `\nPix Key: ${reqPayment.pixKey}`;
866
+ }
867
+ }
868
+ else if ('sharePhoneNumber' in message) {
869
+ m.protocolMessage = {
870
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
871
+ };
872
+ }
873
+ else if ('requestPhoneNumber' in message) {
874
+ m.requestPhoneNumberMessage = {};
318
875
  }
319
- else if ('pollUpdate' in message) {
320
- m.pollUpdateMessage = Types_1.WAProto.Message.PollUpdateMessage.fromObject(message.pollUpdate);
876
+ else if ('newsletterMessage' in message) {
877
+ m.newsletterMessage = Types_1.WAProto.Message.NewsletterMessage.fromObject(message.newsletterMessage);
878
+ }
879
+ else if ('externalAdReply' in message) {
880
+ // Handle sendNyanCat functionality - external ad reply
881
+ const extAdReply = message.externalAdReply;
882
+ m.extendedTextMessage = {
883
+ text: message.text || '',
884
+ contextInfo: {
885
+ externalAdReply: {
886
+ title: extAdReply.title,
887
+ body: extAdReply.body,
888
+ mediaType: extAdReply.mediaType || 1,
889
+ thumbnailUrl: extAdReply.thumbnailUrl,
890
+ thumbnail: extAdReply.thumbnail,
891
+ sourceUrl: extAdReply.sourceUrl,
892
+ showAdAttribution: extAdReply.showAdAttribution || false,
893
+ renderLargerThumbnail: extAdReply.renderLargerThumbnail !== false
894
+ }
895
+ }
896
+ };
897
+ }
898
+ else if ('sendPayment' in message && !!message.sendPayment) {
899
+ const payment = message.sendPayment;
900
+ m.sendPaymentMessage = {
901
+ requestMessageKey: payment.requestMessageKey,
902
+ noteMessage: payment.noteMessage,
903
+ background: payment.background,
904
+ transactionData: payment.transactionData
905
+ };
906
+ }
907
+ else if ('declinePayment' in message && !!message.declinePayment) {
908
+ m.declinePaymentRequestMessage = {
909
+ key: message.declinePayment.key
910
+ };
911
+ }
912
+ else if ('cancelPayment' in message && !!message.cancelPayment) {
913
+ m.cancelPaymentRequestMessage = {
914
+ key: message.cancelPayment.key
915
+ };
916
+ }
917
+ else if ('scheduledCallEdit' in message && !!message.scheduledCallEdit) {
918
+ m.scheduledCallEditMessage = {
919
+ key: message.scheduledCallEdit.key,
920
+ editType: message.scheduledCallEdit.editType || 0 // UNKNOWN = 0, CANCEL = 1
921
+ };
922
+ }
923
+ else if ('pollResultSnapshot' in message && !!message.pollResultSnapshot) {
924
+ m.pollResultSnapshotMessage = {
925
+ name: message.pollResultSnapshot.name,
926
+ pollVotes: message.pollResultSnapshot.pollVotes || [],
927
+ contextInfo: message.pollResultSnapshot.contextInfo,
928
+ pollType: message.pollResultSnapshot.pollType || 0 // POLL = 0, QUIZ = 1
929
+ };
930
+ }
931
+ else if ('pollUpdate' in message && !!message.pollUpdate) {
932
+ m.pollUpdateMessage = {
933
+ pollCreationMessageKey: message.pollUpdate.pollCreationMessageKey,
934
+ vote: message.pollUpdate.vote,
935
+ metadata: message.pollUpdate.metadata,
936
+ senderTimestampMs: message.pollUpdate.senderTimestampMs || Date.now()
937
+ };
938
+ }
939
+ else if ('deviceSent' in message && !!message.deviceSent) {
940
+ const deviceSent = message.deviceSent;
941
+ const innerMessage = await (0, exports.generateWAMessageContent)(deviceSent.message, options);
942
+ m.deviceSentMessage = {
943
+ destinationJid: deviceSent.destinationJid,
944
+ message: innerMessage,
945
+ phash: deviceSent.phash
946
+ };
947
+ }
948
+ else if ('chat' in message && !!message.chat) {
949
+ m.chat = {
950
+ displayName: message.chat.displayName,
951
+ id: message.chat.id
952
+ };
953
+ }
954
+ else if ('payment' in message) {
955
+ // Handle sendPayment functionality
956
+ m.requestPaymentMessage = Types_1.WAProto.Message.RequestPaymentMessage.fromObject({
957
+ currencyCodeIso4217: message.payment.currency || 'EUR',
958
+ amount1000: message.payment.amount1000 || message.payment.amount * 1000,
959
+ requestFrom: message.payment.requestFrom || message.payment.from,
960
+ noteMessage: {
961
+ extendedTextMessage: {
962
+ text: message.payment.text || message.payment.note || '',
963
+ contextInfo: message.payment.contextInfo
964
+ }
965
+ },
966
+ expiryTimestamp: message.payment.expiryTimestamp || message.payment.expiry,
967
+ background: message.payment.background
968
+ });
969
+ }
970
+ else if ('comment' in message) {
971
+ m.commentMessage = {
972
+ message: message.comment.message,
973
+ targetMessageKey: message.comment.targetMessageKey
974
+ };
975
+ }
976
+ else if ('question' in message) {
977
+ m.questionMessage = {
978
+ text: message.question.text,
979
+ contextInfo: message.question.contextInfo
980
+ };
981
+ }
982
+ else if ('questionResponse' in message) {
983
+ m.questionResponseMessage = {
984
+ key: message.questionResponse.key,
985
+ text: message.questionResponse.text
986
+ };
987
+ }
988
+ else if ('statusQuestionAnswer' in message) {
989
+ m.statusQuestionAnswerMessage = {
990
+ key: message.statusQuestionAnswer.key,
991
+ text: message.statusQuestionAnswer.text
992
+ };
993
+ }
994
+ else if ('statusQuoted' in message) {
995
+ m.statusQuotedMessage = {
996
+ type: message.statusQuoted.type,
997
+ text: message.statusQuoted.text,
998
+ thumbnail: message.statusQuoted.thumbnail,
999
+ jid: message.statusQuoted.jid,
1000
+ originalStatusId: message.statusQuoted.originalStatusId
1001
+ };
1002
+ }
1003
+ else if ('statusStickerInteraction' in message) {
1004
+ m.statusStickerInteractionMessage = {
1005
+ key: message.statusStickerInteraction.key,
1006
+ stickerKey: message.statusStickerInteraction.stickerKey,
1007
+ type: message.statusStickerInteraction.type
1008
+ };
1009
+ }
1010
+ else if ('album' in message) {
1011
+ const imageMessages = message.album.filter(item => 'image' in item);
1012
+ const videoMessages = message.album.filter(item => 'video' in item);
1013
+ m.albumMessage = WAProto_1.proto.Message.AlbumMessage.fromObject({
1014
+ expectedImageCount: imageMessages.length,
1015
+ expectedVideoCount: videoMessages.length,
1016
+ });
321
1017
  }
322
1018
  else {
323
1019
  m = await (0, exports.prepareWAMessageMedia)(message, options);
324
1020
  }
325
-
1021
+ if ('buttons' in message && !!message.buttons) {
1022
+ const buttonsMessage = {
1023
+ buttons: message.buttons.map(b => ({ ...b, type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
1024
+ };
1025
+ if ('text' in message) {
1026
+ buttonsMessage.contentText = message.text;
1027
+ buttonsMessage.headerType = ButtonType.EMPTY;
1028
+ }
1029
+ else {
1030
+ if ('caption' in message) {
1031
+ buttonsMessage.contentText = message.caption;
1032
+ }
1033
+ const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
1034
+ buttonsMessage.headerType = ButtonType[type];
1035
+ Object.assign(buttonsMessage, m);
1036
+ }
1037
+ if ('title' in message && !!message.title) {
1038
+ buttonsMessage.text = message.title,
1039
+ buttonsMessage.headerType = ButtonType.TEXT;
1040
+ }
1041
+ if ('footer' in message && !!message.footer) {
1042
+ buttonsMessage.footerText = message.footer;
1043
+ }
1044
+ if ('contextInfo' in message && !!message.contextInfo) {
1045
+ buttonsMessage.contextInfo = message.contextInfo;
1046
+ }
1047
+ if ('mentions' in message && !!message.mentions) {
1048
+ buttonsMessage.contextInfo = { mentionedJid: message.mentions };
1049
+ }
1050
+ m = { buttonsMessage };
1051
+ }
1052
+ else if ('templateButtons' in message && !!message.templateButtons) {
1053
+ const templateMsg = {
1054
+ hydratedButtons: message.templateButtons
1055
+ };
1056
+ if ('text' in message) {
1057
+ templateMsg.hydratedContentText = message.text;
1058
+ }
1059
+ else if ('caption' in message) {
1060
+ templateMsg.hydratedContentText = message.caption;
1061
+ }
1062
+ if ('footer' in message && !!message.footer) {
1063
+ templateMsg.hydratedFooterText = message.footer;
1064
+ }
1065
+ // Add media to template if present
1066
+ if (m && Object.keys(m).length > 0) {
1067
+ Object.assign(templateMsg, m);
1068
+ }
1069
+ m = {
1070
+ templateMessage: {
1071
+ fourRowTemplate: templateMsg,
1072
+ hydratedTemplate: templateMsg
1073
+ }
1074
+ };
1075
+ }
1076
+ if ('sections' in message && !!message.sections) {
1077
+ const listMessage = {
1078
+ sections: message.sections,
1079
+ buttonText: message.buttonText,
1080
+ title: message.title,
1081
+ footerText: message.footer,
1082
+ description: message.text,
1083
+ listType: WAProto_1.proto.Message.ListMessage.ListType.SINGLE_SELECT
1084
+ };
1085
+ m = { listMessage };
1086
+ }
1087
+ if ('interactiveButtons' in message && !!message.interactiveButtons) {
1088
+ const interactiveMessage = {
1089
+ nativeFlowMessage: Types_1.WAProto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1090
+ buttons: message.interactiveButtons,
1091
+ messageVersion: 1,
1092
+ })
1093
+ };
1094
+ if ('text' in message) {
1095
+ interactiveMessage.body = {
1096
+ text: message.text
1097
+ };
1098
+ }
1099
+ else if ('caption' in message) {
1100
+ interactiveMessage.body = {
1101
+ text: message.caption
1102
+ };
1103
+ interactiveMessage.header = {
1104
+ title: message.title,
1105
+ subtitle: message.subtitle,
1106
+ hasMediaAttachment: !!(message === null || message === void 0 ? void 0 : message.media),
1107
+ };
1108
+ Object.assign(interactiveMessage.header, m);
1109
+ }
1110
+ if ('footer' in message && !!message.footer) {
1111
+ if (typeof message.footer === 'string') {
1112
+ interactiveMessage.footer = {
1113
+ text: message.footer
1114
+ };
1115
+ } else if (typeof message.footer === 'object' && message.footer.text) {
1116
+ interactiveMessage.footer = {
1117
+ text: message.footer.text,
1118
+ hasMediaAttachment: !!message.footer.audio
1119
+ };
1120
+ if (message.footer.audio) {
1121
+ const audioMedia = await (0, exports.prepareWAMessageMedia)({ audio: message.footer.audio }, options);
1122
+ interactiveMessage.footer.audioMessage = audioMedia.audioMessage;
1123
+ }
1124
+ }
1125
+ }
1126
+ if ('title' in message && !!message.title) {
1127
+ const headerData = {
1128
+ title: message.title,
1129
+ subtitle: message.subtitle,
1130
+ hasMediaAttachment: !!message.media,
1131
+ };
1132
+
1133
+ // Process media attachments for interactive buttons
1134
+ if (message.media) {
1135
+ if (message.media.image) {
1136
+ const mediaMessage = await (0, exports.prepareWAMessageMedia)({ image: message.media.image }, options);
1137
+ if (mediaMessage.imageMessage) {
1138
+ headerData.imageMessage = mediaMessage.imageMessage;
1139
+ }
1140
+ }
1141
+ else if (message.media.video) {
1142
+ const mediaMessage = await (0, exports.prepareWAMessageMedia)({ video: message.media.video }, options);
1143
+ if (mediaMessage.videoMessage) {
1144
+ headerData.videoMessage = mediaMessage.videoMessage;
1145
+ }
1146
+ }
1147
+ else if (message.media.document) {
1148
+ const mediaMessage = await (0, exports.prepareWAMessageMedia)({ document: message.media.document }, options);
1149
+ if (mediaMessage.documentMessage) {
1150
+ headerData.documentMessage = mediaMessage.documentMessage;
1151
+ }
1152
+ }
1153
+ }
1154
+
1155
+ interactiveMessage.header = headerData;
1156
+ // Support for ProductMessage in header
1157
+ if (message.headerProduct) {
1158
+ const productMedia = await (0, exports.prepareWAMessageMedia)({ image: message.headerProduct.productImage }, options);
1159
+ interactiveMessage.header.productMessage = {
1160
+ product: {
1161
+ ...message.headerProduct,
1162
+ productImage: productMedia.imageMessage
1163
+ }
1164
+ };
1165
+ interactiveMessage.header.hasMediaAttachment = true;
1166
+ } else {
1167
+ Object.assign(interactiveMessage.header, m);
1168
+ }
1169
+ }
1170
+ if ('contextInfo' in message && !!message.contextInfo) {
1171
+ interactiveMessage.contextInfo = message.contextInfo;
1172
+ }
1173
+ if ('mentions' in message && !!message.mentions) {
1174
+ interactiveMessage.contextInfo = { mentionedJid: message.mentions };
1175
+ }
1176
+ m = { interactiveMessage };
1177
+ }
1178
+ if ('shop' in message && !!message.shop) {
1179
+ const interactiveMessage = {
1180
+ shopStorefrontMessage: Types_1.WAProto.Message.InteractiveMessage.ShopMessage.fromObject({
1181
+ surface: message.shop,
1182
+ id: message.id
1183
+ })
1184
+ };
1185
+ if ('text' in message) {
1186
+ interactiveMessage.body = {
1187
+ text: message.text
1188
+ };
1189
+ }
1190
+ else if ('caption' in message) {
1191
+ interactiveMessage.body = {
1192
+ text: message.caption
1193
+ };
1194
+ interactiveMessage.header = {
1195
+ title: message.title,
1196
+ subtitle: message.subtitle,
1197
+ hasMediaAttachment: !!(message === null || message === void 0 ? void 0 : message.media),
1198
+ };
1199
+ Object.assign(interactiveMessage.header, m);
1200
+ }
1201
+ if ('footer' in message && !!message.footer) {
1202
+ interactiveMessage.footer = {
1203
+ text: message.footer
1204
+ };
1205
+ }
1206
+ if ('title' in message && !!message.title) {
1207
+ interactiveMessage.header = {
1208
+ title: message.title,
1209
+ subtitle: message.subtitle,
1210
+ hasMediaAttachment: !!(message === null || message === void 0 ? void 0 : message.media),
1211
+ };
1212
+ Object.assign(interactiveMessage.header, m);
1213
+ }
1214
+ if ('contextInfo' in message && !!message.contextInfo) {
1215
+ interactiveMessage.contextInfo = message.contextInfo;
1216
+ }
1217
+ if ('mentions' in message && !!message.mentions) {
1218
+ interactiveMessage.contextInfo = { mentionedJid: message.mentions };
1219
+ }
1220
+ m = { interactiveMessage };
1221
+ }
1222
+ else if ('collection' in message && !!message.collection) {
1223
+ const interactiveMessage = {
1224
+ collectionMessage: Types_1.WAProto.Message.InteractiveMessage.CollectionMessage.fromObject({
1225
+ bizJid: message.collection.bizJid,
1226
+ id: message.collection.id,
1227
+ messageVersion: message.collection.messageVersion || 1
1228
+ })
1229
+ };
1230
+ if ('text' in message) {
1231
+ interactiveMessage.body = {
1232
+ text: message.text
1233
+ };
1234
+ }
1235
+ if ('footer' in message && !!message.footer) {
1236
+ interactiveMessage.footer = {
1237
+ text: message.footer
1238
+ };
1239
+ }
1240
+ if ('title' in message && !!message.title) {
1241
+ interactiveMessage.header = {
1242
+ title: message.title,
1243
+ subtitle: message.subtitle,
1244
+ hasMediaAttachment: false
1245
+ };
1246
+ }
1247
+ if ('contextInfo' in message && !!message.contextInfo) {
1248
+ interactiveMessage.contextInfo = message.contextInfo;
1249
+ }
1250
+ m = { interactiveMessage };
1251
+ }
1252
+ else if ('invoice' in message && !!message.invoice) {
1253
+ const invoiceData = message.invoice;
1254
+ const invoiceMessage = {
1255
+ note: invoiceData.note,
1256
+ token: invoiceData.token,
1257
+ attachmentType: invoiceData.attachmentType || 0 // IMAGE = 0, PDF = 1
1258
+ };
1259
+ if (invoiceData.attachment) {
1260
+ const attachmentMedia = await (0, exports.prepareWAMessageMedia)({
1261
+ [invoiceData.attachmentType === 1 ? 'document' : 'image']: invoiceData.attachment
1262
+ }, options);
1263
+ if (invoiceData.attachmentType === 1) {
1264
+ invoiceMessage.attachmentMimetype = attachmentMedia.documentMessage.mimetype;
1265
+ invoiceMessage.attachmentMediaKey = attachmentMedia.documentMessage.mediaKey;
1266
+ invoiceMessage.attachmentMediaKeyTimestamp = attachmentMedia.documentMessage.mediaKeyTimestamp;
1267
+ invoiceMessage.attachmentFileSha256 = attachmentMedia.documentMessage.fileSha256;
1268
+ invoiceMessage.attachmentFileEncSha256 = attachmentMedia.documentMessage.fileEncSha256;
1269
+ invoiceMessage.attachmentDirectPath = attachmentMedia.documentMessage.directPath;
1270
+ } else {
1271
+ invoiceMessage.attachmentMimetype = attachmentMedia.imageMessage.mimetype;
1272
+ invoiceMessage.attachmentMediaKey = attachmentMedia.imageMessage.mediaKey;
1273
+ invoiceMessage.attachmentMediaKeyTimestamp = attachmentMedia.imageMessage.mediaKeyTimestamp;
1274
+ invoiceMessage.attachmentFileSha256 = attachmentMedia.imageMessage.fileSha256;
1275
+ invoiceMessage.attachmentFileEncSha256 = attachmentMedia.imageMessage.fileEncSha256;
1276
+ invoiceMessage.attachmentDirectPath = attachmentMedia.imageMessage.directPath;
1277
+ invoiceMessage.attachmentJpegThumbnail = attachmentMedia.imageMessage.jpegThumbnail;
1278
+ }
1279
+ }
1280
+ m = { invoiceMessage };
1281
+ }
326
1282
  if ('cards' in message && !!message.cards && message.cards.length > 0) {
327
- const carouselCardType = message.carouselCardType || 1;
1283
+ const carouselCardType = message.carouselCardType || 1; // HSCROLL_CARDS = 1, ALBUM_IMAGE = 2
328
1284
  const carouselCards = await Promise.all(message.cards.map(async (card) => {
329
1285
  const cardMessage = {
330
1286
  header: {
@@ -333,23 +1289,26 @@ const generateWAMessageContent = async (message, options) => {
333
1289
  }
334
1290
  };
335
1291
 
1292
+ // Add body as separate field if present
336
1293
  if (card.body) {
337
- cardMessage.body = { text: card.body };
1294
+ cardMessage.body = {
1295
+ text: card.body
1296
+ };
338
1297
  }
339
-
1298
+ // Handle media attachments
340
1299
  if (card.image) {
341
- const mediaMessage = await (0, exports.prepareWAMessageMedia)({ image: card.image }, options);
1300
+ const mediaMessage = await prepareWAMessageMedia({ image: card.image }, options);
342
1301
  if (mediaMessage.imageMessage) {
343
1302
  cardMessage.header.imageMessage = mediaMessage.imageMessage;
344
1303
  }
345
1304
  }
346
1305
  else if (card.video) {
347
- const mediaMessage = await (0, exports.prepareWAMessageMedia)({ video: card.video }, options);
1306
+ const mediaMessage = await prepareWAMessageMedia({ video: card.video }, options);
348
1307
  if (mediaMessage.videoMessage) {
349
1308
  cardMessage.header.videoMessage = mediaMessage.videoMessage;
350
1309
  }
351
1310
  }
352
-
1311
+ // Handle buttons
353
1312
  if (card.buttons && card.buttons.length > 0) {
354
1313
  cardMessage.nativeFlowMessage = {
355
1314
  buttons: card.buttons.map(button => ({
@@ -359,14 +1318,14 @@ const generateWAMessageContent = async (message, options) => {
359
1318
  messageVersion: 1,
360
1319
  };
361
1320
  }
362
-
1321
+ // Add footer if present
363
1322
  if (card.footer) {
364
- cardMessage.footer = { text: card.footer };
1323
+ cardMessage.footer = {
1324
+ text: card.footer
1325
+ };
365
1326
  }
366
-
367
1327
  return cardMessage;
368
1328
  }));
369
-
370
1329
  const interactiveMessage = {
371
1330
  carouselMessage: Types_1.WAProto.Message.InteractiveMessage.CarouselMessage.fromObject({
372
1331
  cards: carouselCards,
@@ -374,12 +1333,15 @@ const generateWAMessageContent = async (message, options) => {
374
1333
  carouselCardType: carouselCardType
375
1334
  })
376
1335
  };
377
-
378
1336
  if ('text' in message) {
379
- interactiveMessage.body = { text: message.text };
1337
+ interactiveMessage.body = {
1338
+ text: message.text
1339
+ };
380
1340
  }
381
1341
  if ('footer' in message && !!message.footer) {
382
- interactiveMessage.footer = { text: message.footer };
1342
+ interactiveMessage.footer = {
1343
+ text: message.footer
1344
+ };
383
1345
  }
384
1346
  if ('title' in message && !!message.title) {
385
1347
  interactiveMessage.header = {
@@ -394,25 +1356,20 @@ const generateWAMessageContent = async (message, options) => {
394
1356
  if ('mentions' in message && !!message.mentions) {
395
1357
  interactiveMessage.contextInfo = { mentionedJid: message.mentions };
396
1358
  }
397
-
398
1359
  m = { interactiveMessage };
399
1360
  }
400
-
1361
+ // Interactive messages are commonly sent wrapped in a view-once container on MD.
1362
+ // This improves client compatibility (avoids "update WhatsApp" / invisible messages on some clients).
401
1363
  const shouldWrapInteractive = !!(m === null || m === void 0 ? void 0 : m.interactiveMessage);
402
- const hasViewOnceAlready = !!(m === null || m === void 0 ? void 0 : m.viewOnceMessage) ||
403
- !!(m === null || m === void 0 ? void 0 : m.viewOnceMessageV2) ||
404
- !!(m === null || m === void 0 ? void 0 : m.viewOnceMessageV2Extension);
405
-
1364
+ const hasViewOnceAlready = !!(m === null || m === void 0 ? void 0 : m.viewOnceMessage) || !!(m === null || m === void 0 ? void 0 : m.viewOnceMessageV2) || !!(m === null || m === void 0 ? void 0 : m.viewOnceMessageV2Extension);
406
1365
  if ((('viewOnce' in message && !!message.viewOnce) || shouldWrapInteractive) && !hasViewOnceAlready) {
407
1366
  m = { viewOnceMessageV2: { message: m } };
408
1367
  }
409
-
410
- if ('mentions' in message && (message.mentions?.length)) {
1368
+ if ('mentions' in message && ((_o = message.mentions) === null || _o === void 0 ? void 0 : _o.length)) {
411
1369
  const [messageType] = Object.keys(m);
412
- m[messageType].contextInfo = m[messageType].contextInfo || {};
1370
+ m[messageType].contextInfo = m[messageType] || {};
413
1371
  m[messageType].contextInfo.mentionedJid = message.mentions;
414
1372
  }
415
-
416
1373
  if ('edit' in message) {
417
1374
  m = {
418
1375
  protocolMessage: {
@@ -423,7 +1380,6 @@ const generateWAMessageContent = async (message, options) => {
423
1380
  }
424
1381
  };
425
1382
  }
426
-
427
1383
  if ('contextInfo' in message && !!message.contextInfo) {
428
1384
  const [messageType] = Object.keys(m);
429
1385
  m[messageType] = m[messageType] || {};
@@ -432,94 +1388,406 @@ const generateWAMessageContent = async (message, options) => {
432
1388
  ...message.contextInfo
433
1389
  };
434
1390
  }
435
-
436
1391
  return Types_1.WAProto.Message.fromObject(m);
437
1392
  };
438
1393
  exports.generateWAMessageContent = generateWAMessageContent;
439
-
440
1394
  const generateWAMessageFromContent = (jid, message, options) => {
1395
+ // set timestamp to now
1396
+ // if not specified
441
1397
  if (!options.timestamp) {
442
1398
  options.timestamp = new Date();
443
1399
  }
444
-
445
1400
  const innerMessage = (0, exports.normalizeMessageContent)(message);
446
1401
  const key = (0, exports.getContentType)(innerMessage);
447
1402
  const timestamp = (0, generics_1.unixTimestampSeconds)(options.timestamp);
448
1403
  const { quoted, userJid } = options;
449
-
1404
+ // only set quoted if isn't a newsletter message
450
1405
  if (quoted && !(0, WABinary_1.isJidNewsletter)(jid)) {
451
1406
  const participant = quoted.key.fromMe ? userJid : (quoted.participant || quoted.key.participant || quoted.key.remoteJid);
452
1407
  let quotedMsg = (0, exports.normalizeMessageContent)(quoted.message);
453
1408
  const msgType = (0, exports.getContentType)(quotedMsg);
454
-
1409
+ // strip any redundant properties
455
1410
  if (quotedMsg) {
456
1411
  quotedMsg = WAProto_1.proto.Message.fromObject({ [msgType]: quotedMsg[msgType] });
457
1412
  const quotedContent = quotedMsg[msgType];
458
1413
  if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
459
1414
  delete quotedContent.contextInfo;
460
1415
  }
461
-
462
1416
  const contextInfo = innerMessage[key].contextInfo || {};
463
1417
  contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant);
464
1418
  contextInfo.stanzaId = quoted.key.id;
465
1419
  contextInfo.quotedMessage = quotedMsg;
466
-
1420
+ // if a participant is quoted, then it must be a group
1421
+ // hence, remoteJid of group must also be entered
467
1422
  if (jid !== quoted.key.remoteJid) {
468
1423
  contextInfo.remoteJid = quoted.key.remoteJid;
469
1424
  }
470
-
471
1425
  innerMessage[key].contextInfo = contextInfo;
472
1426
  }
473
1427
  }
474
-
475
- if (!!(options === null || options === void 0 ? void 0 : options.ephemeralExpiration) &&
1428
+ if (
1429
+ // if we want to send a disappearing message
1430
+ !!(options === null || options === void 0 ? void 0 : options.ephemeralExpiration) &&
1431
+ // and it's not a protocol message -- delete, toggle disappear message
476
1432
  key !== 'protocolMessage' &&
1433
+ // already not converted to disappearing message
477
1434
  key !== 'ephemeralMessage' &&
1435
+ // newsletter not accept disappearing messages
478
1436
  !(0, WABinary_1.isJidNewsletter)(jid)) {
479
1437
  innerMessage[key].contextInfo = {
480
1438
  ...(innerMessage[key].contextInfo || {}),
481
1439
  expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL,
1440
+ //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
482
1441
  };
483
1442
  }
484
-
485
- const msg = {
1443
+ message = Types_1.WAProto.Message.fromObject(message);
1444
+ const messageJSON = {
486
1445
  key: {
487
1446
  remoteJid: jid,
488
1447
  fromMe: true,
489
- id: options.messageId || (0, generics_1.generateMessageID)(),
1448
+ id: (options === null || options === void 0 ? void 0 : options.messageId) || (0, generics_1.generateMessageIDV2)(),
490
1449
  },
491
- message: innerMessage,
1450
+ message: message,
492
1451
  messageTimestamp: timestamp,
493
1452
  messageStubParameters: [],
494
- participant: (0, WABinary_1.isJidGroup)(jid) ? userJid : undefined,
1453
+ participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined,
495
1454
  status: Types_1.WAMessageStatus.PENDING
496
1455
  };
497
-
498
- return WAProto_1.proto.WebMessageInfo.fromObject(msg);
1456
+ return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON);
499
1457
  };
500
1458
  exports.generateWAMessageFromContent = generateWAMessageFromContent;
501
-
502
- async function getAggregateVotesInPollMessage({ message, pollUpdates }) {
503
- const votes = {};
504
- const voters = {};
505
-
506
- for (const update of pollUpdates) {
1459
+ const generateWAMessage = async (jid, content, options) => {
1460
+ var _a;
1461
+ // ensure msg ID is with every log
1462
+ options.logger = (_a = options === null || options === void 0 ? void 0 : options.logger) === null || _a === void 0 ? void 0 : _a.child({ msgId: options.messageId });
1463
+ return (0, exports.generateWAMessageFromContent)(jid, await (0, exports.generateWAMessageContent)(content, { newsletter: (0, WABinary_1.isJidNewsletter)(jid), ...options }), options);
1464
+ };
1465
+ exports.generateWAMessage = generateWAMessage;
1466
+ /** Get the key to access the true type of content */
1467
+ const getContentType = (content) => {
1468
+ if (content) {
1469
+ const keys = Object.keys(content);
1470
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
1471
+ return key;
1472
+ }
1473
+ };
1474
+ exports.getContentType = getContentType;
1475
+ /**
1476
+ * Normalizes ephemeral, view once messages to regular message content
1477
+ * Eg. image messages in ephemeral messages, in view once messages etc.
1478
+ * @param content
1479
+ * @returns
1480
+ */
1481
+ const normalizeMessageContent = (content) => {
1482
+ if (!content) {
1483
+ return undefined;
1484
+ }
1485
+ // set max iterations to prevent an infinite loop
1486
+ for (let i = 0; i < 5; i++) {
1487
+ const inner = getFutureProofMessage(content);
1488
+ if (!inner) {
1489
+ break;
1490
+ }
1491
+ content = inner.message;
1492
+ }
1493
+ return content;
1494
+ function getFutureProofMessage(message) {
1495
+ return ((message === null || message === void 0 ? void 0 : message.ephemeralMessage)
1496
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessage)
1497
+ || (message === null || message === void 0 ? void 0 : message.documentWithCaptionMessage)
1498
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2)
1499
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension)
1500
+ || (message === null || message === void 0 ? void 0 : message.editedMessage)
1501
+ || (message === null || message === void 0 ? void 0 : message.groupMentionedMessage)
1502
+ || (message === null || message === void 0 ? void 0 : message.botInvokeMessage)
1503
+ || (message === null || message === void 0 ? void 0 : message.lottieStickerMessage)
1504
+ || (message === null || message === void 0 ? void 0 : message.eventCoverImage)
1505
+ || (message === null || message === void 0 ? void 0 : message.statusMentionMessage)
1506
+ || (message === null || message === void 0 ? void 0 : message.pollCreationOptionImageMessage)
1507
+ || (message === null || message === void 0 ? void 0 : message.associatedChildMessage)
1508
+ || (message === null || message === void 0 ? void 0 : message.groupStatusMentionMessage)
1509
+ || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV4)
1510
+ || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV5)
1511
+ || (message === null || message === void 0 ? void 0 : message.statusAddYours)
1512
+ || (message === null || message === void 0 ? void 0 : message.groupStatusMessage)
1513
+ || (message === null || message === void 0 ? void 0 : message.limitSharingMessage)
1514
+ || (message === null || message === void 0 ? void 0 : message.botTaskMessage)
1515
+ || (message === null || message === void 0 ? void 0 : message.questionMessage)
1516
+ || (message === null || message === void 0 ? void 0 : message.groupStatusMessageV2)
1517
+ || (message === null || message === void 0 ? void 0 : message.botForwardedMessage));
1518
+ }
1519
+ };
1520
+ exports.normalizeMessageContent = normalizeMessageContent;
1521
+ /**
1522
+ * Extract the true message content from a message
1523
+ * Eg. extracts the inner message from a disappearing message/view once message
1524
+ */
1525
+ const extractMessageContent = (content) => {
1526
+ var _a, _b, _c, _d, _e, _f;
1527
+ const extractFromTemplateMessage = (msg) => {
1528
+ if (msg.imageMessage) {
1529
+ return { imageMessage: msg.imageMessage };
1530
+ }
1531
+ else if (msg.documentMessage) {
1532
+ return { documentMessage: msg.documentMessage };
1533
+ }
1534
+ else if (msg.videoMessage) {
1535
+ return { videoMessage: msg.videoMessage };
1536
+ }
1537
+ else if (msg.locationMessage) {
1538
+ return { locationMessage: msg.locationMessage };
1539
+ }
1540
+ else {
1541
+ return {
1542
+ conversation: 'contentText' in msg
1543
+ ? msg.contentText
1544
+ : ('hydratedContentText' in msg ? msg.hydratedContentText : '')
1545
+ };
1546
+ }
1547
+ };
1548
+ content = (0, exports.normalizeMessageContent)(content);
1549
+ if (content === null || content === void 0 ? void 0 : content.buttonsMessage) {
1550
+ return extractFromTemplateMessage(content.buttonsMessage);
1551
+ }
1552
+ if ((_a = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _a === void 0 ? void 0 : _a.hydratedFourRowTemplate) {
1553
+ return extractFromTemplateMessage((_b = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _b === void 0 ? void 0 : _b.hydratedFourRowTemplate);
1554
+ }
1555
+ if ((_c = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _c === void 0 ? void 0 : _c.hydratedTemplate) {
1556
+ return extractFromTemplateMessage((_d = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _d === void 0 ? void 0 : _d.hydratedTemplate);
1557
+ }
1558
+ if ((_e = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _e === void 0 ? void 0 : _e.fourRowTemplate) {
1559
+ return extractFromTemplateMessage((_f = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _f === void 0 ? void 0 : _f.fourRowTemplate);
1560
+ }
1561
+ return content;
1562
+ };
1563
+ exports.extractMessageContent = extractMessageContent;
1564
+ /**
1565
+ * Returns the device predicted by message ID
1566
+ */
1567
+ const getDevice = (id) => /^3A.{18}$/.test(id) ? 'ios' :
1568
+ /^3E.{20}$/.test(id) ? 'web' :
1569
+ /^(.{21}|.{32})$/.test(id) ? 'android' :
1570
+ /^(3F|.{18}$)/.test(id) ? 'desktop' :
1571
+ 'unknown';
1572
+ exports.getDevice = getDevice;
1573
+ /** Upserts a receipt in the message */
1574
+ const updateMessageWithReceipt = (msg, receipt) => {
1575
+ msg.userReceipt = msg.userReceipt || [];
1576
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
1577
+ if (recp) {
1578
+ Object.assign(recp, receipt);
1579
+ }
1580
+ else {
1581
+ msg.userReceipt.push(receipt);
1582
+ }
1583
+ };
1584
+ exports.updateMessageWithReceipt = updateMessageWithReceipt;
1585
+ /** Update the message with a new reaction */
1586
+ const updateMessageWithReaction = (msg, reaction) => {
1587
+ const authorID = (0, generics_1.getKeyAuthor)(reaction.key);
1588
+ const reactions = (msg.reactions || [])
1589
+ .filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID);
1590
+ reaction.text = reaction.text || '';
1591
+ reactions.push(reaction);
1592
+ msg.reactions = reactions;
1593
+ };
1594
+ exports.updateMessageWithReaction = updateMessageWithReaction;
1595
+ /** Update the message with a new poll update */
1596
+ const updateMessageWithPollUpdate = (msg, update) => {
1597
+ var _a, _b;
1598
+ const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey);
1599
+ const reactions = (msg.pollUpdates || [])
1600
+ .filter(r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
1601
+ if ((_b = (_a = update.vote) === null || _a === void 0 ? void 0 : _a.selectedOptions) === null || _b === void 0 ? void 0 : _b.length) {
1602
+ reactions.push(update);
1603
+ }
1604
+ msg.pollUpdates = reactions;
1605
+ };
1606
+ exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate;
1607
+ /**
1608
+ * Aggregates all poll updates in a poll.
1609
+ * @param msg the poll creation message
1610
+ * @param meId your jid
1611
+ * @returns A list of options & their voters
1612
+ */
1613
+ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
1614
+ var _a, _b, _c;
1615
+ 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) || [];
1616
+ const voteHashMap = opts.reduce((acc, opt) => {
1617
+ const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString();
1618
+ acc[hash] = {
1619
+ name: opt.optionName || '',
1620
+ voters: []
1621
+ };
1622
+ return acc;
1623
+ }, {});
1624
+ for (const update of pollUpdates || []) {
507
1625
  const { vote } = update;
508
- if (!vote) continue;
509
-
510
- const selectedOptions = vote.selectedOptions || [];
511
- for (const option of selectedOptions) {
512
- if (!votes[option]) {
513
- votes[option] = [];
514
- voters[option] = new Set();
1626
+ if (!vote) {
1627
+ continue;
1628
+ }
1629
+ for (const option of vote.selectedOptions || []) {
1630
+ const hash = option.toString();
1631
+ let data = voteHashMap[hash];
1632
+ if (!data) {
1633
+ voteHashMap[hash] = {
1634
+ name: 'Unknown',
1635
+ voters: []
1636
+ };
1637
+ data = voteHashMap[hash];
515
1638
  }
516
-
517
- if (!voters[option].has(update.voter)) {
518
- votes[option].push(update.voter);
519
- voters[option].add(update.voter);
1639
+ voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId));
1640
+ }
1641
+ }
1642
+ return Object.values(voteHashMap);
1643
+ }
1644
+ /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
1645
+ const aggregateMessageKeysNotFromMe = (keys) => {
1646
+ const keyMap = {};
1647
+ for (const { remoteJid, id, participant, fromMe } of keys) {
1648
+ if (!fromMe) {
1649
+ const uqKey = `${remoteJid}:${participant || ''}`;
1650
+ if (!keyMap[uqKey]) {
1651
+ keyMap[uqKey] = {
1652
+ jid: remoteJid,
1653
+ participant: participant,
1654
+ messageIds: []
1655
+ };
1656
+ }
1657
+ keyMap[uqKey].messageIds.push(id);
1658
+ }
1659
+ }
1660
+ return Object.values(keyMap);
1661
+ };
1662
+ exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
1663
+ const REUPLOAD_REQUIRED_STATUS = [410, 404];
1664
+ /**
1665
+ * Downloads the given message. Throws an error if it's not a media message
1666
+ */
1667
+ const downloadMediaMessage = async (message, type, options, ctx) => {
1668
+ const result = await downloadMsg()
1669
+ .catch(async (error) => {
1670
+ var _a;
1671
+ if (ctx) {
1672
+ if (axios_1.default.isAxiosError(error)) {
1673
+ // check if the message requires a reupload
1674
+ if (REUPLOAD_REQUIRED_STATUS.includes((_a = error.response) === null || _a === void 0 ? void 0 : _a.status)) {
1675
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...');
1676
+ // request reupload
1677
+ message = await ctx.reuploadRequest(message);
1678
+ const result = await downloadMsg();
1679
+ return result;
1680
+ }
1681
+ }
1682
+ }
1683
+ throw error;
1684
+ });
1685
+ return result;
1686
+ async function downloadMsg() {
1687
+ const mContent = (0, exports.extractMessageContent)(message.message);
1688
+ if (!mContent) {
1689
+ throw new boom_1.Boom('No message present', { statusCode: 400, data: message });
1690
+ }
1691
+ const contentType = (0, exports.getContentType)(mContent);
1692
+ let mediaType = contentType === null || contentType === void 0 ? void 0 : contentType.replace('Message', '');
1693
+ const media = mContent[contentType];
1694
+ if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
1695
+ throw new boom_1.Boom(`"${contentType}" message is not a media message`);
1696
+ }
1697
+ let download;
1698
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
1699
+ download = {
1700
+ directPath: media.thumbnailDirectPath,
1701
+ mediaKey: media.mediaKey
1702
+ };
1703
+ mediaType = 'thumbnail-link';
1704
+ }
1705
+ else {
1706
+ download = media;
1707
+ }
1708
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options);
1709
+ if (type === 'buffer') {
1710
+ const bufferArray = [];
1711
+ for await (const chunk of stream) {
1712
+ bufferArray.push(chunk);
1713
+ }
1714
+ return Buffer.concat(bufferArray);
1715
+ }
1716
+ return stream;
1717
+ }
1718
+ };
1719
+ exports.downloadMediaMessage = downloadMediaMessage;
1720
+ /** Checks whether the given message is a media message; if it is returns the inner content */
1721
+ const assertMediaContent = (content) => {
1722
+ content = (0, exports.extractMessageContent)(content);
1723
+ const mediaContent = (content === null || content === void 0 ? void 0 : content.documentMessage)
1724
+ || (content === null || content === void 0 ? void 0 : content.imageMessage)
1725
+ || (content === null || content === void 0 ? void 0 : content.videoMessage)
1726
+ || (content === null || content === void 0 ? void 0 : content.audioMessage)
1727
+ || (content === null || content === void 0 ? void 0 : content.stickerMessage);
1728
+ if (!mediaContent) {
1729
+ throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content });
1730
+ }
1731
+ return mediaContent;
1732
+ };
1733
+ exports.assertMediaContent = assertMediaContent;
1734
+ const cache_manager_1 = require("./cache-manager");
1735
+ const performance_config_1 = require("./performance-config");
1736
+ /**
1737
+ * Get cache statistics for monitoring performance
1738
+ */
1739
+ const getCacheStats = () => {
1740
+ try {
1741
+ const cacheManager = cache_manager_1.default;
1742
+ const config = performance_config_1.getPerformanceConfig();
1743
+
1744
+ if (!cacheManager || !cacheManager.caches) {
1745
+ return {
1746
+ lidCache: { size: 0, maxSize: 0, ttl: config.cache.lidCache.ttl },
1747
+ jidCache: { size: 0, maxSize: 0, ttl: config.cache.jidCache.ttl }
1748
+ };
1749
+ }
1750
+
1751
+ const lidStats = cacheManager.getStats('lidCache');
1752
+ const jidStats = cacheManager.getStats('jidCache');
1753
+
1754
+ return {
1755
+ lidCache: {
1756
+ size: lidStats?.keys || 0,
1757
+ maxSize: lidStats?.max || config.cache.lidCache.maxSize || 0,
1758
+ ttl: config.cache.lidCache.ttl
1759
+ },
1760
+ jidCache: {
1761
+ size: jidStats?.keys || 0,
1762
+ maxSize: jidStats?.max || config.cache.jidCache.maxSize || 0,
1763
+ ttl: config.cache.jidCache.ttl
520
1764
  }
1765
+ };
1766
+ } catch (error) {
1767
+ const config = performance_config_1.getPerformanceConfig();
1768
+ return {
1769
+ lidCache: { size: 0, maxSize: 0, ttl: config.cache.lidCache.ttl },
1770
+ jidCache: { size: 0, maxSize: 0, ttl: config.cache.jidCache.ttl }
1771
+ };
1772
+ }
1773
+ };
1774
+ exports.getCacheStats = getCacheStats;
1775
+ /**
1776
+ * Clear all caches (useful for testing or memory management)
1777
+ */
1778
+ const clearCache = () => {
1779
+ try {
1780
+ const cacheManager = cache_manager_1.default;
1781
+ if (cacheManager && cacheManager.caches) {
1782
+ Object.keys(cacheManager.caches).forEach(cacheName => {
1783
+ const cache = cacheManager.caches[cacheName];
1784
+ if (cache && typeof cache.flushAll === 'function') {
1785
+ cache.flushAll();
1786
+ }
1787
+ });
521
1788
  }
1789
+ } catch (error) {
1790
+ // Silently fail if cache manager is not available
522
1791
  }
523
-
524
- return votes;
525
- }
1792
+ };
1793
+ exports.clearCache = clearCache;