@areumtecnologia/wasockets 2.0.0 → 2.3.0
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 +1987 -1649
- package/lib/Client/Builders/Buttons.js +127 -0
- package/lib/Client/Builders/EventBuilder.js +122 -0
- package/lib/Client/Builders/InteractiveMessage.js +150 -0
- package/lib/Client/Builders/Lists.js +78 -0
- package/lib/Client/Builders/PollBuilder.js +84 -0
- package/lib/Client/Builders/index.js +21 -0
- package/lib/Client/Constants.js +54 -0
- package/lib/Client/Context/CallContext.js +96 -0
- package/lib/Client/Context/MediaObject.js +155 -0
- package/lib/Client/Context/MessageContext.js +146 -0
- package/lib/Client/Modules/BusinessManager.js +155 -0
- package/lib/Client/Modules/ChannelManager.js +144 -0
- package/lib/Client/Modules/ChatManager.js +113 -0
- package/lib/Client/Modules/CommunityManager.js +136 -0
- package/lib/Client/Modules/GroupManager.js +194 -0
- package/lib/Client/Modules/LabelManager.js +123 -0
- package/lib/Client/Modules/MessageManager.js +198 -0
- package/lib/Client/Modules/PresenceManager.js +68 -0
- package/lib/Client/Modules/PrivacyManager.js +98 -0
- package/lib/Client/Modules/ProfileManager.js +92 -0
- package/lib/Client/Modules/UserManager.js +92 -0
- package/lib/Client/Modules/VoipManager.js +117 -0
- package/lib/Client/Modules/index.js +29 -0
- package/lib/Client/Normalizers/EventNormalizer.js +82 -0
- package/lib/Client/Normalizers/JidNormalizer.js +93 -0
- package/lib/Client/Normalizers/MessageNormalizer.js +134 -0
- package/lib/Client/WaClient.js +438 -0
- package/lib/Client/index.d.ts +464 -0
- package/lib/Client/index.js +76 -0
- package/lib/Defaults/baileys-version.json +1 -1
- package/lib/Defaults/connection.js +14 -1
- package/lib/Socket/index.js +51 -48
- package/lib/Socket/messages-recv.js +74 -13
- package/lib/Socket/messages-send.js +50 -25
- package/lib/Socket/socket.js +13 -0
- package/lib/Socket/voip.js +123 -0
- package/lib/Store/make-cache-manager-store.js +14 -5
- package/lib/Types/index.d.ts +4 -0
- package/lib/Utils/auth-utils.js +16 -1
- package/lib/Utils/browser-utils.js +6 -2
- package/lib/Utils/generics.js +47 -8
- package/lib/Utils/index.d.ts +1 -0
- package/lib/Utils/messages-media.js +44 -21
- package/lib/Utils/pre-key-manager.js +16 -1
- package/lib/Utils/validate-connection.js +10 -11
- package/lib/VoIP/VoIPCrypto.js +150 -0
- package/lib/VoIP/VoIPManager.js +469 -0
- package/lib/VoIP/VoIPMediaEngine.js +224 -0
- package/lib/VoIP/VoIPProtocol.js +354 -0
- package/lib/VoIP/VoIPRelayTransport.js +173 -0
- package/lib/VoIP/VoIPSession.js +314 -0
- package/lib/VoIP/assets/wasm/loader.js +5 -0
- package/lib/VoIP/assets/wasm/whatsapp.wasm +0 -0
- package/lib/VoIP/assets/wasm/worker-modules.js +305 -0
- package/lib/VoIP/engine/audio-feeder.d.mts +15 -0
- package/lib/VoIP/engine/audio-feeder.mjs +243 -0
- package/lib/VoIP/engine/index.d.mts +91 -0
- package/lib/VoIP/engine/index.mjs +1683 -0
- package/lib/VoIP/engine/relay-transport.d.mts +43 -0
- package/lib/VoIP/engine/relay-transport.mjs +607 -0
- package/lib/VoIP/engine/signaling.d.mts +44 -0
- package/lib/VoIP/engine/signaling.mjs +893 -0
- package/lib/VoIP/engine/types.d.mts +183 -0
- package/lib/VoIP/engine/types.mjs +17 -0
- package/lib/VoIP/engine/wasm-engine.d.mts +103 -0
- package/lib/VoIP/engine/wasm-engine.mjs +1397 -0
- package/lib/VoIP/engine/worker-bootstrap.mjs +1049 -0
- package/lib/VoIP/index.js +51 -0
- package/lib/VoIP/utils/audio.d.ts +28 -0
- package/lib/VoIP/utils/audio.js +148 -0
- package/lib/WABinary/generic-utils.js +22 -6
- package/lib/index.d.ts +382 -274
- package/lib/index.js +5 -0
- package/package.json +51 -49
|
@@ -273,15 +273,34 @@ const makeMessagesRecvSocket = (config) => {
|
|
|
273
273
|
}],
|
|
274
274
|
})
|
|
275
275
|
|
|
276
|
-
await
|
|
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
|
|
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:
|
|
315
|
+
count: reason.toString(),
|
|
297
316
|
},
|
|
298
317
|
content: undefined,
|
|
299
318
|
}],
|
|
300
319
|
})
|
|
301
320
|
|
|
302
|
-
await
|
|
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:
|
|
359
|
+
content: acceptContent,
|
|
319
360
|
}],
|
|
320
361
|
})
|
|
321
362
|
|
|
322
|
-
await
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 }) => {
|
package/lib/Socket/socket.js
CHANGED
|
@@ -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
|
|
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 =
|
|
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
|
-
|
|
47
|
-
|
|
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
|
+
}
|
package/lib/Types/index.d.ts
CHANGED
|
@@ -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
|
|
package/lib/Utils/auth-utils.js
CHANGED
|
@@ -3,7 +3,22 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true })
|
|
4
4
|
|
|
5
5
|
const { randomBytes } = require("crypto")
|
|
6
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
package/lib/Utils/generics.js
CHANGED
|
@@ -232,12 +232,34 @@ const printQRIfNecessaryListener = (ev, logger) => {
|
|
|
232
232
|
})
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
+
const parseWaVersion = (ver) => {
|
|
236
|
+
if (Array.isArray(ver) && ver.length === 3) {
|
|
237
|
+
return ver.map(Number)
|
|
238
|
+
}
|
|
239
|
+
if (typeof ver === 'string') {
|
|
240
|
+
const parts = ver.split('.').map(Number)
|
|
241
|
+
if (parts.length === 3 && parts.every(n => !isNaN(n))) {
|
|
242
|
+
return parts
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return undefined
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const getLocalPackageWaVersion = () => {
|
|
249
|
+
try {
|
|
250
|
+
const pkg = require('../../package.json')
|
|
251
|
+
return parseWaVersion(pkg.whatsappVersion) || version
|
|
252
|
+
} catch {
|
|
253
|
+
return version
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
235
257
|
/**
|
|
236
258
|
* utility that fetches latest baileys version from the master branch.
|
|
237
259
|
* Use to ensure your WA connection is always on the latest version
|
|
238
260
|
*/
|
|
239
261
|
const fetchLatestBaileysVersion = async (options = {}) => {
|
|
240
|
-
const URL = 'https://raw.githubusercontent.com/
|
|
262
|
+
const URL = 'https://raw.githubusercontent.com/areumtecnologia/WaSockets/master/package.json'
|
|
241
263
|
try {
|
|
242
264
|
const signal = options.signal || AbortSignal.timeout(15000)
|
|
243
265
|
const result = await fetch(URL, {
|
|
@@ -245,15 +267,22 @@ const fetchLatestBaileysVersion = async (options = {}) => {
|
|
|
245
267
|
signal,
|
|
246
268
|
method: 'GET'
|
|
247
269
|
})
|
|
270
|
+
if (!result.ok) {
|
|
271
|
+
throw new Boom(`Failed to fetch package.json: ${result.statusText}`, { statusCode: result.status })
|
|
272
|
+
}
|
|
248
273
|
const json = await result.json()
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
274
|
+
const remoteVersion = parseWaVersion(json.whatsappVersion)
|
|
275
|
+
if (remoteVersion) {
|
|
276
|
+
return {
|
|
277
|
+
version: remoteVersion,
|
|
278
|
+
isLatest: true
|
|
279
|
+
}
|
|
252
280
|
}
|
|
281
|
+
throw new Boom('whatsappVersion not defined or invalid in remote package.json')
|
|
253
282
|
}
|
|
254
283
|
catch (error) {
|
|
255
284
|
return {
|
|
256
|
-
version:
|
|
285
|
+
version: getLocalPackageWaVersion(),
|
|
257
286
|
isLatest: false,
|
|
258
287
|
error
|
|
259
288
|
}
|
|
@@ -291,7 +320,7 @@ const fetchLatestWaWebVersion = async (options = {}) => {
|
|
|
291
320
|
|
|
292
321
|
if (!match?.[1]) {
|
|
293
322
|
return {
|
|
294
|
-
version:
|
|
323
|
+
version: getLocalPackageWaVersion(),
|
|
295
324
|
isLatest: false,
|
|
296
325
|
error: {
|
|
297
326
|
message: 'Could not find client revision in the fetched content'
|
|
@@ -307,7 +336,7 @@ const fetchLatestWaWebVersion = async (options = {}) => {
|
|
|
307
336
|
}
|
|
308
337
|
catch (error) {
|
|
309
338
|
return {
|
|
310
|
-
version:
|
|
339
|
+
version: getLocalPackageWaVersion(),
|
|
311
340
|
isLatest: false,
|
|
312
341
|
error
|
|
313
342
|
}
|
|
@@ -382,9 +411,19 @@ const getCallStatusFromNode = ({ tag, attrs }) => {
|
|
|
382
411
|
case 'accept':
|
|
383
412
|
status = 'accept'
|
|
384
413
|
break
|
|
385
|
-
|
|
414
|
+
case 'relay':
|
|
415
|
+
status = 'relay'
|
|
416
|
+
break
|
|
417
|
+
case 'relaylatency':
|
|
418
|
+
status = 'relaylatency'
|
|
419
|
+
break
|
|
420
|
+
case 'ring':
|
|
421
|
+
case 'ringing':
|
|
386
422
|
status = 'ringing'
|
|
387
423
|
break
|
|
424
|
+
default:
|
|
425
|
+
status = tag || 'ringing'
|
|
426
|
+
break
|
|
388
427
|
}
|
|
389
428
|
return status
|
|
390
429
|
}
|
package/lib/Utils/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const Browsers: {
|
|
|
15
15
|
macOS(browser: string): [string, string, string]
|
|
16
16
|
baileys(browser: string): [string, string, string]
|
|
17
17
|
windows(browser: string): [string, string, string]
|
|
18
|
+
android(browser: string): [string, string, string]
|
|
18
19
|
appropriate(browser: string): [string, string, string]
|
|
19
20
|
}
|
|
20
21
|
|