@mrlegendbot/baileys 1.2.3 → 1.2.5

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 (212) hide show
  1. package/README.upstream.md +1312 -0
  2. package/WAProto/WAProto.json +1 -0
  3. package/WAProto/WAProto.proto +6275 -0
  4. package/WAProto/compiler.js +550 -0
  5. package/WAProto/index.js +2 -97691
  6. package/lib/Defaults/index.js +89 -92
  7. package/lib/Signal/Group/ciphertext-message.js +9 -10
  8. package/lib/Signal/Group/group-session-builder.js +21 -22
  9. package/lib/Signal/Group/group_cipher.js +75 -69
  10. package/lib/Signal/Group/index.js +0 -1
  11. package/lib/Signal/Group/keyhelper.js +9 -10
  12. package/lib/Signal/Group/sender-chain-key.js +21 -22
  13. package/lib/Signal/Group/sender-key-distribution-message.js +51 -60
  14. package/lib/Signal/Group/sender-key-message.js +56 -62
  15. package/lib/Signal/Group/sender-key-name.js +38 -40
  16. package/lib/Signal/Group/sender-key-record.js +33 -34
  17. package/lib/Signal/Group/sender-key-state.js +72 -76
  18. package/lib/Signal/Group/sender-message-key.js +22 -23
  19. package/lib/Signal/libsignal.js +506 -405
  20. package/lib/Signal/lid-mapping.js +276 -258
  21. package/lib/Socket/Client/index.js +0 -1
  22. package/lib/Socket/Client/types.js +6 -7
  23. package/lib/Socket/Client/websocket.js +48 -49
  24. package/lib/Socket/business.js +349 -369
  25. package/lib/Socket/chats.js +1172 -1148
  26. package/lib/Socket/communities.js +473 -413
  27. package/lib/Socket/groups.js +387 -331
  28. package/lib/Socket/index.js +6 -8
  29. package/lib/Socket/messages-recv.js +1782 -1719
  30. package/lib/Socket/messages-send.js +1554 -2450
  31. package/lib/Socket/mex.js +40 -35
  32. package/lib/Socket/newsletter.js +239 -191
  33. package/lib/Socket/socket.js +992 -923
  34. package/lib/Store/index.js +8 -0
  35. package/lib/Store/keyed-db.js +108 -0
  36. package/lib/Store/make-cache-manager-store.js +70 -0
  37. package/lib/Store/make-in-memory-store.js +417 -0
  38. package/lib/Store/make-ordered-dictionary.js +69 -0
  39. package/lib/Store/object-repository.js +27 -0
  40. package/lib/Types/Auth.js +0 -1
  41. package/lib/Types/Bussines.js +0 -1
  42. package/lib/Types/Call.js +0 -1
  43. package/lib/Types/Chat.js +1 -8
  44. package/lib/Types/Contact.js +0 -1
  45. package/lib/Types/Events.js +1 -2
  46. package/lib/Types/GroupMetadata.js +0 -1
  47. package/lib/Types/Label.js +20 -22
  48. package/lib/Types/LabelAssociation.js +2 -4
  49. package/lib/Types/Message.js +3 -5
  50. package/lib/Types/Mex.js +26 -31
  51. package/lib/Types/Product.js +0 -1
  52. package/lib/Types/Signal.js +1 -2
  53. package/lib/Types/Socket.js +2 -3
  54. package/lib/Types/State.js +4 -52
  55. package/lib/Types/USync.js +0 -1
  56. package/lib/Types/index.js +10 -11
  57. package/lib/Utils/auth-utils.js +240 -277
  58. package/lib/Utils/browser-utils.js +20 -23
  59. package/lib/Utils/business.js +200 -204
  60. package/lib/Utils/chat-utils.js +836 -810
  61. package/lib/Utils/companion-reg-client-utils.js +23 -25
  62. package/lib/Utils/crypto.js +70 -86
  63. package/lib/Utils/decode-wa-message.js +277 -284
  64. package/lib/Utils/event-buffer.js +592 -595
  65. package/lib/Utils/generics.js +312 -335
  66. package/lib/Utils/history.js +123 -115
  67. package/lib/Utils/index.js +1 -2
  68. package/lib/Utils/link-preview.js +74 -73
  69. package/lib/Utils/logger.js +3 -2
  70. package/lib/Utils/lt-hash.js +0 -6
  71. package/lib/Utils/make-mutex.js +27 -27
  72. package/lib/Utils/message-retry-manager.js +213 -254
  73. package/lib/Utils/messages-media.js +741 -715
  74. package/lib/Utils/messages.js +791 -827
  75. package/lib/Utils/noise-handler.js +215 -187
  76. package/lib/Utils/pre-key-manager.js +69 -98
  77. package/lib/Utils/process-message.js +617 -586
  78. package/lib/Utils/reporting-utils.js +553 -234
  79. package/lib/Utils/signal.js +217 -170
  80. package/lib/Utils/stanza-ack.js +27 -35
  81. package/lib/Utils/sync-action-utils.js +37 -41
  82. package/lib/Utils/tc-token-utils.js +120 -141
  83. package/lib/Utils/use-multi-file-auth-state.js +87 -105
  84. package/lib/Utils/use-sqlite-auth-state.js +162 -0
  85. package/lib/Utils/validate-connection.js +190 -188
  86. package/lib/WABinary/constants.js +28 -1294
  87. package/lib/WABinary/decode.js +245 -249
  88. package/lib/WABinary/encode.js +201 -206
  89. package/lib/WABinary/generic-utils.js +86 -89
  90. package/lib/WABinary/index.js +0 -1
  91. package/lib/WABinary/jid-utils.js +68 -77
  92. package/lib/WABinary/types.js +0 -1
  93. package/lib/WAM/BinaryInfo.js +7 -8
  94. package/lib/WAM/constants.js +19892 -22853
  95. package/lib/WAM/encode.js +121 -132
  96. package/lib/WAM/index.js +0 -1
  97. package/lib/WAUSync/Protocols/USyncContactProtocol.js +46 -43
  98. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +47 -48
  99. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +22 -23
  100. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +30 -33
  101. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +19 -20
  102. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +53 -44
  103. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +24 -24
  104. package/lib/WAUSync/Protocols/index.js +0 -1
  105. package/lib/WAUSync/USyncQuery.js +79 -88
  106. package/lib/WAUSync/USyncUser.js +28 -29
  107. package/lib/WAUSync/index.js +0 -1
  108. package/lib/index.js +2 -2
  109. package/package.json +22 -86
  110. package/WAProto/GenerateStatics.sh +0 -3
  111. package/WAProto/fix-imports.js +0 -85
  112. package/WAProto/index.d.ts +0 -14017
  113. package/lib/Defaults/index.d.ts +0 -75
  114. package/lib/Signal/Group/ciphertext-message.d.ts +0 -10
  115. package/lib/Signal/Group/group-session-builder.d.ts +0 -15
  116. package/lib/Signal/Group/group_cipher.d.ts +0 -17
  117. package/lib/Signal/Group/index.d.ts +0 -12
  118. package/lib/Signal/Group/keyhelper.d.ts +0 -11
  119. package/lib/Signal/Group/sender-chain-key.d.ts +0 -14
  120. package/lib/Signal/Group/sender-key-distribution-message.d.ts +0 -17
  121. package/lib/Signal/Group/sender-key-message.d.ts +0 -19
  122. package/lib/Signal/Group/sender-key-name.d.ts +0 -18
  123. package/lib/Signal/Group/sender-key-record.d.ts +0 -31
  124. package/lib/Signal/Group/sender-key-state.d.ts +0 -39
  125. package/lib/Signal/Group/sender-message-key.d.ts +0 -12
  126. package/lib/Signal/libsignal.d.ts +0 -5
  127. package/lib/Signal/lid-mapping.d.ts +0 -23
  128. package/lib/Socket/Client/index.d.ts +0 -3
  129. package/lib/Socket/Client/types.d.ts +0 -16
  130. package/lib/Socket/Client/websocket.d.ts +0 -13
  131. package/lib/Socket/business.d.ts +0 -217
  132. package/lib/Socket/chats.d.ts +0 -125
  133. package/lib/Socket/communities.d.ts +0 -273
  134. package/lib/Socket/groups.d.ts +0 -161
  135. package/lib/Socket/index.d.ts +0 -260
  136. package/lib/Socket/messages-recv.d.ts +0 -203
  137. package/lib/Socket/messages-send.d.ts +0 -199
  138. package/lib/Socket/mex.d.ts +0 -3
  139. package/lib/Socket/newsletter.d.ts +0 -170
  140. package/lib/Socket/socket.d.ts +0 -59
  141. package/lib/Types/Auth.d.ts +0 -117
  142. package/lib/Types/Bussines.d.ts +0 -25
  143. package/lib/Types/Call.d.ts +0 -15
  144. package/lib/Types/Chat.d.ts +0 -124
  145. package/lib/Types/Contact.d.ts +0 -26
  146. package/lib/Types/Events.d.ts +0 -256
  147. package/lib/Types/GroupMetadata.d.ts +0 -71
  148. package/lib/Types/Label.d.ts +0 -47
  149. package/lib/Types/LabelAssociation.d.ts +0 -30
  150. package/lib/Types/Message.d.ts +0 -320
  151. package/lib/Types/Mex.d.ts +0 -141
  152. package/lib/Types/Product.d.ts +0 -79
  153. package/lib/Types/Signal.d.ts +0 -87
  154. package/lib/Types/Socket.d.ts +0 -136
  155. package/lib/Types/State.d.ts +0 -97
  156. package/lib/Types/USync.d.ts +0 -26
  157. package/lib/Types/index.d.ts +0 -66
  158. package/lib/Utils/auth-utils.d.ts +0 -24
  159. package/lib/Utils/browser-utils.d.ts +0 -4
  160. package/lib/Utils/business.d.ts +0 -23
  161. package/lib/Utils/chat-utils.d.ts +0 -100
  162. package/lib/Utils/companion-reg-client-utils.d.ts +0 -17
  163. package/lib/Utils/crypto.d.ts +0 -37
  164. package/lib/Utils/decode-wa-message.d.ts +0 -66
  165. package/lib/Utils/event-buffer.d.ts +0 -36
  166. package/lib/Utils/generics.d.ts +0 -92
  167. package/lib/Utils/history.d.ts +0 -24
  168. package/lib/Utils/identity-change-handler.d.ts +0 -44
  169. package/lib/Utils/identity-change-handler.js +0 -50
  170. package/lib/Utils/index.d.ts +0 -22
  171. package/lib/Utils/link-preview.d.ts +0 -21
  172. package/lib/Utils/logger.d.ts +0 -12
  173. package/lib/Utils/lt-hash.d.ts +0 -8
  174. package/lib/Utils/make-mutex.d.ts +0 -9
  175. package/lib/Utils/message-retry-manager.d.ts +0 -115
  176. package/lib/Utils/messages-media.d.ts +0 -133
  177. package/lib/Utils/messages.d.ts +0 -91
  178. package/lib/Utils/noise-handler.d.ts +0 -20
  179. package/lib/Utils/offline-node-processor.d.ts +0 -17
  180. package/lib/Utils/offline-node-processor.js +0 -40
  181. package/lib/Utils/pre-key-manager.d.ts +0 -28
  182. package/lib/Utils/process-message.d.ts +0 -60
  183. package/lib/Utils/reporting-utils.d.ts +0 -11
  184. package/lib/Utils/signal.d.ts +0 -47
  185. package/lib/Utils/stanza-ack.d.ts +0 -11
  186. package/lib/Utils/sync-action-utils.d.ts +0 -19
  187. package/lib/Utils/tc-token-utils.d.ts +0 -37
  188. package/lib/Utils/use-multi-file-auth-state.d.ts +0 -13
  189. package/lib/Utils/validate-connection.d.ts +0 -11
  190. package/lib/WABinary/constants.d.ts +0 -28
  191. package/lib/WABinary/decode.d.ts +0 -7
  192. package/lib/WABinary/encode.d.ts +0 -3
  193. package/lib/WABinary/generic-utils.d.ts +0 -15
  194. package/lib/WABinary/index.d.ts +0 -6
  195. package/lib/WABinary/jid-utils.d.ts +0 -48
  196. package/lib/WABinary/types.d.ts +0 -19
  197. package/lib/WAM/BinaryInfo.d.ts +0 -9
  198. package/lib/WAM/constants.d.ts +0 -40
  199. package/lib/WAM/encode.d.ts +0 -3
  200. package/lib/WAM/index.d.ts +0 -4
  201. package/lib/WAUSync/Protocols/USyncContactProtocol.d.ts +0 -10
  202. package/lib/WAUSync/Protocols/USyncDeviceProtocol.d.ts +0 -23
  203. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.d.ts +0 -13
  204. package/lib/WAUSync/Protocols/USyncStatusProtocol.d.ts +0 -13
  205. package/lib/WAUSync/Protocols/USyncUsernameProtocol.d.ts +0 -10
  206. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.d.ts +0 -26
  207. package/lib/WAUSync/Protocols/UsyncLIDProtocol.d.ts +0 -10
  208. package/lib/WAUSync/Protocols/index.d.ts +0 -6
  209. package/lib/WAUSync/USyncQuery.d.ts +0 -30
  210. package/lib/WAUSync/USyncUser.d.ts +0 -17
  211. package/lib/WAUSync/index.d.ts +0 -4
  212. package/lib/index.d.ts +0 -12
@@ -1,8 +1,8 @@
1
1
  import { Boom } from '@hapi/boom';
2
2
  import { randomBytes } from 'crypto';
3
3
  import { promises as fs } from 'fs';
4
- import {} from 'stream';
5
- import { proto } from '../../WAProto/index.js';
4
+ import 'stream';
5
+ import { proto } from '../../WAProto/compiler.js';
6
6
  import { CALL_AUDIO_PREFIX, CALL_VIDEO_PREFIX, MEDIA_KEYS, URL_REGEX, WA_DEFAULT_EPHEMERAL } from '../Defaults/index.js';
7
7
  import { WAMessageStatus, WAProto } from '../Types/index.js';
8
8
  import { isJidGroup, isJidNewsletter, isJidStatusBroadcast, jidNormalizedUser } from '../WABinary/index.js';
@@ -11,883 +11,847 @@ import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './gener
11
11
  import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getRawMediaUploadData } from './messages-media.js';
12
12
  import { shouldIncludeReportingToken } from './reporting-utils.js';
13
13
  const MIMETYPE_MAP = {
14
- image: 'image/jpeg',
15
- video: 'video/mp4',
16
- document: 'application/pdf',
17
- audio: 'audio/ogg; codecs=opus',
18
- sticker: 'image/webp',
19
- 'product-catalog-image': 'image/jpeg'
14
+ image: 'image/jpeg',
15
+ video: 'video/mp4',
16
+ document: 'application/pdf',
17
+ audio: 'audio/ogg; codecs=opus',
18
+ sticker: 'image/webp',
19
+ 'product-catalog-image': 'image/jpeg'
20
20
  };
21
21
  const MessageTypeProto = {
22
- image: WAProto.Message.ImageMessage,
23
- video: WAProto.Message.VideoMessage,
24
- audio: WAProto.Message.AudioMessage,
25
- sticker: WAProto.Message.StickerMessage,
26
- document: WAProto.Message.DocumentMessage
22
+ image: WAProto.Message.ImageMessage,
23
+ video: WAProto.Message.VideoMessage,
24
+ audio: WAProto.Message.AudioMessage,
25
+ sticker: WAProto.Message.StickerMessage,
26
+ document: WAProto.Message.DocumentMessage
27
27
  };
28
- /**
29
- * Uses a regex to test whether the string contains a URL, and returns the URL if it does.
30
- * @param text eg. hello https://google.com
31
- * @returns the URL, eg. https://google.com
32
- */
33
- export const extractUrlFromText = (text) => text.match(URL_REGEX)?.[0];
28
+ export const extractUrlFromText = text => text.match(URL_REGEX)?.[0];
34
29
  export const generateLinkPreviewIfRequired = async (text, getUrlInfo, logger) => {
35
- const url = extractUrlFromText(text);
36
- if (!!getUrlInfo && url) {
37
- try {
38
- const urlInfo = await getUrlInfo(url);
39
- return urlInfo;
40
- }
41
- catch (error) {
42
- // ignore if fails
43
- logger?.warn({ trace: error.stack }, 'url generation failed');
44
- }
45
- }
30
+ const url = extractUrlFromText(text);
31
+ if (!!getUrlInfo && url) {
32
+ try {
33
+ const urlInfo = await getUrlInfo(url);
34
+ return urlInfo;
35
+ } catch (error) {
36
+ logger?.warn({
37
+ trace: error.stack
38
+ }, 'url generation failed');
39
+ }
40
+ }
46
41
  };
47
- const assertColor = async (color) => {
48
- let assertedColor;
49
- if (typeof color === 'number') {
50
- assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1;
51
- }
52
- else {
53
- let hex = color.trim().replace('#', '');
54
- if (hex.length <= 6) {
55
- hex = 'FF' + hex.padStart(6, '0');
56
- }
57
- assertedColor = parseInt(hex, 16);
58
- return assertedColor;
59
- }
42
+ const assertColor = async color => {
43
+ let assertedColor;
44
+ if (typeof color === 'number') {
45
+ assertedColor = color > 0 ? color : 0xffffffff + Number(color) + 1;
46
+ } else {
47
+ let hex = color.trim().replace('#', '');
48
+ if (hex.length <= 6) {
49
+ hex = 'FF' + hex.padStart(6, '0');
50
+ }
51
+ assertedColor = parseInt(hex, 16);
52
+ return assertedColor;
53
+ }
60
54
  };
61
55
  export const prepareWAMessageMedia = async (message, options) => {
62
- const logger = options.logger;
63
- let mediaType;
64
- for (const key of MEDIA_KEYS) {
65
- if (key in message) {
66
- mediaType = key;
67
- }
68
- }
69
- if (!mediaType) {
70
- throw new Boom('Invalid media type', { statusCode: 400 });
71
- }
72
- const uploadData = {
73
- ...message,
74
- media: message[mediaType]
75
- };
76
- delete uploadData[mediaType];
77
- // check if cacheable + generate cache key
78
- const cacheableKey = typeof uploadData.media === 'object' &&
79
- 'url' in uploadData.media &&
80
- !!uploadData.media.url &&
81
- !!options.mediaCache &&
82
- mediaType + ':' + uploadData.media.url.toString();
83
- if (mediaType === 'document' && !uploadData.fileName) {
84
- uploadData.fileName = 'file';
85
- }
86
- if (!uploadData.mimetype) {
87
- uploadData.mimetype = MIMETYPE_MAP[mediaType];
88
- }
89
- if (cacheableKey) {
90
- const mediaBuff = await options.mediaCache.get(cacheableKey);
91
- if (mediaBuff) {
92
- logger?.debug({ cacheableKey }, 'got media cache hit');
93
- const obj = proto.Message.decode(mediaBuff);
94
- const key = `${mediaType}Message`;
95
- Object.assign(obj[key], { ...uploadData, media: undefined });
96
- return obj;
97
- }
98
- }
99
- const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
100
- if (isNewsletter) {
101
- logger?.info({ key: cacheableKey }, 'Preparing raw media for newsletter');
102
- const { filePath, fileSha256, fileLength } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
103
- const fileSha256B64 = fileSha256.toString('base64');
104
- const { mediaUrl, directPath } = await options.upload(filePath, {
105
- fileEncSha256B64: fileSha256B64,
106
- mediaType: mediaType,
107
- timeoutMs: options.mediaUploadTimeoutMs
108
- });
109
- await fs.unlink(filePath);
110
- const obj = WAProto.Message.fromObject({
111
- // todo: add more support here
112
- [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
113
- url: mediaUrl,
114
- directPath,
115
- fileSha256,
116
- fileLength,
117
- ...uploadData,
118
- media: undefined
119
- })
120
- });
121
- if (uploadData.ptv) {
122
- obj.ptvMessage = obj.videoMessage;
123
- delete obj.videoMessage;
124
- }
125
- if (obj.stickerMessage) {
126
- obj.stickerMessage.stickerSentTs = Date.now();
127
- }
128
- if (cacheableKey) {
129
- logger?.debug({ cacheableKey }, 'set cache');
130
- await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
131
- }
132
- return obj;
133
- }
134
- const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
135
- const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
136
- const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
137
- const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
138
- const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
139
- const { mediaKey, encFilePath, originalFilePath, fileEncSha256, fileSha256, fileLength } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
140
- logger,
141
- saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
142
- opts: options.options
56
+ const logger = options.logger;
57
+ let mediaType;
58
+ for (const key of MEDIA_KEYS) {
59
+ if (key in message) {
60
+ mediaType = key;
61
+ }
62
+ }
63
+ if (!mediaType) {
64
+ throw new Boom('Invalid media type', {
65
+ statusCode: 400
143
66
  });
144
- const fileEncSha256B64 = fileEncSha256.toString('base64');
145
- const [{ mediaUrl, directPath }] = await Promise.all([
146
- (async () => {
147
- const result = await options.upload(encFilePath, {
148
- fileEncSha256B64,
149
- mediaType,
150
- timeoutMs: options.mediaUploadTimeoutMs
151
- });
152
- logger?.debug({ mediaType, cacheableKey }, 'uploaded media');
153
- return result;
154
- })(),
155
- (async () => {
156
- try {
157
- if (requiresThumbnailComputation) {
158
- const { thumbnail, originalImageDimensions } = await generateThumbnail(originalFilePath, mediaType, options);
159
- uploadData.jpegThumbnail = thumbnail;
160
- if (!uploadData.width && originalImageDimensions) {
161
- uploadData.width = originalImageDimensions.width;
162
- uploadData.height = originalImageDimensions.height;
163
- logger?.debug('set dimensions');
164
- }
165
- logger?.debug('generated thumbnail');
166
- }
167
- if (requiresDurationComputation) {
168
- uploadData.seconds = await getAudioDuration(originalFilePath);
169
- logger?.debug('computed audio duration');
170
- }
171
- if (requiresWaveformProcessing) {
172
- uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
173
- logger?.debug('processed waveform');
174
- }
175
- if (requiresAudioBackground) {
176
- uploadData.backgroundArgb = await assertColor(options.backgroundColor);
177
- logger?.debug('computed backgroundColor audio status');
178
- }
179
- }
180
- catch (error) {
181
- logger?.warn({ trace: error.stack }, 'failed to obtain extra info');
182
- }
183
- })()
184
- ]).finally(async () => {
185
- try {
186
- await fs.unlink(encFilePath);
187
- if (originalFilePath) {
188
- await fs.unlink(originalFilePath);
189
- }
190
- logger?.debug('removed tmp files');
191
- }
192
- catch (error) {
193
- logger?.warn('failed to remove tmp file');
194
- }
67
+ }
68
+ const uploadData = {
69
+ ...message,
70
+ media: message[mediaType]
71
+ };
72
+ delete uploadData[mediaType];
73
+ const cacheableKey = typeof uploadData.media === 'object' && 'url' in uploadData.media && !!uploadData.media.url && !!options.mediaCache && mediaType + ':' + uploadData.media.url.toString();
74
+ if (mediaType === 'document' && !uploadData.fileName) {
75
+ uploadData.fileName = 'file';
76
+ }
77
+ if (!uploadData.mimetype) {
78
+ uploadData.mimetype = MIMETYPE_MAP[mediaType];
79
+ }
80
+ if (cacheableKey) {
81
+ const mediaBuff = await options.mediaCache.get(cacheableKey);
82
+ if (mediaBuff) {
83
+ logger?.debug({
84
+ cacheableKey
85
+ }, 'got media cache hit');
86
+ const obj = proto.Message.decode(mediaBuff);
87
+ const key = `${mediaType}Message`;
88
+ Object.assign(obj[key], {
89
+ ...uploadData,
90
+ media: undefined
91
+ });
92
+ return obj;
93
+ }
94
+ }
95
+ const isNewsletter = !!options.jid && isJidNewsletter(options.jid);
96
+ if (isNewsletter) {
97
+ logger?.info({
98
+ key: cacheableKey
99
+ }, 'Preparing raw media for newsletter');
100
+ const {
101
+ filePath,
102
+ fileSha256,
103
+ fileLength
104
+ } = await getRawMediaUploadData(uploadData.media, options.mediaTypeOverride || mediaType, logger);
105
+ const fileSha256B64 = fileSha256.toString('base64');
106
+ const {
107
+ mediaUrl,
108
+ directPath
109
+ } = await options.upload(filePath, {
110
+ fileEncSha256B64: fileSha256B64,
111
+ mediaType: mediaType,
112
+ timeoutMs: options.mediaUploadTimeoutMs
195
113
  });
114
+ await fs.unlink(filePath);
196
115
  const obj = WAProto.Message.fromObject({
197
- [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
198
- url: mediaUrl,
199
- directPath,
200
- mediaKey,
201
- fileEncSha256,
202
- fileSha256,
203
- fileLength,
204
- mediaKeyTimestamp: unixTimestampSeconds(),
205
- ...uploadData,
206
- media: undefined
207
- })
116
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
117
+ url: mediaUrl,
118
+ directPath,
119
+ fileSha256,
120
+ fileLength,
121
+ ...uploadData,
122
+ media: undefined
123
+ })
208
124
  });
209
125
  if (uploadData.ptv) {
210
- obj.ptvMessage = obj.videoMessage;
211
- delete obj.videoMessage;
126
+ obj.ptvMessage = obj.videoMessage;
127
+ delete obj.videoMessage;
128
+ }
129
+ if (obj.stickerMessage) {
130
+ obj.stickerMessage.stickerSentTs = Date.now();
212
131
  }
213
132
  if (cacheableKey) {
214
- logger?.debug({ cacheableKey }, 'set cache');
215
- await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
133
+ logger?.debug({
134
+ cacheableKey
135
+ }, 'set cache');
136
+ await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
216
137
  }
217
138
  return obj;
139
+ }
140
+ const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined';
141
+ const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined';
142
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined';
143
+ const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true;
144
+ const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation;
145
+ const {
146
+ mediaKey,
147
+ encFilePath,
148
+ originalFilePath,
149
+ fileEncSha256,
150
+ fileSha256,
151
+ fileLength
152
+ } = await encryptedStream(uploadData.media, options.mediaTypeOverride || mediaType, {
153
+ logger,
154
+ saveOriginalFileIfRequired: requiresOriginalForSomeProcessing,
155
+ opts: options.options
156
+ });
157
+ const fileEncSha256B64 = fileEncSha256.toString('base64');
158
+ const [{
159
+ mediaUrl,
160
+ directPath
161
+ }] = await Promise.all([(async () => {
162
+ const result = await options.upload(encFilePath, {
163
+ fileEncSha256B64,
164
+ mediaType,
165
+ timeoutMs: options.mediaUploadTimeoutMs
166
+ });
167
+ logger?.debug({
168
+ mediaType,
169
+ cacheableKey
170
+ }, 'uploaded media');
171
+ return result;
172
+ })(), (async () => {
173
+ try {
174
+ if (requiresThumbnailComputation) {
175
+ const {
176
+ thumbnail,
177
+ originalImageDimensions
178
+ } = await generateThumbnail(originalFilePath, mediaType, options);
179
+ uploadData.jpegThumbnail = thumbnail;
180
+ if (!uploadData.width && originalImageDimensions) {
181
+ uploadData.width = originalImageDimensions.width;
182
+ uploadData.height = originalImageDimensions.height;
183
+ logger?.debug('set dimensions');
184
+ }
185
+ logger?.debug('generated thumbnail');
186
+ }
187
+ if (requiresDurationComputation) {
188
+ uploadData.seconds = await getAudioDuration(originalFilePath);
189
+ logger?.debug('computed audio duration');
190
+ }
191
+ if (requiresWaveformProcessing) {
192
+ uploadData.waveform = await getAudioWaveform(originalFilePath, logger);
193
+ logger?.debug('processed waveform');
194
+ }
195
+ if (requiresAudioBackground) {
196
+ uploadData.backgroundArgb = await assertColor(options.backgroundColor);
197
+ logger?.debug('computed backgroundColor audio status');
198
+ }
199
+ } catch (error) {
200
+ logger?.warn({
201
+ trace: error.stack
202
+ }, 'failed to obtain extra info');
203
+ }
204
+ })()]).finally(async () => {
205
+ try {
206
+ await fs.unlink(encFilePath);
207
+ if (originalFilePath) {
208
+ await fs.unlink(originalFilePath);
209
+ }
210
+ logger?.debug('removed tmp files');
211
+ } catch (error) {
212
+ logger?.warn('failed to remove tmp file');
213
+ }
214
+ });
215
+ const obj = WAProto.Message.fromObject({
216
+ [`${mediaType}Message`]: MessageTypeProto[mediaType].fromObject({
217
+ url: mediaUrl,
218
+ directPath,
219
+ mediaKey,
220
+ fileEncSha256,
221
+ fileSha256,
222
+ fileLength,
223
+ mediaKeyTimestamp: unixTimestampSeconds(),
224
+ ...uploadData,
225
+ media: undefined
226
+ })
227
+ });
228
+ if (uploadData.ptv) {
229
+ obj.ptvMessage = obj.videoMessage;
230
+ delete obj.videoMessage;
231
+ }
232
+ if (cacheableKey) {
233
+ logger?.debug({
234
+ cacheableKey
235
+ }, 'set cache');
236
+ await options.mediaCache.set(cacheableKey, WAProto.Message.encode(obj).finish());
237
+ }
238
+ return obj;
218
239
  };
219
- export const prepareDisappearingMessageSettingContent = (ephemeralExpiration) => {
220
- ephemeralExpiration = ephemeralExpiration || 0;
221
- const content = {
222
- ephemeralMessage: {
223
- message: {
224
- protocolMessage: {
225
- type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
226
- ephemeralExpiration
227
- }
228
- }
229
- }
230
- };
231
- return WAProto.Message.fromObject(content);
240
+ export const prepareDisappearingMessageSettingContent = ephemeralExpiration => {
241
+ ephemeralExpiration = ephemeralExpiration || 0;
242
+ const content = {
243
+ ephemeralMessage: {
244
+ message: {
245
+ protocolMessage: {
246
+ type: WAProto.Message.ProtocolMessage.Type.EPHEMERAL_SETTING,
247
+ ephemeralExpiration
248
+ }
249
+ }
250
+ }
251
+ };
252
+ return WAProto.Message.fromObject(content);
232
253
  };
233
- /**
234
- * Generate forwarded message content like WA does
235
- * @param message the message to forward
236
- * @param options.forceForward will show the message as forwarded even if it is from you
237
- */
238
254
  export const generateForwardMessageContent = (message, forceForward) => {
239
- let content = message.message;
240
- if (!content) {
241
- throw new Boom('no content in message', { statusCode: 400 });
242
- }
243
- // hacky copy
244
- content = normalizeMessageContent(content);
245
- content = proto.Message.decode(proto.Message.encode(content).finish());
246
- let key = Object.keys(content)[0];
247
- let score = content?.[key]?.contextInfo?.forwardingScore || 0;
248
- score += message.key.fromMe && !forceForward ? 0 : 1;
249
- if (key === 'conversation') {
250
- content.extendedTextMessage = { text: content[key] };
251
- delete content.conversation;
252
- key = 'extendedTextMessage';
253
- }
254
- const key_ = content?.[key];
255
- if (score > 0) {
256
- key_.contextInfo = { forwardingScore: score, isForwarded: true };
257
- }
258
- else {
259
- key_.contextInfo = {};
260
- }
261
- return content;
255
+ let content = message.message;
256
+ if (!content) {
257
+ throw new Boom('no content in message', {
258
+ statusCode: 400
259
+ });
260
+ }
261
+ content = normalizeMessageContent(content);
262
+ content = proto.Message.decode(proto.Message.encode(content).finish());
263
+ let key = Object.keys(content)[0];
264
+ let score = content?.[key]?.contextInfo?.forwardingScore || 0;
265
+ score += message.key.fromMe && !forceForward ? 0 : 1;
266
+ if (key === 'conversation') {
267
+ content.extendedTextMessage = {
268
+ text: content[key]
269
+ };
270
+ delete content.conversation;
271
+ key = 'extendedTextMessage';
272
+ }
273
+ const key_ = content?.[key];
274
+ if (score > 0) {
275
+ key_.contextInfo = {
276
+ forwardingScore: score,
277
+ isForwarded: true
278
+ };
279
+ } else {
280
+ key_.contextInfo = {};
281
+ }
282
+ return content;
262
283
  };
263
284
  export const hasNonNullishProperty = (message, key) => {
264
- return (typeof message === 'object' &&
265
- message !== null &&
266
- key in message &&
267
- message[key] !== null &&
268
- message[key] !== undefined);
285
+ return typeof message === 'object' && message !== null && key in message && message[key] !== null && message[key] !== undefined;
269
286
  };
270
287
  function hasOptionalProperty(obj, key) {
271
- return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
288
+ return typeof obj === 'object' && obj !== null && key in obj && obj[key] !== null;
272
289
  }
273
290
  export const generateWAMessageContent = async (message, options) => {
274
- var _a, _b;
275
- let m = {};
276
- if (hasNonNullishProperty(message, 'text')) {
277
- const extContent = { text: message.text };
278
- let urlInfo = message.linkPreview;
279
- if (typeof urlInfo === 'undefined') {
280
- urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
281
- }
282
- if (urlInfo) {
283
- extContent.matchedText = urlInfo['matched-text'];
284
- extContent.jpegThumbnail = urlInfo.jpegThumbnail;
285
- extContent.description = urlInfo.description;
286
- extContent.title = urlInfo.title;
287
- extContent.previewType = 0;
288
- const img = urlInfo.highQualityThumbnail;
289
- if (img) {
290
- extContent.thumbnailDirectPath = img.directPath;
291
- extContent.mediaKey = img.mediaKey;
292
- extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
293
- extContent.thumbnailWidth = img.width;
294
- extContent.thumbnailHeight = img.height;
295
- extContent.thumbnailSha256 = img.fileSha256;
296
- extContent.thumbnailEncSha256 = img.fileEncSha256;
297
- }
298
- }
299
- if (options.backgroundColor) {
300
- extContent.backgroundArgb = await assertColor(options.backgroundColor);
301
- }
302
- if (options.font) {
303
- extContent.font = options.font;
304
- }
305
- m.extendedTextMessage = extContent;
306
- }
307
- else if (hasNonNullishProperty(message, 'contacts')) {
308
- const contactLen = message.contacts.contacts.length;
309
- if (!contactLen) {
310
- throw new Boom('require atleast 1 contact', { statusCode: 400 });
311
- }
312
- if (contactLen === 1) {
313
- m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
314
- }
315
- else {
316
- m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
317
- }
318
- }
319
- else if (hasNonNullishProperty(message, 'location')) {
320
- m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
321
- }
322
- else if (hasNonNullishProperty(message, 'react')) {
323
- if (!message.react.senderTimestampMs) {
324
- message.react.senderTimestampMs = Date.now();
325
- }
326
- m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
327
- }
328
- else if (hasNonNullishProperty(message, 'delete')) {
329
- m.protocolMessage = {
330
- key: message.delete,
331
- type: WAProto.Message.ProtocolMessage.Type.REVOKE
332
- };
333
- }
334
- else if (hasNonNullishProperty(message, 'forward')) {
335
- m = generateForwardMessageContent(message.forward, message.force);
336
- }
337
- else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
338
- const exp = typeof message.disappearingMessagesInChat === 'boolean'
339
- ? message.disappearingMessagesInChat
340
- ? WA_DEFAULT_EPHEMERAL
341
- : 0
342
- : message.disappearingMessagesInChat;
343
- m = prepareDisappearingMessageSettingContent(exp);
344
- }
345
- else if (hasNonNullishProperty(message, 'groupInvite')) {
346
- m.groupInviteMessage = {};
347
- m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
348
- m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
349
- m.groupInviteMessage.caption = message.groupInvite.text;
350
- m.groupInviteMessage.groupJid = message.groupInvite.jid;
351
- m.groupInviteMessage.groupName = message.groupInvite.subject;
352
- //TODO: use built-in interface and get disappearing mode info etc.
353
- //TODO: cache / use store!?
354
- if (options.getProfilePicUrl) {
355
- const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
356
- if (pfpUrl) {
357
- const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
358
- if (resp.ok) {
359
- const buf = Buffer.from(await resp.arrayBuffer());
360
- m.groupInviteMessage.jpegThumbnail = buf;
361
- }
362
- }
363
- }
364
- }
365
- else if (hasNonNullishProperty(message, 'pin')) {
366
- m.pinInChatMessage = {};
367
- m.messageContextInfo = {};
368
- m.pinInChatMessage.key = message.pin;
369
- m.pinInChatMessage.type = message.type;
370
- m.pinInChatMessage.senderTimestampMs = Date.now();
371
- m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
372
- }
373
- else if (hasNonNullishProperty(message, 'buttonReply')) {
374
- switch (message.type) {
375
- case 'template':
376
- m.templateButtonReplyMessage = {
377
- selectedDisplayText: message.buttonReply.displayText,
378
- selectedId: message.buttonReply.id,
379
- selectedIndex: message.buttonReply.index
380
- };
381
- break;
382
- case 'plain':
383
- m.buttonsResponseMessage = {
384
- selectedButtonId: message.buttonReply.id,
385
- selectedDisplayText: message.buttonReply.displayText,
386
- type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
387
- };
388
- break;
389
- }
390
- }
391
- else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
392
- const { videoMessage } = await prepareWAMessageMedia({ video: message.video }, options);
393
- m.ptvMessage = videoMessage;
394
- }
395
- else if (hasNonNullishProperty(message, 'product')) {
396
- const { imageMessage } = await prepareWAMessageMedia({ image: message.product.productImage }, options);
397
- m.productMessage = WAProto.Message.ProductMessage.create({
398
- ...message,
399
- product: {
400
- ...message.product,
401
- productImage: imageMessage
402
- }
291
+ var _a, _b;
292
+ let m = {};
293
+ if (hasNonNullishProperty(message, 'text')) {
294
+ const extContent = {
295
+ text: message.text
296
+ };
297
+ let urlInfo = message.linkPreview;
298
+ if (typeof urlInfo === 'undefined') {
299
+ urlInfo = await generateLinkPreviewIfRequired(message.text, options.getUrlInfo, options.logger);
300
+ }
301
+ if (urlInfo) {
302
+ extContent.matchedText = urlInfo['matched-text'];
303
+ extContent.jpegThumbnail = urlInfo.jpegThumbnail;
304
+ extContent.description = urlInfo.description;
305
+ extContent.title = urlInfo.title;
306
+ extContent.previewType = 0;
307
+ const img = urlInfo.highQualityThumbnail;
308
+ if (img) {
309
+ extContent.thumbnailDirectPath = img.directPath;
310
+ extContent.mediaKey = img.mediaKey;
311
+ extContent.mediaKeyTimestamp = img.mediaKeyTimestamp;
312
+ extContent.thumbnailWidth = img.width;
313
+ extContent.thumbnailHeight = img.height;
314
+ extContent.thumbnailSha256 = img.fileSha256;
315
+ extContent.thumbnailEncSha256 = img.fileEncSha256;
316
+ }
317
+ }
318
+ if (options.backgroundColor) {
319
+ extContent.backgroundArgb = await assertColor(options.backgroundColor);
320
+ }
321
+ if (options.font) {
322
+ extContent.font = options.font;
323
+ }
324
+ m.extendedTextMessage = extContent;
325
+ } else if (hasNonNullishProperty(message, 'contacts')) {
326
+ const contactLen = message.contacts.contacts.length;
327
+ if (!contactLen) {
328
+ throw new Boom('require atleast 1 contact', {
329
+ statusCode: 400
330
+ });
331
+ }
332
+ if (contactLen === 1) {
333
+ m.contactMessage = WAProto.Message.ContactMessage.create(message.contacts.contacts[0]);
334
+ } else {
335
+ m.contactsArrayMessage = WAProto.Message.ContactsArrayMessage.create(message.contacts);
336
+ }
337
+ } else if (hasNonNullishProperty(message, 'location')) {
338
+ m.locationMessage = WAProto.Message.LocationMessage.create(message.location);
339
+ } else if (hasNonNullishProperty(message, 'react')) {
340
+ if (!message.react.senderTimestampMs) {
341
+ message.react.senderTimestampMs = Date.now();
342
+ }
343
+ m.reactionMessage = WAProto.Message.ReactionMessage.create(message.react);
344
+ } else if (hasNonNullishProperty(message, 'delete')) {
345
+ m.protocolMessage = {
346
+ key: message.delete,
347
+ type: WAProto.Message.ProtocolMessage.Type.REVOKE
348
+ };
349
+ } else if (hasNonNullishProperty(message, 'forward')) {
350
+ m = generateForwardMessageContent(message.forward, message.force);
351
+ } else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
352
+ const exp = typeof message.disappearingMessagesInChat === 'boolean' ? message.disappearingMessagesInChat ? WA_DEFAULT_EPHEMERAL : 0 : message.disappearingMessagesInChat;
353
+ m = prepareDisappearingMessageSettingContent(exp);
354
+ } else if (hasNonNullishProperty(message, 'botInvoke')) {
355
+ m.botInvokeMessage = {
356
+ message: message.botInvoke
357
+ };
358
+ } else if (hasNonNullishProperty(message, 'groupInvite')) {
359
+ m.groupInviteMessage = {};
360
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
361
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
362
+ m.groupInviteMessage.caption = message.groupInvite.text;
363
+ m.groupInviteMessage.groupJid = message.groupInvite.jid;
364
+ m.groupInviteMessage.groupName = message.groupInvite.subject;
365
+ if (options.getProfilePicUrl) {
366
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
367
+ if (pfpUrl) {
368
+ const resp = await fetch(pfpUrl, {
369
+ method: 'GET',
370
+ dispatcher: options?.options?.dispatcher
403
371
  });
404
- }
405
- else if (hasNonNullishProperty(message, 'listReply')) {
406
- m.listResponseMessage = { ...message.listReply };
407
- }
408
- else if (hasNonNullishProperty(message, 'event')) {
409
- m.eventMessage = {};
410
- const startTime = Math.floor(message.event.startDate.getTime() / 1000);
411
- if (message.event.call && options.getCallLink) {
412
- const token = await options.getCallLink(message.event.call, { startTime });
413
- m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
414
- }
415
- m.messageContextInfo = {
416
- // encKey
417
- messageSecret: message.event.messageSecret || randomBytes(32)
418
- };
419
- m.eventMessage.name = message.event.name;
420
- m.eventMessage.description = message.event.description;
421
- m.eventMessage.startTime = startTime;
422
- m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
423
- m.eventMessage.isCanceled = message.event.isCancelled ?? false;
424
- m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
425
- m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
426
- m.eventMessage.location = message.event.location;
427
- }
428
- else if (hasNonNullishProperty(message, 'poll')) {
429
- (_a = message.poll).selectableCount || (_a.selectableCount = 0);
430
- (_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
431
- if (!Array.isArray(message.poll.values)) {
432
- throw new Boom('Invalid poll values', { statusCode: 400 });
433
- }
434
- if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
435
- throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
436
- statusCode: 400
437
- });
438
- }
439
- m.messageContextInfo = {
440
- // encKey
441
- messageSecret: message.poll.messageSecret || randomBytes(32)
442
- };
443
- const pollCreationMessage = {
444
- name: message.poll.name,
445
- selectableOptionsCount: message.poll.selectableCount,
446
- options: message.poll.values.map(optionName => ({ optionName }))
447
- };
448
- if (message.poll.toAnnouncementGroup) {
449
- // poll v2 is for community announcement groups (single select and multiple)
450
- m.pollCreationMessageV2 = pollCreationMessage;
451
- }
452
- else {
453
- if (message.poll.selectableCount === 1) {
454
- //poll v3 is for single select polls
455
- m.pollCreationMessageV3 = pollCreationMessage;
456
- }
457
- else {
458
- // poll for multiple choice polls
459
- m.pollCreationMessage = pollCreationMessage;
460
- }
461
- }
462
- }
463
- else if (hasNonNullishProperty(message, 'album')) {
464
- m.albumMessage = {
465
- expectedImageCount: message.album.expectedImageCount,
466
- expectedVideoCount: message.album.expectedVideoCount
467
- };
468
- }
469
- else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
470
- m.protocolMessage = {
471
- type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
472
- };
473
- }
474
- else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
475
- m.requestPhoneNumberMessage = {};
476
- }
477
- else if (hasNonNullishProperty(message, 'limitSharing')) {
478
- m.protocolMessage = {
479
- type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
480
- limitSharing: {
481
- sharingLimited: message.limitSharing === true,
482
- trigger: 1,
483
- limitSharingSettingTimestamp: Date.now(),
484
- initiatedByMe: true
485
- }
486
- };
487
- }
488
- else {
489
- m = await prepareWAMessageMedia(message, options);
490
- }
491
- if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
492
- m = { viewOnceMessage: { message: m } };
493
- }
494
- if ((hasOptionalProperty(message, 'mentions') && message.mentions?.length) ||
495
- (hasOptionalProperty(message, 'mentionAll') && message.mentionAll)) {
496
- const messageType = Object.keys(m)[0];
497
- const key = m[messageType];
498
- if (key && 'contextInfo' in key) {
499
- key.contextInfo = key.contextInfo || {};
500
- if (message.mentions?.length) {
501
- key.contextInfo.mentionedJid = message.mentions;
502
- }
503
- if (message.mentionAll) {
504
- key.contextInfo.nonJidMentions = 1;
505
- }
506
- }
507
- else if (key) {
508
- key.contextInfo = {
509
- mentionedJid: message.mentions,
510
- nonJidMentions: message.mentionAll ? 1 : 0
511
- };
512
- }
513
- }
514
- if (hasOptionalProperty(message, 'edit')) {
515
- m = {
516
- protocolMessage: {
517
- key: message.edit,
518
- editedMessage: m,
519
- timestampMs: Date.now(),
520
- type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
521
- }
372
+ if (resp.ok) {
373
+ const buf = Buffer.from(await resp.arrayBuffer());
374
+ m.groupInviteMessage.jpegThumbnail = buf;
375
+ }
376
+ }
377
+ }
378
+ } else if (hasNonNullishProperty(message, 'pin')) {
379
+ m.pinInChatMessage = {};
380
+ m.messageContextInfo = {};
381
+ m.pinInChatMessage.key = message.pin;
382
+ m.pinInChatMessage.type = message.type;
383
+ m.pinInChatMessage.senderTimestampMs = Date.now();
384
+ m.messageContextInfo.messageAddOnDurationInSecs = message.type === 1 ? message.time || 86400 : 0;
385
+ } else if (hasNonNullishProperty(message, 'buttonReply')) {
386
+ switch (message.type) {
387
+ case 'template':
388
+ m.templateButtonReplyMessage = {
389
+ selectedDisplayText: message.buttonReply.displayText,
390
+ selectedId: message.buttonReply.id,
391
+ selectedIndex: message.buttonReply.index
522
392
  };
523
- }
524
- if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
525
- const messageType = Object.keys(m)[0];
526
- const key = m[messageType];
527
- if ('contextInfo' in key && !!key.contextInfo) {
528
- key.contextInfo = { ...key.contextInfo, ...message.contextInfo };
529
- }
530
- else if (key) {
531
- key.contextInfo = message.contextInfo;
532
- }
533
- }
534
- if (hasOptionalProperty(message, 'albumParentKey') && !!message.albumParentKey) {
535
- m.messageContextInfo = {
536
- ...m.messageContextInfo,
537
- messageAssociation: {
538
- associationType: WAProto.MessageAssociation.AssociationType.MEDIA_ALBUM,
539
- parentMessageKey: message.albumParentKey
540
- }
393
+ break;
394
+ case 'plain':
395
+ m.buttonsResponseMessage = {
396
+ selectedButtonId: message.buttonReply.id,
397
+ selectedDisplayText: message.buttonReply.displayText,
398
+ type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
541
399
  };
542
- }
543
- if (shouldIncludeReportingToken(m)) {
544
- m.messageContextInfo = m.messageContextInfo || {};
545
- if (!m.messageContextInfo.messageSecret) {
546
- m.messageContextInfo.messageSecret = randomBytes(32);
547
- }
548
- }
549
- return WAProto.Message.create(m);
400
+ break;
401
+ }
402
+ } else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
403
+ const {
404
+ videoMessage
405
+ } = await prepareWAMessageMedia({
406
+ video: message.video
407
+ }, options);
408
+ m.ptvMessage = videoMessage;
409
+ } else if (hasNonNullishProperty(message, 'product')) {
410
+ const {
411
+ imageMessage
412
+ } = await prepareWAMessageMedia({
413
+ image: message.product.productImage
414
+ }, options);
415
+ m.productMessage = WAProto.Message.ProductMessage.create({
416
+ ...message,
417
+ product: {
418
+ ...message.product,
419
+ productImage: imageMessage
420
+ }
421
+ });
422
+ } else if (hasNonNullishProperty(message, 'listReply')) {
423
+ m.listResponseMessage = {
424
+ ...message.listReply
425
+ };
426
+ } else if (hasNonNullishProperty(message, 'event')) {
427
+ m.eventMessage = {};
428
+ const startTime = Math.floor(message.event.startDate.getTime() / 1000);
429
+ if (message.event.call && options.getCallLink) {
430
+ const token = await options.getCallLink(message.event.call, {
431
+ startTime
432
+ });
433
+ m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
434
+ }
435
+ m.messageContextInfo = {
436
+ messageSecret: message.event.messageSecret || randomBytes(32)
437
+ };
438
+ m.eventMessage.name = message.event.name;
439
+ m.eventMessage.description = message.event.description;
440
+ m.eventMessage.startTime = startTime;
441
+ m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
442
+ m.eventMessage.isCanceled = message.event.isCancelled ?? false;
443
+ m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
444
+ m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
445
+ m.eventMessage.location = message.event.location;
446
+ } else if (hasNonNullishProperty(message, 'poll')) {
447
+ (_a = message.poll).selectableCount || (_a.selectableCount = 0);
448
+ (_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
449
+ if (!Array.isArray(message.poll.values)) {
450
+ throw new Boom('Invalid poll values', {
451
+ statusCode: 400
452
+ });
453
+ }
454
+ if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
455
+ throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
456
+ statusCode: 400
457
+ });
458
+ }
459
+ m.messageContextInfo = {
460
+ messageSecret: message.poll.messageSecret || randomBytes(32)
461
+ };
462
+ const pollCreationMessage = {
463
+ name: message.poll.name,
464
+ selectableOptionsCount: message.poll.selectableCount,
465
+ options: message.poll.values.map(optionName => ({
466
+ optionName
467
+ }))
468
+ };
469
+ if (message.poll.toAnnouncementGroup) {
470
+ m.pollCreationMessageV2 = pollCreationMessage;
471
+ } else {
472
+ if (message.poll.selectableCount === 1) {
473
+ m.pollCreationMessageV3 = pollCreationMessage;
474
+ } else {
475
+ m.pollCreationMessage = pollCreationMessage;
476
+ }
477
+ }
478
+ } else if (hasNonNullishProperty(message, 'album')) {
479
+ m.albumMessage = {
480
+ expectedImageCount: message.album.expectedImageCount,
481
+ expectedVideoCount: message.album.expectedVideoCount
482
+ };
483
+ } else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
484
+ m.protocolMessage = {
485
+ type: proto.Message.ProtocolMessage.Type.SHARE_PHONE_NUMBER
486
+ };
487
+ } else if (hasNonNullishProperty(message, 'requestPhoneNumber')) {
488
+ m.requestPhoneNumberMessage = {};
489
+ } else if (hasNonNullishProperty(message, 'limitSharing')) {
490
+ m.protocolMessage = {
491
+ type: proto.Message.ProtocolMessage.Type.LIMIT_SHARING,
492
+ limitSharing: {
493
+ sharingLimited: message.limitSharing === true,
494
+ trigger: 1,
495
+ limitSharingSettingTimestamp: Date.now(),
496
+ initiatedByMe: true
497
+ }
498
+ };
499
+ } else {
500
+ m = await prepareWAMessageMedia(message, options);
501
+ }
502
+ if (hasOptionalProperty(message, 'viewOnce') && !!message.viewOnce) {
503
+ m = {
504
+ viewOnceMessage: {
505
+ message: m
506
+ }
507
+ };
508
+ }
509
+ if (hasOptionalProperty(message, 'mentions') && message.mentions?.length || hasOptionalProperty(message, 'mentionAll') && message.mentionAll) {
510
+ const messageType = Object.keys(m)[0];
511
+ const key = m[messageType];
512
+ if (key && 'contextInfo' in key) {
513
+ key.contextInfo = key.contextInfo || {};
514
+ if (message.mentions?.length) {
515
+ key.contextInfo.mentionedJid = message.mentions;
516
+ }
517
+ if (message.mentionAll) {
518
+ key.contextInfo.nonJidMentions = 1;
519
+ }
520
+ } else if (key) {
521
+ key.contextInfo = {
522
+ mentionedJid: message.mentions,
523
+ nonJidMentions: message.mentionAll ? 1 : 0
524
+ };
525
+ }
526
+ }
527
+ if (hasOptionalProperty(message, 'edit')) {
528
+ m = {
529
+ protocolMessage: {
530
+ key: message.edit,
531
+ editedMessage: m,
532
+ timestampMs: Date.now(),
533
+ type: WAProto.Message.ProtocolMessage.Type.MESSAGE_EDIT
534
+ }
535
+ };
536
+ }
537
+ if (hasOptionalProperty(message, 'contextInfo') && !!message.contextInfo) {
538
+ const messageType = Object.keys(m)[0];
539
+ const key = m[messageType];
540
+ if ('contextInfo' in key && !!key.contextInfo) {
541
+ key.contextInfo = {
542
+ ...key.contextInfo,
543
+ ...message.contextInfo
544
+ };
545
+ } else if (key) {
546
+ key.contextInfo = message.contextInfo;
547
+ }
548
+ }
549
+ if (hasOptionalProperty(message, 'albumParentKey') && !!message.albumParentKey) {
550
+ m.messageContextInfo = {
551
+ ...m.messageContextInfo,
552
+ messageAssociation: {
553
+ associationType: WAProto.MessageAssociation.AssociationType.MEDIA_ALBUM,
554
+ parentMessageKey: message.albumParentKey
555
+ }
556
+ };
557
+ }
558
+ if (shouldIncludeReportingToken(m)) {
559
+ m.messageContextInfo = m.messageContextInfo || {};
560
+ if (!m.messageContextInfo.messageSecret) {
561
+ m.messageContextInfo.messageSecret = randomBytes(32);
562
+ }
563
+ }
564
+ return WAProto.Message.create(m);
550
565
  };
551
566
  export const generateWAMessageFromContent = (jid, message, options) => {
552
- // set timestamp to now
553
- // if not specified
554
- if (!options.timestamp) {
555
- options.timestamp = new Date();
556
- }
557
- const innerMessage = normalizeMessageContent(message);
558
- const key = getContentType(innerMessage);
559
- const timestamp = unixTimestampSeconds(options.timestamp);
560
- const { quoted, userJid } = options;
561
- if (quoted && !isJidNewsletter(jid)) {
562
- const participant = quoted.key.fromMe
563
- ? userJid // TODO: Add support for LIDs
564
- : quoted.participant || quoted.key.participant || quoted.key.remoteJid;
565
- let quotedMsg = normalizeMessageContent(quoted.message);
566
- const msgType = getContentType(quotedMsg);
567
- // strip any redundant properties
568
- quotedMsg = proto.Message.create({ [msgType]: quotedMsg[msgType] });
569
- const quotedContent = quotedMsg[msgType];
570
- if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
571
- delete quotedContent.contextInfo;
572
- }
573
- const contextInfo = ('contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo) || {};
574
- contextInfo.participant = jidNormalizedUser(participant);
575
- contextInfo.stanzaId = quoted.key.id;
576
- contextInfo.quotedMessage = quotedMsg;
577
- // if a participant is quoted, then it must be a group
578
- // hence, remoteJid of group must also be entered
579
- if (jid !== quoted.key.remoteJid) {
580
- contextInfo.remoteJid = quoted.key.remoteJid;
581
- }
582
- if (contextInfo && innerMessage[key]) {
583
- /* @ts-ignore */
584
- innerMessage[key].contextInfo = contextInfo;
585
- }
586
- }
587
- if (
588
- // if we want to send a disappearing message
589
- !!options?.ephemeralExpiration &&
590
- // and it's not a protocol message -- delete, toggle disappear message
591
- key !== 'protocolMessage' &&
592
- // already not converted to disappearing message
593
- key !== 'ephemeralMessage' &&
594
- // newsletters don't support ephemeral messages
595
- !isJidNewsletter(jid)) {
596
- /* @ts-ignore */
597
- innerMessage[key].contextInfo = {
598
- ...(innerMessage[key].contextInfo || {}),
599
- expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
600
- //ephemeralSettingTimestamp: options.ephemeralOptions.eph_setting_ts?.toString()
601
- };
602
- }
603
- message = WAProto.Message.create(message);
604
- const messageJSON = {
605
- key: {
606
- remoteJid: jid,
607
- fromMe: true,
608
- id: options?.messageId || generateMessageIDV2()
609
- },
610
- message: message,
611
- messageTimestamp: timestamp,
612
- messageStubParameters: [],
613
- participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined, // TODO: Add support for LIDs
614
- status: WAMessageStatus.PENDING
567
+ if (!options.timestamp) {
568
+ options.timestamp = new Date();
569
+ }
570
+ const innerMessage = normalizeMessageContent(message);
571
+ const key = getContentType(innerMessage);
572
+ const timestamp = unixTimestampSeconds(options.timestamp);
573
+ const {
574
+ quoted,
575
+ userJid
576
+ } = options;
577
+ if (quoted && !isJidNewsletter(jid)) {
578
+ const participant = quoted.key.fromMe ? userJid : quoted.participant || quoted.key.participant || quoted.key.remoteJid;
579
+ let quotedMsg = normalizeMessageContent(quoted.message);
580
+ const msgType = getContentType(quotedMsg);
581
+ quotedMsg = proto.Message.create({
582
+ [msgType]: quotedMsg[msgType]
583
+ });
584
+ const quotedContent = quotedMsg[msgType];
585
+ if (typeof quotedContent === 'object' && quotedContent && 'contextInfo' in quotedContent) {
586
+ delete quotedContent.contextInfo;
587
+ }
588
+ const contextInfo = 'contextInfo' in innerMessage[key] && innerMessage[key]?.contextInfo || {};
589
+ contextInfo.participant = jidNormalizedUser(participant);
590
+ contextInfo.stanzaId = quoted.key.id;
591
+ contextInfo.quotedMessage = quotedMsg;
592
+ if (jid !== quoted.key.remoteJid) {
593
+ contextInfo.remoteJid = quoted.key.remoteJid;
594
+ }
595
+ if (contextInfo && innerMessage[key]) {
596
+ innerMessage[key].contextInfo = contextInfo;
597
+ }
598
+ }
599
+ if (!!options?.ephemeralExpiration && key !== 'protocolMessage' && key !== 'ephemeralMessage' && !isJidNewsletter(jid)) {
600
+ innerMessage[key].contextInfo = {
601
+ ...(innerMessage[key].contextInfo || {}),
602
+ expiration: options.ephemeralExpiration || WA_DEFAULT_EPHEMERAL
615
603
  };
616
- return WAProto.WebMessageInfo.fromObject(messageJSON);
604
+ }
605
+ message = WAProto.Message.create(message);
606
+ const messageJSON = {
607
+ key: {
608
+ remoteJid: jid,
609
+ fromMe: true,
610
+ id: options?.messageId || generateMessageIDV2()
611
+ },
612
+ message: message,
613
+ messageTimestamp: timestamp,
614
+ messageStubParameters: [],
615
+ participant: isJidGroup(jid) || isJidStatusBroadcast(jid) ? userJid : undefined,
616
+ status: WAMessageStatus.PENDING
617
+ };
618
+ return WAProto.WebMessageInfo.fromObject(messageJSON);
617
619
  };
618
620
  export const generateWAMessage = async (jid, content, options) => {
619
- // ensure msg ID is with every log
620
- options.logger = options?.logger?.child({ msgId: options.messageId });
621
- // Pass jid in the options to generateWAMessageContent
622
- return generateWAMessageFromContent(jid, await generateWAMessageContent(content, { ...options, jid }), options);
621
+ options.logger = options?.logger?.child({
622
+ msgId: options.messageId
623
+ });
624
+ return generateWAMessageFromContent(jid, await generateWAMessageContent(content, {
625
+ ...options,
626
+ jid
627
+ }), options);
623
628
  };
624
- /** Get the key to access the true type of content */
625
- export const getContentType = (content) => {
626
- if (content) {
627
- const keys = Object.keys(content);
628
- const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
629
- return key;
630
- }
629
+ export const getContentType = content => {
630
+ if (content) {
631
+ const keys = Object.keys(content);
632
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage');
633
+ return key;
634
+ }
631
635
  };
632
- /**
633
- * Normalizes ephemeral, view once messages to regular message content
634
- * Eg. image messages in ephemeral messages, in view once messages etc.
635
- * @param content
636
- * @returns
637
- */
638
- export const normalizeMessageContent = (content) => {
639
- if (!content) {
640
- return undefined;
641
- }
642
- // set max iterations to prevent an infinite loop
643
- for (let i = 0; i < 5; i++) {
644
- const inner = getFutureProofMessage(content);
645
- if (!inner) {
646
- break;
647
- }
648
- content = inner.message;
649
- }
650
- return content;
651
- function getFutureProofMessage(message) {
652
- return (message?.ephemeralMessage ||
653
- message?.viewOnceMessage ||
654
- message?.documentWithCaptionMessage ||
655
- message?.viewOnceMessageV2 ||
656
- message?.viewOnceMessageV2Extension ||
657
- message?.editedMessage ||
658
- message?.associatedChildMessage ||
659
- message?.groupStatusMessage ||
660
- message?.groupStatusMessageV2);
661
- }
636
+ export const normalizeMessageContent = content => {
637
+ if (!content) {
638
+ return undefined;
639
+ }
640
+ for (let i = 0; i < 5; i++) {
641
+ const inner = getFutureProofMessage(content);
642
+ if (!inner) {
643
+ break;
644
+ }
645
+ content = inner.message;
646
+ }
647
+ return content;
648
+ function getFutureProofMessage(message) {
649
+ return message?.ephemeralMessage || message?.viewOnceMessage || message?.documentWithCaptionMessage || message?.viewOnceMessageV2 || message?.viewOnceMessageV2Extension || message?.editedMessage || message?.associatedChildMessage || message?.groupStatusMessage || message?.groupStatusMessageV2;
650
+ }
662
651
  };
663
- /**
664
- * Extract the true message content from a message
665
- * Eg. extracts the inner message from a disappearing message/view once message
666
- */
667
- export const extractMessageContent = (content) => {
668
- const extractFromTemplateMessage = (msg) => {
669
- if (msg.imageMessage) {
670
- return { imageMessage: msg.imageMessage };
671
- }
672
- else if (msg.documentMessage) {
673
- return { documentMessage: msg.documentMessage };
674
- }
675
- else if (msg.videoMessage) {
676
- return { videoMessage: msg.videoMessage };
677
- }
678
- else if (msg.locationMessage) {
679
- return { locationMessage: msg.locationMessage };
680
- }
681
- else {
682
- return {
683
- conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
684
- };
685
- }
686
- };
687
- content = normalizeMessageContent(content);
688
- if (content?.buttonsMessage) {
689
- return extractFromTemplateMessage(content.buttonsMessage);
690
- }
691
- if (content?.templateMessage?.hydratedFourRowTemplate) {
692
- return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
693
- }
694
- if (content?.templateMessage?.hydratedTemplate) {
695
- return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
696
- }
697
- if (content?.templateMessage?.fourRowTemplate) {
698
- return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
699
- }
700
- return content;
652
+ export const extractMessageContent = content => {
653
+ const extractFromTemplateMessage = msg => {
654
+ if (msg.imageMessage) {
655
+ return {
656
+ imageMessage: msg.imageMessage
657
+ };
658
+ } else if (msg.documentMessage) {
659
+ return {
660
+ documentMessage: msg.documentMessage
661
+ };
662
+ } else if (msg.videoMessage) {
663
+ return {
664
+ videoMessage: msg.videoMessage
665
+ };
666
+ } else if (msg.locationMessage) {
667
+ return {
668
+ locationMessage: msg.locationMessage
669
+ };
670
+ } else {
671
+ return {
672
+ conversation: 'contentText' in msg ? msg.contentText : 'hydratedContentText' in msg ? msg.hydratedContentText : ''
673
+ };
674
+ }
675
+ };
676
+ content = normalizeMessageContent(content);
677
+ if (content?.buttonsMessage) {
678
+ return extractFromTemplateMessage(content.buttonsMessage);
679
+ }
680
+ if (content?.templateMessage?.hydratedFourRowTemplate) {
681
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedFourRowTemplate);
682
+ }
683
+ if (content?.templateMessage?.hydratedTemplate) {
684
+ return extractFromTemplateMessage(content?.templateMessage?.hydratedTemplate);
685
+ }
686
+ if (content?.templateMessage?.fourRowTemplate) {
687
+ return extractFromTemplateMessage(content?.templateMessage?.fourRowTemplate);
688
+ }
689
+ return content;
701
690
  };
702
- /**
703
- * Returns the device predicted by message ID
704
- */
705
- export const getDevice = (id) => /^3A.{18}$/.test(id)
706
- ? 'ios'
707
- : /^3E.{20}$/.test(id)
708
- ? 'web'
709
- : /^(.{21}|.{32})$/.test(id)
710
- ? 'android'
711
- : /^(3F|.{18}$)/.test(id)
712
- ? 'desktop'
713
- : 'unknown';
714
- /** Upserts a receipt in the message */
691
+ export const getDevice = id => /^3A.{18}$/.test(id) ? 'ios' : /^3E.{20}$/.test(id) ? 'web' : /^(.{21}|.{32})$/.test(id) ? 'android' : /^(3F|.{18}$)/.test(id) ? 'desktop' : 'unknown';
715
692
  export const updateMessageWithReceipt = (msg, receipt) => {
716
- msg.userReceipt = msg.userReceipt || [];
717
- const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
718
- if (recp) {
719
- Object.assign(recp, receipt);
720
- }
721
- else {
722
- msg.userReceipt.push(receipt);
723
- }
693
+ msg.userReceipt = msg.userReceipt || [];
694
+ const recp = msg.userReceipt.find(m => m.userJid === receipt.userJid);
695
+ if (recp) {
696
+ Object.assign(recp, receipt);
697
+ } else {
698
+ msg.userReceipt.push(receipt);
699
+ }
724
700
  };
725
- /** Update the message with a new reaction */
726
701
  export const updateMessageWithReaction = (msg, reaction) => {
727
- const authorID = getKeyAuthor(reaction.key);
728
- const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
729
- reaction.text = reaction.text || '';
730
- reactions.push(reaction);
731
- msg.reactions = reactions;
702
+ const authorID = getKeyAuthor(reaction.key);
703
+ const reactions = (msg.reactions || []).filter(r => getKeyAuthor(r.key) !== authorID);
704
+ reaction.text = reaction.text || '';
705
+ reactions.push(reaction);
706
+ msg.reactions = reactions;
732
707
  };
733
- /** Update the message with a new poll update */
734
708
  export const updateMessageWithPollUpdate = (msg, update) => {
735
- const authorID = getKeyAuthor(update.pollUpdateMessageKey);
736
- const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
737
- if (update.vote?.selectedOptions?.length) {
738
- reactions.push(update);
739
- }
740
- msg.pollUpdates = reactions;
709
+ const authorID = getKeyAuthor(update.pollUpdateMessageKey);
710
+ const reactions = (msg.pollUpdates || []).filter(r => getKeyAuthor(r.pollUpdateMessageKey) !== authorID);
711
+ if (update.vote?.selectedOptions?.length) {
712
+ reactions.push(update);
713
+ }
714
+ msg.pollUpdates = reactions;
741
715
  };
742
- /** Update the message with a new event response */
743
716
  export const updateMessageWithEventResponse = (msg, update) => {
744
- const authorID = getKeyAuthor(update.eventResponseMessageKey);
745
- const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
746
- responses.push(update);
747
- msg.eventResponses = responses;
717
+ const authorID = getKeyAuthor(update.eventResponseMessageKey);
718
+ const responses = (msg.eventResponses || []).filter(r => getKeyAuthor(r.eventResponseMessageKey) !== authorID);
719
+ responses.push(update);
720
+ msg.eventResponses = responses;
748
721
  };
749
- /**
750
- * Aggregates all poll updates in a poll.
751
- * @param msg the poll creation message
752
- * @param meId your jid
753
- * @returns A list of options & their voters
754
- */
755
- export function getAggregateVotesInPollMessage({ message, pollUpdates }, meId) {
756
- const opts = message?.pollCreationMessage?.options ||
757
- message?.pollCreationMessageV2?.options ||
758
- message?.pollCreationMessageV3?.options ||
759
- [];
760
- const voteHashMap = opts.reduce((acc, opt) => {
761
- const hash = sha256(Buffer.from(opt.optionName || '')).toString();
762
- acc[hash] = {
763
- name: opt.optionName || '',
764
- voters: []
722
+ export function getAggregateVotesInPollMessage({
723
+ message,
724
+ pollUpdates
725
+ }, meId) {
726
+ const opts = message?.pollCreationMessage?.options || message?.pollCreationMessageV2?.options || message?.pollCreationMessageV3?.options || [];
727
+ const voteHashMap = opts.reduce((acc, opt) => {
728
+ const hash = sha256(Buffer.from(opt.optionName || '')).toString();
729
+ acc[hash] = {
730
+ name: opt.optionName || '',
731
+ voters: []
732
+ };
733
+ return acc;
734
+ }, {});
735
+ for (const update of pollUpdates || []) {
736
+ const {
737
+ vote
738
+ } = update;
739
+ if (!vote) {
740
+ continue;
741
+ }
742
+ for (const option of vote.selectedOptions || []) {
743
+ const hash = option.toString();
744
+ let data = voteHashMap[hash];
745
+ if (!data) {
746
+ voteHashMap[hash] = {
747
+ name: 'Unknown',
748
+ voters: []
765
749
  };
766
- return acc;
767
- }, {});
768
- for (const update of pollUpdates || []) {
769
- const { vote } = update;
770
- if (!vote) {
771
- continue;
772
- }
773
- for (const option of vote.selectedOptions || []) {
774
- const hash = option.toString();
775
- let data = voteHashMap[hash];
776
- if (!data) {
777
- voteHashMap[hash] = {
778
- name: 'Unknown',
779
- voters: []
780
- };
781
- data = voteHashMap[hash];
782
- }
783
- voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
784
- }
750
+ data = voteHashMap[hash];
751
+ }
752
+ voteHashMap[hash].voters.push(getKeyAuthor(update.pollUpdateMessageKey, meId));
785
753
  }
786
- return Object.values(voteHashMap);
754
+ }
755
+ return Object.values(voteHashMap);
787
756
  }
788
- /**
789
- * Aggregates all event responses in an event message.
790
- * @param msg the event creation message
791
- * @param meId your jid
792
- * @returns A list of response types & their responders
793
- */
794
- export function getAggregateResponsesInEventMessage({ eventResponses }, meId) {
795
- const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
796
- const responseMap = {};
797
- for (const type of responseTypes) {
798
- responseMap[type] = {
799
- response: type,
800
- responders: []
801
- };
802
- }
803
- for (const update of eventResponses || []) {
804
- const responseType = update.eventResponse || 'UNKNOWN';
805
- if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
806
- responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
807
- }
808
- }
809
- return Object.values(responseMap);
757
+ export function getAggregateResponsesInEventMessage({
758
+ eventResponses
759
+ }, meId) {
760
+ const responseTypes = ['GOING', 'NOT_GOING', 'MAYBE'];
761
+ const responseMap = {};
762
+ for (const type of responseTypes) {
763
+ responseMap[type] = {
764
+ response: type,
765
+ responders: []
766
+ };
767
+ }
768
+ for (const update of eventResponses || []) {
769
+ const responseType = update.eventResponse || 'UNKNOWN';
770
+ if (responseType !== 'UNKNOWN' && responseMap[responseType]) {
771
+ responseMap[responseType].responders.push(getKeyAuthor(update.eventResponseMessageKey, meId));
772
+ }
773
+ }
774
+ return Object.values(responseMap);
810
775
  }
811
- /** Given a list of message keys, aggregates them by chat & sender. Useful for sending read receipts in bulk */
812
- export const aggregateMessageKeysNotFromMe = (keys) => {
813
- const keyMap = {};
814
- for (const { remoteJid, id, participant, fromMe } of keys) {
815
- if (!fromMe) {
816
- const uqKey = `${remoteJid}:${participant || ''}`;
817
- if (!keyMap[uqKey]) {
818
- keyMap[uqKey] = {
819
- jid: remoteJid,
820
- participant: participant,
821
- messageIds: []
822
- };
823
- }
824
- keyMap[uqKey].messageIds.push(id);
825
- }
776
+ export const aggregateMessageKeysNotFromMe = keys => {
777
+ const keyMap = {};
778
+ for (const {
779
+ remoteJid,
780
+ id,
781
+ participant,
782
+ fromMe
783
+ } of keys) {
784
+ if (!fromMe) {
785
+ const uqKey = `${remoteJid}:${participant || ''}`;
786
+ if (!keyMap[uqKey]) {
787
+ keyMap[uqKey] = {
788
+ jid: remoteJid,
789
+ participant: participant,
790
+ messageIds: []
791
+ };
792
+ }
793
+ keyMap[uqKey].messageIds.push(id);
826
794
  }
827
- return Object.values(keyMap);
795
+ }
796
+ return Object.values(keyMap);
828
797
  };
829
798
  const REUPLOAD_REQUIRED_STATUS = [410, 404];
830
- /**
831
- * Downloads the given message. Throws an error if it's not a media message
832
- */
833
799
  export const downloadMediaMessage = async (message, type, options, ctx) => {
834
- const result = await downloadMsg().catch(async (error) => {
835
- if (ctx &&
836
- typeof error?.status === 'number' && // treat errors with status as HTTP failures requiring reupload
837
- REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
838
- ctx.logger.info({ key: message.key }, 'sending reupload media request...');
839
- // request reupload
840
- message = await ctx.reuploadRequest(message);
841
- const result = await downloadMsg();
842
- return result;
843
- }
844
- throw error;
845
- });
846
- return result;
847
- async function downloadMsg() {
848
- const mContent = extractMessageContent(message.message);
849
- if (!mContent) {
850
- throw new Boom('No message present', { statusCode: 400, data: message });
851
- }
852
- const contentType = getContentType(mContent);
853
- let mediaType = contentType?.replace('Message', '');
854
- const media = mContent[contentType];
855
- if (!media || typeof media !== 'object' || (!('url' in media) && !('thumbnailDirectPath' in media))) {
856
- throw new Boom(`"${contentType}" message is not a media message`);
857
- }
858
- let download;
859
- if ('thumbnailDirectPath' in media && !('url' in media)) {
860
- download = {
861
- directPath: media.thumbnailDirectPath,
862
- mediaKey: media.mediaKey
863
- };
864
- mediaType = 'thumbnail-link';
865
- }
866
- else {
867
- download = media;
868
- }
869
- const stream = await downloadContentFromMessage(download, mediaType, options);
870
- if (type === 'buffer') {
871
- const bufferArray = [];
872
- for await (const chunk of stream) {
873
- bufferArray.push(chunk);
874
- }
875
- return Buffer.concat(bufferArray);
876
- }
877
- return stream;
878
- }
800
+ const result = await downloadMsg().catch(async error => {
801
+ if (ctx && typeof error?.status === 'number' && REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
802
+ ctx.logger.info({
803
+ key: message.key
804
+ }, 'sending reupload media request...');
805
+ message = await ctx.reuploadRequest(message);
806
+ const result = await downloadMsg();
807
+ return result;
808
+ }
809
+ throw error;
810
+ });
811
+ return result;
812
+ async function downloadMsg() {
813
+ const mContent = extractMessageContent(message.message);
814
+ if (!mContent) {
815
+ throw new Boom('No message present', {
816
+ statusCode: 400,
817
+ data: message
818
+ });
819
+ }
820
+ const contentType = getContentType(mContent);
821
+ let mediaType = contentType?.replace('Message', '');
822
+ const media = mContent[contentType];
823
+ if (!media || typeof media !== 'object' || !('url' in media) && !('thumbnailDirectPath' in media)) {
824
+ throw new Boom(`"${contentType}" message is not a media message`);
825
+ }
826
+ let download;
827
+ if ('thumbnailDirectPath' in media && !('url' in media)) {
828
+ download = {
829
+ directPath: media.thumbnailDirectPath,
830
+ mediaKey: media.mediaKey
831
+ };
832
+ mediaType = 'thumbnail-link';
833
+ } else {
834
+ download = media;
835
+ }
836
+ const stream = await downloadContentFromMessage(download, mediaType, options);
837
+ if (type === 'buffer') {
838
+ const bufferArray = [];
839
+ for await (const chunk of stream) {
840
+ bufferArray.push(chunk);
841
+ }
842
+ return Buffer.concat(bufferArray);
843
+ }
844
+ return stream;
845
+ }
879
846
  };
880
- /** Checks whether the given message is a media message; if it is returns the inner content */
881
- export const assertMediaContent = (content) => {
882
- content = extractMessageContent(content);
883
- const mediaContent = content?.documentMessage ||
884
- content?.imageMessage ||
885
- content?.videoMessage ||
886
- content?.audioMessage ||
887
- content?.stickerMessage;
888
- if (!mediaContent) {
889
- throw new Boom('given message is not a media message', { statusCode: 400, data: content });
890
- }
891
- return mediaContent;
847
+ export const assertMediaContent = content => {
848
+ content = extractMessageContent(content);
849
+ const mediaContent = content?.documentMessage || content?.imageMessage || content?.videoMessage || content?.audioMessage || content?.stickerMessage;
850
+ if (!mediaContent) {
851
+ throw new Boom('given message is not a media message', {
852
+ statusCode: 400,
853
+ data: content
854
+ });
855
+ }
856
+ return mediaContent;
892
857
  };
893
- //# sourceMappingURL=messages.js.map