@areumtecnologia/wasockets 2.0.0 → 2.2.3

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 (46) hide show
  1. package/README.md +1929 -1649
  2. package/lib/Defaults/baileys-version.json +1 -1
  3. package/lib/Defaults/connection.js +14 -1
  4. package/lib/Socket/index.js +51 -48
  5. package/lib/Socket/messages-recv.js +74 -13
  6. package/lib/Socket/messages-send.js +50 -25
  7. package/lib/Socket/socket.js +13 -0
  8. package/lib/Socket/voip.js +123 -0
  9. package/lib/Store/make-cache-manager-store.js +14 -5
  10. package/lib/Types/index.d.ts +4 -0
  11. package/lib/Utils/auth-utils.js +16 -1
  12. package/lib/Utils/browser-utils.js +6 -2
  13. package/lib/Utils/generics.js +47 -8
  14. package/lib/Utils/index.d.ts +1 -0
  15. package/lib/Utils/messages-media.js +44 -21
  16. package/lib/Utils/pre-key-manager.js +16 -1
  17. package/lib/Utils/validate-connection.js +10 -11
  18. package/lib/VoIP/VoIPCrypto.js +150 -0
  19. package/lib/VoIP/VoIPManager.js +469 -0
  20. package/lib/VoIP/VoIPMediaEngine.js +224 -0
  21. package/lib/VoIP/VoIPProtocol.js +354 -0
  22. package/lib/VoIP/VoIPRelayTransport.js +173 -0
  23. package/lib/VoIP/VoIPSession.js +314 -0
  24. package/lib/VoIP/assets/wasm/loader.js +5 -0
  25. package/lib/VoIP/assets/wasm/whatsapp.wasm +0 -0
  26. package/lib/VoIP/assets/wasm/worker-modules.js +305 -0
  27. package/lib/VoIP/engine/audio-feeder.d.mts +15 -0
  28. package/lib/VoIP/engine/audio-feeder.mjs +243 -0
  29. package/lib/VoIP/engine/index.d.mts +91 -0
  30. package/lib/VoIP/engine/index.mjs +1683 -0
  31. package/lib/VoIP/engine/relay-transport.d.mts +43 -0
  32. package/lib/VoIP/engine/relay-transport.mjs +607 -0
  33. package/lib/VoIP/engine/signaling.d.mts +44 -0
  34. package/lib/VoIP/engine/signaling.mjs +893 -0
  35. package/lib/VoIP/engine/types.d.mts +183 -0
  36. package/lib/VoIP/engine/types.mjs +17 -0
  37. package/lib/VoIP/engine/wasm-engine.d.mts +103 -0
  38. package/lib/VoIP/engine/wasm-engine.mjs +1397 -0
  39. package/lib/VoIP/engine/worker-bootstrap.mjs +1049 -0
  40. package/lib/VoIP/index.js +51 -0
  41. package/lib/VoIP/utils/audio.d.ts +28 -0
  42. package/lib/VoIP/utils/audio.js +148 -0
  43. package/lib/WABinary/generic-utils.js +22 -6
  44. package/lib/index.d.ts +382 -274
  45. package/lib/index.js +5 -0
  46. package/package.json +28 -32
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": [2, 3000, 1030831524]
2
+ "version": [2, 3000, 1051853749]
3
3
  }
@@ -7,6 +7,19 @@ const { default: logger } = require("../Utils/logger")
7
7
  const { makeLibSignalRepository } = require("../Signal/libsignal")
8
8
  const { version } = require("./baileys-version.json")
9
9
 
10
+ let defaultWaVersion = version
11
+ try {
12
+ const pkg = require("../../package.json")
13
+ if (typeof pkg.whatsappVersion === 'string') {
14
+ const parts = pkg.whatsappVersion.split('.').map(Number)
15
+ if (parts.length === 3 && parts.every(n => !isNaN(n))) {
16
+ defaultWaVersion = parts
17
+ }
18
+ } else if (Array.isArray(pkg.whatsappVersion) && pkg.whatsappVersion.length === 3) {
19
+ defaultWaVersion = pkg.whatsappVersion.map(Number)
20
+ }
21
+ } catch {}
22
+
10
23
  const createGroupMetadataCache = (ttlMs = 300000, maxSize = 250) => {
11
24
  const cache = new Map()
12
25
 
@@ -35,7 +48,7 @@ const createGroupMetadataCache = (ttlMs = 300000, maxSize = 250) => {
35
48
  }
36
49
 
37
50
  const DEFAULT_CONNECTION_CONFIG = {
38
- version,
51
+ version: defaultWaVersion,
39
52
  browser: Browsers.windows('Chrome'),
40
53
  waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
41
54
  connectTimeoutMs: 20000,
@@ -1,48 +1,51 @@
1
- "use strict"
2
-
3
- Object.defineProperty(exports, "__esModule", { value: true })
4
-
5
- const { DEFAULT_CONNECTION_CONFIG, createGroupMetadataCache } = require("../Defaults/connection")
6
- const { makeCommunitiesSocket } = require("./community")
7
- const { PluginManager } = require("../Plugins")
8
-
9
- // export the last socket layer
10
- const makeWASocket = (config = {}) => {
11
- const newConfig = {
12
- ...DEFAULT_CONNECTION_CONFIG,
13
- ...config
14
- }
15
-
16
- // If the user hasn't provided their own history sync function,
17
- // let's create a default one that respects the syncFullHistory flag.
18
- if (config.shouldSyncHistoryMessage === undefined) {
19
- newConfig.shouldSyncHistoryMessage = () => !!newConfig.syncFullHistory
20
- }
21
-
22
- if (!config.cachedGroupMetadata && newConfig.enableGroupCache) {
23
- newConfig.cachedGroupMetadata = createGroupMetadataCache()
24
- }
25
-
26
- const sock = makeCommunitiesSocket(newConfig)
27
- const pluginManager = new PluginManager(sock)
28
-
29
- sock.plugins = pluginManager
30
- sock.use = (plugin, options) => {
31
- pluginManager.use(plugin, options)
32
- return sock
33
- }
34
- sock.registerMiddleware = (hook, fn) => pluginManager.registerMiddleware(hook, fn)
35
- sock.hasPlugin = (name) => pluginManager.hasPlugin(name)
36
- sock.listPlugins = () => pluginManager.listPlugins()
37
-
38
- // Auto-install plugins passed in configuration
39
- if (Array.isArray(config.plugins)) {
40
- for (const p of config.plugins) {
41
- sock.use(p)
42
- }
43
- }
44
-
45
- return sock
46
- }
47
-
48
- exports.default = makeWASocket
1
+ "use strict"
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true })
4
+
5
+ const { DEFAULT_CONNECTION_CONFIG, createGroupMetadataCache } = require("../Defaults/connection")
6
+ const { makeCommunitiesSocket } = require("./community")
7
+ const { PluginManager } = require("../Plugins")
8
+ const { makeVoipSocket } = require("./voip")
9
+
10
+ // export the last socket layer
11
+ const makeWASocket = (config = {}) => {
12
+ const newConfig = {
13
+ ...DEFAULT_CONNECTION_CONFIG,
14
+ ...config
15
+ }
16
+
17
+ // If the user hasn't provided their own history sync function,
18
+ // let's create a default one that respects the syncFullHistory flag.
19
+ if (config.shouldSyncHistoryMessage === undefined) {
20
+ newConfig.shouldSyncHistoryMessage = () => !!newConfig.syncFullHistory
21
+ }
22
+
23
+ if (!config.cachedGroupMetadata && newConfig.enableGroupCache) {
24
+ newConfig.cachedGroupMetadata = createGroupMetadataCache()
25
+ }
26
+
27
+ const baseSock = makeCommunitiesSocket(newConfig)
28
+ const sock = makeVoipSocket(newConfig, baseSock)
29
+ const pluginManager = new PluginManager(sock)
30
+
31
+ sock.plugins = pluginManager
32
+ sock.use = (plugin, options) => {
33
+ pluginManager.use(plugin, options)
34
+ return sock
35
+ }
36
+ sock.registerMiddleware = (hook, fn) => pluginManager.registerMiddleware(hook, fn)
37
+ sock.hasPlugin = (name) => pluginManager.hasPlugin(name)
38
+ sock.listPlugins = () => pluginManager.listPlugins()
39
+
40
+ // Auto-install plugins passed in configuration
41
+ if (Array.isArray(config.plugins)) {
42
+ for (const p of config.plugins) {
43
+ sock.use(p)
44
+ }
45
+ }
46
+
47
+ return sock
48
+ }
49
+
50
+ exports.default = makeWASocket
51
+ exports.makeWASocket = makeWASocket
@@ -273,15 +273,34 @@ const makeMessagesRecvSocket = (config) => {
273
273
  }],
274
274
  })
275
275
 
276
- await query(stanza)
276
+ await sendNode(stanza)
277
277
 
278
278
  return {
279
279
  id: callId,
280
- to: toJid
280
+ to: toJid,
281
+ callKey: encKey
281
282
  }
282
283
  }
283
284
 
284
- const rejectCall = async (callId, callFrom) => {
285
+ const getVoIPRelayTokens = async () => {
286
+ const stanza = {
287
+ tag: 'iq',
288
+ attrs: {
289
+ id: generateMessageID(),
290
+ to: S_WHATSAPP_NET,
291
+ type: 'get',
292
+ xmlns: 'w:p',
293
+ },
294
+ content: [{
295
+ tag: 'relay',
296
+ attrs: { latency: '15' },
297
+ content: undefined,
298
+ }],
299
+ }
300
+ return await query(stanza, 3000)
301
+ }
302
+
303
+ const rejectCall = async (callId, callFrom, reason = '0') => {
285
304
  const stanza = ({
286
305
  tag: 'call',
287
306
  attrs: {
@@ -293,16 +312,38 @@ const makeMessagesRecvSocket = (config) => {
293
312
  attrs: {
294
313
  'call-id': callId,
295
314
  'call-creator': callFrom,
296
- count: '0',
315
+ count: reason.toString(),
297
316
  },
298
317
  content: undefined,
299
318
  }],
300
319
  })
301
320
 
302
- await query(stanza)
321
+ await sendNode(stanza)
303
322
  }
304
323
 
305
- const acceptCall = async (callId, callFrom) => {
324
+ const acceptCall = async (callId, callFrom, options = {}) => {
325
+ const acceptContent = [
326
+ { tag: 'audio', attrs: { enc: 'opus', rate: options.audioRate || '16000' }, content: undefined },
327
+ { tag: 'net', attrs: { medium: '3' }, content: undefined },
328
+ { tag: 'capability', attrs: { ver: '1' }, content: new Uint8Array([1, 4, 255, 131, 207, 4]) },
329
+ { tag: 'encopt', attrs: { keygen: '2' }, content: undefined },
330
+ ]
331
+
332
+ if (options.isVideo) {
333
+ acceptContent.push({
334
+ tag: 'video',
335
+ attrs: {
336
+ enc: 'vp8',
337
+ dec: 'vp8',
338
+ orientation: '0',
339
+ screen_width: '1920',
340
+ screen_height: '1080',
341
+ device_orientation: '0'
342
+ },
343
+ content: undefined
344
+ })
345
+ }
346
+
306
347
  const stanza = ({
307
348
  tag: 'call',
308
349
  attrs: {
@@ -315,14 +356,14 @@ const makeMessagesRecvSocket = (config) => {
315
356
  'call-id': callId,
316
357
  'call-creator': callFrom,
317
358
  },
318
- content: undefined,
359
+ content: acceptContent,
319
360
  }],
320
361
  })
321
362
 
322
- await query(stanza)
363
+ await sendNode(stanza)
323
364
  }
324
365
 
325
- const terminateCall = async (callId, callFrom) => {
366
+ const terminateCall = async (callId, callFrom, reason = 'hangup') => {
326
367
  const stanza = ({
327
368
  tag: 'call',
328
369
  attrs: {
@@ -334,12 +375,13 @@ const makeMessagesRecvSocket = (config) => {
334
375
  attrs: {
335
376
  'call-id': callId,
336
377
  'call-creator': callFrom,
378
+ reason,
337
379
  },
338
380
  content: undefined,
339
381
  }],
340
382
  })
341
383
 
342
- await query(stanza)
384
+ await sendNode(stanza)
343
385
  }
344
386
 
345
387
  const sendRetryRequest = async (node, forceIncludeKeys = false) => {
@@ -1307,8 +1349,9 @@ const makeMessagesRecvSocket = (config) => {
1307
1349
 
1308
1350
  const errorMessage = msg?.messageStubParameters?.[0] || ''
1309
1351
  const isPreKeyError = errorMessage.includes('PreKey')
1352
+ const isSessionOrMacError = errorMessage.includes('MAC') || errorMessage.includes('Session') || errorMessage.includes('Invalid')
1310
1353
 
1311
- logger.debug(`[handleMessage] Attempting retry request for failed decryption`)
1354
+ logger.debug(`[handleMessage] Attempting retry request for failed decryption (isPreKey: ${isPreKeyError}, isSessionOrMac: ${isSessionOrMacError})`)
1312
1355
 
1313
1356
  // Handle both pre-key and normal retries in single mutex
1314
1357
  await retryMutex.mutex(async () => {
@@ -1318,6 +1361,20 @@ const makeMessagesRecvSocket = (config) => {
1318
1361
  return
1319
1362
  }
1320
1363
 
1364
+ // Auto-heal corrupted sessions (Bad MAC, Invalid Key) by purging stale session
1365
+ if (isSessionOrMacError) {
1366
+ const senderJid = node.attrs.participant || node.attrs.from
1367
+ if (senderJid && signalRepository) {
1368
+ try {
1369
+ const sessionId = signalRepository.jidToSignalProtocolAddress(senderJid)
1370
+ await authState.keys.set({ session: { [sessionId]: null } })
1371
+ logger.info({ senderJid, errorMessage }, 'Auto-healed corrupted Signal session for sender')
1372
+ } catch (healErr) {
1373
+ logger.warn({ healErr, senderJid }, 'Failed to clear corrupted session during auto-heal')
1374
+ }
1375
+ }
1376
+ }
1377
+
1321
1378
  // Handle pre-key errors with upload and delay
1322
1379
  if (isPreKeyError) {
1323
1380
  logger.info({ error: errorMessage }, 'PreKey error detected, uploading and retrying')
@@ -1336,7 +1393,8 @@ const makeMessagesRecvSocket = (config) => {
1336
1393
 
1337
1394
  const encNode = getBinaryNodeChild(node, 'enc')
1338
1395
 
1339
- await sendRetryRequest(node, !encNode)
1396
+ // Force include keys if pre-key or session corrupted
1397
+ await sendRetryRequest(node, !encNode || isSessionOrMacError || isPreKeyError)
1340
1398
 
1341
1399
  if (retryRequestDelayMs) {
1342
1400
  await delay(retryRequestDelayMs)
@@ -1438,7 +1496,9 @@ const makeMessagesRecvSocket = (config) => {
1438
1496
  id: callId,
1439
1497
  date: new Date(+attrs.t * 1000),
1440
1498
  offline: !!attrs.offline,
1441
- status
1499
+ status,
1500
+ rawNode: node,
1501
+ infoNode: infoChild
1442
1502
  }
1443
1503
 
1444
1504
  if (status === 'offer') {
@@ -1675,6 +1735,7 @@ const makeMessagesRecvSocket = (config) => {
1675
1735
  rejectCall,
1676
1736
  acceptCall,
1677
1737
  terminateCall,
1738
+ getVoIPRelayTokens,
1678
1739
  fetchMessageHistory,
1679
1740
  requestPlaceholderResend,
1680
1741
  messageRetryManager
@@ -502,11 +502,16 @@ const makeMessagesSocket = (config) => {
502
502
 
503
503
  if (jidsRequiringFetch.length) {
504
504
  // LID if mapped, otherwise original
505
- const wireJids = [
505
+ let wireJids = [
506
506
  ...jidsRequiringFetch.filter(jid => !!isLidUser(jid) || !!isHostedLidUser(jid)),
507
- ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a.lid)
507
+ ...((await signalRepository.lidMapping.getLIDsForPNs(jidsRequiringFetch.filter(jid => !!isPnUser(jid) || !!isHostedPnUser(jid)))) || []).map(a => a?.lid).filter(Boolean)
508
508
  ]
509
509
 
510
+ // If PN numbers do not have an LID mapping in local DB yet, query directly using their PN JIDs
511
+ if (!wireJids.length) {
512
+ wireJids = jidsRequiringFetch
513
+ }
514
+
510
515
  logger.debug({ jidsRequiringFetch, wireJids }, 'fetching sessions')
511
516
 
512
517
  const result = await query({
@@ -655,36 +660,56 @@ const makeMessagesSocket = (config) => {
655
660
  }
656
661
 
657
662
  /** Fetch image for groups, user, and newsletter **/
658
- const profilePictureUrl = async (jid) => {
663
+ const profilePictureUrl = async (jid, type = 'preview', timeoutMs) => {
659
664
  if (isJidNewsletter(jid)) {
660
665
  const metadata = await suki.newsletterMetadata('JID', jid)
661
666
 
662
667
  return getUrlFromDirectPath(metadata.thread_metadata.picture?.direct_path || '')
668
+ } else {
669
+ const normalizedJid = jidNormalizedUser(jid)
670
+ const isUser = isPnUser(normalizedJid) || isLidUser(normalizedJid)
671
+ const me = authState.creds.me
672
+ const isSelf = me && (normalizedJid === jidNormalizedUser(me.id) || (me.lid && normalizedJid === jidNormalizedUser(me.lid)))
663
673
 
664
- }
665
-
666
- else {
667
- const result = await query({
668
- tag: 'iq',
669
- attrs: {
670
- target: jidNormalizedUser(jid),
671
- to: S_WHATSAPP_NET,
672
- type: 'get',
673
- xmlns: 'w:profile:picture'
674
- },
675
- content: [{
676
- tag: 'picture',
677
- attrs: {
678
- type: 'image',
679
- query: 'url'
680
- }
681
- }]
682
- })
674
+ const pictureContent = []
675
+ if (isUser && !isSelf) {
676
+ try {
677
+ const contactTcTokenData = await authState.keys.get('tctoken', [normalizedJid])
678
+ const entry = contactTcTokenData?.[normalizedJid]
679
+ if (entry?.token && entry?.timestamp) {
680
+ pictureContent.push({
681
+ tag: 'tctoken',
682
+ attrs: { t: String(entry.timestamp) },
683
+ content: entry.token
684
+ })
685
+ }
686
+ } catch (e) {
687
+ // ignore tc token error
688
+ }
689
+ }
683
690
 
684
- const child = getBinaryNodeChild(result, 'picture')
691
+ const result = await query({
692
+ tag: 'iq',
693
+ attrs: {
694
+ target: normalizedJid,
695
+ to: S_WHATSAPP_NET,
696
+ type: 'get',
697
+ xmlns: 'w:profile:picture'
698
+ },
699
+ content: [{
700
+ tag: 'picture',
701
+ attrs: {
702
+ type: type === 'image' ? 'image' : 'preview',
703
+ query: 'url'
704
+ },
705
+ content: pictureContent.length ? pictureContent : undefined
706
+ }]
707
+ }, timeoutMs)
685
708
 
686
- return child?.attrs?.url || null
687
- }
709
+ const child = getBinaryNodeChild(result, 'picture')
710
+
711
+ return child?.attrs?.url || null
712
+ }
688
713
  }
689
714
 
690
715
  const relayMessage = async (jid, message, { messageId: msgId, participant, additionalAttributes, useUserDevicesCache, useCachedGroupMetadata, statusJidList, additionalNodes, AI = false }) => {
@@ -479,6 +479,7 @@ const makeSocket = (config) => {
479
479
 
480
480
  // Pre-key upload state management
481
481
  let uploadPreKeysPromise = null
482
+ let preKeyHealthInterval = null
482
483
  let lastUploadTime = 0
483
484
 
484
485
  /** generates and uploads a set of pre-keys to the server */
@@ -665,6 +666,7 @@ const makeSocket = (config) => {
665
666
 
666
667
  clearInterval(keepAliveReq)
667
668
  clearTimeout(qrTimer)
669
+ if (preKeyHealthInterval) { clearInterval(preKeyHealthInterval); preKeyHealthInterval = null; }
668
670
 
669
671
  ws.removeAllListeners('close')
670
672
  ws.removeAllListeners('open')
@@ -986,6 +988,17 @@ const makeSocket = (config) => {
986
988
 
987
989
  clearTimeout(qrTimer) // will never happen in all likelyhood -- but just in case WA sends success on first try
988
990
 
991
+ // Start proactive periodic pre-key check (every 6 hours)
992
+ if (!preKeyHealthInterval) {
993
+ preKeyHealthInterval = setInterval(() => {
994
+ if (ws.isOpen) {
995
+ uploadPreKeysToServerIfRequired().catch(err => {
996
+ logger.warn({ err: err?.message }, 'Periodic pre-key check encountered an issue')
997
+ })
998
+ }
999
+ }, 6 * 3600 * 1000)
1000
+ }
1001
+
989
1002
  ev.emit('creds.update', { me: { ...authState.creds.me, lid: node.attrs.lid } })
990
1003
  ev.emit('connection.update', { connection: 'open' })
991
1004
 
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+
5
+ /**
6
+ * VoIP Socket Mixin for WaSockets
7
+ * Extends the unified 'sock' instance with direct WebAssembly WhatsApp calling
8
+ */
9
+ const makeVoipSocket = (config, baseSocket) => {
10
+ let voipEngine = null;
11
+ let enginePromise = null;
12
+
13
+ const getVoipEngine = async () => {
14
+ if (voipEngine) return voipEngine;
15
+ if (enginePromise) return enginePromise;
16
+
17
+ enginePromise = (async () => {
18
+ const { VoipEngine } = await import("../VoIP/engine/index.mjs");
19
+ const engine = new VoipEngine(baseSocket, config.voip && typeof config.voip === "object" ? config.voip : {});
20
+ await engine.init();
21
+ voipEngine = engine;
22
+ return engine;
23
+ })();
24
+
25
+ return enginePromise;
26
+ };
27
+
28
+ // Auto-init eagerly whenever VoIP is enabled (boolean true or object with autoInit !== false)
29
+ const shouldAutoInit = config.voip === true || (typeof config.voip === "object" && config.voip?.autoInit !== false);
30
+ if (shouldAutoInit) {
31
+ getVoipEngine().catch(err => {
32
+ config.logger?.warn?.({ err: err?.message }, "Failed to eagerly auto-init VoIP engine");
33
+ });
34
+ }
35
+
36
+ // Intercept socket end to clean up VoIP worker threads cleanly
37
+ const originalEnd = baseSocket.end ? baseSocket.end.bind(baseSocket) : undefined;
38
+ baseSocket.end = (error) => {
39
+ try {
40
+ voipEngine?.destroy?.();
41
+ } catch {}
42
+ return originalEnd ? originalEnd(error) : undefined;
43
+ };
44
+
45
+ return {
46
+ ...baseSocket,
47
+
48
+ /**
49
+ * Place an outbound WhatsApp VoIP call (voice or video)
50
+ * @param phoneNumber Destination phone number or JID
51
+ * @param options Call options (durationMs, isVideo, audioSource)
52
+ * @returns ActiveCall instance
53
+ */
54
+ call: async (phoneNumber, options = {}) => {
55
+ const engine = await getVoipEngine();
56
+ return engine.startCall(phoneNumber, options);
57
+ },
58
+
59
+ /**
60
+ * Place an outbound WhatsApp video call
61
+ * @param phoneNumber Destination phone number or JID
62
+ * @param options Call options
63
+ * @returns ActiveCall instance
64
+ */
65
+ callVideo: async (phoneNumber, options = {}) => {
66
+ const engine = await getVoipEngine();
67
+ return engine.startCall(phoneNumber, { ...options, isVideo: true });
68
+ },
69
+
70
+ /**
71
+ * Currently active call on this socket, or null if idle
72
+ */
73
+ get activeCall() {
74
+ return voipEngine?.activeCall || null;
75
+ },
76
+
77
+ /**
78
+ * Clears any active call state to allow a new call
79
+ */
80
+ clearActiveCall: () => {
81
+ voipEngine?.clearActiveCall?.();
82
+ },
83
+
84
+ /**
85
+ * Accept an incoming WhatsApp call (voice or video) via WebAssembly VoIP stack
86
+ * @param callId Unique Call ID from offer
87
+ * @param from Caller JID or phone number
88
+ * @param options Call options (isVideo, audioSource)
89
+ * @returns ActiveCall instance
90
+ */
91
+ acceptCall: async (callId, from, options = {}) => {
92
+ const engine = await getVoipEngine();
93
+ return engine.acceptCall(callId, from, options);
94
+ },
95
+
96
+ /**
97
+ * Answer an incoming call (alias for acceptCall)
98
+ */
99
+ answerCall: async (callId, from, options = {}) => {
100
+ const engine = await getVoipEngine();
101
+ return engine.acceptCall(callId, from, options);
102
+ },
103
+
104
+ /**
105
+ * Reject an incoming call
106
+ */
107
+ rejectCall: async (callId, from, reason = 0) => {
108
+ const engine = await getVoipEngine();
109
+ return engine.rejectCall(callId, from, reason);
110
+ },
111
+
112
+ /**
113
+ * Low-level VoIP engine controller
114
+ */
115
+ voip: {
116
+ getEngine: getVoipEngine,
117
+ destroy: () => voipEngine?.destroy?.()
118
+ }
119
+ };
120
+ };
121
+
122
+ exports.makeVoipSocket = makeVoipSocket;
123
+
@@ -2,7 +2,7 @@
2
2
 
3
3
  Object.defineProperty(exports, "__esModule", { value: true })
4
4
 
5
- const { caching } = require("cache-manager")
5
+ const cacheManager = require("cache-manager")
6
6
  const { proto } = require("../../WAProto")
7
7
  const {
8
8
  BufferJSON,
@@ -12,7 +12,12 @@ const { default: logger } = require("../Utils/logger")
12
12
 
13
13
  const makeCacheManagerAuthState = async (store, sessionKey) => {
14
14
  const defaultKey = (file) => `${sessionKey}:${file}`
15
- const databaseConn = await caching(store)
15
+ const databaseConn = store?.get && store?.set
16
+ ? store
17
+ : typeof cacheManager.createCache === 'function'
18
+ ? (store ? cacheManager.createCache(store) : cacheManager.createCache())
19
+ : await cacheManager.caching(store)
20
+
16
21
  const writeData = async (file, data) => {
17
22
  let ttl = undefined
18
23
  if (file === 'creds') {
@@ -43,8 +48,12 @@ const makeCacheManagerAuthState = async (store, sessionKey) => {
43
48
  }
44
49
  const clearState = async () => {
45
50
  try {
46
- const result = await databaseConn.store.keys(`${sessionKey}*`)
47
- await Promise.all(result.map(async (key) => await databaseConn.del(key)))
51
+ if (typeof databaseConn.clear === 'function') {
52
+ await databaseConn.clear()
53
+ } else if (databaseConn.store?.keys) {
54
+ const result = await databaseConn.store.keys(`${sessionKey}*`)
55
+ await Promise.all(result.map(async (key) => await databaseConn.del(key)))
56
+ }
48
57
  }
49
58
  catch (err) {
50
59
  }
@@ -86,4 +95,4 @@ const makeCacheManagerAuthState = async (store, sessionKey) => {
86
95
 
87
96
  module.exports = {
88
97
  makeCacheManagerAuthState
89
- }
98
+ }
@@ -187,6 +187,10 @@ export interface BaileysEventMap {
187
187
  'contacts.update': Partial<Contact>[]
188
188
  'group-participants.update': { id: string; author?: string; participants: string[]; action: 'add' | 'remove' | 'demote' | 'promote' }
189
189
  'call': WACallEvent[]
190
+ 'call.created': { callId: string; to: string; isVideo?: boolean }
191
+ 'call.ringing': { callId: string; to: string }
192
+ 'call.connected': { callId: string; to: string; isVideo?: boolean }
193
+ 'call.ended': { callId: string; to: string; reason: string }
190
194
  [key: string]: any
191
195
  }
192
196
 
@@ -3,7 +3,22 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true })
4
4
 
5
5
  const { randomBytes } = require("crypto")
6
- const { default: PQueue } = require("p-queue")
6
+ let PQueue
7
+ try {
8
+ const pq = require("p-queue")
9
+ PQueue = pq.default || pq
10
+ } catch {
11
+ PQueue = class SimpleQueue {
12
+ constructor() {
13
+ this._chain = Promise.resolve()
14
+ }
15
+ add(fn) {
16
+ const result = this._chain.then(() => fn())
17
+ this._chain = result.catch(() => {})
18
+ return result
19
+ }
20
+ }
21
+ }
7
22
  const { NodeCache } = require("@cacheable/node-cache")
8
23
  const { DEFAULT_CACHE_TTLS } = require("../Defaults/constants")
9
24
  const { AsyncLocalStorage } = require("async_hooks")
@@ -27,13 +27,17 @@ const Browsers = {
27
27
  macOS: browser => ['Mac OS', browser, '14.4.1'],
28
28
  baileys: browser => ['Baileys', browser, '6.5.0'],
29
29
  windows: browser => ['Windows', browser, '10.0.22631'],
30
- //android: browser => [browser, 'Android', ''],
30
+ android: browser => [browser, 'Android', ''],
31
31
  /** The appropriate browser based on your OS & release */
32
32
  appropriate: browser => [PLATFORM_MAP[platform()] || 'Ubuntu', browser, release()]
33
33
  }
34
34
 
35
35
  const getPlatformId = (browser) => {
36
- const platformType = proto.DeviceProps.PlatformType[browser.toUpperCase()]
36
+ const platform = browser.toUpperCase()
37
+ if (platform === 'ANDROID') {
38
+ return (proto.DeviceProps.PlatformType.ANDROID_PHONE || 16).toString()
39
+ }
40
+ const platformType = proto.DeviceProps.PlatformType[platform]
37
41
  return platformType ? platformType.toString() : '1' //chrome
38
42
  }
39
43