@crysnovax/baileys 2.5.6 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/README.md[old] +1612 -0
- package/WAProto/index.js +14789 -3578
- package/lib/Defaults/index.js +14 -7
- package/lib/Signal/libsignal.js +40 -18
- package/lib/Socket/business.js +2 -1
- package/lib/Socket/chats.js +266 -92
- package/lib/Socket/chats.js[past] +1213 -0
- package/lib/Socket/communities.js +2 -1
- package/lib/Socket/groups.js +24 -5
- package/lib/Socket/messages-recv.js +617 -322
- package/lib/Socket/messages-send.js +160 -61
- package/lib/Socket/newsletter.js +4 -4
- package/lib/Socket/socket.js +49 -20
- package/lib/Types/{Newsletter.js → Mex.js} +10 -4
- package/lib/Types/State.js +37 -1
- package/lib/Types/index.js +1 -1
- package/lib/Utils/auth-utils.js +13 -1
- package/lib/Utils/chat-utils.js +81 -21
- package/lib/Utils/decode-wa-message.js +35 -1
- package/lib/Utils/index.js +1 -0
- package/lib/Utils/messages-media.js +28 -16
- package/lib/Utils/messages-media.js[past] +842 -0
- package/lib/Utils/messages.js +325 -352
- package/lib/Utils/signal.js +43 -1
- package/lib/Utils/tc-token-utils.js +150 -5
- package/lib/Utils/use-sqlite-auth-state.js +108 -0
- package/lib/WABinary/constants.js +91 -0
- package/package.json +45 -10
package/lib/Utils/messages.js
CHANGED
|
@@ -2,16 +2,16 @@ import { Boom } from '@hapi/boom';
|
|
|
2
2
|
import { randomBytes } from 'crypto';
|
|
3
3
|
import { zip } from 'fflate';
|
|
4
4
|
import { promises as fs } from 'fs';
|
|
5
|
-
import {} from 'stream';
|
|
6
5
|
import { proto } from '../../WAProto/index.js';
|
|
7
6
|
import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, DONATE_URL, LIBRARY_NAME, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
|
|
8
7
|
import { AssociationType, ButtonHeaderType, ButtonType, CarouselCardType, ListType, ProtocolType, WAMessageStatus, WAProto } from '../Types/index.js';
|
|
9
|
-
import {
|
|
8
|
+
import { isLidUser, isPnUser, isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
|
|
10
9
|
import { sha256 } from './crypto.js';
|
|
11
10
|
import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
|
|
12
11
|
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
13
12
|
import { prepareRichResponseMessage } from './rich-message-utils.js';
|
|
14
13
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
14
|
+
const CONCURRENCY_LIMIT = 15;
|
|
15
15
|
const MIMETYPE_MAP = {
|
|
16
16
|
image: 'image/jpeg',
|
|
17
17
|
video: 'video/mp4',
|
|
@@ -106,9 +106,9 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
106
106
|
'url' in uploadData.media &&
|
|
107
107
|
!!uploadData.media.url &&
|
|
108
108
|
!!options.mediaCache &&
|
|
109
|
-
mediaType + ':' + uploadData.media.url;
|
|
109
|
+
mediaType + ':' + uploadData.media.url.toString();
|
|
110
110
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
111
|
-
uploadData.fileName =
|
|
111
|
+
uploadData.fileName = 'file';
|
|
112
112
|
}
|
|
113
113
|
if (!uploadData.mimetype) {
|
|
114
114
|
uploadData.mimetype = MIMETYPE_MAP[mediaType];
|
|
@@ -123,55 +123,19 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
123
123
|
return obj;
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
-
const isNewsletter = isJidNewsletter(options.jid);
|
|
127
|
-
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
128
|
-
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData.jpegThumbnail === 'undefined';
|
|
129
|
-
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
130
|
-
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
131
|
-
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
|
|
132
|
-
// Lia@Changes 06-02-26 --- Add few support for sending media to newsletter (≧▽≦)
|
|
126
|
+
const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
|
|
133
127
|
if (isNewsletter) {
|
|
134
128
|
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
|
|
135
129
|
const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
|
|
136
130
|
const fileSha256B64 = fileSha256.toString('base64');
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
timeoutMs: options.mediaUploadTimeoutMs,
|
|
143
|
-
newsletter: isNewsletter
|
|
144
|
-
});
|
|
145
|
-
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
146
|
-
return result;
|
|
147
|
-
})(),
|
|
148
|
-
(async () => {
|
|
149
|
-
try {
|
|
150
|
-
if (requiresThumbnailComputation) {
|
|
151
|
-
const { thumbnail } = await generateThumbnail(filePath, mediaType, options);
|
|
152
|
-
uploadData.jpegThumbnail = thumbnail;
|
|
153
|
-
logger?.debug('generated thumbnail');
|
|
154
|
-
}
|
|
155
|
-
if (requiresDurationComputation) {
|
|
156
|
-
uploadData.seconds = await getAudioDuration(filePath);
|
|
157
|
-
logger?.debug('computed audio duration');
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
162
|
-
}
|
|
163
|
-
})()
|
|
164
|
-
]).finally(async () => {
|
|
165
|
-
try {
|
|
166
|
-
await fs.unlink(filePath);
|
|
167
|
-
logger?.debug('removed tmp files');
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
logger?.warn('failed to remove tmp file');
|
|
171
|
-
}
|
|
131
|
+
const { mediaUrl, directPath, thumbnailDirectPath, thumbnailSha256 } = await options.upload(filePath, {
|
|
132
|
+
fileEncSha256B64: fileSha256B64,
|
|
133
|
+
mediaType: mediaType,
|
|
134
|
+
timeoutMs: options.mediaUploadTimeoutMs,
|
|
135
|
+
newsletter: isNewsletter
|
|
172
136
|
});
|
|
173
|
-
|
|
174
|
-
const obj =
|
|
137
|
+
await fs.unlink(filePath);
|
|
138
|
+
const obj = WAProto.Message.fromObject({
|
|
175
139
|
// todo: add more support here
|
|
176
140
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
177
141
|
url: mediaUrl,
|
|
@@ -180,7 +144,8 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
180
144
|
fileLength,
|
|
181
145
|
thumbnailDirectPath,
|
|
182
146
|
thumbnailSha256,
|
|
183
|
-
...uploadData
|
|
147
|
+
...uploadData,
|
|
148
|
+
media: undefined
|
|
184
149
|
})
|
|
185
150
|
});
|
|
186
151
|
if (uploadData.ptv) {
|
|
@@ -196,6 +161,11 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
196
161
|
}
|
|
197
162
|
return obj;
|
|
198
163
|
}
|
|
164
|
+
const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
|
|
165
|
+
const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
|
|
166
|
+
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
167
|
+
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
168
|
+
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
|
|
199
169
|
const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
|
|
200
170
|
logger,
|
|
201
171
|
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
@@ -253,8 +223,7 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
253
223
|
logger?.warn('failed to remove tmp file');
|
|
254
224
|
}
|
|
255
225
|
});
|
|
256
|
-
|
|
257
|
-
const obj = proto.Message.create({
|
|
226
|
+
const obj = WAProto.Message.fromObject({
|
|
258
227
|
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
259
228
|
url: mediaUrl,
|
|
260
229
|
directPath,
|
|
@@ -263,36 +232,20 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
263
232
|
fileSha256,
|
|
264
233
|
fileLength,
|
|
265
234
|
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
266
|
-
...uploadData
|
|
235
|
+
...uploadData,
|
|
236
|
+
media: undefined
|
|
267
237
|
})
|
|
268
238
|
});
|
|
269
239
|
if (uploadData.ptv) {
|
|
270
240
|
obj.ptvMessage = obj.videoMessage;
|
|
271
241
|
delete obj.videoMessage;
|
|
272
242
|
}
|
|
273
|
-
if (obj.stickerMessage) {
|
|
274
|
-
obj.stickerMessage.stickerSentTs = Date.now();
|
|
275
|
-
}
|
|
276
243
|
if (cacheableKey) {
|
|
277
244
|
logger?.debug({ cacheableKey }, 'set cache');
|
|
278
245
|
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
279
246
|
}
|
|
280
247
|
return obj;
|
|
281
248
|
};
|
|
282
|
-
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
283
|
-
ephemeralExpiration = ephemeralExpiration || 0;
|
|
284
|
-
const content = {
|
|
285
|
-
ephemeralMessage: {
|
|
286
|
-
message: {
|
|
287
|
-
protocolMessage: {
|
|
288
|
-
type: ProtocolType.EPHEMERAL_SETTING,
|
|
289
|
-
ephemeralExpiration
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
};
|
|
294
|
-
return content;
|
|
295
|
-
};
|
|
296
249
|
// Lia@Changes 31-01-26 --- Extract product message into a standalone function so it can also be reused as the header for interactive messages
|
|
297
250
|
const prepareProductMessage = async (message, options) => {
|
|
298
251
|
if (!message.businessOwnerJid) {
|
|
@@ -300,17 +253,14 @@ const prepareProductMessage = async (message, options) => {
|
|
|
300
253
|
}
|
|
301
254
|
const { imageMessage } = await prepareWAMessageMedia({ image: message.image || message.product.productImage }, options);
|
|
302
255
|
// Lia@Changes 01-02-26 --- Add product message default value
|
|
303
|
-
const content =
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
productImage: imageMessage
|
|
311
|
-
}
|
|
256
|
+
const { image, ...content } = message;
|
|
257
|
+
content.product = {
|
|
258
|
+
currencyCode: 'IDR',
|
|
259
|
+
priceAmount1000: 1000,
|
|
260
|
+
title: LIBRARY_NAME,
|
|
261
|
+
...message.product,
|
|
262
|
+
productImage: imageMessage
|
|
312
263
|
};
|
|
313
|
-
delete content.image;
|
|
314
264
|
return content;
|
|
315
265
|
};
|
|
316
266
|
/**
|
|
@@ -319,6 +269,7 @@ const prepareProductMessage = async (message, options) => {
|
|
|
319
269
|
* Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
|
|
320
270
|
* based on https://github.com/WhiskeySockets/Baileys/pull/1561
|
|
321
271
|
*/
|
|
272
|
+
// Lia@Changes 21-04-26 --- Enhanced prepareStickerPackMessage
|
|
322
273
|
const prepareStickerPackMessage = async (message, options) => {
|
|
323
274
|
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'Website: web.crysnovax.link', description = '🏷️ crysnovax/bailey' } = message;
|
|
324
275
|
if (stickers.length > 60) {
|
|
@@ -330,136 +281,124 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
330
281
|
if (!cover) {
|
|
331
282
|
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
332
283
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
284
|
+
const logger = options.logger;
|
|
285
|
+
// Lia@Changes 01-02-26 --- Add caching for sticker pack
|
|
286
|
+
let cacheableKey = false;
|
|
287
|
+
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
288
|
+
const urls = [];
|
|
289
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
290
|
+
const data = stickers[i].data;
|
|
291
|
+
if (typeof data === 'object' && data?.url) {
|
|
292
|
+
urls.push(data.url);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (urls.length > 0) {
|
|
296
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
297
|
+
}
|
|
298
|
+
}
|
|
347
299
|
if (cacheableKey) {
|
|
348
300
|
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
349
301
|
if (mediaBuff) {
|
|
350
|
-
|
|
302
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
351
303
|
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
352
304
|
}
|
|
353
305
|
}
|
|
354
306
|
const lib = await getImageProcessingLibrary();
|
|
307
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
308
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
309
|
+
const hasJimp = 'jimp' in lib && !!lib.jimp?.Jimp;
|
|
310
|
+
if (!hasSharp && !hasImage) {
|
|
311
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
312
|
+
}
|
|
355
313
|
const stickerPackIdValue = generateMessageIDV2();
|
|
356
314
|
const stickerData = {};
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
315
|
+
const stickerMetadata = new Array(stickers.length);
|
|
316
|
+
for (let i = 0; i < stickers.length; i += CONCURRENCY_LIMIT) {
|
|
317
|
+
const promises = [];
|
|
318
|
+
const chunkEnd = Math.min(i + CONCURRENCY_LIMIT, stickers.length);
|
|
319
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
320
|
+
promises.push((async (index) => {
|
|
321
|
+
const sticker = stickers[index];
|
|
322
|
+
const { stream } = await getStream(sticker.data);
|
|
323
|
+
const buffer = await toBuffer(stream);
|
|
324
|
+
let webpBuffer;
|
|
325
|
+
let isAnimated = false;
|
|
326
|
+
if (isWebPBuffer(buffer)) {
|
|
327
|
+
webpBuffer = buffer;
|
|
328
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
329
|
+
}
|
|
330
|
+
else if (hasSharp) {
|
|
331
|
+
webpBuffer = await lib.sharp.default(buffer)
|
|
332
|
+
.resize(512, 512, { fit: 'inside' })
|
|
333
|
+
.webp({ quality: 80 })
|
|
334
|
+
.toBuffer();
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
webpBuffer = await new lib.image.Transformer(buffer)
|
|
338
|
+
.resize(512, 512)
|
|
339
|
+
.webp(80);
|
|
340
|
+
}
|
|
341
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
342
|
+
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
343
|
+
}
|
|
344
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
345
|
+
const fileName = `${hash}.webp`;
|
|
346
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
347
|
+
stickerMetadata[index] = {
|
|
348
|
+
fileName,
|
|
349
|
+
mimetype: 'image/webp',
|
|
350
|
+
isAnimated,
|
|
351
|
+
emojis: sticker.emojis || ['✨'],
|
|
352
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
353
|
+
};
|
|
354
|
+
})(j));
|
|
387
355
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
if (webpBuffer.length > 1024 * 1024) {
|
|
392
|
-
throw new Boom(`Sticker at index ${i} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
393
|
-
}
|
|
394
|
-
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
395
|
-
const fileName = hash + '.webp';
|
|
396
|
-
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
397
|
-
return {
|
|
398
|
-
fileName,
|
|
399
|
-
mimetype: 'image/webp',
|
|
400
|
-
isAnimated,
|
|
401
|
-
emojis: sticker.emojis || ['✨'],
|
|
402
|
-
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
403
|
-
};
|
|
404
|
-
});
|
|
405
|
-
const stickerMetadata = await Promise.all(stickerPromises);
|
|
406
|
-
// Process and add cover/tray icon to the ZIP
|
|
407
|
-
const trayIconFileName = stickerPackIdValue + '.webp';
|
|
356
|
+
await Promise.all(promises);
|
|
357
|
+
}
|
|
358
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
408
359
|
const { stream: coverStream } = await getStream(cover);
|
|
409
360
|
const coverBuffer = await toBuffer(coverStream);
|
|
410
361
|
let coverWebpBuffer;
|
|
411
|
-
|
|
412
|
-
if (isCoverWebP) {
|
|
413
|
-
// Already WebP - preserve original to keep exif metadata
|
|
362
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
414
363
|
coverWebpBuffer = coverBuffer;
|
|
415
364
|
}
|
|
416
|
-
else if (
|
|
417
|
-
coverWebpBuffer = await lib
|
|
418
|
-
.sharp
|
|
419
|
-
.default(coverBuffer)
|
|
365
|
+
else if (hasSharp) {
|
|
366
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
420
367
|
.resize(512, 512, { fit: 'inside' })
|
|
421
368
|
.webp({ quality: 80 })
|
|
422
369
|
.toBuffer();
|
|
423
370
|
}
|
|
424
|
-
else
|
|
425
|
-
coverWebpBuffer = await new lib
|
|
426
|
-
.image
|
|
427
|
-
.Transformer(coverBuffer)
|
|
371
|
+
else {
|
|
372
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
428
373
|
.resize(512, 512)
|
|
429
374
|
.webp(80);
|
|
430
375
|
}
|
|
431
|
-
else {
|
|
432
|
-
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting cover to WebP. Either install sharp or @napi-rs/image or provide cover in WebP format.');
|
|
433
|
-
}
|
|
434
|
-
// Add cover to ZIP data
|
|
435
376
|
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
436
377
|
const zipBuffer = await new Promise((resolve, reject) => {
|
|
437
|
-
zip(stickerData, (error, data) =>
|
|
438
|
-
if (error) {
|
|
439
|
-
reject(error);
|
|
440
|
-
} else {
|
|
441
|
-
resolve(Buffer.from(data));
|
|
442
|
-
}
|
|
443
|
-
});
|
|
378
|
+
zip(stickerData, (error, data) => error ? reject(error) : resolve(Buffer.from(data)));
|
|
444
379
|
});
|
|
445
|
-
const stickerPackSize = zipBuffer.length;
|
|
446
380
|
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
447
|
-
logger
|
|
381
|
+
logger,
|
|
448
382
|
opts: options.options
|
|
449
383
|
});
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
384
|
+
let stickerPackUploadResult;
|
|
385
|
+
try {
|
|
386
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
387
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
388
|
+
mediaType: 'sticker-pack',
|
|
389
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
finally {
|
|
393
|
+
fs.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
394
|
+
}
|
|
456
395
|
const obj = {
|
|
457
|
-
name
|
|
458
|
-
publisher
|
|
396
|
+
name,
|
|
397
|
+
publisher,
|
|
459
398
|
stickerPackId: stickerPackIdValue,
|
|
460
399
|
packDescription: description,
|
|
461
400
|
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
462
|
-
stickerPackSize:
|
|
401
|
+
stickerPackSize: zipBuffer.length,
|
|
463
402
|
stickers: stickerMetadata,
|
|
464
403
|
fileSha256: stickerPackUpload.fileSha256,
|
|
465
404
|
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
@@ -467,31 +406,19 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
467
406
|
directPath: stickerPackUploadResult.directPath,
|
|
468
407
|
fileLength: stickerPackUpload.fileLength,
|
|
469
408
|
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
470
|
-
trayIconFileName
|
|
409
|
+
trayIconFileName
|
|
471
410
|
};
|
|
472
411
|
try {
|
|
473
|
-
// Reuse the cover buffer we already processed for thumbnail generation
|
|
474
412
|
let thumbnailBuffer;
|
|
475
|
-
if (
|
|
476
|
-
thumbnailBuffer = await lib
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
if ('image' in lib && lib.image?.Transformer) {
|
|
484
|
-
thumbnailBuffer = await new lib
|
|
485
|
-
.image
|
|
486
|
-
.Transformer(coverBuffer)
|
|
487
|
-
.resize(252, 252)
|
|
488
|
-
.jpeg();
|
|
489
|
-
}
|
|
490
|
-
else if ('jimp' in lib && lib.jimp?.Jimp) {
|
|
413
|
+
if (hasSharp) {
|
|
414
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
415
|
+
}
|
|
416
|
+
else if (hasImage) {
|
|
417
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
418
|
+
}
|
|
419
|
+
else if (hasJimp) {
|
|
491
420
|
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
492
|
-
thumbnailBuffer = await jimpImage
|
|
493
|
-
.resize({ w: 252, h: 252 })
|
|
494
|
-
.getBuffer('image/jpeg');
|
|
421
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
495
422
|
}
|
|
496
423
|
else {
|
|
497
424
|
throw new Error('No image processing library available for thumbnail generation');
|
|
@@ -500,16 +427,21 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
500
427
|
throw new Error('Failed to generate thumbnail buffer');
|
|
501
428
|
}
|
|
502
429
|
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
503
|
-
logger
|
|
430
|
+
logger,
|
|
504
431
|
opts: options.options,
|
|
505
|
-
mediaKey: stickerPackUpload.mediaKey
|
|
506
|
-
});
|
|
507
|
-
const thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
508
|
-
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
509
|
-
mediaType: 'thumbnail-sticker-pack',
|
|
510
|
-
timeoutMs: options.mediaUploadTimeoutMs
|
|
432
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
511
433
|
});
|
|
512
|
-
|
|
434
|
+
let thumbUploadResult;
|
|
435
|
+
try {
|
|
436
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
437
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
438
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
439
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
finally {
|
|
443
|
+
fs.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
444
|
+
}
|
|
513
445
|
Object.assign(obj, {
|
|
514
446
|
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
515
447
|
thumbnailSha256: thumbUpload.fileSha256,
|
|
@@ -520,18 +452,17 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
520
452
|
});
|
|
521
453
|
}
|
|
522
454
|
catch (error) {
|
|
523
|
-
|
|
455
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
524
456
|
}
|
|
525
|
-
const content = obj;
|
|
526
457
|
if (cacheableKey) {
|
|
527
|
-
|
|
528
|
-
|
|
458
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
459
|
+
options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
529
460
|
}
|
|
530
|
-
return WAProto.Message.StickerPackMessage.fromObject(
|
|
461
|
+
return WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
531
462
|
};
|
|
532
463
|
// Lia@Changes 30-01-26 --- Add native flow button helper for interactive message
|
|
533
464
|
const prepareNativeFlowButtons = (message) => {
|
|
534
|
-
const buttons = message.nativeFlow
|
|
465
|
+
const buttons = message.nativeFlow;
|
|
535
466
|
const isButtonsFieldArray = Array.isArray(buttons);
|
|
536
467
|
const correctedField = isButtonsFieldArray ? buttons : buttons.buttons;
|
|
537
468
|
const messageParamsJson = {};
|
|
@@ -550,10 +481,7 @@ const prepareNativeFlowButtons = (message) => {
|
|
|
550
481
|
Object.assign(messageParamsJson, {
|
|
551
482
|
bottom_sheet: {
|
|
552
483
|
in_thread_buttons_limit: 1,
|
|
553
|
-
divider_indices: Array.from(
|
|
554
|
-
{ length: correctedField.length },
|
|
555
|
-
(_, index) => index
|
|
556
|
-
),
|
|
484
|
+
divider_indices: Array.from({ length: correctedField.length }, (_, index) => index),
|
|
557
485
|
list_title: message.optionTitle || '📄 Select Options',
|
|
558
486
|
button_title: message.optionText
|
|
559
487
|
}
|
|
@@ -590,6 +518,7 @@ const prepareNativeFlowButtons = (message) => {
|
|
|
590
518
|
display_text: buttonText || '🌐 Visit',
|
|
591
519
|
url: button.url,
|
|
592
520
|
merchant_url: button.url,
|
|
521
|
+
webview_interaction: button.useWebview,
|
|
593
522
|
icon: buttonIcon
|
|
594
523
|
})
|
|
595
524
|
};
|
|
@@ -617,17 +546,30 @@ const prepareNativeFlowButtons = (message) => {
|
|
|
617
546
|
}
|
|
618
547
|
return button;
|
|
619
548
|
}),
|
|
620
|
-
messageParamsJson: JSON.stringify(messageParamsJson)
|
|
621
|
-
messageVersion: 1
|
|
549
|
+
messageParamsJson: JSON.stringify(messageParamsJson)
|
|
622
550
|
};
|
|
623
551
|
};
|
|
552
|
+
export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
|
|
553
|
+
ephemeralExpiration = ephemeralExpiration || 0;
|
|
554
|
+
const content = {
|
|
555
|
+
ephemeralMessage: {
|
|
556
|
+
message: {
|
|
557
|
+
protocolMessage: {
|
|
558
|
+
type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
|
|
559
|
+
ephemeralExpiration
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
};
|
|
564
|
+
return WAProto.Message.fromObject(content);
|
|
565
|
+
};
|
|
624
566
|
/**
|
|
625
567
|
* Generate forwarded message content like WA does
|
|
626
568
|
* @param message the message to forward
|
|
627
569
|
* @param options.forceForward will show the message as forwarded even if it is from you
|
|
628
570
|
*/
|
|
629
571
|
export const generateForwardMessageContent = (message, forceForward) => {
|
|
630
|
-
let content = message.message
|
|
572
|
+
let content = message.message;
|
|
631
573
|
if (!content) {
|
|
632
574
|
throw new Boom('no content in message', { statusCode: 400 });
|
|
633
575
|
}
|
|
@@ -643,24 +585,12 @@ export const generateForwardMessageContent = (message, forceForward) => {
|
|
|
643
585
|
key = 'extendedTextMessage';
|
|
644
586
|
}
|
|
645
587
|
const key_ = content?.[key];
|
|
646
|
-
const contextInfo = {};
|
|
647
588
|
if (score > 0) {
|
|
648
|
-
contextInfo
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
// so the server knows where to find the original media
|
|
653
|
-
const remoteJid = message.key?.remoteJid;
|
|
654
|
-
if (remoteJid && isJidNewsletter(remoteJid)) {
|
|
655
|
-
contextInfo.forwardedNewsletterMessageInfo = {
|
|
656
|
-
newsletterJid: remoteJid,
|
|
657
|
-
serverMessageId: message.key?.server_id ? parseInt(message.key.server_id) : null,
|
|
658
|
-
newsletterName: null
|
|
659
|
-
};
|
|
660
|
-
// strip messageContextInfo (contains messageSecret etc.) as WA Web does
|
|
661
|
-
delete content.messageContextInfo;
|
|
589
|
+
key_.contextInfo = { forwardingScore: score, isForwarded: true };
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
key_.contextInfo = {};
|
|
662
593
|
}
|
|
663
|
-
key_.contextInfo = contextInfo;
|
|
664
594
|
return content;
|
|
665
595
|
};
|
|
666
596
|
export const hasNonNullishProperty = (message, key) => {
|
|
@@ -677,21 +607,21 @@ export const hasOptionalProperty = (obj, key) => {
|
|
|
677
607
|
};
|
|
678
608
|
// Lia@Changes 06-02-26 --- Validate album message media to avoid bug 👀
|
|
679
609
|
export const hasValidAlbumMedia = (message) => {
|
|
680
|
-
return message.imageMessage ||
|
|
681
|
-
message.videoMessage;
|
|
610
|
+
return !!(message.imageMessage ||
|
|
611
|
+
message.videoMessage);
|
|
682
612
|
};
|
|
683
613
|
export const hasValidInteractiveHeader = (message) => {
|
|
684
|
-
return message.imageMessage ||
|
|
614
|
+
return !!(message.imageMessage ||
|
|
685
615
|
message.videoMessage ||
|
|
686
616
|
message.documentMessage ||
|
|
687
617
|
message.productMessage ||
|
|
688
|
-
message.locationMessage;
|
|
618
|
+
message.locationMessage);
|
|
689
619
|
};
|
|
690
620
|
// Lia@Changes 30-01-26 --- Validate carousel cards header to avoid bug 👀
|
|
691
621
|
export const hasValidCarouselHeader = (message) => {
|
|
692
|
-
return message.imageMessage ||
|
|
622
|
+
return !!(message.imageMessage ||
|
|
693
623
|
message.videoMessage ||
|
|
694
|
-
message.productMessage;
|
|
624
|
+
message.productMessage);
|
|
695
625
|
};
|
|
696
626
|
export const generateWAMessageContent = async (message, options) => {
|
|
697
627
|
var _a, _b;
|
|
@@ -701,10 +631,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
701
631
|
delete message.raw;
|
|
702
632
|
return message;
|
|
703
633
|
}
|
|
704
|
-
// Lia@Changes 09-04-26 --- Add support for code block
|
|
634
|
+
// Lia@Changes 09-04-26 --- Add support for code block and table with richResponseMessage
|
|
705
635
|
else if (hasNonNullishProperty(message, 'code') ||
|
|
706
|
-
hasNonNullishProperty(message, '
|
|
707
|
-
hasNonNullishProperty(message, 'items') ||
|
|
636
|
+
hasNonNullishProperty(message, 'links') ||
|
|
708
637
|
hasNonNullishProperty(message, 'table') ||
|
|
709
638
|
hasNonNullishProperty(message, 'richResponse')) {
|
|
710
639
|
m = prepareRichResponseMessage(message);
|
|
@@ -720,7 +649,8 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
720
649
|
extContent.jpegThumbnail = urlInfo.jpegThumbnail;
|
|
721
650
|
extContent.description = urlInfo.description;
|
|
722
651
|
extContent.title = urlInfo.title;
|
|
723
|
-
extContent.previewType = 0;
|
|
652
|
+
extContent.previewType = urlInfo.previewType ?? 0;
|
|
653
|
+
extContent.linkPreviewMetadata = urlInfo.linkPreviewMetadata;
|
|
724
654
|
const img = urlInfo.highQualityThumbnail;
|
|
725
655
|
if (img) {
|
|
726
656
|
extContent.thumbnailDirectPath = img.directPath;
|
|
@@ -732,6 +662,21 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
732
662
|
extContent.thumbnailEncSha256 = img.fileEncSha256;
|
|
733
663
|
}
|
|
734
664
|
}
|
|
665
|
+
const faviconData = message.favicon;
|
|
666
|
+
if (faviconData && typeof options.upload === 'function') {
|
|
667
|
+
const { imageMessage } = await prepareWAMessageMedia({
|
|
668
|
+
image: faviconData
|
|
669
|
+
}, options);
|
|
670
|
+
extContent.faviconMMSMetadata = {
|
|
671
|
+
thumbnailDirectPath: imageMessage.directPath,
|
|
672
|
+
mediaKey: imageMessage.mediaKey,
|
|
673
|
+
mediaKeyTimestamp: imageMessage.mediaKeyTimestamp,
|
|
674
|
+
thumbnailWidth: 32,
|
|
675
|
+
thumbnailHeight: 32,
|
|
676
|
+
thumbnailSha256: imageMessage.fileSha256,
|
|
677
|
+
thumbnailEncSha256: imageMessage.fileEncSha256
|
|
678
|
+
};
|
|
679
|
+
}
|
|
735
680
|
if (options.backgroundColor) {
|
|
736
681
|
extContent.backgroundArgb = await assertColor(options.backgroundColor);
|
|
737
682
|
}
|
|
@@ -746,25 +691,25 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
746
691
|
throw new Boom('require atleast 1 contact', { statusCode: 400 });
|
|
747
692
|
}
|
|
748
693
|
if (contactLen === 1) {
|
|
749
|
-
m.contactMessage = message.contacts.contacts[0];
|
|
694
|
+
m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
|
|
750
695
|
}
|
|
751
696
|
else {
|
|
752
|
-
m.contactsArrayMessage = message.contacts;
|
|
697
|
+
m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
|
|
753
698
|
}
|
|
754
699
|
}
|
|
755
700
|
else if (hasNonNullishProperty(message, 'location')) {
|
|
756
|
-
m.locationMessage = message.location;
|
|
701
|
+
m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
|
|
757
702
|
}
|
|
758
703
|
else if (hasNonNullishProperty(message, 'react')) {
|
|
759
704
|
if (!message.react.senderTimestampMs) {
|
|
760
705
|
message.react.senderTimestampMs = Date.now();
|
|
761
706
|
}
|
|
762
|
-
m.reactionMessage = message.react;
|
|
707
|
+
m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
|
|
763
708
|
}
|
|
764
709
|
else if (hasNonNullishProperty(message, 'delete')) {
|
|
765
710
|
m.protocolMessage = {
|
|
766
711
|
key: message.delete,
|
|
767
|
-
type:
|
|
712
|
+
type: WAProto.Message.ProtocolMessage.Type.REVOKE
|
|
768
713
|
};
|
|
769
714
|
}
|
|
770
715
|
else if (hasNonNullishProperty(message, 'forward')) {
|
|
@@ -894,14 +839,13 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
894
839
|
statusCode: 400
|
|
895
840
|
});
|
|
896
841
|
}
|
|
897
|
-
m.messageContextInfo = {
|
|
898
|
-
// encKey
|
|
899
|
-
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
900
|
-
};
|
|
901
842
|
const pollCreationMessage = {
|
|
902
843
|
name: message.poll.name,
|
|
903
844
|
selectableOptionsCount: message.poll.selectableCount,
|
|
904
|
-
options: message.poll.values.map(optionName => ({ optionName }))
|
|
845
|
+
options: message.poll.values.map(optionName => ({ optionName })),
|
|
846
|
+
endTime: message.poll.endDate ? message.poll.endDate.getTime() : undefined,
|
|
847
|
+
hideParticipantName: message.poll.hideVoter ?? false,
|
|
848
|
+
allowAddOption: message.poll.canAddOption ?? false
|
|
905
849
|
};
|
|
906
850
|
if (message.poll.toAnnouncementGroup) {
|
|
907
851
|
// poll v2 is for community announcement groups (single select and multiple)
|
|
@@ -909,7 +853,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
909
853
|
}
|
|
910
854
|
else {
|
|
911
855
|
// Lia@Changes 08-02-26 --- Add quiz message support
|
|
912
|
-
if (message.poll.pollType
|
|
856
|
+
if (message.poll.pollType === 1) {
|
|
913
857
|
if (!message.poll.correctAnswer) {
|
|
914
858
|
throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
|
|
915
859
|
}
|
|
@@ -919,7 +863,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
919
863
|
correctAnswer: {
|
|
920
864
|
optionName: message.poll.correctAnswer.toString()
|
|
921
865
|
},
|
|
922
|
-
pollType:
|
|
866
|
+
pollType: 1,
|
|
923
867
|
selectableOptionsCount: 1
|
|
924
868
|
};
|
|
925
869
|
}
|
|
@@ -932,6 +876,10 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
932
876
|
m.pollCreationMessage = pollCreationMessage;
|
|
933
877
|
}
|
|
934
878
|
}
|
|
879
|
+
m.messageContextInfo = {
|
|
880
|
+
// encKey
|
|
881
|
+
messageSecret: message.poll.messageSecret || randomBytes(32)
|
|
882
|
+
};
|
|
935
883
|
}
|
|
936
884
|
// Lia@Changes 08-02-26 --- Add poll result snapshot message
|
|
937
885
|
else if (hasNonNullishProperty(message, 'pollResult')) {
|
|
@@ -942,7 +890,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
942
890
|
optionVoteCount: parseInt(vote.voteCount)
|
|
943
891
|
}))
|
|
944
892
|
};
|
|
945
|
-
if (message.pollResult.pollType
|
|
893
|
+
if (message.pollResult.pollType === 1) {
|
|
946
894
|
pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
|
|
947
895
|
m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
|
|
948
896
|
}
|
|
@@ -966,25 +914,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
966
914
|
vote: message.pollUpdate.vote
|
|
967
915
|
};
|
|
968
916
|
}
|
|
969
|
-
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
970
|
-
m.protocolMessage = {
|
|
971
|
-
type: ProtocolType.SHARE_PHONE_NUMBER
|
|
972
|
-
};
|
|
973
|
-
}
|
|
974
|
-
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
975
|
-
m.requestPhoneNumberMessage = {};
|
|
976
|
-
}
|
|
977
|
-
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
978
|
-
m.protocolMessage = {
|
|
979
|
-
type: ProtocolType.LIMIT_SHARING,
|
|
980
|
-
limitSharing: {
|
|
981
|
-
sharingLimited: message.limitSharing === true,
|
|
982
|
-
trigger: 1,
|
|
983
|
-
limitSharingSettingTimestamp: Date.now(),
|
|
984
|
-
initiatedByMe: true
|
|
985
|
-
}
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
917
|
// Lia@Changes 01-02-26 --- Add payment invite message
|
|
989
918
|
else if (hasNonNullishProperty(message, 'paymentInviteServiceType')) {
|
|
990
919
|
m.paymentInviteMessage = {
|
|
@@ -1018,12 +947,16 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1018
947
|
}
|
|
1019
948
|
let videoCount = 0;
|
|
1020
949
|
for (let i = 0; i < message.album.length; i++) {
|
|
1021
|
-
if (message.album[i].video)
|
|
1022
|
-
|
|
950
|
+
if (message.album[i].video)
|
|
951
|
+
videoCount++;
|
|
952
|
+
}
|
|
953
|
+
;
|
|
1023
954
|
let imageCount = 0;
|
|
1024
955
|
for (let i = 0; i < message.album.length; i++) {
|
|
1025
|
-
if (message.album[i].image)
|
|
1026
|
-
|
|
956
|
+
if (message.album[i].image)
|
|
957
|
+
imageCount++;
|
|
958
|
+
}
|
|
959
|
+
;
|
|
1027
960
|
if ((videoCount + imageCount) < 2) {
|
|
1028
961
|
throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
|
|
1029
962
|
}
|
|
@@ -1032,6 +965,25 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1032
965
|
expectedVideoCount: videoCount
|
|
1033
966
|
};
|
|
1034
967
|
}
|
|
968
|
+
else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
|
|
969
|
+
m.protocolMessage = {
|
|
970
|
+
type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
|
|
974
|
+
m.requestPhoneNumberMessage = {};
|
|
975
|
+
}
|
|
976
|
+
else if (hasNonNullishProperty(message, 'limitSharing')) {
|
|
977
|
+
m.protocolMessage = {
|
|
978
|
+
type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
|
|
979
|
+
limitSharing: {
|
|
980
|
+
sharingLimited: message.limitSharing === true,
|
|
981
|
+
trigger: 1,
|
|
982
|
+
limitSharingSettingTimestamp: Date.now(),
|
|
983
|
+
initiatedByMe: true
|
|
984
|
+
}
|
|
985
|
+
};
|
|
986
|
+
}
|
|
1035
987
|
else {
|
|
1036
988
|
m = await prepareWAMessageMedia(message, options);
|
|
1037
989
|
}
|
|
@@ -1044,11 +996,11 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1044
996
|
if (hasOptionalProperty(button, 'sections')) {
|
|
1045
997
|
return {
|
|
1046
998
|
nativeFlowInfo: {
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
999
|
+
name: 'single_select',
|
|
1000
|
+
paramsJson: JSON.stringify({
|
|
1001
|
+
title: buttonText,
|
|
1002
|
+
sections: button.sections
|
|
1003
|
+
})
|
|
1052
1004
|
},
|
|
1053
1005
|
type: ButtonType.NATIVE_FLOW
|
|
1054
1006
|
};
|
|
@@ -1056,8 +1008,8 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1056
1008
|
else if (hasOptionalProperty(button, 'name')) {
|
|
1057
1009
|
return {
|
|
1058
1010
|
nativeFlowInfo: {
|
|
1059
|
-
|
|
1060
|
-
|
|
1011
|
+
name: button.name,
|
|
1012
|
+
paramsJson: button.paramsJson
|
|
1061
1013
|
},
|
|
1062
1014
|
type: ButtonType.NATIVE_FLOW
|
|
1063
1015
|
};
|
|
@@ -1140,7 +1092,8 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1140
1092
|
if (hasOptionalProperty(message, 'caption')) {
|
|
1141
1093
|
hydratedTemplate.hydratedTitleText = message.title;
|
|
1142
1094
|
hydratedTemplate.hydratedContentText = message.caption;
|
|
1143
|
-
}
|
|
1095
|
+
}
|
|
1096
|
+
;
|
|
1144
1097
|
Object.assign(hydratedTemplate, m);
|
|
1145
1098
|
}
|
|
1146
1099
|
if (hasOptionalProperty(message, 'footer')) {
|
|
@@ -1152,7 +1105,7 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1152
1105
|
hydratedFourRowTemplate: hydratedTemplate,
|
|
1153
1106
|
hydratedTemplate: hydratedTemplate
|
|
1154
1107
|
}
|
|
1155
|
-
}
|
|
1108
|
+
};
|
|
1156
1109
|
}
|
|
1157
1110
|
else if (hasNonNullishProperty(message, 'nativeFlow')) {
|
|
1158
1111
|
const interactiveMessage = {
|
|
@@ -1177,9 +1130,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1177
1130
|
}
|
|
1178
1131
|
else {
|
|
1179
1132
|
if (hasOptionalProperty(message, 'caption')) {
|
|
1180
|
-
const isValidHeader = hasValidInteractiveHeader(m)
|
|
1133
|
+
const isValidHeader = hasValidInteractiveHeader(m);
|
|
1181
1134
|
if (!isValidHeader) {
|
|
1182
|
-
|
|
1135
|
+
throw new Boom('Invalid media type for interactive message header', { statusCode: 400 });
|
|
1183
1136
|
}
|
|
1184
1137
|
interactiveMessage.header = {
|
|
1185
1138
|
title: message.title || '',
|
|
@@ -1194,11 +1147,11 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1194
1147
|
Object.assign(interactiveMessage.header, m);
|
|
1195
1148
|
}
|
|
1196
1149
|
if (hasOptionalProperty(message, 'audioFooter')) {
|
|
1197
|
-
const
|
|
1150
|
+
const { audioMessage } = await prepareWAMessageMedia({
|
|
1198
1151
|
audio: message.audioFooter
|
|
1199
1152
|
}, options);
|
|
1200
1153
|
interactiveMessage.footer = {
|
|
1201
|
-
audioMessage
|
|
1154
|
+
audioMessage,
|
|
1202
1155
|
hasMediaAttachment: true
|
|
1203
1156
|
};
|
|
1204
1157
|
}
|
|
@@ -1210,17 +1163,17 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1210
1163
|
else if (hasNonNullishProperty(message, 'cards')) {
|
|
1211
1164
|
const interactiveMessage = {
|
|
1212
1165
|
carouselMessage: {
|
|
1213
|
-
cards: await Promise.all(message.cards.map(async card => {
|
|
1166
|
+
cards: await Promise.all(message.cards.map(async (card) => {
|
|
1214
1167
|
let carouselHeader = {};
|
|
1215
1168
|
if (hasNonNullishProperty(card, 'product')) {
|
|
1216
1169
|
carouselHeader.productMessage = await prepareProductMessage(card, options);
|
|
1217
1170
|
}
|
|
1218
1171
|
else {
|
|
1219
|
-
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({
|
|
1172
|
+
carouselHeader = await prepareWAMessageMedia(card, options).catch(() => ({}));
|
|
1220
1173
|
}
|
|
1221
|
-
const isValidHeader = hasValidCarouselHeader(carouselHeader)
|
|
1174
|
+
const isValidHeader = hasValidCarouselHeader(carouselHeader);
|
|
1222
1175
|
if (!isValidHeader) {
|
|
1223
|
-
|
|
1176
|
+
throw new Boom('Invalid media type for carousel card', { statusCode: 400 });
|
|
1224
1177
|
}
|
|
1225
1178
|
const carouselCard = {
|
|
1226
1179
|
nativeFlowMessage: prepareNativeFlowButtons(card.nativeFlow ? card : [])
|
|
@@ -1243,18 +1196,18 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1243
1196
|
Object.assign(carouselCard.header, carouselHeader);
|
|
1244
1197
|
}
|
|
1245
1198
|
if (hasOptionalProperty(card, 'audioFooter')) {
|
|
1246
|
-
const
|
|
1199
|
+
const { audioMessage } = await prepareWAMessageMedia({
|
|
1247
1200
|
audio: card.audioFooter
|
|
1248
1201
|
}, options);
|
|
1249
1202
|
carouselCard.footer = {
|
|
1250
|
-
audioMessage
|
|
1203
|
+
audioMessage,
|
|
1251
1204
|
hasMediaAttachment: true
|
|
1252
1205
|
};
|
|
1253
1206
|
}
|
|
1254
1207
|
else if (hasOptionalProperty(card, 'footer')) {
|
|
1255
1208
|
carouselCard.footer = { text: card.footer };
|
|
1256
1209
|
}
|
|
1257
|
-
return carouselCard
|
|
1210
|
+
return carouselCard;
|
|
1258
1211
|
})),
|
|
1259
1212
|
carouselCardType: CarouselCardType.UNKNOWN,
|
|
1260
1213
|
messageVersion: 1
|
|
@@ -1272,9 +1225,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1272
1225
|
else if (hasNonNullishProperty(message, 'requestPaymentFrom')) {
|
|
1273
1226
|
const requestPaymentMessage = {
|
|
1274
1227
|
amount: {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1228
|
+
currencyCode: 'IDR',
|
|
1229
|
+
offset: 1000,
|
|
1230
|
+
value: 1000
|
|
1278
1231
|
},
|
|
1279
1232
|
amount1000: 1000,
|
|
1280
1233
|
currencyCodeIso4217: 'IDR',
|
|
@@ -1335,9 +1288,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1335
1288
|
mediaType: content.mediaType || 1,
|
|
1336
1289
|
mediaUrl: content.url,
|
|
1337
1290
|
renderLargerThumbnail: content.largeThumbnail,
|
|
1338
|
-
sourceUrl: content.url
|
|
1291
|
+
sourceUrl: content.url,
|
|
1339
1292
|
thumbnail: content.thumbnail,
|
|
1340
|
-
thumbnailUrl: content.url,
|
|
1293
|
+
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1341
1294
|
title: content.title || LIBRARY_NAME
|
|
1342
1295
|
};
|
|
1343
1296
|
delete externalAdReply.subTitle;
|
|
@@ -1354,18 +1307,21 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1354
1307
|
(hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
|
|
1355
1308
|
const messageType = Object.keys(m)[0];
|
|
1356
1309
|
const key = m[messageType];
|
|
1357
|
-
if ('contextInfo' in key
|
|
1358
|
-
key.contextInfo
|
|
1310
|
+
if (key && 'contextInfo' in key) {
|
|
1311
|
+
key.contextInfo = key.contextInfo || {};
|
|
1312
|
+
if (message.mentions?.length) {
|
|
1313
|
+
key.contextInfo.mentionedJid = message.mentions;
|
|
1314
|
+
}
|
|
1315
|
+
if (message.mentionAll) {
|
|
1316
|
+
key.contextInfo.nonJidMentions = 1;
|
|
1317
|
+
}
|
|
1359
1318
|
}
|
|
1360
1319
|
else if (key) {
|
|
1361
1320
|
key.contextInfo = {
|
|
1362
|
-
mentionedJid: message.mentions
|
|
1321
|
+
mentionedJid: message.mentions,
|
|
1322
|
+
nonJidMentions: message.mentionAll ? 1 : 0
|
|
1363
1323
|
};
|
|
1364
1324
|
}
|
|
1365
|
-
if (message.mentionAll) {
|
|
1366
|
-
key.contextInfo.mentionedJid = [];
|
|
1367
|
-
key.contextInfo.nonJidMentions = 1;
|
|
1368
|
-
}
|
|
1369
1325
|
}
|
|
1370
1326
|
if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
|
|
1371
1327
|
const messageType = Object.keys(m)[0];
|
|
@@ -1387,11 +1343,26 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1387
1343
|
else if (key) {
|
|
1388
1344
|
key.contextInfo = {
|
|
1389
1345
|
isGroupStatus: message.groupStatus
|
|
1390
|
-
}
|
|
1346
|
+
};
|
|
1391
1347
|
}
|
|
1392
1348
|
m = { groupStatusMessageV2: { message: m } };
|
|
1393
1349
|
delete message.groupStatus;
|
|
1394
1350
|
}
|
|
1351
|
+
// Lia@Changes 06-05-26 --- Add "spoiler" boolean to set contextInfo.isSpoiler and wrap message into spoilerMessage
|
|
1352
|
+
if (hasOptionalProperty(message, 'spoiler') && !!message.spoiler) {
|
|
1353
|
+
const messageType = Object.keys(m)[0];
|
|
1354
|
+
const key = m[messageType];
|
|
1355
|
+
if ('contextInfo' in key && !!key.contextInfo) {
|
|
1356
|
+
key.contextInfo.isSpoiler = message.spoiler;
|
|
1357
|
+
}
|
|
1358
|
+
else if (key) {
|
|
1359
|
+
key.contextInfo = {
|
|
1360
|
+
isSpoiler: message.spoiler
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
m = { spoilerMessage: { message: m } };
|
|
1364
|
+
delete message.spoiler;
|
|
1365
|
+
}
|
|
1395
1366
|
// Lia@Changes 02-02-26 --- Add "interactiveAsTemplate" boolean to wrap interactiveMessage into templateMessage
|
|
1396
1367
|
else if (hasOptionalProperty(message, 'interactiveAsTemplate') && !!message.interactiveAsTemplate) {
|
|
1397
1368
|
if (!m.interactiveMessage) {
|
|
@@ -1410,6 +1381,10 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1410
1381
|
m = { ephemeralMessage: { message: m } };
|
|
1411
1382
|
delete message.ephemeral;
|
|
1412
1383
|
}
|
|
1384
|
+
// Lia@Changes 16-05-26 --- Add wrap message into lottieStickerMessage by isLottie boolean
|
|
1385
|
+
if (hasOptionalProperty(message, 'isLottie') && !!message.isLottie) {
|
|
1386
|
+
m = { lottieStickerMessage: { message: m } };
|
|
1387
|
+
}
|
|
1413
1388
|
else if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
|
|
1414
1389
|
m = { viewOnceMessage: { message: m } };
|
|
1415
1390
|
}
|
|
@@ -1429,9 +1404,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1429
1404
|
key: message.edit,
|
|
1430
1405
|
editedMessage: m,
|
|
1431
1406
|
timestampMs: Date.now(),
|
|
1432
|
-
type:
|
|
1407
|
+
type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
|
|
1433
1408
|
}
|
|
1434
|
-
}
|
|
1409
|
+
};
|
|
1435
1410
|
}
|
|
1436
1411
|
if (shouldIncludeReportingToken(m)) {
|
|
1437
1412
|
m.messageContextInfo = m.messageContextInfo || {};
|
|
@@ -1439,16 +1414,16 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1439
1414
|
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1440
1415
|
}
|
|
1441
1416
|
}
|
|
1442
|
-
return
|
|
1417
|
+
return WAProto.Message.create(m);
|
|
1443
1418
|
};
|
|
1444
1419
|
export const generateWAMessageFromContent = (jid, message, options) => {
|
|
1445
1420
|
// set timestamp to now
|
|
1446
1421
|
// if not specified
|
|
1447
1422
|
if (!options.timestamp) {
|
|
1448
|
-
options.timestamp = Date
|
|
1423
|
+
options.timestamp = new Date();
|
|
1449
1424
|
}
|
|
1450
|
-
const messageContextInfo = message.messageContextInfo
|
|
1451
1425
|
const innerMessage = normalizeMessageContent(message);
|
|
1426
|
+
const messageContextInfo = message.messageContextInfo;
|
|
1452
1427
|
const key = getContentType(innerMessage);
|
|
1453
1428
|
const timestamp = unixTimestampSeconds(options.timestamp);
|
|
1454
1429
|
const isNewsletter = isJidNewsletter(jid);
|
|
@@ -1501,9 +1476,9 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1501
1476
|
recipientKeyHash: randomBytes(10),
|
|
1502
1477
|
recipientTimestamp: unixTimestampSeconds()
|
|
1503
1478
|
};
|
|
1504
|
-
messageContextInfo.deviceListMetadataVersion = 2
|
|
1479
|
+
messageContextInfo.deviceListMetadataVersion = 2;
|
|
1505
1480
|
}
|
|
1506
|
-
message =
|
|
1481
|
+
message = WAProto.Message.create(message);
|
|
1507
1482
|
const messageJSON = {
|
|
1508
1483
|
key: {
|
|
1509
1484
|
remoteJid: jid,
|
|
@@ -1518,11 +1493,11 @@ export const generateWAMessageFromContent = (jid, message, options) => {
|
|
|
1518
1493
|
};
|
|
1519
1494
|
return WAProto.WebMessageInfo.fromObject(messageJSON);
|
|
1520
1495
|
};
|
|
1521
|
-
export const generateWAMessage = async (jid, content, options
|
|
1496
|
+
export const generateWAMessage = async (jid, content, options) => {
|
|
1522
1497
|
// ensure msg ID is with every log
|
|
1523
1498
|
options.logger = options?.logger?.child({ msgId: options.messageId });
|
|
1524
1499
|
// Pass jid in the options to generateWAMessageContent
|
|
1525
|
-
if (jid
|
|
1500
|
+
if (jid) {
|
|
1526
1501
|
options.jid = jid;
|
|
1527
1502
|
}
|
|
1528
1503
|
return generateWAMessageFromContent(jid, await generateWAMessageContent(content, options), options);
|
|
@@ -1556,8 +1531,7 @@ export const normalizeMessageContent = (content) => {
|
|
|
1556
1531
|
return content;
|
|
1557
1532
|
// Lia@Changes 03-02-26 --- Add all futureProofMessage into getFutureProofMessage()
|
|
1558
1533
|
function getFutureProofMessage(message) {
|
|
1559
|
-
return (
|
|
1560
|
-
message?.associatedChildMessage ||
|
|
1534
|
+
return (message?.associatedChildMessage ||
|
|
1561
1535
|
message?.botForwardedMessage ||
|
|
1562
1536
|
message?.botInvokeMessage ||
|
|
1563
1537
|
message?.botTaskMessage ||
|
|
@@ -1571,16 +1545,19 @@ export const normalizeMessageContent = (content) => {
|
|
|
1571
1545
|
message?.groupStatusMessageV2 ||
|
|
1572
1546
|
message?.limitSharingMessage ||
|
|
1573
1547
|
message?.lottieStickerMessage ||
|
|
1548
|
+
message?.newsletterAdminProfileMessage ||
|
|
1549
|
+
message?.newsletterAdminProfileMessageV2 ||
|
|
1550
|
+
message?.newsletterAdminProfileStatusMessage ||
|
|
1574
1551
|
message?.pollCreationMessageV4 ||
|
|
1575
1552
|
message?.pollCreationOptionImageMessage ||
|
|
1576
1553
|
message?.questionMessage ||
|
|
1577
1554
|
message?.questionReplyMessage ||
|
|
1555
|
+
message?.spoilerMessage ||
|
|
1578
1556
|
message?.statusAddYours ||
|
|
1579
1557
|
message?.statusMentionMessage ||
|
|
1580
1558
|
message?.viewOnceMessage ||
|
|
1581
1559
|
message?.viewOnceMessageV2 ||
|
|
1582
|
-
message?.viewOnceMessageV2Extension
|
|
1583
|
-
);
|
|
1560
|
+
message?.viewOnceMessageV2Extension);
|
|
1584
1561
|
}
|
|
1585
1562
|
};
|
|
1586
1563
|
/**
|
|
@@ -1819,8 +1796,7 @@ export const assertMediaContent = (content) => {
|
|
|
1819
1796
|
*/
|
|
1820
1797
|
const isAnimatedWebP = (buffer) => {
|
|
1821
1798
|
// WebP must start with RIFF....WEBP
|
|
1822
|
-
if (
|
|
1823
|
-
buffer.length < 12 ||
|
|
1799
|
+
if (buffer.length < 12 ||
|
|
1824
1800
|
buffer[0] !== 0x52 ||
|
|
1825
1801
|
buffer[1] !== 0x49 ||
|
|
1826
1802
|
buffer[2] !== 0x46 ||
|
|
@@ -1828,10 +1804,10 @@ const isAnimatedWebP = (buffer) => {
|
|
|
1828
1804
|
buffer[8] !== 0x57 ||
|
|
1829
1805
|
buffer[9] !== 0x45 ||
|
|
1830
1806
|
buffer[10] !== 0x42 ||
|
|
1831
|
-
buffer[11] !== 0x50
|
|
1832
|
-
) {
|
|
1807
|
+
buffer[11] !== 0x50) {
|
|
1833
1808
|
return false;
|
|
1834
|
-
}
|
|
1809
|
+
}
|
|
1810
|
+
;
|
|
1835
1811
|
// Parse chunks starting after RIFF header (12 bytes)
|
|
1836
1812
|
let offset = 12;
|
|
1837
1813
|
while (offset < buffer.length - 8) {
|
|
@@ -1844,23 +1820,27 @@ const isAnimatedWebP = (buffer) => {
|
|
|
1844
1820
|
const flags = buffer[flagsOffset];
|
|
1845
1821
|
if (flags & 0x02) {
|
|
1846
1822
|
return true;
|
|
1847
|
-
}
|
|
1848
|
-
|
|
1849
|
-
|
|
1823
|
+
}
|
|
1824
|
+
;
|
|
1825
|
+
}
|
|
1826
|
+
;
|
|
1827
|
+
}
|
|
1828
|
+
else if (chunkFourCC === 'ANIM' || chunkFourCC === 'ANMF') {
|
|
1850
1829
|
// ANIM or ANMF chunks indicate animation
|
|
1851
1830
|
return true;
|
|
1852
|
-
}
|
|
1831
|
+
}
|
|
1832
|
+
;
|
|
1853
1833
|
// Move to next chunk (chunk size + 8 bytes header, padded to even)
|
|
1854
1834
|
offset += 8 + chunkSize + (chunkSize % 2);
|
|
1855
|
-
}
|
|
1835
|
+
}
|
|
1836
|
+
;
|
|
1856
1837
|
return false;
|
|
1857
1838
|
};
|
|
1858
1839
|
/**
|
|
1859
1840
|
* Checks if a buffer is a WebP file
|
|
1860
1841
|
*/
|
|
1861
1842
|
const isWebPBuffer = (buffer) => {
|
|
1862
|
-
return (
|
|
1863
|
-
buffer.length >= 12 &&
|
|
1843
|
+
return (buffer.length >= 12 &&
|
|
1864
1844
|
buffer[0] === 0x52 &&
|
|
1865
1845
|
buffer[1] === 0x49 &&
|
|
1866
1846
|
buffer[2] === 0x46 &&
|
|
@@ -1868,8 +1848,7 @@ const isWebPBuffer = (buffer) => {
|
|
|
1868
1848
|
buffer[8] === 0x57 &&
|
|
1869
1849
|
buffer[9] === 0x45 &&
|
|
1870
1850
|
buffer[10] === 0x42 &&
|
|
1871
|
-
buffer[11] === 0x50
|
|
1872
|
-
);
|
|
1851
|
+
buffer[11] === 0x50);
|
|
1873
1852
|
};
|
|
1874
1853
|
/**
|
|
1875
1854
|
* Lia@Changes 30-01-26
|
|
@@ -1878,14 +1857,8 @@ const isWebPBuffer = (buffer) => {
|
|
|
1878
1857
|
* A Biz Binary Node is added only for interactive messages
|
|
1879
1858
|
* such as buttons or other supported interactive types.
|
|
1880
1859
|
*/
|
|
1881
|
-
export const shouldIncludeBizBinaryNode = (message) =>
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
!message.interactiveMessage.shopStorefrontMessage;
|
|
1887
|
-
return (message.buttonsMessage ||
|
|
1888
|
-
message.interactiveMessage ||
|
|
1889
|
-
message.listMessage ||
|
|
1890
|
-
hasValidInteractive);
|
|
1891
|
-
};
|
|
1860
|
+
export const shouldIncludeBizBinaryNode = (message) => !!(message.buttonsMessage ||
|
|
1861
|
+
message.listMessage ||
|
|
1862
|
+
message.templateMessage ||
|
|
1863
|
+
(message.interactiveMessage &&
|
|
1864
|
+
message.interactiveMessage.nativeFlowMessage));
|