supunmd-bail 2.1.1 → 2.1.4
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 +1 -1
- package/README.md +428 -0
- package/WAProto/index.js +130529 -45236
- package/engine-requirements.js +1 -1
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/index.d.ts +9 -18
- package/lib/Defaults/index.js +136 -104
- package/lib/Defaults/phonenumber-mcc.json +223 -0
- package/lib/Signal/Group/ciphertext-message.d.ts +0 -1
- package/lib/Signal/Group/ciphertext-message.js +5 -2
- package/lib/Signal/Group/group-session-builder.d.ts +3 -4
- package/lib/Signal/Group/group-session-builder.js +41 -7
- package/lib/Signal/Group/group_cipher.d.ts +4 -4
- package/lib/Signal/Group/group_cipher.js +51 -37
- package/lib/Signal/Group/index.d.ts +11 -12
- package/lib/Signal/Group/index.js +57 -12
- package/lib/Signal/Group/keyhelper.d.ts +1 -2
- package/lib/Signal/Group/keyhelper.js +44 -7
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +5 -2
- package/lib/Signal/Group/sender-chain-key.d.ts +2 -3
- package/lib/Signal/Group/sender-chain-key.js +15 -7
- package/lib/Signal/Group/sender-key-distribution-message.d.ts +1 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +11 -8
- package/lib/Signal/Group/sender-key-message.d.ts +1 -2
- package/lib/Signal/Group/sender-key-message.js +12 -9
- package/lib/Signal/Group/sender-key-name.d.ts +0 -1
- package/lib/Signal/Group/sender-key-name.js +5 -2
- package/lib/Signal/Group/sender-key-record.d.ts +2 -3
- package/lib/Signal/Group/sender-key-record.js +21 -9
- package/lib/Signal/Group/sender-key-state.d.ts +6 -7
- package/lib/Signal/Group/sender-key-state.js +42 -27
- package/lib/Signal/Group/sender-message-key.d.ts +0 -1
- package/lib/Signal/Group/sender-message-key.js +7 -4
- package/lib/Signal/libsignal.d.ts +3 -5
- package/lib/Signal/libsignal.js +90 -258
- package/lib/Socket/Client/{types.d.ts → abstract-socket-client.d.ts} +3 -2
- package/lib/Socket/Client/abstract-socket-client.js +13 -0
- package/lib/Socket/Client/index.d.ts +3 -3
- package/lib/Socket/Client/index.js +19 -3
- 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/web-socket-client.js +62 -0
- package/lib/Socket/business.d.ts +108 -125
- package/lib/Socket/business.js +43 -159
- package/lib/Socket/chats.d.ts +239 -70
- package/lib/Socket/chats.js +384 -363
- package/lib/Socket/dugong.d.ts +254 -0
- package/lib/Socket/dugong.js +484 -0
- package/lib/Socket/groups.d.ts +56 -78
- package/lib/Socket/groups.js +96 -106
- package/lib/Socket/index.d.ts +115 -173
- package/lib/Socket/index.js +10 -17
- package/lib/Socket/messages-recv.d.ts +79 -91
- package/lib/Socket/messages-recv.js +521 -639
- package/lib/Socket/messages-send.d.ts +91 -111
- package/lib/Socket/messages-send.js +438 -599
- package/lib/Socket/newsletter.d.ts +84 -97
- package/lib/Socket/newsletter.js +239 -145
- package/lib/Socket/registration.d.ts +267 -0
- package/lib/Socket/registration.js +166 -0
- package/lib/Socket/socket.d.ts +18 -26
- package/lib/Socket/socket.js +230 -448
- package/lib/Socket/usync.d.ts +16 -17
- package/lib/Socket/usync.js +26 -19
- package/lib/Store/index.d.ts +3 -0
- package/lib/Store/index.js +10 -0
- 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 +118 -0
- package/lib/Store/make-in-memory-store.js +427 -0
- package/lib/Store/make-ordered-dictionary.d.ts +13 -0
- package/lib/Store/make-ordered-dictionary.js +81 -0
- package/lib/Store/object-repository.d.ts +10 -0
- package/lib/Store/object-repository.js +27 -0
- package/lib/Types/Auth.d.ts +12 -13
- package/lib/Types/Auth.js +2 -2
- package/lib/Types/Call.d.ts +1 -2
- package/lib/Types/Call.js +2 -2
- package/lib/Types/Chat.d.ts +13 -34
- package/lib/Types/Chat.js +4 -8
- package/lib/Types/Contact.d.ts +1 -6
- package/lib/Types/Contact.js +2 -2
- package/lib/Types/Events.d.ts +15 -60
- package/lib/Types/Events.js +2 -2
- package/lib/Types/GroupMetadata.d.ts +5 -17
- package/lib/Types/GroupMetadata.js +2 -2
- package/lib/Types/Label.d.ts +0 -12
- package/lib/Types/Label.js +5 -3
- package/lib/Types/LabelAssociation.d.ts +0 -1
- package/lib/Types/LabelAssociation.js +5 -3
- package/lib/Types/Message.d.ts +54 -84
- package/lib/Types/Message.js +9 -11
- package/lib/Types/Newsletter.d.ts +98 -130
- package/lib/Types/Newsletter.js +38 -31
- package/lib/Types/Product.d.ts +1 -2
- package/lib/Types/Product.js +2 -2
- package/lib/Types/Signal.d.ts +1 -20
- package/lib/Types/Signal.js +2 -2
- package/lib/Types/Socket.d.ts +25 -47
- package/lib/Types/Socket.js +2 -3
- package/lib/Types/State.d.ts +2 -14
- package/lib/Types/State.js +2 -13
- package/lib/Types/USync.d.ts +2 -3
- package/lib/Types/USync.js +2 -2
- package/lib/Types/index.d.ts +14 -22
- package/lib/Types/index.js +31 -15
- package/lib/Utils/auth-utils.d.ts +6 -7
- package/lib/Utils/auth-utils.js +148 -199
- package/lib/Utils/baileys-event-stream.d.ts +1 -2
- package/lib/Utils/baileys-event-stream.js +22 -15
- package/lib/Utils/business.d.ts +2 -3
- package/lib/Utils/business.js +69 -66
- package/lib/Utils/chat-utils.d.ts +22 -21
- package/lib/Utils/chat-utils.js +226 -260
- package/lib/Utils/crypto.d.ts +19 -19
- package/lib/Utils/crypto.js +86 -77
- package/lib/Utils/decode-wa-message.d.ts +8 -37
- package/lib/Utils/decode-wa-message.js +83 -164
- package/lib/Utils/event-buffer.d.ts +8 -7
- package/lib/Utils/event-buffer.js +76 -110
- package/lib/Utils/generics.d.ts +29 -27
- package/lib/Utils/generics.js +210 -168
- package/lib/Utils/history.d.ts +8 -12
- package/lib/Utils/history.js +46 -34
- package/lib/Utils/index.d.ts +17 -20
- package/lib/Utils/index.js +33 -20
- package/lib/Utils/link-preview.d.ts +5 -5
- package/lib/Utils/link-preview.js +22 -14
- package/lib/Utils/logger.d.ts +3 -11
- package/lib/Utils/logger.js +7 -3
- package/lib/Utils/lt-hash.d.ts +8 -9
- package/lib/Utils/lt-hash.js +28 -25
- package/lib/Utils/make-mutex.d.ts +2 -3
- package/lib/Utils/make-mutex.js +10 -7
- package/lib/Utils/messages-media.d.ts +44 -42
- package/lib/Utils/messages-media.js +475 -319
- package/lib/Utils/messages.d.ts +18 -17
- package/lib/Utils/messages.js +259 -383
- package/lib/Utils/noise-handler.d.ts +15 -14
- package/lib/Utils/noise-handler.js +38 -30
- package/lib/Utils/process-message.d.ts +13 -14
- package/lib/Utils/process-message.js +147 -239
- package/lib/Utils/signal.d.ts +5 -7
- package/lib/Utils/signal.js +72 -78
- package/lib/Utils/use-multi-file-auth-state.d.ts +2 -2
- package/lib/Utils/use-multi-file-auth-state.js +27 -29
- package/lib/Utils/validate-connection.d.ts +7 -7
- package/lib/Utils/validate-connection.js +106 -72
- package/lib/WABinary/constants.d.ts +27 -25
- package/lib/WABinary/constants.js +20 -1281
- package/lib/WABinary/decode.d.ts +5 -5
- package/lib/WABinary/decode.js +42 -28
- package/lib/WABinary/encode.d.ts +3 -3
- package/lib/WABinary/encode.js +154 -105
- package/lib/WABinary/generic-utils.d.ts +7 -5
- package/lib/WABinary/generic-utils.js +63 -56
- package/lib/WABinary/index.d.ts +5 -6
- package/lib/WABinary/index.js +21 -6
- package/lib/WABinary/jid-utils.d.ts +8 -25
- package/lib/WABinary/jid-utils.js +40 -74
- package/lib/WABinary/types.d.ts +1 -2
- package/lib/WABinary/types.js +2 -2
- package/lib/WAM/BinaryInfo.d.ts +11 -3
- package/lib/WAM/BinaryInfo.js +5 -2
- package/lib/WAM/constants.d.ts +3 -5
- package/lib/WAM/constants.js +11958 -19461
- package/lib/WAM/encode.d.ts +3 -3
- package/lib/WAM/encode.js +22 -17
- package/lib/WAM/index.d.ts +3 -4
- package/lib/WAM/index.js +19 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +3 -4
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +11 -8
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +2 -3
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +14 -11
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +2 -3
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +12 -9
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +2 -3
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +13 -9
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +3 -4
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +22 -20
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +3 -5
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +8 -13
- package/lib/WAUSync/Protocols/index.d.ts +4 -5
- package/lib/WAUSync/Protocols/index.js +20 -5
- package/lib/WAUSync/USyncQuery.d.ts +4 -5
- package/lib/WAUSync/USyncQuery.js +35 -40
- package/lib/WAUSync/USyncUser.d.ts +5 -6
- package/lib/WAUSync/USyncUser.js +5 -2
- package/lib/WAUSync/index.d.ts +3 -4
- package/lib/WAUSync/index.js +19 -4
- package/lib/index.d.ts +9 -19
- package/lib/index.js +47 -24
- package/package.json +109 -99
- package/WAProto/GenerateStatics.sh +0 -3
- package/WAProto/WAProto.proto +0 -5519
- package/WAProto/fix-imports.js +0 -29
- package/WAProto/index.d.ts +0 -11969
- package/lib/Signal/lid-mapping.d.ts +0 -23
- package/lib/Signal/lid-mapping.js +0 -171
- package/lib/Socket/Client/types.js +0 -11
- package/lib/Socket/Client/websocket.js +0 -50
- package/lib/Socket/communities.d.ts +0 -244
- package/lib/Socket/communities.js +0 -431
- package/lib/Socket/mex.d.ts +0 -3
- package/lib/Socket/mex.js +0 -42
- package/lib/Types/Bussines.d.ts +0 -25
- package/lib/Types/Bussines.js +0 -2
- package/lib/Utils/browser-utils.d.ts +0 -4
- package/lib/Utils/browser-utils.js +0 -28
- package/lib/Utils/message-retry-manager.d.ts +0 -82
- package/lib/Utils/message-retry-manager.js +0 -149
- package/lib/Utils/pre-key-manager.d.ts +0 -28
- package/lib/Utils/pre-key-manager.js +0 -106
- /package/lib/{supun → supunmd} +0 -0
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
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;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
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;
|
|
27
|
+
const boom_1 = require("@hapi/boom");
|
|
28
|
+
const child_process_1 = require("child_process");
|
|
29
|
+
const Crypto = __importStar(require("crypto"));
|
|
30
|
+
const events_1 = require("events");
|
|
31
|
+
const fs_1 = require("fs");
|
|
32
|
+
const os_1 = require("os");
|
|
33
|
+
const path_1 = require("path");
|
|
34
|
+
const stream_1 = require("stream");
|
|
35
|
+
const WAProto_1 = require("../../WAProto");
|
|
36
|
+
const Defaults_1 = require("../Defaults");
|
|
37
|
+
const WABinary_1 = require("../WABinary");
|
|
38
|
+
const crypto_1 = require("./crypto");
|
|
39
|
+
const generics_1 = require("./generics");
|
|
40
|
+
const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
|
|
16
41
|
const getImageProcessingLibrary = async () => {
|
|
17
|
-
|
|
18
|
-
|
|
42
|
+
const [_jimp, sharp] = await Promise.all([
|
|
43
|
+
(async () => {
|
|
44
|
+
const jimp = await (import('jimp')
|
|
45
|
+
.catch(() => { }));
|
|
46
|
+
return jimp;
|
|
47
|
+
})(),
|
|
48
|
+
(async () => {
|
|
49
|
+
const sharp = await (import('sharp')
|
|
50
|
+
.catch(() => { }));
|
|
51
|
+
return sharp;
|
|
52
|
+
})()
|
|
53
|
+
]);
|
|
19
54
|
if (sharp) {
|
|
20
55
|
return { sharp };
|
|
21
56
|
}
|
|
57
|
+
const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
|
|
22
58
|
if (jimp) {
|
|
23
59
|
return { jimp };
|
|
24
60
|
}
|
|
25
|
-
throw new Boom('No image processing library available');
|
|
61
|
+
throw new boom_1.Boom('No image processing library available');
|
|
26
62
|
};
|
|
27
|
-
|
|
28
|
-
const hkdfInfo = MEDIA_HKDF_KEY_MAPPING[type];
|
|
63
|
+
const hkdfInfoKey = (type) => {
|
|
64
|
+
const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type];
|
|
29
65
|
return `WhatsApp ${hkdfInfo} Keys`;
|
|
30
66
|
};
|
|
31
|
-
|
|
32
|
-
const { stream } = await getStream(media);
|
|
33
|
-
logger?.debug('got stream for raw upload');
|
|
34
|
-
const hasher = Crypto.createHash('sha256');
|
|
35
|
-
const filePath = join(tmpdir(), mediaType + generateMessageIDV2());
|
|
36
|
-
const fileWriteStream = createWriteStream(filePath);
|
|
37
|
-
let fileLength = 0;
|
|
38
|
-
try {
|
|
39
|
-
for await (const data of stream) {
|
|
40
|
-
fileLength += data.length;
|
|
41
|
-
hasher.update(data);
|
|
42
|
-
if (!fileWriteStream.write(data)) {
|
|
43
|
-
await once(fileWriteStream, 'drain');
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
fileWriteStream.end();
|
|
47
|
-
await once(fileWriteStream, 'finish');
|
|
48
|
-
stream.destroy();
|
|
49
|
-
const fileSha256 = hasher.digest();
|
|
50
|
-
logger?.debug('hashed data for raw upload');
|
|
51
|
-
return {
|
|
52
|
-
filePath: filePath,
|
|
53
|
-
fileSha256,
|
|
54
|
-
fileLength
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
fileWriteStream.destroy();
|
|
59
|
-
stream.destroy();
|
|
60
|
-
try {
|
|
61
|
-
await fs.unlink(filePath);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
//
|
|
65
|
-
}
|
|
66
|
-
throw error;
|
|
67
|
-
}
|
|
68
|
-
};
|
|
67
|
+
exports.hkdfInfoKey = hkdfInfoKey;
|
|
69
68
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
70
|
-
|
|
69
|
+
function getMediaKeys(buffer, mediaType) {
|
|
71
70
|
if (!buffer) {
|
|
72
|
-
throw new Boom('Cannot derive from empty media key');
|
|
71
|
+
throw new boom_1.Boom('Cannot derive from empty media key');
|
|
73
72
|
}
|
|
74
73
|
if (typeof buffer === 'string') {
|
|
75
74
|
buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
|
|
76
75
|
}
|
|
77
76
|
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
|
78
|
-
const expandedMediaKey =
|
|
77
|
+
const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
79
78
|
return {
|
|
80
79
|
iv: expandedMediaKey.slice(0, 16),
|
|
81
80
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
82
|
-
macKey: expandedMediaKey.slice(48, 80)
|
|
81
|
+
macKey: expandedMediaKey.slice(48, 80),
|
|
83
82
|
};
|
|
84
83
|
}
|
|
84
|
+
exports.getMediaKeys = getMediaKeys;
|
|
85
85
|
/** Extracts video thumb using FFMPEG */
|
|
86
86
|
const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
|
|
87
87
|
const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
|
|
88
|
-
exec(cmd, err => {
|
|
88
|
+
(0, child_process_1.exec)(cmd, (err) => {
|
|
89
89
|
if (err) {
|
|
90
90
|
reject(err);
|
|
91
91
|
}
|
|
@@ -94,151 +94,240 @@ const extractVideoThumb = async (path, destPath, time, size) => new Promise((res
|
|
|
94
94
|
}
|
|
95
95
|
});
|
|
96
96
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
bufferOrFilePath = await toBuffer(bufferOrFilePath);
|
|
97
|
+
const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
98
|
+
var _a, _b;
|
|
99
|
+
if (bufferOrFilePath instanceof stream_1.Readable) {
|
|
100
|
+
bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
|
|
102
101
|
}
|
|
103
102
|
const lib = await getImageProcessingLibrary();
|
|
104
|
-
if ('sharp' in lib && typeof lib.sharp
|
|
103
|
+
if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
|
|
105
104
|
const img = lib.sharp.default(bufferOrFilePath);
|
|
106
105
|
const dimensions = await img.metadata();
|
|
107
|
-
const buffer = await img
|
|
106
|
+
const buffer = await img
|
|
107
|
+
.resize(width)
|
|
108
|
+
.jpeg({ quality: 50 })
|
|
109
|
+
.toBuffer();
|
|
108
110
|
return {
|
|
109
111
|
buffer,
|
|
110
112
|
original: {
|
|
111
113
|
width: dimensions.width,
|
|
112
|
-
height: dimensions.height
|
|
113
|
-
}
|
|
114
|
+
height: dimensions.height,
|
|
115
|
+
},
|
|
114
116
|
};
|
|
115
117
|
}
|
|
116
|
-
else if ('jimp' in lib && typeof lib.jimp
|
|
117
|
-
const
|
|
118
|
+
else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
|
|
119
|
+
const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp;
|
|
120
|
+
const jimp = await read(bufferOrFilePath);
|
|
118
121
|
const dimensions = {
|
|
119
|
-
width: jimp.
|
|
120
|
-
height: jimp.
|
|
122
|
+
width: jimp.getWidth(),
|
|
123
|
+
height: jimp.getHeight()
|
|
121
124
|
};
|
|
122
125
|
const buffer = await jimp
|
|
123
|
-
.
|
|
124
|
-
.
|
|
126
|
+
.quality(50)
|
|
127
|
+
.resize(width, AUTO, RESIZE_BILINEAR)
|
|
128
|
+
.getBufferAsync(MIME_JPEG);
|
|
125
129
|
return {
|
|
126
130
|
buffer,
|
|
127
131
|
original: dimensions
|
|
128
132
|
};
|
|
129
133
|
}
|
|
130
134
|
else {
|
|
131
|
-
throw new Boom('No image processing library available');
|
|
135
|
+
throw new boom_1.Boom('No image processing library available');
|
|
132
136
|
}
|
|
133
137
|
};
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
+
exports.extractImageThumb = extractImageThumb;
|
|
139
|
+
const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
|
|
140
|
+
.replace(/\+/g, '-')
|
|
141
|
+
.replace(/\//g, '_')
|
|
142
|
+
.replace(/\=+$/, '')));
|
|
143
|
+
exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
|
|
144
|
+
const generateProfilePicture = async (mediaUpload) => {
|
|
145
|
+
var _a, _b;
|
|
146
|
+
let bufferOrFilePath;
|
|
138
147
|
if (Buffer.isBuffer(mediaUpload)) {
|
|
139
|
-
|
|
148
|
+
bufferOrFilePath = mediaUpload;
|
|
149
|
+
}
|
|
150
|
+
else if ('url' in mediaUpload) {
|
|
151
|
+
bufferOrFilePath = mediaUpload.url.toString();
|
|
140
152
|
}
|
|
141
153
|
else {
|
|
142
|
-
|
|
143
|
-
const { stream } = await getStream(mediaUpload);
|
|
144
|
-
// Convert the resulting stream to a buffer
|
|
145
|
-
buffer = await toBuffer(stream);
|
|
154
|
+
bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
|
|
146
155
|
}
|
|
147
156
|
const lib = await getImageProcessingLibrary();
|
|
148
157
|
let img;
|
|
149
|
-
if ('sharp' in lib && typeof lib.sharp
|
|
150
|
-
img = lib.sharp
|
|
151
|
-
.
|
|
152
|
-
.resize(w, h)
|
|
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)
|
|
153
161
|
.jpeg({
|
|
154
|
-
quality: 50
|
|
162
|
+
quality: 50,
|
|
155
163
|
})
|
|
156
164
|
.toBuffer();
|
|
157
165
|
}
|
|
158
|
-
else if ('jimp' in lib && typeof lib.jimp
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
|
|
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);
|
|
163
175
|
}
|
|
164
176
|
else {
|
|
165
|
-
throw new Boom('No image processing library available');
|
|
177
|
+
throw new boom_1.Boom('No image processing library available');
|
|
166
178
|
}
|
|
167
179
|
return {
|
|
168
|
-
img: await img
|
|
180
|
+
img: await img,
|
|
169
181
|
};
|
|
170
182
|
};
|
|
183
|
+
exports.generateProfilePicture = generateProfilePicture;
|
|
171
184
|
/** gets the SHA256 of the given media message */
|
|
172
|
-
|
|
185
|
+
const mediaMessageSHA256B64 = (message) => {
|
|
173
186
|
const media = Object.values(message)[0];
|
|
174
|
-
return media
|
|
187
|
+
return (media === null || media === void 0 ? void 0 : media.fileSha256) && Buffer.from(media.fileSha256).toString('base64');
|
|
175
188
|
};
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
189
|
+
exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
|
|
190
|
+
async function getAudioDuration(buffer) {
|
|
191
|
+
try {
|
|
192
|
+
const { PassThrough } = require('stream');
|
|
193
|
+
const ff = require('fluent-ffmpeg');
|
|
194
|
+
|
|
195
|
+
return await new Promise((resolve, reject) => {
|
|
196
|
+
const inputStream = new PassThrough();
|
|
197
|
+
inputStream.end(buffer);
|
|
198
|
+
|
|
199
|
+
ff(inputStream)
|
|
200
|
+
.ffprobe((err, data) => {
|
|
201
|
+
if (err) reject(err);
|
|
202
|
+
else resolve(data.format.duration);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
} catch (error) {
|
|
206
|
+
const musicMetadata = await import('music-metadata');
|
|
207
|
+
let metadata;
|
|
208
|
+
if (Buffer.isBuffer(buffer)) {
|
|
209
|
+
metadata = await musicMetadata.parseBuffer(buffer, undefined, {
|
|
210
|
+
duration: true
|
|
211
|
+
});
|
|
212
|
+
} else if (typeof buffer === 'string') {
|
|
213
|
+
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
214
|
+
try {
|
|
215
|
+
metadata = await musicMetadata.parseStream(rStream, undefined, {
|
|
216
|
+
duration: true
|
|
217
|
+
});
|
|
218
|
+
} finally {
|
|
219
|
+
rStream.destroy();
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
metadata = await musicMetadata.parseStream(buffer, undefined, {
|
|
223
|
+
duration: true
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return metadata.format.duration;
|
|
190
227
|
}
|
|
191
|
-
return metadata.format.duration;
|
|
192
228
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
*/
|
|
196
|
-
export async function getAudioWaveform(buffer, logger) {
|
|
229
|
+
exports.getAudioDuration = getAudioDuration;
|
|
230
|
+
async function getAudioWaveform(buffer, logger) {
|
|
197
231
|
try {
|
|
198
|
-
|
|
199
|
-
const
|
|
232
|
+
const { PassThrough } = require('stream');
|
|
233
|
+
const ff = require('fluent-ffmpeg');
|
|
234
|
+
|
|
200
235
|
let audioData;
|
|
201
236
|
if (Buffer.isBuffer(buffer)) {
|
|
202
237
|
audioData = buffer;
|
|
238
|
+
} else if (typeof buffer === 'string') {
|
|
239
|
+
const rStream = require('fs').createReadStream(buffer);
|
|
240
|
+
audioData = await exports.toBuffer(rStream);
|
|
241
|
+
} else {
|
|
242
|
+
audioData = await exports.toBuffer(buffer);
|
|
203
243
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
244
|
+
|
|
245
|
+
return await new Promise((resolve, reject) => {
|
|
246
|
+
const inputStream = new PassThrough();
|
|
247
|
+
inputStream.end(audioData);
|
|
248
|
+
const chunks = [];
|
|
249
|
+
const bars = 64;
|
|
250
|
+
|
|
251
|
+
ff(inputStream)
|
|
252
|
+
.audioChannels(1)
|
|
253
|
+
.audioFrequency(16000)
|
|
254
|
+
.format('s16le')
|
|
255
|
+
.on('error', reject)
|
|
256
|
+
.on('end', () => {
|
|
257
|
+
const rawData = Buffer.concat(chunks);
|
|
258
|
+
const samples = rawData.length / 2;
|
|
259
|
+
const amplitudes = [];
|
|
260
|
+
|
|
261
|
+
for (let i = 0; i < samples; i++) {
|
|
262
|
+
amplitudes.push(Math.abs(rawData.readInt16LE(i * 2)) / 32768);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const blockSize = Math.floor(amplitudes.length / bars);
|
|
266
|
+
const avg = [];
|
|
267
|
+
for (let i = 0; i < bars; i++) {
|
|
268
|
+
const block = amplitudes.slice(i * blockSize, (i + 1) * blockSize);
|
|
269
|
+
avg.push(block.reduce((a, b) => a + b, 0) / block.length);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const max = Math.max(...avg);
|
|
273
|
+
const normalized = avg.map(v => Math.floor((v / max) * 100));
|
|
274
|
+
resolve(new Uint8Array(normalized));
|
|
275
|
+
})
|
|
276
|
+
.pipe()
|
|
277
|
+
.on('data', chunk => chunks.push(chunk));
|
|
278
|
+
});
|
|
279
|
+
} catch (e) {
|
|
280
|
+
logger?.debug(e);
|
|
230
281
|
}
|
|
231
|
-
|
|
232
|
-
|
|
282
|
+
}
|
|
283
|
+
exports.getAudioWaveform = getAudioWaveform;
|
|
284
|
+
async function convertToOpusBuffer(buffer, logger) {
|
|
285
|
+
try {
|
|
286
|
+
const { PassThrough } = require('stream');
|
|
287
|
+
const ff = require('fluent-ffmpeg');
|
|
288
|
+
|
|
289
|
+
return await new Promise((resolve, reject) => {
|
|
290
|
+
const inStream = new PassThrough();
|
|
291
|
+
const outStream = new PassThrough();
|
|
292
|
+
const chunks = [];
|
|
293
|
+
inStream.end(buffer);
|
|
294
|
+
|
|
295
|
+
ff(inStream)
|
|
296
|
+
.noVideo()
|
|
297
|
+
.audioCodec('libopus')
|
|
298
|
+
.format('ogg')
|
|
299
|
+
.audioBitrate('48k')
|
|
300
|
+
.audioChannels(1)
|
|
301
|
+
.audioFrequency(48000)
|
|
302
|
+
.outputOptions([
|
|
303
|
+
'-vn',
|
|
304
|
+
'-b:a 64k',
|
|
305
|
+
'-ac 2',
|
|
306
|
+
'-ar 48000',
|
|
307
|
+
'-map_metadata', '-1',
|
|
308
|
+
'-application', 'voip'
|
|
309
|
+
])
|
|
310
|
+
.on('error', reject)
|
|
311
|
+
.on('end', () => resolve(Buffer.concat(chunks)))
|
|
312
|
+
.pipe(outStream, {
|
|
313
|
+
end: true
|
|
314
|
+
});
|
|
315
|
+
outStream.on('data', c => chunks.push(c));
|
|
316
|
+
});
|
|
317
|
+
} catch (e) {
|
|
318
|
+
logger?.debug(e);
|
|
319
|
+
throw e;
|
|
233
320
|
}
|
|
234
321
|
}
|
|
235
|
-
|
|
236
|
-
|
|
322
|
+
exports.convertToOpusBuffer = convertToOpusBuffer;
|
|
323
|
+
const toReadable = (buffer) => {
|
|
324
|
+
const readable = new stream_1.Readable({ read: () => { } });
|
|
237
325
|
readable.push(buffer);
|
|
238
326
|
readable.push(null);
|
|
239
327
|
return readable;
|
|
240
328
|
};
|
|
241
|
-
|
|
329
|
+
exports.toReadable = toReadable;
|
|
330
|
+
const toBuffer = async (stream) => {
|
|
242
331
|
const chunks = [];
|
|
243
332
|
for await (const chunk of stream) {
|
|
244
333
|
chunks.push(chunk);
|
|
@@ -246,47 +335,45 @@ export const toBuffer = async (stream) => {
|
|
|
246
335
|
stream.destroy();
|
|
247
336
|
return Buffer.concat(chunks);
|
|
248
337
|
};
|
|
249
|
-
|
|
338
|
+
exports.toBuffer = toBuffer;
|
|
339
|
+
const getStream = async (item, opts) => {
|
|
250
340
|
if (Buffer.isBuffer(item)) {
|
|
251
|
-
return { stream: toReadable(item), type: 'buffer' };
|
|
341
|
+
return { stream: (0, exports.toReadable)(item), type: 'buffer' };
|
|
252
342
|
}
|
|
253
343
|
if ('stream' in item) {
|
|
254
344
|
return { stream: item.stream, type: 'readable' };
|
|
255
345
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
|
|
259
|
-
return { stream: toReadable(buffer), type: 'buffer' };
|
|
260
|
-
}
|
|
261
|
-
if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
|
|
262
|
-
return { stream: await getHttpStream(item.url, opts), type: 'remote' };
|
|
346
|
+
if (item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) {
|
|
347
|
+
return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' };
|
|
263
348
|
}
|
|
264
|
-
return { stream: createReadStream(item.url), type: 'file' };
|
|
349
|
+
return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' };
|
|
265
350
|
};
|
|
351
|
+
exports.getStream = getStream;
|
|
266
352
|
/** generates a thumbnail for a given media, if required */
|
|
267
|
-
|
|
353
|
+
async function generateThumbnail(file, mediaType, options) {
|
|
354
|
+
var _a;
|
|
268
355
|
let thumbnail;
|
|
269
356
|
let originalImageDimensions;
|
|
270
357
|
if (mediaType === 'image') {
|
|
271
|
-
const { buffer, original } = await extractImageThumb(file);
|
|
358
|
+
const { buffer, original } = await (0, exports.extractImageThumb)(file);
|
|
272
359
|
thumbnail = buffer.toString('base64');
|
|
273
360
|
if (original.width && original.height) {
|
|
274
361
|
originalImageDimensions = {
|
|
275
362
|
width: original.width,
|
|
276
|
-
height: original.height
|
|
363
|
+
height: original.height,
|
|
277
364
|
};
|
|
278
365
|
}
|
|
279
366
|
}
|
|
280
367
|
else if (mediaType === 'video') {
|
|
281
|
-
const imgFilename = join(getTmpFilesDirectory(),
|
|
368
|
+
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
|
|
282
369
|
try {
|
|
283
370
|
await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
|
|
284
|
-
const buff = await
|
|
371
|
+
const buff = await fs_1.promises.readFile(imgFilename);
|
|
285
372
|
thumbnail = buff.toString('base64');
|
|
286
|
-
await
|
|
373
|
+
await fs_1.promises.unlink(imgFilename);
|
|
287
374
|
}
|
|
288
375
|
catch (err) {
|
|
289
|
-
options.logger
|
|
376
|
+
(_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
|
|
290
377
|
}
|
|
291
378
|
}
|
|
292
379
|
return {
|
|
@@ -294,120 +381,180 @@ export async function generateThumbnail(file, mediaType, options) {
|
|
|
294
381
|
originalImageDimensions
|
|
295
382
|
};
|
|
296
383
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
384
|
+
exports.generateThumbnail = generateThumbnail;
|
|
385
|
+
const getHttpStream = async (url, options = {}) => {
|
|
386
|
+
const { default: axios } = await import('axios');
|
|
387
|
+
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
|
|
388
|
+
return fetched.data;
|
|
389
|
+
};
|
|
390
|
+
exports.getHttpStream = getHttpStream;
|
|
391
|
+
const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
392
|
+
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
393
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
|
|
394
|
+
let bodyPath;
|
|
395
|
+
let didSaveToTmpPath = false;
|
|
396
|
+
try {
|
|
397
|
+
const buffer = await (0, exports.toBuffer)(stream);
|
|
398
|
+
if (type === 'file') {
|
|
399
|
+
bodyPath = media.url;
|
|
400
|
+
}
|
|
401
|
+
else if (saveOriginalFileIfRequired) {
|
|
402
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
403
|
+
(0, fs_1.writeFileSync)(bodyPath, buffer);
|
|
404
|
+
didSaveToTmpPath = true;
|
|
405
|
+
}
|
|
406
|
+
const fileLength = buffer.length;
|
|
407
|
+
const fileSha256 = Crypto.createHash('sha256').update(buffer).digest();
|
|
408
|
+
stream === null || stream === void 0 ? void 0 : stream.destroy();
|
|
409
|
+
logger === null || logger === void 0 ? void 0 : logger.debug('prepare stream data successfully');
|
|
410
|
+
return {
|
|
411
|
+
mediaKey: undefined,
|
|
412
|
+
encWriteStream: buffer,
|
|
413
|
+
fileLength,
|
|
414
|
+
fileSha256,
|
|
415
|
+
fileEncSha256: undefined,
|
|
416
|
+
bodyPath,
|
|
417
|
+
didSaveToTmpPath
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
// destroy all streams with error
|
|
422
|
+
stream.destroy();
|
|
423
|
+
if (didSaveToTmpPath) {
|
|
424
|
+
try {
|
|
425
|
+
await fs_1.promises.unlink(bodyPath);
|
|
426
|
+
}
|
|
427
|
+
catch (err) {
|
|
428
|
+
logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
throw error;
|
|
305
432
|
}
|
|
306
|
-
// @ts-ignore Node18+ Readable.fromWeb exists
|
|
307
|
-
return Readable.fromWeb(response.body);
|
|
308
433
|
};
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
434
|
+
exports.prepareStream = prepareStream;
|
|
435
|
+
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
|
|
436
|
+
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
437
|
+
|
|
438
|
+
let finalStream = stream;
|
|
439
|
+
if (mediaType === 'audio' && (isPtt === true || forceOpus === true)) {
|
|
440
|
+
try {
|
|
441
|
+
const buffer = await (0, exports.toBuffer)(stream);
|
|
442
|
+
const opusBuffer = await exports.convertToOpusBuffer(buffer, logger);
|
|
443
|
+
finalStream = (0, exports.toReadable)(opusBuffer);
|
|
444
|
+
} catch (error) {
|
|
445
|
+
const { stream: newStream } = await (0, exports.getStream)(media, opts);
|
|
446
|
+
finalStream = newStream;
|
|
447
|
+
}
|
|
321
448
|
}
|
|
449
|
+
|
|
450
|
+
const mediaKey = Crypto.randomBytes(32);
|
|
451
|
+
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
|
|
452
|
+
const encWriteStream = new stream_1.Readable({ read: () => { } });
|
|
453
|
+
let bodyPath;
|
|
454
|
+
let writeStream;
|
|
455
|
+
let didSaveToTmpPath = false;
|
|
456
|
+
|
|
457
|
+
if (type === 'file') {
|
|
458
|
+
bodyPath = media.url;
|
|
459
|
+
}
|
|
460
|
+
else if (saveOriginalFileIfRequired) {
|
|
461
|
+
bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
|
|
462
|
+
writeStream = (0, fs_1.createWriteStream)(bodyPath);
|
|
463
|
+
didSaveToTmpPath = true;
|
|
464
|
+
}
|
|
465
|
+
|
|
322
466
|
let fileLength = 0;
|
|
323
467
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
sha256Enc.update(buff);
|
|
329
|
-
hmac.update(buff);
|
|
330
|
-
encFileWriteStream.write(buff);
|
|
331
|
-
};
|
|
468
|
+
let hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
469
|
+
let sha256Plain = Crypto.createHash('sha256');
|
|
470
|
+
let sha256Enc = Crypto.createHash('sha256');
|
|
471
|
+
|
|
332
472
|
try {
|
|
333
|
-
for await (const data of
|
|
473
|
+
for await (const data of finalStream) {
|
|
334
474
|
fileLength += data.length;
|
|
335
|
-
if (type === 'remote'
|
|
336
|
-
opts
|
|
337
|
-
fileLength + data.length > opts.maxContentLength) {
|
|
338
|
-
throw new Boom(`content length exceeded when encrypting "${type}"`, {
|
|
475
|
+
if (type === 'remote'
|
|
476
|
+
&& (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
|
|
477
|
+
&& fileLength + data.length > opts.maxContentLength) {
|
|
478
|
+
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
|
|
339
479
|
data: { media, type }
|
|
340
480
|
});
|
|
341
481
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
482
|
+
|
|
483
|
+
sha256Plain = sha256Plain.update(data);
|
|
484
|
+
if (writeStream) {
|
|
485
|
+
if (!writeStream.write(data)) {
|
|
486
|
+
await (0, events_1.once)(writeStream, 'drain');
|
|
345
487
|
}
|
|
346
488
|
}
|
|
347
|
-
sha256Plain.update(data);
|
|
348
489
|
onChunk(aes.update(data));
|
|
349
490
|
}
|
|
491
|
+
|
|
350
492
|
onChunk(aes.final());
|
|
351
493
|
const mac = hmac.digest().slice(0, 10);
|
|
352
|
-
sha256Enc.update(mac);
|
|
494
|
+
sha256Enc = sha256Enc.update(mac);
|
|
353
495
|
const fileSha256 = sha256Plain.digest();
|
|
354
496
|
const fileEncSha256 = sha256Enc.digest();
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
497
|
+
|
|
498
|
+
encWriteStream.push(mac);
|
|
499
|
+
encWriteStream.push(null);
|
|
500
|
+
writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
|
|
501
|
+
finalStream.destroy();
|
|
502
|
+
|
|
360
503
|
return {
|
|
361
504
|
mediaKey,
|
|
362
|
-
|
|
363
|
-
|
|
505
|
+
encWriteStream,
|
|
506
|
+
bodyPath,
|
|
364
507
|
mac,
|
|
365
508
|
fileEncSha256,
|
|
366
509
|
fileSha256,
|
|
367
|
-
fileLength
|
|
510
|
+
fileLength,
|
|
511
|
+
didSaveToTmpPath
|
|
368
512
|
};
|
|
369
513
|
}
|
|
370
514
|
catch (error) {
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
originalFileStream?.destroy?.();
|
|
515
|
+
encWriteStream.destroy();
|
|
516
|
+
writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
|
|
374
517
|
aes.destroy();
|
|
375
518
|
hmac.destroy();
|
|
376
519
|
sha256Plain.destroy();
|
|
377
520
|
sha256Enc.destroy();
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
await
|
|
521
|
+
finalStream.destroy();
|
|
522
|
+
|
|
523
|
+
if (didSaveToTmpPath) {
|
|
524
|
+
try {
|
|
525
|
+
await fs_1.promises.unlink(bodyPath);
|
|
526
|
+
}
|
|
527
|
+
catch (err) {
|
|
383
528
|
}
|
|
384
|
-
}
|
|
385
|
-
catch (err) {
|
|
386
|
-
logger?.error({ err }, 'failed deleting tmp files');
|
|
387
529
|
}
|
|
388
530
|
throw error;
|
|
389
531
|
}
|
|
532
|
+
|
|
533
|
+
function onChunk(buff) {
|
|
534
|
+
sha256Enc = sha256Enc.update(buff);
|
|
535
|
+
hmac = hmac.update(buff);
|
|
536
|
+
encWriteStream.push(buff);
|
|
537
|
+
}
|
|
390
538
|
};
|
|
539
|
+
exports.encryptedStream = encryptedStream;
|
|
391
540
|
const DEF_HOST = 'mmg.whatsapp.net';
|
|
392
541
|
const AES_CHUNK_SIZE = 16;
|
|
393
542
|
const toSmallestChunkSize = (num) => {
|
|
394
543
|
return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
|
|
395
544
|
};
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
const downloadUrl =
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
403
|
-
const keys = await getMediaKeys(mediaKey, type);
|
|
404
|
-
return downloadEncryptedContent(downloadUrl, keys, opts);
|
|
545
|
+
const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
546
|
+
exports.getUrlFromDirectPath = getUrlFromDirectPath;
|
|
547
|
+
const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
548
|
+
const downloadUrl = url || (0, exports.getUrlFromDirectPath)(directPath);
|
|
549
|
+
const keys = getMediaKeys(mediaKey, type);
|
|
550
|
+
return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
|
|
405
551
|
};
|
|
552
|
+
exports.downloadContentFromMessage = downloadContentFromMessage;
|
|
406
553
|
/**
|
|
407
554
|
* Decrypts and downloads an AES256-CBC encrypted file given the keys.
|
|
408
555
|
* Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
|
|
409
556
|
* */
|
|
410
|
-
|
|
557
|
+
const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
|
|
411
558
|
let bytesFetched = 0;
|
|
412
559
|
let startChunk = 0;
|
|
413
560
|
let firstBlockIsIV = false;
|
|
@@ -421,14 +568,9 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
|
|
|
421
568
|
}
|
|
422
569
|
}
|
|
423
570
|
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
|
|
424
|
-
const headersInit = options?.headers ? options.headers : undefined;
|
|
425
571
|
const headers = {
|
|
426
|
-
...(
|
|
427
|
-
|
|
428
|
-
? Object.fromEntries(headersInit)
|
|
429
|
-
: headersInit
|
|
430
|
-
: {}),
|
|
431
|
-
Origin: DEFAULT_ORIGIN
|
|
572
|
+
...(options === null || options === void 0 ? void 0 : options.headers) || {},
|
|
573
|
+
Origin: Defaults_1.DEFAULT_ORIGIN,
|
|
432
574
|
};
|
|
433
575
|
if (startChunk || endChunk) {
|
|
434
576
|
headers.Range = `bytes=${startChunk}-`;
|
|
@@ -437,9 +579,11 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
|
|
|
437
579
|
}
|
|
438
580
|
}
|
|
439
581
|
// download the message
|
|
440
|
-
const fetched = await getHttpStream(downloadUrl, {
|
|
441
|
-
...
|
|
442
|
-
headers
|
|
582
|
+
const fetched = await (0, exports.getHttpStream)(downloadUrl, {
|
|
583
|
+
...options || {},
|
|
584
|
+
headers,
|
|
585
|
+
maxBodyLength: Infinity,
|
|
586
|
+
maxContentLength: Infinity,
|
|
443
587
|
});
|
|
444
588
|
let remainingBytes = Buffer.from([]);
|
|
445
589
|
let aes;
|
|
@@ -454,7 +598,7 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
|
|
|
454
598
|
push(bytes);
|
|
455
599
|
}
|
|
456
600
|
};
|
|
457
|
-
const output = new Transform({
|
|
601
|
+
const output = new stream_1.Transform({
|
|
458
602
|
transform(chunk, _, callback) {
|
|
459
603
|
let data = Buffer.concat([remainingBytes, chunk]);
|
|
460
604
|
const decryptLength = toSmallestChunkSize(data.length);
|
|
@@ -489,15 +633,18 @@ export const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, {
|
|
|
489
633
|
catch (error) {
|
|
490
634
|
callback(error);
|
|
491
635
|
}
|
|
492
|
-
}
|
|
636
|
+
},
|
|
493
637
|
});
|
|
494
638
|
return fetched.pipe(output, { end: true });
|
|
495
639
|
};
|
|
496
|
-
|
|
497
|
-
|
|
640
|
+
exports.downloadEncryptedContent = downloadEncryptedContent;
|
|
641
|
+
function extensionForMediaMessage(message) {
|
|
642
|
+
const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1];
|
|
498
643
|
const type = Object.keys(message)[0];
|
|
499
644
|
let extension;
|
|
500
|
-
if (type === 'locationMessage' ||
|
|
645
|
+
if (type === 'locationMessage' ||
|
|
646
|
+
type === 'liveLocationMessage' ||
|
|
647
|
+
type === 'productMessage') {
|
|
501
648
|
extension = '.jpeg';
|
|
502
649
|
}
|
|
503
650
|
else {
|
|
@@ -506,54 +653,55 @@ export function extensionForMediaMessage(message) {
|
|
|
506
653
|
}
|
|
507
654
|
return extension;
|
|
508
655
|
}
|
|
509
|
-
|
|
510
|
-
|
|
656
|
+
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
657
|
+
const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
658
|
+
return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
|
|
659
|
+
var _a, _b;
|
|
660
|
+
const { default: axios } = await import('axios');
|
|
511
661
|
// send a query JSON to obtain the url & auth token to upload our media
|
|
512
662
|
let uploadInfo = await refreshMediaConn(false);
|
|
513
663
|
let urls;
|
|
514
664
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
515
|
-
|
|
516
|
-
|
|
665
|
+
const chunks = [];
|
|
666
|
+
if (!Buffer.isBuffer(stream)) {
|
|
667
|
+
for await (const chunk of stream) {
|
|
668
|
+
chunks.push(chunk);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
const reqBody = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks);
|
|
672
|
+
fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
|
|
673
|
+
let media = Defaults_1.MEDIA_PATH_MAP[mediaType];
|
|
674
|
+
if (newsletter) {
|
|
675
|
+
media = media === null || media === void 0 ? void 0 : media.replace('/mms/', '/newsletter/newsletter-');
|
|
676
|
+
}
|
|
677
|
+
for (const { hostname, maxContentLengthBytes } of hosts) {
|
|
517
678
|
logger.debug(`uploading to "${hostname}"`);
|
|
518
679
|
const auth = encodeURIComponent(uploadInfo.auth); // the auth token
|
|
519
|
-
const url = `https://${hostname}${
|
|
520
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
680
|
+
const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
521
681
|
let result;
|
|
522
682
|
try {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
683
|
+
if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
|
|
684
|
+
throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
|
|
685
|
+
}
|
|
686
|
+
const body = await axios.post(url, reqBody, {
|
|
687
|
+
...options,
|
|
528
688
|
headers: {
|
|
529
|
-
...
|
|
530
|
-
const hdrs = options?.headers;
|
|
531
|
-
if (!hdrs)
|
|
532
|
-
return {};
|
|
533
|
-
return Array.isArray(hdrs) ? Object.fromEntries(hdrs) : hdrs;
|
|
534
|
-
})(),
|
|
689
|
+
...options.headers || {},
|
|
535
690
|
'Content-Type': 'application/octet-stream',
|
|
536
|
-
Origin: DEFAULT_ORIGIN
|
|
691
|
+
'Origin': Defaults_1.DEFAULT_ORIGIN
|
|
537
692
|
},
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
693
|
+
httpsAgent: fetchAgent,
|
|
694
|
+
timeout: timeoutMs,
|
|
695
|
+
responseType: 'json',
|
|
696
|
+
maxBodyLength: Infinity,
|
|
697
|
+
maxContentLength: Infinity,
|
|
541
698
|
});
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
parsed = await response.json();
|
|
545
|
-
}
|
|
546
|
-
catch {
|
|
547
|
-
parsed = undefined;
|
|
548
|
-
}
|
|
549
|
-
result = parsed;
|
|
550
|
-
if (result?.url || result?.directPath) {
|
|
699
|
+
result = body.data;
|
|
700
|
+
if ((result === null || result === void 0 ? void 0 : result.url) || (result === null || result === void 0 ? void 0 : result.directPath)) {
|
|
551
701
|
urls = {
|
|
552
702
|
mediaUrl: result.url,
|
|
553
703
|
directPath: result.direct_path,
|
|
554
|
-
|
|
555
|
-
fbid: result.fbid,
|
|
556
|
-
ts: result.ts
|
|
704
|
+
handle: result.handle
|
|
557
705
|
};
|
|
558
706
|
break;
|
|
559
707
|
}
|
|
@@ -563,33 +711,37 @@ export const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, opt
|
|
|
563
711
|
}
|
|
564
712
|
}
|
|
565
713
|
catch (error) {
|
|
566
|
-
|
|
567
|
-
|
|
714
|
+
if (axios.isAxiosError(error)) {
|
|
715
|
+
result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
|
|
716
|
+
}
|
|
717
|
+
const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
|
|
718
|
+
logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
|
|
568
719
|
}
|
|
569
720
|
}
|
|
570
721
|
if (!urls) {
|
|
571
|
-
throw new Boom('Media upload failed on all hosts', { statusCode: 500 });
|
|
722
|
+
throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 });
|
|
572
723
|
}
|
|
573
724
|
return urls;
|
|
574
725
|
};
|
|
575
726
|
};
|
|
727
|
+
exports.getWAUploadToServer = getWAUploadToServer;
|
|
576
728
|
const getMediaRetryKey = (mediaKey) => {
|
|
577
|
-
return hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
|
|
729
|
+
return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
|
|
578
730
|
};
|
|
579
731
|
/**
|
|
580
732
|
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
|
581
733
|
*/
|
|
582
|
-
|
|
734
|
+
const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
583
735
|
const recp = { stanzaId: key.id };
|
|
584
|
-
const recpBuffer = proto.ServerErrorReceipt.encode(recp).finish();
|
|
736
|
+
const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
|
|
585
737
|
const iv = Crypto.randomBytes(12);
|
|
586
|
-
const retryKey =
|
|
587
|
-
const ciphertext = aesEncryptGCM(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
738
|
+
const retryKey = getMediaRetryKey(mediaKey);
|
|
739
|
+
const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
588
740
|
const req = {
|
|
589
741
|
tag: 'receipt',
|
|
590
742
|
attrs: {
|
|
591
743
|
id: key.id,
|
|
592
|
-
to: jidNormalizedUser(meId),
|
|
744
|
+
to: (0, WABinary_1.jidNormalizedUser)(meId),
|
|
593
745
|
type: 'server-error'
|
|
594
746
|
},
|
|
595
747
|
content: [
|
|
@@ -608,7 +760,7 @@ export const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
608
760
|
tag: 'rmr',
|
|
609
761
|
attrs: {
|
|
610
762
|
jid: key.remoteJid,
|
|
611
|
-
from_me: (!!key.fromMe).toString(),
|
|
763
|
+
'from_me': (!!key.fromMe).toString(),
|
|
612
764
|
// @ts-ignore
|
|
613
765
|
participant: key.participant || undefined
|
|
614
766
|
}
|
|
@@ -617,8 +769,9 @@ export const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
|
617
769
|
};
|
|
618
770
|
return req;
|
|
619
771
|
};
|
|
620
|
-
|
|
621
|
-
|
|
772
|
+
exports.encryptMediaRetryRequest = encryptMediaRetryRequest;
|
|
773
|
+
const decodeMediaRetryNode = (node) => {
|
|
774
|
+
const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr');
|
|
622
775
|
const event = {
|
|
623
776
|
key: {
|
|
624
777
|
id: node.attrs.id,
|
|
@@ -627,37 +780,40 @@ export const decodeMediaRetryNode = (node) => {
|
|
|
627
780
|
participant: rmrNode.attrs.participant
|
|
628
781
|
}
|
|
629
782
|
};
|
|
630
|
-
const errorNode = getBinaryNodeChild(node, 'error');
|
|
783
|
+
const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
|
|
631
784
|
if (errorNode) {
|
|
632
785
|
const errorCode = +errorNode.attrs.code;
|
|
633
|
-
event.error = new Boom(`Failed to re-upload media (${errorCode})`, {
|
|
634
|
-
data: errorNode.attrs,
|
|
635
|
-
statusCode: getStatusCodeForMediaRetry(errorCode)
|
|
636
|
-
});
|
|
786
|
+
event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode) });
|
|
637
787
|
}
|
|
638
788
|
else {
|
|
639
|
-
const encryptedInfoNode = getBinaryNodeChild(node, 'encrypt');
|
|
640
|
-
const ciphertext = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_p');
|
|
641
|
-
const iv = getBinaryNodeChildBuffer(encryptedInfoNode, 'enc_iv');
|
|
789
|
+
const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt');
|
|
790
|
+
const ciphertext = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p');
|
|
791
|
+
const iv = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv');
|
|
642
792
|
if (ciphertext && iv) {
|
|
643
793
|
event.media = { ciphertext, iv };
|
|
644
794
|
}
|
|
645
795
|
else {
|
|
646
|
-
event.error = new Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
|
|
796
|
+
event.error = new boom_1.Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
|
|
647
797
|
}
|
|
648
798
|
}
|
|
649
799
|
return event;
|
|
650
800
|
};
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
const
|
|
654
|
-
|
|
801
|
+
exports.decodeMediaRetryNode = decodeMediaRetryNode;
|
|
802
|
+
const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
803
|
+
const retryKey = getMediaRetryKey(mediaKey);
|
|
804
|
+
const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
|
|
805
|
+
return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
|
|
655
806
|
};
|
|
656
|
-
|
|
807
|
+
exports.decryptMediaRetryData = decryptMediaRetryData;
|
|
808
|
+
const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
|
|
809
|
+
exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
|
|
657
810
|
const MEDIA_RETRY_STATUS_MAP = {
|
|
658
|
-
[proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
|
|
659
|
-
[proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
|
|
660
|
-
[proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
661
|
-
[proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
|
|
811
|
+
[WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
|
|
812
|
+
[WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
|
|
813
|
+
[WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
814
|
+
[WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
|
|
662
815
|
};
|
|
663
|
-
|
|
816
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
817
|
+
function __importStar(arg0) {
|
|
818
|
+
throw new Error('Function not implemented.');
|
|
819
|
+
}
|