supunmd-bail 2.1.1 → 2.1.3

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