gifted-baileys 1.5.5 → 1.5.7

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