@pontasockets/baileys 0.3.2 → 0.3.4

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.
@@ -404,7 +404,7 @@ const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetwee
404
404
  const key = key_
405
405
  transactionCache[key] = transactionCache[key] || {}
406
406
  // Special handling for pre-keys and signed-pre-keys
407
- if (key === 'pre-key') {
407
+ if (key === 'pre-key' || key === 'signed-pre-key') {
408
408
  await handlePreKeyOperations(data, key, transactionCache, mutations, logger, true)
409
409
  }
410
410
  else {
@@ -475,17 +475,22 @@ const addTransactionCapability = (state, logger, { maxCommitRetries, delayBetwee
475
475
  if (transactionsInProgress === 1) {
476
476
  logger.trace('entering transaction')
477
477
  }
478
- // Release the transaction mutex now that we've updated the counter
479
- // This allows other transactions to start preparing
480
- releaseTxMutex()
481
478
  try {
482
479
  return await executeTransactionWork(work)
483
480
  }
484
481
  finally {
485
482
  cleanupTransactionState();
483
+ // Release AFTER commit & cleanup to prevent race conditions
484
+ // where another transaction starts before this one finishes writing
485
+ releaseTxMutex()
486
486
  }
487
487
  }
488
488
  catch (error) {
489
+ // Only release here if we haven't already released in the finally block
490
+ // (i.e., error thrown before transactionsInProgress was incremented)
491
+ if (transactionsInProgress > 0) {
492
+ cleanupTransactionState()
493
+ }
489
494
  releaseTxMutex()
490
495
  throw error
491
496
  }
@@ -520,4 +525,4 @@ module.exports = {
520
525
  makeCacheableSignalKeyStore,
521
526
  addTransactionCapability,
522
527
  initAuthCreds
523
- }
528
+ }
@@ -40,6 +40,7 @@ const makeEventBuffer = (logger) => {
40
40
  let data = makeBufferData()
41
41
  let isBuffering = false
42
42
  let bufferTimeout = null
43
+ let flushPendingTimeout = null
43
44
  let bufferCount = 0
44
45
  const MAX_HISTORY_CACHE_SIZE = 10000 // Limit the history cache size to prevent memory bloat
45
46
  const BUFFER_TIMEOUT_MS = 30000 // 30 seconds
@@ -81,11 +82,15 @@ const makeEventBuffer = (logger) => {
81
82
  isBuffering = false
82
83
  bufferCount = 0
83
84
 
84
- // Clear timeout
85
+ // Clear timeouts
85
86
  if (bufferTimeout) {
86
87
  clearTimeout(bufferTimeout)
87
88
  bufferTimeout = null
88
89
  }
90
+ if (flushPendingTimeout) {
91
+ clearTimeout(flushPendingTimeout)
92
+ flushPendingTimeout = null
93
+ }
89
94
 
90
95
  // Clear history cache if it exceeds the max size
91
96
  if (historyCache.size > MAX_HISTORY_CACHE_SIZE) {
@@ -138,15 +143,6 @@ const makeEventBuffer = (logger) => {
138
143
  buffer()
139
144
  try {
140
145
  const result = await work(...args)
141
-
142
- // If this is the only buffer, flush after a small delay
143
- if (bufferCount === 1) {
144
- setTimeout(() => {
145
- if (isBuffering && bufferCount === 1) {
146
- flush()
147
- }
148
- }, 100) // Small delay to allow nested buffers
149
- }
150
146
  return result
151
147
  }
152
148
  catch (error) {
@@ -155,9 +151,16 @@ const makeEventBuffer = (logger) => {
155
151
  finally {
156
152
  bufferCount = Math.max(0, bufferCount - 1)
157
153
 
158
- if (bufferCount === 0) {
159
- // Auto-flush when no other buffers are active
160
- setTimeout(flush, 100)
154
+ if (bufferCount === 0 && isBuffering) {
155
+ // Only schedule ONE flush timer at a time to prevent timer storms
156
+ if (!flushPendingTimeout) {
157
+ flushPendingTimeout = setTimeout(() => {
158
+ flushPendingTimeout = null
159
+ if (isBuffering && bufferCount === 0) {
160
+ flush()
161
+ }
162
+ }, 100)
163
+ }
161
164
  }
162
165
  }
163
166
  }
@@ -591,4 +594,4 @@ const stringifyMessageKey = (key) => `${key.remoteJid},${key.id},${key.fromMe ?
591
594
 
592
595
  module.exports = {
593
596
  makeEventBuffer
594
- }
597
+ }
@@ -1,17 +1,10 @@
1
1
  "use strict"
2
2
 
3
- var __importDefault = (this && this.__importDefault) || function(mod) {
4
- return (mod && mod.__esModule) ? mod : {
5
- "default": mod
6
- }
7
- }
8
-
9
3
  Object.defineProperty(exports, "__esModule", {
10
4
  value: true
11
5
  })
12
6
 
13
7
  const boom_1 = require("@hapi/boom")
14
- const axios_1 = __importDefault(require("axios"))
15
8
  const crypto_1 = require("crypto")
16
9
  const fs_1 = require("fs")
17
10
  const WAProto_1 = require("../../WAProto")
@@ -27,7 +20,7 @@ const MIMETYPE_MAP = {
27
20
  image: 'image/jpeg',
28
21
  video: 'video/mp4',
29
22
  document: 'application/pdf',
30
- audio: 'audio/ogg codecs=opus',
23
+ audio: 'audio/ogg; codecs=opus',
31
24
  sticker: 'image/webp',
32
25
  'product-catalog-image': 'image/jpeg'
33
26
  }
@@ -168,7 +161,7 @@ const prepareWAMessageMedia = async (message, options) => {
168
161
 
169
162
  const requiresDurationComputation = mediaType === 'audio' && typeof uploadData.seconds === 'undefined'
170
163
  const requiresThumbnailComputation = (mediaType === 'image' || mediaType === 'video') && typeof uploadData['jpegThumbnail'] === 'undefined'
171
- const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true
164
+ const requiresWaveformProcessing = mediaType === 'audio' && uploadData.ptt === true && typeof uploadData.waveform === 'undefined'
172
165
  const requiresAudioBackground = options.backgroundColor && mediaType === 'audio' && uploadData.ptt === true
173
166
  const requiresOriginalForSomeProcessing = requiresDurationComputation || requiresThumbnailComputation
174
167
 
@@ -321,7 +314,7 @@ const prepareAlbumMessageContent = async (jid, albums, options) => {
321
314
  },
322
315
  ...options
323
316
  })
324
- } else if ('video' in message) {
317
+ } else if ('video' in media) {
325
318
  mediaMsg = await generateWAMessage(jid, {
326
319
  video: media.video,
327
320
  ...media,
@@ -383,9 +376,9 @@ const generateForwardMessageContent = (message, forceForward) => {
383
376
  content = WAProto_1.proto.Message.decode(WAProto_1.proto.Message.encode(content).finish())
384
377
 
385
378
  let key = Object.keys(content)[0]
386
- let score = content[key].contextInfo?.forwardingScore || 0
379
+ let score = content?.[key]?.contextInfo?.forwardingScore || 0
387
380
 
388
- if (forceForward) score += forceForward ? forceForward : 1
381
+ score += message.key.fromMe && !forceForward ? 0 : 1
389
382
 
390
383
  if (key === 'conversation') {
391
384
  content.extendedTextMessage = {
@@ -905,7 +898,7 @@ const generateWAMessageContent = async (message, options) => {
905
898
  directPath: stickerPackUploadResult.directPath,
906
899
  fileLength: stickerPackUpload.fileLength,
907
900
  mediaKeyTimestamp: generics_1.unixTimestampSeconds(),
908
- trayIconFileName: `${stickerPackId}.png`,
901
+ trayIconFileName: `${stickerPackId}.webp`,
909
902
  imageDataHash,
910
903
  thumbnailDirectPath: coverImage.directPath,
911
904
  thumbnailFileSha256: coverImage.fileSha256,
@@ -1189,7 +1182,7 @@ const generateWAMessageContent = async (message, options) => {
1189
1182
  }
1190
1183
  }
1191
1184
 
1192
- if ('footer' in message && !message.footer) {
1185
+ if ('footer' in message && !!message.footer) {
1193
1186
  interactiveMessage.footer = {
1194
1187
  text: message.footer
1195
1188
  }
@@ -1398,9 +1391,12 @@ const generateWAMessageFromContent = (jid, message, options) => {
1398
1391
  messageSecret: crypto_1.randomBytes(32),
1399
1392
  ...message.messageContextInfo
1400
1393
  }
1394
+ }
1395
+
1396
+ if (!!options?.ephemeralExpiration && key !== 'protocolMessage' && key !== 'ephemeralMessage' && !WABinary_1.isJidNewsletter(jid)) {
1401
1397
  innerMessage[key].contextInfo = {
1402
1398
  ...(innerMessage[key].contextInfo || {}),
1403
- expiration: options.ephemeralExpiration ? options.ephemeralExpiration : 0
1399
+ expiration: options.ephemeralExpiration
1404
1400
  }
1405
1401
  }
1406
1402
 
@@ -1427,6 +1423,10 @@ const generateWAMessage = async (jid, content, options) => {
1427
1423
  msgId: options.messageId
1428
1424
  })
1429
1425
 
1426
+ if (jid) {
1427
+ options.jid = jid
1428
+ }
1429
+
1430
1430
  return generateWAMessageFromContent(jid, await generateWAMessageContent(content, {
1431
1431
  newsletter: WABinary_1.isJidNewsletter(jid),
1432
1432
  ...options
@@ -1436,7 +1436,7 @@ const generateWAMessage = async (jid, content, options) => {
1436
1436
  const getContentType = (content) => {
1437
1437
  if (content) {
1438
1438
  const keys = Object.keys(content)
1439
- const key = keys.find(k => (k === 'conversation' || k.endsWith('Message') || k.endsWith('V2') || k.endsWith('V3') || k.endsWith('V4') || k.endsWith('V5')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
1439
+ const key = keys.find(k => (k === 'conversation' || k.includes('Message')) && k !== 'senderKeyDistributionMessage' && k !== 'messageContextInfo')
1440
1440
 
1441
1441
  return key
1442
1442
  }
@@ -1459,7 +1459,7 @@ const normalizeMessageContent = (content) => {
1459
1459
  return content
1460
1460
 
1461
1461
  function getFutureProofMessage(message) {
1462
- return ((message === null || message === void 0 ? void 0 : message.editedMessage) || (message === null || message === void 0 ? void 0 : message.statusAddYours) || (message === null || message === void 0 ? void 0 : message.botTaskMessage) || (message === null || message === void 0 ? void 0 : message.eventCoverImage) || (message === null || message === void 0 ? void 0 : message.questionMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessage) || (message === null || message === void 0 ? void 0 : message.botInvokeMessage) || (message === null || message === void 0 ? void 0 : message.ephemeralMessage) || (message === null || message === void 0 ? void 0 : message.lottieStickerMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMessage) || (message === null || message === void 0 ? void 0 : message.limitSharingMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2) || (message === null || message === void 0 ? void 0 : message.botForwardedMessage) || (message === null || message === void 0 ? void 0 : message.statusMentionMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMessageV2) || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV4) || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV5) || (message === null || message === void 0 ? void 0 : message.associatedChildMessage) || (message === null || message === void 0 ? void 0 : message.groupMentionedMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMentionMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension) || (message === null || message === void 0 ? void 0 : message.documentWithCaptionMessage) || (message === null || message === void 0 ? void 0 : message.pollCreationOptionImageMessage))
1462
+ return ((message === null || message === void 0 ? void 0 : message.editedMessage) || (message === null || message === void 0 ? void 0 : message.statusAddYours) || (message === null || message === void 0 ? void 0 : message.botTaskMessage) || (message === null || message === void 0 ? void 0 : message.eventCoverImage) || (message === null || message === void 0 ? void 0 : message.questionMessage) || (message === null || message === void 0 ? void 0 : message.questionReplyMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessage) || (message === null || message === void 0 ? void 0 : message.botInvokeMessage) || (message === null || message === void 0 ? void 0 : message.ephemeralMessage) || (message === null || message === void 0 ? void 0 : message.lottieStickerMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMessage) || (message === null || message === void 0 ? void 0 : message.limitSharingMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2) || (message === null || message === void 0 ? void 0 : message.botForwardedMessage) || (message === null || message === void 0 ? void 0 : message.statusMentionMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMessageV2) || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV4) || (message === null || message === void 0 ? void 0 : message.pollCreationMessageV5) || (message === null || message === void 0 ? void 0 : message.associatedChildMessage) || (message === null || message === void 0 ? void 0 : message.groupMentionedMessage) || (message === null || message === void 0 ? void 0 : message.groupStatusMentionMessage) || (message === null || message === void 0 ? void 0 : message.viewOnceMessageV2Extension) || (message === null || message === void 0 ? void 0 : message.documentWithCaptionMessage) || (message === null || message === void 0 ? void 0 : message.pollCreationOptionImageMessage) || (message === null || message === void 0 ? void 0 : message.spoilerMessage) || (message === null || message === void 0 ? void 0 : message.newsletterAdminProfileMessage) || (message === null || message === void 0 ? void 0 : message.newsletterAdminProfileMessageV2) || (message === null || message === void 0 ? void 0 : message.newsletterAdminProfileStatusMessage))
1463
1463
  }
1464
1464
  }
1465
1465
 
@@ -1617,7 +1617,7 @@ const REUPLOAD_REQUIRED_STATUS = [410, 404]
1617
1617
 
1618
1618
  const downloadMediaMessage = async (message, type, options, ctx) => {
1619
1619
  const result = await downloadMsg().catch(async (error) => {
1620
- if (ctx && axios_1.default.isAxiosError(error) && REUPLOAD_REQUIRED_STATUS.includes(error.response?.status)) {
1620
+ if (ctx && typeof error?.status === 'number' && REUPLOAD_REQUIRED_STATUS.includes(error.status)) {
1621
1621
  ctx.logger.info({
1622
1622
  key: message.key
1623
1623
  }, 'sending reupload media request...')
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true })
8
8
 
9
9
  const async_mutex_1 = __importDefault(require("async-mutex"))
10
10
  const promises_1 = require("fs/promises")
11
+ const { rename: fsRename } = promises_1
11
12
  const path_1 = require("path")
12
13
  const WAProto_1 = require("../../WAProto")
13
14
  const auth_utils_1 = require("./auth-utils")
@@ -37,13 +38,20 @@ const getFileLock = (path) => {
37
38
  * Would recommend writing an auth state for use with a proper SQL or No-SQL DB
38
39
  * */
39
40
  const useMultiFileAuthState = async (folder) => {
41
+ // Debounce timers per file to avoid rapid successive writes hammering disk
42
+ const writeDebounceTimers = new Map()
43
+ const WRITE_DEBOUNCE_MS = 300
44
+
40
45
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
46
  const writeData = async (data, file) => {
42
47
  const filePath = path_1.join(folder, fixFileName(file))
43
48
  const mutex = getFileLock(filePath)
44
49
  return mutex.acquire().then(async (release) => {
45
50
  try {
46
- await promises_1.writeFile(filePath, JSON.stringify(data, generics_1.BufferJSON.replacer))
51
+ // Atomic write: write to temp file then rename to prevent corruption
52
+ const tmpPath = filePath + '.tmp'
53
+ await promises_1.writeFile(tmpPath, JSON.stringify(data, generics_1.BufferJSON.replacer))
54
+ await fsRename(tmpPath, filePath)
47
55
  } finally {
48
56
  release()
49
57
  }
@@ -120,7 +128,24 @@ const useMultiFileAuthState = async (folder) => {
120
128
  }
121
129
  }
122
130
  },
123
- saveCreds: async () => {
131
+ // saveCreds debounced by default — aman dipanggil tiap creds.update
132
+ // tanpa spam disk. Untuk force immediate write, pakai saveCredsNow.
133
+ saveCreds: () => {
134
+ const existing = writeDebounceTimers.get('creds.json')
135
+ if (existing) clearTimeout(existing)
136
+ const timer = setTimeout(() => {
137
+ writeDebounceTimers.delete('creds.json')
138
+ writeData(creds, 'creds.json').catch(err => {
139
+ console.error('[useMultiFileAuthState] Failed to save creds.json:', err)
140
+ })
141
+ }, WRITE_DEBOUNCE_MS)
142
+ writeDebounceTimers.set('creds.json', timer)
143
+ },
144
+ // Force immediate write — pakai saat disconnect/cleanup agar creds tidak hilang
145
+ saveCredsNow: async () => {
146
+ const existing = writeDebounceTimers.get('creds.json')
147
+ if (existing) clearTimeout(existing)
148
+ writeDebounceTimers.delete('creds.json')
124
149
  return writeData(creds, 'creds.json')
125
150
  }
126
151
  }
@@ -128,4 +153,4 @@ const useMultiFileAuthState = async (folder) => {
128
153
 
129
154
  module.exports = {
130
155
  useMultiFileAuthState
131
- }
156
+ }
@@ -58,7 +58,7 @@ const generateLoginNode = (userJid, config) => {
58
58
  const { user, device } = WABinary_1.jidDecode(userJid)
59
59
  const payload = {
60
60
  ...getClientPayload(config),
61
- passive: false,
61
+ passive: true,
62
62
  pull: true,
63
63
  username: +user,
64
64
  device: device,
@@ -185,4 +185,4 @@ module.exports = {
185
185
  generateRegistrationNode,
186
186
  configureSuccessfulPairing,
187
187
  encodeSignedDeviceIdentity
188
- }
188
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pontasockets/baileys",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "Baileys is a lightweight JavaScript library for interacting with the WhatsApp Web API using WebSocket.",
5
5
  "keywords": [
6
6
  "facebook",