@itsliaaa/baileys 0.1.29 → 0.1.31
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 +170 -25
- package/WAProto/index.js +380 -0
- package/lib/Defaults/index.js +1 -1
- package/lib/Utils/generics.js +1 -1
- package/lib/Utils/messages.js +220 -269
- package/lib/Utils/use-single-file-auth-state.js +31 -15
- package/package.json +1 -1
package/lib/Utils/messages.js
CHANGED
|
@@ -12,6 +12,7 @@ import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './gener
|
|
|
12
12
|
import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
|
|
13
13
|
import { prepareRichResponseMessage } from './rich-message-utils.js';
|
|
14
14
|
import { shouldIncludeReportingToken } from './reporting-utils.js';
|
|
15
|
+
const CONCURRENCY_LIMIT = 10;
|
|
15
16
|
const MIMETYPE_MAP = {
|
|
16
17
|
image: 'image/jpeg',
|
|
17
18
|
video: 'video/mp4',
|
|
@@ -82,31 +83,24 @@ const assertColor = async (color) => {
|
|
|
82
83
|
return assertedColor;
|
|
83
84
|
}
|
|
84
85
|
};
|
|
86
|
+
// Lia@Changes 21-04-26 --- Refactor prepareWAMessageMedia function
|
|
85
87
|
export const prepareWAMessageMedia = async (message, options) => {
|
|
86
88
|
const logger = options.logger;
|
|
87
|
-
|
|
88
|
-
for (const key of MEDIA_KEYS) {
|
|
89
|
-
if (key in message) {
|
|
90
|
-
mediaType = key;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
89
|
+
const mediaType = MEDIA_KEYS.find(key => key in message);
|
|
93
90
|
if (!mediaType) {
|
|
94
91
|
throw new Boom('Invalid media type', { statusCode: 400 });
|
|
95
92
|
}
|
|
96
|
-
const uploadData = {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
};
|
|
93
|
+
const uploadData = { ...message };
|
|
94
|
+
const mediaPayload = uploadData[mediaType];
|
|
95
|
+
delete uploadData[mediaType];
|
|
100
96
|
if (uploadData.image || uploadData.video) {
|
|
101
97
|
uploadData.annotations = mediaAnnotation;
|
|
102
98
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
'url' in uploadData.media &&
|
|
107
|
-
!!uploadData.media.url &&
|
|
99
|
+
const cacheableKey = typeof mediaPayload === 'object' &&
|
|
100
|
+
'url' in mediaPayload &&
|
|
101
|
+
!!mediaPayload.url &&
|
|
108
102
|
!!options.mediaCache &&
|
|
109
|
-
mediaType
|
|
103
|
+
`${mediaType}:${mediaPayload.url}`;
|
|
110
104
|
if (mediaType === 'document' && !uploadData.fileName) {
|
|
111
105
|
uploadData.fileName = LIBRARY_NAME;
|
|
112
106
|
}
|
|
@@ -119,7 +113,7 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
119
113
|
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
120
114
|
const obj = proto.Message.decode(mediaBuff);
|
|
121
115
|
const key = `${mediaType}Message`;
|
|
122
|
-
Object.assign(obj[key],
|
|
116
|
+
Object.assign(obj[key], uploadData);
|
|
123
117
|
return obj;
|
|
124
118
|
}
|
|
125
119
|
}
|
|
@@ -129,142 +123,118 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
129
123
|
const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
|
|
130
124
|
const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
|
|
131
125
|
const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation || requiresWaveformProcessing;
|
|
126
|
+
let mediaUrl,
|
|
127
|
+
directPath,
|
|
128
|
+
thumbnailDirectPath,
|
|
129
|
+
thumbnailSha256,
|
|
130
|
+
fileSha256,
|
|
131
|
+
fileLength,
|
|
132
|
+
mediaKey,
|
|
133
|
+
fileEncSha256;
|
|
132
134
|
// Lia@Changes 06-02-26 --- Add few support for sending media to newsletter (≧▽≦)
|
|
133
135
|
if (isNewsletter) {
|
|
134
136
|
logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
|
|
135
|
-
const
|
|
137
|
+
const rawData = await getRawMediaUploadData(mediaPayload, options.mediaTypeOverride || mediaType, logger);
|
|
138
|
+
fileSha256 = rawData.fileSha256;
|
|
139
|
+
fileLength = rawData.fileLength;
|
|
140
|
+
const filePath = rawData.filePath;
|
|
136
141
|
const fileSha256B64 = fileSha256.toString('base64');
|
|
137
|
-
const [
|
|
138
|
-
(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
});
|
|
145
|
-
logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
|
|
146
|
-
return result;
|
|
147
|
-
})(),
|
|
142
|
+
const [uploadResult] = await Promise.all([
|
|
143
|
+
options.upload(filePath, {
|
|
144
|
+
fileEncSha256B64: fileSha256B64,
|
|
145
|
+
mediaType,
|
|
146
|
+
timeoutMs: options.mediaUploadTimeoutMs,
|
|
147
|
+
newsletter: true
|
|
148
|
+
}),
|
|
148
149
|
(async () => {
|
|
149
150
|
try {
|
|
150
151
|
if (requiresThumbnailComputation) {
|
|
151
152
|
const { thumbnail } = await generateThumbnail(filePath, mediaType, options);
|
|
152
153
|
uploadData.jpegThumbnail = thumbnail;
|
|
153
|
-
logger?.debug('generated thumbnail');
|
|
154
154
|
}
|
|
155
155
|
if (requiresDurationComputation) {
|
|
156
156
|
uploadData.seconds = await getAudioDuration(filePath);
|
|
157
|
-
logger?.debug('computed audio duration');
|
|
158
157
|
}
|
|
159
158
|
}
|
|
160
159
|
catch (error) {
|
|
161
160
|
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
162
161
|
}
|
|
163
162
|
})()
|
|
164
|
-
])
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
delete uploadData.media;
|
|
174
|
-
const obj = proto.Message.create({
|
|
175
|
-
// todo: add more support here
|
|
176
|
-
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
|
|
177
|
-
url: mediaUrl,
|
|
178
|
-
directPath,
|
|
179
|
-
fileSha256,
|
|
180
|
-
fileLength,
|
|
181
|
-
thumbnailDirectPath,
|
|
182
|
-
thumbnailSha256,
|
|
183
|
-
...uploadData
|
|
184
|
-
})
|
|
185
|
-
});
|
|
186
|
-
if (uploadData.ptv) {
|
|
187
|
-
obj.ptvMessage = obj.videoMessage;
|
|
188
|
-
delete obj.videoMessage;
|
|
189
|
-
}
|
|
190
|
-
if (obj.stickerMessage) {
|
|
191
|
-
obj.stickerMessage.stickerSentTs = Date.now();
|
|
192
|
-
}
|
|
193
|
-
if (cacheableKey) {
|
|
194
|
-
logger?.debug({ cacheableKey }, 'set cache');
|
|
195
|
-
await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
196
|
-
}
|
|
197
|
-
return obj;
|
|
163
|
+
]);
|
|
164
|
+
// todo: add more support here
|
|
165
|
+
mediaUrl = uploadResult.mediaUrl;
|
|
166
|
+
directPath = uploadResult.directPath;
|
|
167
|
+
thumbnailDirectPath = uploadResult.thumbnailDirectPath;
|
|
168
|
+
thumbnailSha256 = uploadResult.thumbnailSha256;
|
|
169
|
+
fs.unlink(filePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
198
170
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
171
|
+
else {
|
|
172
|
+
const encryptedData = await encryptedStream(mediaPayload, options.mediaTypeOverride || mediaType, {
|
|
173
|
+
logger,
|
|
174
|
+
saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
|
|
175
|
+
opts: options.options
|
|
176
|
+
});
|
|
177
|
+
mediaKey = encryptedData.mediaKey;
|
|
178
|
+
fileEncSha256 = encryptedData.fileEncSha256;
|
|
179
|
+
fileSha256 = encryptedData.fileSha256;
|
|
180
|
+
fileLength = encryptedData.fileLength;
|
|
181
|
+
const encFilePath = encryptedData.encFilePath;
|
|
182
|
+
const originalFilePath = encryptedData.originalFilePath;
|
|
183
|
+
const fileEncSha256B64 = fileEncSha256.toString('base64');
|
|
184
|
+
const [uploadResult] = await Promise.all([
|
|
185
|
+
options.upload(encFilePath, {
|
|
208
186
|
fileEncSha256B64,
|
|
209
187
|
mediaType,
|
|
210
188
|
timeoutMs: options.mediaUploadTimeoutMs
|
|
211
|
-
})
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
189
|
+
}),
|
|
190
|
+
(async () => {
|
|
191
|
+
try {
|
|
192
|
+
if (requiresThumbnailComputation) {
|
|
193
|
+
const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
|
|
194
|
+
uploadData.jpegThumbnail = thumbnail;
|
|
195
|
+
if (!uploadData.width && originalImageDimensions) {
|
|
196
|
+
uploadData.width = originalImageDimensions.width;
|
|
197
|
+
uploadData.height = originalImageDimensions.height;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (requiresDurationComputation) {
|
|
201
|
+
uploadData.seconds = await getAudioDuration(originalFilePath);
|
|
202
|
+
}
|
|
203
|
+
if (requiresWaveformProcessing) {
|
|
204
|
+
uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
|
|
205
|
+
}
|
|
206
|
+
if (requiresAudioBackground) {
|
|
207
|
+
uploadData.backgroundArgb = await assertColor(options.backgroundColor);
|
|
224
208
|
}
|
|
225
|
-
logger?.debug('generated thumbnail');
|
|
226
|
-
}
|
|
227
|
-
if (requiresDurationComputation) {
|
|
228
|
-
uploadData.seconds = await getAudioDuration(originalFilePath);
|
|
229
|
-
logger?.debug('computed audio duration');
|
|
230
|
-
}
|
|
231
|
-
if (requiresWaveformProcessing) {
|
|
232
|
-
uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
|
|
233
|
-
logger?.debug('processed waveform');
|
|
234
209
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
logger?.debug('computed backgroundColor audio status');
|
|
210
|
+
catch (error) {
|
|
211
|
+
logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
|
|
238
212
|
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
213
|
+
})()
|
|
214
|
+
]);
|
|
215
|
+
mediaUrl = uploadResult.mediaUrl;
|
|
216
|
+
directPath = uploadResult.directPath;
|
|
217
|
+
fs.unlink(encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
218
|
+
fs.unlink(originalFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
219
|
+
}
|
|
220
|
+
const messagePayload = {
|
|
221
|
+
url: mediaUrl,
|
|
222
|
+
directPath,
|
|
223
|
+
fileSha256,
|
|
224
|
+
fileLength,
|
|
225
|
+
...uploadData
|
|
226
|
+
};
|
|
227
|
+
if (isNewsletter) {
|
|
228
|
+
messagePayload.thumbnailDirectPath = thumbnailDirectPath;
|
|
229
|
+
messagePayload.thumbnailSha256 = thumbnailSha256;
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
messagePayload.mediaKey = mediaKey;
|
|
233
|
+
messagePayload.fileEncSha256 = fileEncSha256;
|
|
234
|
+
messagePayload.mediaKeyTimestamp = unixTimestampSeconds();
|
|
235
|
+
}
|
|
257
236
|
const obj = proto.Message.create({
|
|
258
|
-
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject(
|
|
259
|
-
url: mediaUrl,
|
|
260
|
-
directPath,
|
|
261
|
-
mediaKey,
|
|
262
|
-
fileEncSha256,
|
|
263
|
-
fileSha256,
|
|
264
|
-
fileLength,
|
|
265
|
-
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
266
|
-
...uploadData
|
|
267
|
-
})
|
|
237
|
+
[`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject(messagePayload)
|
|
268
238
|
});
|
|
269
239
|
if (uploadData.ptv) {
|
|
270
240
|
obj.ptvMessage = obj.videoMessage;
|
|
@@ -274,8 +244,8 @@ export const prepareWAMessageMedia = async (message, options) => {
|
|
|
274
244
|
obj.stickerMessage.stickerSentTs = Date.now();
|
|
275
245
|
}
|
|
276
246
|
if (cacheableKey) {
|
|
277
|
-
logger?.debug({ cacheableKey }, 'set cache');
|
|
278
|
-
|
|
247
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
248
|
+
options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
|
|
279
249
|
}
|
|
280
250
|
return obj;
|
|
281
251
|
};
|
|
@@ -319,6 +289,7 @@ const prepareProductMessage = async (message, options) => {
|
|
|
319
289
|
* Credits: Work on ensuring stickerPackMessage fields are valid by @jlucaso1 (https://github.com/jlucaso1).
|
|
320
290
|
* based on https://github.com/WhiskeySockets/Baileys/pull/1561
|
|
321
291
|
*/
|
|
292
|
+
// Lia@Changes 21-04-26 --- Enhanced prepareStickerPackMessage
|
|
322
293
|
const prepareStickerPackMessage = async (message, options) => {
|
|
323
294
|
const { cover, stickers = [], name = '📦 Sticker Pack', publisher = 'GitHub: itsliaaa', description = '🏷️ itsliaaa/baileys' } = message;
|
|
324
295
|
if (stickers.length > 60) {
|
|
@@ -330,136 +301,124 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
330
301
|
if (!cover) {
|
|
331
302
|
throw new Boom('Sticker pack must contain a cover', { statusCode: 400 });
|
|
332
303
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
304
|
+
const logger = options.logger;
|
|
305
|
+
// Lia@Changes 01-02-26 --- Add caching for sticker pack
|
|
306
|
+
let cacheableKey = false;
|
|
307
|
+
if (Array.isArray(stickers) && stickers.length && options.mediaCache) {
|
|
308
|
+
const urls = [];
|
|
309
|
+
for (let i = 0; i < stickers.length; i++) {
|
|
310
|
+
const data = stickers[i].data;
|
|
311
|
+
if (typeof data === 'object' && data?.url) {
|
|
312
|
+
urls.push(data.url);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (urls.length > 0) {
|
|
316
|
+
cacheableKey = 'sticker:' + urls.join('@');
|
|
317
|
+
}
|
|
318
|
+
}
|
|
347
319
|
if (cacheableKey) {
|
|
348
320
|
const mediaBuff = await options.mediaCache.get(cacheableKey);
|
|
349
321
|
if (mediaBuff) {
|
|
350
|
-
|
|
322
|
+
logger?.debug({ cacheableKey }, 'got media cache hit');
|
|
351
323
|
return proto.Message.StickerPackMessage.decode(mediaBuff);
|
|
352
324
|
}
|
|
353
325
|
}
|
|
354
326
|
const lib = await getImageProcessingLibrary();
|
|
327
|
+
const hasSharp = 'sharp' in lib && !!lib.sharp?.default;
|
|
328
|
+
const hasImage = 'image' in lib && !!lib.image?.Transformer;
|
|
329
|
+
const hasJimp = 'jimp' in lib && !!lib.jimp?.Jimp;
|
|
330
|
+
if (!hasSharp && !hasImage) {
|
|
331
|
+
throw new Boom('No image processing library (sharp or @napi-rs/image) available for converting sticker to WebP.');
|
|
332
|
+
}
|
|
355
333
|
const stickerPackIdValue = generateMessageIDV2();
|
|
356
334
|
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
|
-
|
|
335
|
+
const stickerMetadata = new Array(stickers.length);
|
|
336
|
+
for (let i = 0; i < stickers.length; i += CONCURRENCY_LIMIT) {
|
|
337
|
+
const promises = [];
|
|
338
|
+
const chunkEnd = Math.min(i + CONCURRENCY_LIMIT, stickers.length);
|
|
339
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
340
|
+
promises.push((async (index) => {
|
|
341
|
+
const sticker = stickers[index];
|
|
342
|
+
const { stream } = await getStream(sticker.data);
|
|
343
|
+
const buffer = await toBuffer(stream);
|
|
344
|
+
let webpBuffer;
|
|
345
|
+
let isAnimated = false;
|
|
346
|
+
if (isWebPBuffer(buffer)) {
|
|
347
|
+
webpBuffer = buffer;
|
|
348
|
+
isAnimated = isAnimatedWebP(buffer);
|
|
349
|
+
}
|
|
350
|
+
else if (hasSharp) {
|
|
351
|
+
webpBuffer = await lib.sharp.default(buffer)
|
|
352
|
+
.resize(512, 512, { fit: 'inside' })
|
|
353
|
+
.webp({ quality: 80 })
|
|
354
|
+
.toBuffer();
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
webpBuffer = await new lib.image.Transformer(buffer)
|
|
358
|
+
.resize(512, 512)
|
|
359
|
+
.webp(80);
|
|
360
|
+
}
|
|
361
|
+
if (webpBuffer.length > 1024 * 1024) {
|
|
362
|
+
throw new Boom(`Sticker at index ${index} exceeds the 1MB size limit`, { statusCode: 400 });
|
|
363
|
+
}
|
|
364
|
+
const hash = sha256(webpBuffer).toString('base64').replace(/\//g, '-');
|
|
365
|
+
const fileName = `${hash}.webp`;
|
|
366
|
+
stickerData[fileName] = [new Uint8Array(webpBuffer), { level: 0 }];
|
|
367
|
+
stickerMetadata[index] = {
|
|
368
|
+
fileName,
|
|
369
|
+
mimetype: 'image/webp',
|
|
370
|
+
isAnimated,
|
|
371
|
+
emojis: sticker.emojis || ['✨'],
|
|
372
|
+
accessibilityLabel: sticker.accessibilityLabel || ''
|
|
373
|
+
};
|
|
374
|
+
})(j));
|
|
387
375
|
}
|
|
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';
|
|
376
|
+
await Promise.all(promises);
|
|
377
|
+
}
|
|
378
|
+
const trayIconFileName = `${stickerPackIdValue}.webp`;
|
|
408
379
|
const { stream: coverStream } = await getStream(cover);
|
|
409
380
|
const coverBuffer = await toBuffer(coverStream);
|
|
410
381
|
let coverWebpBuffer;
|
|
411
|
-
|
|
412
|
-
if (isCoverWebP) {
|
|
413
|
-
// Already WebP - preserve original to keep exif metadata
|
|
382
|
+
if (isWebPBuffer(coverBuffer)) {
|
|
414
383
|
coverWebpBuffer = coverBuffer;
|
|
415
384
|
}
|
|
416
|
-
else if (
|
|
417
|
-
coverWebpBuffer = await lib
|
|
418
|
-
.sharp
|
|
419
|
-
.default(coverBuffer)
|
|
385
|
+
else if (hasSharp) {
|
|
386
|
+
coverWebpBuffer = await lib.sharp.default(coverBuffer)
|
|
420
387
|
.resize(512, 512, { fit: 'inside' })
|
|
421
388
|
.webp({ quality: 80 })
|
|
422
389
|
.toBuffer();
|
|
423
390
|
}
|
|
424
|
-
else
|
|
425
|
-
coverWebpBuffer = await new lib
|
|
426
|
-
.image
|
|
427
|
-
.Transformer(coverBuffer)
|
|
391
|
+
else {
|
|
392
|
+
coverWebpBuffer = await new lib.image.Transformer(coverBuffer)
|
|
428
393
|
.resize(512, 512)
|
|
429
394
|
.webp(80);
|
|
430
395
|
}
|
|
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
396
|
stickerData[trayIconFileName] = [new Uint8Array(coverWebpBuffer), { level: 0 }];
|
|
436
397
|
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
|
-
});
|
|
398
|
+
zip(stickerData, (error, data) => error ? reject(error) : resolve(Buffer.from(data)));
|
|
444
399
|
});
|
|
445
|
-
const stickerPackSize = zipBuffer.length;
|
|
446
400
|
const stickerPackUpload = await encryptedStream(zipBuffer, 'sticker-pack', {
|
|
447
|
-
logger
|
|
401
|
+
logger,
|
|
448
402
|
opts: options.options
|
|
449
403
|
});
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
404
|
+
let stickerPackUploadResult;
|
|
405
|
+
try {
|
|
406
|
+
stickerPackUploadResult = await options.upload(stickerPackUpload.encFilePath, {
|
|
407
|
+
fileEncSha256B64: stickerPackUpload.fileEncSha256.toString('base64'),
|
|
408
|
+
mediaType: 'sticker-pack',
|
|
409
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
fs.unlink(stickerPackUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
414
|
+
}
|
|
456
415
|
const obj = {
|
|
457
|
-
name
|
|
458
|
-
publisher
|
|
416
|
+
name,
|
|
417
|
+
publisher,
|
|
459
418
|
stickerPackId: stickerPackIdValue,
|
|
460
419
|
packDescription: description,
|
|
461
420
|
stickerPackOrigin: proto.Message.StickerPackMessage.StickerPackOrigin.USER_CREATED,
|
|
462
|
-
stickerPackSize:
|
|
421
|
+
stickerPackSize: zipBuffer.length,
|
|
463
422
|
stickers: stickerMetadata,
|
|
464
423
|
fileSha256: stickerPackUpload.fileSha256,
|
|
465
424
|
fileEncSha256: stickerPackUpload.fileEncSha256,
|
|
@@ -467,31 +426,19 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
467
426
|
directPath: stickerPackUploadResult.directPath,
|
|
468
427
|
fileLength: stickerPackUpload.fileLength,
|
|
469
428
|
mediaKeyTimestamp: unixTimestampSeconds(),
|
|
470
|
-
trayIconFileName
|
|
429
|
+
trayIconFileName
|
|
471
430
|
};
|
|
472
431
|
try {
|
|
473
|
-
// Reuse the cover buffer we already processed for thumbnail generation
|
|
474
432
|
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) {
|
|
433
|
+
if (hasSharp) {
|
|
434
|
+
thumbnailBuffer = await lib.sharp.default(coverBuffer).resize(252, 252).jpeg().toBuffer();
|
|
435
|
+
}
|
|
436
|
+
else if (hasImage) {
|
|
437
|
+
thumbnailBuffer = await new lib.image.Transformer(coverBuffer).resize(252, 252).jpeg();
|
|
438
|
+
}
|
|
439
|
+
else if (hasJimp) {
|
|
491
440
|
const jimpImage = await lib.jimp.Jimp.read(coverBuffer);
|
|
492
|
-
thumbnailBuffer = await jimpImage
|
|
493
|
-
.resize({ w: 252, h: 252 })
|
|
494
|
-
.getBuffer('image/jpeg');
|
|
441
|
+
thumbnailBuffer = await jimpImage.resize({ w: 252, h: 252 }).getBuffer('image/jpeg');
|
|
495
442
|
}
|
|
496
443
|
else {
|
|
497
444
|
throw new Error('No image processing library available for thumbnail generation');
|
|
@@ -500,16 +447,21 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
500
447
|
throw new Error('Failed to generate thumbnail buffer');
|
|
501
448
|
}
|
|
502
449
|
const thumbUpload = await encryptedStream(thumbnailBuffer, 'thumbnail-sticker-pack', {
|
|
503
|
-
logger
|
|
450
|
+
logger,
|
|
504
451
|
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
|
|
452
|
+
mediaKey: stickerPackUpload.mediaKey
|
|
511
453
|
});
|
|
512
|
-
|
|
454
|
+
let thumbUploadResult;
|
|
455
|
+
try {
|
|
456
|
+
thumbUploadResult = await options.upload(thumbUpload.encFilePath, {
|
|
457
|
+
fileEncSha256B64: thumbUpload.fileEncSha256.toString('base64'),
|
|
458
|
+
mediaType: 'thumbnail-sticker-pack',
|
|
459
|
+
timeoutMs: options.mediaUploadTimeoutMs
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
finally {
|
|
463
|
+
fs.unlink(thumbUpload.encFilePath).catch(() => logger?.warn('failed to remove tmp file'));
|
|
464
|
+
}
|
|
513
465
|
Object.assign(obj, {
|
|
514
466
|
thumbnailDirectPath: thumbUploadResult.directPath,
|
|
515
467
|
thumbnailSha256: thumbUpload.fileSha256,
|
|
@@ -520,14 +472,13 @@ const prepareStickerPackMessage = async (message, options) => {
|
|
|
520
472
|
});
|
|
521
473
|
}
|
|
522
474
|
catch (error) {
|
|
523
|
-
|
|
475
|
+
logger?.warn(`Thumbnail generation failed: ${error}`);
|
|
524
476
|
}
|
|
525
|
-
const content = obj;
|
|
526
477
|
if (cacheableKey) {
|
|
527
|
-
|
|
528
|
-
|
|
478
|
+
logger?.debug({ cacheableKey }, 'set cache (background)');
|
|
479
|
+
options.mediaCache.set(cacheableKey, WAProto.Message.StickerPackMessage.encode(obj).finish());
|
|
529
480
|
}
|
|
530
|
-
return WAProto.Message.StickerPackMessage.fromObject(
|
|
481
|
+
return WAProto.Message.StickerPackMessage.fromObject(obj);
|
|
531
482
|
};
|
|
532
483
|
// Lia@Changes 30-01-26 --- Add native flow button helper for interactive message
|
|
533
484
|
const prepareNativeFlowButtons = (message) => {
|
|
@@ -1319,6 +1270,12 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1319
1270
|
}
|
|
1320
1271
|
m = { invoiceMessage };
|
|
1321
1272
|
}
|
|
1273
|
+
if (shouldIncludeReportingToken(m)) {
|
|
1274
|
+
m.messageContextInfo = m.messageContextInfo || {};
|
|
1275
|
+
if (!m.messageContextInfo.messageSecret) {
|
|
1276
|
+
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1322
1279
|
// Lia@Changes 31-01-26 --- Add direct externalAdReply access (no need to create contextInfo first)
|
|
1323
1280
|
if (hasOptionalProperty(message, 'externalAdReply') && !!message.externalAdReply) {
|
|
1324
1281
|
const messageType = Object.keys(m)[0];
|
|
@@ -1336,9 +1293,9 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1336
1293
|
mediaType: content.mediaType || 1,
|
|
1337
1294
|
mediaUrl: content.url,
|
|
1338
1295
|
renderLargerThumbnail: content.largeThumbnail,
|
|
1339
|
-
sourceUrl: content.url
|
|
1296
|
+
sourceUrl: content.url,
|
|
1340
1297
|
thumbnail: content.thumbnail,
|
|
1341
|
-
thumbnailUrl: content.url,
|
|
1298
|
+
thumbnailUrl: content.url + '?update=' + Date.now(),
|
|
1342
1299
|
title: content.title || LIBRARY_NAME
|
|
1343
1300
|
};
|
|
1344
1301
|
delete externalAdReply.subTitle;
|
|
@@ -1434,12 +1391,6 @@ export const generateWAMessageContent = async (message, options) => {
|
|
|
1434
1391
|
}
|
|
1435
1392
|
}
|
|
1436
1393
|
}
|
|
1437
|
-
if (shouldIncludeReportingToken(m)) {
|
|
1438
|
-
m.messageContextInfo = m.messageContextInfo || {};
|
|
1439
|
-
if (!m.messageContextInfo.messageSecret) {
|
|
1440
|
-
m.messageContextInfo.messageSecret = randomBytes(32);
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
1394
|
return proto.Message.create(m);
|
|
1444
1395
|
};
|
|
1445
1396
|
export const generateWAMessageFromContent = (jid, message, options) => {
|