jagproject 26.3.23 → 26.3.26

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.
Files changed (209) hide show
  1. package/WAProto/GenerateStatics.sh +3 -4
  2. package/WAProto/WAProto.proto +1215 -511
  3. package/WAProto/fix-imports.js +73 -0
  4. package/WAProto/index.d.ts +14017 -0
  5. package/WAProto/index.js +64857 -145167
  6. package/engine-requirements.js +4 -7
  7. package/lib/Defaults/index.d.ts +74 -0
  8. package/lib/Defaults/index.js +49 -35
  9. package/lib/Defaults/phonenumber-mcc.json +223 -0
  10. package/lib/Defaults/wileys-version.json +2 -2
  11. package/lib/Signal/Group/ciphertext-message.d.ts +10 -0
  12. package/lib/Signal/Group/group-session-builder.d.ts +15 -0
  13. package/lib/Signal/Group/group-session-builder.js +5 -3
  14. package/lib/Signal/Group/group_cipher.d.ts +17 -0
  15. package/lib/Signal/Group/group_cipher.js +35 -46
  16. package/lib/Signal/Group/index.d.ts +12 -0
  17. package/lib/Signal/Group/index.js +21 -21
  18. package/lib/Signal/Group/keyhelper.d.ts +11 -0
  19. package/lib/Signal/Group/keyhelper.js +2 -2
  20. package/lib/Signal/Group/sender-chain-key.d.ts +14 -0
  21. package/lib/Signal/Group/sender-chain-key.js +5 -10
  22. package/lib/Signal/Group/sender-key-distribution-message.d.ts +17 -0
  23. package/lib/Signal/Group/sender-key-distribution-message.js +7 -7
  24. package/lib/Signal/Group/sender-key-message.d.ts +19 -0
  25. package/lib/Signal/Group/sender-key-message.js +8 -8
  26. package/lib/Signal/Group/sender-key-name.d.ts +18 -0
  27. package/lib/Signal/Group/sender-key-record.d.ts +31 -0
  28. package/lib/Signal/Group/sender-key-record.js +7 -16
  29. package/lib/Signal/Group/sender-key-state.d.ts +39 -0
  30. package/lib/Signal/Group/sender-key-state.js +25 -37
  31. package/lib/Signal/Group/sender-message-key.d.ts +12 -0
  32. package/lib/Signal/Group/sender-message-key.js +2 -2
  33. package/lib/Signal/libsignal.d.ts +5 -0
  34. package/lib/Signal/libsignal.js +358 -54
  35. package/lib/Signal/lid-mapping.d.ts +19 -0
  36. package/lib/Signal/lid-mapping.js +274 -0
  37. package/lib/Socket/Client/index.d.ts +3 -0
  38. package/lib/Socket/Client/index.js +2 -2
  39. package/lib/Socket/Client/types.d.ts +16 -0
  40. package/lib/Socket/Client/types.js +1 -0
  41. package/lib/Socket/Client/websocket.d.ts +13 -0
  42. package/lib/Socket/Client/websocket.js +18 -30
  43. package/lib/Socket/business.d.ts +202 -0
  44. package/lib/Socket/business.js +160 -38
  45. package/lib/Socket/chats.d.ts +111 -0
  46. package/lib/Socket/chats.js +497 -314
  47. package/lib/Socket/communities.d.ts +258 -0
  48. package/lib/Socket/communities.js +438 -0
  49. package/lib/Socket/community.js +333 -0
  50. package/lib/Socket/groups.d.ts +150 -0
  51. package/lib/Socket/groups.js +229 -91
  52. package/lib/Socket/index.d.ts +245 -0
  53. package/lib/Socket/index.js +9 -6
  54. package/lib/Socket/messages-recv.d.ts +187 -0
  55. package/lib/Socket/messages-recv.js +1105 -501
  56. package/lib/Socket/messages-send.d.ts +183 -0
  57. package/lib/Socket/messages-send.js +1181 -501
  58. package/lib/Socket/mex.d.ts +3 -0
  59. package/lib/Socket/mex.js +45 -0
  60. package/lib/Socket/newsletter.d.ts +160 -0
  61. package/lib/Socket/newsletter.js +227 -200
  62. package/lib/Socket/socket.d.ts +55 -0
  63. package/lib/Socket/socket.js +507 -206
  64. package/lib/Socket/usync.js +6 -6
  65. package/lib/Store/index.js +17 -5
  66. package/lib/Store/make-cache-manager-store.js +83 -0
  67. package/lib/Store/make-in-memory-store.js +48 -89
  68. package/lib/Store/make-ordered-dictionary.js +1 -1
  69. package/lib/Types/Auth.d.ts +116 -0
  70. package/lib/Types/Bussines.d.ts +25 -0
  71. package/lib/Types/Bussines.js +2 -0
  72. package/lib/Types/Call.d.ts +15 -0
  73. package/lib/Types/Chat.d.ts +123 -0
  74. package/lib/Types/Chat.js +7 -1
  75. package/lib/Types/Contact.d.ts +24 -0
  76. package/lib/Types/Events.d.ts +237 -0
  77. package/lib/Types/Events.js +1 -0
  78. package/lib/Types/GroupMetadata.d.ts +67 -0
  79. package/lib/Types/Label.d.ts +47 -0
  80. package/lib/Types/Label.js +1 -3
  81. package/lib/Types/LabelAssociation.d.ts +30 -0
  82. package/lib/Types/LabelAssociation.js +1 -3
  83. package/lib/Types/Message.d.ts +305 -0
  84. package/lib/Types/Message.js +9 -5
  85. package/lib/Types/MexUpdates.js +11 -0
  86. package/lib/Types/Newsletter.d.ts +135 -0
  87. package/lib/Types/Newsletter.js +36 -11
  88. package/lib/Types/Product.d.ts +79 -0
  89. package/lib/Types/Signal.d.ts +76 -0
  90. package/lib/Types/Signal.js +1 -0
  91. package/lib/Types/Socket.d.ts +133 -0
  92. package/lib/Types/Socket.js +1 -0
  93. package/lib/Types/State.d.ts +39 -0
  94. package/lib/Types/State.js +12 -0
  95. package/lib/Types/USync.d.ts +26 -0
  96. package/lib/Types/USync.js +1 -0
  97. package/lib/Types/index.d.ts +65 -0
  98. package/lib/Types/index.js +14 -14
  99. package/lib/Utils/audioToBuffer.js +31 -0
  100. package/lib/Utils/auth-utils.d.ts +19 -0
  101. package/lib/Utils/auth-utils.js +222 -123
  102. package/lib/Utils/baileys-event-stream.js +60 -0
  103. package/lib/Utils/bridge-runtime.d.ts +1 -0
  104. package/lib/Utils/bridge-runtime.js +14 -0
  105. package/lib/Utils/browser-utils.d.ts +4 -0
  106. package/lib/Utils/browser-utils.js +38 -29
  107. package/lib/Utils/business.d.ts +23 -0
  108. package/lib/Utils/business.js +54 -48
  109. package/lib/Utils/chat-utils.d.ts +70 -0
  110. package/lib/Utils/chat-utils.js +284 -189
  111. package/lib/Utils/crypto.d.ts +37 -0
  112. package/lib/Utils/crypto.js +16 -41
  113. package/lib/Utils/decode-wa-message.d.ts +48 -0
  114. package/lib/Utils/decode-wa-message.js +128 -48
  115. package/lib/Utils/event-buffer.d.ts +34 -0
  116. package/lib/Utils/event-buffer.js +124 -62
  117. package/lib/Utils/generics.d.ts +91 -0
  118. package/lib/Utils/generics.js +154 -138
  119. package/lib/Utils/history.d.ts +22 -0
  120. package/lib/Utils/history.js +77 -34
  121. package/lib/Utils/identity-change-handler.d.ts +37 -0
  122. package/lib/Utils/identity-change-handler.js +54 -0
  123. package/lib/Utils/index.d.ts +22 -0
  124. package/lib/Utils/index.js +32 -19
  125. package/lib/Utils/link-preview.d.ts +21 -0
  126. package/lib/Utils/link-preview.js +12 -17
  127. package/lib/Utils/logger.d.ts +13 -0
  128. package/lib/Utils/lt-hash.d.ts +8 -0
  129. package/lib/Utils/lt-hash.js +2 -43
  130. package/lib/Utils/make-mutex.d.ts +9 -0
  131. package/lib/Utils/make-mutex.js +21 -27
  132. package/lib/Utils/message-retry-manager.d.ts +110 -0
  133. package/lib/Utils/message-retry-manager.js +143 -45
  134. package/lib/Utils/messages-media.d.ts +130 -0
  135. package/lib/Utils/messages-media.js +429 -502
  136. package/lib/Utils/messages-newsletter.d.ts +84 -0
  137. package/lib/Utils/messages-newsletter.js +295 -0
  138. package/lib/Utils/messages.d.ts +92 -0
  139. package/lib/Utils/messages.js +1025 -674
  140. package/lib/Utils/noise-handler.d.ts +20 -0
  141. package/lib/Utils/noise-handler.js +145 -91
  142. package/lib/Utils/pre-key-manager.d.ts +28 -0
  143. package/lib/Utils/pre-key-manager.js +112 -0
  144. package/lib/Utils/process-message.d.ts +60 -0
  145. package/lib/Utils/process-message.js +316 -184
  146. package/lib/Utils/reporting-utils.d.ts +11 -0
  147. package/lib/Utils/reporting-utils.js +262 -0
  148. package/lib/Utils/resolve-jid.d.ts +43 -0
  149. package/lib/Utils/resolve-jid.js +95 -0
  150. package/lib/Utils/rust-bridge-shim.d.ts +22 -0
  151. package/lib/Utils/rust-bridge-shim.js +70 -0
  152. package/lib/Utils/serial-task-queue.js +29 -0
  153. package/lib/Utils/signal.d.ts +34 -0
  154. package/lib/Utils/signal.js +56 -39
  155. package/lib/Utils/streamToBuffer.js +17 -0
  156. package/lib/Utils/sync-action-utils.d.ts +19 -0
  157. package/lib/Utils/sync-action-utils.js +52 -0
  158. package/lib/Utils/tc-token-utils.d.ts +12 -0
  159. package/lib/Utils/tc-token-utils.js +20 -0
  160. package/lib/Utils/use-mongo-file-auth-state.js +71 -0
  161. package/lib/Utils/use-multi-file-auth-state.d.ts +13 -0
  162. package/lib/Utils/use-multi-file-auth-state.js +11 -12
  163. package/lib/Utils/use-single-file-auth-state.js +73 -0
  164. package/lib/Utils/validate-connection.d.ts +11 -0
  165. package/lib/Utils/validate-connection.js +59 -82
  166. package/lib/Utils/wileys-event-stream.js +1 -61
  167. package/lib/WABinary/constants.d.ts +28 -0
  168. package/lib/WABinary/decode.d.ts +7 -0
  169. package/lib/WABinary/decode.js +39 -4
  170. package/lib/WABinary/encode.d.ts +3 -0
  171. package/lib/WABinary/encode.js +17 -11
  172. package/lib/WABinary/generic-utils.d.ts +15 -0
  173. package/lib/WABinary/generic-utils.js +46 -18
  174. package/lib/WABinary/index.d.ts +6 -0
  175. package/lib/WABinary/index.js +9 -5
  176. package/lib/WABinary/jid-utils.d.ts +48 -0
  177. package/lib/WABinary/jid-utils.js +67 -37
  178. package/lib/WABinary/types.d.ts +19 -0
  179. package/lib/WABinary/types.js +34 -0
  180. package/lib/WAM/BinaryInfo.d.ts +9 -0
  181. package/lib/WAM/constants.d.ts +40 -0
  182. package/lib/WAM/constants.js +19183 -11678
  183. package/lib/WAM/encode.d.ts +3 -0
  184. package/lib/WAM/encode.js +15 -17
  185. package/lib/WAM/index.d.ts +4 -0
  186. package/lib/WAM/index.js +3 -3
  187. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +10 -0
  188. package/lib/WAUSync/Protocols/USyncContactProtocol.js +6 -6
  189. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +23 -0
  190. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +9 -9
  191. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +13 -0
  192. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +6 -6
  193. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +13 -0
  194. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +7 -8
  195. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +26 -0
  196. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +18 -17
  197. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +10 -0
  198. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +11 -3
  199. package/lib/WAUSync/Protocols/index.d.ts +5 -0
  200. package/lib/WAUSync/Protocols/index.js +6 -4
  201. package/lib/WAUSync/USyncQuery.d.ts +29 -0
  202. package/lib/WAUSync/USyncQuery.js +38 -30
  203. package/lib/WAUSync/USyncUser.d.ts +13 -0
  204. package/lib/WAUSync/index.d.ts +4 -0
  205. package/lib/WAUSync/index.js +3 -3
  206. package/lib/index.d.ts +12 -0
  207. package/lib/index.js +3 -5
  208. package/package.json +7 -4
  209. package/LICENSE +0 -21
@@ -32,61 +32,80 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
35
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.prepareStream = exports.getHttpStream = exports.getStream = exports.toBuffer = exports.toReadable = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.extractVideoThumb = exports.hkdfInfoKey = void 0;
36
+ exports.prepareStream = exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.uploadWithNodeHttp = 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
37
  exports.getMediaKeys = getMediaKeys;
41
- exports.uploadFile = uploadFile;
42
- exports.vid2jpg = vid2jpg;
43
38
  exports.getAudioDuration = getAudioDuration;
44
39
  exports.getAudioWaveform = getAudioWaveform;
45
40
  exports.generateThumbnail = generateThumbnail;
46
41
  exports.extensionForMediaMessage = extensionForMediaMessage;
42
+ const events_1 = require("events");
47
43
  const boom_1 = require("@hapi/boom");
48
- const axios_1 = __importDefault(require("axios"));
49
- const form_data_1 = __importDefault(require("form-data"));
50
- const cheerio = __importStar(require("cheerio"));
51
44
  const Crypto = __importStar(require("crypto"));
52
- const events_1 = require("events");
53
45
  const fs_1 = require("fs");
54
46
  const os_1 = require("os");
55
47
  const path_1 = require("path");
56
- const jimp_1 = __importDefault(require("jimp"));
57
48
  const stream_1 = require("stream");
58
- const child_process_1 = require("child_process");
59
- const WAProto_1 = require("../../WAProto");
60
- const Defaults_1 = require("../Defaults");
61
- const WABinary_1 = require("../WABinary");
62
- const crypto_1 = require("./crypto");
63
- const generics_1 = require("./generics");
49
+ const url_1 = require("url");
50
+ const index_js_1 = require("../../WAProto/index.js");
51
+ const index_js_2 = require("../Defaults/index.js");
52
+ const index_js_3 = require("../WABinary/index.js");
53
+ const crypto_js_1 = require("./crypto.js");
54
+ const generics_js_1 = require("./generics.js");
64
55
  const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
65
56
  const getImageProcessingLibrary = async () => {
66
- const [_jimp, sharp] = await Promise.all([
67
- (async () => {
68
- const jimp = await (Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }));
69
- return jimp;
70
- })(),
71
- (async () => {
72
- const sharp = await (Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { }));
73
- return sharp;
74
- })()
75
- ]);
57
+ //@ts-ignore
58
+ const [jimp, sharp] = await Promise.all([Promise.resolve().then(() => __importStar(require('jimp'))).catch(() => { }), Promise.resolve().then(() => __importStar(require('sharp'))).catch(() => { })]);
76
59
  if (sharp) {
77
60
  return { sharp };
78
61
  }
79
- const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
80
62
  if (jimp) {
81
63
  return { jimp };
82
64
  }
83
65
  throw new boom_1.Boom('No image processing library available');
84
66
  };
85
67
  const hkdfInfoKey = (type) => {
86
- const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type];
68
+ const hkdfInfo = index_js_2.MEDIA_HKDF_KEY_MAPPING[type];
87
69
  return `WhatsApp ${hkdfInfo} Keys`;
88
70
  };
89
71
  exports.hkdfInfoKey = hkdfInfoKey;
72
+ const getRawMediaUploadData = async (media, mediaType, logger) => {
73
+ const { stream } = await (0, exports.getStream)(media);
74
+ const hasher = Crypto.createHash('sha256');
75
+ const filePath = (0, path_1.join)((0, os_1.tmpdir)(), mediaType + (0, generics_js_1.generateMessageIDV2)());
76
+ const fileWriteStream = (0, fs_1.createWriteStream)(filePath);
77
+ let fileLength = 0;
78
+ try {
79
+ for await (const data of stream) {
80
+ fileLength += data.length;
81
+ hasher.update(data);
82
+ if (!fileWriteStream.write(data)) {
83
+ await (0, events_1.once)(fileWriteStream, 'drain');
84
+ }
85
+ }
86
+ fileWriteStream.end();
87
+ await (0, events_1.once)(fileWriteStream, 'finish');
88
+ stream.destroy();
89
+ const fileSha256 = hasher.digest();
90
+ return {
91
+ filePath: filePath,
92
+ fileSha256,
93
+ fileLength
94
+ };
95
+ }
96
+ catch (error) {
97
+ fileWriteStream.destroy();
98
+ stream.destroy();
99
+ try {
100
+ await fs_1.promises.unlink(filePath);
101
+ }
102
+ catch {
103
+ //
104
+ }
105
+ throw error;
106
+ }
107
+ };
108
+ exports.getRawMediaUploadData = getRawMediaUploadData;
90
109
  /** generates all the keys required to encrypt/decrypt & sign a media message */
91
110
  async function getMediaKeys(buffer, mediaType) {
92
111
  if (!buffer) {
@@ -96,215 +115,41 @@ async function getMediaKeys(buffer, mediaType) {
96
115
  buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
97
116
  }
98
117
  // expand using HKDF to 112 bytes, also pass in the relevant app info
99
- const expandedMediaKey = await (0, crypto_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
118
+ const expandedMediaKey = (0, crypto_js_1.hkdf)(buffer, 112, { info: (0, exports.hkdfInfoKey)(mediaType) });
100
119
  return {
101
120
  iv: expandedMediaKey.slice(0, 16),
102
121
  cipherKey: expandedMediaKey.slice(16, 48),
103
- macKey: expandedMediaKey.slice(48, 80),
122
+ macKey: expandedMediaKey.slice(48, 80)
104
123
  };
105
124
  }
106
- async function uploadFile(buffer, logger) {
107
- const { fromBuffer } = await Promise.resolve().then(() => __importStar(require('file-type')));
108
- const fileType = await fromBuffer(buffer);
109
- if (!fileType)
110
- throw new Error("Failed to detect file type.");
111
- const { ext, mime } = fileType;
112
- const services = [
113
- {
114
- name: "catbox",
115
- url: "https://catbox.moe/user/api.php",
116
- buildForm: () => {
117
- const form = new form_data_1.default();
118
- form.append("fileToUpload", buffer, {
119
- filename: `file.${ext}`,
120
- contentType: mime || "application/octet-stream"
121
- });
122
- form.append("reqtype", "fileupload");
123
- return form;
124
- },
125
- parseResponse: res => res.data
126
- },
127
- {
128
- name: "pdi.moe",
129
- url: "https://scdn.pdi.moe/upload",
130
- buildForm: () => {
131
- const form = new form_data_1.default();
132
- form.append("file", buffer, {
133
- filename: `file.${ext}`,
134
- contentType: mime
135
- });
136
- return form;
137
- },
138
- parseResponse: res => res.data.result.url
139
- },
140
- {
141
- name: "qu.ax",
142
- url: "https://qu.ax/upload.php",
143
- buildForm: () => {
144
- const form = new form_data_1.default();
145
- form.append("files[]", buffer, {
146
- filename: `file.${ext}`,
147
- contentType: mime || "application/octet-stream"
148
- });
149
- return form;
150
- },
151
- parseResponse: res => {
152
- var _a, _b, _c;
153
- if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
154
- throw new Error("Failed to get URL from qu.ax");
155
- return res.data.files[0].url;
156
- }
157
- },
158
- {
159
- name: "uguu.se",
160
- url: "https://uguu.se/upload.php",
161
- buildForm: () => {
162
- const form = new form_data_1.default();
163
- form.append("files[]", buffer, {
164
- filename: `file.${ext}`,
165
- contentType: mime || "application/octet-stream"
166
- });
167
- return form;
168
- },
169
- parseResponse: res => {
170
- var _a, _b, _c;
171
- if (!((_c = (_b = (_a = res.data) === null || _a === void 0 ? void 0 : _a.files) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.url))
172
- throw new Error("Failed to get URL from uguu.se");
173
- return res.data.files[0].url;
174
- }
175
- },
176
- {
177
- name: "tmpfiles",
178
- url: "https://tmpfiles.org/api/v1/upload",
179
- buildForm: () => {
180
- const form = new form_data_1.default();
181
- form.append("file", buffer, {
182
- filename: `file.${ext}`,
183
- contentType: mime
184
- });
185
- return form;
186
- },
187
- parseResponse: res => {
188
- const match = res.data.data.url.match(/https:\/\/tmpfiles\.org\/(.*)/);
189
- if (!match)
190
- throw new Error("Failed to parse tmpfiles URL.");
191
- return `https://tmpfiles.org/dl/${match[1]}`;
192
- }
193
- }
194
- ];
195
- for (const service of services) {
196
- try {
197
- const form = service.buildForm();
198
- const res = await axios_1.default.post(service.url, form, {
199
- headers: form.getHeaders()
200
- });
201
- const url = service.parseResponse(res);
202
- return url;
203
- }
204
- catch (error) {
205
- logger === null || logger === void 0 ? void 0 : logger.debug(`[${service.name}] eror:`, (error === null || error === void 0 ? void 0 : error.message) || error);
206
- }
207
- }
208
- throw new Error("All upload services failed.");
209
- }
210
- async function vid2jpg(videoUrl) {
211
- try {
212
- const { data } = await axios_1.default.get(`https://ezgif.com/video-to-jpg?url=${encodeURIComponent(videoUrl)}`);
213
- const $ = cheerio.load(data);
214
- const fileToken = $('input[name="file"]').attr("value");
215
- if (!fileToken) {
216
- throw new Error("Failed to retrieve file token. The video URL may be invalid or inaccessible.");
217
- }
218
- const formData = new URLSearchParams();
219
- formData.append("file", fileToken);
220
- formData.append("end", "1");
221
- formData.append("video-to-jpg", "Convert to JPG!");
222
- const convert = await axios_1.default.post(`https://ezgif.com/video-to-jpg/${fileToken}`, formData);
223
- const $2 = cheerio.load(convert.data);
224
- let imageUrl = $2("#output img").first().attr("src");
225
- if (!imageUrl) {
226
- throw new Error("Could not locate the converted image output.");
227
- }
228
- if (imageUrl.startsWith("//")) {
229
- imageUrl = "https:" + imageUrl;
230
- }
231
- else if (imageUrl.startsWith("/")) {
232
- const cdnMatch = imageUrl.match(/\/(s\d+\..+?)\/.*/);
233
- if (cdnMatch) {
234
- imageUrl = "https://" + imageUrl.slice(2);
235
- }
236
- else {
237
- imageUrl = "https://ezgif.com" + imageUrl;
238
- }
239
- }
240
- return imageUrl;
241
- }
242
- catch (error) {
243
- throw new Error("Failed to convert video to JPG: " + error.message);
244
- }
245
- }
246
- /**
247
- * Extracts video thumbnail using FFmpeg
248
- */
249
- const extractVideoThumb = async (videoPath, time = '00:00:00', size = { width: 256 }) => {
250
- return new Promise((resolve, reject) => {
251
- const args = [
252
- '-ss', time,
253
- '-i', videoPath,
254
- '-y',
255
- '-vf', `scale=${size.width}:-1`,
256
- '-vframes', '1',
257
- '-f', 'image2',
258
- '-vcodec', 'mjpeg',
259
- 'pipe:1'
260
- ];
261
- const ffmpeg = (0, child_process_1.spawn)('ffmpeg', args);
262
- const chunks = [];
263
- let errorOutput = '';
264
- ffmpeg.stdout.on('data', chunk => chunks.push(chunk));
265
- ffmpeg.stderr.on('data', data => {
266
- errorOutput += data.toString();
267
- });
268
- ffmpeg.on('error', reject);
269
- ffmpeg.on('close', code => {
270
- if (code === 0) return resolve(Buffer.concat(chunks));
271
- reject(new Error(`ffmpeg exited with code ${code}\n${errorOutput}`));
272
- });
273
- });
274
- };
275
- exports.extractVideoThumb = extractVideoThumb;
276
125
  const extractImageThumb = async (bufferOrFilePath, width = 32) => {
277
- var _a, _b;
126
+ // TODO: Move entirely to sharp, removing jimp as it supports readable streams
127
+ // This will have positive speed and performance impacts as well as minimizing RAM usage.
278
128
  if (bufferOrFilePath instanceof stream_1.Readable) {
279
129
  bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
280
130
  }
281
131
  const lib = await getImageProcessingLibrary();
282
- if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
132
+ if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
283
133
  const img = lib.sharp.default(bufferOrFilePath);
284
134
  const dimensions = await img.metadata();
285
- const buffer = await img
286
- .resize(width)
287
- .jpeg({ quality: 50 })
288
- .toBuffer();
135
+ const buffer = await img.resize(width).jpeg({ quality: 50 }).toBuffer();
289
136
  return {
290
137
  buffer,
291
138
  original: {
292
139
  width: dimensions.width,
293
- height: dimensions.height,
294
- },
140
+ height: dimensions.height
141
+ }
295
142
  };
296
143
  }
297
- else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
298
- const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp;
299
- const jimp = await read(bufferOrFilePath);
144
+ else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'object') {
145
+ const jimp = await lib.jimp.Jimp.read(bufferOrFilePath);
300
146
  const dimensions = {
301
- width: jimp.getWidth(),
302
- height: jimp.getHeight()
147
+ width: jimp.width,
148
+ height: jimp.height
303
149
  };
304
150
  const buffer = await jimp
305
- .quality(50)
306
- .resize(width, AUTO, RESIZE_BILINEAR)
307
- .getBufferAsync(MIME_JPEG);
151
+ .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
152
+ .getBuffer('image/jpeg', { quality: 50 });
308
153
  return {
309
154
  buffer,
310
155
  original: dimensions
@@ -315,176 +160,130 @@ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
315
160
  }
316
161
  };
317
162
  exports.extractImageThumb = extractImageThumb;
318
- const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
319
- .replace(/\+/g, '-')
320
- .replace(/\//g, '_')
321
- .replace(/\=+$/, '')));
163
+ const encodeBase64EncodedStringForUpload = (b64) => encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''));
322
164
  exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
323
- const generateProfilePicture = async (mediaUpload) => {
324
- let bufferOrFilePath;
325
- let img;
165
+ const generateProfilePicture = async (mediaUpload, dimensions) => {
166
+ let buffer;
167
+ const { width: w = 640, height: h = 640 } = dimensions || {};
326
168
  if (Buffer.isBuffer(mediaUpload)) {
327
- bufferOrFilePath = mediaUpload;
169
+ buffer = mediaUpload;
328
170
  }
329
- else if ('url' in mediaUpload) {
330
- bufferOrFilePath = mediaUpload.url.toString();
171
+ else {
172
+ // Use getStream to handle all WAMediaUpload types (Buffer, Stream, URL)
173
+ const { stream } = await (0, exports.getStream)(mediaUpload);
174
+ // Convert the resulting stream to a buffer
175
+ buffer = await (0, exports.toBuffer)(stream);
176
+ }
177
+ const lib = await getImageProcessingLibrary();
178
+ let img;
179
+ if ('sharp' in lib && typeof lib.sharp?.default === 'function') {
180
+ img = lib.sharp
181
+ .default(buffer)
182
+ .resize(w, h)
183
+ .jpeg({
184
+ quality: 50
185
+ })
186
+ .toBuffer();
187
+ }
188
+ else if ('jimp' in lib && typeof lib.jimp?.Jimp === 'function') {
189
+ const jimp = await lib.jimp.Jimp.read(buffer);
190
+ const min = Math.min(jimp.width, jimp.height);
191
+ const cropped = jimp.crop({ x: 0, y: 0, w: min, h: min });
192
+ img = cropped.resize({ w, h, mode: lib.jimp.ResizeStrategy.BILINEAR }).getBuffer('image/jpeg', { quality: 50 });
331
193
  }
332
194
  else {
333
- bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
195
+ throw new boom_1.Boom('No image processing library available');
334
196
  }
335
- const jimp = await jimp_1.default.read(bufferOrFilePath);
336
- const cropped = jimp.getWidth() > jimp.getHeight() ? jimp.resize(550, -1) : jimp.resize(-1, 650);
337
- img = cropped
338
- .quality(100)
339
- .getBufferAsync(jimp_1.default.MIME_JPEG);
340
197
  return {
341
- img: await img,
198
+ img: await img
342
199
  };
343
200
  };
344
201
  exports.generateProfilePicture = generateProfilePicture;
345
202
  /** gets the SHA256 of the given media message */
346
203
  const mediaMessageSHA256B64 = (message) => {
347
204
  const media = Object.values(message)[0];
348
- return (media === null || media === void 0 ? void 0 : media.fileSha256) && Buffer.from(media.fileSha256).toString('base64');
205
+ return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64');
349
206
  };
350
207
  exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
351
- async function getAudioDuration(buffer) {
208
+ async function getAudioDuration(buffer, mimeType) {
352
209
  const musicMetadata = await Promise.resolve().then(() => __importStar(require('music-metadata')));
353
210
  let metadata;
354
211
  const options = {
355
- duration: true
212
+ duration: true,
213
+ ...(mimeType ? { mimeType } : {})
356
214
  };
357
215
  if (Buffer.isBuffer(buffer)) {
358
- metadata = await musicMetadata.parseBuffer(buffer, undefined, options);
216
+ metadata = await musicMetadata.parseBuffer(buffer, mimeType || undefined, options);
359
217
  }
360
218
  else if (typeof buffer === 'string') {
361
- metadata = await musicMetadata.parseFile(buffer, options);
219
+ // parseFile tidak support mimeType langsung, tapi kita bisa baca sebagai buffer
220
+ // supaya mimeType hint bisa dipass — penting untuk m4a/aac yang nama filenya tanpa ekstensi
221
+ try {
222
+ metadata = await musicMetadata.parseFile(buffer, options);
223
+ }
224
+ catch (_) {
225
+ // fallback: baca file sebagai buffer lalu parse dengan mimeType
226
+ const { readFileSync } = await Promise.resolve().then(() => __importStar(require('fs')));
227
+ const buf = readFileSync(buffer);
228
+ metadata = await musicMetadata.parseBuffer(buf, mimeType || undefined, options);
229
+ }
362
230
  }
363
231
  else {
364
- metadata = await musicMetadata.parseStream(buffer, undefined, options);
232
+ metadata = await musicMetadata.parseStream(buffer, mimeType || undefined, options);
365
233
  }
366
- return metadata.format.duration;
234
+ const dur = metadata.format.duration;
235
+ // Jangan return NaN/undefined — WhatsApp akan tampilkan "Loading..." kalau seconds invalid
236
+ return (typeof dur === 'number' && !isNaN(dur) && isFinite(dur)) ? Math.round(dur) : 0;
367
237
  }
368
- // Ganti / tempelkan seluruh function getAudioWaveform berikut
238
+ /**
239
+ referenced from and modifying https://github.com/wppconnect-team/wa-js/blob/main/src/chat/functions/prepareAudioWaveform.ts
240
+ */
369
241
  async function getAudioWaveform(buffer, logger) {
370
242
  try {
371
- // prefer ffmpeg-based robust extraction (supports opus/ogg/mp3/m4a etc.)
372
- const { PassThrough } = require('stream');
373
- const ffmpeg = require('fluent-ffmpeg');
374
- // try to use ffmpeg-static if available (bundled binary)
375
- try {
376
- const ffmpegPath = require('ffmpeg-static');
377
- if (ffmpegPath) {
378
- ffmpeg.setFfmpegPath(ffmpegPath);
379
- }
380
- } catch (e) {
381
- // ffmpeg-static not installed — hope system ffmpeg exists in PATH
382
- logger?.debug?.('ffmpeg-static not available, relying on system ffmpeg');
383
- }
384
-
385
- // normalize input to Buffer
243
+ // @ts-ignore
244
+ const { default: decoder } = await Promise.resolve().then(() => __importStar(require('audio-decode')));
386
245
  let audioData;
387
246
  if (Buffer.isBuffer(buffer)) {
388
247
  audioData = buffer;
389
- } else if (typeof buffer === 'string') {
390
- // path given
391
- const fs = require('fs');
392
- audioData = await (async () => {
393
- if (fs.existsSync(buffer)) return fs.readFileSync(buffer);
394
- // otherwise try to read as URL/stream via toBuffer helper if available
395
- return await exports.toBuffer(buffer);
396
- })();
397
- } else {
398
- audioData = await exports.toBuffer(buffer);
399
248
  }
400
-
401
- // convert to raw PCM 16-bit signed little endian mono using ffmpeg in-memory
402
- return await new Promise((resolve, reject) => {
403
- try {
404
- const chunks = [];
405
- const inputStream = new PassThrough();
406
- inputStream.end(audioData);
407
-
408
- // pipe ffmpeg -> raw PCM signed 16 bit LE, mono, 16000/44100 sample rate doesn't matter much
409
- ffmpeg(inputStream)
410
- .format('s16le')
411
- .audioChannels(1)
412
- .audioFrequency(16000)
413
- .noVideo()
414
- .on('error', (err) => {
415
- logger?.debug?.('ffmpeg waveform error', err?.message || err);
416
- // fallback: try a simpler approach using audio-decode if available
417
- try {
418
- (async () => {
419
- const { default: decoder } = await eval('import("audio-decode")');
420
- const audioBuf = await exports.toBuffer(audioData);
421
- const audioDecoded = await decoder(audioBuf);
422
- const raw = audioDecoded.getChannelData(0);
423
- const result = normalizeToUint8(raw);
424
- resolve(result);
425
- })();
426
- } catch (e) {
427
- reject(err);
428
- }
429
- })
430
- .on('end', () => {
431
- try {
432
- const buffer = Buffer.concat(chunks);
433
- // buffer contains 16-bit signed PCM little endian
434
- // convert to Float32 array (-1..1) first
435
- const samples = new Int16Array(buffer.buffer, buffer.byteOffset, Math.floor(buffer.length / 2));
436
- const floatSamples = new Float32Array(samples.length);
437
- for (let i = 0; i < samples.length; i++) {
438
- floatSamples[i] = samples[i] / 32768; // normalize int16 to [-1,1]
439
- }
440
- const result = normalizeToUint8(floatSamples);
441
- resolve(result);
442
- } catch (e) {
443
- reject(e);
444
- }
445
- })
446
- .pipe()
447
- .on('data', (c) => chunks.push(c));
448
- } catch (e) {
449
- reject(e);
450
- }
451
- });
452
- } catch (e) {
453
- logger?.debug?.(e);
454
- return undefined;
455
- }
456
- }
457
-
458
- // helper: normalize float array (-1..1) into Uint8Array (0..100 per bar)
459
- function normalizeToUint8(data, samples = 64) {
460
- try {
461
- const blockSize = Math.floor(data.length / samples) || 1;
462
- const filtered = [];
249
+ else if (typeof buffer === 'string') {
250
+ const rStream = (0, fs_1.createReadStream)(buffer);
251
+ audioData = await (0, exports.toBuffer)(rStream);
252
+ }
253
+ else {
254
+ audioData = await (0, exports.toBuffer)(buffer);
255
+ }
256
+ // Skip audio-decode for large buffers (> 3MB)
257
+ if (audioData.length > 3 * 1024 * 1024) {
258
+ return undefined;
259
+ }
260
+ const audioBuffer = await decoder(audioData);
261
+ const rawData = audioBuffer.getChannelData(0);
262
+ const samples = 64;
263
+ const blockSize = Math.floor(rawData.length / samples);
264
+ const filteredData = [];
463
265
  for (let i = 0; i < samples; i++) {
464
- let blockStart = i * blockSize;
266
+ const blockStart = blockSize * i;
465
267
  let sum = 0;
466
- for (let j = 0; j < blockSize && (blockStart + j) < data.length; j++) {
467
- sum += Math.abs(data[blockStart + j]);
268
+ for (let j = 0; j < blockSize; j++) {
269
+ sum = sum + Math.abs(rawData[blockStart + j]);
468
270
  }
469
- filtered.push(sum / blockSize);
271
+ filteredData.push(sum / blockSize);
470
272
  }
471
- const max = Math.max(...filtered) || 1;
472
- const normalized = filtered.map(v => Math.floor((v / max) * 100));
473
- return new Uint8Array(normalized);
474
- } catch (e) {
475
- return undefined;
273
+ const multiplier = Math.pow(Math.max(...filteredData), -1);
274
+ const normalizedData = filteredData.map(n => n * multiplier);
275
+ const waveform = new Uint8Array(normalizedData.map(n => Math.floor(100 * n)));
276
+ return waveform;
277
+ }
278
+ catch (e) {
476
279
  }
477
280
  }
478
-
479
281
  const toReadable = (buffer) => {
480
- const readable = new stream_1.Readable({ read: () => { } });
282
+ const readable = new stream_1.Readable({ read: () => { }, highWaterMark: 64 * 1024 });
481
283
  readable.push(buffer);
482
284
  readable.push(null);
483
285
  return readable;
484
286
  };
485
- // ================================================================
486
- // Tambahan utilitas media (opsional, jika Wileyss punya bawaan sendiri biarkan bagian bawah tetap ada)
487
- // ================================================================
488
287
  exports.toReadable = toReadable;
489
288
  const toBuffer = async (stream) => {
490
289
  const chunks = [];
@@ -502,7 +301,12 @@ const getStream = async (item, opts) => {
502
301
  if ('stream' in item) {
503
302
  return { stream: item.stream, type: 'readable' };
504
303
  }
505
- if (item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) {
304
+ const urlStr = item.url.toString();
305
+ if (urlStr.startsWith('data:')) {
306
+ const buffer = Buffer.from(urlStr.split(',')[1], 'base64');
307
+ return { stream: (0, exports.toReadable)(buffer), type: 'buffer' };
308
+ }
309
+ if (urlStr.startsWith('http://') || urlStr.startsWith('https://')) {
506
310
  return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' };
507
311
  }
508
312
  return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' };
@@ -510,7 +314,6 @@ const getStream = async (item, opts) => {
510
314
  exports.getStream = getStream;
511
315
  /** generates a thumbnail for a given media, if required */
512
316
  async function generateThumbnail(file, mediaType, options) {
513
- var _a;
514
317
  let thumbnail;
515
318
  let originalImageDimensions;
516
319
  if (mediaType === 'image') {
@@ -519,37 +322,12 @@ async function generateThumbnail(file, mediaType, options) {
519
322
  if (original.width && original.height) {
520
323
  originalImageDimensions = {
521
324
  width: original.width,
522
- height: original.height,
325
+ height: original.height
523
326
  };
524
327
  }
525
328
  }
526
329
  else if (mediaType === 'video') {
527
- try {
528
- let videoPath = file;
529
- if (Buffer.isBuffer(file) || file instanceof stream_1.Readable) {
530
- videoPath = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.mp4');
531
- const buffer = Buffer.isBuffer(file) ? file : await (0, exports.toBuffer)(file);
532
- await fs_1.promises.writeFile(videoPath, buffer);
533
- }
534
- const thumbnailBuffer = await (0, exports.extractVideoThumb)(videoPath);
535
- const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageIDV2)() + '.jpg');
536
- await fs_1.promises.writeFile(imgFilename, thumbnailBuffer);
537
- const { buffer: processedThumbnailBuffer, original } = await (0, exports.extractImageThumb)(imgFilename);
538
- thumbnail = processedThumbnailBuffer.toString('base64');
539
- if (original.width && original.height) {
540
- originalImageDimensions = {
541
- width: original.width,
542
- height: original.height,
543
- };
544
- }
545
- await fs_1.promises.unlink(imgFilename);
546
- if (videoPath !== file) {
547
- await fs_1.promises.unlink(videoPath);
548
- }
549
- }
550
- catch (err) {
551
- (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
552
- }
330
+ // Video thumbnail generation skipped (ffmpeg removed)
553
331
  }
554
332
  return {
555
333
  thumbnail,
@@ -557,67 +335,33 @@ async function generateThumbnail(file, mediaType, options) {
557
335
  };
558
336
  }
559
337
  const getHttpStream = async (url, options = {}) => {
560
- const fetched = await axios_1.default.get(url.toString(), { ...options, responseType: 'stream' });
561
- return fetched.data;
562
- };
563
- exports.getHttpStream = getHttpStream;
564
- const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
565
- const { stream, type } = await (0, exports.getStream)(media, opts);
566
- logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
567
- let bodyPath;
568
- let didSaveToTmpPath = false;
569
- try {
570
- const buffer = await (0, exports.toBuffer)(stream);
571
- if (type === 'file') {
572
- bodyPath = media.url;
573
- }
574
- else if (saveOriginalFileIfRequired) {
575
- bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
576
- (0, fs_1.writeFileSync)(bodyPath, buffer);
577
- didSaveToTmpPath = true;
578
- }
579
- const fileLength = buffer.length;
580
- const fileSha256 = Crypto.createHash('sha256').update(buffer).digest();
581
- stream === null || stream === void 0 ? void 0 : stream.destroy();
582
- logger === null || logger === void 0 ? void 0 : logger.debug('prepare stream data successfully');
583
- return {
584
- mediaKey: undefined,
585
- encWriteStream: buffer,
586
- fileLength,
587
- fileSha256,
588
- fileEncSha256: undefined,
589
- bodyPath,
590
- didSaveToTmpPath
591
- };
592
- }
593
- catch (error) {
594
- stream.destroy();
595
- if (didSaveToTmpPath) {
596
- try {
597
- await fs_1.promises.unlink(bodyPath);
598
- }
599
- catch (err) {
600
- logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
601
- }
602
- }
603
- throw error;
338
+ const response = await fetch(url.toString(), {
339
+ dispatcher: options.dispatcher,
340
+ method: 'GET',
341
+ headers: options.headers
342
+ });
343
+ if (!response.ok) {
344
+ throw new boom_1.Boom(`Failed to fetch stream from ${url}`, { statusCode: response.status, data: { url } });
604
345
  }
346
+ // @ts-ignore Node18+ Readable.fromWeb exists
347
+ return response.body instanceof stream_1.Readable ? response.body : stream_1.Readable.fromWeb(response.body);
605
348
  };
606
- exports.prepareStream = prepareStream;
607
- const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
349
+ exports.getHttpStream = getHttpStream;
350
+ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
608
351
  const { stream, type } = await (0, exports.getStream)(media, opts);
609
- logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
352
+ let finalStream = stream;
353
+ let opusConverted = false;
610
354
  const mediaKey = Crypto.randomBytes(32);
611
355
  const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType);
612
- const encWriteStream = new stream_1.Readable({ read: () => { } });
356
+ const encWriteStream = new stream_1.Readable({ read: () => { }, highWaterMark: 64 * 1024 });
613
357
  let bodyPath;
614
358
  let writeStream;
615
359
  let didSaveToTmpPath = false;
616
360
  if (type === 'file') {
617
- bodyPath = media.url;
361
+ bodyPath = media.url?.toString?.() || media.url;
618
362
  }
619
363
  else if (saveOriginalFileIfRequired) {
620
- bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageIDV2)());
364
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_js_1.generateMessageIDV2)());
621
365
  writeStream = (0, fs_1.createWriteStream)(bodyPath);
622
366
  didSaveToTmpPath = true;
623
367
  }
@@ -626,12 +370,17 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
626
370
  let hmac = Crypto.createHmac('sha256', macKey).update(iv);
627
371
  let sha256Plain = Crypto.createHash('sha256');
628
372
  let sha256Enc = Crypto.createHash('sha256');
373
+ const onChunk = (buff) => {
374
+ sha256Enc = sha256Enc.update(buff);
375
+ hmac = hmac.update(buff);
376
+ encWriteStream.push(buff);
377
+ };
629
378
  try {
630
- for await (const data of stream) {
379
+ for await (const data of finalStream) {
631
380
  fileLength += data.length;
632
- if (type === 'remote'
633
- && (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
634
- && fileLength + data.length > opts.maxContentLength) {
381
+ if (type === 'remote' &&
382
+ opts?.maxContentLength &&
383
+ fileLength + data.length > opts.maxContentLength) {
635
384
  throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
636
385
  data: { media, type }
637
386
  });
@@ -651,9 +400,10 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
651
400
  const fileEncSha256 = sha256Enc.digest();
652
401
  encWriteStream.push(mac);
653
402
  encWriteStream.push(null);
654
- writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
655
- stream.destroy();
656
- logger === null || logger === void 0 ? void 0 : logger.debug('encrypted data successfully');
403
+ writeStream?.end();
404
+ if (writeStream)
405
+ await (0, events_1.once)(writeStream, 'finish');
406
+ finalStream.destroy();
657
407
  return {
658
408
  mediaKey,
659
409
  encWriteStream,
@@ -662,12 +412,13 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
662
412
  fileEncSha256,
663
413
  fileSha256,
664
414
  fileLength,
665
- didSaveToTmpPath
415
+ didSaveToTmpPath,
416
+ opusConverted
666
417
  };
667
418
  }
668
419
  catch (error) {
669
420
  encWriteStream.destroy();
670
- writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
421
+ writeStream?.destroy();
671
422
  aes.destroy();
672
423
  hmac.destroy();
673
424
  sha256Plain.destroy();
@@ -677,17 +428,10 @@ const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfReq
677
428
  try {
678
429
  await fs_1.promises.unlink(bodyPath);
679
430
  }
680
- catch (err) {
681
- logger === null || logger === void 0 ? void 0 : logger.error({ err }, 'failed to save to tmp path');
682
- }
431
+ catch (_) { }
683
432
  }
684
433
  throw error;
685
434
  }
686
- function onChunk(buff) {
687
- sha256Enc = sha256Enc.update(buff);
688
- hmac = hmac.update(buff);
689
- encWriteStream.push(buff);
690
- }
691
435
  };
692
436
  exports.encryptedStream = encryptedStream;
693
437
  const DEF_HOST = 'mmg.whatsapp.net';
@@ -698,7 +442,7 @@ const toSmallestChunkSize = (num) => {
698
442
  const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
699
443
  exports.getUrlFromDirectPath = getUrlFromDirectPath;
700
444
  const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
701
- const isValidMediaUrl = url === null || url === void 0 ? void 0 : url.startsWith('https://mmg.whatsapp.net/');
445
+ const isValidMediaUrl = url?.startsWith('https://mmg.whatsapp.net/');
702
446
  const downloadUrl = isValidMediaUrl ? url : (0, exports.getUrlFromDirectPath)(directPath);
703
447
  if (!downloadUrl) {
704
448
  throw new boom_1.Boom('No valid media URL or directPath present in message', { statusCode: 400 });
@@ -707,10 +451,15 @@ const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, o
707
451
  return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
708
452
  };
709
453
  exports.downloadContentFromMessage = downloadContentFromMessage;
454
+ /**
455
+ * Decrypts and downloads an AES256-CBC encrypted file given the keys.
456
+ * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
457
+ * */
710
458
  const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
711
459
  let bytesFetched = 0;
712
460
  let startChunk = 0;
713
461
  let firstBlockIsIV = false;
462
+ // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
714
463
  if (startByte) {
715
464
  const chunk = toSmallestChunkSize(startByte || 0);
716
465
  if (chunk) {
@@ -720,9 +469,14 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
720
469
  }
721
470
  }
722
471
  const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
472
+ const headersInit = options?.headers ? options.headers : undefined;
723
473
  const headers = {
724
- ...(options === null || options === void 0 ? void 0 : options.headers) || {},
725
- Origin: Defaults_1.DEFAULT_ORIGIN,
474
+ ...(headersInit
475
+ ? Array.isArray(headersInit)
476
+ ? Object.fromEntries(headersInit)
477
+ : headersInit
478
+ : {}),
479
+ Origin: index_js_2.DEFAULT_ORIGIN
726
480
  };
727
481
  if (startChunk || endChunk) {
728
482
  headers.Range = `bytes=${startChunk}-`;
@@ -730,11 +484,10 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
730
484
  headers.Range += endChunk;
731
485
  }
732
486
  }
487
+ // download the message
733
488
  const fetched = await (0, exports.getHttpStream)(downloadUrl, {
734
- ...options || {},
735
- headers,
736
- maxBodyLength: Infinity,
737
- maxContentLength: Infinity,
489
+ ...(options || {}),
490
+ headers
738
491
  });
739
492
  let remainingBytes = Buffer.from([]);
740
493
  let aes;
@@ -762,6 +515,8 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
762
515
  data = data.slice(AES_CHUNK_SIZE);
763
516
  }
764
517
  aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
518
+ // if an end byte that is not EOF is specified
519
+ // stop auto padding (PKCS7) -- otherwise throws an error for decryption
765
520
  if (endByte) {
766
521
  aes.setAutoPadding(false);
767
522
  }
@@ -782,18 +537,16 @@ const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startB
782
537
  catch (error) {
783
538
  callback(error);
784
539
  }
785
- },
540
+ }
786
541
  });
787
542
  return fetched.pipe(output, { end: true });
788
543
  };
789
544
  exports.downloadEncryptedContent = downloadEncryptedContent;
790
545
  function extensionForMediaMessage(message) {
791
- const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1];
546
+ const getExtension = (mimetype) => mimetype.split(';')[0]?.split('/')[1];
792
547
  const type = Object.keys(message)[0];
793
548
  let extension;
794
- if (type === 'locationMessage' ||
795
- type === 'liveLocationMessage' ||
796
- type === 'productMessage') {
549
+ if (type === 'locationMessage' || type === 'liveLocationMessage' || type === 'productMessage') {
797
550
  extension = '.jpeg';
798
551
  }
799
552
  else {
@@ -802,39 +555,166 @@ function extensionForMediaMessage(message) {
802
555
  }
803
556
  return extension;
804
557
  }
558
+ const isNodeRuntime = () => {
559
+ return (typeof process !== 'undefined' &&
560
+ process.versions?.node !== null &&
561
+ typeof process.versions.bun === 'undefined' &&
562
+ typeof globalThis.Deno === 'undefined');
563
+ };
564
+ const uploadWithNodeHttp = async ({ url, filePath, headers, timeoutMs, agent }, redirectCount = 0) => {
565
+ if (redirectCount > 5) {
566
+ throw new Error('Too many redirects');
567
+ }
568
+ const parsedUrl = new url_1.URL(url);
569
+ const httpModule = parsedUrl.protocol === 'https:' ? await Promise.resolve().then(() => __importStar(require('https'))) : await Promise.resolve().then(() => __importStar(require('http')));
570
+ // Get file size for Content-Length header (required for Node.js streaming)
571
+ const fileStats = await fs_1.promises.stat(filePath);
572
+ const fileSize = fileStats.size;
573
+ return new Promise((resolve, reject) => {
574
+ const req = httpModule.request({
575
+ hostname: parsedUrl.hostname,
576
+ port: parsedUrl.port || (parsedUrl.protocol === 'https:' ? 443 : 80),
577
+ path: parsedUrl.pathname + parsedUrl.search,
578
+ method: 'POST',
579
+ headers: {
580
+ ...headers,
581
+ 'Content-Length': fileSize
582
+ },
583
+ agent,
584
+ timeout: timeoutMs
585
+ }, res => {
586
+ // Handle redirects (3xx)
587
+ if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
588
+ res.resume(); // Consume response to free resources
589
+ const newUrl = new url_1.URL(res.headers.location, url).toString();
590
+ resolve((0, exports.uploadWithNodeHttp)({
591
+ url: newUrl,
592
+ filePath,
593
+ headers,
594
+ timeoutMs,
595
+ agent
596
+ }, redirectCount + 1));
597
+ return;
598
+ }
599
+ let body = '';
600
+ res.on('data', chunk => (body += chunk));
601
+ res.on('end', () => {
602
+ try {
603
+ resolve(JSON.parse(body));
604
+ }
605
+ catch {
606
+ resolve(undefined);
607
+ }
608
+ });
609
+ });
610
+ req.on('error', reject);
611
+ req.on('timeout', () => {
612
+ req.destroy();
613
+ reject(new Error('Upload timeout'));
614
+ });
615
+ const stream = (0, fs_1.createReadStream)(filePath);
616
+ stream.pipe(req);
617
+ stream.on('error', err => {
618
+ req.destroy();
619
+ reject(err);
620
+ });
621
+ });
622
+ };
623
+ exports.uploadWithNodeHttp = uploadWithNodeHttp;
624
+ const uploadWithFetch = async ({ url, filePath, headers, timeoutMs, agent }) => {
625
+ // Convert Node.js Readable to Web ReadableStream
626
+ const nodeStream = (0, fs_1.createReadStream)(filePath);
627
+ const webStream = stream_1.Readable.toWeb(nodeStream);
628
+ const response = await fetch(url, {
629
+ dispatcher: agent,
630
+ method: 'POST',
631
+ body: webStream,
632
+ headers,
633
+ duplex: 'half',
634
+ signal: timeoutMs ? AbortSignal.timeout(timeoutMs) : undefined
635
+ });
636
+ try {
637
+ return (await response.json());
638
+ }
639
+ catch {
640
+ return undefined;
641
+ }
642
+ };
643
+ /**
644
+ * Uploads media to WhatsApp servers.
645
+ *
646
+ * ## Why we have two upload implementations:
647
+ *
648
+ * Node.js's native `fetch` (powered by undici) has a known bug where it buffers
649
+ * the entire request body in memory before sending, even when using streams.
650
+ * This causes memory issues with large files (e.g., 1GB file = 1GB+ memory usage).
651
+ * See: https://github.com/nodejs/undici/issues/4058
652
+ *
653
+ * Other runtimes (Bun, Deno, browsers) correctly stream the request body without
654
+ * buffering, so we can use the web-standard Fetch API there.
655
+ *
656
+ * ## Future considerations:
657
+ * Once the undici bug is fixed, we can simplify this to use only the Fetch API
658
+ * across all runtimes. Monitor the GitHub issue for updates.
659
+ */
660
+ const uploadMedia = async (params, logger) => {
661
+ if (isNodeRuntime()) {
662
+ return (0, exports.uploadWithNodeHttp)(params);
663
+ }
664
+ else {
665
+ return uploadWithFetch(params);
666
+ }
667
+ };
805
668
  const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
806
- return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
807
- var _a, _b;
669
+ return async (streamOrPath, { mediaType, fileEncSha256B64, timeoutMs, newsletter }) => {
670
+ // send a query JSON to obtain the url & auth token to upload our media
808
671
  let uploadInfo = await refreshMediaConn(false);
809
672
  let urls;
810
673
  const hosts = [...customUploadHosts, ...uploadInfo.hosts];
811
- const chunks = [];
812
- if (!Buffer.isBuffer(stream)) {
813
- for await (const chunk of stream) {
674
+ fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
675
+ // Prepare common headers
676
+ const customHeaders = (() => {
677
+ const hdrs = options?.headers;
678
+ if (!hdrs)
679
+ return {};
680
+ return Array.isArray(hdrs) ? Object.fromEntries(hdrs) : hdrs;
681
+ })();
682
+ const headers = {
683
+ ...customHeaders,
684
+ 'Content-Type': 'application/octet-stream',
685
+ Origin: index_js_2.DEFAULT_ORIGIN
686
+ };
687
+ // Collect buffer from Readable stream or read from file path
688
+ let reqBuffer;
689
+ if (Buffer.isBuffer(streamOrPath)) {
690
+ reqBuffer = streamOrPath;
691
+ }
692
+ else if (typeof streamOrPath === 'string') {
693
+ reqBuffer = await fs_1.promises.readFile(streamOrPath);
694
+ }
695
+ else {
696
+ // Readable stream
697
+ const chunks = [];
698
+ for await (const chunk of streamOrPath)
814
699
  chunks.push(chunk);
815
- }
700
+ reqBuffer = Buffer.concat(chunks);
816
701
  }
817
- const reqBody = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks);
818
- fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
819
- let media = Defaults_1.MEDIA_PATH_MAP[mediaType];
702
+ // Newsletter uses different upload path
703
+ let mediaPath = index_js_2.MEDIA_PATH_MAP[mediaType];
820
704
  if (newsletter) {
821
- media = media === null || media === void 0 ? void 0 : media.replace('/mms/', '/newsletter/newsletter-');
705
+ mediaPath = mediaPath?.replace('/mms/', '/newsletter/newsletter-');
822
706
  }
823
707
  for (const { hostname, maxContentLengthBytes } of hosts) {
824
- logger.debug(`uploading to "${hostname}"`);
825
708
  const auth = encodeURIComponent(uploadInfo.auth);
826
- const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
709
+ const url = `https://${hostname}${mediaPath}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
827
710
  let result;
828
711
  try {
829
- if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
830
- throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
831
- }
832
- const body = await axios_1.default.post(url, reqBody, {
712
+ // Upload buffer directly like wiley (avoids file I/O issues)
713
+ const axios = (await Promise.resolve().then(() => __importStar(require('axios')))).default;
714
+ const body = await axios.post(url, reqBuffer, {
833
715
  ...options,
834
716
  headers: {
835
- ...options.headers || {},
836
- 'Content-Type': 'application/octet-stream',
837
- 'Origin': Defaults_1.DEFAULT_ORIGIN
717
+ ...headers,
838
718
  },
839
719
  httpsAgent: fetchAgent,
840
720
  timeout: timeoutMs,
@@ -843,11 +723,14 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
843
723
  maxContentLength: Infinity,
844
724
  });
845
725
  result = body.data;
846
- if ((result === null || result === void 0 ? void 0 : result.url) || (result === null || result === void 0 ? void 0 : result.directPath)) {
726
+ if (result?.url || result?.direct_path) {
847
727
  urls = {
848
728
  mediaUrl: result.url,
849
729
  directPath: result.direct_path,
850
- handle: result.handle
730
+ handle: result.handle,
731
+ meta_hmac: result.meta_hmac,
732
+ fbid: result.fbid,
733
+ ts: result.ts
851
734
  };
852
735
  break;
853
736
  }
@@ -857,11 +740,8 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
857
740
  }
858
741
  }
859
742
  catch (error) {
860
- if (axios_1.default.isAxiosError(error)) {
861
- result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
862
- }
863
- const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
864
- logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
743
+ const isLast = hostname === hosts[uploadInfo.hosts.length - 1]?.hostname;
744
+ logger.warn({ trace: error?.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
865
745
  }
866
746
  }
867
747
  if (!urls) {
@@ -872,22 +752,28 @@ const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options },
872
752
  };
873
753
  exports.getWAUploadToServer = getWAUploadToServer;
874
754
  const getMediaRetryKey = (mediaKey) => {
875
- return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
755
+ return (0, crypto_js_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
876
756
  };
877
- const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
757
+ /**
758
+ * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
759
+ */
760
+ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
878
761
  const recp = { stanzaId: key.id };
879
- const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
762
+ const recpBuffer = index_js_1.proto.ServerErrorReceipt.encode(recp).finish();
880
763
  const iv = Crypto.randomBytes(12);
881
- const retryKey = await getMediaRetryKey(mediaKey);
882
- const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
764
+ const retryKey = getMediaRetryKey(mediaKey);
765
+ const ciphertext = (0, crypto_js_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
883
766
  const req = {
884
767
  tag: 'receipt',
885
768
  attrs: {
886
769
  id: key.id,
887
- to: (0, WABinary_1.jidNormalizedUser)(meId),
770
+ to: (0, index_js_3.jidNormalizedUser)(meId),
888
771
  type: 'server-error'
889
772
  },
890
773
  content: [
774
+ // this encrypt node is actually pretty useless
775
+ // the media is returned even without this node
776
+ // keeping it here to maintain parity with WA Web
891
777
  {
892
778
  tag: 'encrypt',
893
779
  attrs: {},
@@ -900,7 +786,8 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
900
786
  tag: 'rmr',
901
787
  attrs: {
902
788
  jid: key.remoteJid,
903
- 'from_me': (!!key.fromMe).toString(),
789
+ from_me: (!!key.fromMe).toString(),
790
+ // @ts-ignore
904
791
  participant: key.participant || undefined
905
792
  }
906
793
  }
@@ -910,7 +797,7 @@ const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
910
797
  };
911
798
  exports.encryptMediaRetryRequest = encryptMediaRetryRequest;
912
799
  const decodeMediaRetryNode = (node) => {
913
- const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr');
800
+ const rmrNode = (0, index_js_3.getBinaryNodeChild)(node, 'rmr');
914
801
  const event = {
915
802
  key: {
916
803
  id: node.attrs.id,
@@ -919,15 +806,18 @@ const decodeMediaRetryNode = (node) => {
919
806
  participant: rmrNode.attrs.participant
920
807
  }
921
808
  };
922
- const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
809
+ const errorNode = (0, index_js_3.getBinaryNodeChild)(node, 'error');
923
810
  if (errorNode) {
924
811
  const errorCode = +errorNode.attrs.code;
925
- event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode) });
812
+ event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, {
813
+ data: errorNode.attrs,
814
+ statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode)
815
+ });
926
816
  }
927
817
  else {
928
- const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt');
929
- const ciphertext = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p');
930
- const iv = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv');
818
+ const encryptedInfoNode = (0, index_js_3.getBinaryNodeChild)(node, 'encrypt');
819
+ const ciphertext = (0, index_js_3.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p');
820
+ const iv = (0, index_js_3.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv');
931
821
  if (ciphertext && iv) {
932
822
  event.media = { ciphertext, iv };
933
823
  }
@@ -938,20 +828,57 @@ const decodeMediaRetryNode = (node) => {
938
828
  return event;
939
829
  };
940
830
  exports.decodeMediaRetryNode = decodeMediaRetryNode;
941
- const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
942
- const retryKey = await getMediaRetryKey(mediaKey);
943
- const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
944
- return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
831
+ const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
832
+ const retryKey = getMediaRetryKey(mediaKey);
833
+ const plaintext = (0, crypto_js_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
834
+ return index_js_1.proto.MediaRetryNotification.decode(plaintext);
945
835
  };
946
836
  exports.decryptMediaRetryData = decryptMediaRetryData;
947
837
  const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
948
838
  exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
949
839
  const MEDIA_RETRY_STATUS_MAP = {
950
- [WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
951
- [WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
952
- [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
953
- [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
840
+ [index_js_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
841
+ [index_js_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
842
+ [index_js_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
843
+ [index_js_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418
844
+ };
845
+ const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts, isPtt, forceOpus } = {}) => {
846
+ const { stream, type } = await (0, exports.getStream)(media, opts);
847
+ let buffer = await (0, exports.toBuffer)(stream);
848
+ let opusConverted = false;
849
+ let bodyPath;
850
+ let didSaveToTmpPath = false;
851
+ try {
852
+ if (type === 'file') {
853
+ bodyPath = media.url?.toString?.() || media.url;
854
+ }
855
+ else if (saveOriginalFileIfRequired) {
856
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_js_1.generateMessageIDV2)());
857
+ const { writeFileSync } = await Promise.resolve().then(() => __importStar(require('fs')));
858
+ writeFileSync(bodyPath, buffer);
859
+ didSaveToTmpPath = true;
860
+ }
861
+ const fileLength = buffer.length;
862
+ const fileSha256 = Crypto.createHash('sha256').update(buffer).digest();
863
+ return {
864
+ mediaKey: undefined,
865
+ encWriteStream: buffer,
866
+ fileLength,
867
+ fileSha256,
868
+ fileEncSha256: undefined,
869
+ bodyPath,
870
+ didSaveToTmpPath,
871
+ opusConverted
872
+ };
873
+ }
874
+ catch (error) {
875
+ if (didSaveToTmpPath && bodyPath) {
876
+ try {
877
+ await fs_1.promises.unlink(bodyPath);
878
+ }
879
+ catch (_) { }
880
+ }
881
+ throw error;
882
+ }
954
883
  };
955
- function __importStar(arg0) {
956
- throw new Error('Function not implemented.');
957
- }
884
+ exports.prepareStream = prepareStream;