@prototypevip/baileys 0.0.1

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 (118) hide show
  1. package/LICENSE +23 -0
  2. package/WAProto/index.js +131118 -0
  3. package/engine-requirements.js +1 -0
  4. package/lib/Defaults/index.js +187 -0
  5. package/lib/Signal/Group/ciphertext-message.js +11 -0
  6. package/lib/Signal/Group/group-session-builder.js +41 -0
  7. package/lib/Signal/Group/group_cipher.js +108 -0
  8. package/lib/Signal/Group/index.js +11 -0
  9. package/lib/Signal/Group/keyhelper.js +14 -0
  10. package/lib/Signal/Group/sender-chain-key.js +31 -0
  11. package/lib/Signal/Group/sender-key-distribution-message.js +66 -0
  12. package/lib/Signal/Group/sender-key-message.js +79 -0
  13. package/lib/Signal/Group/sender-key-name.js +49 -0
  14. package/lib/Signal/Group/sender-key-record.js +46 -0
  15. package/lib/Signal/Group/sender-key-state.js +104 -0
  16. package/lib/Signal/Group/sender-message-key.js +29 -0
  17. package/lib/Signal/libsignal.js +532 -0
  18. package/lib/Signal/lid-mapping.js +312 -0
  19. package/lib/Socket/Client/index.js +2 -0
  20. package/lib/Socket/Client/types.js +10 -0
  21. package/lib/Socket/Client/websocket.js +64 -0
  22. package/lib/Socket/business.js +293 -0
  23. package/lib/Socket/chats.js +1124 -0
  24. package/lib/Socket/communities.js +528 -0
  25. package/lib/Socket/groups.js +465 -0
  26. package/lib/Socket/index.js +14 -0
  27. package/lib/Socket/messages-recv.js +2325 -0
  28. package/lib/Socket/messages-send.js +1746 -0
  29. package/lib/Socket/mex.js +55 -0
  30. package/lib/Socket/newsletter.js +317 -0
  31. package/lib/Socket/socket.js +1185 -0
  32. package/lib/Store/cache-factory.js +20 -0
  33. package/lib/Store/index.js +5 -0
  34. package/lib/Store/make-in-memory-store.js +523 -0
  35. package/lib/Store/make-ordered-dictionary.js +203 -0
  36. package/lib/Store/object-repository.js +23 -0
  37. package/lib/Store/prototype-store.js +958 -0
  38. package/lib/Types/Auth.js +1 -0
  39. package/lib/Types/Bussines.js +1 -0
  40. package/lib/Types/Call.js +1 -0
  41. package/lib/Types/Chat.js +7 -0
  42. package/lib/Types/Contact.js +1 -0
  43. package/lib/Types/Events.js +1 -0
  44. package/lib/Types/GroupMetadata.js +1 -0
  45. package/lib/Types/Label.js +24 -0
  46. package/lib/Types/LabelAssociation.js +6 -0
  47. package/lib/Types/Message.js +18 -0
  48. package/lib/Types/Mex.js +37 -0
  49. package/lib/Types/Newsletter.js +36 -0
  50. package/lib/Types/Product.js +1 -0
  51. package/lib/Types/RichType.js +25 -0
  52. package/lib/Types/Signal.js +1 -0
  53. package/lib/Types/Socket.js +2 -0
  54. package/lib/Types/State.js +78 -0
  55. package/lib/Types/USync.js +1 -0
  56. package/lib/Types/index.js +33 -0
  57. package/lib/Utils/auth-utils.js +303 -0
  58. package/lib/Utils/browser-utils.js +32 -0
  59. package/lib/Utils/business.js +245 -0
  60. package/lib/Utils/chat-utils.js +971 -0
  61. package/lib/Utils/companion-reg-client-utils.js +54 -0
  62. package/lib/Utils/constants.js +24 -0
  63. package/lib/Utils/crypto.js +133 -0
  64. package/lib/Utils/cstoken-utils.js +41 -0
  65. package/lib/Utils/decode-wa-message.js +406 -0
  66. package/lib/Utils/event-buffer.js +616 -0
  67. package/lib/Utils/generics.js +438 -0
  68. package/lib/Utils/history.js +147 -0
  69. package/lib/Utils/identity-change-handler.js +63 -0
  70. package/lib/Utils/index.js +29 -0
  71. package/lib/Utils/interactive-message-handler.js +478 -0
  72. package/lib/Utils/link-preview.js +91 -0
  73. package/lib/Utils/logger.js +2 -0
  74. package/lib/Utils/lt-hash.js +6 -0
  75. package/lib/Utils/make-mutex.js +31 -0
  76. package/lib/Utils/message-retry-manager.js +291 -0
  77. package/lib/Utils/messages-media.js +881 -0
  78. package/lib/Utils/messages.js +1434 -0
  79. package/lib/Utils/noise-handler.js +229 -0
  80. package/lib/Utils/normalize-message-identity.js +73 -0
  81. package/lib/Utils/offline-node-processor.js +50 -0
  82. package/lib/Utils/pre-key-manager.js +119 -0
  83. package/lib/Utils/process-message.js +648 -0
  84. package/lib/Utils/reporting-utils.js +355 -0
  85. package/lib/Utils/rich-message-utils.js +264 -0
  86. package/lib/Utils/signal.js +233 -0
  87. package/lib/Utils/stanza-ack.js +33 -0
  88. package/lib/Utils/sticker-utils.js +221 -0
  89. package/lib/Utils/sync-action-utils.js +53 -0
  90. package/lib/Utils/tc-token-utils.js +182 -0
  91. package/lib/Utils/use-multi-file-auth-state.js +118 -0
  92. package/lib/Utils/use-prototype-auth-state.js +217 -0
  93. package/lib/Utils/use-single-file-auth-state.js +94 -0
  94. package/lib/Utils/validate-connection.js +235 -0
  95. package/lib/WABinary/constants.js +1468 -0
  96. package/lib/WABinary/decode.js +258 -0
  97. package/lib/WABinary/encode.js +219 -0
  98. package/lib/WABinary/generic-utils.js +202 -0
  99. package/lib/WABinary/index.js +5 -0
  100. package/lib/WABinary/jid-utils.js +94 -0
  101. package/lib/WABinary/types.js +1 -0
  102. package/lib/WAM/BinaryInfo.js +9 -0
  103. package/lib/WAM/constants.js +20669 -0
  104. package/lib/WAM/encode.js +151 -0
  105. package/lib/WAM/index.js +3 -0
  106. package/lib/WAUSync/Protocols/USyncContactProtocol.js +28 -0
  107. package/lib/WAUSync/Protocols/USyncDeviceProtocol.js +59 -0
  108. package/lib/WAUSync/Protocols/USyncDisappearingModeProtocol.js +20 -0
  109. package/lib/WAUSync/Protocols/USyncStatusProtocol.js +29 -0
  110. package/lib/WAUSync/Protocols/USyncUsernameProtocol.js +24 -0
  111. package/lib/WAUSync/Protocols/UsyncBotProfileProtocol.js +59 -0
  112. package/lib/WAUSync/Protocols/UsyncLIDProtocol.js +21 -0
  113. package/lib/WAUSync/Protocols/index.js +4 -0
  114. package/lib/WAUSync/USyncQuery.js +133 -0
  115. package/lib/WAUSync/USyncUser.js +22 -0
  116. package/lib/WAUSync/index.js +3 -0
  117. package/lib/index.js +12 -0
  118. package/package.json +78 -0
@@ -0,0 +1,478 @@
1
+ import { proto as WAProto } from '../../WAProto/index.js';
2
+ import crypto from 'crypto';
3
+ import * as Utils_1 from '../Utils/index.js';
4
+
5
+ export default class InteractiveMessageHandler {
6
+ constructor(utils, waUploadToServer, relayMessageFn) {
7
+ this.utils = utils;
8
+ this.relayMessage = relayMessageFn;
9
+ this.waUploadToServer = waUploadToServer;
10
+
11
+ this.bail = {
12
+ generateWAMessageContent: this.utils.generateWAMessageContent || Utils_1.generateWAMessageContent,
13
+ generateMessageID: Utils_1.generateMessageID,
14
+ getContentType: (msg) => Object.keys(msg.message || {})[0]
15
+ };
16
+ }
17
+
18
+ detectType(content) {
19
+ if (content.requestPaymentMessage) return 'PAYMENT';
20
+ if (content.productMessage) return 'PRODUCT';
21
+ if (content.interactiveMessage) return 'INTERACTIVE';
22
+ if (content.albumMessage) return 'ALBUM';
23
+ if (content.eventMessage) return 'EVENT';
24
+ if (content.pollResultMessage) return 'POLL_RESULT';
25
+ if (content.groupStatusMessage) return 'GROUP_STORY';
26
+ return null;
27
+ }
28
+
29
+ async handlePayment(content, quoted) {
30
+ const data = content.requestPaymentMessage;
31
+ let notes = {};
32
+
33
+ if (data.sticker?.stickerMessage) {
34
+ notes = {
35
+ stickerMessage: {
36
+ ...data.sticker.stickerMessage,
37
+ contextInfo: {
38
+ stanzaId: quoted?.key?.id,
39
+ participant: quoted?.key?.participant || content.sender,
40
+ quotedMessage: quoted?.message
41
+ }
42
+ }
43
+ };
44
+ } else if (data.note) {
45
+ notes = {
46
+ extendedTextMessage: {
47
+ text: data.note,
48
+ contextInfo: {
49
+ stanzaId: quoted?.key?.id,
50
+ participant: quoted?.key?.participant || content.sender,
51
+ quotedMessage: quoted?.message
52
+ }
53
+ }
54
+ };
55
+ }
56
+
57
+ return {
58
+ requestPaymentMessage: WAProto.Message.RequestPaymentMessage.fromObject({
59
+ expiryTimestamp: data.expiry || 0,
60
+ amount1000: data.amount || 0,
61
+ currencyCodeIso4217: data.currency || "IDR",
62
+ requestFrom: data.from || "0@s.whatsapp.net",
63
+ noteMessage: notes,
64
+ background: data.background ?? {
65
+ id: "DEFAULT",
66
+ placeholderArgb: 0xFFF0F0F0
67
+ }
68
+ })
69
+ };
70
+ }
71
+
72
+ async handleProduct(content, jid, quoted) {
73
+ const {
74
+ title,
75
+ description,
76
+ thumbnail,
77
+ productId,
78
+ retailerId,
79
+ url,
80
+ body = "",
81
+ footer = "",
82
+ buttons = [],
83
+ priceAmount1000 = null,
84
+ currencyCode = "IDR"
85
+ } = content.productMessage;
86
+
87
+ let productImage;
88
+
89
+ if (Buffer.isBuffer(thumbnail)) {
90
+ const { imageMessage } = await this.utils.generateWAMessageContent(
91
+ { image: thumbnail },
92
+ { upload: this.waUploadToServer }
93
+ );
94
+ productImage = imageMessage;
95
+ } else if (typeof thumbnail === 'object' && thumbnail.url) {
96
+ const { imageMessage } = await this.utils.generateWAMessageContent(
97
+ { image: { url: thumbnail.url }},
98
+ { upload: this.waUploadToServer }
99
+ );
100
+ productImage = imageMessage;
101
+ }
102
+
103
+ return {
104
+ viewOnceMessage: {
105
+ message: {
106
+ interactiveMessage: {
107
+ body: { text: body },
108
+ footer: { text: footer },
109
+ header: {
110
+ title,
111
+ hasMediaAttachment: true,
112
+ productMessage: {
113
+ product: {
114
+ productImage,
115
+ productId,
116
+ title,
117
+ description,
118
+ currencyCode,
119
+ priceAmount1000,
120
+ retailerId,
121
+ url,
122
+ productImageCount: 1
123
+ },
124
+ businessOwnerJid: "0@s.whatsapp.net"
125
+ }
126
+ },
127
+ nativeFlowMessage: { buttons }
128
+ }
129
+ }
130
+ }
131
+ };
132
+ }
133
+
134
+ async handleInteractive(content, jid, quoted) {
135
+ const {
136
+ title,
137
+ footer,
138
+ thumbnail,
139
+ image,
140
+ video,
141
+ document,
142
+ mimetype,
143
+ fileName,
144
+ jpegThumbnail,
145
+ contextInfo,
146
+ externalAdReply,
147
+ buttons = [],
148
+ nativeFlowMessage,
149
+ header
150
+ } = content.interactiveMessage;
151
+
152
+ let media = null;
153
+ let mediaType = null;
154
+
155
+ if (thumbnail) {
156
+ media = await this.utils.prepareWAMessageMedia(
157
+ { image: { url: thumbnail } },
158
+ { upload: this.waUploadToServer }
159
+ );
160
+ mediaType = 'image';
161
+ } else if (image) {
162
+ if (typeof image === 'object' && image.url) {
163
+ media = await this.utils.prepareWAMessageMedia(
164
+ { image: { url: image.url } },
165
+ { upload: this.waUploadToServer }
166
+ );
167
+ } else {
168
+ media = await this.utils.prepareWAMessageMedia(
169
+ { image: image },
170
+ { upload: this.waUploadToServer }
171
+ );
172
+ }
173
+ mediaType = 'image';
174
+ } else if (video) {
175
+ if (typeof video === 'object' && video.url) {
176
+ media = await this.utils.prepareWAMessageMedia(
177
+ { video: { url: video.url } },
178
+ { upload: this.waUploadToServer }
179
+ );
180
+ } else {
181
+ media = await this.utils.prepareWAMessageMedia(
182
+ { video: video },
183
+ { upload: this.waUploadToServer }
184
+ );
185
+ }
186
+ mediaType = 'video';
187
+ } else if (document) {
188
+ let documentPayload = {
189
+ document: document
190
+ };
191
+ if (jpegThumbnail) {
192
+ if (typeof jpegThumbnail === 'object' && jpegThumbnail.url) {
193
+ documentPayload.jpegThumbnail = { url: jpegThumbnail.url };
194
+ } else {
195
+ documentPayload.jpegThumbnail = jpegThumbnail;
196
+ }
197
+ }
198
+
199
+ media = await this.utils.prepareWAMessageMedia(
200
+ documentPayload,
201
+ { upload: this.waUploadToServer }
202
+ );
203
+ if (fileName) {
204
+ media.documentMessage.fileName = fileName;
205
+ }
206
+ if (mimetype) {
207
+ media.documentMessage.mimetype = mimetype;
208
+ }
209
+ mediaType = 'document';
210
+ }
211
+ let interactiveMessage = {
212
+ body: { text: title || "" },
213
+ footer: { text: footer || "" }
214
+ };
215
+ if (buttons && buttons.length > 0) {
216
+ interactiveMessage.nativeFlowMessage = {
217
+ buttons: buttons
218
+ };
219
+ if (nativeFlowMessage) {
220
+ interactiveMessage.nativeFlowMessage = {
221
+ ...interactiveMessage.nativeFlowMessage,
222
+ ...nativeFlowMessage
223
+ };
224
+ }
225
+ } else if (nativeFlowMessage) {
226
+ interactiveMessage.nativeFlowMessage = nativeFlowMessage;
227
+ }
228
+
229
+ if (media) {
230
+ interactiveMessage.header = {
231
+ title: header || "",
232
+ hasMediaAttachment: true,
233
+ ...media
234
+ };
235
+ } else {
236
+ interactiveMessage.header = {
237
+ title: header || "",
238
+ hasMediaAttachment: false
239
+ };
240
+ }
241
+
242
+ let finalContextInfo = {};
243
+ if (contextInfo) {
244
+ finalContextInfo = {
245
+ mentionedJid: contextInfo.mentionedJid || [],
246
+ forwardingScore: contextInfo.forwardingScore || 0,
247
+ isForwarded: contextInfo.isForwarded || false,
248
+ ...contextInfo
249
+ };
250
+ }
251
+
252
+ if (externalAdReply) {
253
+ finalContextInfo.externalAdReply = {
254
+ title: externalAdReply.title || "",
255
+ body: externalAdReply.body || "",
256
+ mediaType: externalAdReply.mediaType || 1,
257
+ thumbnailUrl: externalAdReply.thumbnailUrl || "",
258
+ mediaUrl: externalAdReply.mediaUrl || "",
259
+ sourceUrl: externalAdReply.sourceUrl || "",
260
+ showAdAttribution: externalAdReply.showAdAttribution || false,
261
+ renderLargerThumbnail: externalAdReply.renderLargerThumbnail || false,
262
+ ...externalAdReply
263
+ };
264
+ }
265
+
266
+ if (Object.keys(finalContextInfo).length > 0) {
267
+ interactiveMessage.contextInfo = finalContextInfo;
268
+ }
269
+ return {
270
+ interactiveMessage: interactiveMessage
271
+ };
272
+ }
273
+
274
+ async handleAlbum(content, jid, quoted) {
275
+ const array = content.albumMessage;
276
+ const album = await this.utils.generateWAMessageFromContent(jid, {
277
+ messageContextInfo: {
278
+ messageSecret: crypto.randomBytes(32),
279
+ },
280
+ albumMessage: {
281
+ expectedImageCount: array.filter((a) => a.hasOwnProperty("image")).length,
282
+ expectedVideoCount: array.filter((a) => a.hasOwnProperty("video")).length,
283
+ },
284
+ }, {
285
+ userJid: this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net',
286
+ quoted,
287
+ upload: this.waUploadToServer
288
+ });
289
+
290
+ await this.relayMessage(jid, album.message, {
291
+ messageId: album.key.id,
292
+ });
293
+
294
+ for (let content of array) {
295
+ const img = await this.utils.generateWAMessage(jid, content, {
296
+ upload: this.waUploadToServer,
297
+ });
298
+
299
+ img.message.messageContextInfo = {
300
+ messageSecret: crypto.randomBytes(32),
301
+ messageAssociation: {
302
+ associationType: 1,
303
+ parentMessageKey: album.key,
304
+ },
305
+ participant: "0@s.whatsapp.net",
306
+ remoteJid: "status@broadcast",
307
+ forwardingScore: 99999,
308
+ isForwarded: true,
309
+ mentionedJid: [jid],
310
+ starred: true,
311
+ labels: ["Y", "Important"],
312
+ isHighlighted: true,
313
+ businessMessageForwardInfo: {
314
+ businessOwnerJid: jid,
315
+ },
316
+ dataSharingContext: {
317
+ showMmDisclosure: true,
318
+ },
319
+ };
320
+
321
+ img.message.forwardedNewsletterMessageInfo = {
322
+ newsletterJid: "0@newsletter",
323
+ serverMessageId: 1,
324
+ newsletterName: `WhatsApp`,
325
+ contentType: 1,
326
+ timestamp: new Date().toISOString(),
327
+ senderName: "Alip Clutch",
328
+ content: "Text Message",
329
+ priority: "high",
330
+ status: "sent",
331
+ };
332
+
333
+ img.message.disappearingMode = {
334
+ initiator: 3,
335
+ trigger: 4,
336
+ initiatorDeviceJid: jid,
337
+ initiatedByExternalService: true,
338
+ initiatedByUserDevice: true,
339
+ initiatedBySystem: true,
340
+ initiatedByServer: true,
341
+ initiatedByAdmin: true,
342
+ initiatedByUser: true,
343
+ initiatedByApp: true,
344
+ initiatedByBot: true,
345
+ initiatedByMe: true,
346
+ };
347
+
348
+ await this.relayMessage(jid, img.message, {
349
+ messageId: img.key.id,
350
+ quoted: {
351
+ key: {
352
+ remoteJid: album.key.remoteJid,
353
+ id: album.key.id,
354
+ fromMe: true,
355
+ participant: this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net',
356
+ },
357
+ message: album.message,
358
+ },
359
+ });
360
+ }
361
+ return album;
362
+ }
363
+
364
+ async handleEvent(content, jid, quoted) {
365
+ const eventData = content.eventMessage;
366
+
367
+ const msg = await this.utils.generateWAMessageFromContent(jid, {
368
+ viewOnceMessage: {
369
+ message: {
370
+ messageContextInfo: {
371
+ deviceListMetadata: {},
372
+ deviceListMetadataVersion: 2,
373
+ messageSecret: crypto.randomBytes(32),
374
+ supportPayload: JSON.stringify({
375
+ version: 2,
376
+ is_ai_message: true,
377
+ should_show_system_message: true,
378
+ ticket_id: crypto.randomBytes(16).toString('hex')
379
+ })
380
+ },
381
+ eventMessage: {
382
+ contextInfo: {
383
+ mentionedJid: [jid],
384
+ participant: jid,
385
+ remoteJid: "status@broadcast",
386
+ forwardedNewsletterMessageInfo: {
387
+ newsletterName: "alip clutch.",
388
+ newsletterJid: "120363406965898096@newsletter",
389
+ serverMessageId: 1
390
+ }
391
+ },
392
+ isCanceled: eventData.isCanceled || false,
393
+ name: eventData.name,
394
+ description: eventData.description,
395
+ location: eventData.location || {
396
+ degreesLatitude: 0,
397
+ degreesLongitude: 0,
398
+ name: "Location"
399
+ },
400
+ joinLink: eventData.joinLink || '',
401
+ startTime: typeof eventData.startTime === 'string' ? parseInt(eventData.startTime) : eventData.startTime || Date.now(),
402
+ endTime: typeof eventData.endTime === 'string' ? parseInt(eventData.endTime) : eventData.endTime || Date.now() + 3600000,
403
+ extraGuestsAllowed: eventData.extraGuestsAllowed !== false
404
+ }
405
+ }
406
+ }
407
+ }, { quoted });
408
+
409
+ await this.relayMessage(jid, msg.message, {
410
+ messageId: msg.key.id
411
+ });
412
+ return msg;
413
+ }
414
+
415
+ async handlePollResult(content, jid, quoted) {
416
+ const pollData = content.pollResultMessage;
417
+
418
+ const msg = await this.utils.generateWAMessageFromContent(jid, {
419
+ pollResultSnapshotMessage: {
420
+ name: pollData.name,
421
+ pollVotes: pollData.pollVotes.map(vote => ({
422
+ optionName: vote.optionName,
423
+ optionVoteCount: typeof vote.optionVoteCount === 'number'
424
+ ? vote.optionVoteCount.toString()
425
+ : vote.optionVoteCount
426
+ }))
427
+ }
428
+ }, {
429
+ userJid: this.utils.generateMessageID().split('@')[0] + '@s.whatsapp.net',
430
+ quoted
431
+ });
432
+
433
+ await this.relayMessage(jid, msg.message, {
434
+ messageId: msg.key.id
435
+ });
436
+
437
+ return msg;
438
+ }
439
+
440
+ async handleGroupStory(content, jid, quoted) {
441
+ const storyData = content.groupStatusMessage;
442
+ let waMsgContent;
443
+
444
+ if (storyData.message) {
445
+ waMsgContent = storyData;
446
+ } else {
447
+ if (typeof this.bail?.generateWAMessageContent === "function") {
448
+ waMsgContent = await this.bail.generateWAMessageContent(storyData, {
449
+ upload: this.waUploadToServer
450
+ });
451
+ } else if (typeof this.utils?.generateWAMessageContent === "function") {
452
+ waMsgContent = await this.utils.generateWAMessageContent(storyData, {
453
+ upload: this.waUploadToServer
454
+ });
455
+ } else if (typeof this.utils?.prepareMessageContent === "function") {
456
+ waMsgContent = await this.utils.prepareMessageContent(storyData, {
457
+ upload: this.waUploadToServer
458
+ });
459
+ } else {
460
+ waMsgContent = await Utils_1.generateWAMessageContent(storyData, {
461
+ upload: this.waUploadToServer
462
+ });
463
+ }
464
+ }
465
+
466
+ let msg = {
467
+ message: {
468
+ groupStatusMessageV2: {
469
+ message: waMsgContent.message || waMsgContent
470
+ }
471
+ }
472
+ };
473
+
474
+ return await this.relayMessage(jid, msg.message, {
475
+ messageId: this.bail.generateMessageID()
476
+ });
477
+ }
478
+ }
@@ -0,0 +1,91 @@
1
+ import { prepareWAMessageMedia } from "./messages.js";
2
+ import { extractImageThumb, getHttpStream } from "./messages-media.js";
3
+ const THUMBNAIL_WIDTH_PX = 192;
4
+ /** Fetches an image and generates a thumbnail for it */ const getCompressedJpegThumbnail =
5
+ async (url, { thumbnailWidth, fetchOpts }) => {
6
+ const stream = await getHttpStream(url, fetchOpts);
7
+ const result = await extractImageThumb(stream, thumbnailWidth);
8
+ return result;
9
+ };
10
+ /**
11
+ * Given a piece of text, checks for any URL present, generates link preview for the same and returns it
12
+ * Return undefined if the fetch failed or no URL was found
13
+ * @param text first matched URL in text
14
+ * @returns the URL info required to generate link preview
15
+ */ export const getUrlInfo = async (
16
+ text,
17
+ opts = { thumbnailWidth: THUMBNAIL_WIDTH_PX, fetchOpts: { timeout: 3e3 } },
18
+ ) => {
19
+ try {
20
+ // retries
21
+ const retries = 0;
22
+ const maxRetry = 5;
23
+ const { getLinkPreview } = await import("link-preview-js");
24
+ let previewLink = text;
25
+ if (!text.startsWith("https://") && !text.startsWith("http://")) {
26
+ previewLink = "https://" + previewLink;
27
+ }
28
+ const info = await getLinkPreview(previewLink, {
29
+ ...opts.fetchOpts,
30
+ followRedirects: "follow",
31
+ handleRedirects: (baseURL, forwardedURL) => {
32
+ const urlObj = new URL(baseURL);
33
+ const forwardedURLObj = new URL(forwardedURL);
34
+ if (retries >= maxRetry) {
35
+ return false;
36
+ }
37
+ if (
38
+ forwardedURLObj.hostname === urlObj.hostname ||
39
+ forwardedURLObj.hostname === "www." + urlObj.hostname ||
40
+ "www." + forwardedURLObj.hostname === urlObj.hostname
41
+ ) {
42
+ retries + 1;
43
+ return true;
44
+ } else {
45
+ return false;
46
+ }
47
+ },
48
+ headers: opts.fetchOpts?.headers,
49
+ });
50
+ if (info && "title" in info && info.title) {
51
+ const [image] = info.images;
52
+ const urlInfo = {
53
+ "canonical-url": info.url,
54
+ "matched-text": text,
55
+ title: info.title,
56
+ description: info.description,
57
+ originalThumbnailUrl: image,
58
+ };
59
+ if (opts.uploadImage) {
60
+ const { imageMessage } = await prepareWAMessageMedia(
61
+ { image: { url: image } },
62
+ {
63
+ upload: opts.uploadImage,
64
+ mediaTypeOverride: "thumbnail-link",
65
+ options: opts.fetchOpts,
66
+ },
67
+ );
68
+ urlInfo.jpegThumbnail = imageMessage?.jpegThumbnail
69
+ ? Buffer.from(imageMessage.jpegThumbnail)
70
+ : undefined;
71
+ urlInfo.highQualityThumbnail = imageMessage || undefined;
72
+ } else {
73
+ try {
74
+ urlInfo.jpegThumbnail = image
75
+ ? (await getCompressedJpegThumbnail(image, opts)).buffer
76
+ : undefined;
77
+ } catch (error) {
78
+ opts.logger?.debug(
79
+ { err: error.stack, url: previewLink },
80
+ "error in generating thumbnail",
81
+ );
82
+ }
83
+ }
84
+ return urlInfo;
85
+ }
86
+ } catch (error) {
87
+ if (!error.message.includes("receive a valid")) {
88
+ throw error;
89
+ }
90
+ }
91
+ };
@@ -0,0 +1,2 @@
1
+ import P from "pino";
2
+ export default P({ timestamp: () => `,"time":"${new Date().toJSON()}"` });
@@ -0,0 +1,6 @@
1
+ import { LTHashAntiTampering } from "whatsapp-rust-bridge";
2
+ /**
3
+ * LT Hash is a summation based hash algorithm that maintains the integrity of a piece of data
4
+ * over a series of mutations. You can add/remove mutations and it'll return a hash equal to
5
+ * if the same series of mutations was made sequentially.
6
+ */ export const LT_HASH_ANTI_TAMPERING = new LTHashAntiTampering();
@@ -0,0 +1,31 @@
1
+ import { Mutex as AsyncMutex } from "async-mutex";
2
+ export const makeMutex = () => {
3
+ const mutex = new AsyncMutex();
4
+ return {
5
+ mutex(code) {
6
+ return mutex.runExclusive(code);
7
+ },
8
+ };
9
+ };
10
+ export const makeKeyedMutex = () => {
11
+ const map = new Map();
12
+ return {
13
+ async mutex(key, task) {
14
+ let entry = map.get(key);
15
+ if (!entry) {
16
+ entry = { mutex: new AsyncMutex(), refCount: 0 };
17
+ map.set(key, entry);
18
+ }
19
+ entry.refCount++;
20
+ try {
21
+ return await entry.mutex.runExclusive(task);
22
+ } finally {
23
+ entry.refCount--;
24
+ // only delete it if this is still the current entry
25
+ if (entry.refCount === 0 && map.get(key) === entry) {
26
+ map.delete(key);
27
+ }
28
+ }
29
+ },
30
+ };
31
+ };