@itsliaaa/baileys 0.1.17 → 0.1.18

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
@@ -2,7 +2,14 @@
2
2
 
3
3
  ![Logo](https://files.catbox.moe/c5s9g0.jpg)
4
4
 
5
- A lightweight fork of Baileys with a few fixes and a small adjustment.
5
+ A lightweight fork of Baileys with practical fixes and small but meaningful improvements.
6
+
7
+ ### ✨ Highlights
8
+
9
+ This fork focuses on clarity and safety:
10
+
11
+ - 🚫 No obfuscation. Easy to read and audit.
12
+ - 🚫 No auto-follow channel (newsletter) behavior.
6
13
 
7
14
  ### ⚙️ Changes
8
15
 
@@ -10,7 +17,7 @@ A lightweight fork of Baileys with a few fixes and a small adjustment.
10
17
  - 🖼️ Fixed an issue where media could not be sent to newsletters due to an upstream issue.
11
18
  - 📁 Reintroduced [`makeInMemoryStore`](#%EF%B8%8F-implementing-a-data-store) with a minimal ESM adaptation and small adjustments for Baileys v7.
12
19
  - 📦 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.
20
+ - 🗃️ 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
21
 
15
22
  #### 📨 Message Handling & Compatibility
16
23
  - 👉🏻 Added support for sending [interactive message](#-sending-interactive-messages) types (button, list, interactive, template, carousel).
@@ -959,6 +966,62 @@ await sock.requestPairingCode(phoneNumber, customPairingCode)
959
966
  console.log('🔗 Pairing code', ':', customPairingCode)
960
967
  ```
961
968
 
969
+ ##### 🖼️ Image Processing
970
+
971
+ > [!NOTE]
972
+ Automatically use available image processing library: `sharp`, `@napi-rs/image`, or `jimp`
973
+
974
+ ```javascript
975
+ import { getImageProcessingLibrary } from '@itsliaaa/baileys'
976
+ import { readFile } from 'fs/promises'
977
+
978
+ const lib = await getImageProcessingLibrary()
979
+
980
+ const bufferOrFilePath = './path/to/image.jpg'
981
+ const width = 512
982
+
983
+ let output
984
+
985
+ // --- If sharp installed
986
+ if (lib.sharp?.default) {
987
+ const img = lib.sharp.default(bufferOrFilePath)
988
+
989
+ output = await img.resize(width)
990
+ .jpeg({ quality: 80 })
991
+ .toBuffer()
992
+ }
993
+
994
+ // --- If @napi-rs/image installed
995
+ else if (lib.image?.Transformer) {
996
+ // --- Must in buffer format
997
+ const inputBuffer = Buffer.isBuffer(bufferOrFilePath)
998
+ ? bufferOrFilePath
999
+ : await readFile(bufferOrFilePath)
1000
+
1001
+ const img = new lib.image.Transformer(inputBuffer)
1002
+
1003
+ output = await img.resize(width, undefined, 0)
1004
+ .jpeg(50)
1005
+ }
1006
+
1007
+ // --- If jimp installed
1008
+ else if (lib.jimp?.Jimp) {
1009
+ const img = await lib.jimp.Jimp.read(bufferOrFilePath)
1010
+
1011
+ output = await img
1012
+ .resize({ w: width, mode: lib.jimp.ResizeStrategy.BILINEAR })
1013
+ .getBuffer('image/jpeg', { quality: 50 });
1014
+ }
1015
+
1016
+ // --- Fallback
1017
+ else {
1018
+ throw new Error('No image processing available')
1019
+ }
1020
+
1021
+ console.log('✅ Process completed!')
1022
+ console.dir(output, { depth: null })
1023
+ ```
1024
+
962
1025
  ##### 📣 Newsletter Management
963
1026
 
964
1027
  ```javascript
@@ -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,
@@ -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 = () => {
@@ -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
  }
@@ -1330,8 +1330,9 @@ export const generateWAMessageContent = async (message, options) => {
1330
1330
  ...content,
1331
1331
  body: content.body,
1332
1332
  mediaType: content.mediaType || 1,
1333
- mediaUrl: content.url + '?update=' + Date.now(),
1333
+ mediaUrl: content.url,
1334
1334
  renderLargerThumbnail: content.largeThumbnail,
1335
+ sourceUrl: content.url + '?update=' + Date.now(),
1335
1336
  thumbnail: content.thumbnail,
1336
1337
  thumbnailUrl: content.url,
1337
1338
  title: content.title || LIBRARY_NAME
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.18",
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": {