@itsliaaa/baileys 0.1.17 โ†’ 0.1.19

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.
package/README.md CHANGED
@@ -1,8 +1,36 @@
1
1
  # ๐ŸŒฑ @itsliaaa/baileys
2
2
 
3
- ![Logo](https://files.catbox.moe/c5s9g0.jpg)
4
-
5
- A lightweight fork of Baileys with a few fixes and a small adjustment.
3
+ [![Logo](https://files.catbox.moe/c5s9g0.jpg)](https://www.npmjs.com/package/@itsliaaa/baileys)
4
+
5
+ <p align="center">
6
+ <a href="https://www.npmjs.com/package/@itsliaaa/baileys">
7
+ <img src="https://img.shields.io/npm/v/@itsliaaa/baileys?color=FFFFFF&labelColor=red&logo=npm&logoColor=white&style=for-the-badge"/>
8
+ </a>
9
+ <a href="https://www.npmjs.com/package/@itsliaaa/baileys">
10
+ <img src="https://img.shields.io/npm/dm/@itsliaaa/baileys?color=FFFFFF&labelColor=red&logo=npm&logoColor=white&style=for-the-badge"/>
11
+ </a>
12
+ <a href="https://github.com/itsliaaa/baileys">
13
+ <img src="https://img.shields.io/github/stars/itsliaaa/baileys?color=FFFFFF&labelColor=black&logo=github&logoColor=white&style=for-the-badge"/>
14
+ </a>
15
+ <a href="LICENSE">
16
+ <img src="https://img.shields.io/badge/license-MIT-blue?labelColor=black&style=for-the-badge"/>
17
+ </a>
18
+ <a href="https://nodejs.org">
19
+ <img src="https://img.shields.io/badge/node-%3E%3D20-339933?logo=node.js&labelColor=green&logoColor=white&style=for-the-badge"/>
20
+ </a>
21
+ <a href="#">
22
+ <img src="https://img.shields.io/badge/ESM-only?logo=javascript&labelColor=yellow&logoColor=black&style=for-the-badge"/>
23
+ </a>
24
+ </p>
25
+
26
+ A lightweight fork of Baileys with practical fixes and small but meaningful improvements.
27
+
28
+ ### โœจ Highlights
29
+
30
+ This fork focuses on clarity and safety:
31
+
32
+ - ๐Ÿšซ No obfuscation. Easy to read and audit.
33
+ - ๐Ÿšซ No auto-follow channel (newsletter) behavior.
6
34
 
7
35
  ### โš™๏ธ Changes
8
36
 
@@ -10,12 +38,19 @@ A lightweight fork of Baileys with a few fixes and a small adjustment.
10
38
  - ๐Ÿ–ผ๏ธ Fixed an issue where media could not be sent to newsletters due to an upstream issue.
11
39
  - ๐Ÿ“ Reintroduced [`makeInMemoryStore`](#%EF%B8%8F-implementing-a-data-store) with a minimal ESM adaptation and small adjustments for Baileys v7.
12
40
  - ๐Ÿ“ฆ Switched FFmpeg execution from `exec` to `spawn` for safer process handling.
13
- - ๐Ÿ—ƒ๏ธ Added `@napi-rs/image` as a supported image processing backend in `getImageProcessingLibrary()`, offering a balance between performance and compatibility.
41
+ - ๐Ÿ—ƒ๏ธ Added [`@napi-rs/image`](https://www.npmjs.com/package/@napi-rs/image) as a supported image processing backend in [`getImageProcessingLibrary()`](#%EF%B8%8F-image-processing), offering a balance between performance and compatibility.
14
42
 
15
43
  #### ๐Ÿ“จ Message Handling & Compatibility
16
- - ๐Ÿ‘‰๐Ÿป Added support for sending [interactive message](#-sending-interactive-messages) types (button, list, interactive, template, carousel).
17
- - ๐Ÿ“ฉ Added support for [album messages](#%EF%B8%8F-album-image--video), [group status messages](#4%EF%B8%8Fโƒฃ-group-status), [status mention messages](#%EF%B8%8F-status-mention), [sticker pack messages](#-sticker-pack), and several [payment-related messages](#-sending-payment-messages) (request payment, payment invite, order, invoice).
18
- - ๐Ÿ“ฐ Simplified sending messages with ad thumbnails via [`externalAdReply`](#3%EF%B8%8Fโƒฃ-external-ad-reply) without requiring manual `contextInfo`.
44
+ - ๐Ÿ‘‰๐Ÿป Added support for sending [interactive messages](#-sending-interactive-messages) (buttons, lists, interactive, templates, carousel).
45
+ - ๐Ÿ“ฉ Expanded message support for:
46
+ - [album messages](#%EF%B8%8F-album-image--video)
47
+ - [group status messages](#4%EF%B8%8Fโƒฃ-group-status)
48
+ - [status mention messages](#%EF%B8%8F-status-mention)
49
+ - [sticker pack messages](#-sticker-pack)
50
+ - [messages with code blocks](#-message-with-code-block)
51
+ - [messages with tables](#-message-with-table)
52
+ - [payment-related messages](#-sending-payment-messages) (payment requests, invites, orders, invoices).
53
+ - ๐Ÿ“ฐ Simplified sending messages with ad thumbnails using [`externalAdReply`](#3%EF%B8%8Fโƒฃ-external-ad-reply), without requiring manual `contextInfo`.
19
54
 
20
55
  #### ๐Ÿงฉ Additional Message Options
21
56
  - ๐Ÿ‘๏ธ Added optional boolean flags for message handling:
@@ -370,6 +405,32 @@ sock.sendMessage([jidA, jidB, jidC], {
370
405
  })
371
406
  ```
372
407
 
408
+ ##### ๐Ÿงพ Message with Code Block
409
+
410
+ ```javascript
411
+ sock.sendMessage(jid, {
412
+ header: 'Example Usage',
413
+ code: 'console.log("Hello, World!")',
414
+ footer: 'Pretty simple, right?',
415
+ language: 'javascript'
416
+ })
417
+ ```
418
+
419
+ ##### ๐Ÿ“‹ Message with Table
420
+
421
+ ```javascript
422
+ sock.sendMessage(jid, {
423
+ header: 'Comparison between Node.js, Bun, and Deno',
424
+ title: 'Runtime Comparison',
425
+ table: [
426
+ ['', 'Node.js', 'Bun', 'Deno'],
427
+ ['Engine', 'V8 (C++)', 'JavaScriptCore (C++)', 'V8 (C++)'],
428
+ ['Performance', '4/5', '5/5', '4/5']
429
+ ],
430
+ footer: 'Does this help clarify the differences?'
431
+ })
432
+ ```
433
+
373
434
  #### ๐Ÿ“ Sending Media Messages
374
435
 
375
436
  > [!NOTE]
@@ -662,7 +723,7 @@ sock.sendMessage(jid, {
662
723
  caption: '๐Ÿ–ผ๏ธ Image 3',
663
724
  footer: '๐Ÿท๏ธ Pinterest',
664
725
  optionText: '๐Ÿ‘‰๐Ÿป Select Options',
665
- optionTitle: '๐Ÿ“„ Select Options',
726
+ optionTitle: '๐Ÿ‘‰๐Ÿป Select Options',
666
727
  offerText: '๐Ÿท๏ธ New Coupon!',
667
728
  offerCode: '@itsliaaa/baileys',
668
729
  offerUrl: 'https://www.npmjs.com/package/@itsliaaa/baileys',
@@ -959,6 +1020,62 @@ await sock.requestPairingCode(phoneNumber, customPairingCode)
959
1020
  console.log('๐Ÿ”— Pairing code', ':', customPairingCode)
960
1021
  ```
961
1022
 
1023
+ ##### ๐Ÿ–ผ๏ธ Image Processing
1024
+
1025
+ > [!NOTE]
1026
+ Automatically use available image processing library: `sharp`, `@napi-rs/image`, or `jimp`
1027
+
1028
+ ```javascript
1029
+ import { getImageProcessingLibrary } from '@itsliaaa/baileys'
1030
+ import { readFile } from 'fs/promises'
1031
+
1032
+ const lib = await getImageProcessingLibrary()
1033
+
1034
+ const bufferOrFilePath = './path/to/image.jpg'
1035
+ const width = 512
1036
+
1037
+ let output
1038
+
1039
+ // --- If sharp installed
1040
+ if (lib.sharp?.default) {
1041
+ const img = lib.sharp.default(bufferOrFilePath)
1042
+
1043
+ output = await img.resize(width)
1044
+ .jpeg({ quality: 80 })
1045
+ .toBuffer()
1046
+ }
1047
+
1048
+ // --- If @napi-rs/image installed
1049
+ else if (lib.image?.Transformer) {
1050
+ // --- Must in buffer format
1051
+ const inputBuffer = Buffer.isBuffer(bufferOrFilePath)
1052
+ ? bufferOrFilePath
1053
+ : await readFile(bufferOrFilePath)
1054
+
1055
+ const img = new lib.image.Transformer(inputBuffer)
1056
+
1057
+ output = await img.resize(width, undefined, 0)
1058
+ .jpeg(50)
1059
+ }
1060
+
1061
+ // --- If jimp installed
1062
+ else if (lib.jimp?.Jimp) {
1063
+ const img = await lib.jimp.Jimp.read(bufferOrFilePath)
1064
+
1065
+ output = await img
1066
+ .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
1067
+ .getBuffer('image/jpeg', { quality: 50 });
1068
+ }
1069
+
1070
+ // --- Fallback
1071
+ else {
1072
+ throw new Error('No image processing available')
1073
+ }
1074
+
1075
+ console.log('โœ… Process completed!')
1076
+ console.dir(output, { depth: null })
1077
+ ```
1078
+
962
1079
  ##### ๐Ÿ“ฃ Newsletter Management
963
1080
 
964
1081
  ```javascript
@@ -26,6 +26,7 @@ export const NOISE_MODE = 'Noise_XX_25519_AESGCM_SHA256\0\0\0\0';
26
26
  export const DICT_VERSION = 3;
27
27
  export const KEY_BUNDLE_TYPE = Buffer.from([5]);
28
28
  export const NOISE_WA_HEADER = Buffer.from([87, 65, 6, DICT_VERSION]); // last is "DICT_VERSION"
29
+ export const LEXER_REGEX = /(\/\/.*|\/\*[\s\S]*?\*\/|#.*)|("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`[\s\S]*?`)|(\b[a-zA-Z_]\w*\b)(?=\s*\()|(\b[a-zA-Z_]\w*\b)|(\b\d+(?:\.\d+)?\b)|(\s+|[^\w\s]+)/g;
29
30
  /** from: https://stackoverflow.com/questions/3809401/what-is-a-good-regular-expression-to-match-a-url */
30
31
  export const URL_REGEX = /https:\/\/(?![^:@\/\s]+:[^:@\/\s]+@)[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:\d+)?(\/[^\s]*)?/g;
31
32
  export const WA_CERT_DETAILS = {
@@ -140,4 +141,10 @@ export const TimeMs = {
140
141
  Hour: 60 * 60 * 1000,
141
142
  Day: 24 * 60 * 60 * 1000,
142
143
  Week: 7 * 24 * 60 * 60 * 1000
144
+ };
145
+ export const FORWARDED_AI_BOT_INFO = {
146
+ isForwarded: true,
147
+ forwardingScore: 1,
148
+ forwardedAiBotMessageInfo: { botJid: '867051314767696@bot' },
149
+ forwardOrigin: 4
143
150
  };
@@ -171,6 +171,10 @@ export function makeLibSignalRepository(auth, logger, pnToLIDFunc) {
171
171
  await auth.keys.set({ session: sessionUpdates });
172
172
  }, `delete-${jids.length}-sessions`);
173
173
  },
174
+ close () {
175
+ migratedSessionCache.clear();
176
+ lidMapping.close();
177
+ },
174
178
  async migrateSession(fromJid, toJid) {
175
179
  // TODO: use usync to handle this entire mess
176
180
  if (!fromJid || (!isLidUser(toJid) && !isHostedLidUser(toJid)))
@@ -267,4 +267,10 @@ export class LIDMappingStore {
267
267
  }
268
268
  return Object.values(successfulPairs).length ? Object.values(successfulPairs) : null;
269
269
  }
270
+ /**
271
+ * Close the cache and release resources
272
+ */
273
+ close() {
274
+ this.mappingCache.clear();
275
+ }
270
276
  }
@@ -17,7 +17,7 @@ const USER_ID_CACHE = new Map();
17
17
  export const makeChatsSocket = (config) => {
18
18
  const { logger, markOnlineOnConnect, fireInitQueries, appStateMacVerification, shouldIgnoreJid, shouldSyncHistoryMessage, getMessage } = config;
19
19
  const sock = makeSocket(config);
20
- const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession } = sock;
20
+ const { ev, ws, authState, generateMessageTag, sendNode, query, signalRepository, onUnexpectedError, sendUnifiedSession, registerSocketEndHandler } = sock;
21
21
  let privacySettings;
22
22
  let syncState = SyncState.Connecting;
23
23
  /** this mutex ensures that messages are processed in order */
@@ -35,9 +35,9 @@ export const makeChatsSocket = (config) => {
35
35
  stdTTL: DEFAULT_CACHE_TTLS.MSG_RETRY, // 1 hour
36
36
  useClones: false
37
37
  });
38
- if (!config.placeholderResendCache) {
39
- config.placeholderResendCache = placeholderResendCache;
40
- }
38
+ // if (!config.placeholderResendCache) {
39
+ // config.placeholderResendCache = placeholderResendCache;
40
+ // }
41
41
  /** helper function to fetch the given app state sync key */
42
42
  const getAppStateSyncKey = async (keyId) => {
43
43
  const { [keyId]: key } = await authState.keys.get('app-state-sync-key', [keyId]);
@@ -997,6 +997,17 @@ export const makeChatsSocket = (config) => {
997
997
  logger.warn({ lid, pn, error }, 'Failed to store LID-PN mapping');
998
998
  }
999
999
  });
1000
+ registerSocketEndHandler(() => {
1001
+ if (awaitingSyncTimeout) {
1002
+ clearTimeout(awaitingSyncTimeout);
1003
+ awaitingSyncTimeout = undefined;
1004
+ }
1005
+ if (!config.placeholderResendCache && placeholderResendCache.close) {
1006
+ placeholderResendCache.close();
1007
+ }
1008
+ syncState = SyncState.Connecting;
1009
+ privacySettings = undefined;
1010
+ });
1000
1011
  return {
1001
1012
  ...sock,
1002
1013
  createCallLink,
@@ -15,7 +15,7 @@ import { makeMessagesSocket } from './messages-send.js';
15
15
  export const makeMessagesRecvSocket = (config) => {
16
16
  const { logger, retryRequestDelayMs, maxMsgRetryCount, getMessage, shouldIgnoreJid, enableAutoSessionRecreation } = config;
17
17
  const sock = makeMessagesSocket(config);
18
- const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, generateMessageTag, messageRetryManager } = sock;
18
+ const { ev, authState, ws, messageMutex, notificationMutex, receiptMutex, signalRepository, query, upsertMessage, resyncAppState, onUnexpectedError, assertSessions, sendNode, relayMessage, sendReceipt, uploadPreKeys, sendPeerDataOperationMessage, generateMessageTag, messageRetryManager, registerSocketEndHandler } = sock;
19
19
  /** this mutex ensures that each retryRequest will wait for the previous one to finish */
20
20
  const retryMutex = makeMutex();
21
21
  const devicesMutex = makeMutex();
@@ -1449,6 +1449,19 @@ export const makeMessagesRecvSocket = (config) => {
1449
1449
  logger.trace(`sendActiveReceipts set to "${sendActiveReceipts}"`);
1450
1450
  }
1451
1451
  });
1452
+ registerSocketEndHandler(() => {
1453
+ if (!config.msgRetryCounterCache && msgRetryCache.close) {
1454
+ msgRetryCache.close();
1455
+ }
1456
+ if (!config.callOfferCache && callOfferCache.close) {
1457
+ callOfferCache.close();
1458
+ }
1459
+ if (!config.placeholderResendCache && placeholderResendCache.close) {
1460
+ placeholderResendCache.close();
1461
+ }
1462
+ identityAssertDebounce.close();
1463
+ sendActiveReceipts = false;
1464
+ });
1452
1465
  return {
1453
1466
  ...sock,
1454
1467
  sendMessageAck,
@@ -14,7 +14,7 @@ import { makeNewsletterSocket } from './newsletter.js';
14
14
  export const makeMessagesSocket = (config) => {
15
15
  const { logger, linkPreviewImageThumbnailWidth, generateHighQualityLinkPreview, options: httpRequestOptions, patchMessageBeforeSending, cachedGroupMetadata, enableRecentMessageCache, maxMsgRetryCount } = config;
16
16
  const sock = makeNewsletterSocket(config);
17
- const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral } = sock;
17
+ const { ev, authState, messageMutex, signalRepository, upsertMessage, query, fetchPrivacySettings, sendNode, groupMetadata, groupToggleEphemeral, registerSocketEndHandler } = sock;
18
18
  const userDevicesCache = config.userDevicesCache ??=
19
19
  new NodeCache({
20
20
  stdTTL: DEFAULT_CACHE_TTLS.USER_DEVICES, // 5 minutes
@@ -939,6 +939,18 @@ export const makeMessagesSocket = (config) => {
939
939
  };
940
940
  const waUploadToServer = getWAUploadToServer(config, refreshMediaConn);
941
941
  const waitForMsgMediaUpdate = bindWaitForEvent(ev, 'messages.media-update');
942
+ registerSocketEndHandler(() => {
943
+ if (!config.userDevicesCache && userDevicesCache.close) {
944
+ userDevicesCache.close();
945
+ }
946
+ if (peerSessionsCache.close) {
947
+ peerSessionsCache.close();
948
+ }
949
+ mediaConn = undefined;
950
+ if (messageRetryManager) {
951
+ messageRetryManager.clear();
952
+ }
953
+ });
942
954
  return {
943
955
  ...sock,
944
956
  getPrivacyTokens,
@@ -269,6 +269,7 @@ export const makeSocket = (config) => {
269
269
  let keepAliveReq;
270
270
  let qrTimer;
271
271
  let closed = false;
272
+ const socketEndHandlers = [];
272
273
  /** log & process any unexpected errors */
273
274
  const onUnexpectedError = (err, msg) => {
274
275
  logger.error({ err }, `unexpected error in '${msg}'`);
@@ -485,12 +486,21 @@ export const makeSocket = (config) => {
485
486
  ws.removeAllListeners('close');
486
487
  ws.removeAllListeners('open');
487
488
  ws.removeAllListeners('message');
489
+ signalRepository.close?.();
488
490
  if (!ws.isClosed && !ws.isClosing) {
489
491
  try {
490
492
  await ws.close();
491
493
  }
492
494
  catch { }
493
495
  }
496
+ for (const handler of socketEndHandlers) {
497
+ try {
498
+ handler(error);
499
+ }
500
+ catch (err) {
501
+ logger.error({ err }, 'error in socket end handler');
502
+ }
503
+ }
494
504
  ev.emit('connection.update', {
495
505
  connection: 'close',
496
506
  lastDisconnect: {
@@ -499,6 +509,7 @@ export const makeSocket = (config) => {
499
509
  }
500
510
  });
501
511
  ev.removeAllListeners('connection.update');
512
+ ev.destroy();
502
513
  };
503
514
  const waitForSocketOpen = async () => {
504
515
  if (ws.isOpen) {
@@ -890,6 +901,9 @@ export const makeSocket = (config) => {
890
901
  logger.debug({ error }, 'failed to send unified_session telemetry');
891
902
  }
892
903
  };
904
+ const registerSocketEndHandler = (handler) => {
905
+ socketEndHandlers.push(handler);
906
+ }
893
907
  return {
894
908
  type: 'md',
895
909
  ws,
@@ -907,6 +921,7 @@ export const makeSocket = (config) => {
907
921
  sendNode,
908
922
  logout,
909
923
  end,
924
+ registerSocketEndHandler,
910
925
  onUnexpectedError,
911
926
  uploadPreKeys,
912
927
  uploadPreKeysToServerIfRequired,
@@ -1,6 +1,5 @@
1
1
  /**
2
- * Lia@Note 03-02-26
3
- * --- [WIP]
2
+ * Lia@Note 03-02-26 [WIP]
4
3
  * Manually converted to ESM and modified by me
5
4
  * Originally based on @whiskeysockets/baileys v6.7.16
6
5
  * Minor adjustments for compatibility with baileys v7 (โ ใคโ โ‰งโ โ–ฝโ โ‰ฆโ )โ ใค
@@ -0,0 +1,22 @@
1
+ export var CodeHighlightType;
2
+ (function (CodeHighlightType) {
3
+ CodeHighlightType[CodeHighlightType["DEFAULT"] = 0] = "DEFAULT";
4
+ CodeHighlightType[CodeHighlightType["KEYWORD"] = 1] = "KEYWORD";
5
+ CodeHighlightType[CodeHighlightType["METHOD"] = 2] = "METHOD";
6
+ CodeHighlightType[CodeHighlightType["STRING"] = 3] = "STRING";
7
+ CodeHighlightType[CodeHighlightType["NUMBER"] = 4] = "NUMBER";
8
+ CodeHighlightType[CodeHighlightType["COMMENT"] = 5] = "COMMENT";
9
+ })(CodeHighlightType || (CodeHighlightType = {}));
10
+ export var RichSubMessageType;
11
+ (function (RichSubMessageType) {
12
+ RichSubMessageType[RichSubMessageType["UNKNOWN"] = 0] = "UNKNOWN";
13
+ RichSubMessageType[RichSubMessageType["GRID_IMAGE"] = 1] = "GRID_IMAGE";
14
+ RichSubMessageType[RichSubMessageType["TEXT"] = 2] = "TEXT";
15
+ RichSubMessageType[RichSubMessageType["INLINE_IMAGE"] = 3] = "INLINE_IMAGE";
16
+ RichSubMessageType[RichSubMessageType["TABLE"] = 4] = "TABLE";
17
+ RichSubMessageType[RichSubMessageType["CODE"] = 5] = "CODE";
18
+ RichSubMessageType[RichSubMessageType["DYNAMIC"] = 6] = "DYNAMIC";
19
+ RichSubMessageType[RichSubMessageType["MAP"] = 7] = "MAP";
20
+ RichSubMessageType[RichSubMessageType["LATEX"] = 8] = "LATEX";
21
+ RichSubMessageType[RichSubMessageType["CONTENT_ITEMS"] = 9] = "CONTENT_ITEMS";
22
+ })(RichSubMessageType || (RichSubMessageType = {}));
@@ -169,7 +169,23 @@ export const makeEventBuffer = (logger) => {
169
169
  },
170
170
  on: (...args) => ev.on(...args),
171
171
  off: (...args) => ev.off(...args),
172
- removeAllListeners: (...args) => ev.removeAllListeners(...args)
172
+ removeAllListeners: (...args) => ev.removeAllListeners(...args),
173
+ destroy() {
174
+ // Clear buffer timeout
175
+ if (bufferTimeout) {
176
+ clearTimeout(bufferTimeout);
177
+ bufferTimeout = null;
178
+ }
179
+ // Clear history cache
180
+ historyCache.clear();
181
+ // Reset buffer data
182
+ data = makeBufferData();
183
+ isBuffering = false;
184
+ bufferCount = 0;
185
+ // Remove all listeners
186
+ ev.removeAllListeners();
187
+ logger.debug('Event buffer destroyed');
188
+ }
173
189
  };
174
190
  };
175
191
  const makeBufferData = () => {
@@ -2,6 +2,7 @@ export * from './generics.js';
2
2
  export * from './decode-wa-message.js';
3
3
  export * from './messages.js';
4
4
  export * from './messages-media.js';
5
+ export * from './rich-message-utils.js';
5
6
  export * from './validate-connection.js';
6
7
  export * from './crypto.js';
7
8
  export * from './signal.js';
@@ -210,6 +210,23 @@ export class MessageRetryManager {
210
210
  this.logger.debug(`Cancelled pending phone request for message ${messageId}`);
211
211
  }
212
212
  }
213
+ clear() {
214
+ this.recentMessagesMap.clear();
215
+ this.messageKeyIndex.clear();
216
+ this.sessionRecreateHistory.clear();
217
+ this.retryCounters.clear();
218
+ for (const messageId of Object.keys(this.pendingPhoneRequests)) {
219
+ this.cancelPendingPhoneRequest(messageId);
220
+ }
221
+ this.statistics = {
222
+ totalRetries: 0,
223
+ successfulRetries: 0,
224
+ failedRetries: 0,
225
+ mediaRetries: 0,
226
+ sessionRecreations: 0,
227
+ phoneRequests: 0
228
+ };
229
+ }
213
230
  keyToString(key) {
214
231
  return `${key.to}${MESSAGE_KEY_SEPARATOR}${key.id}`;
215
232
  }
@@ -10,6 +10,7 @@ import { isPnUser, isLidUser, isJidGroup, isJidNewsletter, isJidStatusBroadcast,
10
10
  import { sha256 } from './crypto.js';
11
11
  import { generateMessageIDV2, getKeyAuthor, unixTimestampSeconds } from './generics.js';
12
12
  import { downloadContentFromMessage, encryptedStream, generateThumbnail, getAudioDuration, getAudioWaveform, getImageProcessingLibrary, getRawMediaUploadData, getStream, toBuffer } from './messages-media.js';
13
+ import { generateRichCodeBlock, generateRichTable, wrapToBotForwardedMessage } from './rich-message-utils.js';
13
14
  import { shouldIncludeReportingToken } from './reporting-utils.js';
14
15
  const MIMETYPE_MAP = {
15
16
  image: 'image/jpeg',
@@ -692,7 +693,7 @@ export const generateWAMessageContent = async (message, options) => {
692
693
  // Lia@Changes 30-01-26 --- Add "raw" boolean to send raw messages directly via generateWAMessage()
693
694
  if (hasNonNullishProperty(message, 'raw')) {
694
695
  delete message.raw;
695
- return proto.Message.create(message);
696
+ return message;
696
697
  }
697
698
  else if (hasNonNullishProperty(message, 'text')) {
698
699
  const extContent = { text: message.text };
@@ -726,27 +727,25 @@ export const generateWAMessageContent = async (message, options) => {
726
727
  m.extendedTextMessage = extContent;
727
728
  }
728
729
  else if (hasNonNullishProperty(message, 'contacts')) {
729
- const { contacts } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
730
- const contactLen = contacts.contacts.length;
730
+ const contactLen = message.contacts.contacts.length;
731
731
  if (!contactLen) {
732
732
  throw new Boom('require atleast 1 contact', { statusCode: 400 });
733
733
  }
734
734
  if (contactLen === 1) {
735
- m.contactMessage = contacts.contacts[0];
735
+ m.contactMessage = message.contacts.contacts[0];
736
736
  }
737
737
  else {
738
- m.contactsArrayMessage = contacts;
738
+ m.contactsArrayMessage = message.contacts;
739
739
  }
740
740
  }
741
741
  else if (hasNonNullishProperty(message, 'location')) {
742
742
  m.locationMessage = message.location;
743
743
  }
744
744
  else if (hasNonNullishProperty(message, 'react')) {
745
- const { react } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
746
- if (!react.senderTimestampMs) {
747
- react.senderTimestampMs = Date.now();
745
+ if (!message.react.senderTimestampMs) {
746
+ message.react.senderTimestampMs = Date.now();
748
747
  }
749
- m.reactionMessage = react;
748
+ m.reactionMessage = message.react;
750
749
  }
751
750
  else if (hasNonNullishProperty(message, 'delete')) {
752
751
  m.protocolMessage = {
@@ -758,26 +757,24 @@ export const generateWAMessageContent = async (message, options) => {
758
757
  m = generateForwardMessageContent(message.forward, message.force);
759
758
  }
760
759
  else if (hasNonNullishProperty(message, 'disappearingMessagesInChat')) {
761
- const { disappearingMessagesInChat } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
762
- const exp = typeof disappearingMessagesInChat === 'boolean'
763
- ? disappearingMessagesInChat
760
+ const exp = typeof message.disappearingMessagesInChat === 'boolean'
761
+ ? message.disappearingMessagesInChat
764
762
  ? WA_DEFAULT_EPHEMERAL
765
763
  : 0
766
- : disappearingMessagesInChat;
764
+ : message.disappearingMessagesInChat;
767
765
  m = prepareDisappearingMessageSettingContent(exp);
768
766
  }
769
767
  else if (hasNonNullishProperty(message, 'groupInvite')) {
770
- const { groupInvite } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
771
768
  m.groupInviteMessage = {};
772
- m.groupInviteMessage.inviteCode = groupInvite.inviteCode;
773
- m.groupInviteMessage.inviteExpiration = groupInvite.inviteExpiration;
774
- m.groupInviteMessage.caption = groupInvite.text;
775
- m.groupInviteMessage.groupJid = groupInvite.jid;
776
- m.groupInviteMessage.groupName = groupInvite.subject;
769
+ m.groupInviteMessage.inviteCode = message.groupInvite.inviteCode;
770
+ m.groupInviteMessage.inviteExpiration = message.groupInvite.inviteExpiration;
771
+ m.groupInviteMessage.caption = message.groupInvite.text;
772
+ m.groupInviteMessage.groupJid = message.groupInvite.jid;
773
+ m.groupInviteMessage.groupName = message.groupInvite.subject;
777
774
  //TODO: use built-in interface and get disappearing mode info etc.
778
775
  //TODO: cache / use store!?
779
776
  if (options.getProfilePicUrl) {
780
- const pfpUrl = await options.getProfilePicUrl(groupInvite.jid, 'preview');
777
+ const pfpUrl = await options.getProfilePicUrl(message.groupInvite.jid, 'preview');
781
778
  if (pfpUrl) {
782
779
  const resp = await fetch(pfpUrl, { method: 'GET', dispatcher: options?.options?.dispatcher });
783
780
  if (resp.ok) {
@@ -805,47 +802,44 @@ export const generateWAMessageContent = async (message, options) => {
805
802
  m.keepInChatMessage.timestampMs = Date.now();
806
803
  }
807
804
  else if (hasNonNullishProperty(message, 'flowReply')) {
808
- const { flowReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
809
805
  m.interactiveResponseMessage = {
810
806
  body: {
811
- format: flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
812
- text: flowReply.text
807
+ format: message.flowReply.format || proto.Message.InteractiveResponseMessage.Body.Format.DEFAULT,
808
+ text: message.flowReply.text
813
809
  },
814
810
  nativeFlowResponseMessage: {
815
- name: flowReply.name,
816
- paramsJson: flowReply.paramsJson || '{}',
817
- version: flowReply.version || 1
811
+ name: message.flowReply.name,
812
+ paramsJson: message.flowReply.paramsJson || '{}',
813
+ version: message.flowReply.version || 1
818
814
  }
819
815
  };
820
816
  }
821
817
  else if (hasNonNullishProperty(message, 'buttonReply')) {
822
- const { buttonReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
823
818
  switch (message.type) {
824
819
  case 'template':
825
820
  m.templateButtonReplyMessage = {
826
- selectedDisplayText: buttonReply.displayText,
827
- selectedId: buttonReply.id,
828
- selectedIndex: buttonReply.index
821
+ selectedDisplayText: message.buttonReply.displayText,
822
+ selectedId: message.buttonReply.id,
823
+ selectedIndex: message.buttonReply.index
829
824
  };
830
825
  break;
831
826
  case 'plain':
832
827
  m.buttonsResponseMessage = {
833
- selectedButtonId: buttonReply.id,
834
- selectedDisplayText: buttonReply.displayText,
828
+ selectedButtonId: message.buttonReply.id,
829
+ selectedDisplayText: message.buttonReply.displayText,
835
830
  type: proto.Message.ButtonsResponseMessage.Type.DISPLAY_TEXT
836
831
  };
837
832
  break;
838
833
  }
839
834
  }
840
835
  else if (hasNonNullishProperty(message, 'listReply')) {
841
- const { listReply } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
842
836
  m.listResponseMessage = {
843
- description: listReply.description,
837
+ description: message.listReply.description,
844
838
  listType: proto.Message.ListResponseMessage.ListType.SINGLE_SELECT,
845
839
  singleSelectReply: {
846
- selectedRowId: listReply.id
840
+ selectedRowId: message.listReply.id
847
841
  },
848
- title: listReply.title
842
+ title: message.listReply.title
849
843
  };
850
844
  }
851
845
  else if (hasOptionalProperty(message, 'ptv') && message.ptv) {
@@ -856,68 +850,66 @@ export const generateWAMessageContent = async (message, options) => {
856
850
  m.productMessage = await prepareProductMessage(message, options);
857
851
  }
858
852
  else if (hasNonNullishProperty(message, 'event')) {
859
- const { event } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
860
853
  m.eventMessage = {};
861
- const startTime = Math.floor(event.startDate.getTime() / 1000);
862
- if (event.call && options.getCallLink) {
863
- const token = await options.getCallLink(event.call, { startTime });
864
- m.eventMessage.joinLink = (event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
854
+ const startTime = Math.floor(message.event.startDate.getTime() / 1000);
855
+ if (message.event.call && options.getCallLink) {
856
+ const token = await options.getCallLink(message.event.call, { startTime });
857
+ m.eventMessage.joinLink = (message.event.call === 'audio' ? CALL_AUDIO_PREFIX : CALL_VIDEO_PREFIX) + token;
865
858
  }
866
859
  m.messageContextInfo = {
867
860
  // encKey
868
- messageSecret: event.messageSecret || randomBytes(32)
861
+ messageSecret: message.event.messageSecret || randomBytes(32)
869
862
  };
870
- m.eventMessage.name = event.name;
871
- m.eventMessage.description = event.description;
863
+ m.eventMessage.name = message.event.name;
864
+ m.eventMessage.description = message.event.description;
872
865
  m.eventMessage.startTime = startTime;
873
- m.eventMessage.endTime = event.endDate ? event.endDate.getTime() / 1000 : undefined;
874
- m.eventMessage.isCanceled = event.isCancelled ?? false;
875
- m.eventMessage.extraGuestsAllowed = event.extraGuestsAllowed;
876
- m.eventMessage.isScheduleCall = event.isScheduleCall ?? false;
877
- m.eventMessage.location = event.location;
866
+ m.eventMessage.endTime = message.event.endDate ? message.event.endDate.getTime() / 1000 : undefined;
867
+ m.eventMessage.isCanceled = message.event.isCancelled ?? false;
868
+ m.eventMessage.extraGuestsAllowed = message.event.extraGuestsAllowed;
869
+ m.eventMessage.isScheduleCall = message.event.isScheduleCall ?? false;
870
+ m.eventMessage.location = message.event.location;
878
871
  }
879
872
  else if (hasNonNullishProperty(message, 'poll')) {
880
- const { poll } = message; // Lia@Changes 04-02-26 --- Destructured for readability & cleaner access (โ โœทโ โ€ฟโ โœทโ )
881
- (_a = poll).selectableCount || (_a.selectableCount = 0);
882
- (_b = poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
883
- if (!Array.isArray(poll.values)) {
873
+ (_a = message.poll).selectableCount || (_a.selectableCount = 0);
874
+ (_b = message.poll).toAnnouncementGroup || (_b.toAnnouncementGroup = false);
875
+ if (!Array.isArray(message.poll.values)) {
884
876
  throw new Boom('Invalid poll values', { statusCode: 400 });
885
877
  }
886
- if (poll.selectableCount < 0 || poll.selectableCount > poll.values.length) {
887
- throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${poll.values.length}`, {
878
+ if (message.poll.selectableCount < 0 || message.poll.selectableCount > message.poll.values.length) {
879
+ throw new Boom(`poll.selectableCount in poll should be >= 0 and <= ${message.poll.values.length}`, {
888
880
  statusCode: 400
889
881
  });
890
882
  }
891
883
  m.messageContextInfo = {
892
884
  // encKey
893
- messageSecret: poll.messageSecret || randomBytes(32)
885
+ messageSecret: message.poll.messageSecret || randomBytes(32)
894
886
  };
895
887
  const pollCreationMessage = {
896
- name: poll.name,
897
- selectableOptionsCount: poll.selectableCount,
898
- options: poll.values.map(optionName => ({ optionName }))
888
+ name: message.poll.name,
889
+ selectableOptionsCount: message.poll.selectableCount,
890
+ options: message.poll.values.map(optionName => ({ optionName }))
899
891
  };
900
- if (poll.toAnnouncementGroup) {
892
+ if (message.poll.toAnnouncementGroup) {
901
893
  // poll v2 is for community announcement groups (single select and multiple)
902
894
  m.pollCreationMessageV2 = pollCreationMessage;
903
895
  }
904
896
  else {
905
897
  // Lia@Changes 08-02-26 --- Add quiz message support
906
- if (poll.pollType === 1) {
907
- if (!poll.correctAnswer) {
898
+ if (message.poll.pollType == 1) {
899
+ if (!message.poll.correctAnswer) {
908
900
  throw new Boom('No "correctAnswer" provided for quiz', { statusCode: 400 });
909
901
  }
910
902
  m.pollCreationMessageV5 = {
911
903
  // Lia@Note 08-02-26 --- quiz for newsletter only
912
904
  ...pollCreationMessage,
913
905
  correctAnswer: {
914
- optionName: poll.correctAnswer.toString()
906
+ optionName: message.poll.correctAnswer.toString()
915
907
  },
916
- pollType: poll.pollType,
908
+ pollType: message.poll.pollType,
917
909
  selectableOptionsCount: 1
918
910
  };
919
911
  }
920
- else if (poll.selectableCount === 1) {
912
+ else if (message.poll.selectableCount === 1) {
921
913
  //poll v3 is for single select polls
922
914
  m.pollCreationMessageV3 = pollCreationMessage;
923
915
  }
@@ -929,15 +921,14 @@ export const generateWAMessageContent = async (message, options) => {
929
921
  }
930
922
  // Lia@Changes 08-02-26 --- Add poll result snapshot message
931
923
  else if (hasNonNullishProperty(message, 'pollResult')) {
932
- const { pollResult } = message;
933
924
  const pollResultSnapshotMessage = {
934
- name: pollResult.name,
935
- pollVotes: pollResult.votes.map(vote => ({
925
+ name: message.pollResult.name,
926
+ pollVotes: message.pollResult.votes.map(vote => ({
936
927
  optionName: vote.name,
937
928
  optionVoteCount: parseInt(vote.voteCount)
938
929
  }))
939
930
  };
940
- if (pollResult.pollType === 1) {
931
+ if (message.pollResult.pollType == 1) {
941
932
  pollResultSnapshotMessage.pollType = proto.Message.PollType.QUIZ;
942
933
  m.pollResultSnapshotMessageV3 = pollResultSnapshotMessage;
943
934
  }
@@ -948,18 +939,17 @@ export const generateWAMessageContent = async (message, options) => {
948
939
  }
949
940
  // Lia@Changes 08-02-26 --- Add poll update message
950
941
  else if (hasNonNullishProperty(message, 'pollUpdate')) {
951
- const { pollUpdate } = message;
952
- if (!pollUpdate.key) {
942
+ if (!message.pollUpdate.key) {
953
943
  throw new Boom('Message key is required', { statusCode: 400 });
954
944
  }
955
- if (!pollUpdate.vote) {
945
+ if (!message.pollUpdate.vote) {
956
946
  throw new Boom('Encrypted vote payload is required', { statusCode: 400 });
957
947
  }
958
948
  m.pollUpdateMessage = {
959
- metadata: pollUpdate.metadata,
960
- pollCreationMessageKey: pollUpdate.key,
949
+ metadata: message.pollUpdate.metadata,
950
+ pollCreationMessageKey: message.pollUpdate.key,
961
951
  senderTimestampMs: Date.now(),
962
- vote: pollUpdate.vote
952
+ vote: message.pollUpdate.vote
963
953
  };
964
954
  }
965
955
  else if (hasNonNullishProperty(message, 'sharePhoneNumber')) {
@@ -1009,17 +999,16 @@ export const generateWAMessageContent = async (message, options) => {
1009
999
  }
1010
1000
  // Lia@Changes 31-01-26 --- Add support for album messages
1011
1001
  else if (hasNonNullishProperty(message, 'album')) {
1012
- const { album } = message;
1013
- if (!Array.isArray(album)) {
1002
+ if (!Array.isArray(message.album)) {
1014
1003
  throw new Boom('Invalid album type. Expected an array.', { statusCode: 400 });
1015
1004
  }
1016
1005
  let videoCount = 0;
1017
- for (let i = 0; i < album.length; i++) {
1018
- if (album[i].video) videoCount++;
1006
+ for (let i = 0; i < message.album.length; i++) {
1007
+ if (message.album[i].video) videoCount++;
1019
1008
  };
1020
1009
  let imageCount = 0;
1021
- for (let i = 0; i < album.length; i++) {
1022
- if (album[i].image) imageCount++;
1010
+ for (let i = 0; i < message.album.length; i++) {
1011
+ if (message.album[i].image) imageCount++;
1023
1012
  };
1024
1013
  if ((videoCount + imageCount) < 2) {
1025
1014
  throw new Boom('Minimum provide 2 media to upload album message', { statusCode: 400 });
@@ -1029,6 +1018,13 @@ export const generateWAMessageContent = async (message, options) => {
1029
1018
  expectedVideoCount: videoCount
1030
1019
  };
1031
1020
  }
1021
+ // Lia@Changes 09-04-26 --- Add support for code block and table
1022
+ else if (hasNonNullishProperty(message, 'code')) {
1023
+ m.richResponseMessage = generateRichCodeBlock(message);
1024
+ }
1025
+ else if (hasNonNullishProperty(message, 'table')) {
1026
+ m.richResponseMessage = generateRichTable(message);
1027
+ }
1032
1028
  else {
1033
1029
  m = await prepareWAMessageMedia(message, options);
1034
1030
  }
@@ -1330,8 +1326,9 @@ export const generateWAMessageContent = async (message, options) => {
1330
1326
  ...content,
1331
1327
  body: content.body,
1332
1328
  mediaType: content.mediaType || 1,
1333
- mediaUrl: content.url + '?update=' + Date.now(),
1329
+ mediaUrl: content.url,
1334
1330
  renderLargerThumbnail: content.largeThumbnail,
1331
+ sourceUrl: content.url + '?update=' + Date.now(),
1335
1332
  thumbnail: content.thumbnail,
1336
1333
  thumbnailUrl: content.url,
1337
1334
  title: content.title || LIBRARY_NAME
@@ -1419,6 +1416,10 @@ export const generateWAMessageContent = async (message, options) => {
1419
1416
  m = { viewOnceMessageV2Extension: { message: m } };
1420
1417
  delete message.viewOnceV2Extension;
1421
1418
  }
1419
+ // Lia@Note 09-04-26 --- Wrap richResponseMessage into botForwardedMessage
1420
+ if (!!m.richResponseMessage) {
1421
+ m = wrapToBotForwardedMessage(m)
1422
+ }
1422
1423
  if (hasOptionalProperty(message, 'edit')) {
1423
1424
  m = {
1424
1425
  protocolMessage: {
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Lia@Changes 09-04-26 [WIP]
3
+ * Add support for tables and code blocks in richResponseMessage (wrapped in botForwardedMessage)
4
+ */
5
+ import { FORWARDED_AI_BOT_INFO, LEXER_REGEX } from '../Defaults/index.js';
6
+ import { CodeHighlightType } from '../Types/RichType.js';
7
+ const CPP_KEYWORDS = new Set([
8
+ 'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor', 'bool', 'break', 'case',
9
+ 'catch', 'char', 'class', 'compl', 'concept', 'const', 'consteval', 'constexpr', 'constinit',
10
+ 'const_cast', 'continue', 'co_await', 'co_return', 'co_yield', 'decltype', 'default', 'delete',
11
+ 'do', 'double', 'dynamic_cast', 'else', 'enum', 'explicit', 'export', 'extern', 'false', 'float',
12
+ 'for', 'friend', 'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new', 'noexcept',
13
+ 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq', 'private', 'protected', 'public', 'register',
14
+ 'reinterpret_cast', 'requires', 'return', 'short', 'signed', 'sizeof', 'static', 'static_assert',
15
+ 'static_cast', 'struct', 'switch', 'template', 'this', 'thread_local', 'throw', 'true', 'try',
16
+ 'typedef', 'typeid', 'typename', 'union', 'unsigned', 'using', 'virtual', 'void', 'volatile',
17
+ 'wchar_t', 'while', 'xor', 'xor_eq'
18
+ ]);
19
+ const CSS_KEYWORDS = new Set([
20
+ 'import', 'media', 'font-face', 'keyframes', 'supports', 'charset',
21
+ 'important', 'root', 'hover', 'active', 'focus', 'visited', 'before', 'after',
22
+ 'not', 'nth-child', 'first-child', 'last-child', 'only-child',
23
+ 'none', 'inherit', 'initial', 'unset', 'auto', 'transparent', 'currentcolor'
24
+ ]);
25
+ const GO_KEYWORDS = new Set([
26
+ 'break', 'default', 'func', 'interface', 'select', 'case', 'defer', 'go', 'map', 'struct',
27
+ 'chan', 'else', 'goto', 'package', 'switch', 'const', 'fallthrough', 'if', 'range', 'type',
28
+ 'continue', 'for', 'import', 'return', 'var', 'true', 'false', 'nil'
29
+ ]);
30
+ const HTML_KEYWORDS = new Set([
31
+ 'html', 'head', 'body', 'title', 'meta', 'link', 'script', 'style',
32
+ 'header', 'footer', 'main', 'section', 'article', 'aside', 'nav',
33
+ 'div', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'a', 'img',
34
+ 'ul', 'ol', 'li', 'table', 'tr', 'td', 'th', 'thead', 'tbody',
35
+ 'form', 'input', 'button', 'select', 'textarea', 'label', 'option',
36
+ 'canvas', 'svg', 'iframe', 'video', 'audio', 'source'
37
+ ]);
38
+ const JS_KEYWORDS = new Set([
39
+ 'import', 'export', 'from', 'default', 'as',
40
+ 'const', 'let', 'var', 'function', 'class', 'extends', 'new',
41
+ 'return', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue',
42
+ 'try', 'catch', 'finally', 'throw',
43
+ 'async', 'await', 'yield',
44
+ 'typeof', 'instanceof', 'in', 'of', 'delete', 'void',
45
+ 'true', 'false', 'null', 'undefined', 'NaN', 'Infinity',
46
+ 'this', 'super', 'static', 'get', 'set',
47
+ 'debugger', 'with'
48
+ ]);
49
+ const PYTHON_KEYWORDS = new Set([
50
+ 'import', 'from', 'as', 'def', 'class', 'return', 'if', 'elif', 'else',
51
+ 'for', 'while', 'break', 'continue', 'try', 'except', 'finally', 'raise',
52
+ 'with', 'yield', 'lambda', 'pass', 'del', 'global', 'nonlocal', 'assert',
53
+ 'True', 'False', 'None', 'and', 'or', 'not', 'in', 'is', 'async', 'await',
54
+ 'self', 'print'
55
+ ]);
56
+ const RUST_KEYWORDS = new Set([
57
+ 'as', 'break', 'const', 'continue', 'crate', 'else', 'enum', 'extern', 'false', 'fn', 'for',
58
+ 'if', 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return',
59
+ 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use',
60
+ 'where', 'while', 'async', 'await', 'dyn', 'abstract', 'become', 'box', 'do', 'final',
61
+ 'macro', 'override', 'priv', 'typeof', 'unsized', 'virtual', 'yield'
62
+ ]);
63
+ const NOOP = new Set([])
64
+ export const LANGUAGE_KEYWORDS = {
65
+ css: CSS_KEYWORDS,
66
+ html: HTML_KEYWORDS,
67
+ javascript: JS_KEYWORDS,
68
+ typescript: JS_KEYWORDS,
69
+ js: JS_KEYWORDS,
70
+ ts: JS_KEYWORDS,
71
+ python: PYTHON_KEYWORDS,
72
+ py: PYTHON_KEYWORDS,
73
+ go: GO_KEYWORDS,
74
+ golang: GO_KEYWORDS,
75
+ cpp: CPP_KEYWORDS,
76
+ 'c++': CPP_KEYWORDS,
77
+ rust: RUST_KEYWORDS,
78
+ rs: RUST_KEYWORDS,
79
+ };
80
+ export const tokenizeCode = (code, language = 'javascript') => {
81
+ const keywords = LANGUAGE_KEYWORDS[language] || NOOP;
82
+ const blocks = [];
83
+ LEXER_REGEX.lastIndex = 0;
84
+ let match;
85
+ while ((match = LEXER_REGEX.exec(code)) !== null) {
86
+ if (match[1]) {
87
+ blocks.push({ highlightType: CodeHighlightType.COMMENT, codeContent: match[1] });
88
+ }
89
+ else if (match[2]) {
90
+ blocks.push({ highlightType: CodeHighlightType.STRING, codeContent: match[2] });
91
+ }
92
+ else if (match[3]) {
93
+ blocks.push({
94
+ highlightType: keywords.has(match[3]) ? CodeHighlightType.KEYWORD : CodeHighlightType.METHOD,
95
+ codeContent: match[3],
96
+ });
97
+ }
98
+ else if (match[4]) {
99
+ blocks.push({
100
+ highlightType: keywords.has(match[4]) ? CodeHighlightType.KEYWORD : CodeHighlightType.DEFAULT,
101
+ codeContent: match[4],
102
+ });
103
+ }
104
+ else if (match[5]) {
105
+ blocks.push({ highlightType: CodeHighlightType.NUMBER, codeContent: match[5] });
106
+ }
107
+ else {
108
+ blocks.push({ highlightType: CodeHighlightType.DEFAULT, codeContent: match[6] });
109
+ }
110
+ }
111
+ return blocks;
112
+ };
113
+ export const wrapToBotForwardedMessage = (message) =>
114
+ ({
115
+ messageContextInfo: {
116
+ botMetadata: {
117
+ // Lia@Note 09-04-26 --- TODO: Fill verificationMetadata field
118
+ verificationMetadata: {},
119
+ botRenderingConfigMetadata: {}
120
+ }
121
+ },
122
+ botForwardedMessage: { message }
123
+ });
124
+ export const generateRichCodeBlock = ({ header, code, footer, language } = {}) => {
125
+ language ??= 'javascript'
126
+ const submessages = [];
127
+ if (header) {
128
+ submessages.push({
129
+ messageType: 2,
130
+ messageText: header
131
+ });
132
+ }
133
+ submessages.push({
134
+ messageType: 5,
135
+ codeMetadata: {
136
+ codeLanguage: language,
137
+ codeBlocks: tokenizeCode(code, language)
138
+ }
139
+ });
140
+ if (footer) {
141
+ submessages.push({
142
+ messageType: 2,
143
+ messageText: footer
144
+ });
145
+ }
146
+ return ({
147
+ submessages,
148
+ messageType: 1,
149
+ contextInfo: FORWARDED_AI_BOT_INFO
150
+ });
151
+ };
152
+ export const generateRichTable = ({ header, title, table, footer } = {}) => {
153
+ const tableRows = table.map((items, index) => ({
154
+ isHeading: index == 0,
155
+ items
156
+ }));
157
+ const submessages = [];
158
+ if (header) {
159
+ submessages.push({
160
+ messageType: 2,
161
+ messageText: header
162
+ });
163
+ }
164
+ submessages.push({
165
+ messageType: 4,
166
+ tableMetadata: {
167
+ title,
168
+ rows: tableRows
169
+ }
170
+ });
171
+ if (footer) {
172
+ submessages.push({
173
+ messageType: 2,
174
+ messageText: footer
175
+ });
176
+ }
177
+ return ({
178
+ submessages,
179
+ messageType: 1,
180
+ contextInfo: FORWARDED_AI_BOT_INFO
181
+ });
182
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@itsliaaa/baileys",
3
- "version": "0.1.17",
4
- "description": "A simple fork of Baileys for WhatsApp automation",
3
+ "version": "0.1.19",
4
+ "description": "A lightweight fork of Baileys with practical fixes and small but meaningful improvements.",
5
5
  "main": "lib/index.js",
6
6
  "type": "module",
7
7
  "scripts": {