riftcore 1.4.5 → 1.4.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/README.md +422 -0
- package/WAProto/index.js +79296 -118676
- package/example.js +13 -0
- package/lib/Defaults/baileys-version.json +2 -2
- package/lib/Defaults/index.d.ts +7 -16
- package/lib/Defaults/index.js +103 -90
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Socket/Client/{types.d.ts → abstract-socket-client.d.ts} +2 -1
- package/lib/Socket/Client/index.d.ts +3 -2
- package/lib/Socket/Client/index.js +3 -2
- package/lib/Socket/Client/mobile-socket-client.d.ts +13 -0
- package/lib/Socket/Client/mobile-socket-client.js +65 -0
- package/lib/Socket/Client/{websocket.d.ts → web-socket-client.d.ts} +1 -2
- package/lib/Socket/Client/{websocket.js → web-socket-client.js} +5 -54
- package/lib/Socket/business.d.ts +58 -59
- package/lib/Socket/chats.d.ts +27 -29
- package/lib/Socket/chats.js +90 -97
- package/lib/Socket/erlanggaofficial.d.ts +219 -0
- package/lib/Socket/erlanggaofficial.js +439 -0
- package/lib/Socket/groups.d.ts +32 -41
- package/lib/Socket/groups.js +5 -20
- package/lib/Socket/index.d.ts +64 -63
- package/lib/Socket/index.js +2 -2
- package/lib/Socket/messages-recv.js +65 -9
- package/lib/Socket/messages-send.d.ts +47 -49
- package/lib/Socket/messages-send.js +377 -541
- package/lib/Socket/newsletter.d.ts +37 -39
- package/lib/Socket/newsletter.js +41 -73
- package/lib/Socket/registration.d.ts +267 -0
- package/lib/Socket/registration.js +166 -0
- package/lib/Socket/socket.d.ts +43 -270
- package/lib/Socket/socket.js +38 -62
- package/lib/Socket/usync.d.ts +3 -3
- package/lib/Store/index.d.ts +2 -1
- package/lib/Store/index.js +3 -1
- package/lib/Store/make-cache-manager-store.d.ts +13 -0
- package/lib/Store/make-cache-manager-store.js +83 -0
- package/lib/Store/make-in-memory-store.d.ts +24 -24
- package/lib/Store/make-in-memory-store.js +15 -27
- 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 +7 -0
- package/lib/Types/Call.d.ts +1 -1
- package/lib/Types/Chat.d.ts +7 -14
- package/lib/Types/Contact.d.ts +1 -5
- package/lib/Types/Events.d.ts +2 -44
- package/lib/Types/GroupMetadata.d.ts +2 -11
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Message.d.ts +21 -148
- package/lib/Types/Message.js +2 -0
- package/lib/Types/Newsletter.d.ts +13 -0
- package/lib/Types/Newsletter.js +17 -3
- package/lib/Types/Socket.d.ts +9 -17
- package/lib/Types/index.d.ts +1 -8
- package/lib/Types/index.js +2 -2
- package/lib/Utils/auth-utils.d.ts +3 -3
- package/lib/Utils/auth-utils.js +13 -6
- package/lib/Utils/business.js +2 -2
- package/lib/Utils/chat-utils.d.ts +16 -15
- package/lib/Utils/chat-utils.js +35 -36
- package/lib/Utils/crypto.d.ts +16 -15
- package/lib/Utils/crypto.js +29 -71
- package/lib/Utils/decode-wa-message.d.ts +6 -22
- package/lib/Utils/decode-wa-message.js +56 -65
- package/lib/Utils/event-buffer.d.ts +2 -2
- package/lib/Utils/event-buffer.js +7 -11
- package/lib/Utils/generics.d.ts +20 -17
- package/lib/Utils/generics.js +23 -73
- package/lib/Utils/history.d.ts +0 -4
- package/lib/Utils/history.js +6 -4
- package/lib/Utils/link-preview.d.ts +2 -2
- package/lib/Utils/link-preview.js +1 -34
- package/lib/Utils/logger.d.ts +3 -10
- 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 +24 -28
- package/lib/Utils/messages-media.js +157 -406
- package/lib/Utils/messages.d.ts +10 -13
- package/lib/Utils/messages.js +48 -345
- package/lib/Utils/noise-handler.d.ts +12 -10
- package/lib/Utils/noise-handler.js +23 -18
- package/lib/Utils/process-message.d.ts +4 -5
- package/lib/Utils/process-message.js +25 -108
- package/lib/Utils/signal.d.ts +1 -2
- package/lib/Utils/signal.js +35 -37
- package/lib/Utils/use-multi-file-auth-state.d.ts +1 -0
- package/lib/Utils/use-multi-file-auth-state.js +6 -51
- package/lib/Utils/validate-connection.d.ts +4 -3
- package/lib/Utils/validate-connection.js +66 -90
- package/lib/WABinary/constants.d.ts +4 -4
- package/lib/WABinary/constants.js +13 -1276
- package/lib/WABinary/decode.d.ts +4 -3
- package/lib/WABinary/decode.js +13 -26
- package/lib/WABinary/encode.d.ts +2 -1
- package/lib/WABinary/encode.js +17 -39
- package/lib/WABinary/generic-utils.d.ts +3 -1
- package/lib/WABinary/generic-utils.js +10 -2
- package/lib/WABinary/jid-utils.d.ts +5 -11
- package/lib/WABinary/jid-utils.js +5 -28
- package/lib/WAM/BinaryInfo.d.ts +11 -2
- package/lib/WAM/encode.d.ts +2 -1
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +1 -1
- package/lib/index.d.ts +2 -6
- package/lib/index.js +15 -31
- package/package.json +51 -39
- package/WAProto/GenerateStatics.sh +0 -4
- package/WAProto/WAProto.proto +0 -4775
- package/WAProto/index.d.ts +0 -55057
- package/WAProto/index.ts.ts +0 -53473
- package/lib/Socket/setup.js +0 -481
- package/lib/Socket/setup.ts +0 -623
- package/lib/WABinary/jid-utils.js.bak +0 -83
- /package/lib/Socket/Client/{types.js → abstract-socket-client.js} +0 -0
|
@@ -15,47 +15,23 @@ 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) ||
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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 };
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
37
24
|
};
|
|
38
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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.
|
|
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;
|
|
26
|
+
exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.extensionForMediaMessage = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.prepareStream = exports.getHttpStream = exports.generateThumbnail = exports.getStream = exports.toBuffer = exports.toReadable = exports.getAudioWaveform = exports.getAudioDuration = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.getMediaKeys = exports.hkdfInfoKey = void 0;
|
|
47
27
|
const boom_1 = require("@hapi/boom");
|
|
48
|
-
const
|
|
49
|
-
const form_data_1 = __importDefault(require("form-data"));
|
|
50
|
-
const cheerio = __importStar(require("cheerio"));
|
|
28
|
+
const child_process_1 = require("child_process");
|
|
51
29
|
const Crypto = __importStar(require("crypto"));
|
|
52
30
|
const events_1 = require("events");
|
|
53
31
|
const fs_1 = require("fs");
|
|
54
32
|
const os_1 = require("os");
|
|
55
33
|
const path_1 = require("path");
|
|
56
|
-
const jimp_1 = __importDefault(require("jimp"));
|
|
57
34
|
const stream_1 = require("stream");
|
|
58
|
-
const child_process_1 = require("child_process");
|
|
59
35
|
const WAProto_1 = require("../../WAProto");
|
|
60
36
|
const Defaults_1 = require("../Defaults");
|
|
61
37
|
const WABinary_1 = require("../WABinary");
|
|
@@ -65,11 +41,13 @@ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
|
|
|
65
41
|
const getImageProcessingLibrary = async () => {
|
|
66
42
|
const [_jimp, sharp] = await Promise.all([
|
|
67
43
|
(async () => {
|
|
68
|
-
const jimp = await (
|
|
44
|
+
const jimp = await (import('jimp')
|
|
45
|
+
.catch(() => { }));
|
|
69
46
|
return jimp;
|
|
70
47
|
})(),
|
|
71
48
|
(async () => {
|
|
72
|
-
const sharp = await (
|
|
49
|
+
const sharp = await (import('sharp')
|
|
50
|
+
.catch(() => { }));
|
|
73
51
|
return sharp;
|
|
74
52
|
})()
|
|
75
53
|
]);
|
|
@@ -88,7 +66,7 @@ const hkdfInfoKey = (type) => {
|
|
|
88
66
|
};
|
|
89
67
|
exports.hkdfInfoKey = hkdfInfoKey;
|
|
90
68
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
91
|
-
|
|
69
|
+
function getMediaKeys(buffer, mediaType) {
|
|
92
70
|
if (!buffer) {
|
|
93
71
|
throw new boom_1.Boom('Cannot derive from empty media key');
|
|
94
72
|
}
|
|
@@ -96,183 +74,26 @@ async function getMediaKeys(buffer, mediaType) {
|
|
|
96
74
|
buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
|
|
97
75
|
}
|
|
98
76
|
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
|
99
|
-
const expandedMediaKey =
|
|
77
|
+
const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
100
78
|
return {
|
|
101
79
|
iv: expandedMediaKey.slice(0, 16),
|
|
102
80
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
103
81
|
macKey: expandedMediaKey.slice(48, 80),
|
|
104
82
|
};
|
|
105
83
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
}
|
|
193
|
-
}
|
|
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;
|
|
84
|
+
exports.getMediaKeys = getMediaKeys;
|
|
85
|
+
/** Extracts video thumb using FFMPEG */
|
|
86
|
+
const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
|
|
87
|
+
const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
|
|
88
|
+
(0, child_process_1.exec)(cmd, (err) => {
|
|
89
|
+
if (err) {
|
|
90
|
+
reject(err);
|
|
230
91
|
}
|
|
231
|
-
else
|
|
232
|
-
|
|
233
|
-
if (cdnMatch) {
|
|
234
|
-
imageUrl = "https://" + imageUrl.slice(2);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
imageUrl = "https://ezgif.com" + imageUrl;
|
|
238
|
-
}
|
|
92
|
+
else {
|
|
93
|
+
resolve();
|
|
239
94
|
}
|
|
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
|
-
});
|
|
273
95
|
});
|
|
274
|
-
};
|
|
275
|
-
exports.extractVideoThumb = extractVideoThumb;
|
|
96
|
+
});
|
|
276
97
|
const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
277
98
|
var _a, _b;
|
|
278
99
|
if (bufferOrFilePath instanceof stream_1.Readable) {
|
|
@@ -321,8 +142,8 @@ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
|
|
|
321
142
|
.replace(/\=+$/, '')));
|
|
322
143
|
exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
|
|
323
144
|
const generateProfilePicture = async (mediaUpload) => {
|
|
145
|
+
var _a, _b;
|
|
324
146
|
let bufferOrFilePath;
|
|
325
|
-
let img;
|
|
326
147
|
if (Buffer.isBuffer(mediaUpload)) {
|
|
327
148
|
bufferOrFilePath = mediaUpload;
|
|
328
149
|
}
|
|
@@ -332,11 +153,29 @@ const generateProfilePicture = async (mediaUpload) => {
|
|
|
332
153
|
else {
|
|
333
154
|
bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
|
|
334
155
|
}
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
.
|
|
339
|
-
|
|
156
|
+
const lib = await getImageProcessingLibrary();
|
|
157
|
+
let img;
|
|
158
|
+
if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
|
|
159
|
+
img = lib.sharp.default(bufferOrFilePath)
|
|
160
|
+
.resize(640, 640)
|
|
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
|
+
}
|
|
340
179
|
return {
|
|
341
180
|
img: await img,
|
|
342
181
|
};
|
|
@@ -349,138 +188,68 @@ const mediaMessageSHA256B64 = (message) => {
|
|
|
349
188
|
};
|
|
350
189
|
exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
|
|
351
190
|
async function getAudioDuration(buffer) {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
.ffprobe((err, data) => {
|
|
362
|
-
if (err) reject(err);
|
|
363
|
-
else resolve(data.format.duration);
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
} catch (error) {
|
|
367
|
-
const musicMetadata = await import('music-metadata');
|
|
368
|
-
let metadata;
|
|
369
|
-
if (Buffer.isBuffer(buffer)) {
|
|
370
|
-
metadata = await musicMetadata.parseBuffer(buffer, undefined, {
|
|
371
|
-
duration: true
|
|
372
|
-
});
|
|
373
|
-
} else if (typeof buffer === 'string') {
|
|
374
|
-
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
375
|
-
try {
|
|
376
|
-
metadata = await musicMetadata.parseStream(rStream, undefined, {
|
|
377
|
-
duration: true
|
|
378
|
-
});
|
|
379
|
-
} finally {
|
|
380
|
-
rStream.destroy();
|
|
381
|
-
}
|
|
382
|
-
} else {
|
|
383
|
-
metadata = await musicMetadata.parseStream(buffer, undefined, {
|
|
384
|
-
duration: true
|
|
385
|
-
});
|
|
191
|
+
const musicMetadata = await import('music-metadata');
|
|
192
|
+
let metadata;
|
|
193
|
+
if (Buffer.isBuffer(buffer)) {
|
|
194
|
+
metadata = await musicMetadata.parseBuffer(buffer, undefined, { duration: true });
|
|
195
|
+
}
|
|
196
|
+
else if (typeof buffer === 'string') {
|
|
197
|
+
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
198
|
+
try {
|
|
199
|
+
metadata = await musicMetadata.parseStream(rStream, undefined, { duration: true });
|
|
386
200
|
}
|
|
387
|
-
|
|
201
|
+
finally {
|
|
202
|
+
rStream.destroy();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
metadata = await musicMetadata.parseStream(buffer, undefined, { duration: true });
|
|
388
207
|
}
|
|
208
|
+
return metadata.format.duration;
|
|
389
209
|
}
|
|
390
210
|
exports.getAudioDuration = getAudioDuration;
|
|
211
|
+
/**
|
|
212
|
+
referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
|
|
213
|
+
*/
|
|
391
214
|
async function getAudioWaveform(buffer, logger) {
|
|
392
215
|
try {
|
|
393
|
-
const
|
|
394
|
-
const ff = require('fluent-ffmpeg');
|
|
395
|
-
|
|
216
|
+
const audioDecode = (buffer) => import('audio-decode').then(({ default: audioDecode }) => audioDecode(buffer));
|
|
396
217
|
let audioData;
|
|
397
218
|
if (Buffer.isBuffer(buffer)) {
|
|
398
219
|
audioData = buffer;
|
|
399
|
-
} else if (typeof buffer === 'string') {
|
|
400
|
-
const rStream = require('fs').createReadStream(buffer);
|
|
401
|
-
audioData = await exports.toBuffer(rStream);
|
|
402
|
-
} else {
|
|
403
|
-
audioData = await exports.toBuffer(buffer);
|
|
404
220
|
}
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
const max = Math.max(...avg);
|
|
434
|
-
const normalized = avg.map(v => Math.floor((v / max) * 100));
|
|
435
|
-
resolve(new Uint8Array(normalized));
|
|
436
|
-
})
|
|
437
|
-
.pipe()
|
|
438
|
-
.on('data', chunk => chunks.push(chunk));
|
|
439
|
-
});
|
|
440
|
-
} catch (e) {
|
|
441
|
-
logger?.debug(e);
|
|
221
|
+
else if (typeof buffer === 'string') {
|
|
222
|
+
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
223
|
+
audioData = await (0, exports.toBuffer)(rStream);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
audioData = await (0, exports.toBuffer)(buffer);
|
|
227
|
+
}
|
|
228
|
+
const audioBuffer = await audioDecode(audioData);
|
|
229
|
+
const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
|
|
230
|
+
const samples = 64; // Number of samples we want to have in our final data set
|
|
231
|
+
const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
|
|
232
|
+
const filteredData = [];
|
|
233
|
+
for (let i = 0; i < samples; i++) {
|
|
234
|
+
const blockStart = blockSize * i; // the location of the first sample in the block
|
|
235
|
+
let sum = 0;
|
|
236
|
+
for (let j = 0; j < blockSize; j++) {
|
|
237
|
+
sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
|
|
238
|
+
}
|
|
239
|
+
filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
|
|
240
|
+
}
|
|
241
|
+
// This guarantees that the largest data point will be set to 1, and the rest of the data will scale proportionally.
|
|
242
|
+
const multiplier = Math.pow(Math.max(...filteredData), -1);
|
|
243
|
+
const normalizedData = filteredData.map((n) => n * multiplier);
|
|
244
|
+
// Generate waveform like WhatsApp
|
|
245
|
+
const waveform = new Uint8Array(normalizedData.map((n) => Math.floor(100 * n)));
|
|
246
|
+
return waveform;
|
|
442
247
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
async function convertToOpusBuffer(buffer, logger) {
|
|
446
|
-
try {
|
|
447
|
-
const { PassThrough } = require('stream');
|
|
448
|
-
const ff = require('fluent-ffmpeg');
|
|
449
|
-
|
|
450
|
-
return await new Promise((resolve, reject) => {
|
|
451
|
-
const inStream = new PassThrough();
|
|
452
|
-
const outStream = new PassThrough();
|
|
453
|
-
const chunks = [];
|
|
454
|
-
inStream.end(buffer);
|
|
455
|
-
|
|
456
|
-
ff(inStream)
|
|
457
|
-
.noVideo()
|
|
458
|
-
.audioCodec('libopus')
|
|
459
|
-
.format('ogg')
|
|
460
|
-
.audioBitrate('48k')
|
|
461
|
-
.audioChannels(1)
|
|
462
|
-
.audioFrequency(48000)
|
|
463
|
-
.outputOptions([
|
|
464
|
-
'-vn',
|
|
465
|
-
'-b:a 64k',
|
|
466
|
-
'-ac 2',
|
|
467
|
-
'-ar 48000',
|
|
468
|
-
'-map_metadata', '-1',
|
|
469
|
-
'-application', 'voip'
|
|
470
|
-
])
|
|
471
|
-
.on('error', reject)
|
|
472
|
-
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
473
|
-
.pipe(outStream, {
|
|
474
|
-
end: true
|
|
475
|
-
});
|
|
476
|
-
outStream.on('data', c => chunks.push(c));
|
|
477
|
-
});
|
|
478
|
-
} catch (e) {
|
|
479
|
-
logger?.debug(e);
|
|
480
|
-
throw e;
|
|
248
|
+
catch (e) {
|
|
249
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('Failed to generate waveform: ' + e);
|
|
481
250
|
}
|
|
482
251
|
}
|
|
483
|
-
exports.
|
|
252
|
+
exports.getAudioWaveform = getAudioWaveform;
|
|
484
253
|
const toReadable = (buffer) => {
|
|
485
254
|
const readable = new stream_1.Readable({ read: () => { } });
|
|
486
255
|
readable.push(buffer);
|
|
@@ -526,28 +295,12 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
526
295
|
}
|
|
527
296
|
}
|
|
528
297
|
else if (mediaType === 'video') {
|
|
298
|
+
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
|
|
529
299
|
try {
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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
|
-
}
|
|
300
|
+
await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
|
|
301
|
+
const buff = await fs_1.promises.readFile(imgFilename);
|
|
302
|
+
thumbnail = buff.toString('base64');
|
|
547
303
|
await fs_1.promises.unlink(imgFilename);
|
|
548
|
-
if (videoPath !== file) {
|
|
549
|
-
await fs_1.promises.unlink(videoPath);
|
|
550
|
-
}
|
|
551
304
|
}
|
|
552
305
|
catch (err) {
|
|
553
306
|
(_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
|
|
@@ -558,8 +311,10 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
558
311
|
originalImageDimensions
|
|
559
312
|
};
|
|
560
313
|
}
|
|
314
|
+
exports.generateThumbnail = generateThumbnail;
|
|
561
315
|
const getHttpStream = async (url, options = {}) => {
|
|
562
|
-
const
|
|
316
|
+
const { default: axios } = await import('axios');
|
|
317
|
+
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
|
|
563
318
|
return fetched.data;
|
|
564
319
|
};
|
|
565
320
|
exports.getHttpStream = getHttpStream;
|
|
@@ -574,7 +329,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
574
329
|
bodyPath = media.url;
|
|
575
330
|
}
|
|
576
331
|
else if (saveOriginalFileIfRequired) {
|
|
577
|
-
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.
|
|
332
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
578
333
|
(0, fs_1.writeFileSync)(bodyPath, buffer);
|
|
579
334
|
didSaveToTmpPath = true;
|
|
580
335
|
}
|
|
@@ -593,6 +348,7 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
593
348
|
};
|
|
594
349
|
}
|
|
595
350
|
catch (error) {
|
|
351
|
+
// destroy all streams with error
|
|
596
352
|
stream.destroy();
|
|
597
353
|
if (didSaveToTmpPath) {
|
|
598
354
|
try {
|
|
@@ -606,51 +362,34 @@ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequi
|
|
|
606
362
|
}
|
|
607
363
|
};
|
|
608
364
|
exports.prepareStream = prepareStream;
|
|
609
|
-
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts
|
|
365
|
+
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
610
366
|
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
611
|
-
logger
|
|
612
|
-
|
|
613
|
-
let finalStream = stream;
|
|
614
|
-
|
|
615
|
-
// Tambahan untuk konversi audio jadi Opus (PTT)
|
|
616
|
-
if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
|
|
617
|
-
try {
|
|
618
|
-
const buffer = await (0, exports.toBuffer)(stream);
|
|
619
|
-
const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
|
|
620
|
-
finalStream = (0, exports.toReadable)(opusBuffer);
|
|
621
|
-
logger?.debug('converted audio to Opus');
|
|
622
|
-
} catch (error) {
|
|
623
|
-
logger?.error('failed to convert audio to Opus, fallback to original stream');
|
|
624
|
-
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
625
|
-
finalStream = newStream;
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
|
|
367
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
|
|
629
368
|
const mediaKey = Crypto.randomBytes(32);
|
|
630
|
-
const { cipherKey, iv, macKey } =
|
|
369
|
+
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
|
|
631
370
|
const encWriteStream = new stream_1.Readable({ read: () => { } });
|
|
632
371
|
let bodyPath;
|
|
633
372
|
let writeStream;
|
|
634
373
|
let didSaveToTmpPath = false;
|
|
635
|
-
|
|
636
374
|
if (type === 'file') {
|
|
637
375
|
bodyPath = media.url;
|
|
638
|
-
}
|
|
639
|
-
|
|
376
|
+
}
|
|
377
|
+
else if (saveOriginalFileIfRequired) {
|
|
378
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
640
379
|
writeStream = (0, fs_1.createWriteStream)(bodyPath);
|
|
641
380
|
didSaveToTmpPath = true;
|
|
642
381
|
}
|
|
643
|
-
|
|
644
382
|
let fileLength = 0;
|
|
645
383
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
646
384
|
let hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
647
385
|
let sha256Plain = Crypto.createHash('sha256');
|
|
648
386
|
let sha256Enc = Crypto.createHash('sha256');
|
|
649
|
-
|
|
650
387
|
try {
|
|
651
|
-
for await (const data of
|
|
388
|
+
for await (const data of stream) {
|
|
652
389
|
fileLength += data.length;
|
|
653
|
-
if (type === 'remote'
|
|
390
|
+
if (type === 'remote'
|
|
391
|
+
&& (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
|
|
392
|
+
&& fileLength + data.length > opts.maxContentLength) {
|
|
654
393
|
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
|
|
655
394
|
data: { media, type }
|
|
656
395
|
});
|
|
@@ -663,20 +402,16 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
663
402
|
}
|
|
664
403
|
onChunk(aes.update(data));
|
|
665
404
|
}
|
|
666
|
-
|
|
667
405
|
onChunk(aes.final());
|
|
668
406
|
const mac = hmac.digest().slice(0, 10);
|
|
669
407
|
sha256Enc = sha256Enc.update(mac);
|
|
670
408
|
const fileSha256 = sha256Plain.digest();
|
|
671
409
|
const fileEncSha256 = sha256Enc.digest();
|
|
672
|
-
|
|
673
410
|
encWriteStream.push(mac);
|
|
674
411
|
encWriteStream.push(null);
|
|
675
|
-
writeStream
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
logger?.debug('encrypted data successfully');
|
|
679
|
-
|
|
412
|
+
writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
|
|
413
|
+
stream.destroy();
|
|
414
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('encrypted data successfully');
|
|
680
415
|
return {
|
|
681
416
|
mediaKey,
|
|
682
417
|
encWriteStream,
|
|
@@ -687,25 +422,26 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
|
|
|
687
422
|
fileLength,
|
|
688
423
|
didSaveToTmpPath
|
|
689
424
|
};
|
|
690
|
-
}
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
// destroy all streams with error
|
|
691
428
|
encWriteStream.destroy();
|
|
692
|
-
writeStream
|
|
429
|
+
writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
|
|
693
430
|
aes.destroy();
|
|
694
431
|
hmac.destroy();
|
|
695
432
|
sha256Plain.destroy();
|
|
696
433
|
sha256Enc.destroy();
|
|
697
|
-
|
|
698
|
-
|
|
434
|
+
stream.destroy();
|
|
699
435
|
if (didSaveToTmpPath) {
|
|
700
436
|
try {
|
|
701
437
|
await fs_1.promises.unlink(bodyPath);
|
|
702
|
-
}
|
|
703
|
-
|
|
438
|
+
}
|
|
439
|
+
catch (err) {
|
|
440
|
+
logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
|
|
704
441
|
}
|
|
705
442
|
}
|
|
706
443
|
throw error;
|
|
707
444
|
}
|
|
708
|
-
|
|
709
445
|
function onChunk(buff) {
|
|
710
446
|
sha256Enc = sha256Enc.update(buff);
|
|
711
447
|
hmac = hmac.update(buff);
|
|
@@ -720,20 +456,21 @@ const toSmallestChunkSize = (num) => {
|
|
|
720
456
|
};
|
|
721
457
|
const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
722
458
|
exports.getUrlFromDirectPath = getUrlFromDirectPath;
|
|
723
|
-
const downloadContentFromMessage =
|
|
724
|
-
const
|
|
725
|
-
const
|
|
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);
|
|
459
|
+
const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
460
|
+
const downloadUrl = url || (0, exports.getUrlFromDirectPath)(directPath);
|
|
461
|
+
const keys = getMediaKeys(mediaKey, type);
|
|
730
462
|
return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
|
|
731
463
|
};
|
|
732
464
|
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
|
+
* */
|
|
733
469
|
const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
|
|
734
470
|
let bytesFetched = 0;
|
|
735
471
|
let startChunk = 0;
|
|
736
472
|
let firstBlockIsIV = false;
|
|
473
|
+
// if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
|
|
737
474
|
if (startByte) {
|
|
738
475
|
const chunk = toSmallestChunkSize(startByte || 0);
|
|
739
476
|
if (chunk) {
|
|
@@ -753,6 +490,7 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
753
490
|
headers.Range += endChunk;
|
|
754
491
|
}
|
|
755
492
|
}
|
|
493
|
+
// download the message
|
|
756
494
|
const fetched = await (0, exports.getHttpStream)(downloadUrl, {
|
|
757
495
|
...options || {},
|
|
758
496
|
headers,
|
|
@@ -785,6 +523,8 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
785
523
|
data = data.slice(AES_CHUNK_SIZE);
|
|
786
524
|
}
|
|
787
525
|
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
|
|
788
528
|
if (endByte) {
|
|
789
529
|
aes.setAutoPadding(false);
|
|
790
530
|
}
|
|
@@ -825,9 +565,12 @@ function extensionForMediaMessage(message) {
|
|
|
825
565
|
}
|
|
826
566
|
return extension;
|
|
827
567
|
}
|
|
568
|
+
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
828
569
|
const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
829
570
|
return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
|
|
830
571
|
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
|
|
831
574
|
let uploadInfo = await refreshMediaConn(false);
|
|
832
575
|
let urls;
|
|
833
576
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
@@ -845,14 +588,14 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
845
588
|
}
|
|
846
589
|
for (const { hostname, maxContentLengthBytes } of hosts) {
|
|
847
590
|
logger.debug(`uploading to "${hostname}"`);
|
|
848
|
-
const auth = encodeURIComponent(uploadInfo.auth);
|
|
591
|
+
const auth = encodeURIComponent(uploadInfo.auth); // the auth token
|
|
849
592
|
const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
850
593
|
let result;
|
|
851
594
|
try {
|
|
852
595
|
if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
|
|
853
596
|
throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
|
|
854
597
|
}
|
|
855
|
-
const body = await
|
|
598
|
+
const body = await axios.post(url, reqBody, {
|
|
856
599
|
...options,
|
|
857
600
|
headers: {
|
|
858
601
|
...options.headers || {},
|
|
@@ -880,7 +623,7 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
880
623
|
}
|
|
881
624
|
}
|
|
882
625
|
catch (error) {
|
|
883
|
-
if (
|
|
626
|
+
if (axios.isAxiosError(error)) {
|
|
884
627
|
result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
885
628
|
}
|
|
886
629
|
const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
|
|
@@ -897,11 +640,14 @@ exports.getWAUploadToServer = getWAUploadToServer;
|
|
|
897
640
|
const getMediaRetryKey = (mediaKey) => {
|
|
898
641
|
return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
|
|
899
642
|
};
|
|
900
|
-
|
|
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) => {
|
|
901
647
|
const recp = { stanzaId: key.id };
|
|
902
648
|
const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
|
|
903
649
|
const iv = Crypto.randomBytes(12);
|
|
904
|
-
const retryKey =
|
|
650
|
+
const retryKey = getMediaRetryKey(mediaKey);
|
|
905
651
|
const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
906
652
|
const req = {
|
|
907
653
|
tag: 'receipt',
|
|
@@ -911,6 +657,9 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
911
657
|
type: 'server-error'
|
|
912
658
|
},
|
|
913
659
|
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
|
|
914
663
|
{
|
|
915
664
|
tag: 'encrypt',
|
|
916
665
|
attrs: {},
|
|
@@ -924,6 +673,7 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
924
673
|
attrs: {
|
|
925
674
|
jid: key.remoteJid,
|
|
926
675
|
'from_me': (!!key.fromMe).toString(),
|
|
676
|
+
// @ts-ignore
|
|
927
677
|
participant: key.participant || undefined
|
|
928
678
|
}
|
|
929
679
|
}
|
|
@@ -961,8 +711,8 @@ const decodeMediaRetryNode = (node) => {
|
|
|
961
711
|
return event;
|
|
962
712
|
};
|
|
963
713
|
exports.decodeMediaRetryNode = decodeMediaRetryNode;
|
|
964
|
-
const decryptMediaRetryData =
|
|
965
|
-
const retryKey =
|
|
714
|
+
const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
715
|
+
const retryKey = getMediaRetryKey(mediaKey);
|
|
966
716
|
const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
|
|
967
717
|
return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
|
|
968
718
|
};
|
|
@@ -975,6 +725,7 @@ const MEDIA_RETRY_STATUS_MAP = {
|
|
|
975
725
|
[WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
976
726
|
[WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
|
|
977
727
|
};
|
|
728
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
978
729
|
function __importStar(arg0) {
|
|
979
730
|
throw new Error('Function not implemented.');
|
|
980
|
-
}
|
|
731
|
+
}
|