ichigo-baileys 1.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.
Files changed (99) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +98 -0
  3. package/WAProto/index.d.ts +15933 -0
  4. package/WAProto/index.js +115857 -0
  5. package/engine-requirements.js +10 -0
  6. package/lib/Defaults/index.js +120 -0
  7. package/lib/Defaults/noxleyss-version.json +3 -0
  8. package/lib/Signal/Group/ciphertext-message.js +15 -0
  9. package/lib/Signal/Group/group-session-builder.js +64 -0
  10. package/lib/Signal/Group/group_cipher.js +96 -0
  11. package/lib/Signal/Group/index.js +57 -0
  12. package/lib/Signal/Group/keyhelper.js +55 -0
  13. package/lib/Signal/Group/queue-job.js +57 -0
  14. package/lib/Signal/Group/sender-chain-key.js +34 -0
  15. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  16. package/lib/Signal/Group/sender-key-message.js +69 -0
  17. package/lib/Signal/Group/sender-key-name.js +51 -0
  18. package/lib/Signal/Group/sender-key-record.js +53 -0
  19. package/lib/Signal/Group/sender-key-state.js +99 -0
  20. package/lib/Signal/Group/sender-message-key.js +29 -0
  21. package/lib/Signal/libsignal.js +184 -0
  22. package/lib/Socket/Client/index.js +18 -0
  23. package/lib/Socket/Client/types.js +13 -0
  24. package/lib/Socket/Client/websocket.js +72 -0
  25. package/lib/Socket/business.js +260 -0
  26. package/lib/Socket/chats.js +997 -0
  27. package/lib/Socket/groups.js +332 -0
  28. package/lib/Socket/index.js +10 -0
  29. package/lib/Socket/messages-recv.js +1149 -0
  30. package/lib/Socket/messages-send.js +944 -0
  31. package/lib/Socket/newsletter.js +279 -0
  32. package/lib/Socket/socket.js +646 -0
  33. package/lib/Socket/usync.js +70 -0
  34. package/lib/Store/index.js +8 -0
  35. package/lib/Store/make-in-memory-store.js +439 -0
  36. package/lib/Store/make-ordered-dictionary.js +81 -0
  37. package/lib/Store/object-repository.js +27 -0
  38. package/lib/Types/Auth.js +2 -0
  39. package/lib/Types/Call.js +2 -0
  40. package/lib/Types/Chat.js +4 -0
  41. package/lib/Types/Contact.js +2 -0
  42. package/lib/Types/Events.js +2 -0
  43. package/lib/Types/GroupMetadata.js +2 -0
  44. package/lib/Types/Label.js +27 -0
  45. package/lib/Types/LabelAssociation.js +9 -0
  46. package/lib/Types/Message.js +7 -0
  47. package/lib/Types/Newsletter.js +18 -0
  48. package/lib/Types/Product.js +2 -0
  49. package/lib/Types/Signal.js +2 -0
  50. package/lib/Types/Socket.js +2 -0
  51. package/lib/Types/State.js +2 -0
  52. package/lib/Types/USync.js +2 -0
  53. package/lib/Types/index.js +42 -0
  54. package/lib/Utils/auth-utils.js +199 -0
  55. package/lib/Utils/browser-utils.js +35 -0
  56. package/lib/Utils/business.js +234 -0
  57. package/lib/Utils/chat-utils.js +730 -0
  58. package/lib/Utils/crypto.js +193 -0
  59. package/lib/Utils/decode-wa-message.js +217 -0
  60. package/lib/Utils/event-buffer.js +518 -0
  61. package/lib/Utils/generics.js +467 -0
  62. package/lib/Utils/history.js +94 -0
  63. package/lib/Utils/index.js +35 -0
  64. package/lib/Utils/link-preview.js +126 -0
  65. package/lib/Utils/logger.js +7 -0
  66. package/lib/Utils/lt-hash.js +51 -0
  67. package/lib/Utils/make-mutex.js +43 -0
  68. package/lib/Utils/message-retry-manager.js +185 -0
  69. package/lib/Utils/messages-media.js +879 -0
  70. package/lib/Utils/messages.js +1944 -0
  71. package/lib/Utils/noise-handler.js +150 -0
  72. package/lib/Utils/noxleyss-event-stream.js +63 -0
  73. package/lib/Utils/process-message.js +404 -0
  74. package/lib/Utils/signal.js +153 -0
  75. package/lib/Utils/use-multi-file-auth-state.js +124 -0
  76. package/lib/Utils/validate-connection.js +229 -0
  77. package/lib/WABinary/constants.js +1303 -0
  78. package/lib/WABinary/decode.js +265 -0
  79. package/lib/WABinary/encode.js +250 -0
  80. package/lib/WABinary/generic-utils.js +110 -0
  81. package/lib/WABinary/index.js +21 -0
  82. package/lib/WABinary/jid-utils.js +85 -0
  83. package/lib/WABinary/types.js +2 -0
  84. package/lib/WAM/BinaryInfo.js +13 -0
  85. package/lib/WAM/constants.js +15350 -0
  86. package/lib/WAM/encode.js +155 -0
  87. package/lib/WAM/index.js +19 -0
  88. package/lib/WAUSync/Protocols/USyncContactProtocol.js +32 -0
  89. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +57 -0
  90. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +30 -0
  91. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +42 -0
  92. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -0
  93. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -0
  94. package/lib/WAUSync/Protocols/index.js +20 -0
  95. package/lib/WAUSync/USyncQuery.js +89 -0
  96. package/lib/WAUSync/USyncUser.js +26 -0
  97. package/lib/WAUSync/index.js +19 -0
  98. package/lib/index.js +116 -0
  99. package/package.json +119 -0
@@ -0,0 +1,1944 @@
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
+
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");
18
+ const Jimp = require("jimp");
19
+ const { zip } = require("fflate");
20
+ const { randomUUID } = require("crypto");
21
+
22
+ const MIMETYPE_MAP = {
23
+ image: 'image/jpeg',
24
+ video: 'video/mp4',
25
+ document: 'application/pdf',
26
+ audio: 'audio/ogg; codecs=opus',
27
+ sticker: 'image/webp',
28
+ 'product-catalog-image': 'image/jpeg',
29
+ };
30
+ const MessageTypeProto = {
31
+ 'image': Types_1.WAProto.Message.ImageMessage,
32
+ 'video': Types_1.WAProto.Message.VideoMessage,
33
+ 'audio': Types_1.WAProto.Message.AudioMessage,
34
+ 'sticker': Types_1.WAProto.Message.StickerMessage,
35
+ 'document': Types_1.WAProto.Message.DocumentMessage,
36
+ };
37
+ const ButtonType = WAProto_1.proto.Message.ButtonsMessage.HeaderType;
38
+
39
+ const extractUrlFromText = (text) => { var _a; return (_a = text.match(Defaults_1.URL_REGEX)) === null || _a === void 0 ? void 0 : _a[0]; };
40
+ const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
41
+ const url = extractUrlFromText(text);
42
+ if (!!getUrlInfo && url) {
43
+ try {
44
+ const urlInfo = await getUrlInfo(url);
45
+ return urlInfo;
46
+ } catch (error) {
47
+ logger === null || logger === void 0 ? void 0 : logger.warn({ trace: error.stack }, 'url generation failed');
48
+ }
49
+ }
50
+ };
51
+ const assertColor = async (color) => {
52
+ let assertedColor;
53
+ if (typeof color === 'number') {
54
+ assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1;
55
+ } else {
56
+ let hex = color.trim().replace('#', '');
57
+ if (hex.length <= 6) {
58
+ hex = 'FF' + hex.padStart(6, '0');
59
+ }
60
+ assertedColor = parseInt(hex, 16);
61
+ return assertedColor;
62
+ }
63
+ };
64
+
65
+ const prepareWAMessageMedia = async (message, options) => {
66
+ const logger = options.logger;
67
+ let mediaType;
68
+ for (const key of Defaults_1.MEDIA_KEYS) {
69
+ if (key in message) {
70
+ mediaType = key;
71
+ }
72
+ }
73
+ if (!mediaType) {
74
+ throw new boom_1.Boom('Invalid media type', { statusCode: 400 });
75
+ }
76
+ const uploadData = {
77
+ ...message,
78
+ media: message[mediaType]
79
+ };
80
+ delete uploadData[mediaType];
81
+
82
+ const cacheableKey = typeof uploadData.media === 'object' &&
83
+ ('url' in uploadData.media) &&
84
+ !!uploadData.media.url &&
85
+ !!options.mediaCache && (
86
+ mediaType + ':' + uploadData.media.url.toString());
87
+
88
+ if (mediaType === 'document' && !uploadData.fileName) {
89
+ uploadData.fileName = 'file';
90
+ }
91
+ if (!uploadData.mimetype) {
92
+ uploadData.mimetype = MIMETYPE_MAP[mediaType];
93
+ }
94
+
95
+ if (cacheableKey) {
96
+ const mediaBuff = options.mediaCache.get(cacheableKey);
97
+ if (mediaBuff) {
98
+ logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'got media cache hit');
99
+ const obj = Types_1.WAProto.Message.decode(mediaBuff);
100
+ const key = `${mediaType}Message`;
101
+ Object.assign(obj[key], { ...uploadData, media: undefined });
102
+ return obj;
103
+ }
104
+ }
105
+
106
+ const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
107
+ const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') &&
108
+ (typeof uploadData['jpegThumbnail'] === 'undefined');
109
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true;
110
+ const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
111
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
112
+
113
+ const { mediaKey, encWriteStream, bodyPath, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath, } = await (options.newsletter ? messages_media_1.prepareStream : messages_media_1.encryptedStream)(uploadData.media, options.mediaTypeOverride || mediaType, {
114
+ logger,
115
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
116
+ opts: options.options
117
+ });
118
+
119
+ const fileEncSha256B64 = (options.newsletter ? fileSha256 : fileEncSha256 !== null && fileEncSha256 !== void 0 ? fileEncSha256 : fileSha256).toString('base64');
120
+
121
+ const [{ mediaUrl, directPath, handle }] = await Promise.all([
122
+ (async () => {
123
+ const result = await options.upload(encWriteStream, { fileEncSha256B64, mediaType, timeoutMs: options.mediaUploadTimeoutMs });
124
+ logger === null || logger === void 0 ? void 0 : logger.debug({ mediaType, cacheableKey }, 'uploaded media');
125
+ return result;
126
+ })(),
127
+ (async () => {
128
+ try {
129
+ if (requiresThumbnailComputation) {
130
+ const { thumbnail, originalImageDimensions } = await (0, messages_media_1.generateThumbnail)(bodyPath, mediaType, options);
131
+ uploadData.jpegThumbnail = thumbnail;
132
+ if (!uploadData.width && originalImageDimensions) {
133
+ uploadData.width = originalImageDimensions.width;
134
+ uploadData.height = originalImageDimensions.height;
135
+ logger === null || logger === void 0 ? void 0 : logger.debug('set dimensions');
136
+ }
137
+ logger === null || logger === void 0 ? void 0 : logger.debug('generated thumbnail');
138
+ }
139
+ if (requiresDurationComputation) {
140
+ uploadData.seconds = await (0, messages_media_1.getAudioDuration)(bodyPath);
141
+ logger === null || logger === void 0 ? void 0 : logger.debug('computed audio duration');
142
+ }
143
+ if (requiresWaveformProcessing) {
144
+ uploadData.waveform = await (0, messages_media_1.getAudioWaveform)(bodyPath, logger);
145
+ logger === null || logger === void 0 ? void 0 : logger.debug('processed waveform');
146
+ }
147
+ if (requiresAudioBackground) {
148
+ uploadData.backgroundArgb = await assertColor(options.backgroundColor);
149
+ logger === null || logger === void 0 ? void 0 : logger.debug('computed backgroundColor audio status');
150
+ }
151
+ } catch (error) {
152
+ logger === null || logger === void 0 ? void 0 : logger.warn({ trace: error.stack }, 'failed to obtain extra info');
153
+ }
154
+ })(),
155
+ ]).finally(async () => {
156
+ if (!Buffer.isBuffer(encWriteStream)) {
157
+ encWriteStream.destroy();
158
+ }
159
+ if (didSaveToTmpPath && bodyPath) {
160
+ try {
161
+ await fs_1.promises.access(bodyPath);
162
+ await fs_1.promises.unlink(bodyPath);
163
+ logger === null || logger === void 0 ? void 0 : logger.debug('removed tmp file');
164
+ } catch (error) {
165
+ logger === null || logger === void 0 ? void 0 : logger.warn('failed to remove tmp file');
166
+ }
167
+ }
168
+ });
169
+
170
+ const obj = Types_1.WAProto.Message.fromObject({
171
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
172
+ url: handle ? undefined : mediaUrl,
173
+ directPath,
174
+ mediaKey: mediaKey,
175
+ fileEncSha256: fileEncSha256,
176
+ fileSha256,
177
+ fileLength,
178
+ mediaKeyTimestamp: handle ? undefined : (0, generics_1.unixTimestampSeconds)(),
179
+ ...uploadData,
180
+ media: undefined
181
+ })
182
+ });
183
+ if (uploadData.ptv) {
184
+ obj.ptvMessage = obj.videoMessage;
185
+ delete obj.videoMessage;
186
+ }
187
+ if (cacheableKey) {
188
+ logger === null || logger === void 0 ? void 0 : logger.debug({ cacheableKey }, 'set cache');
189
+ options.mediaCache.set(cacheableKey, Types_1.WAProto.Message.encode(obj).finish());
190
+ }
191
+ return obj;
192
+ };
193
+
194
+ const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
195
+ ephemeralExpiration = ephemeralExpiration || 0;
196
+ const content = {
197
+ ephemeralMessage: {
198
+ message: {
199
+ protocolMessage: {
200
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
201
+ ephemeralExpiration
202
+ }
203
+ }
204
+ }
205
+ };
206
+ return Types_1.WAProto.Message.fromObject(content);
207
+ };
208
+
209
+ async function processLocationThumbnail(input, options = {}) {
210
+ const { maxWidth = 300, maxHeight = 300, quality = 80 } = options;
211
+ let buffer;
212
+ if (typeof input === 'string' && (input.startsWith('http://') || input.startsWith('https://'))) {
213
+ const resp = await axios_1.default.get(input, { responseType: 'arraybuffer' });
214
+ buffer = Buffer.from(resp.data);
215
+ } else if (typeof input === 'string') {
216
+ buffer = await fs_1.promises.readFile(input);
217
+ } else if (Buffer.isBuffer(input)) {
218
+ buffer = input;
219
+ } else {
220
+ throw new Error('thumbnail must be URL, path or Buffer');
221
+ }
222
+ const image = await Jimp.read(buffer);
223
+ image.scaleToFit(maxWidth, maxHeight);
224
+ return await image.quality(quality).getBufferAsync(Jimp.MIME_JPEG);
225
+ }
226
+
227
+ async function prepareAlbumMessageContent(jid, albums, options) {
228
+ if (!Array.isArray(albums)) {
229
+ throw new Error('albums must be an array containing media objects.');
230
+ }
231
+ if (albums.length === 0) {
232
+ throw new Error('albums cannot be empty. At least one media item is required.');
233
+ }
234
+ const validCount = albums.filter((m) => 'image' in m || 'video' in m).length;
235
+ if (validCount === 0) {
236
+ throw new Error('albums contains no valid media. Use \'image\' or \'video\' keys.');
237
+ }
238
+
239
+ let mediaHandle;
240
+ let mediaMsg;
241
+ const message = [];
242
+ const albumMsg = generateWAMessageFromContent(
243
+ jid,
244
+ {
245
+ albumMessage: {
246
+ expectedImageCount: albums.filter((item) => 'image' in item).length,
247
+ expectedVideoCount: albums.filter((item) => 'video' in item).length,
248
+ },
249
+ },
250
+ options
251
+ );
252
+ await options.conn.relayMessage(jid, albumMsg.message, {
253
+ messageId: albumMsg.key.id,
254
+ });
255
+
256
+ for (const media of albums) {
257
+ let content = {};
258
+ if ('image' in media) {
259
+ content = { image: media.image };
260
+ } else if ('video' in media) {
261
+ content = { video: media.video };
262
+ } else {
263
+ continue;
264
+ }
265
+ mediaMsg = await generateWAMessage(
266
+ jid,
267
+ { ...content, ...media },
268
+ {
269
+ userJid: options.userJid,
270
+ upload: async (encFilePath, opts) => {
271
+ const up = await options.conn.waUploadToServer(encFilePath, {
272
+ ...opts,
273
+ newsletter: (0, WABinary_1.isJidNewsletter)(jid),
274
+ });
275
+ mediaHandle = up.handle;
276
+ return up;
277
+ },
278
+ ...options,
279
+ }
280
+ );
281
+ if (mediaMsg) {
282
+ mediaMsg.message.messageContextInfo = {
283
+ messageSecret: (0, crypto_1.randomBytes)(32),
284
+ messageAssociation: {
285
+ associationType: WAProto_1.proto.MessageAssociation.AssociationType.MEDIA_ALBUM,
286
+ parentMessageKey: albumMsg.key,
287
+ },
288
+ };
289
+ }
290
+ message.push(mediaMsg);
291
+ }
292
+ return message;
293
+ }
294
+
295
+ const generateForwardMessageContent = (message, forceForward) => {
296
+ var _a;
297
+ let content = message.message;
298
+ if (!content) {
299
+ throw new boom_1.Boom('no content in message', { statusCode: 400 });
300
+ }
301
+ content = normalizeMessageContent(content);
302
+ content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish());
303
+ let key = Object.keys(content)[0];
304
+ let score = ((_a = content[key].contextInfo) === null || _a === void 0 ? void 0 : _a.forwardingScore) || 0;
305
+ score += message.key.fromMe && !forceForward ? 0 : 1;
306
+ if (key === 'conversation') {
307
+ content.extendedTextMessage = { text: content[key] };
308
+ delete content.conversation;
309
+ key = 'extendedTextMessage';
310
+ }
311
+ if (score > 0) {
312
+ content[key].contextInfo = { forwardingScore: score, isForwarded: true };
313
+ } else {
314
+ content[key].contextInfo = {};
315
+ }
316
+ return content;
317
+ };
318
+
319
+ function extractIE(text, { extract = true, hyperlink = true, citation = true, latex = true } = {}) {
320
+ if (!extract) return { text, ie: [], inline_entities: [] };
321
+ const createIE = (type, ie) => {
322
+ if (type === 'hyperlink') return {
323
+ key: ie.key,
324
+ metadata: {
325
+ display_name: ie.text,
326
+ is_trusted: ie.is_trusted,
327
+ url: ie.url,
328
+ __typename: 'GenAIInlineLinkItem',
329
+ },
330
+ };
331
+ if (type === 'citation') return {
332
+ key: ie.key,
333
+ metadata: {
334
+ reference_id: ie.reference_id,
335
+ reference_url: ie.url,
336
+ reference_title: ie.url,
337
+ reference_display_name: ie.url,
338
+ sources: [],
339
+ __typename: 'GenAISearchCitationItem',
340
+ },
341
+ };
342
+ if (type === 'latex') return {
343
+ key: ie.key,
344
+ metadata: {
345
+ latex_expression: ie.text,
346
+ latex_image: { url: ie.url, width: Number(ie.width) || 100, height: Number(ie.height) || 100 },
347
+ font_height: Number(ie.font_height) || 83.333333333333,
348
+ padding: Number(ie.padding) || 15,
349
+ __typename: 'GenAILatexItem',
350
+ },
351
+ };
352
+ };
353
+ let ie = [], inline_entities = [], result = '', last = 0,
354
+ citation_index = 1, hyperlink_index = 0, latex_index = 0, stack = [];
355
+ for (let i = 0; i < text.length; i++) {
356
+ if (text[i] === '[' && text[i - 1] !== '\\') {
357
+ stack.push(i);
358
+ } else if (text[i] === ']' && (text[i + 1] === '(' || text[i + 1] === '<')) {
359
+ let start = stack.pop();
360
+ if (start == null) continue;
361
+ let open = text[i + 1], close = open === '(' ? ')' : '>',
362
+ type = open === '(' ? 'link' : 'latex', end = i + 2, depth = 1;
363
+ while (end < text.length && depth) {
364
+ if (text[end] === open && text[end - 1] !== '\\') depth++;
365
+ else if (text[end] === close && text[end - 1] !== '\\') depth--;
366
+ end++;
367
+ }
368
+ if (depth) continue;
369
+ let raw = text.slice(start + 1, i).trim(), url = text.slice(i + 2, end - 1).trim(), key, tag, data;
370
+ if (type === 'latex') {
371
+ if (!latex) continue;
372
+ let [txt = '', width = null, height = null, font_height = null, padding = null] = raw.split('|');
373
+ key = `LEVVI_LATEX_${latex_index++}`;
374
+ tag = `{{${key}}}${txt || 'image'}{{/${key}}}`;
375
+ data = { type: 'latex', ie: { key, text: txt, url, width, height, font_height, padding } };
376
+ } else if (raw) {
377
+ if (!hyperlink) continue;
378
+ const trusted = !url.startsWith('!');
379
+ if (!trusted) url = url.slice(1);
380
+ key = `LEVVI_HYPERLINK_${hyperlink_index++}`;
381
+ tag = `{{${key}}}${url}{{/${key}}}`;
382
+ data = { type: 'hyperlink', ie: { key, text: raw, url, is_trusted: trusted } };
383
+ } else {
384
+ if (!citation) continue;
385
+ key = `LEVVI_CITATION_${citation_index - 1}`;
386
+ tag = `{{${key}}}${url}{{/${key}}}`;
387
+ data = { type: 'citation', ie: { reference_id: citation_index++, key, text: '', url } };
388
+ }
389
+ result += text.slice(last, start) + tag;
390
+ last = end;
391
+ ie.push(data);
392
+ const entity = createIE(data.type, data.ie);
393
+ if (entity) inline_entities.push(entity);
394
+ i = end - 1;
395
+ }
396
+ }
397
+ result += text.slice(last);
398
+ return { text: result, ie, inline_entities };
399
+ }
400
+
401
+ async function waitAllPromises(input) {
402
+ const isPromise = (v) => v && typeof v.then === 'function';
403
+ const isObject = (v) => v && typeof v === 'object';
404
+ const deep = async (v) => {
405
+ if (isPromise(v)) return deep(await v);
406
+ if (Array.isArray(v)) return Promise.all(v.map(deep));
407
+ if (isObject(v)) {
408
+ const entries = await Promise.all(Object.entries(v).map(async ([k, val]) => [k, await deep(val)]));
409
+ return Object.fromEntries(entries);
410
+ }
411
+ return v;
412
+ };
413
+ return deep(await input);
414
+ }
415
+
416
+ function tokenizer(code, lang = 'javascript') {
417
+ const keywordsMap = {
418
+ javascript: new Set(['break','case','catch','continue','debugger','delete','do','else','finally','for','function','if','in','instanceof','new','return','switch','this','throw','try','typeof','var','void','while','with','true','false','null','undefined','class','const','let','super','extends','export','import','yield','static','constructor','async','await','get','set']),
419
+ typescript: new Set(['abstract','any','as','asserts','bigint','boolean','declare','enum','implements','infer','interface','is','keyof','module','namespace','never','readonly','require','number','object','override','private','protected','public','satisfies','string','symbol','type','unknown','using','from','break','case','catch','continue','do','else','finally','for','function','if','new','return','switch','this','throw','try','var','void','while','class','const','let','extends','import','export','async','await']),
420
+ python: new Set(['False','None','True','and','as','assert','async','await','break','class','continue','def','del','elif','else','except','finally','for','from','global','if','import','in','is','lambda','nonlocal','not','or','pass','raise','return','try','while','with','yield']),
421
+ java: new Set(['abstract','assert','boolean','break','byte','case','catch','char','class','const','continue','default','do','double','else','enum','extends','final','finally','float','for','goto','if','implements','import','instanceof','int','interface','long','native','new','package','private','protected','public','return','short','static','strictfp','super','switch','synchronized','this','throw','throws','transient','try','void','volatile','while']),
422
+ golang: new Set(['break','case','chan','const','continue','default','defer','else','fallthrough','for','func','go','goto','if','import','interface','map','package','range','return','select','struct','switch','type','var']),
423
+ c: new Set(['auto','break','case','char','const','continue','default','do','double','else','enum','extern','float','for','goto','if','int','long','register','return','short','signed','sizeof','static','struct','switch','typedef','union','unsigned','void','volatile','while']),
424
+ cpp: new Set(['alignas','alignof','and','auto','bool','break','case','catch','class','const','constexpr','continue','delete','do','double','else','enum','explicit','export','extern','false','float','for','friend','if','inline','int','long','mutable','namespace','new','noexcept','nullptr','operator','private','protected','public','return','short','signed','sizeof','static','struct','switch','template','this','throw','true','try','typedef','typename','union','unsigned','using','virtual','void','while']),
425
+ php: new Set(['abstract','and','array','as','break','callable','case','catch','class','clone','const','continue','declare','default','do','echo','else','elseif','empty','enddeclare','endfor','endforeach','endif','endswitch','endwhile','extends','final','finally','fn','for','foreach','function','global','goto','if','implements','include','include_once','instanceof','interface','match','namespace','new','null','or','private','protected','public','require','require_once','return','static','switch','throw','trait','try','use','var','while','yield']),
426
+ rust: new Set(['as','break','const','continue','crate','else','enum','extern','false','fn','for','if','impl','in','let','loop','match','mod','move','mut','pub','ref','return','self','Self','static','struct','super','trait','true','type','unsafe','use','where','while']),
427
+ html: new Set(['html','head','body','div','span','p','a','img','video','audio','script','style','link','meta','form','input','button','table','tr','td','th','ul','ol','li','section','article','header','footer','nav','main']),
428
+ bash: new Set(['if','then','else','elif','fi','for','while','do','done','case','esac','function','in','select','until','break','continue','return','export','readonly','local','declare']),
429
+ markdown: new Set(['#','##','###','####','#####','######']),
430
+ };
431
+ if (!lang || lang === 'txt' || lang === 'text' || lang === 'plaintext') {
432
+ return {
433
+ codeBlock: [{ codeContent: code, highlightType: 0 }],
434
+ unified_codeBlock: [{ content: code, type: 'DEFAULT' }],
435
+ };
436
+ }
437
+ const TYPE_MAP = { 0:'DEFAULT', 1:'KEYWORD', 2:'METHOD', 3:'STR', 4:'NUMBER', 5:'COMMENT' };
438
+ const keywords = keywordsMap[lang.toLowerCase()] || new Set();
439
+ const tokens = [];
440
+ let i = 0;
441
+ const push = (content, type) => {
442
+ if (!content) return;
443
+ const last = tokens[tokens.length - 1];
444
+ if (last && last.highlightType === type) last.codeContent += content;
445
+ else tokens.push({ codeContent: content, highlightType: type });
446
+ };
447
+ const isIdentifier = (char) => {
448
+ switch (lang.toLowerCase()) {
449
+ case 'css': return /[a-zA-Z0-9_$-]/.test(char);
450
+ case 'html': return /[a-zA-Z0-9_$:-]/.test(char);
451
+ default: return /[a-zA-Z0-9_$]/.test(char);
452
+ }
453
+ };
454
+ while (i < code.length) {
455
+ const c = code[i];
456
+ if (/\s/.test(c)) {
457
+ let s = i;
458
+ while (i < code.length && /\s/.test(code[i])) i++;
459
+ push(code.slice(s, i), 0);
460
+ continue;
461
+ }
462
+ if ((c === '/' && code[i + 1] === '/') || (c === '#' && ['python','bash'].includes(lang))) {
463
+ let s = i;
464
+ while (i < code.length && code[i] !== '\n') i++;
465
+ push(code.slice(s, i), 5);
466
+ continue;
467
+ }
468
+ if (c === '"' || c === "'" || c === '`') {
469
+ let s = i;
470
+ const q = c;
471
+ i++;
472
+ while (i < code.length) {
473
+ if (code[i] === '\\' && i + 1 < code.length) i += 2;
474
+ else if (code[i] === q) { i++; break; }
475
+ else i++;
476
+ }
477
+ push(code.slice(s, i), 3);
478
+ continue;
479
+ }
480
+ if (/[0-9]/.test(c)) {
481
+ let s = i;
482
+ while (i < code.length && /[0-9._]/.test(code[i])) i++;
483
+ push(code.slice(s, i), 4);
484
+ continue;
485
+ }
486
+ if (/[a-zA-Z_$]/.test(c)) {
487
+ let s = i;
488
+ while (i < code.length && isIdentifier(code[i])) i++;
489
+ const word = code.slice(s, i);
490
+ let type = 0;
491
+ if (keywords.has(word)) type = 1;
492
+ else if (lang === 'css') {
493
+ let j = i;
494
+ while (j < code.length && /\s/.test(code[j])) j++;
495
+ if (code[j] === ':') type = 1;
496
+ } else if (lang === 'html') {
497
+ let p = s - 1;
498
+ while (p >= 0 && /\s/.test(code[p])) p--;
499
+ if (code[p] === '<' || (code[p] === '/' && code[p-1] === '<')) type = 1;
500
+ }
501
+ if (type === 0) {
502
+ let j = i;
503
+ while (j < code.length && /\s/.test(code[j])) j++;
504
+ if (code[j] === '(') type = 2;
505
+ }
506
+ push(word, type);
507
+ continue;
508
+ }
509
+ push(c, 0);
510
+ i++;
511
+ }
512
+ return {
513
+ codeBlock: tokens,
514
+ unified_codeBlock: tokens.map(t => ({ content: t.codeContent, type: TYPE_MAP[t.highlightType] })),
515
+ };
516
+ }
517
+
518
+ function toTableMetadata(arr, { hyperlink = true, citation = true, latex = true } = {}) {
519
+ if (!Array.isArray(arr) || !arr.every(row => Array.isArray(row) && row.every(cell => typeof cell === 'string'))) {
520
+ throw new TypeError('Table must be a nested array of strings');
521
+ }
522
+ const [header, ...rows] = arr;
523
+ const maxLen = Math.max(header.length, ...rows.map(r => r.length));
524
+ const normalize = (r) => [...r, ...Array(maxLen - r.length).fill('')];
525
+ const unified_rows = [
526
+ { is_header: true, cells: normalize(header) },
527
+ ...rows.map(r => ({ is_header: false, cells: normalize(r) })),
528
+ ].map(row => {
529
+ const markdown_cells = row.cells.map(cell => {
530
+ const extracted = extractIE(cell, { hyperlink, citation, latex });
531
+ return { text: extracted.text, ...(extracted.inline_entities.length ? { inline_entities: extracted.inline_entities } : {}) };
532
+ });
533
+ return { ...row, ...(markdown_cells.some(c => c.inline_entities?.length) ? { markdown_cells } : {}) };
534
+ });
535
+ const rowsMeta = unified_rows.map(r => ({
536
+ items: r.cells,
537
+ ...(r.is_header ? { isHeading: true } : {}),
538
+ }));
539
+ return { title: '', rows: rowsMeta, unified_rows };
540
+ }
541
+
542
+ function newLayout(name, data, extra = {}) {
543
+ return {
544
+ ...extra,
545
+ view_model: {
546
+ [Array.isArray(data) ? 'primitives' : 'primitive']: data,
547
+ __typename: `GenAI${name}LayoutViewModel`,
548
+ },
549
+ };
550
+ }
551
+
552
+ const generateWAMessageContent = async (message, options) => {
553
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
554
+ var _p, _q;
555
+ let m = {};
556
+
557
+ if ('text' in message) {
558
+ const extContent = { text: message.text };
559
+ let urlInfo = message.linkPreview;
560
+ if (typeof urlInfo === 'undefined') {
561
+ urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
562
+ }
563
+ if (urlInfo) {
564
+ extContent.matchedText = urlInfo['matched-text'];
565
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail;
566
+ extContent.description = urlInfo.description;
567
+ extContent.title = urlInfo.title;
568
+ extContent.previewType = 0;
569
+ const img = urlInfo.highQualityThumbnail;
570
+ if (img) {
571
+ extContent.thumbnailDirectPath = img.directPath;
572
+ extContent.mediaKey = img.mediaKey;
573
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
574
+ extContent.thumbnailWidth = img.width;
575
+ extContent.thumbnailHeight = img.height;
576
+ extContent.thumbnailSha256 = img.fileSha256;
577
+ extContent.thumbnailEncSha256 = img.fileEncSha256;
578
+ }
579
+ }
580
+ if (options.backgroundColor) {
581
+ extContent.backgroundArgb = await assertColor(options.backgroundColor);
582
+ }
583
+ if (options.font) {
584
+ extContent.font = options.font;
585
+ }
586
+ m.extendedTextMessage = extContent;
587
+ } else if ('contacts' in message) {
588
+ const contactLen = message.contacts.contacts.length;
589
+ if (!contactLen) {
590
+ throw new boom_1.Boom('require atleast 1 contact', { statusCode: 400 });
591
+ }
592
+ if (contactLen === 1) {
593
+ m.contactMessage = Types_1.WAProto.Message.ContactMessage.fromObject(message.contacts.contacts[0]);
594
+ } else {
595
+ m.contactsArrayMessage = Types_1.WAProto.Message.ContactsArrayMessage.fromObject(message.contacts);
596
+ }
597
+ } else if ('location' in message) {
598
+ m.locationMessage = Types_1.WAProto.Message.LocationMessage.fromObject(message.location);
599
+ } else if ('react' in message) {
600
+ if (!message.react.senderTimestampMs) {
601
+ message.react.senderTimestampMs = Date.now();
602
+ }
603
+ m.reactionMessage = Types_1.WAProto.Message.ReactionMessage.fromObject(message.react);
604
+ } else if ('delete' in message) {
605
+ m.protocolMessage = {
606
+ key: message.delete,
607
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.REVOKE
608
+ };
609
+ } else if ('forward' in message) {
610
+ m = generateForwardMessageContent(message.forward, message.force);
611
+ } else if ('disappearingMessagesInChat' in message) {
612
+ const exp = typeof message.disappearingMessagesInChat === 'boolean' ?
613
+ (message.disappearingMessagesInChat ? Defaults_1.WA_DEFAULT_EPHEMERAL : 0) :
614
+ message.disappearingMessagesInChat;
615
+ m = prepareDisappearingMessageSettingContent(exp);
616
+ } else if ('groupInvite' in message) {
617
+ m.groupInviteMessage = {};
618
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
619
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
620
+ m.groupInviteMessage.caption = message.groupInvite.text;
621
+ m.groupInviteMessage.groupJid = message.groupInvite.jid;
622
+ m.groupInviteMessage.groupName = message.groupInvite.subject;
623
+ if (options.getProfilePicUrl) {
624
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
625
+ if (pfpUrl) {
626
+ const resp = await axios_1.default.get(pfpUrl, { responseType: 'arraybuffer' });
627
+ if (resp.status === 200) {
628
+ m.groupInviteMessage.jpegThumbnail = resp.data;
629
+ }
630
+ }
631
+ }
632
+ } else if ('pin' in message) {
633
+ m.pinInChatMessage = {};
634
+ m.messageContextInfo = {};
635
+
636
+ m.pinInChatMessage.key = WAProto_1.proto.MessageKey.create(message.pin.key);
637
+ m.pinInChatMessage.type = message.pin.type;
638
+ m.pinInChatMessage.senderTimestampMs = Date.now();
639
+
640
+ m.messageContextInfo.messageAddOnDurationInSecs =
641
+ message.pin.type === 1 ? message.pin.time || 86400 : 0;
642
+ } else if ('keep' in message) {
643
+ m.keepInChatMessage = {};
644
+ m.keepInChatMessage.key = message.keep;
645
+ m.keepInChatMessage.keepType = message.type;
646
+ m.keepInChatMessage.timestampMs = Date.now();
647
+ } else if ('call' in message) {
648
+ m = {
649
+ scheduledCallCreationMessage: {
650
+ scheduledTimestampMs: (_a = message.call.time) !== null && _a !== void 0 ? _a : Date.now(),
651
+ callType: (_b = message.call.type) !== null && _b !== void 0 ? _b : 1,
652
+ title: message.call.title
653
+ }
654
+ };
655
+ } else if ('paymentInvite' in message) {
656
+ m.paymentInviteMessage = {
657
+ serviceType: message.paymentInvite.type,
658
+ expiryTimestamp: message.paymentInvite.expiry
659
+ };
660
+ } else if ('buttonReply' in message) {
661
+ switch (message.type) {
662
+ case 'template':
663
+ m.templateButtonReplyMessage = {
664
+ selectedDisplayText: message.buttonReply.displayText,
665
+ selectedId: message.buttonReply.id,
666
+ selectedIndex: message.buttonReply.index,
667
+ };
668
+ break;
669
+ case 'plain':
670
+ m.buttonsResponseMessage = {
671
+ selectedButtonId: message.buttonReply.id,
672
+ selectedDisplayText: message.buttonReply.displayText,
673
+ type: WAProto_1.proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT,
674
+ };
675
+ break;
676
+ }
677
+ } else if ('ptv' in message && message.ptv) {
678
+ const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
679
+ m.ptvMessage = videoMessage;
680
+ } else if ('product' in message) {
681
+ const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
682
+ m.productMessage = Types_1.WAProto.Message.ProductMessage.fromObject({
683
+ ...message,
684
+ product: {
685
+ ...message.product,
686
+ productImage: imageMessage,
687
+ }
688
+ });
689
+ } else if ('order' in message) {
690
+ m.orderMessage = Types_1.WAProto.Message.OrderMessage.fromObject({
691
+ orderId: message.order.id,
692
+ thumbnail: message.order.thumbnail,
693
+ itemCount: message.order.itemCount,
694
+ status: message.order.status,
695
+ surface: message.order.surface,
696
+ orderTitle: message.order.title,
697
+ message: message.order.text,
698
+ sellerJid: message.order.seller,
699
+ token: message.order.token,
700
+ totalAmount1000: message.order.amount,
701
+ totalCurrencyCode: message.order.currency
702
+ });
703
+ } else if ('listReply' in message) {
704
+ m.listResponseMessage = { ...message.listReply };
705
+ } else if ('poll' in message) {
706
+ (_p = message.poll).selectableCount || (_p.selectableCount = 0);
707
+ (_q = message.poll).toAnnouncementGroup || (_q.toAnnouncementGroup = false);
708
+ if (!Array.isArray(message.poll.values)) {
709
+ throw new boom_1.Boom('Invalid poll values', { statusCode: 400 });
710
+ }
711
+ if (message.poll.selectableCount < 0
712
+ || message.poll.selectableCount > message.poll.values.length) {
713
+ throw new boom_1.Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, { statusCode: 400 });
714
+ }
715
+ m.messageContextInfo = {
716
+ messageSecret: message.poll.messageSecret || (0, crypto_1.randomBytes)(32),
717
+ };
718
+ const pollCreationMessage = {
719
+ name: message.poll.name,
720
+ selectableOptionsCount: message.poll.selectableCount,
721
+ options: message.poll.values.map(optionName => ({ optionName })),
722
+ };
723
+ if (message.poll.toAnnouncementGroup) {
724
+ m.pollCreationMessageV2 = pollCreationMessage;
725
+ } else {
726
+ if (message.poll.selectableCount === 1) {
727
+ m.pollCreationMessageV3 = pollCreationMessage;
728
+ } else {
729
+ m.pollCreationMessage = pollCreationMessage;
730
+ }
731
+ }
732
+ } else if ('event' in message) {
733
+ m.messageContextInfo = {
734
+ messageSecret: message.event.messageSecret || (0, crypto_1.randomBytes)(32),
735
+ };
736
+ m.eventMessage = { ...message.event };
737
+ } else if ('inviteAdmin' in message) {
738
+ m.newsletterAdminInviteMessage = {};
739
+ m.newsletterAdminInviteMessage.inviteExpiration = message.inviteAdmin.inviteExpiration;
740
+ m.newsletterAdminInviteMessage.caption = message.inviteAdmin.text;
741
+ m.newsletterAdminInviteMessage.newsletterJid = message.inviteAdmin.jid;
742
+ m.newsletterAdminInviteMessage.newsletterName = message.inviteAdmin.subject;
743
+ m.newsletterAdminInviteMessage.jpegThumbnail = message.inviteAdmin.thumbnail;
744
+ } else if ('requestPayment' in message) {
745
+ const sticker = ((_c = message === null || message === void 0 ? void 0 : message.requestPayment) === null || _c === void 0 ? void 0 : _c.sticker) ?
746
+ await prepareWAMessageMedia({ sticker: (_d = message === null || message === void 0 ? void 0 : message.requestPayment) === null || _d === void 0 ? void 0 : _d.sticker, ...options }, options)
747
+ : null;
748
+ let notes = {};
749
+ if ((_e = message === null || message === void 0 ? void 0 : message.requestPayment) === null || _e === void 0 ? void 0 : _e.sticker) {
750
+ notes = {
751
+ stickerMessage: {
752
+ ...sticker === null || sticker === void 0 ? void 0 : sticker.stickerMessage,
753
+ contextInfo: (_f = message === null || message === void 0 ? void 0 : message.requestPayment) === null || _f === void 0 ? void 0 : _f.contextInfo
754
+ }
755
+ };
756
+ } else if (message.requestPayment.note) {
757
+ notes = {
758
+ extendedTextMessage: {
759
+ text: message.requestPayment.note,
760
+ contextInfo: (_g = message === null || message === void 0 ? void 0 : message.requestPayment) === null || _g === void 0 ? void 0 : _g.contextInfo,
761
+ }
762
+ };
763
+ } else {
764
+ throw new boom_1.Boom('Invalid media type', { statusCode: 400 });
765
+ }
766
+ m.requestPaymentMessage = Types_1.WAProto.Message.RequestPaymentMessage.fromObject({
767
+ expiryTimestamp: message.requestPayment.expiry,
768
+ amount1000: message.requestPayment.amount,
769
+ currencyCodeIso4217: message.requestPayment.currency,
770
+ requestFrom: message.requestPayment.from,
771
+ noteMessage: { ...notes },
772
+ background: (_h = message.requestPayment.background) !== null && _h !== void 0 ? _h : null,
773
+ });
774
+ } else if ('sharePhoneNumber' in message) {
775
+ m.protocolMessage = {
776
+ type: WAProto_1.proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
777
+ };
778
+ } else if ('requestPhoneNumber' in message) {
779
+ m.requestPhoneNumberMessage = {};
780
+ } else if ('album' in message) {
781
+ const imageMessages = message.album.filter(item => 'image' in item);
782
+ const videoMessages = message.album.filter(item => 'video' in item);
783
+ m.albumMessage = WAProto_1.proto.Message.AlbumMessage.fromObject({
784
+ expectedImageCount: imageMessages.length,
785
+ expectedVideoCount: videoMessages.length,
786
+ });
787
+ } else if ('groupStatus' in message) {
788
+ const gs = message.groupStatus;
789
+ if (!gs.message) throw new Error('groupStatus.message is required');
790
+ const audienceType = gs.audienceType || 0;
791
+ const backgroundColor = gs.backgroundArgb;
792
+ const textColor = gs.textArgb;
793
+ const font = gs.font;
794
+
795
+ let messageContent = gs.message;
796
+ if (typeof messageContent === 'string') {
797
+ messageContent = {
798
+ extendedTextMessage: {
799
+ text: messageContent,
800
+ contextInfo: {
801
+ statusAudienceMetadata: { audienceType }
802
+ },
803
+ ...(backgroundColor && { backgroundArgb: backgroundColor }),
804
+ ...(textColor && { textArgb: textColor }),
805
+ ...(font && { font: font })
806
+ }
807
+ };
808
+ } else if (messageContent.extendedTextMessage) {
809
+ if (backgroundColor) messageContent.extendedTextMessage.backgroundArgb = backgroundColor;
810
+ if (textColor) messageContent.extendedTextMessage.textArgb = textColor;
811
+ if (font) messageContent.extendedTextMessage.font = font;
812
+ messageContent.extendedTextMessage.contextInfo = {
813
+ ...messageContent.extendedTextMessage.contextInfo,
814
+ statusAudienceMetadata: { audienceType }
815
+ };
816
+ } else if (
817
+ messageContent.imageMessage ||
818
+ messageContent.videoMessage ||
819
+ messageContent.audioMessage ||
820
+ messageContent.documentMessage
821
+ ) {
822
+ const key = Object.keys(messageContent)[0];
823
+ if (!messageContent[key].contextInfo) {
824
+ messageContent[key].contextInfo = {};
825
+ }
826
+ messageContent[key].contextInfo.statusAudienceMetadata = {
827
+ audienceType: audienceType
828
+ };
829
+ }
830
+ m = { groupStatusMessageV2: { message: messageContent } };
831
+ } else if ('orderStatus' in message) {
832
+ const os = message.orderStatus;
833
+ if (!os.image) throw new Error('image is required for orderStatus');
834
+ let imageInput = os.image;
835
+ if (typeof imageInput === 'string' && !imageInput.startsWith('http://') && !imageInput.startsWith('https://')) {
836
+ imageInput = await fs_1.promises.readFile(imageInput);
837
+ }
838
+ const media = await prepareWAMessageMedia(
839
+ { image: imageInput },
840
+ { upload: options.upload, ...options }
841
+ );
842
+ m = {
843
+ viewOnceMessage: {
844
+ message: {
845
+ messageContextInfo: {
846
+ deviceListMetadata: {},
847
+ deviceListMetadataVersion: 2
848
+ },
849
+ interactiveMessage: WAProto_1.proto.Message.InteractiveMessage.create({
850
+ header: {
851
+ title: os.title || 'Order Status',
852
+ hasMediaAttachment: true,
853
+ ...media
854
+ },
855
+ body: {
856
+ text: os.text || 'Silakan cek status pesanan Anda.'
857
+ },
858
+ footer: {
859
+ text: os.footer || 'Powered by LevviCode'
860
+ },
861
+ nativeFlowMessage: {
862
+ buttons: [
863
+ {
864
+ name: 'order_status',
865
+ buttonParamsJson: JSON.stringify({
866
+ reference_id: os.referenceId || 'LV-001',
867
+ order: {
868
+ status: os.status || 'PROCESSING',
869
+ subtotal: {
870
+ value: os.subtotalValue || 0,
871
+ offset: os.subtotalOffset || 100
872
+ },
873
+ tax: {
874
+ value: os.taxValue || 0,
875
+ offset: os.taxOffset || 100
876
+ },
877
+ currency: os.currency || 'IDR'
878
+ }
879
+ })
880
+ }
881
+ ]
882
+ }
883
+ })
884
+ }
885
+ }
886
+ };
887
+ } else if ('buttonsMessage' in message) {
888
+ const bm = message.buttonsMessage
889
+
890
+ let locationMessage
891
+
892
+ if (bm.locationMessage) {
893
+ const loc = bm.locationMessage
894
+ const thumbInput = loc.jpegThumbnail || loc.thumbnail
895
+
896
+ let thumb
897
+ if (thumbInput) {
898
+ thumb = await processLocationThumbnail(thumbInput)
899
+ }
900
+
901
+ locationMessage = WAProto_1.proto.Message.LocationMessage.create({
902
+ degreesLatitude: loc.degreesLatitude || 0,
903
+ degreesLongitude: loc.degreesLongitude || 0,
904
+ name: loc.name || '',
905
+ address: loc.address || '',
906
+ jpegThumbnail: thumb
907
+ })
908
+ }
909
+
910
+ const buttons = (bm.buttons || []).map(btn =>
911
+ WAProto_1.proto.Message.ButtonsMessage.Button.create({
912
+ buttonId: btn.buttonId || '',
913
+ buttonText: WAProto_1.proto.Message.ButtonsMessage.Button.ButtonText.create({
914
+ displayText: btn.buttonText?.displayText || ''
915
+ }),
916
+ type: btn.type || 1,
917
+ nativeFlowInfo: btn.nativeFlowInfo
918
+ ? WAProto_1.proto.Message.ButtonsMessage.Button.NativeFlowInfo.create({
919
+ name: btn.nativeFlowInfo.name || '',
920
+ paramsJson: btn.nativeFlowInfo.paramsJson || '',
921
+ buttonParamsJson:
922
+ btn.nativeFlowInfo.buttonParamsJson ||
923
+ btn.nativeFlowInfo.paramsJson ||
924
+ ''
925
+ })
926
+ : undefined
927
+ })
928
+ )
929
+
930
+ m = {
931
+ buttonsMessage: WAProto_1.proto.Message.ButtonsMessage.create({
932
+ locationMessage,
933
+ contentText: bm.contentText || '',
934
+ footerText: bm.footerText || '',
935
+ buttons,
936
+ headerType: bm.headerType || 6,
937
+ viewOnce: !!bm.viewOnce
938
+ })
939
+ }
940
+ } else if ('stickerPack' in message) {
941
+ const {
942
+ stickers: stickers,
943
+ cover: cover,
944
+ name: name,
945
+ publisher: publisher,
946
+ packId: packId,
947
+ description: description,
948
+ } = message.stickerPack;
949
+ const stickerData = {};
950
+ const stickerPromises = stickers.map(async (s, i) => {
951
+ let buffer;
952
+ if (typeof s.sticker === 'string' && (s.sticker.startsWith('http://') || s.sticker.startsWith('https://'))) {
953
+ const resp = await axios_1.default.get(s.sticker, { responseType: 'arraybuffer' });
954
+ buffer = Buffer.from(resp.data);
955
+ } else if (typeof s.sticker === 'string') {
956
+ buffer = await fs_1.promises.readFile(s.sticker);
957
+ } else if (Buffer.isBuffer(s.sticker)) {
958
+ buffer = s.sticker;
959
+ } else {
960
+ throw new Error('sticker must be URL, path or Buffer');
961
+ }
962
+ const hash = (0, crypto_2.sha256)(buffer).toString('base64url');
963
+ const fileName = `${i.toString().padStart(2, '0')}_${hash}.webp`;
964
+ stickerData[fileName] = [new Uint8Array(buffer), { level: 0 }];
965
+ return {
966
+ fileName: fileName,
967
+ mimetype: 'image/webp',
968
+ isAnimated: s.isAnimated || false,
969
+ isLottie: s.isLottie || false,
970
+ emojis: s.emojis || [],
971
+ accessibilityLabel: s.accessibilityLabel || '',
972
+ };
973
+ });
974
+ const stickerMetadata = await Promise.all(stickerPromises);
975
+
976
+ const zipBuffer = await new Promise((resolve, reject) => {
977
+ zip(stickerData, (err, data) => {
978
+ if (err) reject(err);
979
+ else resolve(Buffer.from(data));
980
+ });
981
+ });
982
+
983
+ let coverBuffer;
984
+ if (typeof cover === 'string' && (cover.startsWith('http://') || cover.startsWith('https://'))) {
985
+ const resp = await axios_1.default.get(cover, { responseType: 'arraybuffer' });
986
+ coverBuffer = Buffer.from(resp.data);
987
+ } else if (typeof cover === 'string') {
988
+ coverBuffer = await fs_1.promises.readFile(cover);
989
+ } else if (Buffer.isBuffer(cover)) {
990
+ coverBuffer = cover;
991
+ } else {
992
+ throw new Error('cover must be URL, path or Buffer');
993
+ }
994
+
995
+ const [stickerPackUpload, coverUpload] = await Promise.all([
996
+ (0, messages_media_1.encryptedStream)(zipBuffer, 'sticker-pack', {
997
+ logger: options.logger,
998
+ opts: options.options,
999
+ }),
1000
+ prepareWAMessageMedia(
1001
+ { image: coverBuffer },
1002
+ { ...options, mediaTypeOverride: 'image' }
1003
+ ),
1004
+ ]);
1005
+
1006
+ const stickerPackUploadResult = await options.upload(stickerPackUpload.encWriteStream, {
1007
+ fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
1008
+ mediaType: 'sticker-pack',
1009
+ timeoutMs: options.mediaUploadTimeoutMs,
1010
+ });
1011
+
1012
+ const coverImage = coverUpload.imageMessage;
1013
+ const imageDataHash = (0, crypto_2.sha256)(coverBuffer).toString('base64');
1014
+ const stickerPackId = packId || (0, generics_1.generateMessageID)();
1015
+
1016
+ m.stickerPackMessage = {
1017
+ name: name,
1018
+ publisher: publisher,
1019
+ stickerPackId: stickerPackId,
1020
+ packDescription: description,
1021
+ stickerPackOrigin: WAProto_1.proto.Message.StickerPackMessage.StickerPackOrigin.THIRD_PARTY,
1022
+ stickerPackSize: stickerPackUpload.fileLength,
1023
+ stickers: stickerMetadata,
1024
+ fileSha256: stickerPackUpload.fileSha256,
1025
+ fileEncSha256: stickerPackUpload.fileEncSha256,
1026
+ mediaKey: stickerPackUpload.mediaKey,
1027
+ directPath: stickerPackUploadResult.directPath,
1028
+ fileLength: stickerPackUpload.fileLength,
1029
+ mediaKeyTimestamp: (0, generics_1.unixTimestampSeconds)(),
1030
+ trayIconFileName: `${stickerPackId}.png`,
1031
+ imageDataHash: imageDataHash,
1032
+ thumbnailDirectPath: coverImage.directPath,
1033
+ thumbnailFileSha256: coverImage.fileSha256,
1034
+ thumbnailFileEncSha256: coverImage.fileEncSha256,
1035
+ thumbnailHeight: coverImage.height,
1036
+ thumbnailWidth: coverImage.width,
1037
+ };
1038
+ } else if ('richMessage' in message) {
1039
+ const rich = message.richMessage;
1040
+ const submessages = [];
1041
+ const sections = [];
1042
+ const richResponseSources = [];
1043
+
1044
+ if (rich.text) {
1045
+ let finalText = rich.text;
1046
+ finalText = finalText.replace(/\[citation\]\((.+?)\)/g, (match, url) => {
1047
+ return `[](\n${url})`;
1048
+ });
1049
+
1050
+ const parsed = typeof finalText === 'string' ? extractIE(finalText) : finalText;
1051
+ const text = parsed.text || parsed;
1052
+ const inline_entities = parsed.ie ? parsed.ie.map(({ type, ie }) => {
1053
+ if (type === 'hyperlink') return {
1054
+ key: ie.key,
1055
+ metadata: {
1056
+ display_name: ie.text,
1057
+ is_trusted: ie.is_trusted,
1058
+ url: ie.url,
1059
+ __typename: 'GenAIInlineLinkItem'
1060
+ }
1061
+ };
1062
+ if (type === 'citation') return {
1063
+ key: ie.key,
1064
+ metadata: {
1065
+ reference_id: ie.reference_id,
1066
+ reference_url: ie.url,
1067
+ reference_title: ie.url,
1068
+ reference_display_name: ie.url,
1069
+ sources: [],
1070
+ __typename: 'GenAISearchCitationItem'
1071
+ }
1072
+ };
1073
+ if (type === 'latex') return {
1074
+ key: ie.key,
1075
+ metadata: {
1076
+ latex_expression: ie.text || '',
1077
+ latex_image: {
1078
+ url: ie.url,
1079
+ width: Number(ie.width) || 100,
1080
+ height: Number(ie.height) || 100
1081
+ },
1082
+ font_height: Number(ie.font_height) || 83.33,
1083
+ padding: Number(ie.padding) || 15,
1084
+ __typename: 'GenAILatexItem'
1085
+ }
1086
+ };
1087
+ return null;
1088
+ }).filter(Boolean) : [];
1089
+ submessages.push({ messageType: 2, messageText: text });
1090
+ sections.push(newLayout('Single', {
1091
+ text,
1092
+ ...(inline_entities.length && { inline_entities }),
1093
+ __typename: 'GenAIMarkdownTextUXPrimitive'
1094
+ }));
1095
+ }
1096
+
1097
+ if (rich.code) {
1098
+ const { language, code } = rich.code;
1099
+ const tok = tokenizer(code, language);
1100
+ submessages.push({ messageType: 5, codeMetadata: { codeLanguage: language, codeBlocks: tok.codeBlock } });
1101
+ sections.push(newLayout('Single', { language, code_blocks: tok.unified_codeBlock, __typename: 'GenAICodeUXPrimitive' }));
1102
+ }
1103
+
1104
+ if (rich.table) {
1105
+ const meta = toTableMetadata(rich.table);
1106
+ submessages.push({ messageType: 4, tableMetadata: { title: meta.title, rows: meta.rows } });
1107
+ sections.push(newLayout('Single', { rows: meta.unified_rows, __typename: 'GenATableUXPrimitive' }));
1108
+ }
1109
+
1110
+ if (rich.image) {
1111
+ const url = rich.image;
1112
+ submessages.push({
1113
+ messageType: 1,
1114
+ gridImageMetadata: {
1115
+ gridImageUrl: { imagePreviewUrl: url },
1116
+ imageUrls: [{ imagePreviewUrl: url, imageHighResUrl: url, sourceUrl: 'https://www.levvicode.cloud/' }]
1117
+ }
1118
+ });
1119
+ sections.push(newLayout('Single', {
1120
+ media: { url, mime_type: 'image/jpeg' },
1121
+ imagine_type: 3,
1122
+ status: { status: 'READY' },
1123
+ __typename: 'GenAIImaginePrimitive'
1124
+ }));
1125
+ }
1126
+
1127
+ if (rich.images) {
1128
+ const urls = Array.isArray(rich.images) ? rich.images : [rich.images];
1129
+ const imageUrls = urls.map(url => ({
1130
+ imagePreviewUrl: url,
1131
+ imageHighResUrl: url,
1132
+ sourceUrl: 'https://www.levvicode.cloud/'
1133
+ }));
1134
+ submessages.push({
1135
+ messageType: 1,
1136
+ gridImageMetadata: {
1137
+ gridImageUrl: { imagePreviewUrl: imageUrls[0]?.imagePreviewUrl },
1138
+ imageUrls
1139
+ }
1140
+ });
1141
+ imageUrls.forEach(({ imagePreviewUrl }) => {
1142
+ sections.push(newLayout('Single', {
1143
+ media: { url: imagePreviewUrl, mime_type: 'image/jpeg' },
1144
+ imagine_type: 3,
1145
+ status: { status: 'READY' },
1146
+ __typename: 'GenAIImaginePrimitive'
1147
+ }));
1148
+ });
1149
+ }
1150
+
1151
+ if (rich.video) {
1152
+ const url = rich.video;
1153
+ sections.push(newLayout('Single', {
1154
+ media: { url, mime_type: 'video/mp4', duration: 10 },
1155
+ imagine_type: 'ANIMATE',
1156
+ status: { status: 'READY' },
1157
+ __typename: 'GenAIImaginePrimitive'
1158
+ }));
1159
+ }
1160
+
1161
+ if (rich.productSingle) {
1162
+ const product = rich.productSingle;
1163
+ sections.push({
1164
+ view_model: {
1165
+ primitive: {
1166
+ title: product.title,
1167
+ brand: product.brand,
1168
+ price: product.price,
1169
+ sale_price: product.sale_price,
1170
+ product_url: product.product_url,
1171
+ image: {
1172
+ url: product.image
1173
+ },
1174
+ additional_images: (product.additional_images || [{ url: product.icon || product.image }]),
1175
+ __typename: "GenAIProductItemCardPrimitive"
1176
+ },
1177
+ __typename: "GenAISingleLayoutViewModel"
1178
+ }
1179
+ });
1180
+ }
1181
+
1182
+ if (rich.productMultiple) {
1183
+ const products = rich.productMultiple;
1184
+ sections.push({
1185
+ view_model: {
1186
+ primitives: products.map(item => ({
1187
+ title: item.title,
1188
+ brand: item.brand,
1189
+ price: item.price,
1190
+ sale_price: item.sale_price,
1191
+ product_url: item.product_url,
1192
+ image: {
1193
+ url: item.image
1194
+ },
1195
+ additional_images: item.additional_images || [{ url: item.icon || item.image }],
1196
+ __typename: "GenAIProductItemCardPrimitive"
1197
+ })),
1198
+ __typename: "GenAIHScrollLayoutViewModel"
1199
+ }
1200
+ });
1201
+ }
1202
+
1203
+ if (rich.post) {
1204
+ const posts = Array.isArray(rich.post) ? rich.post : [rich.post];
1205
+ sections.push(newLayout("HScroll", posts.map(p => ({
1206
+ post_id: p.post_id || randomUUID(),
1207
+ source_app: p.source_app || "INSTAGRAM",
1208
+ post_type: p.post_type || "IMAGE",
1209
+ orientation: p.orientation || "LANDSCAPE",
1210
+ title: p.title || "",
1211
+ subtitle: p.subtitle || "",
1212
+ username: p.username || "",
1213
+ is_verified: !!p.is_verified,
1214
+ profile_picture_url: p.profile_picture_url || "",
1215
+ thumbnail_url: p.thumbnail_url || "",
1216
+ post_caption: p.post_caption || "",
1217
+ post_url: p.post_url || "https://levvicode.cloud",
1218
+ post_deeplink: p.post_deeplink || p.post_url || "https://levvicode.cloud",
1219
+ likes_count: p.likes_count || 0,
1220
+ comments_count: p.comments_count || 0,
1221
+ shares_count: p.shares_count || 0,
1222
+ views_count: p.views_count || 0,
1223
+ footer_label: p.footer_label || "",
1224
+ footer_icon: p.footer_icon || "",
1225
+ is_carousel: posts.length > 1,
1226
+ __typename: "GenAIPostPrimitive"
1227
+ }))));
1228
+ }
1229
+
1230
+ if (rich.reels) {
1231
+ const items = Array.isArray(rich.reels) ? rich.reels : [rich.reels];
1232
+ const reels = items.map(i => ({
1233
+ ...i,
1234
+ _avatar: i.profileIconUrl || i.profile_url || '',
1235
+ _thumbnail: i.thumbnailUrl || i.thumbnail || '',
1236
+ _videoUrl: i.videoUrl || i.url || ''
1237
+ }));
1238
+ submessages.push({
1239
+ messageType: 9,
1240
+ contentItemsMetadata: {
1241
+ contentType: 1,
1242
+ itemsMetadata: reels.map(item => ({
1243
+ reelItem: { title: item.title || '', profileIconUrl: item._avatar, thumbnailUrl: item._thumbnail, videoUrl: item._videoUrl }
1244
+ }))
1245
+ }
1246
+ });
1247
+ reels.forEach((item, idx) => richResponseSources.push({
1248
+ provider: 'LevviCode',
1249
+ thumbnailCDNURL: item._thumbnail,
1250
+ sourceProviderURL: item._videoUrl,
1251
+ sourceQuery: '',
1252
+ faviconCDNURL: item._avatar,
1253
+ citationNumber: idx + 1,
1254
+ sourceTitle: item.title || ''
1255
+ }));
1256
+ sections.push(newLayout('HScroll', reels.map(item => ({
1257
+ reels_url: item._videoUrl,
1258
+ thumbnail_url: item._thumbnail,
1259
+ creator: item.title || '',
1260
+ avatar_url: item._avatar,
1261
+ reels_title: item.reels_title || item.title || '',
1262
+ likes_count: item.likes_count || 0,
1263
+ shares_count: item.shares_count || 0,
1264
+ view_count: item.view_count || 0,
1265
+ reel_source: item.reel_source || 'IG',
1266
+ is_verified: !!(item.is_verified || item.verified),
1267
+ __typename: 'GenAIReelPrimitive'
1268
+ }))));
1269
+ }
1270
+
1271
+ if (rich.sources) {
1272
+ const sourceArr = Array.isArray(rich.sources) ? rich.sources : [rich.sources];
1273
+ const sourceItems = sourceArr.map(s => {
1274
+ if (typeof s === 'object' && !Array.isArray(s)) return s;
1275
+ let iconUrl = s[0];
1276
+ return {
1277
+ source_type: 'THIRD_PARTY',
1278
+ source_display_name: s[2] || '',
1279
+ source_subtitle: 'AI',
1280
+ source_url: s[1] || '',
1281
+ favicon: { url: iconUrl, mime_type: 'image/jpeg', width: 16, height: 16 }
1282
+ };
1283
+ });
1284
+ sections.push(newLayout('Single', { sources: sourceItems, __typename: 'GenAISearchResultPrimitive' }));
1285
+ }
1286
+
1287
+ if (rich.tip) sections.push(newLayout('Single', { text: rich.tip, __typename: 'GenAIMetadataTextPrimitive' }));
1288
+
1289
+ if (rich.suggestions) {
1290
+ const suggest = Array.isArray(rich.suggestions) ? rich.suggestions : [rich.suggestions];
1291
+ sections.push(newLayout('ActionRow', suggest.map(text => ({ prompt_text: text, prompt_type: 'SUGGESTED_PROMPT', __typename: 'GenAIFollowUpSuggestionPillPrimitive' }))));
1292
+ }
1293
+
1294
+ if (rich.footer) sections.push(newLayout('Single', { text: rich.footer, __typename: 'GenAIMetadataTextPrimitive' }));
1295
+
1296
+ const unifiedData = {
1297
+ response_id: randomUUID(),
1298
+ sections: await waitAllPromises(sections)
1299
+ };
1300
+
1301
+ m = {
1302
+ messageContextInfo: {
1303
+ deviceListMetadata: {},
1304
+ deviceListMetadataVersion: 2,
1305
+ botMetadata: {
1306
+ messageDisclaimerText: rich.title || '',
1307
+ richResponseSourcesMetadata: { sources: richResponseSources }
1308
+ }
1309
+ },
1310
+ botForwardedMessage: {
1311
+ message: {
1312
+ richResponseMessage: {
1313
+ messageType: 1,
1314
+ submessages: await waitAllPromises(submessages),
1315
+ unifiedResponse: { data: Buffer.from(JSON.stringify(unifiedData)).toString('base64') },
1316
+ contextInfo: { forwardingScore: 1, isForwarded: true, forwardedAiBotMessageInfo: { botJid: '0@bot' }, forwardOrigin: 4 }
1317
+ }
1318
+ }
1319
+ }
1320
+ };
1321
+ } else if ('richMessageV2' in message) {
1322
+ const rich = message.richMessageV2
1323
+ const submessages = []
1324
+ const sections = []
1325
+
1326
+ if (rich.text) {
1327
+ submessages.push({
1328
+ messageType: 2,
1329
+ messageText: rich.text
1330
+ })
1331
+
1332
+ sections.push({
1333
+ view_model: {
1334
+ primitive: {
1335
+ text: rich.text,
1336
+ __typename: 'GenAIMarkdownTextUXPrimitive'
1337
+ },
1338
+ __typename: 'GenAISingleLayoutViewModel'
1339
+ }
1340
+ })
1341
+ }
1342
+
1343
+ if (rich.table) {
1344
+ const rows = [
1345
+ {
1346
+ items: rich.table.headers.map(String),
1347
+ isHeading: true
1348
+ },
1349
+ ...rich.table.rows.map(row => ({
1350
+ items: row.map(String)
1351
+ }))
1352
+ ]
1353
+
1354
+ submessages.push({
1355
+ messageType: 4,
1356
+ tableMetadata: {
1357
+ title: rich.table.title || '',
1358
+ rows
1359
+ }
1360
+ })
1361
+
1362
+ sections.push({
1363
+ view_model: {
1364
+ primitive: {
1365
+ title: rich.table.title || '',
1366
+ rows,
1367
+ __typename: 'GenAITableUXPrimitive'
1368
+ },
1369
+ __typename: 'GenAISingleLayoutViewModel'
1370
+ }
1371
+ })
1372
+ }
1373
+
1374
+ if (rich.code) {
1375
+ const { codeBlock, unified_codeBlock } = tokenizer(
1376
+ rich.code.content,
1377
+ rich.code.language
1378
+ )
1379
+
1380
+ submessages.push({
1381
+ messageType: 5,
1382
+ codeMetadata: {
1383
+ codeLanguage: rich.code.language,
1384
+ codeBlocks: codeBlock
1385
+ }
1386
+ })
1387
+
1388
+ sections.push({
1389
+ view_model: {
1390
+ primitive: {
1391
+ language: rich.code.language,
1392
+ code_blocks: unified_codeBlock,
1393
+ __typename: 'GenAICodeUXPrimitive'
1394
+ },
1395
+ __typename: 'GenAISingleLayoutViewModel'
1396
+ }
1397
+ })
1398
+ }
1399
+
1400
+ m = {
1401
+ messageContextInfo: {
1402
+ deviceListMetadata: {},
1403
+ deviceListMetadataVersion: 2,
1404
+ botMetadata: {
1405
+ messageDisclaimerText: rich.disclaimer || '',
1406
+ richResponseSourcesMetadata: {
1407
+ sources: []
1408
+ }
1409
+ }
1410
+ },
1411
+ botForwardedMessage: {
1412
+ message: {
1413
+ richResponseMessage: {
1414
+ messageType: 1,
1415
+ submessages,
1416
+ unifiedResponse: {
1417
+ data: Buffer.from(JSON.stringify({
1418
+ response_id: randomUUID(),
1419
+ sections
1420
+ }))
1421
+ },
1422
+ contextInfo: {
1423
+ forwardingScore: 1,
1424
+ isForwarded: true,
1425
+ forwardedAiBotMessageInfo: {
1426
+ botJid: rich.botJid || '0@bot'
1427
+ },
1428
+ forwardOrigin: 4
1429
+ }
1430
+ }
1431
+ }
1432
+ }
1433
+ }
1434
+ }else {
1435
+ m = await prepareWAMessageMedia(message, options);
1436
+ }
1437
+
1438
+ if ('buttons' in message && !!message.buttons) {
1439
+ const buttonsMessage = {
1440
+ buttons: message.buttons.map(b => ({ ...b, type: WAProto_1.proto.Message.ButtonsMessage.Button.Type.RESPONSE }))
1441
+ };
1442
+ if ('text' in message) {
1443
+ buttonsMessage.contentText = message.text;
1444
+ buttonsMessage.headerType = ButtonType.EMPTY;
1445
+ } else {
1446
+ if ('caption' in message) {
1447
+ buttonsMessage.contentText = message.caption;
1448
+ }
1449
+ const type = Object.keys(m)[0].replace('Message', '').toUpperCase();
1450
+ buttonsMessage.headerType = ButtonType[type];
1451
+ Object.assign(buttonsMessage, m);
1452
+ }
1453
+ if ('title' in message && !!message.title) {
1454
+ buttonsMessage.text = message.title;
1455
+ buttonsMessage.headerType = ButtonType.TEXT;
1456
+ }
1457
+ if ('footer' in message && !!message.footer) {
1458
+ buttonsMessage.footerText = message.footer;
1459
+ }
1460
+ if ('contextInfo' in message && !!message.contextInfo) {
1461
+ buttonsMessage.contextInfo = message.contextInfo;
1462
+ }
1463
+ if ('mentions' in message && !!message.mentions) {
1464
+ buttonsMessage.contextInfo = { mentionedJid: message.mentions };
1465
+ }
1466
+ m = { buttonsMessage };
1467
+ } else if ('templateButtons' in message && !!message.templateButtons) {
1468
+ const msg = {
1469
+ hydratedButtons: message.hasOwnProperty("templateButtons") ? message.templateButtons : message.templateButtons
1470
+ };
1471
+ if ('text' in message) {
1472
+ msg.hydratedContentText = message.text;
1473
+ } else {
1474
+ if ('caption' in message) {
1475
+ msg.hydratedContentText = message.caption;
1476
+ }
1477
+ Object.assign(msg, m);
1478
+ }
1479
+ if ('footer' in message && !!message.footer) {
1480
+ msg.hydratedFooterText = message.footer;
1481
+ }
1482
+ m = {
1483
+ templateMessage: {
1484
+ fourRowTemplate: msg,
1485
+ hydratedTemplate: msg
1486
+ }
1487
+ };
1488
+ }
1489
+ if ('sections' in message && !!message.sections) {
1490
+ const listMessage = {
1491
+ sections: message.sections,
1492
+ buttonText: message.buttonText,
1493
+ title: message.title,
1494
+ footerText: message.footer,
1495
+ description: message.text,
1496
+ listType: WAProto_1.proto.Message.ListMessage.ListType.SINGLE_SELECT
1497
+ };
1498
+ m = { listMessage };
1499
+ }
1500
+ if ('interactiveButtons' in message && !!message.interactiveButtons) {
1501
+ const interactiveMessage = {
1502
+ nativeFlowMessage: Types_1.WAProto.Message.InteractiveMessage.NativeFlowMessage.fromObject({
1503
+ buttons: message.interactiveButtons,
1504
+ })
1505
+ };
1506
+ if ('text' in message) {
1507
+ interactiveMessage.body = {
1508
+ text: message.text
1509
+ };
1510
+ } else if ('caption' in message) {
1511
+ interactiveMessage.body = {
1512
+ text: message.caption
1513
+ };
1514
+ interactiveMessage.header = {
1515
+ title: message.title,
1516
+ subtitle: message.subtitle,
1517
+ hasMediaAttachment: (_j = message === null || message === void 0 ? void 0 : message.media) !== null && _j !== void 0 ? _j : false,
1518
+ };
1519
+ Object.assign(interactiveMessage.header, m);
1520
+ }
1521
+ if ('footer' in message && !!message.footer) {
1522
+ interactiveMessage.footer = {
1523
+ text: message.footer
1524
+ };
1525
+ }
1526
+ if ('title' in message && !!message.title) {
1527
+ interactiveMessage.header = {
1528
+ title: message.title,
1529
+ subtitle: message.subtitle,
1530
+ hasMediaAttachment: (_k = message === null || message === void 0 ? void 0 : message.media) !== null && _k !== void 0 ? _k : false,
1531
+ };
1532
+ Object.assign(interactiveMessage.header, m);
1533
+ }
1534
+ if ('contextInfo' in message && !!message.contextInfo) {
1535
+ interactiveMessage.contextInfo = message.contextInfo;
1536
+ }
1537
+ if ('mentions' in message && !!message.mentions) {
1538
+ interactiveMessage.contextInfo = { mentionedJid: message.mentions };
1539
+ }
1540
+ m = { interactiveMessage };
1541
+ }
1542
+ if ('shop' in message && !!message.shop) {
1543
+ const interactiveMessage = {
1544
+ shopStorefrontMessage: Types_1.WAProto.Message.InteractiveMessage.ShopMessage.fromObject({
1545
+ surface: message.shop,
1546
+ id: message.id
1547
+ })
1548
+ };
1549
+ if ('text' in message) {
1550
+ interactiveMessage.body = {
1551
+ text: message.text
1552
+ };
1553
+ } else if ('caption' in message) {
1554
+ interactiveMessage.body = {
1555
+ text: message.caption
1556
+ };
1557
+ interactiveMessage.header = {
1558
+ title: message.title,
1559
+ subtitle: message.subtitle,
1560
+ hasMediaAttachment: (_l = message === null || message === void 0 ? void 0 : message.media) !== null && _l !== void 0 ? _l : false,
1561
+ };
1562
+ Object.assign(interactiveMessage.header, m);
1563
+ }
1564
+ if ('footer' in message && !!message.footer) {
1565
+ interactiveMessage.footer = {
1566
+ text: message.footer
1567
+ };
1568
+ }
1569
+ if ('title' in message && !!message.title) {
1570
+ interactiveMessage.header = {
1571
+ title: message.title,
1572
+ subtitle: message.subtitle,
1573
+ hasMediaAttachment: (_m = message === null || message === void 0 ? void 0 : message.media) !== null && _m !== void 0 ? _m : false,
1574
+ };
1575
+ Object.assign(interactiveMessage.header, m);
1576
+ }
1577
+ if ('contextInfo' in message && !!message.contextInfo) {
1578
+ interactiveMessage.contextInfo = message.contextInfo;
1579
+ }
1580
+ if ('mentions' in message && !!message.mentions) {
1581
+ interactiveMessage.contextInfo = { mentionedJid: message.mentions };
1582
+ }
1583
+ m = { interactiveMessage };
1584
+ }
1585
+ if ('viewOnce' in message && !!message.viewOnce) {
1586
+ m = { viewOnceMessage: { message: m } };
1587
+ }
1588
+ if ('mentions' in message && ((_o = message.mentions) === null || _o === void 0 ? void 0 : _o.length)) {
1589
+ const [messageType] = Object.keys(m);
1590
+ m[messageType].contextInfo = m[messageType] || {};
1591
+ m[messageType].contextInfo.mentionedJid = message.mentions;
1592
+ }
1593
+ if ('edit' in message) {
1594
+ m = {
1595
+ protocolMessage: {
1596
+ key: message.edit,
1597
+ editedMessage: m,
1598
+ timestampMs: Date.now(),
1599
+ type: Types_1.WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
1600
+ }
1601
+ };
1602
+ }
1603
+ if ('contextInfo' in message && !!message.contextInfo) {
1604
+ const [messageType] = Object.keys(m);
1605
+ m[messageType] = m[messageType] || {};
1606
+ m[messageType].contextInfo = message.contextInfo;
1607
+ }
1608
+ return Types_1.WAProto.Message.fromObject(m);
1609
+ };
1610
+
1611
+ const generateWAMessageFromContent = (jid, message, options) => {
1612
+ if (!options.timestamp) {
1613
+ options.timestamp = new Date();
1614
+ }
1615
+ const innerMessage = normalizeMessageContent(message);
1616
+ const key = getContentType(innerMessage);
1617
+ const timestamp = (0, generics_1.unixTimestampSeconds)(options.timestamp);
1618
+ const { quoted, userJid } = options;
1619
+
1620
+ if (quoted && !(0, WABinary_1.isJidNewsletter)(jid)) {
1621
+ const participant = quoted.key.fromMe ? userJid : (quoted.participant || quoted.key.participant || quoted.key.remoteJid);
1622
+ let quotedMsg = normalizeMessageContent(quoted.message);
1623
+ const msgType = getContentType(quotedMsg);
1624
+ if (quotedMsg) {
1625
+ quotedMsg = WAProto_1.proto.Message.fromObject({ [msgType]: quotedMsg[msgType] });
1626
+ const quotedContent = quotedMsg[msgType];
1627
+ if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
1628
+ delete quotedContent.contextInfo;
1629
+ }
1630
+ const contextInfo = innerMessage[key].contextInfo || {};
1631
+ contextInfo.participant = (0, WABinary_1.jidNormalizedUser)(participant);
1632
+ contextInfo.stanzaId = quoted.key.id;
1633
+ contextInfo.quotedMessage = quotedMsg;
1634
+ if (jid !== quoted.key.remoteJid) {
1635
+ contextInfo.remoteJid = quoted.key.remoteJid;
1636
+ }
1637
+ innerMessage[key].contextInfo = contextInfo;
1638
+ }
1639
+ }
1640
+
1641
+ if (
1642
+ !!(options === null || options === void 0 ? void 0 : options.ephemeralExpiration) &&
1643
+ key !== 'protocolMessage' &&
1644
+ key !== 'ephemeralMessage' &&
1645
+ !(0, WABinary_1.isJidNewsletter)(jid)) {
1646
+ innerMessage[key].contextInfo = {
1647
+ ...(innerMessage[key].contextInfo || {}),
1648
+ expiration: options.ephemeralExpiration || Defaults_1.WA_DEFAULT_EPHEMERAL,
1649
+ };
1650
+ }
1651
+
1652
+ message = Types_1.WAProto.Message.fromObject(message);
1653
+ const messageJSON = {
1654
+ key: {
1655
+ remoteJid: jid,
1656
+ fromMe: true,
1657
+ id: (options === null || options === void 0 ? void 0 : options.messageId) || (0, generics_1.generateMessageIDV2)(),
1658
+ },
1659
+ message: message,
1660
+ messageTimestamp: timestamp,
1661
+ messageStubParameters: [],
1662
+ participant: (0, WABinary_1.isJidGroup)(jid) || (0, WABinary_1.isJidStatusBroadcast)(jid) ? userJid : undefined,
1663
+ status: Types_1.WAMessageStatus.PENDING
1664
+ };
1665
+ return Types_1.WAProto.WebMessageInfo.fromObject(messageJSON);
1666
+ };
1667
+
1668
+ const generateWAMessage = async (jid, content, options) => {
1669
+ var _a;
1670
+ options.logger = (_a = options === null || options === void 0 ? void 0 : options.logger) === null || _a === void 0 ? void 0 : _a.child({ msgId: options.messageId });
1671
+ return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { newsletter: (0, WABinary_1.isJidNewsletter)(jid), ...options }), options);
1672
+ };
1673
+
1674
+ const getContentType = (content) => {
1675
+ if (content) {
1676
+ const keys = Object.keys(content);
1677
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
1678
+ return key;
1679
+ }
1680
+ };
1681
+
1682
+ const normalizeMessageContent = (content) => {
1683
+ if (!content) {
1684
+ return undefined;
1685
+ }
1686
+ for (let i = 0; i < 5; i++) {
1687
+ const inner = getFutureProofMessage(content);
1688
+ if (!inner) {
1689
+ break;
1690
+ }
1691
+ content = inner.message;
1692
+ }
1693
+ return content;
1694
+ function getFutureProofMessage(message) {
1695
+ return ((message === null || message === void 0 ? void 0 : message.ephemeralMessage)
1696
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessage)
1697
+ || (message === null || message === void 0 ? void 0 : message.documentWithCaptionMessage)
1698
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2)
1699
+ || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension)
1700
+ || (message === null || message === void 0 ? void 0 : message.editedMessage)
1701
+ || (message === null || message === void 0 ? void 0 : message.groupMentionedMessage)
1702
+ || (message === null || message === void 0 ? void 0 : message.botInvokeMessage)
1703
+ || (message === null || message === void 0 ? void 0 : message.lottieStickerMessage)
1704
+ || (message === null || message === void 0 ? void 0 : message.eventCoverImage)
1705
+ || (message === null || message === void 0 ? void 0 : message.statusMentionMessage)
1706
+ || (message === null || message === void 0 ? void 0 : message.pollCreationOptionImageMessage)
1707
+ || (message === null || message === void 0 ? void 0 : message.associatedChildMessage)
1708
+ || (message === null || message === void 0 ? void 0 : message.groupStatusMentionMessage)
1709
+ || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV4)
1710
+ || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV5)
1711
+ || (message === null || message === void 0 ? void 0 : message.statusAddYours)
1712
+ || (message === null || message === void 0 ? void 0 : message.groupStatusMessage)
1713
+ || (message === null || message === void 0 ? void 0 : message.limitSharingMessage)
1714
+ || (message === null || message === void 0 ? void 0 : message.botTaskMessage)
1715
+ || (message === null || message === void 0 ? void 0 : message.questionMessage)
1716
+ || (message === null || message === void 0 ? void 0 : message.groupStatusMessageV2)
1717
+ || (message === null || message === void 0 ? void 0 : message.botForwardedMessage));
1718
+ }
1719
+ };
1720
+
1721
+ const extractMessageContent = (content) => {
1722
+ var _a, _b, _c, _d, _e, _f;
1723
+ const extractFromTemplateMessage = (msg) => {
1724
+ if (msg.imageMessage) {
1725
+ return { imageMessage: msg.imageMessage };
1726
+ } else if (msg.documentMessage) {
1727
+ return { documentMessage: msg.documentMessage };
1728
+ } else if (msg.videoMessage) {
1729
+ return { videoMessage: msg.videoMessage };
1730
+ } else if (msg.locationMessage) {
1731
+ return { locationMessage: msg.locationMessage };
1732
+ } else {
1733
+ return {
1734
+ conversation: 'contentText' in msg
1735
+ ? msg.contentText
1736
+ : ('hydratedContentText' in msg ? msg.hydratedContentText : '')
1737
+ };
1738
+ }
1739
+ };
1740
+ content = normalizeMessageContent(content);
1741
+ if (content === null || content === void 0 ? void 0 : content.buttonsMessage) {
1742
+ return extractFromTemplateMessage(content.buttonsMessage);
1743
+ }
1744
+ if ((_a = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _a === void 0 ? void 0 : _a.hydratedFourRowTemplate) {
1745
+ return extractFromTemplateMessage((_b = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _b === void 0 ? void 0 : _b.hydratedFourRowTemplate);
1746
+ }
1747
+ if ((_c = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _c === void 0 ? void 0 : _c.hydratedTemplate) {
1748
+ return extractFromTemplateMessage((_d = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _d === void 0 ? void 0 : _d.hydratedTemplate);
1749
+ }
1750
+ if ((_e = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _e === void 0 ? void 0 : _e.fourRowTemplate) {
1751
+ return extractFromTemplateMessage((_f = content === null || content === void 0 ? void 0 : content.templateMessage) === null || _f === void 0 ? void 0 : _f.fourRowTemplate);
1752
+ }
1753
+ return content;
1754
+ };
1755
+
1756
+ const getDevice = (id) => /^3A.{18}$/.test(id) ? 'ios' :
1757
+ /^3E.{20}$/.test(id) ? 'web' :
1758
+ /^(.{21}|.{32})$/.test(id) ? 'android' :
1759
+ /^(3F|.{18}$)/.test(id) ? 'desktop' :
1760
+ 'unknown';
1761
+
1762
+ const updateMessageWithReceipt = (msg, receipt) => {
1763
+ msg.userReceipt = msg.userReceipt || [];
1764
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
1765
+ if (recp) {
1766
+ Object.assign(recp, receipt);
1767
+ } else {
1768
+ msg.userReceipt.push(receipt);
1769
+ }
1770
+ };
1771
+
1772
+ const updateMessageWithReaction = (msg, reaction) => {
1773
+ const authorID = (0, generics_1.getKeyAuthor)(reaction.key);
1774
+ const reactions = (msg.reactions || [])
1775
+ .filter(r => (0, generics_1.getKeyAuthor)(r.key) !== authorID);
1776
+ reaction.text = reaction.text || '';
1777
+ reactions.push(reaction);
1778
+ msg.reactions = reactions;
1779
+ };
1780
+
1781
+ const updateMessageWithPollUpdate = (msg, update) => {
1782
+ var _a, _b;
1783
+ const authorID = (0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey);
1784
+ const reactions = (msg.pollUpdates || [])
1785
+ .filter(r => (0, generics_1.getKeyAuthor)(r.pollUpdateMessageKey) !== authorID);
1786
+ if ((_b = (_a = update.vote) === null || _a === void 0 ? void 0 : _a.selectedOptions) === null || _b === void 0 ? void 0 : _b.length) {
1787
+ reactions.push(update);
1788
+ }
1789
+ msg.pollUpdates = reactions;
1790
+ };
1791
+
1792
+ function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
1793
+ var _a, _b, _c;
1794
+ 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) || [];
1795
+ const voteHashMap = opts.reduce((acc, opt) => {
1796
+ const hash = (0, crypto_2.sha256)(Buffer.from(opt.optionName || '')).toString();
1797
+ acc[hash] = {
1798
+ name: opt.optionName || '',
1799
+ voters: []
1800
+ };
1801
+ return acc;
1802
+ }, {});
1803
+ for (const update of pollUpdates || []) {
1804
+ const { vote } = update;
1805
+ if (!vote) {
1806
+ continue;
1807
+ }
1808
+ for (const option of vote.selectedOptions || []) {
1809
+ const hash = option.toString();
1810
+ let data = voteHashMap[hash];
1811
+ if (!data) {
1812
+ voteHashMap[hash] = {
1813
+ name: 'Unknown',
1814
+ voters: []
1815
+ };
1816
+ data = voteHashMap[hash];
1817
+ }
1818
+ voteHashMap[hash].voters.push((0, generics_1.getKeyAuthor)(update.pollUpdateMessageKey, meId));
1819
+ }
1820
+ }
1821
+ return Object.values(voteHashMap);
1822
+ }
1823
+
1824
+ const aggregateMessageKeysNotFromMe = (keys) => {
1825
+ const keyMap = {};
1826
+ for (const { remoteJid, id, participant, fromMe } of keys) {
1827
+ if (!fromMe) {
1828
+ const uqKey = `${remoteJid}:${participant || ''}`;
1829
+ if (!keyMap[uqKey]) {
1830
+ keyMap[uqKey] = {
1831
+ jid: remoteJid,
1832
+ participant: participant,
1833
+ messageIds: []
1834
+ };
1835
+ }
1836
+ keyMap[uqKey].messageIds.push(id);
1837
+ }
1838
+ }
1839
+ return Object.values(keyMap);
1840
+ };
1841
+
1842
+ const REUPLOAD_REQUIRED_STATUS = [410, 404];
1843
+
1844
+ const downloadMediaMessage = async (message, type, options, ctx) => {
1845
+ const result = await downloadMsg()
1846
+ .catch(async (error) => {
1847
+ var _a;
1848
+ if (ctx) {
1849
+ if (axios_1.default.isAxiosError(error)) {
1850
+ if (REUPLOAD_REQUIRED_STATUS.includes((_a = error.response) === null || _a === void 0 ? void 0 : _a.status)) {
1851
+ ctx.logger.info({ key: message.key }, 'sending reupload media request...');
1852
+ message = await ctx.reuploadRequest(message);
1853
+ const result = await downloadMsg();
1854
+ return result;
1855
+ }
1856
+ }
1857
+ }
1858
+ throw error;
1859
+ });
1860
+ return result;
1861
+ async function downloadMsg() {
1862
+ const mContent = extractMessageContent(message.message);
1863
+ if (!mContent) {
1864
+ throw new boom_1.Boom('No message present', { statusCode: 400, data: message });
1865
+ }
1866
+ const contentType = getContentType(mContent);
1867
+ let mediaType = contentType === null || contentType === void 0 ? void 0 : contentType.replace('Message', '');
1868
+ const media = mContent[contentType];
1869
+ if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
1870
+ throw new boom_1.Boom(`"${contentType}" message is not a media message`);
1871
+ }
1872
+ let download;
1873
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
1874
+ download = {
1875
+ directPath: media.thumbnailDirectPath,
1876
+ mediaKey: media.mediaKey
1877
+ };
1878
+ mediaType = 'thumbnail-link';
1879
+ } else {
1880
+ download = media;
1881
+ }
1882
+ const stream = await (0, messages_media_1.downloadContentFromMessage)(download, mediaType, options);
1883
+ if (type === 'buffer') {
1884
+ const bufferArray = [];
1885
+ for await (const chunk of stream) {
1886
+ bufferArray.push(chunk);
1887
+ }
1888
+ return Buffer.concat(bufferArray);
1889
+ }
1890
+ return stream;
1891
+ }
1892
+ };
1893
+
1894
+ const assertMediaContent = (content) => {
1895
+ content = extractMessageContent(content);
1896
+ const mediaContent = (content === null || content === void 0 ? void 0 : content.documentMessage)
1897
+ || (content === null || content === void 0 ? void 0 : content.imageMessage)
1898
+ || (content === null || content === void 0 ? void 0 : content.videoMessage)
1899
+ || (content === null || content === void 0 ? void 0 : content.audioMessage)
1900
+ || (content === null || content === void 0 ? void 0 : content.stickerMessage);
1901
+ if (!mediaContent) {
1902
+ throw new boom_1.Boom('given message is not a media message', { statusCode: 400, data: content });
1903
+ }
1904
+ return mediaContent;
1905
+ };
1906
+
1907
+ const toJid = (id) => {
1908
+ if (!id) return '';
1909
+ if (id.endsWith('@lid')) return id.replace('@lid', '@s.whatsapp.net');
1910
+ if (id.includes('@')) return id;
1911
+ return `${id}@s.whatsapp.net`;
1912
+ };
1913
+
1914
+ const getSenderLid = (message) => {
1915
+ const sender = message.key.participant || message.key.remoteJid;
1916
+ const user = (0, WABinary_1.jidDecode)(sender)?.user || '';
1917
+ const lid = (0, WABinary_1.jidEncode)(user, 'lid');
1918
+ console.log('sender lid:', lid);
1919
+ return { jid: sender, lid };
1920
+ };
1921
+
1922
+ exports.extractUrlFromText = extractUrlFromText;
1923
+ exports.generateLinkPreviewIfRequired = generateLinkPreviewIfRequired;
1924
+ exports.prepareWAMessageMedia = prepareWAMessageMedia;
1925
+ exports.prepareDisappearingMessageSettingContent = prepareDisappearingMessageSettingContent;
1926
+ exports.generateForwardMessageContent = generateForwardMessageContent;
1927
+ exports.generateWAMessageContent = generateWAMessageContent;
1928
+ exports.generateWAMessageFromContent = generateWAMessageFromContent;
1929
+ exports.generateWAMessage = generateWAMessage;
1930
+ exports.getContentType = getContentType;
1931
+ exports.normalizeMessageContent = normalizeMessageContent;
1932
+ exports.extractMessageContent = extractMessageContent;
1933
+ exports.getDevice = getDevice;
1934
+ exports.updateMessageWithReceipt = updateMessageWithReceipt;
1935
+ exports.updateMessageWithReaction = updateMessageWithReaction;
1936
+ exports.updateMessageWithPollUpdate = updateMessageWithPollUpdate;
1937
+ exports.getAggregateVotesInPollMessage = getAggregateVotesInPollMessage;
1938
+ exports.aggregateMessageKeysNotFromMe = aggregateMessageKeysNotFromMe;
1939
+ exports.downloadMediaMessage = downloadMediaMessage;
1940
+ exports.assertMediaContent = assertMediaContent;
1941
+ exports.toJid = toJid;
1942
+ exports.getSenderLid = getSenderLid;
1943
+ exports.prepareAlbumMessageContent = prepareAlbumMessageContent;
1944
+ exports.processLocationThumbnail = processLocationThumbnail;