alipclutch-baileys 8.3.0 → 8.4.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/LICENSE +3 -1
- package/README.md +10 -9
- package/WAProto/GenerateStatics.sh +4 -0
- package/WAProto/WAProto.proto +4775 -0
- package/WAProto/index.d.ts +55057 -0
- package/WAProto/index.js +56887 -17535
- package/WAProto/index.ts.ts +53473 -0
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/index.d.ts +16 -7
- package/lib/Defaults/index.js +90 -119
- package/lib/Socket/Client/index.d.ts +2 -3
- package/lib/Socket/Client/index.js +2 -3
- package/lib/Socket/Client/types.d.ts +0 -1
- package/lib/Socket/Client/types.js +0 -1
- package/lib/Socket/Client/websocket.d.ts +0 -1
- package/lib/Socket/Client/websocket.js +0 -1
- package/lib/Socket/business.d.ts +59 -58
- package/lib/Socket/chats.d.ts +45 -230
- package/lib/Socket/chats.js +139 -238
- package/lib/Socket/groups.d.ts +41 -32
- package/lib/Socket/groups.js +38 -23
- package/lib/Socket/index.d.ts +63 -64
- package/lib/Socket/index.js +2 -3
- package/lib/Socket/messages-recv.js +9 -65
- package/lib/Socket/messages-send.d.ts +49 -47
- package/lib/Socket/messages-send.js +584 -395
- package/lib/Socket/newsletter.d.ts +39 -37
- package/lib/Socket/newsletter.js +88 -123
- package/lib/Socket/setup.js +433 -0
- package/lib/Socket/{dugong.d.ts → setup.ts} +17 -52
- package/lib/Socket/socket.d.ts +10 -10
- package/lib/Socket/socket.js +62 -38
- package/lib/Socket/usync.d.ts +3 -4
- package/lib/Socket/usync.js +0 -1
- package/lib/Store/index.d.ts +1 -2
- package/lib/Store/index.js +1 -3
- package/lib/Store/make-in-memory-store.d.ts +24 -24
- package/lib/Store/make-in-memory-store.js +26 -14
- package/lib/Store/make-ordered-dictionary.d.ts +1 -1
- package/lib/Store/make-ordered-dictionary.js +2 -2
- package/lib/Types/Auth.d.ts +0 -7
- package/lib/Types/Call.d.ts +1 -1
- package/lib/Types/Chat.d.ts +14 -7
- package/lib/Types/Contact.d.ts +5 -1
- package/lib/Types/Events.d.ts +44 -2
- package/lib/Types/GroupMetadata.d.ts +11 -2
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Message.d.ts +148 -21
- package/lib/Types/Message.js +0 -2
- package/lib/Types/Newsletter.d.ts +73 -97
- package/lib/Types/Newsletter.js +18 -38
- package/lib/Types/Socket.d.ts +17 -9
- package/lib/Types/index.d.ts +8 -1
- package/lib/Types/index.js +2 -2
- package/lib/Utils/auth-utils.d.ts +3 -3
- package/lib/Utils/auth-utils.js +6 -13
- package/lib/Utils/business.js +2 -2
- package/lib/Utils/chat-utils.d.ts +15 -16
- package/lib/Utils/chat-utils.js +36 -35
- package/lib/Utils/crypto.d.ts +15 -16
- package/lib/Utils/crypto.js +71 -29
- package/lib/Utils/decode-wa-message.d.ts +22 -6
- package/lib/Utils/decode-wa-message.js +65 -56
- package/lib/Utils/event-buffer.d.ts +2 -2
- package/lib/Utils/event-buffer.js +11 -7
- package/lib/Utils/generics.d.ts +17 -20
- package/lib/Utils/generics.js +95 -75
- package/lib/Utils/history.d.ts +4 -0
- package/lib/Utils/history.js +4 -6
- package/lib/Utils/link-preview.d.ts +2 -2
- package/lib/Utils/link-preview.js +34 -1
- package/lib/Utils/logger.d.ts +10 -3
- package/lib/Utils/lt-hash.d.ts +2 -2
- package/lib/Utils/lt-hash.js +6 -6
- package/lib/Utils/make-mutex.d.ts +2 -2
- package/lib/Utils/messages-media.d.ts +28 -24
- package/lib/Utils/messages-media.js +272 -111
- package/lib/Utils/messages.d.ts +13 -10
- package/lib/Utils/messages.js +323 -50
- package/lib/Utils/noise-handler.d.ts +10 -12
- package/lib/Utils/noise-handler.js +18 -23
- package/lib/Utils/process-message.d.ts +5 -4
- package/lib/Utils/process-message.js +108 -25
- package/lib/Utils/signal.d.ts +2 -1
- package/lib/Utils/signal.js +26 -26
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -1
- package/lib/Utils/use-multi-file-auth-state.js +51 -6
- package/lib/Utils/validate-connection.d.ts +3 -4
- package/lib/Utils/validate-connection.js +90 -66
- package/lib/WABinary/constants.d.ts +24 -27
- package/lib/WABinary/constants.js +1276 -13
- package/lib/WABinary/decode.d.ts +3 -4
- package/lib/WABinary/decode.js +26 -13
- package/lib/WABinary/encode.d.ts +1 -2
- package/lib/WABinary/encode.js +137 -152
- package/lib/WABinary/generic-utils.d.ts +1 -4
- package/lib/WABinary/generic-utils.js +37 -125
- package/lib/WABinary/jid-utils.d.ts +11 -5
- package/lib/WABinary/jid-utils.js +28 -5
- package/lib/WABinary/jid-utils.js.bak +83 -0
- package/lib/WAM/BinaryInfo.d.ts +2 -11
- package/lib/WAM/encode.d.ts +1 -2
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -1
- package/lib/index.d.ts +0 -1
- package/lib/index.js +27 -15
- package/package.json +18 -31
- package/engine-requirements.js +0 -10
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
- package/lib/Socket/Client/abstract-socket-client.js +0 -13
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/Client/web-socket-client.d.ts +0 -12
- package/lib/Socket/Client/web-socket-client.js +0 -62
- package/lib/Socket/dugong.js +0 -483
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Store/make-cache-manager-store.d.ts +0 -13
- package/lib/Store/make-cache-manager-store.js +0 -83
|
@@ -15,23 +15,47 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
37
|
};
|
|
25
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.
|
|
39
|
+
exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.prepareStream = exports.getHttpStream = exports.getStream = exports.toBuffer = exports.toReadable = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.extractVideoThumb = exports.hkdfInfoKey = void 0;
|
|
40
|
+
exports.getMediaKeys = getMediaKeys;
|
|
41
|
+
exports.uploadFile = uploadFile;
|
|
42
|
+
exports.vid2jpg = vid2jpg;
|
|
43
|
+
exports.getAudioDuration = getAudioDuration;
|
|
44
|
+
exports.getAudioWaveform = getAudioWaveform;
|
|
45
|
+
exports.generateThumbnail = generateThumbnail;
|
|
46
|
+
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
27
47
|
const boom_1 = require("@hapi/boom");
|
|
28
|
-
const
|
|
48
|
+
const axios_1 = __importDefault(require("axios"));
|
|
49
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
50
|
+
const cheerio = __importStar(require("cheerio"));
|
|
29
51
|
const Crypto = __importStar(require("crypto"));
|
|
30
52
|
const events_1 = require("events");
|
|
31
53
|
const fs_1 = require("fs");
|
|
32
54
|
const os_1 = require("os");
|
|
33
55
|
const path_1 = require("path");
|
|
56
|
+
const jimp_1 = __importDefault(require("jimp"));
|
|
34
57
|
const stream_1 = require("stream");
|
|
58
|
+
const child_process_1 = require("child_process");
|
|
35
59
|
const WAProto_1 = require("../../WAProto");
|
|
36
60
|
const Defaults_1 = require("../Defaults");
|
|
37
61
|
const WABinary_1 = require("../WABinary");
|
|
@@ -41,13 +65,11 @@ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
|
|
|
41
65
|
const getImageProcessingLibrary = async () => {
|
|
42
66
|
const [_jimp, sharp] = await Promise.all([
|
|
43
67
|
(async () => {
|
|
44
|
-
const jimp = await (
|
|
45
|
-
.catch(() => { }));
|
|
68
|
+
const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }));
|
|
46
69
|
return jimp;
|
|
47
70
|
})(),
|
|
48
71
|
(async () => {
|
|
49
|
-
const sharp = await (
|
|
50
|
-
.catch(() => { }));
|
|
72
|
+
const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }));
|
|
51
73
|
return sharp;
|
|
52
74
|
})()
|
|
53
75
|
]);
|
|
@@ -66,7 +88,7 @@ const hkdfInfoKey = (type) => {
|
|
|
66
88
|
};
|
|
67
89
|
exports.hkdfInfoKey = hkdfInfoKey;
|
|
68
90
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
69
|
-
function getMediaKeys(buffer, mediaType) {
|
|
91
|
+
async function getMediaKeys(buffer, mediaType) {
|
|
70
92
|
if (!buffer) {
|
|
71
93
|
throw new boom_1.Boom('Cannot derive from empty media key');
|
|
72
94
|
}
|
|
@@ -74,26 +96,183 @@ function getMediaKeys(buffer, mediaType) {
|
|
|
74
96
|
buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
|
|
75
97
|
}
|
|
76
98
|
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
|
77
|
-
const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
99
|
+
const expandedMediaKey = await (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
78
100
|
return {
|
|
79
101
|
iv: expandedMediaKey.slice(0, 16),
|
|
80
102
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
81
103
|
macKey: expandedMediaKey.slice(48, 80),
|
|
82
104
|
};
|
|
83
105
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
106
|
+
async function uploadFile(buffer, logger) {
|
|
107
|
+
const { fromBuffer } = await Promise.resolve().then(() => __importStar(require('file-type')));
|
|
108
|
+
const fileType = await fromBuffer(buffer);
|
|
109
|
+
if (!fileType)
|
|
110
|
+
throw new Error("Failed to detect file type.");
|
|
111
|
+
const { ext, mime } = fileType;
|
|
112
|
+
const services = [
|
|
113
|
+
{
|
|
114
|
+
name: "catbox",
|
|
115
|
+
url: "https://catbox.moe/user/api.php",
|
|
116
|
+
buildForm: () => {
|
|
117
|
+
const form = new form_data_1.default();
|
|
118
|
+
form.append("fileToUpload", buffer, {
|
|
119
|
+
filename: `file.${ext}`,
|
|
120
|
+
contentType: mime || "application/octet-stream"
|
|
121
|
+
});
|
|
122
|
+
form.append("reqtype", "fileupload");
|
|
123
|
+
return form;
|
|
124
|
+
},
|
|
125
|
+
parseResponse: res => res.data
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "pdi.moe",
|
|
129
|
+
url: "https://scdn.pdi.moe/upload",
|
|
130
|
+
buildForm: () => {
|
|
131
|
+
const form = new form_data_1.default();
|
|
132
|
+
form.append("file", buffer, {
|
|
133
|
+
filename: `file.${ext}`,
|
|
134
|
+
contentType: mime
|
|
135
|
+
});
|
|
136
|
+
return form;
|
|
137
|
+
},
|
|
138
|
+
parseResponse: res => res.data.result.url
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "qu.ax",
|
|
142
|
+
url: "https://qu.ax/upload.php",
|
|
143
|
+
buildForm: () => {
|
|
144
|
+
const form = new form_data_1.default();
|
|
145
|
+
form.append("files[]", buffer, {
|
|
146
|
+
filename: `file.${ext}`,
|
|
147
|
+
contentType: mime || "application/octet-stream"
|
|
148
|
+
});
|
|
149
|
+
return form;
|
|
150
|
+
},
|
|
151
|
+
parseResponse: res => {
|
|
152
|
+
var _a, _b, _c;
|
|
153
|
+
if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
|
|
154
|
+
throw new Error("Failed to get URL from qu.ax");
|
|
155
|
+
return res.data.files[0].url;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "uguu.se",
|
|
160
|
+
url: "https://uguu.se/upload.php",
|
|
161
|
+
buildForm: () => {
|
|
162
|
+
const form = new form_data_1.default();
|
|
163
|
+
form.append("files[]", buffer, {
|
|
164
|
+
filename: `file.${ext}`,
|
|
165
|
+
contentType: mime || "application/octet-stream"
|
|
166
|
+
});
|
|
167
|
+
return form;
|
|
168
|
+
},
|
|
169
|
+
parseResponse: res => {
|
|
170
|
+
var _a, _b, _c;
|
|
171
|
+
if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
|
|
172
|
+
throw new Error("Failed to get URL from uguu.se");
|
|
173
|
+
return res.data.files[0].url;
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: "tmpfiles",
|
|
178
|
+
url: "https://tmpfiles.org/api/v1/upload",
|
|
179
|
+
buildForm: () => {
|
|
180
|
+
const form = new form_data_1.default();
|
|
181
|
+
form.append("file", buffer, {
|
|
182
|
+
filename: `file.${ext}`,
|
|
183
|
+
contentType: mime
|
|
184
|
+
});
|
|
185
|
+
return form;
|
|
186
|
+
},
|
|
187
|
+
parseResponse: res => {
|
|
188
|
+
const match = res.data.data.url.match(/https:\/\/tmpfiles\.org\/(.*)/);
|
|
189
|
+
if (!match)
|
|
190
|
+
throw new Error("Failed to parse tmpfiles URL.");
|
|
191
|
+
return `https://tmpfiles.org/dl/${match[1]}`;
|
|
192
|
+
}
|
|
91
193
|
}
|
|
92
|
-
|
|
93
|
-
|
|
194
|
+
];
|
|
195
|
+
for (const service of services) {
|
|
196
|
+
try {
|
|
197
|
+
const form = service.buildForm();
|
|
198
|
+
const res = await axios_1.default.post(service.url, form, {
|
|
199
|
+
headers: form.getHeaders()
|
|
200
|
+
});
|
|
201
|
+
const url = service.parseResponse(res);
|
|
202
|
+
return url;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
logger === null || logger === void 0 ? void 0 : logger.debug(`[${service.name}] eror:`, (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
throw new Error("All upload services failed.");
|
|
209
|
+
}
|
|
210
|
+
async function vid2jpg(videoUrl) {
|
|
211
|
+
try {
|
|
212
|
+
const { data } = await axios_1.default.get(`https://ezgif.com/video-to-jpg?url=${encodeURIComponent(videoUrl)}`);
|
|
213
|
+
const $ = cheerio.load(data);
|
|
214
|
+
const fileToken = $('input[name="file"]').attr("value");
|
|
215
|
+
if (!fileToken) {
|
|
216
|
+
throw new Error("Failed to retrieve file token. The video URL may be invalid or inaccessible.");
|
|
217
|
+
}
|
|
218
|
+
const formData = new URLSearchParams();
|
|
219
|
+
formData.append("file", fileToken);
|
|
220
|
+
formData.append("end", "1");
|
|
221
|
+
formData.append("video-to-jpg", "Convert to JPG!");
|
|
222
|
+
const convert = await axios_1.default.post(`https://ezgif.com/video-to-jpg/${fileToken}`, formData);
|
|
223
|
+
const $2 = cheerio.load(convert.data);
|
|
224
|
+
let imageUrl = $2("#output img").first().attr("src");
|
|
225
|
+
if (!imageUrl) {
|
|
226
|
+
throw new Error("Could not locate the converted image output.");
|
|
227
|
+
}
|
|
228
|
+
if (imageUrl.startsWith("//")) {
|
|
229
|
+
imageUrl = "https:" + imageUrl;
|
|
94
230
|
}
|
|
231
|
+
else if (imageUrl.startsWith("/")) {
|
|
232
|
+
const cdnMatch = imageUrl.match(/\/(s\d+\..+?)\/.*/);
|
|
233
|
+
if (cdnMatch) {
|
|
234
|
+
imageUrl = "https://" + imageUrl.slice(2);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
imageUrl = "https://ezgif.com" + imageUrl;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return imageUrl;
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
throw new Error("Failed to convert video to JPG: " + error.message);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Extracts video thumbnail using FFmpeg
|
|
248
|
+
*/
|
|
249
|
+
const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 256 }) => {
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
const args = [
|
|
252
|
+
'-ss', time,
|
|
253
|
+
'-i', videoPath,
|
|
254
|
+
'-y',
|
|
255
|
+
'-vf', `scale=${size.width}:-1`,
|
|
256
|
+
'-vframes', '1',
|
|
257
|
+
'-f', 'image2',
|
|
258
|
+
'-vcodec', 'mjpeg',
|
|
259
|
+
'pipe:1'
|
|
260
|
+
];
|
|
261
|
+
const ffmpeg = (0, child_process_1.spawn)('ffmpeg', args);
|
|
262
|
+
const chunks = [];
|
|
263
|
+
let errorOutput = '';
|
|
264
|
+
ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
|
|
265
|
+
ffmpeg.stderr.on('data', data => {
|
|
266
|
+
errorOutput += data.toString();
|
|
267
|
+
});
|
|
268
|
+
ffmpeg.on('error', reject);
|
|
269
|
+
ffmpeg.on('close', code => {
|
|
270
|
+
if (code === 0) return resolve(Buffer.concat(chunks));
|
|
271
|
+
reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`));
|
|
272
|
+
});
|
|
95
273
|
});
|
|
96
|
-
}
|
|
274
|
+
};
|
|
275
|
+
exports.extractVideoThumb = extractVideoThumb;
|
|
97
276
|
const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
98
277
|
var _a, _b;
|
|
99
278
|
if (bufferOrFilePath instanceof stream_1.Readable) {
|
|
@@ -142,8 +321,8 @@ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
|
|
|
142
321
|
.replace(/\=+$/, '')));
|
|
143
322
|
exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
|
|
144
323
|
const generateProfilePicture = async (mediaUpload) => {
|
|
145
|
-
var _a, _b;
|
|
146
324
|
let bufferOrFilePath;
|
|
325
|
+
let img;
|
|
147
326
|
if (Buffer.isBuffer(mediaUpload)) {
|
|
148
327
|
bufferOrFilePath = mediaUpload;
|
|
149
328
|
}
|
|
@@ -153,29 +332,11 @@ const generateProfilePicture = async (mediaUpload) => {
|
|
|
153
332
|
else {
|
|
154
333
|
bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
|
|
155
334
|
}
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
.jpeg({
|
|
162
|
-
quality: 50,
|
|
163
|
-
})
|
|
164
|
-
.toBuffer();
|
|
165
|
-
}
|
|
166
|
-
else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
|
|
167
|
-
const { read, MIME_JPEG, RESIZE_BILINEAR } = lib.jimp;
|
|
168
|
-
const jimp = await read(bufferOrFilePath);
|
|
169
|
-
const min = Math.min(jimp.getWidth(), jimp.getHeight());
|
|
170
|
-
const cropped = jimp.crop(0, 0, min, min);
|
|
171
|
-
img = cropped
|
|
172
|
-
.quality(50)
|
|
173
|
-
.resize(640, 640, RESIZE_BILINEAR)
|
|
174
|
-
.getBufferAsync(MIME_JPEG);
|
|
175
|
-
}
|
|
176
|
-
else {
|
|
177
|
-
throw new boom_1.Boom('No image processing library available');
|
|
178
|
-
}
|
|
335
|
+
const jimp = await jimp_1.default.read(bufferOrFilePath);
|
|
336
|
+
const cropped = jimp.getWidth() > jimp.getHeight() ? jimp.resize(550, -1) : jimp.resize(-1, 650);
|
|
337
|
+
img = cropped
|
|
338
|
+
.quality(100)
|
|
339
|
+
.getBufferAsync(jimp_1.default.MIME_JPEG);
|
|
179
340
|
return {
|
|
180
341
|
img: await img,
|
|
181
342
|
};
|
|
@@ -365,12 +526,28 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
365
526
|
}
|
|
366
527
|
}
|
|
367
528
|
else if (mediaType === 'video') {
|
|
368
|
-
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
|
|
369
529
|
try {
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
530
|
+
let videoPath = file;
|
|
531
|
+
if (Buffer.isBuffer(file) || file instanceof stream_1.Readable) {
|
|
532
|
+
videoPath = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.mp4');
|
|
533
|
+
const buffer = Buffer.isBuffer(file) ? file : await (0, exports.toBuffer)(file);
|
|
534
|
+
await fs_1.promises.writeFile(videoPath, buffer);
|
|
535
|
+
}
|
|
536
|
+
const thumbnailBuffer = await (0, exports.extractVideoThumb)(videoPath);
|
|
537
|
+
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
|
|
538
|
+
await fs_1.promises.writeFile(imgFilename, thumbnailBuffer);
|
|
539
|
+
const { buffer: processedThumbnailBuffer, original } = await (0, exports.extractImageThumb)(imgFilename);
|
|
540
|
+
thumbnail = processedThumbnailBuffer.toString('base64');
|
|
541
|
+
if (original.width && original.height) {
|
|
542
|
+
originalImageDimensions = {
|
|
543
|
+
width: original.width,
|
|
544
|
+
height: original.height,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
373
547
|
await fs_1.promises.unlink(imgFilename);
|
|
548
|
+
if (videoPath !== file) {
|
|
549
|
+
await fs_1.promises.unlink(videoPath);
|
|
550
|
+
}
|
|
374
551
|
}
|
|
375
552
|
catch (err) {
|
|
376
553
|
(_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
|
|
@@ -381,10 +558,8 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
381
558
|
originalImageDimensions
|
|
382
559
|
};
|
|
383
560
|
}
|
|
384
|
-
exports.generateThumbnail = generateThumbnail;
|
|
385
561
|
const getHttpStream = async (url, options = {}) => {
|
|
386
|
-
const
|
|
387
|
-
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
|
|
562
|
+
const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' });
|
|
388
563
|
return fetched.data;
|
|
389
564
|
};
|
|
390
565
|
exports.getHttpStream = getHttpStream;
|
|
@@ -399,7 +574,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
399
574
|
bodyPath = media.url;
|
|
400
575
|
}
|
|
401
576
|
else if (saveOriginalFileIfRequired) {
|
|
402
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.
|
|
577
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
403
578
|
(0, fs_1.writeFileSync)(bodyPath, buffer);
|
|
404
579
|
didSaveToTmpPath = true;
|
|
405
580
|
}
|
|
@@ -418,7 +593,6 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
418
593
|
};
|
|
419
594
|
}
|
|
420
595
|
catch (error) {
|
|
421
|
-
// destroy all streams with error
|
|
422
596
|
stream.destroy();
|
|
423
597
|
if (didSaveToTmpPath) {
|
|
424
598
|
try {
|
|
@@ -434,52 +608,53 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
434
608
|
exports.prepareStream = prepareStream;
|
|
435
609
|
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
|
|
436
610
|
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
437
|
-
|
|
438
|
-
|
|
611
|
+
logger?.debug('fetched media stream');
|
|
612
|
+
|
|
613
|
+
let finalStream = stream;
|
|
614
|
+
|
|
615
|
+
// Tambahan untuk konversi audio jadi Opus (PTT)
|
|
439
616
|
if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
|
|
440
617
|
try {
|
|
441
618
|
const buffer = await (0, exports.toBuffer)(stream);
|
|
442
619
|
const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
|
|
443
620
|
finalStream = (0, exports.toReadable)(opusBuffer);
|
|
621
|
+
logger?.debug('converted audio to Opus');
|
|
444
622
|
} catch (error) {
|
|
623
|
+
logger?.error('failed to convert audio to Opus, fallback to original stream');
|
|
445
624
|
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
446
625
|
finalStream = newStream;
|
|
447
626
|
}
|
|
448
627
|
}
|
|
449
628
|
|
|
450
629
|
const mediaKey = Crypto.randomBytes(32);
|
|
451
|
-
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
|
|
630
|
+
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
|
|
452
631
|
const encWriteStream = new stream_1.Readable({ read: () => { } });
|
|
453
632
|
let bodyPath;
|
|
454
633
|
let writeStream;
|
|
455
634
|
let didSaveToTmpPath = false;
|
|
456
|
-
|
|
635
|
+
|
|
457
636
|
if (type === 'file') {
|
|
458
637
|
bodyPath = media.url;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
638
|
+
} else if (saveOriginalFileIfRequired) {
|
|
639
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
462
640
|
writeStream = (0, fs_1.createWriteStream)(bodyPath);
|
|
463
641
|
didSaveToTmpPath = true;
|
|
464
642
|
}
|
|
465
|
-
|
|
643
|
+
|
|
466
644
|
let fileLength = 0;
|
|
467
645
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
468
646
|
let hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
469
647
|
let sha256Plain = Crypto.createHash('sha256');
|
|
470
648
|
let sha256Enc = Crypto.createHash('sha256');
|
|
471
|
-
|
|
649
|
+
|
|
472
650
|
try {
|
|
473
651
|
for await (const data of finalStream) {
|
|
474
652
|
fileLength += data.length;
|
|
475
|
-
if (type === 'remote'
|
|
476
|
-
&& (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
|
|
477
|
-
&& fileLength + data.length > opts.maxContentLength) {
|
|
653
|
+
if (type === 'remote' && (opts?.maxContentLength) && fileLength + data.length > opts.maxContentLength) {
|
|
478
654
|
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
|
|
479
655
|
data: { media, type }
|
|
480
656
|
});
|
|
481
657
|
}
|
|
482
|
-
|
|
483
658
|
sha256Plain = sha256Plain.update(data);
|
|
484
659
|
if (writeStream) {
|
|
485
660
|
if (!writeStream.write(data)) {
|
|
@@ -488,18 +663,20 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
488
663
|
}
|
|
489
664
|
onChunk(aes.update(data));
|
|
490
665
|
}
|
|
491
|
-
|
|
666
|
+
|
|
492
667
|
onChunk(aes.final());
|
|
493
668
|
const mac = hmac.digest().slice(0, 10);
|
|
494
669
|
sha256Enc = sha256Enc.update(mac);
|
|
495
670
|
const fileSha256 = sha256Plain.digest();
|
|
496
671
|
const fileEncSha256 = sha256Enc.digest();
|
|
497
|
-
|
|
672
|
+
|
|
498
673
|
encWriteStream.push(mac);
|
|
499
674
|
encWriteStream.push(null);
|
|
500
|
-
writeStream
|
|
675
|
+
writeStream?.end();
|
|
501
676
|
finalStream.destroy();
|
|
502
|
-
|
|
677
|
+
|
|
678
|
+
logger?.debug('encrypted data successfully');
|
|
679
|
+
|
|
503
680
|
return {
|
|
504
681
|
mediaKey,
|
|
505
682
|
encWriteStream,
|
|
@@ -510,26 +687,25 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
510
687
|
fileLength,
|
|
511
688
|
didSaveToTmpPath
|
|
512
689
|
};
|
|
513
|
-
}
|
|
514
|
-
catch (error) {
|
|
690
|
+
} catch (error) {
|
|
515
691
|
encWriteStream.destroy();
|
|
516
|
-
writeStream
|
|
692
|
+
writeStream?.destroy();
|
|
517
693
|
aes.destroy();
|
|
518
694
|
hmac.destroy();
|
|
519
695
|
sha256Plain.destroy();
|
|
520
696
|
sha256Enc.destroy();
|
|
521
697
|
finalStream.destroy();
|
|
522
|
-
|
|
698
|
+
|
|
523
699
|
if (didSaveToTmpPath) {
|
|
524
700
|
try {
|
|
525
701
|
await fs_1.promises.unlink(bodyPath);
|
|
526
|
-
}
|
|
527
|
-
|
|
702
|
+
} catch (err) {
|
|
703
|
+
logger?.error({ err }, 'failed to save to tmp path');
|
|
528
704
|
}
|
|
529
705
|
}
|
|
530
706
|
throw error;
|
|
531
707
|
}
|
|
532
|
-
|
|
708
|
+
|
|
533
709
|
function onChunk(buff) {
|
|
534
710
|
sha256Enc = sha256Enc.update(buff);
|
|
535
711
|
hmac = hmac.update(buff);
|
|
@@ -544,21 +720,20 @@ const toSmallestChunkSize = (num) => {
|
|
|
544
720
|
};
|
|
545
721
|
const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
546
722
|
exports.getUrlFromDirectPath = getUrlFromDirectPath;
|
|
547
|
-
const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
548
|
-
const
|
|
549
|
-
const
|
|
723
|
+
const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
724
|
+
const isValidMediaUrl = url === null || url === void 0 ? void 0 : url.startsWith('https://mmg.whatsapp.net/');
|
|
725
|
+
const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath);
|
|
726
|
+
if (!downloadUrl) {
|
|
727
|
+
throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 });
|
|
728
|
+
}
|
|
729
|
+
const keys = await getMediaKeys(mediaKey, type);
|
|
550
730
|
return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
|
|
551
731
|
};
|
|
552
732
|
exports.downloadContentFromMessage = downloadContentFromMessage;
|
|
553
|
-
/**
|
|
554
|
-
* Decrypts and downloads an AES256-CBC encrypted file given the keys.
|
|
555
|
-
* Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
|
|
556
|
-
* */
|
|
557
733
|
const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
|
|
558
734
|
let bytesFetched = 0;
|
|
559
735
|
let startChunk = 0;
|
|
560
736
|
let firstBlockIsIV = false;
|
|
561
|
-
// if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
|
|
562
737
|
if (startByte) {
|
|
563
738
|
const chunk = toSmallestChunkSize(startByte || 0);
|
|
564
739
|
if (chunk) {
|
|
@@ -578,7 +753,6 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
578
753
|
headers.Range += endChunk;
|
|
579
754
|
}
|
|
580
755
|
}
|
|
581
|
-
// download the message
|
|
582
756
|
const fetched = await (0, exports.getHttpStream)(downloadUrl, {
|
|
583
757
|
...options || {},
|
|
584
758
|
headers,
|
|
@@ -611,8 +785,6 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
611
785
|
data = data.slice(AES_CHUNK_SIZE);
|
|
612
786
|
}
|
|
613
787
|
aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
|
|
614
|
-
// if an end byte that is not EOF is specified
|
|
615
|
-
// stop auto padding (PKCS7) -- otherwise throws an error for decryption
|
|
616
788
|
if (endByte) {
|
|
617
789
|
aes.setAutoPadding(false);
|
|
618
790
|
}
|
|
@@ -653,12 +825,9 @@ function extensionForMediaMessage(message) {
|
|
|
653
825
|
}
|
|
654
826
|
return extension;
|
|
655
827
|
}
|
|
656
|
-
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
657
828
|
const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
658
829
|
return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
|
|
659
830
|
var _a, _b;
|
|
660
|
-
const { default: axios } = await import('axios');
|
|
661
|
-
// send a query JSON to obtain the url & auth token to upload our media
|
|
662
831
|
let uploadInfo = await refreshMediaConn(false);
|
|
663
832
|
let urls;
|
|
664
833
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
@@ -676,14 +845,14 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
676
845
|
}
|
|
677
846
|
for (const { hostname, maxContentLengthBytes } of hosts) {
|
|
678
847
|
logger.debug(`uploading to "${hostname}"`);
|
|
679
|
-
const auth = encodeURIComponent(uploadInfo.auth);
|
|
848
|
+
const auth = encodeURIComponent(uploadInfo.auth);
|
|
680
849
|
const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
681
850
|
let result;
|
|
682
851
|
try {
|
|
683
852
|
if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
|
|
684
853
|
throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
|
|
685
854
|
}
|
|
686
|
-
const body = await
|
|
855
|
+
const body = await axios_1.default.post(url, reqBody, {
|
|
687
856
|
...options,
|
|
688
857
|
headers: {
|
|
689
858
|
...options.headers || {},
|
|
@@ -711,7 +880,7 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
711
880
|
}
|
|
712
881
|
}
|
|
713
882
|
catch (error) {
|
|
714
|
-
if (
|
|
883
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
715
884
|
result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
716
885
|
}
|
|
717
886
|
const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
|
|
@@ -728,14 +897,11 @@ exports.getWAUploadToServer = getWAUploadToServer;
|
|
|
728
897
|
const getMediaRetryKey = (mediaKey) => {
|
|
729
898
|
return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
|
|
730
899
|
};
|
|
731
|
-
|
|
732
|
-
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
|
733
|
-
*/
|
|
734
|
-
const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
900
|
+
const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
735
901
|
const recp = { stanzaId: key.id };
|
|
736
902
|
const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
|
|
737
903
|
const iv = Crypto.randomBytes(12);
|
|
738
|
-
const retryKey = getMediaRetryKey(mediaKey);
|
|
904
|
+
const retryKey = await getMediaRetryKey(mediaKey);
|
|
739
905
|
const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
740
906
|
const req = {
|
|
741
907
|
tag: 'receipt',
|
|
@@ -745,9 +911,6 @@ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
|
745
911
|
type: 'server-error'
|
|
746
912
|
},
|
|
747
913
|
content: [
|
|
748
|
-
// this encrypt node is actually pretty useless
|
|
749
|
-
// the media is returned even without this node
|
|
750
|
-
// keeping it here to maintain parity with WA Web
|
|
751
914
|
{
|
|
752
915
|
tag: 'encrypt',
|
|
753
916
|
attrs: {},
|
|
@@ -761,7 +924,6 @@ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
|
761
924
|
attrs: {
|
|
762
925
|
jid: key.remoteJid,
|
|
763
926
|
'from_me': (!!key.fromMe).toString(),
|
|
764
|
-
// @ts-ignore
|
|
765
927
|
participant: key.participant || undefined
|
|
766
928
|
}
|
|
767
929
|
}
|
|
@@ -799,8 +961,8 @@ const decodeMediaRetryNode = (node) => {
|
|
|
799
961
|
return event;
|
|
800
962
|
};
|
|
801
963
|
exports.decodeMediaRetryNode = decodeMediaRetryNode;
|
|
802
|
-
const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
803
|
-
const retryKey = getMediaRetryKey(mediaKey);
|
|
964
|
+
const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
965
|
+
const retryKey = await getMediaRetryKey(mediaKey);
|
|
804
966
|
const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
|
|
805
967
|
return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
|
|
806
968
|
};
|
|
@@ -813,7 +975,6 @@ const MEDIA_RETRY_STATUS_MAP = {
|
|
|
813
975
|
[WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
814
976
|
[WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
|
|
815
977
|
};
|
|
816
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
817
978
|
function __importStar(arg0) {
|
|
818
979
|
throw new Error('Function not implemented.');
|
|
819
|
-
}
|
|
980
|
+
}
|