cuki-bailx 1.2.5 → 2.0.7
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/WAProto/AICommon.js +27981 -0
- package/WAProto/AICommon.proto +713 -0
- package/WAProto/Ephemeral.js +295 -0
- package/WAProto/Ephemeral.proto +7 -0
- package/WAProto/GenerateStatics.sh +4 -0
- package/WAProto/WAProto.proto +4775 -0
- package/WAProto/cuki.js +7 -0
- package/WAProto/index.js +56886 -17506
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/index.js +117 -102
- package/lib/Socket/Client/index.js +2 -3
- package/lib/Socket/Client/{web-socket-client.js → websocket.js} +54 -5
- package/lib/Socket/chats.js +97 -90
- package/lib/Socket/groups.js +20 -5
- package/lib/Socket/index.js +2 -2
- package/lib/Socket/messages-recv.js +9 -65
- package/lib/Socket/messages-send.js +612 -286
- package/lib/Socket/newsletter.js +68 -41
- package/lib/Socket/{dugong.js → setup.js} +5 -13
- package/lib/Socket/socket.js +58 -32
- package/lib/Store/index.js +1 -3
- package/lib/Store/make-in-memory-store.js +26 -14
- package/lib/Store/make-ordered-dictionary.js +2 -2
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Message.js +0 -2
- package/lib/Types/Newsletter.js +3 -17
- package/lib/Types/index.js +2 -2
- package/lib/Utils/auth-utils.js +6 -13
- package/lib/Utils/business.js +2 -2
- package/lib/Utils/chat-utils.js +36 -35
- package/lib/Utils/crypto.js +71 -29
- package/lib/Utils/decode-wa-message.js +65 -56
- package/lib/Utils/event-buffer.js +11 -7
- package/lib/Utils/generics.js +73 -23
- package/lib/Utils/history.js +4 -6
- package/lib/Utils/link-preview.js +34 -1
- package/lib/Utils/lt-hash.js +6 -6
- package/lib/Utils/messages-media.js +479 -161
- package/lib/Utils/messages.js +391 -77
- package/lib/Utils/noise-handler.js +19 -23
- package/lib/Utils/signal.js +47 -36
- package/lib/Utils/use-multi-file-auth-state.js +51 -6
- package/lib/Utils/validate-connection.js +94 -66
- package/lib/WABinary/constants.js +1276 -13
- package/lib/WABinary/decode.js +26 -13
- package/lib/WABinary/encode.js +39 -17
- package/lib/WABinary/generic-utils.js +2 -85
- package/lib/WABinary/jid-utils.js +12 -5
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -1
- package/lib/index.js +18 -5
- package/package.json +100 -105
- package/engine-requirements.js +0 -10
- package/lib/Defaults/index.d.ts +0 -53
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -9
- package/lib/Signal/Group/group-session-builder.d.ts +0 -14
- package/lib/Signal/Group/group_cipher.d.ts +0 -17
- package/lib/Signal/Group/index.d.ts +0 -11
- package/lib/Signal/Group/keyhelper.d.ts +0 -10
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/sender-chain-key.d.ts +0 -13
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -16
- package/lib/Signal/Group/sender-key-message.d.ts +0 -18
- package/lib/Signal/Group/sender-key-name.d.ts +0 -17
- package/lib/Signal/Group/sender-key-record.d.ts +0 -30
- package/lib/Signal/Group/sender-key-state.d.ts +0 -38
- package/lib/Signal/Group/sender-message-key.d.ts +0 -11
- package/lib/Signal/libsignal.d.ts +0 -3
- package/lib/Socket/Client/abstract-socket-client.d.ts +0 -17
- package/lib/Socket/Client/index.d.ts +0 -3
- 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/business.d.ts +0 -171
- package/lib/Socket/chats.d.ts +0 -80
- package/lib/Socket/dugong.d.ts +0 -219
- package/lib/Socket/groups.d.ts +0 -115
- package/lib/Socket/index.d.ts +0 -173
- package/lib/Socket/messages-recv.d.ts +0 -161
- package/lib/Socket/messages-send.d.ts +0 -149
- package/lib/Socket/newsletter.d.ts +0 -134
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/socket.d.ts +0 -43
- package/lib/Socket/socket.js.bak +0 -630
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Store/index.d.ts +0 -3
- package/lib/Store/make-cache-manager-store.d.ts +0 -13
- package/lib/Store/make-cache-manager-store.js +0 -83
- package/lib/Store/make-in-memory-store.d.ts +0 -118
- package/lib/Store/make-ordered-dictionary.d.ts +0 -13
- package/lib/Store/object-repository.d.ts +0 -10
- package/lib/Types/Auth.d.ts +0 -110
- package/lib/Types/Call.d.ts +0 -13
- package/lib/Types/Chat.d.ts +0 -102
- package/lib/Types/Contact.d.ts +0 -19
- package/lib/Types/Events.d.ts +0 -157
- package/lib/Types/GroupMetadata.d.ts +0 -55
- package/lib/Types/Label.d.ts +0 -35
- package/lib/Types/LabelAssociation.d.ts +0 -29
- package/lib/Types/Message.d.ts +0 -273
- package/lib/Types/Newsletter.d.ts +0 -92
- package/lib/Types/Product.d.ts +0 -78
- package/lib/Types/Signal.d.ts +0 -57
- package/lib/Types/Socket.d.ts +0 -111
- package/lib/Types/State.d.ts +0 -27
- package/lib/Types/USync.d.ts +0 -25
- package/lib/Types/index.d.ts +0 -57
- package/lib/Utils/auth-utils.d.ts +0 -18
- package/lib/Utils/baileys-event-stream.d.ts +0 -16
- package/lib/Utils/business.d.ts +0 -22
- package/lib/Utils/chat-utils.d.ts +0 -71
- package/lib/Utils/crypto.d.ts +0 -41
- package/lib/Utils/decode-wa-message.d.ts +0 -19
- package/lib/Utils/event-buffer.d.ts +0 -35
- package/lib/Utils/generics.d.ts +0 -92
- package/lib/Utils/history.d.ts +0 -15
- package/lib/Utils/index.d.ts +0 -17
- package/lib/Utils/link-preview.d.ts +0 -21
- package/lib/Utils/logger.d.ts +0 -4
- package/lib/Utils/lt-hash.d.ts +0 -12
- package/lib/Utils/make-mutex.d.ts +0 -7
- package/lib/Utils/messages-media.d.ts +0 -116
- package/lib/Utils/messages.d.ts +0 -77
- package/lib/Utils/noise-handler.d.ts +0 -21
- package/lib/Utils/process-message.d.ts +0 -41
- package/lib/Utils/signal.d.ts +0 -32
- package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
- package/lib/Utils/validate-connection.d.ts +0 -11
- package/lib/WABinary/constants.d.ts +0 -27
- package/lib/WABinary/decode.d.ts +0 -7
- package/lib/WABinary/encode.d.ts +0 -3
- package/lib/WABinary/generic-utils.d.ts +0 -16
- package/lib/WABinary/index.d.ts +0 -5
- package/lib/WABinary/jid-utils.d.ts +0 -31
- package/lib/WABinary/types.d.ts +0 -18
- package/lib/WAM/BinaryInfo.d.ts +0 -17
- package/lib/WAM/constants.d.ts +0 -38
- package/lib/WAM/encode.d.ts +0 -3
- package/lib/WAM/index.d.ts +0 -3
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -9
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -22
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -12
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -25
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -8
- package/lib/WAUSync/Protocols/index.d.ts +0 -4
- package/lib/WAUSync/USyncQuery.d.ts +0 -28
- package/lib/WAUSync/USyncUser.d.ts +0 -12
- package/lib/WAUSync/index.d.ts +0 -3
- package/lib/index.d.ts +0 -12
- package/lib/index.js.bak +0 -48
- /package/lib/Socket/Client/{abstract-socket-client.js → types.js} +0 -0
|
@@ -15,23 +15,67 @@ 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 =
|
|
39
|
+
exports.getStatusCodeForMediaRetry =
|
|
40
|
+
exports.decryptMediaRetryData =
|
|
41
|
+
exports.decodeMediaRetryNode =
|
|
42
|
+
exports.encryptMediaRetryRequest =
|
|
43
|
+
exports.getWAUploadToServer =
|
|
44
|
+
exports.downloadEncryptedContent =
|
|
45
|
+
exports.downloadContentFromMessage =
|
|
46
|
+
exports.getUrlFromDirectPath =
|
|
47
|
+
exports.encryptedStream =
|
|
48
|
+
exports.prepareStream =
|
|
49
|
+
exports.getHttpStream =
|
|
50
|
+
exports.getStream =
|
|
51
|
+
exports.toBuffer =
|
|
52
|
+
exports.toReadable =
|
|
53
|
+
exports.mediaMessageSHA256B64 =
|
|
54
|
+
exports.generateProfilePicture =
|
|
55
|
+
exports.encodeBase64EncodedStringForUpload =
|
|
56
|
+
exports.extractImageThumb =
|
|
57
|
+
exports.extractVideoThumb =
|
|
58
|
+
exports.hkdfInfoKey = void 0;
|
|
59
|
+
exports.getMediaKeys = getMediaKeys;
|
|
60
|
+
exports.uploadFile = uploadFile;
|
|
61
|
+
exports.vid2jpg = vid2jpg;
|
|
62
|
+
exports.getAudioDuration = getAudioDuration;
|
|
63
|
+
exports.getAudioWaveform = getAudioWaveform;
|
|
64
|
+
exports.generateThumbnail = generateThumbnail;
|
|
65
|
+
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
66
|
+
// Kazumari Baileys
|
|
27
67
|
const boom_1 = require("@hapi/boom");
|
|
28
|
-
const
|
|
68
|
+
const axios_1 = __importDefault(require("axios"));
|
|
69
|
+
const form_data_1 = __importDefault(require("form-data"));
|
|
70
|
+
const cheerio = __importStar(require("cheerio"));
|
|
29
71
|
const Crypto = __importStar(require("crypto"));
|
|
30
72
|
const events_1 = require("events");
|
|
31
73
|
const fs_1 = require("fs");
|
|
32
74
|
const os_1 = require("os");
|
|
33
75
|
const path_1 = require("path");
|
|
76
|
+
const jimp_1 = __importDefault(require("jimp"));
|
|
34
77
|
const stream_1 = require("stream");
|
|
78
|
+
const child_process_1 = require("child_process");
|
|
35
79
|
const WAProto_1 = require("../../WAProto");
|
|
36
80
|
const Defaults_1 = require("../Defaults");
|
|
37
81
|
const WABinary_1 = require("../WABinary");
|
|
@@ -41,13 +85,11 @@ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
|
|
|
41
85
|
const getImageProcessingLibrary = async () => {
|
|
42
86
|
const [_jimp, sharp] = await Promise.all([
|
|
43
87
|
(async () => {
|
|
44
|
-
const jimp = await (
|
|
45
|
-
.catch(() => { }));
|
|
88
|
+
const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }));
|
|
46
89
|
return jimp;
|
|
47
90
|
})(),
|
|
48
91
|
(async () => {
|
|
49
|
-
const sharp = await (
|
|
50
|
-
.catch(() => { }));
|
|
92
|
+
const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }));
|
|
51
93
|
return sharp;
|
|
52
94
|
})()
|
|
53
95
|
]);
|
|
@@ -66,7 +108,7 @@ const hkdfInfoKey = (type) => {
|
|
|
66
108
|
};
|
|
67
109
|
exports.hkdfInfoKey = hkdfInfoKey;
|
|
68
110
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
69
|
-
function getMediaKeys(buffer, mediaType) {
|
|
111
|
+
async function getMediaKeys(buffer, mediaType) {
|
|
70
112
|
if (!buffer) {
|
|
71
113
|
throw new boom_1.Boom('Cannot derive from empty media key');
|
|
72
114
|
}
|
|
@@ -74,26 +116,183 @@ function getMediaKeys(buffer, mediaType) {
|
|
|
74
116
|
buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
|
|
75
117
|
}
|
|
76
118
|
// 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) });
|
|
119
|
+
const expandedMediaKey = await (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
78
120
|
return {
|
|
79
121
|
iv: expandedMediaKey.slice(0, 16),
|
|
80
122
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
81
123
|
macKey: expandedMediaKey.slice(48, 80),
|
|
82
124
|
};
|
|
83
125
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
126
|
+
async function uploadFile(buffer, logger) {
|
|
127
|
+
const { fromBuffer } = await Promise.resolve().then(() => __importStar(require('file-type')));
|
|
128
|
+
const fileType = await fromBuffer(buffer);
|
|
129
|
+
if (!fileType)
|
|
130
|
+
throw new Error("Failed to detect file type.");
|
|
131
|
+
const { ext, mime } = fileType;
|
|
132
|
+
const services = [
|
|
133
|
+
{
|
|
134
|
+
name: "catbox",
|
|
135
|
+
url: "https://catbox.moe/user/api.php",
|
|
136
|
+
buildForm: () => {
|
|
137
|
+
const form = new form_data_1.default();
|
|
138
|
+
form.append("fileToUpload", buffer, {
|
|
139
|
+
filename: `file.${ext}`,
|
|
140
|
+
contentType: mime || "application/octet-stream"
|
|
141
|
+
});
|
|
142
|
+
form.append("reqtype", "fileupload");
|
|
143
|
+
return form;
|
|
144
|
+
},
|
|
145
|
+
parseResponse: res => res.data
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "pdi.moe",
|
|
149
|
+
url: "https://scdn.pdi.moe/upload",
|
|
150
|
+
buildForm: () => {
|
|
151
|
+
const form = new form_data_1.default();
|
|
152
|
+
form.append("file", buffer, {
|
|
153
|
+
filename: `file.${ext}`,
|
|
154
|
+
contentType: mime
|
|
155
|
+
});
|
|
156
|
+
return form;
|
|
157
|
+
},
|
|
158
|
+
parseResponse: res => res.data.result.url
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "qu.ax",
|
|
162
|
+
url: "https://qu.ax/upload.php",
|
|
163
|
+
buildForm: () => {
|
|
164
|
+
const form = new form_data_1.default();
|
|
165
|
+
form.append("files[]", buffer, {
|
|
166
|
+
filename: `file.${ext}`,
|
|
167
|
+
contentType: mime || "application/octet-stream"
|
|
168
|
+
});
|
|
169
|
+
return form;
|
|
170
|
+
},
|
|
171
|
+
parseResponse: res => {
|
|
172
|
+
var _a, _b, _c;
|
|
173
|
+
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))
|
|
174
|
+
throw new Error("Failed to get URL from qu.ax");
|
|
175
|
+
return res.data.files[0].url;
|
|
176
|
+
}
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: "uguu.se",
|
|
180
|
+
url: "https://uguu.se/upload.php",
|
|
181
|
+
buildForm: () => {
|
|
182
|
+
const form = new form_data_1.default();
|
|
183
|
+
form.append("files[]", buffer, {
|
|
184
|
+
filename: `file.${ext}`,
|
|
185
|
+
contentType: mime || "application/octet-stream"
|
|
186
|
+
});
|
|
187
|
+
return form;
|
|
188
|
+
},
|
|
189
|
+
parseResponse: res => {
|
|
190
|
+
var _a, _b, _c;
|
|
191
|
+
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))
|
|
192
|
+
throw new Error("Failed to get URL from uguu.se");
|
|
193
|
+
return res.data.files[0].url;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "tmpfiles",
|
|
198
|
+
url: "https://tmpfiles.org/api/v1/upload",
|
|
199
|
+
buildForm: () => {
|
|
200
|
+
const form = new form_data_1.default();
|
|
201
|
+
form.append("file", buffer, {
|
|
202
|
+
filename: `file.${ext}`,
|
|
203
|
+
contentType: mime
|
|
204
|
+
});
|
|
205
|
+
return form;
|
|
206
|
+
},
|
|
207
|
+
parseResponse: res => {
|
|
208
|
+
const match = res.data.data.url.match(/https:\/\/tmpfiles\.org\/(.*)/);
|
|
209
|
+
if (!match)
|
|
210
|
+
throw new Error("Failed to parse tmpfiles URL.");
|
|
211
|
+
return `https://tmpfiles.org/dl/${match[1]}`;
|
|
212
|
+
}
|
|
91
213
|
}
|
|
92
|
-
|
|
93
|
-
|
|
214
|
+
];
|
|
215
|
+
for (const service of services) {
|
|
216
|
+
try {
|
|
217
|
+
const form = service.buildForm();
|
|
218
|
+
const res = await axios_1.default.post(service.url, form, {
|
|
219
|
+
headers: form.getHeaders()
|
|
220
|
+
});
|
|
221
|
+
const url = service.parseResponse(res);
|
|
222
|
+
return url;
|
|
94
223
|
}
|
|
224
|
+
catch (error) {
|
|
225
|
+
logger === null || logger === void 0 ? void 0 : logger.debug(`[${service.name}] eror:`, (error === null || error === void 0 ? void 0 : error.message) || error);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
throw new Error("All upload services failed.");
|
|
229
|
+
}
|
|
230
|
+
async function vid2jpg(videoUrl) {
|
|
231
|
+
try {
|
|
232
|
+
const { data } = await axios_1.default.get(`https://ezgif.com/video-to-jpg?url=${encodeURIComponent(videoUrl)}`);
|
|
233
|
+
const $ = cheerio.load(data);
|
|
234
|
+
const fileToken = $('input[name="file"]').attr("value");
|
|
235
|
+
if (!fileToken) {
|
|
236
|
+
throw new Error("Failed to retrieve file token. The video URL may be invalid or inaccessible.");
|
|
237
|
+
}
|
|
238
|
+
const formData = new URLSearchParams();
|
|
239
|
+
formData.append("file", fileToken);
|
|
240
|
+
formData.append("end", "1");
|
|
241
|
+
formData.append("video-to-jpg", "Convert to JPG!");
|
|
242
|
+
const convert = await axios_1.default.post(`https://ezgif.com/video-to-jpg/${fileToken}`, formData);
|
|
243
|
+
const $2 = cheerio.load(convert.data);
|
|
244
|
+
let imageUrl = $2("#output img").first().attr("src");
|
|
245
|
+
if (!imageUrl) {
|
|
246
|
+
throw new Error("Could not locate the converted image output.");
|
|
247
|
+
}
|
|
248
|
+
if (imageUrl.startsWith("//")) {
|
|
249
|
+
imageUrl = "https:" + imageUrl;
|
|
250
|
+
}
|
|
251
|
+
else if (imageUrl.startsWith("/")) {
|
|
252
|
+
const cdnMatch = imageUrl.match(/\/(s\d+\..+?)\/.*/);
|
|
253
|
+
if (cdnMatch) {
|
|
254
|
+
imageUrl = "https://" + imageUrl.slice(2);
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
imageUrl = "https://ezgif.com" + imageUrl;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return imageUrl;
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
throw new Error("Failed to convert video to JPG: " + error.message);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Extracts video thumbnail using FFmpeg
|
|
268
|
+
*/
|
|
269
|
+
const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 256 }) => {
|
|
270
|
+
return new Promise((resolve, reject) => {
|
|
271
|
+
const args = [
|
|
272
|
+
'-ss', time,
|
|
273
|
+
'-i', videoPath,
|
|
274
|
+
'-y',
|
|
275
|
+
'-vf', `scale=${size.width}:-1`,
|
|
276
|
+
'-vframes', '1',
|
|
277
|
+
'-f', 'image2',
|
|
278
|
+
'-vcodec', 'mjpeg',
|
|
279
|
+
'pipe:1'
|
|
280
|
+
];
|
|
281
|
+
const ffmpeg = (0, child_process_1.spawn)('ffmpeg', args);
|
|
282
|
+
const chunks = [];
|
|
283
|
+
let errorOutput = '';
|
|
284
|
+
ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
|
|
285
|
+
ffmpeg.stderr.on('data', data => {
|
|
286
|
+
errorOutput += data.toString();
|
|
287
|
+
});
|
|
288
|
+
ffmpeg.on('error', reject);
|
|
289
|
+
ffmpeg.on('close', code => {
|
|
290
|
+
if (code === 0) return resolve(Buffer.concat(chunks));
|
|
291
|
+
reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`));
|
|
292
|
+
});
|
|
95
293
|
});
|
|
96
|
-
}
|
|
294
|
+
};
|
|
295
|
+
exports.extractVideoThumb = extractVideoThumb;
|
|
97
296
|
const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
98
297
|
var _a, _b;
|
|
99
298
|
if (bufferOrFilePath instanceof stream_1.Readable) {
|
|
@@ -142,8 +341,8 @@ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
|
|
|
142
341
|
.replace(/\=+$/, '')));
|
|
143
342
|
exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
|
|
144
343
|
const generateProfilePicture = async (mediaUpload) => {
|
|
145
|
-
var _a, _b;
|
|
146
344
|
let bufferOrFilePath;
|
|
345
|
+
let img;
|
|
147
346
|
if (Buffer.isBuffer(mediaUpload)) {
|
|
148
347
|
bufferOrFilePath = mediaUpload;
|
|
149
348
|
}
|
|
@@ -153,29 +352,11 @@ const generateProfilePicture = async (mediaUpload) => {
|
|
|
153
352
|
else {
|
|
154
353
|
bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
|
|
155
354
|
}
|
|
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
|
-
}
|
|
355
|
+
const jimp = await jimp_1.default.read(bufferOrFilePath);
|
|
356
|
+
const cropped = jimp.getWidth() > jimp.getHeight() ? jimp.resize(550, -1) : jimp.resize(-1, 650);
|
|
357
|
+
img = cropped
|
|
358
|
+
.quality(100)
|
|
359
|
+
.getBufferAsync(jimp_1.default.MIME_JPEG);
|
|
179
360
|
return {
|
|
180
361
|
img: await img,
|
|
181
362
|
};
|
|
@@ -188,68 +369,173 @@ const mediaMessageSHA256B64 = (message) => {
|
|
|
188
369
|
};
|
|
189
370
|
exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
|
|
190
371
|
async function getAudioDuration(buffer) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
372
|
+
try {
|
|
373
|
+
const { PassThrough } = require('stream');
|
|
374
|
+
const ff = require('fluent-ffmpeg');
|
|
375
|
+
|
|
376
|
+
return await new Promise((resolve, reject) => {
|
|
377
|
+
const inputStream = new PassThrough();
|
|
378
|
+
inputStream.end(buffer);
|
|
379
|
+
|
|
380
|
+
ff(inputStream)
|
|
381
|
+
.ffprobe((err, data) => {
|
|
382
|
+
if (err) reject(err);
|
|
383
|
+
else resolve(data.format.duration);
|
|
384
|
+
});
|
|
385
|
+
});
|
|
386
|
+
} catch (error) {
|
|
387
|
+
const musicMetadata = await import('music-metadata');
|
|
388
|
+
let metadata;
|
|
389
|
+
if (Buffer.isBuffer(buffer)) {
|
|
390
|
+
metadata = await musicMetadata.parseBuffer(buffer, undefined, {
|
|
391
|
+
duration: true
|
|
392
|
+
});
|
|
393
|
+
} else if (typeof buffer === 'string') {
|
|
394
|
+
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
395
|
+
try {
|
|
396
|
+
metadata = await musicMetadata.parseStream(rStream, undefined, {
|
|
397
|
+
duration: true
|
|
398
|
+
});
|
|
399
|
+
} finally {
|
|
400
|
+
rStream.destroy();
|
|
401
|
+
}
|
|
402
|
+
} else {
|
|
403
|
+
metadata = await musicMetadata.parseStream(buffer, undefined, {
|
|
404
|
+
duration: true
|
|
405
|
+
});
|
|
203
406
|
}
|
|
407
|
+
return metadata.format.duration;
|
|
204
408
|
}
|
|
205
|
-
else {
|
|
206
|
-
metadata = await musicMetadata.parseStream(buffer, undefined, { duration: true });
|
|
207
|
-
}
|
|
208
|
-
return metadata.format.duration;
|
|
209
409
|
}
|
|
210
410
|
exports.getAudioDuration = getAudioDuration;
|
|
211
|
-
/**
|
|
212
|
-
referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
|
|
213
|
-
*/
|
|
214
411
|
async function getAudioWaveform(buffer, logger) {
|
|
215
412
|
try {
|
|
216
|
-
const
|
|
413
|
+
const { PassThrough } = require('stream');
|
|
414
|
+
const ff = require('fluent-ffmpeg');
|
|
415
|
+
|
|
217
416
|
let audioData;
|
|
218
417
|
if (Buffer.isBuffer(buffer)) {
|
|
219
418
|
audioData = buffer;
|
|
419
|
+
} else if (typeof buffer === 'string') {
|
|
420
|
+
const rStream = require('fs').createReadStream(buffer);
|
|
421
|
+
audioData = await exports.toBuffer(rStream);
|
|
422
|
+
} else {
|
|
423
|
+
audioData = await exports.toBuffer(buffer);
|
|
220
424
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
425
|
+
|
|
426
|
+
return await new Promise((resolve, reject) => {
|
|
427
|
+
const inputStream = new PassThrough();
|
|
428
|
+
inputStream.end(audioData);
|
|
429
|
+
const chunks = [];
|
|
430
|
+
const bars = 64;
|
|
431
|
+
|
|
432
|
+
ff(inputStream)
|
|
433
|
+
.audioChannels(1)
|
|
434
|
+
.audioFrequency(16000)
|
|
435
|
+
.format('s16le')
|
|
436
|
+
.on('error', reject)
|
|
437
|
+
.on('end', () => {
|
|
438
|
+
const rawData = Buffer.concat(chunks);
|
|
439
|
+
const samples = rawData.length / 2;
|
|
440
|
+
const amplitudes = [];
|
|
441
|
+
|
|
442
|
+
for (let i = 0; i < samples; i++) {
|
|
443
|
+
amplitudes.push(Math.abs(rawData.readInt16LE(i * 2)) / 32768);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const blockSize = Math.floor(amplitudes.length / bars);
|
|
447
|
+
const avg = [];
|
|
448
|
+
for (let i = 0; i < bars; i++) {
|
|
449
|
+
const block = amplitudes.slice(i * blockSize, (i + 1) * blockSize);
|
|
450
|
+
avg.push(block.reduce((a, b) => a + b, 0) / block.length);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const max = Math.max(...avg);
|
|
454
|
+
const normalized = avg.map(v => Math.floor((v / max) * 100));
|
|
455
|
+
resolve(new Uint8Array(normalized));
|
|
456
|
+
})
|
|
457
|
+
.pipe()
|
|
458
|
+
.on('data', chunk => chunks.push(chunk));
|
|
459
|
+
});
|
|
460
|
+
} catch (e) {
|
|
461
|
+
logger?.debug(e);
|
|
250
462
|
}
|
|
251
463
|
}
|
|
252
464
|
exports.getAudioWaveform = getAudioWaveform;
|
|
465
|
+
async function convertToOpusBuffer(buffer, logger) {
|
|
466
|
+
try {
|
|
467
|
+
const { PassThrough } = require('stream');
|
|
468
|
+
const ff = require('fluent-ffmpeg');
|
|
469
|
+
|
|
470
|
+
return await new Promise((resolve, reject) => {
|
|
471
|
+
const inStream = new PassThrough();
|
|
472
|
+
const outStream = new PassThrough();
|
|
473
|
+
const chunks = [];
|
|
474
|
+
inStream.end(buffer);
|
|
475
|
+
|
|
476
|
+
ff(inStream)
|
|
477
|
+
.noVideo()
|
|
478
|
+
.audioCodec('libopus')
|
|
479
|
+
.format('ogg')
|
|
480
|
+
.audioBitrate('48k')
|
|
481
|
+
.audioChannels(1)
|
|
482
|
+
.audioFrequency(48000)
|
|
483
|
+
.outputOptions([
|
|
484
|
+
'-vn',
|
|
485
|
+
'-b:a 64k',
|
|
486
|
+
'-ac 2',
|
|
487
|
+
'-ar 48000',
|
|
488
|
+
'-map_metadata', '-1',
|
|
489
|
+
'-application', 'voip'
|
|
490
|
+
])
|
|
491
|
+
.on('error', reject)
|
|
492
|
+
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
493
|
+
.pipe(outStream, {
|
|
494
|
+
end: true
|
|
495
|
+
});
|
|
496
|
+
outStream.on('data', c => chunks.push(c));
|
|
497
|
+
});
|
|
498
|
+
} catch (e) {
|
|
499
|
+
logger?.debug(e);
|
|
500
|
+
throw e;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
exports.convertToOpusBuffer = convertToOpusBuffer;
|
|
504
|
+
async function convertToMp4Buffer(buffer, logger) {
|
|
505
|
+
try {
|
|
506
|
+
const { PassThrough } = require('stream');
|
|
507
|
+
const ff = require('fluent-ffmpeg');
|
|
508
|
+
|
|
509
|
+
return await new Promise((resolve, reject) => {
|
|
510
|
+
const inStream = new PassThrough();
|
|
511
|
+
const outStream = new PassThrough();
|
|
512
|
+
const chunks = [];
|
|
513
|
+
|
|
514
|
+
inStream.end(buffer);
|
|
515
|
+
|
|
516
|
+
ff(inStream)
|
|
517
|
+
.videoCodec('libx264')
|
|
518
|
+
.audioCodec('aac')
|
|
519
|
+
.format('mp4')
|
|
520
|
+
.outputOptions([
|
|
521
|
+
'-preset', 'veryfast',
|
|
522
|
+
'-crf', '23',
|
|
523
|
+
'-movflags', 'faststart',
|
|
524
|
+
'-map_metadata', '-1'
|
|
525
|
+
])
|
|
526
|
+
.on('error', reject)
|
|
527
|
+
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
528
|
+
.pipe(outStream, { end: true });
|
|
529
|
+
|
|
530
|
+
outStream.on('data', c => chunks.push(c));
|
|
531
|
+
});
|
|
532
|
+
} catch (e) {
|
|
533
|
+
logger?.debug(e);
|
|
534
|
+
throw e;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
exports.convertToMp4Buffer = convertToMp4Buffer;
|
|
253
539
|
const toReadable = (buffer) => {
|
|
254
540
|
const readable = new stream_1.Readable({ read: () => { } });
|
|
255
541
|
readable.push(buffer);
|
|
@@ -295,12 +581,28 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
295
581
|
}
|
|
296
582
|
}
|
|
297
583
|
else if (mediaType === 'video') {
|
|
298
|
-
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
|
|
299
584
|
try {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
585
|
+
let videoPath = file;
|
|
586
|
+
if (Buffer.isBuffer(file) || file instanceof stream_1.Readable) {
|
|
587
|
+
videoPath = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.mp4');
|
|
588
|
+
const buffer = Buffer.isBuffer(file) ? file : await (0, exports.toBuffer)(file);
|
|
589
|
+
await fs_1.promises.writeFile(videoPath, buffer);
|
|
590
|
+
}
|
|
591
|
+
const thumbnailBuffer = await (0, exports.extractVideoThumb)(videoPath);
|
|
592
|
+
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
|
|
593
|
+
await fs_1.promises.writeFile(imgFilename, thumbnailBuffer);
|
|
594
|
+
const { buffer: processedThumbnailBuffer, original } = await (0, exports.extractImageThumb)(imgFilename);
|
|
595
|
+
thumbnail = processedThumbnailBuffer.toString('base64');
|
|
596
|
+
if (original.width && original.height) {
|
|
597
|
+
originalImageDimensions = {
|
|
598
|
+
width: original.width,
|
|
599
|
+
height: original.height,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
303
602
|
await fs_1.promises.unlink(imgFilename);
|
|
603
|
+
if (videoPath !== file) {
|
|
604
|
+
await fs_1.promises.unlink(videoPath);
|
|
605
|
+
}
|
|
304
606
|
}
|
|
305
607
|
catch (err) {
|
|
306
608
|
(_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
|
|
@@ -311,10 +613,8 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
311
613
|
originalImageDimensions
|
|
312
614
|
};
|
|
313
615
|
}
|
|
314
|
-
exports.generateThumbnail = generateThumbnail;
|
|
315
616
|
const getHttpStream = async (url, options = {}) => {
|
|
316
|
-
const
|
|
317
|
-
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
|
|
617
|
+
const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' });
|
|
318
618
|
return fetched.data;
|
|
319
619
|
};
|
|
320
620
|
exports.getHttpStream = getHttpStream;
|
|
@@ -329,7 +629,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
329
629
|
bodyPath = media.url;
|
|
330
630
|
}
|
|
331
631
|
else if (saveOriginalFileIfRequired) {
|
|
332
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.
|
|
632
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
333
633
|
(0, fs_1.writeFileSync)(bodyPath, buffer);
|
|
334
634
|
didSaveToTmpPath = true;
|
|
335
635
|
}
|
|
@@ -348,7 +648,6 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
348
648
|
};
|
|
349
649
|
}
|
|
350
650
|
catch (error) {
|
|
351
|
-
// destroy all streams with error
|
|
352
651
|
stream.destroy();
|
|
353
652
|
if (didSaveToTmpPath) {
|
|
354
653
|
try {
|
|
@@ -362,56 +661,90 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
362
661
|
}
|
|
363
662
|
};
|
|
364
663
|
exports.prepareStream = prepareStream;
|
|
365
|
-
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
664
|
+
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus, convertVideo } = {}) => {
|
|
366
665
|
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
367
|
-
logger
|
|
666
|
+
logger?.debug('fetched media stream');
|
|
667
|
+
|
|
668
|
+
let finalStream = stream;
|
|
669
|
+
|
|
670
|
+
if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
|
|
671
|
+
try {
|
|
672
|
+
const buffer = await (0, exports.toBuffer)(stream);
|
|
673
|
+
const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
|
|
674
|
+
finalStream = (0, exports.toReadable)(opusBuffer);
|
|
675
|
+
logger?.debug('converted audio to Opus');
|
|
676
|
+
} catch (error) {
|
|
677
|
+
logger?.error('failed to convert audio to Opus, fallback to original stream');
|
|
678
|
+
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
679
|
+
finalStream = newStream;
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
if (mediaType === 'video' && convertVideo === true) {
|
|
684
|
+
try {
|
|
685
|
+
const buffer = await (0, exports.toBuffer)(stream);
|
|
686
|
+
const mp4Buffer = await exports.convertToMp4Buffer(buffer, logger);
|
|
687
|
+
finalStream = (0, exports.toReadable)(mp4Buffer);
|
|
688
|
+
logger?.debug('converted video to mp4');
|
|
689
|
+
} catch (error) {
|
|
690
|
+
logger?.error('failed to convert video to mp4, fallback to original stream');
|
|
691
|
+
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
692
|
+
finalStream = newStream;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
368
696
|
const mediaKey = Crypto.randomBytes(32);
|
|
369
|
-
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
|
|
370
|
-
const encWriteStream = new stream_1.Readable({ read: () => {
|
|
697
|
+
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
|
|
698
|
+
const encWriteStream = new stream_1.Readable({ read: () => {} });
|
|
371
699
|
let bodyPath;
|
|
372
700
|
let writeStream;
|
|
373
701
|
let didSaveToTmpPath = false;
|
|
702
|
+
|
|
374
703
|
if (type === 'file') {
|
|
375
704
|
bodyPath = media.url;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
705
|
+
} else if (saveOriginalFileIfRequired) {
|
|
706
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
379
707
|
writeStream = (0, fs_1.createWriteStream)(bodyPath);
|
|
380
708
|
didSaveToTmpPath = true;
|
|
381
709
|
}
|
|
710
|
+
|
|
382
711
|
let fileLength = 0;
|
|
383
712
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
384
713
|
let hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
385
714
|
let sha256Plain = Crypto.createHash('sha256');
|
|
386
715
|
let sha256Enc = Crypto.createHash('sha256');
|
|
716
|
+
|
|
387
717
|
try {
|
|
388
|
-
for await (const data of
|
|
718
|
+
for await (const data of finalStream) {
|
|
389
719
|
fileLength += data.length;
|
|
390
|
-
if (type === 'remote'
|
|
391
|
-
|
|
392
|
-
&& fileLength + data.length > opts.maxContentLength) {
|
|
393
|
-
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
|
|
394
|
-
data: { media, type }
|
|
395
|
-
});
|
|
720
|
+
if (type === 'remote' && (opts?.maxContentLength) && fileLength + data.length > opts.maxContentLength) {
|
|
721
|
+
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, { data: { media, type } });
|
|
396
722
|
}
|
|
723
|
+
|
|
397
724
|
sha256Plain = sha256Plain.update(data);
|
|
725
|
+
|
|
398
726
|
if (writeStream) {
|
|
399
727
|
if (!writeStream.write(data)) {
|
|
400
728
|
await (0, events_1.once)(writeStream, 'drain');
|
|
401
729
|
}
|
|
402
730
|
}
|
|
731
|
+
|
|
403
732
|
onChunk(aes.update(data));
|
|
404
733
|
}
|
|
734
|
+
|
|
405
735
|
onChunk(aes.final());
|
|
406
736
|
const mac = hmac.digest().slice(0, 10);
|
|
407
737
|
sha256Enc = sha256Enc.update(mac);
|
|
408
738
|
const fileSha256 = sha256Plain.digest();
|
|
409
739
|
const fileEncSha256 = sha256Enc.digest();
|
|
740
|
+
|
|
410
741
|
encWriteStream.push(mac);
|
|
411
742
|
encWriteStream.push(null);
|
|
412
|
-
writeStream
|
|
413
|
-
|
|
414
|
-
|
|
743
|
+
writeStream?.end();
|
|
744
|
+
finalStream.destroy();
|
|
745
|
+
|
|
746
|
+
logger?.debug('encrypted data successfully');
|
|
747
|
+
|
|
415
748
|
return {
|
|
416
749
|
mediaKey,
|
|
417
750
|
encWriteStream,
|
|
@@ -422,32 +755,32 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
422
755
|
fileLength,
|
|
423
756
|
didSaveToTmpPath
|
|
424
757
|
};
|
|
425
|
-
}
|
|
426
|
-
catch (error) {
|
|
427
|
-
// destroy all streams with error
|
|
758
|
+
} catch (error) {
|
|
428
759
|
encWriteStream.destroy();
|
|
429
|
-
writeStream
|
|
760
|
+
writeStream?.destroy();
|
|
430
761
|
aes.destroy();
|
|
431
762
|
hmac.destroy();
|
|
432
763
|
sha256Plain.destroy();
|
|
433
764
|
sha256Enc.destroy();
|
|
434
|
-
|
|
765
|
+
finalStream.destroy();
|
|
766
|
+
|
|
435
767
|
if (didSaveToTmpPath) {
|
|
436
768
|
try {
|
|
437
769
|
await fs_1.promises.unlink(bodyPath);
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
|
|
770
|
+
} catch (err) {
|
|
771
|
+
logger?.error({ err }, 'failed to save to tmp path');
|
|
441
772
|
}
|
|
442
773
|
}
|
|
443
774
|
throw error;
|
|
444
775
|
}
|
|
776
|
+
|
|
445
777
|
function onChunk(buff) {
|
|
446
778
|
sha256Enc = sha256Enc.update(buff);
|
|
447
779
|
hmac = hmac.update(buff);
|
|
448
780
|
encWriteStream.push(buff);
|
|
449
781
|
}
|
|
450
782
|
};
|
|
783
|
+
|
|
451
784
|
exports.encryptedStream = encryptedStream;
|
|
452
785
|
const DEF_HOST = 'mmg.whatsapp.net';
|
|
453
786
|
const AES_CHUNK_SIZE = 16;
|
|
@@ -456,21 +789,20 @@ const toSmallestChunkSize = (num) => {
|
|
|
456
789
|
};
|
|
457
790
|
const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
458
791
|
exports.getUrlFromDirectPath = getUrlFromDirectPath;
|
|
459
|
-
const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
460
|
-
const
|
|
461
|
-
const
|
|
792
|
+
const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
793
|
+
const isValidMediaUrl = url === null || url === void 0 ? void 0 : url.startsWith('https://mmg.whatsapp.net/');
|
|
794
|
+
const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath);
|
|
795
|
+
if (!downloadUrl) {
|
|
796
|
+
throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 });
|
|
797
|
+
}
|
|
798
|
+
const keys = await getMediaKeys(mediaKey, type);
|
|
462
799
|
return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
|
|
463
800
|
};
|
|
464
801
|
exports.downloadContentFromMessage = downloadContentFromMessage;
|
|
465
|
-
/**
|
|
466
|
-
* Decrypts and downloads an AES256-CBC encrypted file given the keys.
|
|
467
|
-
* Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
|
|
468
|
-
* */
|
|
469
802
|
const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
|
|
470
803
|
let bytesFetched = 0;
|
|
471
804
|
let startChunk = 0;
|
|
472
805
|
let firstBlockIsIV = false;
|
|
473
|
-
// if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
|
|
474
806
|
if (startByte) {
|
|
475
807
|
const chunk = toSmallestChunkSize(startByte || 0);
|
|
476
808
|
if (chunk) {
|
|
@@ -490,7 +822,6 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
490
822
|
headers.Range += endChunk;
|
|
491
823
|
}
|
|
492
824
|
}
|
|
493
|
-
// download the message
|
|
494
825
|
const fetched = await (0, exports.getHttpStream)(downloadUrl, {
|
|
495
826
|
...options || {},
|
|
496
827
|
headers,
|
|
@@ -523,8 +854,6 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
523
854
|
data = data.slice(AES_CHUNK_SIZE);
|
|
524
855
|
}
|
|
525
856
|
aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
|
|
526
|
-
// if an end byte that is not EOF is specified
|
|
527
|
-
// stop auto padding (PKCS7) -- otherwise throws an error for decryption
|
|
528
857
|
if (endByte) {
|
|
529
858
|
aes.setAutoPadding(false);
|
|
530
859
|
}
|
|
@@ -565,12 +894,9 @@ function extensionForMediaMessage(message) {
|
|
|
565
894
|
}
|
|
566
895
|
return extension;
|
|
567
896
|
}
|
|
568
|
-
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
569
897
|
const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
570
898
|
return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
|
|
571
899
|
var _a, _b;
|
|
572
|
-
const { default: axios } = await import('axios');
|
|
573
|
-
// send a query JSON to obtain the url & auth token to upload our media
|
|
574
900
|
let uploadInfo = await refreshMediaConn(false);
|
|
575
901
|
let urls;
|
|
576
902
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
@@ -588,14 +914,14 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
588
914
|
}
|
|
589
915
|
for (const { hostname, maxContentLengthBytes } of hosts) {
|
|
590
916
|
logger.debug(`uploading to "${hostname}"`);
|
|
591
|
-
const auth = encodeURIComponent(uploadInfo.auth);
|
|
917
|
+
const auth = encodeURIComponent(uploadInfo.auth);
|
|
592
918
|
const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
593
919
|
let result;
|
|
594
920
|
try {
|
|
595
921
|
if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
|
|
596
922
|
throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
|
|
597
923
|
}
|
|
598
|
-
const body = await
|
|
924
|
+
const body = await axios_1.default.post(url, reqBody, {
|
|
599
925
|
...options,
|
|
600
926
|
headers: {
|
|
601
927
|
...options.headers || {},
|
|
@@ -623,7 +949,7 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
623
949
|
}
|
|
624
950
|
}
|
|
625
951
|
catch (error) {
|
|
626
|
-
if (
|
|
952
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
627
953
|
result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
628
954
|
}
|
|
629
955
|
const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
|
|
@@ -640,14 +966,11 @@ exports.getWAUploadToServer = getWAUploadToServer;
|
|
|
640
966
|
const getMediaRetryKey = (mediaKey) => {
|
|
641
967
|
return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
|
|
642
968
|
};
|
|
643
|
-
|
|
644
|
-
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
|
645
|
-
*/
|
|
646
|
-
const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
969
|
+
const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
647
970
|
const recp = { stanzaId: key.id };
|
|
648
971
|
const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
|
|
649
972
|
const iv = Crypto.randomBytes(12);
|
|
650
|
-
const retryKey = getMediaRetryKey(mediaKey);
|
|
973
|
+
const retryKey = await getMediaRetryKey(mediaKey);
|
|
651
974
|
const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
652
975
|
const req = {
|
|
653
976
|
tag: 'receipt',
|
|
@@ -657,9 +980,6 @@ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
|
657
980
|
type: 'server-error'
|
|
658
981
|
},
|
|
659
982
|
content: [
|
|
660
|
-
// this encrypt node is actually pretty useless
|
|
661
|
-
// the media is returned even without this node
|
|
662
|
-
// keeping it here to maintain parity with WA Web
|
|
663
983
|
{
|
|
664
984
|
tag: 'encrypt',
|
|
665
985
|
attrs: {},
|
|
@@ -673,7 +993,6 @@ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
|
673
993
|
attrs: {
|
|
674
994
|
jid: key.remoteJid,
|
|
675
995
|
'from_me': (!!key.fromMe).toString(),
|
|
676
|
-
// @ts-ignore
|
|
677
996
|
participant: key.participant || undefined
|
|
678
997
|
}
|
|
679
998
|
}
|
|
@@ -711,8 +1030,8 @@ const decodeMediaRetryNode = (node) => {
|
|
|
711
1030
|
return event;
|
|
712
1031
|
};
|
|
713
1032
|
exports.decodeMediaRetryNode = decodeMediaRetryNode;
|
|
714
|
-
const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
715
|
-
const retryKey = getMediaRetryKey(mediaKey);
|
|
1033
|
+
const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
1034
|
+
const retryKey = await getMediaRetryKey(mediaKey);
|
|
716
1035
|
const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
|
|
717
1036
|
return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
|
|
718
1037
|
};
|
|
@@ -725,7 +1044,6 @@ const MEDIA_RETRY_STATUS_MAP = {
|
|
|
725
1044
|
[WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
726
1045
|
[WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
|
|
727
1046
|
};
|
|
728
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
729
1047
|
function __importStar(arg0) {
|
|
730
1048
|
throw new Error('Function not implemented.');
|
|
731
|
-
}
|
|
1049
|
+
}
|