@sixcore/baileys 1.0.0 → 1.0.2

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 (228) hide show
  1. package/WAProto/index.js +14270 -302
  2. package/jessica.js +91 -0
  3. package/lib/Defaults/baileys-version.json +1 -1
  4. package/lib/Defaults/index.js +117 -79
  5. package/lib/Defaults/phonenumber-mcc.json +223 -0
  6. package/lib/Signal/Group/ciphertext-message.d.ts +9 -0
  7. package/lib/Signal/Group/ciphertext-message.js +15 -0
  8. package/lib/Signal/Group/group-session-builder.d.ts +14 -0
  9. package/lib/Signal/Group/group-session-builder.js +64 -0
  10. package/lib/Signal/Group/group_cipher.d.ts +17 -0
  11. package/lib/Signal/Group/group_cipher.js +96 -0
  12. package/lib/Signal/Group/index.d.ts +11 -0
  13. package/lib/Signal/Group/index.js +57 -0
  14. package/lib/Signal/Group/keyhelper.d.ts +10 -0
  15. package/lib/Signal/Group/keyhelper.js +55 -0
  16. package/lib/Signal/Group/queue-job.d.ts +1 -0
  17. package/lib/Signal/Group/queue-job.js +57 -0
  18. package/lib/Signal/Group/sender-chain-key.d.ts +13 -0
  19. package/lib/Signal/Group/sender-chain-key.js +34 -0
  20. package/lib/Signal/Group/sender-key-distribution-message.d.ts +16 -0
  21. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  22. package/lib/Signal/Group/sender-key-message.d.ts +18 -0
  23. package/lib/Signal/Group/sender-key-message.js +69 -0
  24. package/lib/Signal/Group/sender-key-name.d.ts +17 -0
  25. package/lib/Signal/Group/sender-key-name.js +51 -0
  26. package/lib/Signal/Group/sender-key-record.d.ts +30 -0
  27. package/lib/Signal/Group/sender-key-record.js +53 -0
  28. package/lib/Signal/Group/sender-key-state.d.ts +38 -0
  29. package/lib/Signal/Group/sender-key-state.js +99 -0
  30. package/lib/Signal/Group/sender-message-key.d.ts +11 -0
  31. package/{WASignalGroup/sender_message_key.js → lib/Signal/Group/sender-message-key.js} +6 -16
  32. package/lib/Signal/libsignal.js +51 -29
  33. package/lib/Socket/business.d.ts +43 -42
  34. package/lib/Socket/chats.d.ts +222 -36
  35. package/lib/Socket/chats.js +173 -153
  36. package/lib/Socket/dugong.d.ts +254 -0
  37. package/lib/Socket/dugong.js +484 -0
  38. package/lib/Socket/groups.d.ts +7 -7
  39. package/lib/Socket/groups.js +37 -35
  40. package/lib/Socket/index.d.ts +52 -51
  41. package/lib/Socket/index.js +1 -0
  42. package/lib/Socket/messages-recv.d.ts +37 -34
  43. package/lib/Socket/messages-recv.js +175 -37
  44. package/lib/Socket/messages-send.d.ts +12 -18
  45. package/lib/Socket/messages-send.js +396 -574
  46. package/lib/Socket/newsletter.d.ts +28 -26
  47. package/lib/Socket/newsletter.js +132 -121
  48. package/lib/Socket/registration.d.ts +52 -49
  49. package/lib/Socket/registration.js +7 -7
  50. package/lib/Socket/socket.d.ts +0 -1
  51. package/lib/Socket/socket.js +49 -27
  52. package/lib/Socket/usync.d.ts +10 -11
  53. package/lib/Store/make-cache-manager-store.d.ts +1 -2
  54. package/lib/Store/make-in-memory-store.d.ts +2 -2
  55. package/lib/Store/make-in-memory-store.js +1 -5
  56. package/lib/Store/make-ordered-dictionary.js +2 -2
  57. package/lib/Types/Auth.d.ts +1 -0
  58. package/lib/Types/Call.d.ts +1 -1
  59. package/lib/Types/Chat.d.ts +7 -12
  60. package/lib/Types/Events.d.ts +2 -17
  61. package/lib/Types/GroupMetadata.d.ts +2 -3
  62. package/lib/Types/Label.d.ts +0 -11
  63. package/lib/Types/Label.js +1 -1
  64. package/lib/Types/LabelAssociation.js +1 -1
  65. package/lib/Types/Message.d.ts +10 -170
  66. package/lib/Types/Newsletter.d.ts +97 -86
  67. package/lib/Types/Newsletter.js +38 -32
  68. package/lib/Types/Socket.d.ts +2 -7
  69. package/lib/Types/index.d.ts +0 -9
  70. package/lib/Types/index.js +1 -1
  71. package/lib/Utils/auth-utils.js +14 -35
  72. package/lib/Utils/business.d.ts +1 -1
  73. package/lib/Utils/business.js +2 -2
  74. package/lib/Utils/chat-utils.d.ts +12 -11
  75. package/lib/Utils/chat-utils.js +36 -52
  76. package/lib/Utils/crypto.d.ts +16 -15
  77. package/lib/Utils/crypto.js +26 -74
  78. package/lib/Utils/decode-wa-message.d.ts +0 -17
  79. package/lib/Utils/decode-wa-message.js +17 -53
  80. package/lib/Utils/event-buffer.js +7 -10
  81. package/lib/Utils/generics.d.ts +17 -13
  82. package/lib/Utils/generics.js +79 -58
  83. package/lib/Utils/history.d.ts +2 -6
  84. package/lib/Utils/history.js +6 -4
  85. package/lib/Utils/logger.d.ts +3 -1
  86. package/lib/Utils/lt-hash.js +12 -12
  87. package/lib/Utils/make-mutex.d.ts +2 -2
  88. package/lib/Utils/messages-media.d.ts +28 -25
  89. package/lib/Utils/messages-media.js +733 -557
  90. package/lib/Utils/messages.js +68 -473
  91. package/lib/Utils/noise-handler.d.ts +5 -4
  92. package/lib/Utils/noise-handler.js +14 -19
  93. package/lib/Utils/process-message.d.ts +5 -5
  94. package/lib/Utils/process-message.js +23 -75
  95. package/lib/Utils/signal.d.ts +1 -2
  96. package/lib/Utils/signal.js +26 -32
  97. package/lib/Utils/use-multi-file-auth-state.d.ts +1 -0
  98. package/lib/Utils/use-multi-file-auth-state.js +66 -242
  99. package/lib/Utils/validate-connection.d.ts +1 -1
  100. package/lib/Utils/validate-connection.js +88 -64
  101. package/lib/WABinary/constants.d.ts +27 -24
  102. package/lib/WABinary/decode.d.ts +2 -1
  103. package/lib/WABinary/decode.js +11 -23
  104. package/lib/WABinary/encode.d.ts +2 -1
  105. package/lib/WABinary/encode.js +147 -134
  106. package/lib/WABinary/generic-utils.d.ts +5 -2
  107. package/lib/WABinary/generic-utils.js +125 -37
  108. package/lib/WABinary/jid-utils.d.ts +1 -1
  109. package/lib/WAM/BinaryInfo.d.ts +11 -2
  110. package/lib/WAM/encode.d.ts +2 -1
  111. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +3 -3
  112. package/lib/WAUSync/USyncUser.d.ts +2 -0
  113. package/lib/index.d.ts +12 -0
  114. package/lib/index.js +64 -1
  115. package/package.json +113 -51
  116. package/WAProto/GenerateStatics.sh +0 -4
  117. package/WAProto/WAProto.proto +0 -4357
  118. package/WAProto/index.d.ts +0 -50383
  119. package/WASignalGroup/GroupProtocol.js +0 -1697
  120. package/WASignalGroup/ciphertext_message.js +0 -16
  121. package/WASignalGroup/generate-proto.sh +0 -1
  122. package/WASignalGroup/group.proto +0 -42
  123. package/WASignalGroup/group_cipher.js +0 -120
  124. package/WASignalGroup/group_session_builder.js +0 -46
  125. package/WASignalGroup/index.js +0 -5
  126. package/WASignalGroup/keyhelper.js +0 -21
  127. package/WASignalGroup/protobufs.js +0 -3
  128. package/WASignalGroup/queue_job.js +0 -69
  129. package/WASignalGroup/sender_chain_key.js +0 -50
  130. package/WASignalGroup/sender_key_distribution_message.js +0 -78
  131. package/WASignalGroup/sender_key_message.js +0 -92
  132. package/WASignalGroup/sender_key_name.js +0 -70
  133. package/WASignalGroup/sender_key_record.js +0 -56
  134. package/WASignalGroup/sender_key_state.js +0 -129
  135. package/lib/Utils/use-single-file-auth-state.d.ts +0 -12
  136. package/lib/Utils/use-single-file-auth-state.js +0 -75
  137. package/src/Defaults/baileys-version.json +0 -3
  138. package/src/Defaults/index.ts +0 -133
  139. package/src/Signal/Group/ciphertext-message.ts +0 -9
  140. package/src/Signal/Group/group-session-builder.ts +0 -56
  141. package/src/Signal/Group/group_cipher.ts +0 -117
  142. package/src/Signal/Group/index.ts +0 -11
  143. package/src/Signal/Group/keyhelper.ts +0 -28
  144. package/src/Signal/Group/sender-chain-key.ts +0 -34
  145. package/src/Signal/Group/sender-key-distribution-message.ts +0 -95
  146. package/src/Signal/Group/sender-key-message.ts +0 -96
  147. package/src/Signal/Group/sender-key-name.ts +0 -66
  148. package/src/Signal/Group/sender-key-record.ts +0 -69
  149. package/src/Signal/Group/sender-key-state.ts +0 -134
  150. package/src/Signal/Group/sender-message-key.ts +0 -36
  151. package/src/Signal/libsignal.ts +0 -447
  152. package/src/Signal/lid-mapping.ts +0 -209
  153. package/src/Socket/Client/index.ts +0 -2
  154. package/src/Socket/Client/types.ts +0 -22
  155. package/src/Socket/Client/websocket.ts +0 -56
  156. package/src/Socket/business.ts +0 -421
  157. package/src/Socket/chats.ts +0 -1223
  158. package/src/Socket/communities.ts +0 -477
  159. package/src/Socket/groups.ts +0 -361
  160. package/src/Socket/index.ts +0 -22
  161. package/src/Socket/messages-recv.ts +0 -1563
  162. package/src/Socket/messages-send.ts +0 -1210
  163. package/src/Socket/mex.ts +0 -58
  164. package/src/Socket/newsletter.ts +0 -229
  165. package/src/Socket/socket.ts +0 -1072
  166. package/src/Types/Auth.ts +0 -115
  167. package/src/Types/Bussines.ts +0 -20
  168. package/src/Types/Call.ts +0 -14
  169. package/src/Types/Chat.ts +0 -138
  170. package/src/Types/Contact.ts +0 -24
  171. package/src/Types/Events.ts +0 -132
  172. package/src/Types/GroupMetadata.ts +0 -70
  173. package/src/Types/Label.ts +0 -48
  174. package/src/Types/LabelAssociation.ts +0 -35
  175. package/src/Types/Message.ts +0 -424
  176. package/src/Types/Newsletter.ts +0 -98
  177. package/src/Types/Product.ts +0 -85
  178. package/src/Types/Signal.ts +0 -76
  179. package/src/Types/Socket.ts +0 -150
  180. package/src/Types/State.ts +0 -43
  181. package/src/Types/USync.ts +0 -27
  182. package/src/Types/globals.d.ts +0 -8
  183. package/src/Types/index.ts +0 -67
  184. package/src/Utils/auth-utils.ts +0 -331
  185. package/src/Utils/browser-utils.ts +0 -31
  186. package/src/Utils/business.ts +0 -286
  187. package/src/Utils/chat-utils.ts +0 -933
  188. package/src/Utils/crypto.ts +0 -184
  189. package/src/Utils/decode-wa-message.ts +0 -355
  190. package/src/Utils/event-buffer.ts +0 -662
  191. package/src/Utils/generics.ts +0 -470
  192. package/src/Utils/history.ts +0 -114
  193. package/src/Utils/index.ts +0 -18
  194. package/src/Utils/link-preview.ts +0 -111
  195. package/src/Utils/logger.ts +0 -13
  196. package/src/Utils/lt-hash.ts +0 -65
  197. package/src/Utils/make-mutex.ts +0 -45
  198. package/src/Utils/message-retry-manager.ts +0 -229
  199. package/src/Utils/messages-media.ts +0 -820
  200. package/src/Utils/messages.ts +0 -1137
  201. package/src/Utils/noise-handler.ts +0 -192
  202. package/src/Utils/pre-key-manager.ts +0 -126
  203. package/src/Utils/process-message.ts +0 -622
  204. package/src/Utils/signal.ts +0 -214
  205. package/src/Utils/use-multi-file-auth-state.ts +0 -136
  206. package/src/Utils/validate-connection.ts +0 -253
  207. package/src/WABinary/constants.ts +0 -1305
  208. package/src/WABinary/decode.ts +0 -281
  209. package/src/WABinary/encode.ts +0 -253
  210. package/src/WABinary/generic-utils.ts +0 -127
  211. package/src/WABinary/index.ts +0 -5
  212. package/src/WABinary/jid-utils.ts +0 -128
  213. package/src/WABinary/types.ts +0 -17
  214. package/src/WAM/BinaryInfo.ts +0 -12
  215. package/src/WAM/constants.ts +0 -22889
  216. package/src/WAM/encode.ts +0 -169
  217. package/src/WAM/index.ts +0 -3
  218. package/src/WAUSync/Protocols/USyncContactProtocol.ts +0 -32
  219. package/src/WAUSync/Protocols/USyncDeviceProtocol.ts +0 -78
  220. package/src/WAUSync/Protocols/USyncDisappearingModeProtocol.ts +0 -35
  221. package/src/WAUSync/Protocols/USyncStatusProtocol.ts +0 -44
  222. package/src/WAUSync/Protocols/UsyncBotProfileProtocol.ts +0 -76
  223. package/src/WAUSync/Protocols/UsyncLIDProtocol.ts +0 -33
  224. package/src/WAUSync/Protocols/index.ts +0 -4
  225. package/src/WAUSync/USyncQuery.ts +0 -133
  226. package/src/WAUSync/USyncUser.ts +0 -32
  227. package/src/WAUSync/index.ts +0 -3
  228. package/src/index.ts +0 -13
@@ -1,14 +1,9 @@
1
1
  "use strict";
2
-
3
- // ─────────────────────────────────────────────────────────────────────────────
4
- // Lena-Baileys — Módulo de Mídia
5
- // ─────────────────────────────────────────────────────────────────────────────
6
-
7
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
3
  if (k2 === undefined) k2 = k;
9
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
10
5
  if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
7
  }
13
8
  Object.defineProperty(o, k2, desc);
14
9
  }) : (function(o, m, k, k2) {
@@ -20,624 +15,805 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
20
15
  }) : function(o, v) {
21
16
  o["default"] = v;
22
17
  });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
-
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
41
25
  Object.defineProperty(exports, "__esModule", { value: true });
42
-
43
- exports.getStatusCodeForMediaRetry =
44
- exports.decryptMediaRetryData =
45
- exports.decodeMediaRetryNode =
46
- exports.encryptMediaRetryRequest =
47
- exports.getWAUploadToServer =
48
- exports.downloadEncryptedContent =
49
- exports.downloadContentFromMessageV1 =
50
- exports.clearDownloadCache =
51
- exports.detectMimeType =
52
- exports.downloadContentFromMessage =
53
- exports.getUrlFromDirectPath =
54
- exports.encryptedStream =
55
- exports.prepareStream =
56
- exports.getHttpStream =
57
- exports.getStream =
58
- exports.toBuffer =
59
- exports.toReadable =
60
- exports.mediaMessageSHA256B64 =
61
- exports.generateProfilePicture =
62
- exports.encodeBase64EncodedStringForUpload =
63
- exports.extractImageThumb =
64
- exports.hkdfInfoKey = void 0;
65
-
66
- exports.getMediaKeys = getMediaKeys;
67
- exports.getAudioDuration = getAudioDuration;
68
- exports.getAudioWaveform = getAudioWaveform;
69
- exports.generateThumbnail = generateThumbnail;
70
- exports.extensionForMediaMessage = extensionForMediaMessage;
71
-
72
- // ─── Dependências ─────────────────────────────────────────────────────────────
73
- const { Boom } = require("@hapi/boom");
74
- const { exec } = require("child_process");
75
- const Crypto = __importStar(require("crypto"));
76
- const { once } = require("events");
77
- const { createReadStream, createWriteStream, writeFileSync, promises: fsp } = require("fs");
78
- const { tmpdir } = require("os");
79
- const { join } = require("path");
80
- const { Readable, Transform } = require("stream");
81
- const Proto = require("../../WAProto");
82
- const Config = require("../Defaults");
83
- const JidUtils = require("../WABinary");
84
- const CryptoUtils = require("./crypto");
85
- const Helpers = require("./generics");
86
-
87
- // ─── Cache de downloads ───────────────────────────────────────────────────────
88
- const DOWNLOAD_CACHE = new Map()
89
- const MAX_RETRIES = 3
90
- const RETRY_DELAY_MS = 1000
91
-
92
- // ─── Helpers internos ─────────────────────────────────────────────────────────
93
- const getTmpDir = () => tmpdir()
94
- const sleep = (ms) => new Promise(r => setTimeout(r, ms))
95
-
96
- // ─── Detecta mime type pelo buffer ───────────────────────────────────────────
97
- const detectMimeType = (buffer) => {
98
- if (!buffer || buffer.length < 4) return 'application/octet-stream'
99
- const hex = buffer.slice(0, 4).toString('hex')
100
- if (hex.startsWith('ffd8ff')) return 'image/jpeg'
101
- if (hex.startsWith('89504e47')) return 'image/png'
102
- if (hex.startsWith('47494638')) return 'image/gif'
103
- if (hex.startsWith('25504446')) return 'application/pdf'
104
- if (hex.startsWith('494433') || hex.startsWith('fffb')) return 'audio/mpeg'
105
- if (hex.startsWith('4f676753')) return 'audio/ogg'
106
- if (hex.startsWith('1a45dfa3')) return 'video/webm'
107
- if (buffer.slice(4, 8).toString('ascii') === 'ftyp') return 'video/mp4'
108
- if (hex.startsWith('52494646')) return 'audio/wav'
109
- if (hex.startsWith('504b0304')) return 'application/zip'
110
- return 'application/octet-stream'
111
- }
112
- exports.detectMimeType = detectMimeType
113
-
114
- // ─── Limpar cache de downloads ────────────────────────────────────────────────
115
- const clearDownloadCache = () => DOWNLOAD_CACHE.clear()
116
- exports.clearDownloadCache = clearDownloadCache
117
-
118
- // ─── Biblioteca de processamento de imagem ────────────────────────────────────
26
+ exports.getStatusCodeForMediaRetry = exports.decryptMediaRetryData = exports.decodeMediaRetryNode = exports.encryptMediaRetryRequest = exports.getWAUploadToServer = exports.extensionForMediaMessage = exports.downloadEncryptedContent = exports.downloadContentFromMessage = exports.getUrlFromDirectPath = exports.encryptedStream = exports.prepareStream = exports.getHttpStream = exports.generateThumbnail = exports.getStream = exports.toBuffer = exports.toReadable = exports.getAudioWaveform = exports.getAudioDuration = exports.mediaMessageSHA256B64 = exports.generateProfilePicture = exports.encodeBase64EncodedStringForUpload = exports.extractImageThumb = exports.getMediaKeys = exports.hkdfInfoKey = void 0;
27
+ const boom_1 = require("@hapi/boom");
28
+ const child_process_1 = require("child_process");
29
+ const Crypto = __importStar(require("crypto"));
30
+ const events_1 = require("events");
31
+ const fs_1 = require("fs");
32
+ const os_1 = require("os");
33
+ const path_1 = require("path");
34
+ const stream_1 = require("stream");
35
+ const WAProto_1 = require("../../WAProto");
36
+ const Defaults_1 = require("../Defaults");
37
+ const WABinary_1 = require("../WABinary");
38
+ const crypto_1 = require("./crypto");
39
+ const generics_1 = require("./generics");
40
+ const getTmpFilesDirectory = () => (0, os_1.tmpdir)();
119
41
  const getImageProcessingLibrary = async () => {
120
42
  const [_jimp, sharp] = await Promise.all([
121
- import('jimp').catch(() => {}),
122
- import('sharp').catch(() => {}),
123
- ])
124
- if (sharp) return { sharp }
125
- const jimp = _jimp?.default || _jimp
126
- if (jimp) return { jimp }
127
- throw new Boom('Nenhuma biblioteca de imagem disponível')
128
- }
129
-
130
- // ─── Chave HKDF por tipo de mídia ────────────────────────────────────────────
131
- const hkdfInfoKey = (type) => `WhatsApp ${Config.MEDIA_HKDF_KEY_MAPPING[type]} Keys`
132
- exports.hkdfInfoKey = hkdfInfoKey
133
-
134
- // ─── Deriva chaves de criptografia ───────────────────────────────────────────
135
- async function getMediaKeys(buffer, mediaType) {
136
- if (!buffer) throw new Boom('Chave de mídia vazia')
137
- if (typeof buffer === 'string') buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64')
138
- const expanded = await CryptoUtils.hkdf(buffer, 112, { info: hkdfInfoKey(mediaType) })
139
- return {
140
- iv: expanded.slice(0, 16),
141
- cipherKey: expanded.slice(16, 48),
142
- macKey: expanded.slice(48, 80),
43
+ (async () => {
44
+ const jimp = await (import('jimp')
45
+ .catch(() => { }));
46
+ return jimp;
47
+ })(),
48
+ (async () => {
49
+ const sharp = await (import('sharp')
50
+ .catch(() => { }));
51
+ return sharp;
52
+ })()
53
+ ]);
54
+ if (sharp) {
55
+ return { sharp };
56
+ }
57
+ const jimp = (_jimp === null || _jimp === void 0 ? void 0 : _jimp.default) || _jimp;
58
+ if (jimp) {
59
+ return { jimp };
60
+ }
61
+ throw new boom_1.Boom('No image processing library available');
62
+ };
63
+ const hkdfInfoKey = (type) => {
64
+ const hkdfInfo = Defaults_1.MEDIA_HKDF_KEY_MAPPING[type];
65
+ return `WhatsApp ${hkdfInfo} Keys`;
66
+ };
67
+ exports.hkdfInfoKey = hkdfInfoKey;
68
+ /** generates all the keys required to encrypt/decrypt & sign a media message */
69
+ function getMediaKeys(buffer, mediaType) {
70
+ if (!buffer) {
71
+ throw new boom_1.Boom('Cannot derive from empty media key');
143
72
  }
73
+ if (typeof buffer === 'string') {
74
+ buffer = Buffer.from(buffer.replace('data:;base64,', ''), 'base64');
75
+ }
76
+ // 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) });
78
+ return {
79
+ iv: expandedMediaKey.slice(0, 16),
80
+ cipherKey: expandedMediaKey.slice(16, 48),
81
+ macKey: expandedMediaKey.slice(48, 80),
82
+ };
144
83
  }
145
-
146
- // ─── Extrai thumbnail de vídeo via ffmpeg ─────────────────────────────────────
147
- const extractVideoThumb = (path, dest, time, size) => new Promise((resolve, reject) => {
148
- const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${dest}`
149
- exec(cmd, (err) => err ? reject(err) : resolve())
150
- })
151
-
152
- // ─── Extrai thumbnail de imagem ───────────────────────────────────────────────
153
- const extractImageThumb = async (bufferOrPath, width = 32) => {
154
- var _a, _b
155
- if (bufferOrPath instanceof Readable) bufferOrPath = await toBuffer(bufferOrPath)
156
- const lib = await getImageProcessingLibrary()
84
+ exports.getMediaKeys = getMediaKeys;
85
+ /** Extracts video thumb using FFMPEG */
86
+ const extractVideoThumb = async (path, destPath, time, size) => new Promise((resolve, reject) => {
87
+ const cmd = `ffmpeg -ss ${time} -i ${path} -y -vf scale=${size.width}:-1 -vframes 1 -f image2 ${destPath}`;
88
+ (0, child_process_1.exec)(cmd, (err) => {
89
+ if (err) {
90
+ reject(err);
91
+ }
92
+ else {
93
+ resolve();
94
+ }
95
+ });
96
+ });
97
+ const extractImageThumb = async (bufferOrFilePath, width = 32) => {
98
+ var _a, _b;
99
+ if (bufferOrFilePath instanceof stream_1.Readable) {
100
+ bufferOrFilePath = await (0, exports.toBuffer)(bufferOrFilePath);
101
+ }
102
+ const lib = await getImageProcessingLibrary();
157
103
  if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
158
- const img = lib.sharp.default(bufferOrPath)
159
- const meta = await img.metadata()
160
- const buf = await img.resize(width).jpeg({ quality: 50 }).toBuffer()
161
- return { buffer: buf, original: { width: meta.width, height: meta.height } }
162
- } else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
163
- const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp
164
- const jimp = await read(bufferOrPath)
165
- const buf = await jimp.quality(50).resize(width, AUTO, RESIZE_BILINEAR).getBufferAsync(MIME_JPEG)
166
- return { buffer: buf, original: { width: jimp.getWidth(), height: jimp.getHeight() } }
167
- }
168
- throw new Boom('Nenhuma biblioteca de imagem disponível')
169
- }
170
- exports.extractImageThumb = extractImageThumb
171
-
172
- // ─── Encode base64 URL-safe para upload ──────────────────────────────────────
173
- const encodeBase64EncodedStringForUpload = (b64) =>
174
- encodeURIComponent(b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''))
175
- exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload
176
-
177
- // ─── Gera foto de perfil ──────────────────────────────────────────────────────
104
+ const img = lib.sharp.default(bufferOrFilePath);
105
+ const dimensions = await img.metadata();
106
+ const buffer = await img
107
+ .resize(width)
108
+ .jpeg({ quality: 50 })
109
+ .toBuffer();
110
+ return {
111
+ buffer,
112
+ original: {
113
+ width: dimensions.width,
114
+ height: dimensions.height,
115
+ },
116
+ };
117
+ }
118
+ else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
119
+ const { read, MIME_JPEG, RESIZE_BILINEAR, AUTO } = lib.jimp;
120
+ const jimp = await read(bufferOrFilePath);
121
+ const dimensions = {
122
+ width: jimp.getWidth(),
123
+ height: jimp.getHeight()
124
+ };
125
+ const buffer = await jimp
126
+ .quality(50)
127
+ .resize(width, AUTO, RESIZE_BILINEAR)
128
+ .getBufferAsync(MIME_JPEG);
129
+ return {
130
+ buffer,
131
+ original: dimensions
132
+ };
133
+ }
134
+ else {
135
+ throw new boom_1.Boom('No image processing library available');
136
+ }
137
+ };
138
+ exports.extractImageThumb = extractImageThumb;
139
+ const encodeBase64EncodedStringForUpload = (b64) => (encodeURIComponent(b64
140
+ .replace(/\+/g, '-')
141
+ .replace(/\//g, '_')
142
+ .replace(/\=+$/, '')));
143
+ exports.encodeBase64EncodedStringForUpload = encodeBase64EncodedStringForUpload;
178
144
  const generateProfilePicture = async (mediaUpload) => {
179
- const input = Buffer.isBuffer(mediaUpload)
180
- ? mediaUpload
181
- : typeof mediaUpload === 'object' && 'url' in mediaUpload
182
- ? mediaUpload.url.toString()
183
- : await toBuffer(mediaUpload.stream)
184
- const { read, MIME_JPEG, AUTO } = require('jimp')
185
- const jimp = await read(input)
186
- const cropped = jimp.crop(0, 0, jimp.getWidth(), jimp.getHeight())
187
- return { img: await cropped.quality(100).scaleToFit(720, 720, AUTO).getBufferAsync(MIME_JPEG) }
188
- }
189
- exports.generateProfilePicture = generateProfilePicture
190
-
191
- // ─── SHA256 de mensagem de mídia ──────────────────────────────────────────────
145
+ var _a, _b;
146
+ let bufferOrFilePath;
147
+ if (Buffer.isBuffer(mediaUpload)) {
148
+ bufferOrFilePath = mediaUpload;
149
+ }
150
+ else if ('url' in mediaUpload) {
151
+ bufferOrFilePath = mediaUpload.url.toString();
152
+ }
153
+ else {
154
+ bufferOrFilePath = await (0, exports.toBuffer)(mediaUpload.stream);
155
+ }
156
+ const lib = await getImageProcessingLibrary();
157
+ let img;
158
+ if ('sharp' in lib && typeof ((_a = lib.sharp) === null || _a === void 0 ? void 0 : _a.default) === 'function') {
159
+ img = lib.sharp.default(bufferOrFilePath)
160
+ .resize(640, 640)
161
+ .jpeg({
162
+ quality: 50,
163
+ })
164
+ .toBuffer();
165
+ }
166
+ else if ('jimp' in lib && typeof ((_b = lib.jimp) === null || _b === void 0 ? void 0 : _b.read) === 'function') {
167
+ const { read, MIME_JPEG, RESIZE_BILINEAR } = lib.jimp;
168
+ const jimp = await read(bufferOrFilePath);
169
+ const min = Math.min(jimp.getWidth(), jimp.getHeight());
170
+ const cropped = jimp.crop(0, 0, min, min);
171
+ img = cropped
172
+ .quality(50)
173
+ .resize(640, 640, RESIZE_BILINEAR)
174
+ .getBufferAsync(MIME_JPEG);
175
+ }
176
+ else {
177
+ throw new boom_1.Boom('No image processing library available');
178
+ }
179
+ return {
180
+ img: await img,
181
+ };
182
+ };
183
+ exports.generateProfilePicture = generateProfilePicture;
184
+ /** gets the SHA256 of the given media message */
192
185
  const mediaMessageSHA256B64 = (message) => {
193
- const media = Object.values(message)[0]
194
- return media?.fileSha256 && Buffer.from(media.fileSha256).toString('base64')
195
- }
196
- exports.mediaMessageSHA256B64 = mediaMessageSHA256B64
197
-
198
- // ─── Duração de áudio ─────────────────────────────────────────────────────────
186
+ const media = Object.values(message)[0];
187
+ return (media === null || media === void 0 ? void 0 : media.fileSha256) && Buffer.from(media.fileSha256).toString('base64');
188
+ };
189
+ exports.mediaMessageSHA256B64 = mediaMessageSHA256B64;
199
190
  async function getAudioDuration(buffer) {
200
- const mm = await import('music-metadata')
201
- if (Buffer.isBuffer(buffer)) return (await mm.parseBuffer(buffer, undefined, { duration: true })).format.duration
202
- if (typeof buffer === 'string') {
203
- const rs = createReadStream(buffer)
204
- try { return (await mm.parseStream(rs, undefined, { duration: true })).format.duration }
205
- finally { rs.destroy() }
191
+ try {
192
+ const { PassThrough } = require('stream');
193
+ const ff = require('fluent-ffmpeg');
194
+
195
+ return await new Promise((resolve, reject) => {
196
+ const inputStream = new PassThrough();
197
+ inputStream.end(buffer);
198
+
199
+ ff(inputStream)
200
+ .ffprobe((err, data) => {
201
+ if (err) reject(err);
202
+ else resolve(data.format.duration);
203
+ });
204
+ });
205
+ } catch (error) {
206
+ const musicMetadata = await import('music-metadata');
207
+ let metadata;
208
+ if (Buffer.isBuffer(buffer)) {
209
+ metadata = await musicMetadata.parseBuffer(buffer, undefined, {
210
+ duration: true
211
+ });
212
+ } else if (typeof buffer === 'string') {
213
+ const rStream = (0, fs_1.createReadStream)(buffer);
214
+ try {
215
+ metadata = await musicMetadata.parseStream(rStream, undefined, {
216
+ duration: true
217
+ });
218
+ } finally {
219
+ rStream.destroy();
220
+ }
221
+ } else {
222
+ metadata = await musicMetadata.parseStream(buffer, undefined, {
223
+ duration: true
224
+ });
225
+ }
226
+ return metadata.format.duration;
206
227
  }
207
- return (await mm.parseStream(buffer, undefined, { duration: true })).format.duration
208
228
  }
209
-
210
- // ─── Waveform de áudio ────────────────────────────────────────────────────────
229
+ exports.getAudioDuration = getAudioDuration;
211
230
  async function getAudioWaveform(buffer, logger) {
212
231
  try {
213
- const audioDecode = (b) => import('audio-decode').then(({ default: d }) => d(b))
214
- let data = Buffer.isBuffer(buffer) ? buffer
215
- : typeof buffer === 'string' ? await toBuffer(createReadStream(buffer))
216
- : await toBuffer(buffer)
217
- const decoded = await audioDecode(data)
218
- const raw = decoded.getChannelData(0)
219
- const samples = 64
220
- const blockSize = Math.floor(raw.length / samples)
221
- const filtered = Array.from({ length: samples }, (_, i) => {
222
- let sum = 0
223
- for (let j = 0; j < blockSize; j++) sum += Math.abs(raw[i * blockSize + j])
224
- return sum / blockSize
225
- })
226
- const mult = Math.pow(Math.max(...filtered), -1)
227
- return new Uint8Array(filtered.map(n => Math.floor(100 * n * mult)))
232
+ const { PassThrough } = require('stream');
233
+ const ff = require('fluent-ffmpeg');
234
+
235
+ let audioData;
236
+ if (Buffer.isBuffer(buffer)) {
237
+ audioData = buffer;
238
+ } else if (typeof buffer === 'string') {
239
+ const rStream = require('fs').createReadStream(buffer);
240
+ audioData = await exports.toBuffer(rStream);
241
+ } else {
242
+ audioData = await exports.toBuffer(buffer);
243
+ }
244
+
245
+ return await new Promise((resolve, reject) => {
246
+ const inputStream = new PassThrough();
247
+ inputStream.end(audioData);
248
+ const chunks = [];
249
+ const bars = 64;
250
+
251
+ ff(inputStream)
252
+ .audioChannels(1)
253
+ .audioFrequency(16000)
254
+ .format('s16le')
255
+ .on('error', reject)
256
+ .on('end', () => {
257
+ const rawData = Buffer.concat(chunks);
258
+ const samples = rawData.length / 2;
259
+ const amplitudes = [];
260
+
261
+ for (let i = 0; i < samples; i++) {
262
+ amplitudes.push(Math.abs(rawData.readInt16LE(i * 2)) / 32768);
263
+ }
264
+
265
+ const blockSize = Math.floor(amplitudes.length / bars);
266
+ const avg = [];
267
+ for (let i = 0; i < bars; i++) {
268
+ const block = amplitudes.slice(i * blockSize, (i + 1) * blockSize);
269
+ avg.push(block.reduce((a, b) => a + b, 0) / block.length);
270
+ }
271
+
272
+ const max = Math.max(...avg);
273
+ const normalized = avg.map(v => Math.floor((v / max) * 100));
274
+ resolve(new Uint8Array(normalized));
275
+ })
276
+ .pipe()
277
+ .on('data', chunk => chunks.push(chunk));
278
+ });
228
279
  } catch (e) {
229
- logger?.debug('Falha ao gerar waveform: ' + e)
280
+ logger?.debug(e);
230
281
  }
231
282
  }
232
-
233
- // ─── Buffer / Stream helpers ──────────────────────────────────────────────────
234
- const toReadable = (buffer) => {
235
- const r = new Readable({ read: () => {} })
236
- r.push(buffer)
237
- r.push(null)
238
- return r
283
+ exports.getAudioWaveform = getAudioWaveform;
284
+ async function convertToOpusBuffer(buffer, logger) {
285
+ try {
286
+ const { PassThrough } = require('stream');
287
+ const ff = require('fluent-ffmpeg');
288
+
289
+ return await new Promise((resolve, reject) => {
290
+ const inStream = new PassThrough();
291
+ const outStream = new PassThrough();
292
+ const chunks = [];
293
+ inStream.end(buffer);
294
+
295
+ ff(inStream)
296
+ .noVideo()
297
+ .audioCodec('libopus')
298
+ .format('ogg')
299
+ .audioBitrate('48k')
300
+ .audioChannels(1)
301
+ .audioFrequency(48000)
302
+ .outputOptions([
303
+ '-vn',
304
+ '-b:a 64k',
305
+ '-ac 2',
306
+ '-ar 48000',
307
+ '-map_metadata', '-1',
308
+ '-application', 'voip'
309
+ ])
310
+ .on('error', reject)
311
+ .on('end', () => resolve(Buffer.concat(chunks)))
312
+ .pipe(outStream, {
313
+ end: true
314
+ });
315
+ outStream.on('data', c => chunks.push(c));
316
+ });
317
+ } catch (e) {
318
+ logger?.debug(e);
319
+ throw e;
320
+ }
239
321
  }
240
- exports.toReadable = toReadable
241
-
322
+ exports.convertToOpusBuffer = convertToOpusBuffer;
323
+ const toReadable = (buffer) => {
324
+ const readable = new stream_1.Readable({ read: () => { } });
325
+ readable.push(buffer);
326
+ readable.push(null);
327
+ return readable;
328
+ };
329
+ exports.toReadable = toReadable;
242
330
  const toBuffer = async (stream) => {
243
- const chunks = []
244
- for await (const chunk of stream) chunks.push(chunk)
245
- stream.destroy()
246
- return Buffer.concat(chunks)
247
- }
248
- exports.toBuffer = toBuffer
249
-
331
+ const chunks = [];
332
+ for await (const chunk of stream) {
333
+ chunks.push(chunk);
334
+ }
335
+ stream.destroy();
336
+ return Buffer.concat(chunks);
337
+ };
338
+ exports.toBuffer = toBuffer;
250
339
  const getStream = async (item, opts) => {
251
- if (Buffer.isBuffer(item)) return { stream: toReadable(item), type: 'buffer' }
252
- if ('stream' in item) return { stream: item.stream, type: 'readable' }
253
- const url = item.url.toString()
254
- if (url.startsWith('http://') || url.startsWith('https://'))
255
- return { stream: await getHttpStream(item.url, opts), type: 'remote' }
256
- return { stream: createReadStream(item.url), type: 'file' }
257
- }
258
- exports.getStream = getStream
259
-
260
- // ─── Gera thumbnail ───────────────────────────────────────────────────────────
340
+ if (Buffer.isBuffer(item)) {
341
+ return { stream: (0, exports.toReadable)(item), type: 'buffer' };
342
+ }
343
+ if ('stream' in item) {
344
+ return { stream: item.stream, type: 'readable' };
345
+ }
346
+ if (item.url.toString().startsWith('http://') || item.url.toString().startsWith('https://')) {
347
+ return { stream: await (0, exports.getHttpStream)(item.url, opts), type: 'remote' };
348
+ }
349
+ return { stream: (0, fs_1.createReadStream)(item.url), type: 'file' };
350
+ };
351
+ exports.getStream = getStream;
352
+ /** generates a thumbnail for a given media, if required */
261
353
  async function generateThumbnail(file, mediaType, options) {
262
- var _a
263
- let thumbnail, originalImageDimensions
354
+ var _a;
355
+ let thumbnail;
356
+ let originalImageDimensions;
264
357
  if (mediaType === 'image') {
265
- const { buffer, original } = await extractImageThumb(file)
266
- thumbnail = buffer.toString('base64')
267
- if (original.width && original.height) originalImageDimensions = original
268
- } else if (mediaType === 'video') {
269
- const imgPath = join(getTmpDir(), Helpers.generateMessageID() + '.jpg')
358
+ const { buffer, original } = await (0, exports.extractImageThumb)(file);
359
+ thumbnail = buffer.toString('base64');
360
+ if (original.width && original.height) {
361
+ originalImageDimensions = {
362
+ width: original.width,
363
+ height: original.height,
364
+ };
365
+ }
366
+ }
367
+ else if (mediaType === 'video') {
368
+ const imgFilename = (0, path_1.join)(getTmpFilesDirectory(), (0, generics_1.generateMessageID)() + '.jpg');
270
369
  try {
271
- await extractVideoThumb(file, imgPath, '00:00:00', { width: 32, height: 32 })
272
- thumbnail = (await fsp.readFile(imgPath)).toString('base64')
273
- await fsp.unlink(imgPath)
274
- } catch (err) {
275
- (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('Falha ao gerar thumbnail de vídeo: ' + err)
370
+ await extractVideoThumb(file, imgFilename, '00:00:00', { width: 32, height: 32 });
371
+ const buff = await fs_1.promises.readFile(imgFilename);
372
+ thumbnail = buff.toString('base64');
373
+ await fs_1.promises.unlink(imgFilename);
374
+ }
375
+ catch (err) {
376
+ (_a = options.logger) === null || _a === void 0 ? void 0 : _a.debug('could not generate video thumb: ' + err);
276
377
  }
277
378
  }
278
- return { thumbnail, originalImageDimensions }
379
+ return {
380
+ thumbnail,
381
+ originalImageDimensions
382
+ };
279
383
  }
280
-
281
- // ─── HTTP Stream ──────────────────────────────────────────────────────────────
384
+ exports.generateThumbnail = generateThumbnail;
282
385
  const getHttpStream = async (url, options = {}) => {
283
- const { default: axios } = await import('axios')
284
- const res = await axios.get(url.toString(), { ...options, responseType: 'stream' })
285
- return res.data
286
- }
287
- exports.getHttpStream = getHttpStream
288
-
289
- // ─── Prepara stream (newsletter) ──────────────────────────────────────────────
386
+ const { default: axios } = await import('axios');
387
+ const fetched = await axios.get(url.toString(), { ...options, responseType: 'stream' });
388
+ return fetched.data;
389
+ };
390
+ exports.getHttpStream = getHttpStream;
290
391
  const prepareStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
291
- const { stream, type } = await getStream(media, opts)
292
- logger?.debug('stream de mídia obtido')
293
- let bodyPath, didSaveToTmpPath = false
392
+ const { stream, type } = await (0, exports.getStream)(media, opts);
393
+ logger === null || logger === void 0 ? void 0 : logger.debug('fetched media stream');
394
+ let bodyPath;
395
+ let didSaveToTmpPath = false;
294
396
  try {
295
- const buffer = await toBuffer(stream)
296
- if (type === 'file') { bodyPath = media.url }
397
+ const buffer = await (0, exports.toBuffer)(stream);
398
+ if (type === 'file') {
399
+ bodyPath = media.url;
400
+ }
297
401
  else if (saveOriginalFileIfRequired) {
298
- bodyPath = join(getTmpDir(), mediaType + Helpers.generateMessageID())
299
- writeFileSync(bodyPath, buffer)
300
- didSaveToTmpPath = true
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;
301
447
  }
302
- const fileSha256 = Crypto.createHash('sha256').update(buffer).digest()
303
- stream?.destroy()
304
- return { mediaKey: undefined, encWriteStream: buffer, fileLength: buffer.length, fileSha256, fileEncSha256: undefined, bodyPath, didSaveToTmpPath }
305
- } catch (error) {
306
- stream.destroy()
307
- if (didSaveToTmpPath) { try { await fsp.unlink(bodyPath) } catch (e) { logger?.error({ e }, 'falha ao remover tmp') } }
308
- throw error
309
448
  }
310
- }
311
- exports.prepareStream = prepareStream
312
449
 
313
- // ─── Stream encriptado ────────────────────────────────────────────────────────
314
- const encryptedStream = async (media, mediaType, { logger, saveOriginalFileIfRequired, opts } = {}) => {
315
- const { stream, type } = await getStream(media, opts)
316
- logger?.debug('stream de mídia obtido')
317
- const mediaKey = Crypto.randomBytes(32)
318
- const { cipherKey, iv, macKey } = await getMediaKeys(mediaKey, mediaType)
319
- const encWriteStream = new Readable({ read: () => {} })
320
- let bodyPath, writeStream, didSaveToTmpPath = false
321
- if (type === 'file') { bodyPath = media.url }
450
+ const mediaKey = Crypto.randomBytes(32);
451
+ const { cipherKey, iv, macKey } = getMediaKeys(mediaKey, mediaType);
452
+ const encWriteStream = new stream_1.Readable({ read: () => { } });
453
+ let bodyPath;
454
+ let writeStream;
455
+ let didSaveToTmpPath = false;
456
+
457
+ if (type === 'file') {
458
+ bodyPath = media.url;
459
+ }
322
460
  else if (saveOriginalFileIfRequired) {
323
- bodyPath = join(getTmpDir(), mediaType + Helpers.generateMessageID())
324
- writeStream = createWriteStream(bodyPath)
325
- didSaveToTmpPath = true
326
- }
327
- let fileLength = 0
328
- const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv)
329
- let hmac = Crypto.createHmac('sha256', macKey).update(iv)
330
- let sha256Plain = Crypto.createHash('sha256')
331
- let sha256Enc = Crypto.createHash('sha256')
332
-
333
- function onChunk(buff) {
334
- sha256Enc = sha256Enc.update(buff)
335
- hmac = hmac.update(buff)
336
- encWriteStream.push(buff)
461
+ bodyPath = (0, path_1.join)(getTmpFilesDirectory(), mediaType + (0, generics_1.generateMessageID)());
462
+ writeStream = (0, fs_1.createWriteStream)(bodyPath);
463
+ didSaveToTmpPath = true;
337
464
  }
338
-
465
+
466
+ let fileLength = 0;
467
+ const aes = Crypto.createCipheriv('aes-256-cbc', cipherKey, iv);
468
+ let hmac = Crypto.createHmac('sha256', macKey).update(iv);
469
+ let sha256Plain = Crypto.createHash('sha256');
470
+ let sha256Enc = Crypto.createHash('sha256');
471
+
339
472
  try {
340
- for await (const data of stream) {
341
- fileLength += data.length
342
- if (type === 'remote' && opts?.maxContentLength && fileLength + data.length > opts.maxContentLength)
343
- throw new Boom(`conteúdo excedeu o limite para "${type}"`, { data: { media, type } })
344
- sha256Plain = sha256Plain.update(data)
345
- if (writeStream) { if (!writeStream.write(data)) await once(writeStream, 'drain') }
346
- onChunk(aes.update(data))
473
+ for await (const data of finalStream) {
474
+ fileLength += data.length;
475
+ if (type === 'remote'
476
+ && (opts === null || opts === void 0 ? void 0 : opts.maxContentLength)
477
+ && fileLength + data.length > opts.maxContentLength) {
478
+ throw new boom_1.Boom(`content length exceeded when encrypting "${type}"`, {
479
+ data: { media, type }
480
+ });
481
+ }
482
+
483
+ sha256Plain = sha256Plain.update(data);
484
+ if (writeStream) {
485
+ if (!writeStream.write(data)) {
486
+ await (0, events_1.once)(writeStream, 'drain');
487
+ }
488
+ }
489
+ onChunk(aes.update(data));
347
490
  }
348
- onChunk(aes.final())
349
- const mac = hmac.digest().slice(0, 10)
350
- sha256Enc = sha256Enc.update(mac)
351
- const fileSha256 = sha256Plain.digest()
352
- const fileEncSha256 = sha256Enc.digest()
353
- encWriteStream.push(mac)
354
- encWriteStream.push(null)
355
- writeStream?.end()
356
- stream.destroy()
357
- logger?.debug('dados encriptados com sucesso')
358
- return { mediaKey, encWriteStream, bodyPath, mac, fileEncSha256, fileSha256, fileLength, didSaveToTmpPath }
359
- } catch (error) {
360
- encWriteStream.destroy(); writeStream?.destroy()
361
- aes.destroy(); hmac.destroy(); sha256Plain.destroy(); sha256Enc.destroy(); stream.destroy()
362
- if (didSaveToTmpPath) { try { await fsp.unlink(bodyPath) } catch (e) { logger?.error({ e }, 'falha ao remover tmp') } }
363
- throw error
364
- }
365
- }
366
- exports.encryptedStream = encryptedStream
367
-
368
- // ─── URL de download ──────────────────────────────────────────────────────────
369
- const DEF_HOST = 'mmg.whatsapp.net'
370
- const AES_CHUNK_SIZE = 16
371
- const toSmallestChunk = (num) => Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE
372
- const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`
373
- exports.getUrlFromDirectPath = getUrlFromDirectPath
374
-
375
- // ─────────────────────────────────────────────────────────────────────────────
376
- // downloadContentFromMessage — original
377
- // ─────────────────────────────────────────────────────────────────────────────
378
- const downloadContentFromMessage = async ({ mediaKey, directPath, url }, type, opts = {}) => {
379
- const downloadUrl = url || getUrlFromDirectPath(directPath)
380
- const keys = await getMediaKeys(mediaKey, type)
381
- return downloadEncryptedContent(downloadUrl, keys, opts)
382
- }
383
- exports.downloadContentFromMessage = downloadContentFromMessage
384
-
385
- // ─────────────────────────────────────────────────────────────────────────────
386
- // downloadContentFromMessageV1 — versão melhorada
387
- // Melhorias: retry automático, progresso, cache, timeout, mime type, buffer, arquivo
388
- // ─────────────────────────────────────────────────────────────────────────────
389
- const downloadContentFromMessageV1 = async (
390
- { mediaKey, directPath, url },
391
- type,
392
- opts = {}
393
- ) => {
394
- const {
395
- onProgress = null, // ({ downloaded, total, percent }) => void
396
- useCache = false, // cache em memória
397
- saveToFile = null, // salvar direto em arquivo
398
- timeoutMs = 60_000, // timeout em ms
399
- retries = MAX_RETRIES,
400
- asBuffer = false, // retornar buffer em vez de stream
401
- } = opts
402
-
403
- const downloadUrl = url || getUrlFromDirectPath(directPath)
404
-
405
- // ── Cache ────────────────────────────────────────────────────────────────
406
- if (useCache && DOWNLOAD_CACHE.has(downloadUrl)) {
407
- const cached = DOWNLOAD_CACHE.get(downloadUrl)
408
- return asBuffer ? cached : toReadable(cached)
491
+
492
+ onChunk(aes.final());
493
+ const mac = hmac.digest().slice(0, 10);
494
+ sha256Enc = sha256Enc.update(mac);
495
+ const fileSha256 = sha256Plain.digest();
496
+ const fileEncSha256 = sha256Enc.digest();
497
+
498
+ encWriteStream.push(mac);
499
+ encWriteStream.push(null);
500
+ writeStream === null || writeStream === void 0 ? void 0 : writeStream.end();
501
+ finalStream.destroy();
502
+
503
+ return {
504
+ mediaKey,
505
+ encWriteStream,
506
+ bodyPath,
507
+ mac,
508
+ fileEncSha256,
509
+ fileSha256,
510
+ fileLength,
511
+ didSaveToTmpPath
512
+ };
409
513
  }
410
-
411
- const keys = await getMediaKeys(mediaKey, type)
412
-
413
- // ── Retry automático ──────────────────────────────────────────────────────
414
- let lastError
415
- for (let attempt = 1; attempt <= retries; attempt++) {
416
- try {
417
- const result = await _executeDownload({ downloadUrl, keys, opts, onProgress, saveToFile, timeoutMs, asBuffer })
418
- if (useCache && Buffer.isBuffer(result)) DOWNLOAD_CACHE.set(downloadUrl, result)
419
- return result
420
- } catch (err) {
421
- lastError = err
422
- if (attempt < retries) {
423
- const delay = RETRY_DELAY_MS * attempt
424
- console.warn(`[downloadV1] Tentativa ${attempt}/${retries} falhou. Retentando em ${delay}ms...`)
425
- await sleep(delay)
514
+ catch (error) {
515
+ encWriteStream.destroy();
516
+ writeStream === null || writeStream === void 0 ? void 0 : writeStream.destroy();
517
+ aes.destroy();
518
+ hmac.destroy();
519
+ sha256Plain.destroy();
520
+ sha256Enc.destroy();
521
+ finalStream.destroy();
522
+
523
+ if (didSaveToTmpPath) {
524
+ try {
525
+ await fs_1.promises.unlink(bodyPath);
526
+ }
527
+ catch (err) {
426
528
  }
427
529
  }
530
+ throw error;
428
531
  }
429
-
430
- throw new Error(`[downloadV1] Falhou após ${retries} tentativas: ${lastError?.message}`)
431
- }
432
- exports.downloadContentFromMessageV1 = downloadContentFromMessageV1
433
-
434
- // ─── Executa download com progresso/timeout ───────────────────────────────────
435
- const _executeDownload = async ({ downloadUrl, keys, opts, onProgress, saveToFile, timeoutMs, asBuffer }) => {
436
- const controller = new AbortController()
437
- const timer = setTimeout(() => controller.abort(), timeoutMs)
438
- let fetchedStream
439
- try {
440
- fetchedStream = await downloadEncryptedContent(downloadUrl, keys, {
441
- ...opts,
442
- options: { ...(opts.options || {}), signal: controller.signal }
443
- })
444
- } finally {
445
- clearTimeout(timer)
532
+
533
+ function onChunk(buff) {
534
+ sha256Enc = sha256Enc.update(buff);
535
+ hmac = hmac.update(buff);
536
+ encWriteStream.push(buff);
446
537
  }
447
-
448
- if (!onProgress && !saveToFile && !asBuffer) return fetchedStream
449
-
450
- return new Promise((resolve, reject) => {
451
- const chunks = []
452
- let downloaded = 0
453
- const total = parseInt(fetchedStream.headers?.['content-length'] || '0', 10)
454
-
455
- fetchedStream.on('data', (chunk) => {
456
- chunks.push(chunk)
457
- downloaded += chunk.length
458
- if (onProgress) onProgress({
459
- downloaded,
460
- total,
461
- percent: total > 0 ? Math.round((downloaded / total) * 100) : null,
462
- })
463
- })
464
-
465
- fetchedStream.on('end', async () => {
466
- const buffer = Buffer.concat(chunks)
467
- if (saveToFile) {
468
- try { await fsp.writeFile(saveToFile, buffer); resolve(saveToFile) }
469
- catch (err) { reject(err) }
470
- return
471
- }
472
- resolve(asBuffer ? buffer : toReadable(buffer))
473
- })
474
-
475
- fetchedStream.on('error', reject)
476
- })
477
- }
478
-
479
- // ─────────────────────────────────────────────────────────────────────────────
480
- // downloadEncryptedContent — desencripta stream AES256-CBC
481
- // ─────────────────────────────────────────────────────────────────────────────
538
+ };
539
+ exports.encryptedStream = encryptedStream;
540
+ const DEF_HOST = 'mmg.whatsapp.net';
541
+ const AES_CHUNK_SIZE = 16;
542
+ const toSmallestChunkSize = (num) => {
543
+ return Math.floor(num / AES_CHUNK_SIZE) * AES_CHUNK_SIZE;
544
+ };
545
+ const getUrlFromDirectPath = (directPath) => `https://${DEF_HOST}${directPath}`;
546
+ exports.getUrlFromDirectPath = getUrlFromDirectPath;
547
+ const downloadContentFromMessage = ({ mediaKey, directPath, url }, type, opts = {}) => {
548
+ const downloadUrl = url || (0, exports.getUrlFromDirectPath)(directPath);
549
+ const keys = getMediaKeys(mediaKey, type);
550
+ return (0, exports.downloadEncryptedContent)(downloadUrl, keys, opts);
551
+ };
552
+ exports.downloadContentFromMessage = downloadContentFromMessage;
553
+ /**
554
+ * Decrypts and downloads an AES256-CBC encrypted file given the keys.
555
+ * Assumes the SHA256 of the plaintext is appended to the end of the ciphertext
556
+ * */
482
557
  const downloadEncryptedContent = async (downloadUrl, { cipherKey, iv }, { startByte, endByte, options } = {}) => {
483
- let bytesFetched = 0, startChunk = 0, firstBlockIsIV = false
558
+ let bytesFetched = 0;
559
+ let startChunk = 0;
560
+ let firstBlockIsIV = false;
561
+ // if a start byte is specified -- then we need to fetch the previous chunk as that will form the IV
484
562
  if (startByte) {
485
- const chunk = toSmallestChunk(startByte || 0)
486
- if (chunk) { startChunk = chunk - AES_CHUNK_SIZE; bytesFetched = chunk; firstBlockIsIV = true }
563
+ const chunk = toSmallestChunkSize(startByte || 0);
564
+ if (chunk) {
565
+ startChunk = chunk - AES_CHUNK_SIZE;
566
+ bytesFetched = chunk;
567
+ firstBlockIsIV = true;
568
+ }
487
569
  }
488
- const endChunk = endByte ? toSmallestChunk(endByte || 0) + AES_CHUNK_SIZE : undefined
489
- const headers = { ...(options?.headers || {}), Origin: Config.DEFAULT_ORIGIN }
570
+ const endChunk = endByte ? toSmallestChunkSize(endByte || 0) + AES_CHUNK_SIZE : undefined;
571
+ const headers = {
572
+ ...(options === null || options === void 0 ? void 0 : options.headers) || {},
573
+ Origin: Defaults_1.DEFAULT_ORIGIN,
574
+ };
490
575
  if (startChunk || endChunk) {
491
- headers.Range = `bytes=${startChunk}-`
492
- if (endChunk) headers.Range += endChunk
576
+ headers.Range = `bytes=${startChunk}-`;
577
+ if (endChunk) {
578
+ headers.Range += endChunk;
579
+ }
493
580
  }
494
- const fetched = await getHttpStream(downloadUrl, { ...options || {}, headers, maxBodyLength: Infinity, maxContentLength: Infinity })
495
- let remainingBytes = Buffer.from([])
496
- let aes
497
-
581
+ // download the message
582
+ const fetched = await (0, exports.getHttpStream)(downloadUrl, {
583
+ ...options || {},
584
+ headers,
585
+ maxBodyLength: Infinity,
586
+ maxContentLength: Infinity,
587
+ });
588
+ let remainingBytes = Buffer.from([]);
589
+ let aes;
498
590
  const pushBytes = (bytes, push) => {
499
591
  if (startByte || endByte) {
500
- const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0)
501
- const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0)
502
- push(bytes.slice(start, end))
503
- bytesFetched += bytes.length
504
- } else {
505
- push(bytes)
592
+ const start = bytesFetched >= startByte ? undefined : Math.max(startByte - bytesFetched, 0);
593
+ const end = bytesFetched + bytes.length < endByte ? undefined : Math.max(endByte - bytesFetched, 0);
594
+ push(bytes.slice(start, end));
595
+ bytesFetched += bytes.length;
506
596
  }
507
- }
508
-
509
- const output = new Transform({
597
+ else {
598
+ push(bytes);
599
+ }
600
+ };
601
+ const output = new stream_1.Transform({
510
602
  transform(chunk, _, callback) {
511
- let data = Buffer.concat([remainingBytes, chunk])
512
- const decryptLen = toSmallestChunk(data.length)
513
- remainingBytes = data.slice(decryptLen)
514
- data = data.slice(0, decryptLen)
603
+ let data = Buffer.concat([remainingBytes, chunk]);
604
+ const decryptLength = toSmallestChunkSize(data.length);
605
+ remainingBytes = data.slice(decryptLength);
606
+ data = data.slice(0, decryptLength);
515
607
  if (!aes) {
516
- let ivValue = iv
517
- if (firstBlockIsIV) { ivValue = data.slice(0, AES_CHUNK_SIZE); data = data.slice(AES_CHUNK_SIZE) }
518
- aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue)
519
- if (endByte) aes.setAutoPadding(false)
608
+ let ivValue = iv;
609
+ if (firstBlockIsIV) {
610
+ ivValue = data.slice(0, AES_CHUNK_SIZE);
611
+ data = data.slice(AES_CHUNK_SIZE);
612
+ }
613
+ aes = Crypto.createDecipheriv('aes-256-cbc', cipherKey, ivValue);
614
+ // if an end byte that is not EOF is specified
615
+ // stop auto padding (PKCS7) -- otherwise throws an error for decryption
616
+ if (endByte) {
617
+ aes.setAutoPadding(false);
618
+ }
619
+ }
620
+ try {
621
+ pushBytes(aes.update(data), b => this.push(b));
622
+ callback();
623
+ }
624
+ catch (error) {
625
+ callback(error);
520
626
  }
521
- try { pushBytes(aes.update(data), b => this.push(b)); callback() }
522
- catch (err) { callback(err) }
523
627
  },
524
628
  final(callback) {
525
- try { pushBytes(aes.final(), b => this.push(b)); callback() }
526
- catch (err) { callback(err) }
527
- }
528
- })
529
-
530
- return fetched.pipe(output, { end: true })
531
- }
532
- exports.downloadEncryptedContent = downloadEncryptedContent
533
-
534
- // ─── Extensão por tipo de mensagem ───────────────────────────────────────────
629
+ try {
630
+ pushBytes(aes.final(), b => this.push(b));
631
+ callback();
632
+ }
633
+ catch (error) {
634
+ callback(error);
635
+ }
636
+ },
637
+ });
638
+ return fetched.pipe(output, { end: true });
639
+ };
640
+ exports.downloadEncryptedContent = downloadEncryptedContent;
535
641
  function extensionForMediaMessage(message) {
536
- const type = Object.keys(message)[0]
537
- if (['locationMessage', 'liveLocationMessage', 'productMessage'].includes(type)) return '.jpeg'
538
- return '.' + message[type].mimetype.split(';')[0].split('/')[1]
642
+ const getExtension = (mimetype) => mimetype.split(';')[0].split('/')[1];
643
+ const type = Object.keys(message)[0];
644
+ let extension;
645
+ if (type === 'locationMessage' ||
646
+ type === 'liveLocationMessage' ||
647
+ type === 'productMessage') {
648
+ extension = '.jpeg';
649
+ }
650
+ else {
651
+ const messageContent = message[type];
652
+ extension = getExtension(messageContent.mimetype);
653
+ }
654
+ return extension;
539
655
  }
540
-
541
- // ─── Upload para servidor WA ──────────────────────────────────────────────────
656
+ exports.extensionForMediaMessage = extensionForMediaMessage;
542
657
  const getWAUploadToServer = ({ customUploadHosts, fetchAgent, logger, options }, refreshMediaConn) => {
543
658
  return async (stream, { mediaType, fileEncSha256B64, newsletter, timeoutMs }) => {
544
- var _a, _b
545
- const { default: axios } = await import('axios')
546
- let uploadInfo = await refreshMediaConn(false)
547
- let urls
548
- const hosts = [...customUploadHosts, ...uploadInfo.hosts]
549
- const chunks = []
550
- if (!Buffer.isBuffer(stream)) { for await (const c of stream) chunks.push(c) }
551
- const body = Buffer.isBuffer(stream) ? stream : Buffer.concat(chunks)
552
- fileEncSha256B64 = encodeBase64EncodedStringForUpload(fileEncSha256B64)
553
- let media = Config.MEDIA_PATH_MAP[mediaType]
554
- if (newsletter) media = media?.replace('/mms/', '/newsletter/newsletter-')
555
-
659
+ var _a, _b;
660
+ const { default: axios } = await import('axios');
661
+ // send a query JSON to obtain the url & auth token to upload our media
662
+ let uploadInfo = await refreshMediaConn(false);
663
+ let urls;
664
+ 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
+ fileEncSha256B64 = (0, exports.encodeBase64EncodedStringForUpload)(fileEncSha256B64);
673
+ let media = Defaults_1.MEDIA_PATH_MAP[mediaType];
674
+ if (newsletter) {
675
+ media = media === null || media === void 0 ? void 0 : media.replace('/mms/', '/newsletter/newsletter-');
676
+ }
556
677
  for (const { hostname, maxContentLengthBytes } of hosts) {
557
- logger.debug(`enviando para "${hostname}"`)
558
- const auth = encodeURIComponent(uploadInfo.auth)
559
- const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`
560
- let result
678
+ logger.debug(`uploading to "${hostname}"`);
679
+ const auth = encodeURIComponent(uploadInfo.auth); // the auth token
680
+ const url = `https://${hostname}${media}/${fileEncSha256B64}?auth=${auth}&token=${fileEncSha256B64}`;
681
+ let result;
561
682
  try {
562
- if (maxContentLengthBytes && body.length > maxContentLengthBytes)
563
- throw new Boom(`Body muito grande para "${hostname}"`, { statusCode: 413 })
564
- const res = await axios.post(url, body, {
683
+ if (maxContentLengthBytes && reqBody.length > maxContentLengthBytes) {
684
+ throw new boom_1.Boom(`Body too large for "${hostname}"`, { statusCode: 413 });
685
+ }
686
+ const body = await axios.post(url, reqBody, {
565
687
  ...options,
566
- headers: { ...options.headers || {}, 'Content-Type': 'application/octet-stream', 'Origin': Config.DEFAULT_ORIGIN },
567
- httpsAgent: fetchAgent, timeout: timeoutMs, responseType: 'json',
568
- maxBodyLength: Infinity, maxContentLength: Infinity,
569
- })
570
- result = res.data
571
- if (result?.url || result?.directPath) {
572
- urls = { mediaUrl: result.url, directPath: result.direct_path, handle: result.handle }
573
- break
574
- } else {
575
- uploadInfo = await refreshMediaConn(true)
576
- throw new Error(`upload falhou: ${JSON.stringify(result)}`)
688
+ headers: {
689
+ ...options.headers || {},
690
+ 'Content-Type': 'application/octet-stream',
691
+ 'Origin': Defaults_1.DEFAULT_ORIGIN
692
+ },
693
+ httpsAgent: fetchAgent,
694
+ timeout: timeoutMs,
695
+ responseType: 'json',
696
+ maxBodyLength: Infinity,
697
+ maxContentLength: Infinity,
698
+ });
699
+ result = body.data;
700
+ if ((result === null || result === void 0 ? void 0 : result.url) || (result === null || result === void 0 ? void 0 : result.directPath)) {
701
+ urls = {
702
+ mediaUrl: result.url,
703
+ directPath: result.direct_path,
704
+ handle: result.handle
705
+ };
706
+ break;
707
+ }
708
+ else {
709
+ uploadInfo = await refreshMediaConn(true);
710
+ throw new Error(`upload failed, reason: ${JSON.stringify(result)}`);
711
+ }
712
+ }
713
+ catch (error) {
714
+ if (axios.isAxiosError(error)) {
715
+ result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data;
577
716
  }
578
- } catch (error) {
579
- if (axios.isAxiosError(error)) result = (_a = error.response) === null || _a === void 0 ? void 0 : _a.data
580
- const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname)
581
- logger.warn({ trace: error.stack, uploadResult: result }, `Erro ao enviar para ${hostname}${isLast ? '' : ', tentando próximo...'}`)
717
+ const isLast = hostname === ((_b = hosts[uploadInfo.hosts.length - 1]) === null || _b === void 0 ? void 0 : _b.hostname);
718
+ logger.warn({ trace: error.stack, uploadResult: result }, `Error in uploading to ${hostname} ${isLast ? '' : ', retrying...'}`);
582
719
  }
583
720
  }
584
- if (!urls) throw new Boom('Upload de mídia falhou em todos os hosts', { statusCode: 500 })
585
- return urls
586
- }
587
- }
588
- exports.getWAUploadToServer = getWAUploadToServer
589
-
590
- // ─── Retry de mídia ───────────────────────────────────────────────────────────
591
- const getMediaRetryKey = (mediaKey) => CryptoUtils.hkdf(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' })
592
-
593
- const encryptMediaRetryRequest = async (key, mediaKey, meId) => {
594
- const recp = Proto.proto.ServerErrorReceipt.encode({ stanzaId: key.id }).finish()
595
- const iv = Crypto.randomBytes(12)
596
- const retryKey = await getMediaRetryKey(mediaKey)
597
- const ciphertext = CryptoUtils.aesEncryptGCM(recp, retryKey, iv, Buffer.from(key.id))
598
- return {
721
+ if (!urls) {
722
+ throw new boom_1.Boom('Media upload failed on all hosts', { statusCode: 500 });
723
+ }
724
+ return urls;
725
+ };
726
+ };
727
+ exports.getWAUploadToServer = getWAUploadToServer;
728
+ const getMediaRetryKey = (mediaKey) => {
729
+ return (0, crypto_1.hkdf)(mediaKey, 32, { info: 'WhatsApp Media Retry Notification' });
730
+ };
731
+ /**
732
+ * Generate a binary node that will request the phone to re-upload the media & return the newly uploaded URL
733
+ */
734
+ const encryptMediaRetryRequest = (key, mediaKey, meId) => {
735
+ const recp = { stanzaId: key.id };
736
+ const recpBuffer = WAProto_1.proto.ServerErrorReceipt.encode(recp).finish();
737
+ const iv = Crypto.randomBytes(12);
738
+ const retryKey = getMediaRetryKey(mediaKey);
739
+ const ciphertext = (0, crypto_1.aesEncryptGCM)(recpBuffer, retryKey, iv, Buffer.from(key.id));
740
+ const req = {
599
741
  tag: 'receipt',
600
- attrs: { id: key.id, to: JidUtils.jidNormalizedUser(meId), type: 'server-error' },
742
+ attrs: {
743
+ id: key.id,
744
+ to: (0, WABinary_1.jidNormalizedUser)(meId),
745
+ type: 'server-error'
746
+ },
601
747
  content: [
602
- { tag: 'encrypt', attrs: {}, content: [
603
- { tag: 'enc_p', attrs: {}, content: ciphertext },
604
- { tag: 'enc_iv', attrs: {}, content: iv }
605
- ]},
606
- { tag: 'rmr', attrs: { jid: key.remoteJid, 'from_me': (!!key.fromMe).toString(), participant: key.participant || undefined } }
748
+ // this encrypt node is actually pretty useless
749
+ // the media is returned even without this node
750
+ // keeping it here to maintain parity with WA Web
751
+ {
752
+ tag: 'encrypt',
753
+ attrs: {},
754
+ content: [
755
+ { tag: 'enc_p', attrs: {}, content: ciphertext },
756
+ { tag: 'enc_iv', attrs: {}, content: iv }
757
+ ]
758
+ },
759
+ {
760
+ tag: 'rmr',
761
+ attrs: {
762
+ jid: key.remoteJid,
763
+ 'from_me': (!!key.fromMe).toString(),
764
+ // @ts-ignore
765
+ participant: key.participant || undefined
766
+ }
767
+ }
607
768
  ]
608
- }
609
- }
610
- exports.encryptMediaRetryRequest = encryptMediaRetryRequest
611
-
769
+ };
770
+ return req;
771
+ };
772
+ exports.encryptMediaRetryRequest = encryptMediaRetryRequest;
612
773
  const decodeMediaRetryNode = (node) => {
613
- const rmr = JidUtils.getBinaryNodeChild(node, 'rmr')
614
- const event = { key: { id: node.attrs.id, remoteJid: rmr.attrs.jid, fromMe: rmr.attrs.from_me === 'true', participant: rmr.attrs.participant } }
615
- const err = JidUtils.getBinaryNodeChild(node, 'error')
616
- if (err) {
617
- const code = +err.attrs.code
618
- event.error = new Boom(`Falha ao reenviar mídia (${code})`, { data: err.attrs, statusCode: getStatusCodeForMediaRetry(code) })
619
- } else {
620
- const enc = JidUtils.getBinaryNodeChild(node, 'encrypt')
621
- const ciphertext = JidUtils.getBinaryNodeChildBuffer(enc, 'enc_p')
622
- const iv = JidUtils.getBinaryNodeChildBuffer(enc, 'enc_iv')
623
- event.media = (ciphertext && iv) ? { ciphertext, iv } : undefined
624
- if (!event.media) event.error = new Boom('Falha ao reenviar mídia (sem ciphertext)', { statusCode: 404 })
625
- }
626
- return event
627
- }
628
- exports.decodeMediaRetryNode = decodeMediaRetryNode
629
-
630
- const decryptMediaRetryData = async ({ ciphertext, iv }, mediaKey, msgId) => {
631
- const retryKey = await getMediaRetryKey(mediaKey)
632
- return Proto.proto.MediaRetryNotification.decode(CryptoUtils.aesDecryptGCM(ciphertext, retryKey, iv, Buffer.from(msgId)))
633
- }
634
- exports.decryptMediaRetryData = decryptMediaRetryData
635
-
774
+ const rmrNode = (0, WABinary_1.getBinaryNodeChild)(node, 'rmr');
775
+ const event = {
776
+ key: {
777
+ id: node.attrs.id,
778
+ remoteJid: rmrNode.attrs.jid,
779
+ fromMe: rmrNode.attrs.from_me === 'true',
780
+ participant: rmrNode.attrs.participant
781
+ }
782
+ };
783
+ const errorNode = (0, WABinary_1.getBinaryNodeChild)(node, 'error');
784
+ if (errorNode) {
785
+ const errorCode = +errorNode.attrs.code;
786
+ event.error = new boom_1.Boom(`Failed to re-upload media (${errorCode})`, { data: errorNode.attrs, statusCode: (0, exports.getStatusCodeForMediaRetry)(errorCode) });
787
+ }
788
+ else {
789
+ const encryptedInfoNode = (0, WABinary_1.getBinaryNodeChild)(node, 'encrypt');
790
+ const ciphertext = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_p');
791
+ const iv = (0, WABinary_1.getBinaryNodeChildBuffer)(encryptedInfoNode, 'enc_iv');
792
+ if (ciphertext && iv) {
793
+ event.media = { ciphertext, iv };
794
+ }
795
+ else {
796
+ event.error = new boom_1.Boom('Failed to re-upload media (missing ciphertext)', { statusCode: 404 });
797
+ }
798
+ }
799
+ return event;
800
+ };
801
+ exports.decodeMediaRetryNode = decodeMediaRetryNode;
802
+ const decryptMediaRetryData = ({ ciphertext, iv }, mediaKey, msgId) => {
803
+ const retryKey = getMediaRetryKey(mediaKey);
804
+ const plaintext = (0, crypto_1.aesDecryptGCM)(ciphertext, retryKey, iv, Buffer.from(msgId));
805
+ return WAProto_1.proto.MediaRetryNotification.decode(plaintext);
806
+ };
807
+ exports.decryptMediaRetryData = decryptMediaRetryData;
808
+ const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code];
809
+ exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry;
636
810
  const MEDIA_RETRY_STATUS_MAP = {
637
- [Proto.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
638
- [Proto.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
639
- [Proto.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
640
- [Proto.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
811
+ [WAProto_1.proto.MediaRetryNotification.ResultType.SUCCESS]: 200,
812
+ [WAProto_1.proto.MediaRetryNotification.ResultType.DECRYPTION_ERROR]: 412,
813
+ [WAProto_1.proto.MediaRetryNotification.ResultType.NOT_FOUND]: 404,
814
+ [WAProto_1.proto.MediaRetryNotification.ResultType.GENERAL_ERROR]: 418,
815
+ };
816
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
817
+ function __importStar(arg0) {
818
+ throw new Error('Function not implemented.');
641
819
  }
642
- const getStatusCodeForMediaRetry = (code) => MEDIA_RETRY_STATUS_MAP[code]
643
- exports.getStatusCodeForMediaRetry = getStatusCodeForMediaRetry