@hbmodsofc/baileys 1.5.2 → 1.7.6
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/WAProto/index.js +19671 -152026
- package/engine-requirements.js +4 -4
- package/lib/Defaults/index.d.ts +12 -8
- package/lib/Defaults/index.js +90 -124
- package/lib/Signal/Group/group_cipher.d.ts +0 -1
- package/lib/Signal/Group/group_cipher.js +28 -39
- package/lib/Signal/Group/sender-chain-key.d.ts +1 -1
- package/lib/Signal/Group/sender-chain-key.js +9 -2
- package/lib/Signal/Group/sender-key-distribution-message.js +3 -3
- package/lib/Signal/Group/sender-key-message.js +3 -3
- package/lib/Signal/Group/sender-key-state.d.ts +4 -4
- package/lib/Signal/Group/sender-key-state.js +47 -16
- package/lib/Signal/libsignal.d.ts +7 -3
- package/lib/Signal/libsignal.js +224 -39
- package/lib/Signal/lid-mapping.d.ts +26 -0
- package/lib/Signal/lid-mapping.js +146 -0
- package/lib/Socket/Client/index.d.ts +2 -3
- package/lib/Socket/Client/index.js +2 -3
- package/lib/Socket/Client/{abstract-socket-client.d.ts → types.d.ts} +1 -3
- package/lib/Socket/Client/{web-socket-client.d.ts → websocket.d.ts} +1 -1
- package/lib/Socket/Client/{web-socket-client.js → websocket.js} +10 -16
- package/lib/Socket/business.d.ts +94 -78
- package/lib/Socket/business.js +130 -11
- package/lib/Socket/chats.d.ts +63 -233
- package/lib/Socket/chats.js +234 -184
- package/lib/Socket/communities.d.ts +232 -0
- package/lib/Socket/communities.js +402 -0
- package/lib/Socket/groups.d.ts +62 -41
- package/lib/Socket/groups.js +76 -64
- package/lib/Socket/index.d.ts +129 -83
- package/lib/Socket/index.js +13 -6
- package/lib/Socket/messages-recv.d.ts +59 -48
- package/lib/Socket/messages-recv.js +516 -371
- package/lib/Socket/messages-send.d.ts +86 -67
- package/lib/Socket/messages-send.js +1091 -1
- package/lib/Socket/mex.d.ts +2 -0
- package/lib/Socket/mex.js +45 -0
- package/lib/Socket/newsletter.d.ts +76 -64
- package/lib/Socket/newsletter.js +184 -1
- package/lib/Socket/socket.d.ts +19 -13
- package/lib/Socket/socket.js +805 -1
- package/lib/Types/Auth.d.ts +4 -10
- package/lib/Types/Bussines.d.ts +24 -0
- package/lib/Types/Bussines.js +2 -0
- package/lib/Types/Call.d.ts +1 -1
- package/lib/Types/Chat.d.ts +29 -9
- package/lib/Types/Chat.js +7 -1
- package/lib/Types/Contact.d.ts +5 -1
- package/lib/Types/Events.d.ts +55 -14
- package/lib/Types/GroupMetadata.d.ts +15 -5
- package/lib/Types/Label.d.ts +11 -0
- package/lib/Types/Label.js +1 -1
- package/lib/Types/LabelAssociation.js +1 -1
- package/lib/Types/Message.d.ts +75 -49
- package/lib/Types/Message.js +10 -7
- package/lib/Types/Newsletter.d.ts +129 -98
- package/lib/Types/Newsletter.js +33 -38
- package/lib/Types/Product.d.ts +1 -1
- package/lib/Types/Signal.d.ts +29 -1
- package/lib/Types/Socket.d.ts +48 -22
- package/lib/Types/State.d.ts +13 -2
- package/lib/Types/State.js +12 -0
- package/lib/Types/USync.d.ts +1 -1
- package/lib/Types/index.d.ts +10 -3
- package/lib/Types/index.js +2 -2
- package/lib/Utils/auth-utils.d.ts +3 -3
- package/lib/Utils/auth-utils.js +378 -102
- package/lib/Utils/baileys-event-stream.js +1 -1
- package/lib/Utils/business.d.ts +2 -2
- package/lib/Utils/business.js +19 -13
- package/lib/Utils/chat-utils.d.ts +21 -22
- package/lib/Utils/chat-utils.js +201 -154
- package/lib/Utils/crypto.d.ts +18 -19
- package/lib/Utils/crypto.js +78 -37
- package/lib/Utils/decode-wa-message.d.ts +34 -7
- package/lib/Utils/decode-wa-message.js +138 -66
- package/lib/Utils/event-buffer.d.ts +6 -8
- package/lib/Utils/event-buffer.js +81 -43
- package/lib/Utils/generics.d.ts +27 -27
- package/lib/Utils/generics.js +128 -133
- package/lib/Utils/history.d.ts +9 -5
- package/lib/Utils/history.js +17 -23
- package/lib/Utils/index.d.ts +2 -0
- package/lib/Utils/index.js +2 -0
- package/lib/Utils/lidToJid-test.d.ts +11 -0
- package/lib/Utils/lidToJid-test.js +27 -0
- package/lib/Utils/link-preview.d.ts +4 -4
- package/lib/Utils/link-preview.js +40 -12
- package/lib/Utils/logger.d.ts +11 -3
- package/lib/Utils/lt-hash.d.ts +8 -8
- package/lib/Utils/lt-hash.js +23 -24
- package/lib/Utils/make-mutex.d.ts +2 -2
- package/lib/Utils/make-mutex.js +3 -2
- package/lib/Utils/message-retry-manager.d.ts +81 -0
- package/lib/Utils/message-retry-manager.js +152 -0
- package/lib/Utils/messages-media.d.ts +37 -41
- package/lib/Utils/messages-media.js +252 -368
- package/lib/Utils/messages.d.ts +13 -15
- package/lib/Utils/messages.js +274 -261
- package/lib/Utils/noise-handler.d.ts +13 -15
- package/lib/Utils/noise-handler.js +20 -26
- package/lib/Utils/process-message.d.ts +9 -8
- package/lib/Utils/process-message.js +157 -93
- package/lib/Utils/signal.d.ts +6 -5
- package/lib/Utils/signal.js +37 -29
- package/lib/Utils/use-multi-file-auth-state.d.ts +1 -2
- package/lib/Utils/use-multi-file-auth-state.js +12 -7
- package/lib/Utils/validate-connection.d.ts +5 -6
- package/lib/Utils/validate-connection.js +39 -97
- package/lib/WABinary/constants.d.ts +24 -27
- package/lib/WABinary/constants.js +1276 -13
- package/lib/WABinary/decode.d.ts +3 -4
- package/lib/WABinary/decode.js +28 -14
- package/lib/WABinary/encode.d.ts +1 -2
- package/lib/WABinary/encode.js +134 -147
- package/lib/WABinary/generic-utils.d.ts +4 -7
- package/lib/WABinary/generic-utils.js +40 -125
- package/lib/WABinary/jid-utils.d.ts +13 -8
- package/lib/WABinary/jid-utils.js +27 -16
- package/lib/WAM/BinaryInfo.d.ts +2 -11
- package/lib/WAM/constants.d.ts +3 -2
- package/lib/WAM/constants.js +2252 -2359
- package/lib/WAM/encode.d.ts +1 -2
- package/lib/WAM/encode.js +8 -11
- package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +2 -2
- package/lib/WAUSync/Protocols/USyncContactProtocol.js +3 -4
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +2 -2
- package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +5 -5
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +2 -2
- package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +5 -5
- package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +2 -2
- package/lib/WAUSync/Protocols/USyncStatusProtocol.js +5 -6
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +2 -2
- package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +1 -1
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +4 -3
- package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +11 -3
- package/lib/WAUSync/USyncQuery.d.ts +2 -2
- package/lib/WAUSync/USyncQuery.js +19 -15
- package/lib/WAUSync/USyncUser.d.ts +5 -5
- package/lib/WAUSync/index.d.ts +1 -1
- package/lib/WAUSync/index.js +1 -1
- package/package.json +102 -102
- package/lib/Defaults/baileys-version.json +0 -3
- package/lib/Defaults/phonenumber-mcc.json +0 -223
- package/lib/Signal/Group/queue-job.d.ts +0 -1
- package/lib/Signal/Group/queue-job.js +0 -57
- package/lib/Socket/Client/mobile-socket-client.d.ts +0 -13
- package/lib/Socket/Client/mobile-socket-client.js +0 -65
- package/lib/Socket/hbmods.d.ts +0 -253
- package/lib/Socket/hbmods.js +0 -1
- package/lib/Socket/registration.d.ts +0 -267
- package/lib/Socket/registration.js +0 -166
- package/lib/Socket/usync.d.ts +0 -36
- package/lib/Socket/usync.js +0 -70
- /package/lib/Socket/Client/{abstract-socket-client.js → types.js} +0 -0
|
@@ -15,16 +15,35 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
24
37
|
};
|
|
25
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.
|
|
39
|
+
exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.getHttpStream = exports.getStream = exports.toBuffer = exports.toReadable = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.getRawMediaUploadData = exports.hkdfInfoKey = void 0;
|
|
40
|
+
exports.getMediaKeys = getMediaKeys;
|
|
41
|
+
exports.getAudioDuration = getAudioDuration;
|
|
42
|
+
exports.getAudioWaveform = getAudioWaveform;
|
|
43
|
+
exports.generateThumbnail = generateThumbnail;
|
|
44
|
+
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
27
45
|
const boom_1 = require("@hapi/boom");
|
|
46
|
+
const axios_1 = __importDefault(require("axios"));
|
|
28
47
|
const child_process_1 = require("child_process");
|
|
29
48
|
const Crypto = __importStar(require("crypto"));
|
|
30
49
|
const events_1 = require("events");
|
|
@@ -32,29 +51,18 @@ const fs_1 = require("fs");
|
|
|
32
51
|
const os_1 = require("os");
|
|
33
52
|
const path_1 = require("path");
|
|
34
53
|
const stream_1 = require("stream");
|
|
35
|
-
const
|
|
54
|
+
const index_js_1 = require("../../WAProto/index.js");
|
|
36
55
|
const Defaults_1 = require("../Defaults");
|
|
37
56
|
const WABinary_1 = require("../WABinary");
|
|
38
57
|
const crypto_1 = require("./crypto");
|
|
39
58
|
const generics_1 = require("./generics");
|
|
40
59
|
const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
|
|
41
60
|
const getImageProcessingLibrary = async () => {
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
]);
|
|
61
|
+
//@ts-ignore
|
|
62
|
+
const [jimp, sharp] = await Promise.all([Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }), Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { })]);
|
|
54
63
|
if (sharp) {
|
|
55
64
|
return { sharp };
|
|
56
65
|
}
|
|
57
|
-
const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
|
|
58
66
|
if (jimp) {
|
|
59
67
|
return { jimp };
|
|
60
68
|
}
|
|
@@ -65,8 +73,47 @@ const hkdfInfoKey = (type) => {
|
|
|
65
73
|
return `WhatsApp ${hkdfInfo} Keys`;
|
|
66
74
|
};
|
|
67
75
|
exports.hkdfInfoKey = hkdfInfoKey;
|
|
76
|
+
const getRawMediaUploadData = async (media, mediaType, logger) => {
|
|
77
|
+
const { stream } = await (0, exports.getStream)(media);
|
|
78
|
+
logger?.debug('got stream for raw upload');
|
|
79
|
+
const hasher = Crypto.createHash('sha256');
|
|
80
|
+
const filePath = (0, path_1.join)((0, os_1.tmpdir)(), mediaType + (0, generics_1.generateMessageIDV2)());
|
|
81
|
+
const fileWriteStream = (0, fs_1.createWriteStream)(filePath);
|
|
82
|
+
let fileLength = 0;
|
|
83
|
+
try {
|
|
84
|
+
for await (const data of stream) {
|
|
85
|
+
fileLength += data.length;
|
|
86
|
+
hasher.update(data);
|
|
87
|
+
if (!fileWriteStream.write(data)) {
|
|
88
|
+
await (0, events_1.once)(fileWriteStream, 'drain');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
fileWriteStream.end();
|
|
92
|
+
await (0, events_1.once)(fileWriteStream, 'finish');
|
|
93
|
+
stream.destroy();
|
|
94
|
+
const fileSha256 = hasher.digest();
|
|
95
|
+
logger?.debug('hashed data for raw upload');
|
|
96
|
+
return {
|
|
97
|
+
filePath: filePath,
|
|
98
|
+
fileSha256,
|
|
99
|
+
fileLength
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
fileWriteStream.destroy();
|
|
104
|
+
stream.destroy();
|
|
105
|
+
try {
|
|
106
|
+
await fs_1.promises.unlink(filePath);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
//
|
|
110
|
+
}
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
exports.getRawMediaUploadData = getRawMediaUploadData;
|
|
68
115
|
/** generates all the keys required to encrypt/decrypt & sign a media message */
|
|
69
|
-
function getMediaKeys(buffer, mediaType) {
|
|
116
|
+
async function getMediaKeys(buffer, mediaType) {
|
|
70
117
|
if (!buffer) {
|
|
71
118
|
throw new boom_1.Boom('Cannot derive from empty media key');
|
|
72
119
|
}
|
|
@@ -74,18 +121,17 @@ function getMediaKeys(buffer, mediaType) {
|
|
|
74
121
|
buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
|
|
75
122
|
}
|
|
76
123
|
// expand using HKDF to 112 bytes, also pass in the relevant app info
|
|
77
|
-
const expandedMediaKey = (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
124
|
+
const expandedMediaKey = await (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
|
|
78
125
|
return {
|
|
79
126
|
iv: expandedMediaKey.slice(0, 16),
|
|
80
127
|
cipherKey: expandedMediaKey.slice(16, 48),
|
|
81
|
-
macKey: expandedMediaKey.slice(48, 80)
|
|
128
|
+
macKey: expandedMediaKey.slice(48, 80)
|
|
82
129
|
};
|
|
83
130
|
}
|
|
84
|
-
exports.getMediaKeys = getMediaKeys;
|
|
85
131
|
/** Extracts video thumb using FFMPEG */
|
|
86
132
|
const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
|
|
87
133
|
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,
|
|
134
|
+
(0, child_process_1.exec)(cmd, err => {
|
|
89
135
|
if (err) {
|
|
90
136
|
reject(err);
|
|
91
137
|
}
|
|
@@ -95,37 +141,33 @@ const extractVideoThumb = async (path, destPath, time, size) => new Promise((res
|
|
|
95
141
|
});
|
|
96
142
|
});
|
|
97
143
|
const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
98
|
-
|
|
144
|
+
// TODO: Move entirely to sharp, removing jimp as it supports readable streams
|
|
145
|
+
// This will have positive speed and performance impacts as well as minimizing RAM usage.
|
|
99
146
|
if (bufferOrFilePath instanceof stream_1.Readable) {
|
|
100
147
|
bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
|
|
101
148
|
}
|
|
102
149
|
const lib = await getImageProcessingLibrary();
|
|
103
|
-
if ('sharp' in lib && typeof
|
|
150
|
+
if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
|
|
104
151
|
const img = lib.sharp.default(bufferOrFilePath);
|
|
105
152
|
const dimensions = await img.metadata();
|
|
106
|
-
const buffer = await img
|
|
107
|
-
.resize(width)
|
|
108
|
-
.jpeg({ quality: 50 })
|
|
109
|
-
.toBuffer();
|
|
153
|
+
const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer();
|
|
110
154
|
return {
|
|
111
155
|
buffer,
|
|
112
156
|
original: {
|
|
113
157
|
width: dimensions.width,
|
|
114
|
-
height: dimensions.height
|
|
115
|
-
}
|
|
158
|
+
height: dimensions.height
|
|
159
|
+
}
|
|
116
160
|
};
|
|
117
161
|
}
|
|
118
|
-
else if ('jimp' in lib && typeof
|
|
119
|
-
const
|
|
120
|
-
const jimp = await read(bufferOrFilePath);
|
|
162
|
+
else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
|
163
|
+
const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
|
|
121
164
|
const dimensions = {
|
|
122
|
-
width: jimp.
|
|
123
|
-
height: jimp.
|
|
165
|
+
width: jimp.width,
|
|
166
|
+
height: jimp.height
|
|
124
167
|
};
|
|
125
168
|
const buffer = await jimp
|
|
126
|
-
.
|
|
127
|
-
.
|
|
128
|
-
.getBufferAsync(MIME_JPEG);
|
|
169
|
+
.resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
|
|
170
|
+
.getBuffer('image/jpeg', { quality: 50 });
|
|
129
171
|
return {
|
|
130
172
|
buffer,
|
|
131
173
|
original: dimensions
|
|
@@ -136,190 +178,110 @@ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
|
|
|
136
178
|
}
|
|
137
179
|
};
|
|
138
180
|
exports.extractImageThumb = extractImageThumb;
|
|
139
|
-
const encodeBase64EncodedStringForUpload = (b64) =>
|
|
140
|
-
.replace(/\+/g, '-')
|
|
141
|
-
.replace(/\//g, '_')
|
|
142
|
-
.replace(/\=+$/, '')));
|
|
181
|
+
const encodeBase64EncodedStringForUpload = (b64) => encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''));
|
|
143
182
|
exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
|
|
144
|
-
const generateProfilePicture = async (mediaUpload) => {
|
|
145
|
-
|
|
146
|
-
|
|
183
|
+
const generateProfilePicture = async (mediaUpload, dimensions) => {
|
|
184
|
+
let buffer;
|
|
185
|
+
const { width: w = 640, height: h = 640 } = dimensions || {};
|
|
147
186
|
if (Buffer.isBuffer(mediaUpload)) {
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
else if ('url' in mediaUpload) {
|
|
151
|
-
bufferOrFilePath = mediaUpload.url.toString();
|
|
187
|
+
buffer = mediaUpload;
|
|
152
188
|
}
|
|
153
189
|
else {
|
|
154
|
-
|
|
190
|
+
// Use getStream to handle all WAMediaUpload types (Buffer, Stream, URL)
|
|
191
|
+
const { stream } = await (0, exports.getStream)(mediaUpload);
|
|
192
|
+
// Convert the resulting stream to a buffer
|
|
193
|
+
buffer = await (0, exports.toBuffer)(stream);
|
|
155
194
|
}
|
|
156
195
|
const lib = await getImageProcessingLibrary();
|
|
157
196
|
let img;
|
|
158
|
-
if ('sharp' in lib && typeof
|
|
159
|
-
img = lib.sharp
|
|
160
|
-
.
|
|
197
|
+
if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
|
|
198
|
+
img = lib.sharp
|
|
199
|
+
.default(buffer)
|
|
200
|
+
.resize(w, h)
|
|
161
201
|
.jpeg({
|
|
162
|
-
quality: 50
|
|
202
|
+
quality: 50
|
|
163
203
|
})
|
|
164
204
|
.toBuffer();
|
|
165
205
|
}
|
|
166
|
-
else if ('jimp' in lib && typeof
|
|
167
|
-
const
|
|
168
|
-
const
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
img = cropped
|
|
172
|
-
.quality(50)
|
|
173
|
-
.resize(640, 640, RESIZE_BILINEAR)
|
|
174
|
-
.getBufferAsync(MIME_JPEG);
|
|
206
|
+
else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
|
|
207
|
+
const jimp = await lib.jimp.Jimp.read(buffer);
|
|
208
|
+
const min = Math.min(jimp.width, jimp.height);
|
|
209
|
+
const cropped = jimp.crop({ x: 0, y: 0, w: min, h: min });
|
|
210
|
+
img = cropped.resize({ w, h, mode: lib.jimp.ResizeStrategy.BILINEAR }).getBuffer('image/jpeg', { quality: 50 });
|
|
175
211
|
}
|
|
176
212
|
else {
|
|
177
213
|
throw new boom_1.Boom('No image processing library available');
|
|
178
214
|
}
|
|
179
215
|
return {
|
|
180
|
-
img: await img
|
|
216
|
+
img: await img
|
|
181
217
|
};
|
|
182
218
|
};
|
|
183
219
|
exports.generateProfilePicture = generateProfilePicture;
|
|
184
220
|
/** gets the SHA256 of the given media message */
|
|
185
221
|
const mediaMessageSHA256B64 = (message) => {
|
|
186
222
|
const media = Object.values(message)[0];
|
|
187
|
-
return
|
|
223
|
+
return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64');
|
|
188
224
|
};
|
|
189
225
|
exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
|
|
190
226
|
async function getAudioDuration(buffer) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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;
|
|
227
|
+
const musicMetadata = await Promise.resolve().then(() => __importStar(require('music-metadata')));
|
|
228
|
+
let metadata;
|
|
229
|
+
const options = {
|
|
230
|
+
duration: true
|
|
231
|
+
};
|
|
232
|
+
if (Buffer.isBuffer(buffer)) {
|
|
233
|
+
metadata = await musicMetadata.parseBuffer(buffer, undefined, options);
|
|
234
|
+
}
|
|
235
|
+
else if (typeof buffer === 'string') {
|
|
236
|
+
metadata = await musicMetadata.parseFile(buffer, options);
|
|
227
237
|
}
|
|
238
|
+
else {
|
|
239
|
+
metadata = await musicMetadata.parseStream(buffer, undefined, options);
|
|
240
|
+
}
|
|
241
|
+
return metadata.format.duration;
|
|
228
242
|
}
|
|
229
|
-
|
|
243
|
+
/**
|
|
244
|
+
referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
|
|
245
|
+
*/
|
|
230
246
|
async function getAudioWaveform(buffer, logger) {
|
|
231
247
|
try {
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
248
|
+
// @ts-ignore
|
|
249
|
+
const { default: decoder } = await Promise.resolve().then(() => __importStar(require('audio-decode')));
|
|
235
250
|
let audioData;
|
|
236
251
|
if (Buffer.isBuffer(buffer)) {
|
|
237
252
|
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);
|
|
243
253
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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);
|
|
254
|
+
else if (typeof buffer === 'string') {
|
|
255
|
+
const rStream = (0, fs_1.createReadStream)(buffer);
|
|
256
|
+
audioData = await (0, exports.toBuffer)(rStream);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
audioData = await (0, exports.toBuffer)(buffer);
|
|
260
|
+
}
|
|
261
|
+
const audioBuffer = await decoder(audioData);
|
|
262
|
+
const rawData = audioBuffer.getChannelData(0); // We only need to work with one channel of data
|
|
263
|
+
const samples = 64; // Number of samples we want to have in our final data set
|
|
264
|
+
const blockSize = Math.floor(rawData.length / samples); // the number of samples in each subdivision
|
|
265
|
+
const filteredData = [];
|
|
266
|
+
for (let i = 0; i < samples; i++) {
|
|
267
|
+
const blockStart = blockSize * i; // the location of the first sample in the block
|
|
268
|
+
let sum = 0;
|
|
269
|
+
for (let j = 0; j < blockSize; j++) {
|
|
270
|
+
sum = sum + Math.abs(rawData[blockStart + j]); // find the sum of all the samples in the block
|
|
271
|
+
}
|
|
272
|
+
filteredData.push(sum / blockSize); // divide the sum by the block size to get the average
|
|
273
|
+
}
|
|
274
|
+
// This guarantees that the largest data point will be set to 1, and the rest of the data will scale proportionally.
|
|
275
|
+
const multiplier = Math.pow(Math.max(...filteredData), -1);
|
|
276
|
+
const normalizedData = filteredData.map(n => n * multiplier);
|
|
277
|
+
// Generate waveform like WhatsApp
|
|
278
|
+
const waveform = new Uint8Array(normalizedData.map(n => Math.floor(100 * n)));
|
|
279
|
+
return waveform;
|
|
281
280
|
}
|
|
282
|
-
|
|
283
|
-
|
|
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;
|
|
281
|
+
catch (e) {
|
|
282
|
+
logger?.debug('Failed to generate waveform: ' + e);
|
|
320
283
|
}
|
|
321
284
|
}
|
|
322
|
-
exports.convertToOpusBuffer = convertToOpusBuffer;
|
|
323
285
|
const toReadable = (buffer) => {
|
|
324
286
|
const readable = new stream_1.Readable({ read: () => { } });
|
|
325
287
|
readable.push(buffer);
|
|
@@ -343,7 +305,12 @@ const getStream = async (item, opts) => {
|
|
|
343
305
|
if ('stream' in item) {
|
|
344
306
|
return { stream: item.stream, type: 'readable' };
|
|
345
307
|
}
|
|
346
|
-
|
|
308
|
+
const urlStr = item.url.toString();
|
|
309
|
+
if (urlStr.startsWith('data:')) {
|
|
310
|
+
const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
|
|
311
|
+
return { stream: (0, exports.toReadable)(buffer), type: 'buffer' };
|
|
312
|
+
}
|
|
313
|
+
if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
|
|
347
314
|
return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' };
|
|
348
315
|
}
|
|
349
316
|
return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' };
|
|
@@ -351,7 +318,6 @@ const getStream = async (item, opts) => {
|
|
|
351
318
|
exports.getStream = getStream;
|
|
352
319
|
/** generates a thumbnail for a given media, if required */
|
|
353
320
|
async function generateThumbnail(file, mediaType, options) {
|
|
354
|
-
var _a;
|
|
355
321
|
let thumbnail;
|
|
356
322
|
let originalImageDimensions;
|
|
357
323
|
if (mediaType === 'image') {
|
|
@@ -360,12 +326,12 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
360
326
|
if (original.width && original.height) {
|
|
361
327
|
originalImageDimensions = {
|
|
362
328
|
width: original.width,
|
|
363
|
-
height: original.height
|
|
329
|
+
height: original.height
|
|
364
330
|
};
|
|
365
331
|
}
|
|
366
332
|
}
|
|
367
333
|
else if (mediaType === 'video') {
|
|
368
|
-
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.
|
|
334
|
+
const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
|
|
369
335
|
try {
|
|
370
336
|
await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
|
|
371
337
|
const buff = await fs_1.promises.readFile(imgFilename);
|
|
@@ -373,7 +339,7 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
373
339
|
await fs_1.promises.unlink(imgFilename);
|
|
374
340
|
}
|
|
375
341
|
catch (err) {
|
|
376
|
-
|
|
342
|
+
options.logger?.debug('could not generate video thumb: ' + err);
|
|
377
343
|
}
|
|
378
344
|
}
|
|
379
345
|
return {
|
|
@@ -381,160 +347,90 @@ async function generateThumbnail(file, mediaType, options) {
|
|
|
381
347
|
originalImageDimensions
|
|
382
348
|
};
|
|
383
349
|
}
|
|
384
|
-
exports.generateThumbnail = generateThumbnail;
|
|
385
350
|
const getHttpStream = async (url, options = {}) => {
|
|
386
|
-
const
|
|
387
|
-
const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
|
|
351
|
+
const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' });
|
|
388
352
|
return fetched.data;
|
|
389
353
|
};
|
|
390
354
|
exports.getHttpStream = getHttpStream;
|
|
391
|
-
const
|
|
355
|
+
const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
|
|
392
356
|
const { stream, type } = await (0, exports.getStream)(media, opts);
|
|
393
|
-
logger
|
|
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;
|
|
432
|
-
}
|
|
433
|
-
};
|
|
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
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
357
|
+
logger?.debug('fetched media stream');
|
|
450
358
|
const mediaKey = Crypto.randomBytes(32);
|
|
451
|
-
const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
let
|
|
455
|
-
let
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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
|
-
|
|
359
|
+
const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
|
|
360
|
+
const encFilePath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)() + '-enc');
|
|
361
|
+
const encFileWriteStream = (0, fs_1.createWriteStream)(encFilePath);
|
|
362
|
+
let originalFileStream;
|
|
363
|
+
let originalFilePath;
|
|
364
|
+
if (saveOriginalFileIfRequired) {
|
|
365
|
+
originalFilePath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)() + '-original');
|
|
366
|
+
originalFileStream = (0, fs_1.createWriteStream)(originalFilePath);
|
|
367
|
+
}
|
|
466
368
|
let fileLength = 0;
|
|
467
369
|
const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
370
|
+
const hmac = Crypto.createHmac('sha256', macKey).update(iv);
|
|
371
|
+
const sha256Plain = Crypto.createHash('sha256');
|
|
372
|
+
const sha256Enc = Crypto.createHash('sha256');
|
|
373
|
+
const onChunk = (buff) => {
|
|
374
|
+
sha256Enc.update(buff);
|
|
375
|
+
hmac.update(buff);
|
|
376
|
+
encFileWriteStream.write(buff);
|
|
377
|
+
};
|
|
472
378
|
try {
|
|
473
|
-
for await (const data of
|
|
379
|
+
for await (const data of stream) {
|
|
474
380
|
fileLength += data.length;
|
|
475
|
-
if (type === 'remote'
|
|
476
|
-
&& (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
|
|
477
|
-
&& fileLength + data.length > opts.maxContentLength) {
|
|
381
|
+
if (type === 'remote' && opts?.maxContentLength && fileLength + data.length > opts.maxContentLength) {
|
|
478
382
|
throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
|
|
479
383
|
data: { media, type }
|
|
480
384
|
});
|
|
481
385
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
if (!writeStream.write(data)) {
|
|
486
|
-
await (0, events_1.once)(writeStream, 'drain');
|
|
386
|
+
if (originalFileStream) {
|
|
387
|
+
if (!originalFileStream.write(data)) {
|
|
388
|
+
await (0, events_1.once)(originalFileStream, 'drain');
|
|
487
389
|
}
|
|
488
390
|
}
|
|
391
|
+
sha256Plain.update(data);
|
|
489
392
|
onChunk(aes.update(data));
|
|
490
393
|
}
|
|
491
|
-
|
|
492
394
|
onChunk(aes.final());
|
|
493
395
|
const mac = hmac.digest().slice(0, 10);
|
|
494
|
-
sha256Enc
|
|
396
|
+
sha256Enc.update(mac);
|
|
495
397
|
const fileSha256 = sha256Plain.digest();
|
|
496
398
|
const fileEncSha256 = sha256Enc.digest();
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
399
|
+
encFileWriteStream.write(mac);
|
|
400
|
+
encFileWriteStream.end();
|
|
401
|
+
originalFileStream?.end?.();
|
|
402
|
+
stream.destroy();
|
|
403
|
+
logger?.debug('encrypted data successfully');
|
|
503
404
|
return {
|
|
504
405
|
mediaKey,
|
|
505
|
-
|
|
506
|
-
|
|
406
|
+
originalFilePath,
|
|
407
|
+
encFilePath,
|
|
507
408
|
mac,
|
|
508
409
|
fileEncSha256,
|
|
509
410
|
fileSha256,
|
|
510
|
-
fileLength
|
|
511
|
-
didSaveToTmpPath
|
|
411
|
+
fileLength
|
|
512
412
|
};
|
|
513
413
|
}
|
|
514
414
|
catch (error) {
|
|
515
|
-
|
|
516
|
-
|
|
415
|
+
// destroy all streams with error
|
|
416
|
+
encFileWriteStream.destroy();
|
|
417
|
+
originalFileStream?.destroy?.();
|
|
517
418
|
aes.destroy();
|
|
518
419
|
hmac.destroy();
|
|
519
420
|
sha256Plain.destroy();
|
|
520
421
|
sha256Enc.destroy();
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
await fs_1.promises.unlink(
|
|
526
|
-
}
|
|
527
|
-
catch (err) {
|
|
422
|
+
stream.destroy();
|
|
423
|
+
try {
|
|
424
|
+
await fs_1.promises.unlink(encFilePath);
|
|
425
|
+
if (originalFilePath) {
|
|
426
|
+
await fs_1.promises.unlink(originalFilePath);
|
|
528
427
|
}
|
|
529
428
|
}
|
|
429
|
+
catch (err) {
|
|
430
|
+
logger?.error({ err }, 'failed deleting tmp files');
|
|
431
|
+
}
|
|
530
432
|
throw error;
|
|
531
433
|
}
|
|
532
|
-
|
|
533
|
-
function onChunk(buff) {
|
|
534
|
-
sha256Enc = sha256Enc.update(buff);
|
|
535
|
-
hmac = hmac.update(buff);
|
|
536
|
-
encWriteStream.push(buff);
|
|
537
|
-
}
|
|
538
434
|
};
|
|
539
435
|
exports.encryptedStream = encryptedStream;
|
|
540
436
|
const DEF_HOST = 'mmg.whatsapp.net';
|
|
@@ -544,9 +440,13 @@ const toSmallestChunkSize = (num) => {
|
|
|
544
440
|
};
|
|
545
441
|
const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
|
|
546
442
|
exports.getUrlFromDirectPath = getUrlFromDirectPath;
|
|
547
|
-
const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
548
|
-
const
|
|
549
|
-
const
|
|
443
|
+
const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
|
|
444
|
+
const isValidMediaUrl = url?.startsWith('https://mmg.whatsapp.net/');
|
|
445
|
+
const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath);
|
|
446
|
+
if (!downloadUrl) {
|
|
447
|
+
throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 });
|
|
448
|
+
}
|
|
449
|
+
const keys = await getMediaKeys(mediaKey, type);
|
|
550
450
|
return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
|
|
551
451
|
};
|
|
552
452
|
exports.downloadContentFromMessage = downloadContentFromMessage;
|
|
@@ -569,8 +469,8 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
569
469
|
}
|
|
570
470
|
const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
|
|
571
471
|
const headers = {
|
|
572
|
-
...(options
|
|
573
|
-
Origin: Defaults_1.DEFAULT_ORIGIN
|
|
472
|
+
...(options?.headers || {}),
|
|
473
|
+
Origin: Defaults_1.DEFAULT_ORIGIN
|
|
574
474
|
};
|
|
575
475
|
if (startChunk || endChunk) {
|
|
576
476
|
headers.Range = `bytes=${startChunk}-`;
|
|
@@ -580,10 +480,10 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
580
480
|
}
|
|
581
481
|
// download the message
|
|
582
482
|
const fetched = await (0, exports.getHttpStream)(downloadUrl, {
|
|
583
|
-
...options || {},
|
|
483
|
+
...(options || {}),
|
|
584
484
|
headers,
|
|
585
485
|
maxBodyLength: Infinity,
|
|
586
|
-
maxContentLength: Infinity
|
|
486
|
+
maxContentLength: Infinity
|
|
587
487
|
});
|
|
588
488
|
let remainingBytes = Buffer.from([]);
|
|
589
489
|
let aes;
|
|
@@ -633,18 +533,16 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
|
|
|
633
533
|
catch (error) {
|
|
634
534
|
callback(error);
|
|
635
535
|
}
|
|
636
|
-
}
|
|
536
|
+
}
|
|
637
537
|
});
|
|
638
538
|
return fetched.pipe(output, { end: true });
|
|
639
539
|
};
|
|
640
540
|
exports.downloadEncryptedContent = downloadEncryptedContent;
|
|
641
541
|
function extensionForMediaMessage(message) {
|
|
642
|
-
const getExtension = (mimetype) => mimetype.split(';')[0]
|
|
542
|
+
const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
|
|
643
543
|
const type = Object.keys(message)[0];
|
|
644
544
|
let extension;
|
|
645
|
-
if (type === 'locationMessage' ||
|
|
646
|
-
type === 'liveLocationMessage' ||
|
|
647
|
-
type === 'productMessage') {
|
|
545
|
+
if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
|
|
648
546
|
extension = '.jpeg';
|
|
649
547
|
}
|
|
650
548
|
else {
|
|
@@ -653,55 +551,42 @@ function extensionForMediaMessage(message) {
|
|
|
653
551
|
}
|
|
654
552
|
return extension;
|
|
655
553
|
}
|
|
656
|
-
exports.extensionForMediaMessage = extensionForMediaMessage;
|
|
657
554
|
const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
|
|
658
|
-
return async (
|
|
659
|
-
var _a, _b;
|
|
660
|
-
const { default: axios } = await import('axios');
|
|
555
|
+
return async (filePath, { mediaType, fileEncSha256B64, timeoutMs }) => {
|
|
661
556
|
// send a query JSON to obtain the url & auth token to upload our media
|
|
662
557
|
let uploadInfo = await refreshMediaConn(false);
|
|
663
558
|
let urls;
|
|
664
559
|
const hosts = [...customUploadHosts, ...uploadInfo.hosts];
|
|
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
560
|
fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
|
|
673
|
-
|
|
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) {
|
|
561
|
+
for (const { hostname } of hosts) {
|
|
678
562
|
logger.debug(`uploading to "${hostname}"`);
|
|
679
563
|
const auth = encodeURIComponent(uploadInfo.auth); // the auth token
|
|
680
|
-
const url = `https://${hostname}${
|
|
564
|
+
const url = `https://${hostname}${Defaults_1.MEDIA_PATH_MAP[mediaType]}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
|
|
565
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
681
566
|
let result;
|
|
682
567
|
try {
|
|
683
|
-
|
|
684
|
-
throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
|
|
685
|
-
}
|
|
686
|
-
const body = await axios.post(url, reqBody, {
|
|
568
|
+
const body = await axios_1.default.post(url, (0, fs_1.createReadStream)(filePath), {
|
|
687
569
|
...options,
|
|
570
|
+
maxRedirects: 0,
|
|
688
571
|
headers: {
|
|
689
|
-
...options.headers || {},
|
|
572
|
+
...(options.headers || {}),
|
|
690
573
|
'Content-Type': 'application/octet-stream',
|
|
691
|
-
|
|
574
|
+
Origin: Defaults_1.DEFAULT_ORIGIN
|
|
692
575
|
},
|
|
693
576
|
httpsAgent: fetchAgent,
|
|
694
577
|
timeout: timeoutMs,
|
|
695
578
|
responseType: 'json',
|
|
696
579
|
maxBodyLength: Infinity,
|
|
697
|
-
maxContentLength: Infinity
|
|
580
|
+
maxContentLength: Infinity
|
|
698
581
|
});
|
|
699
582
|
result = body.data;
|
|
700
|
-
if (
|
|
583
|
+
if (result?.url || result?.directPath) {
|
|
701
584
|
urls = {
|
|
702
585
|
mediaUrl: result.url,
|
|
703
586
|
directPath: result.direct_path,
|
|
704
|
-
|
|
587
|
+
meta_hmac: result.meta_hmac,
|
|
588
|
+
fbid: result.fbid,
|
|
589
|
+
ts: result.ts
|
|
705
590
|
};
|
|
706
591
|
break;
|
|
707
592
|
}
|
|
@@ -711,10 +596,10 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
|
|
|
711
596
|
}
|
|
712
597
|
}
|
|
713
598
|
catch (error) {
|
|
714
|
-
if (
|
|
715
|
-
result =
|
|
599
|
+
if (axios_1.default.isAxiosError(error)) {
|
|
600
|
+
result = error.response?.data;
|
|
716
601
|
}
|
|
717
|
-
const isLast = hostname ===
|
|
602
|
+
const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname;
|
|
718
603
|
logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
|
|
719
604
|
}
|
|
720
605
|
}
|
|
@@ -731,11 +616,11 @@ const getMediaRetryKey = (mediaKey) => {
|
|
|
731
616
|
/**
|
|
732
617
|
* Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
|
|
733
618
|
*/
|
|
734
|
-
const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
619
|
+
const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
|
|
735
620
|
const recp = { stanzaId: key.id };
|
|
736
|
-
const recpBuffer =
|
|
621
|
+
const recpBuffer = index_js_1.proto.ServerErrorReceipt.encode(recp).finish();
|
|
737
622
|
const iv = Crypto.randomBytes(12);
|
|
738
|
-
const retryKey = getMediaRetryKey(mediaKey);
|
|
623
|
+
const retryKey = await getMediaRetryKey(mediaKey);
|
|
739
624
|
const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
|
|
740
625
|
const req = {
|
|
741
626
|
tag: 'receipt',
|
|
@@ -760,7 +645,7 @@ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
|
|
|
760
645
|
tag: 'rmr',
|
|
761
646
|
attrs: {
|
|
762
647
|
jid: key.remoteJid,
|
|
763
|
-
|
|
648
|
+
from_me: (!!key.fromMe).toString(),
|
|
764
649
|
// @ts-ignore
|
|
765
650
|
participant: key.participant || undefined
|
|
766
651
|
}
|
|
@@ -783,7 +668,10 @@ const decodeMediaRetryNode = (node) => {
|
|
|
783
668
|
const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
|
|
784
669
|
if (errorNode) {
|
|
785
670
|
const errorCode = +errorNode.attrs.code;
|
|
786
|
-
event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, {
|
|
671
|
+
event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, {
|
|
672
|
+
data: errorNode.attrs,
|
|
673
|
+
statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode)
|
|
674
|
+
});
|
|
787
675
|
}
|
|
788
676
|
else {
|
|
789
677
|
const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt');
|
|
@@ -799,21 +687,17 @@ const decodeMediaRetryNode = (node) => {
|
|
|
799
687
|
return event;
|
|
800
688
|
};
|
|
801
689
|
exports.decodeMediaRetryNode = decodeMediaRetryNode;
|
|
802
|
-
const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
803
|
-
const retryKey = getMediaRetryKey(mediaKey);
|
|
690
|
+
const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
|
|
691
|
+
const retryKey = await getMediaRetryKey(mediaKey);
|
|
804
692
|
const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
|
|
805
|
-
return
|
|
693
|
+
return index_js_1.proto.MediaRetryNotification.decode(plaintext);
|
|
806
694
|
};
|
|
807
695
|
exports.decryptMediaRetryData = decryptMediaRetryData;
|
|
808
696
|
const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
|
|
809
697
|
exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
|
|
810
698
|
const MEDIA_RETRY_STATUS_MAP = {
|
|
811
|
-
[
|
|
812
|
-
[
|
|
813
|
-
[
|
|
814
|
-
[
|
|
699
|
+
[index_js_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
|
|
700
|
+
[index_js_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
|
|
701
|
+
[index_js_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
|
|
702
|
+
[index_js_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
|
|
815
703
|
};
|
|
816
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
817
|
-
function __importStar(arg0) {
|
|
818
|
-
throw new Error('Function not implemented.');
|
|
819
|
-
}
|