@lazyneoaz/testfca 1.0.4 → 1.0.5
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/package.json +1 -1
- package/src/apis/listenMqtt.js +106 -102
- package/src/e2ee/bridge.js +75 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lazyneoaz/testfca",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"type": "commonjs",
|
|
5
5
|
"description": "Advanced Facebook Chat API client for building Messenger bots — supports real-time messaging, thread management, MQTT, session stability, anti-automation protection, and real E2EE via Signal Protocol.",
|
|
6
6
|
"main": "index.js",
|
package/src/apis/listenMqtt.js
CHANGED
|
@@ -592,130 +592,134 @@ async function listenMqtt(defaultFuncs, api, ctx, globalCallback, scheduleReconn
|
|
|
592
592
|
}
|
|
593
593
|
while (ctx._e2eePollingActive) {
|
|
594
594
|
try {
|
|
595
|
-
// Yield to event loop before blocking poll
|
|
595
|
+
// Yield to event loop before blocking poll
|
|
596
596
|
await new Promise(resolve => setImmediate(resolve));
|
|
597
597
|
if (!ctx._e2eePollingActive) break;
|
|
598
598
|
|
|
599
|
-
//
|
|
600
|
-
const
|
|
599
|
+
// pollEvents returns { events: [...] } — iterate the array
|
|
600
|
+
const result = await api.e2ee.pollEvents(1000);
|
|
601
601
|
if (!ctx._e2eePollingActive) break;
|
|
602
602
|
|
|
603
|
-
// No
|
|
604
|
-
if (!
|
|
603
|
+
// No events returned (timeout or empty)
|
|
604
|
+
if (!result || !Array.isArray(result.events) || result.events.length === 0) continue;
|
|
605
605
|
|
|
606
|
-
const
|
|
606
|
+
for (const ev of result.events) {
|
|
607
|
+
if (!ctx._e2eePollingActive) break;
|
|
607
608
|
|
|
608
|
-
|
|
609
|
-
if (evType === 'timeout') continue;
|
|
609
|
+
const evType = (ev.type || '').toLowerCase();
|
|
610
610
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
utils.warn("E2EE", "Bridge closed. Stopping DM poll loop.");
|
|
614
|
-
ctx._e2eePollingActive = false;
|
|
615
|
-
break;
|
|
616
|
-
}
|
|
611
|
+
// ── timeout: no event arrived during the poll window ──
|
|
612
|
+
if (evType === 'timeout') continue;
|
|
617
613
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
utils.warn("E2EE", "Bridge error event:", msg);
|
|
622
|
-
if (ev.data && ev.data.code === 1) {
|
|
614
|
+
// ── closed: bridge shut down ──
|
|
615
|
+
if (evType === 'closed') {
|
|
616
|
+
utils.warn("E2EE", "Bridge closed. Stopping DM poll loop.");
|
|
623
617
|
ctx._e2eePollingActive = false;
|
|
624
618
|
break;
|
|
625
619
|
}
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
620
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
621
|
+
// ── permanent error (e.g. session invalid) ──
|
|
622
|
+
if (evType === 'error') {
|
|
623
|
+
const msg = ev.data && ev.data.message ? ev.data.message : JSON.stringify(ev.data);
|
|
624
|
+
utils.warn("E2EE", "Bridge error event:", msg);
|
|
625
|
+
if (ev.data && ev.data.code === 1) {
|
|
626
|
+
ctx._e2eePollingActive = false;
|
|
627
|
+
break;
|
|
628
|
+
}
|
|
629
|
+
continue;
|
|
630
|
+
}
|
|
634
631
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
if (!d) continue;
|
|
639
|
-
// threadId may be 0 for pure-JID threads — fall back to chatJid
|
|
640
|
-
const chatJid = d.chatJid || '';
|
|
641
|
-
const senderJid = d.senderJid || '';
|
|
642
|
-
const threadID = String(d.threadId || chatJid.replace(/@.*/, '') || '');
|
|
643
|
-
const senderID = String(d.senderId || senderJid.replace(/@.*/, '') || '');
|
|
644
|
-
if (!threadID || !senderID) {
|
|
645
|
-
utils.log("E2EE", "e2eeMessage missing IDs — raw:", JSON.stringify(d).slice(0, 300));
|
|
632
|
+
// ── E2EE connection confirmed by the bridge ──
|
|
633
|
+
if (evType === 'e2eeconnected') {
|
|
634
|
+
utils.log("E2EE", "E2EE fully connected (bridge event).");
|
|
646
635
|
continue;
|
|
647
636
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
threadID
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
637
|
+
|
|
638
|
+
// ── Incoming E2EE DM ─────────────────────────────────
|
|
639
|
+
if (evType === 'e2eemessage') {
|
|
640
|
+
const d = ev.data;
|
|
641
|
+
if (!d) continue;
|
|
642
|
+
// threadId may be 0 for pure-JID threads — fall back to chatJid
|
|
643
|
+
const chatJid = d.chatJid || '';
|
|
644
|
+
const senderJid = d.senderJid || '';
|
|
645
|
+
const threadID = String(d.threadId || chatJid.replace(/@.*/, '') || '');
|
|
646
|
+
const senderID = String(d.senderId || senderJid.replace(/@.*/, '') || '');
|
|
647
|
+
if (!threadID || !senderID) {
|
|
648
|
+
utils.log("E2EE", "e2eeMessage missing IDs — raw:", JSON.stringify(d).slice(0, 300));
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
if (senderID === ctx.userID.toString() && !ctx.globalOptions.selfListen) continue;
|
|
652
|
+
|
|
653
|
+
utils.log("E2EE", "Incoming E2EE DM from", senderID, "→ thread", threadID, "body:", JSON.stringify(d.text || '').slice(0, 80));
|
|
654
|
+
|
|
655
|
+
const fmtMsg = {
|
|
656
|
+
type: 'message',
|
|
657
|
+
senderID: utils.formatID(senderID),
|
|
658
|
+
body: d.text || '',
|
|
659
|
+
threadID: utils.formatID(threadID),
|
|
660
|
+
messageID: d.id || '',
|
|
661
|
+
isGroup: false,
|
|
662
|
+
attachments: [],
|
|
663
|
+
mentions: {},
|
|
664
|
+
timestamp: Number(d.timestampMs || Date.now()),
|
|
665
|
+
isUnread: true,
|
|
666
|
+
};
|
|
667
|
+
if (ctx.globalOptions.autoMarkDelivery) {
|
|
668
|
+
try { api.markAsDelivered(fmtMsg.threadID, fmtMsg.messageID); } catch (_) {}
|
|
669
|
+
}
|
|
670
|
+
if (ctx.globalOptions.autoMarkRead) {
|
|
671
|
+
try { api.markAsRead(fmtMsg.threadID); } catch (_) {}
|
|
672
|
+
}
|
|
673
|
+
globalCallback(null, fmtMsg);
|
|
674
|
+
continue;
|
|
666
675
|
}
|
|
667
|
-
|
|
668
|
-
|
|
676
|
+
|
|
677
|
+
// ── Incoming regular (non-E2EE) message via bridge ───
|
|
678
|
+
if (evType === 'message') {
|
|
679
|
+
const d = ev.data;
|
|
680
|
+
if (!d) continue;
|
|
681
|
+
const threadID = String(d.threadId || '');
|
|
682
|
+
const senderID = String(d.senderId || '');
|
|
683
|
+
if (!threadID || !senderID) continue;
|
|
684
|
+
if (senderID === ctx.userID.toString() && !ctx.globalOptions.selfListen) continue;
|
|
685
|
+
globalCallback(null, {
|
|
686
|
+
type: 'message',
|
|
687
|
+
senderID: utils.formatID(senderID),
|
|
688
|
+
body: d.text || '',
|
|
689
|
+
threadID: utils.formatID(threadID),
|
|
690
|
+
messageID: d.id || '',
|
|
691
|
+
isGroup: !!(d.isGroup),
|
|
692
|
+
attachments: [],
|
|
693
|
+
mentions: {},
|
|
694
|
+
timestamp: Number(d.timestampMs || Date.now()),
|
|
695
|
+
isUnread: true,
|
|
696
|
+
});
|
|
697
|
+
continue;
|
|
669
698
|
}
|
|
670
|
-
globalCallback(null, fmtMsg);
|
|
671
|
-
continue;
|
|
672
|
-
}
|
|
673
699
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
});
|
|
694
|
-
continue;
|
|
695
|
-
}
|
|
700
|
+
// ── E2EE reaction ────────────────────────────────────
|
|
701
|
+
if (evType === 'e2eereaction') {
|
|
702
|
+
if (!ctx.globalOptions.listenEvents) continue;
|
|
703
|
+
const d = ev.data;
|
|
704
|
+
if (!d) continue;
|
|
705
|
+
const chatJid = d.chatJid || '';
|
|
706
|
+
const senderJid = d.senderJid || '';
|
|
707
|
+
globalCallback(null, {
|
|
708
|
+
type: 'message_reaction',
|
|
709
|
+
threadID: utils.formatID(chatJid.replace(/@.*/, '')),
|
|
710
|
+
messageID: d.messageId || '',
|
|
711
|
+
reaction: d.reaction || '',
|
|
712
|
+
senderID: utils.formatID(senderJid.replace(/@.*/, '')),
|
|
713
|
+
offlineThreadingId: null,
|
|
714
|
+
timestamp: Date.now(),
|
|
715
|
+
isGroup: false,
|
|
716
|
+
});
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
696
719
|
|
|
697
|
-
|
|
698
|
-
if (evType === 'e2eereaction') {
|
|
699
|
-
if (!ctx.globalOptions.listenEvents) continue;
|
|
700
|
-
const d = ev.data;
|
|
701
|
-
if (!d) continue;
|
|
702
|
-
const chatJid = d.chatJid || '';
|
|
703
|
-
const senderJid = d.senderJid || '';
|
|
704
|
-
globalCallback(null, {
|
|
705
|
-
type: 'message_reaction',
|
|
706
|
-
threadID: utils.formatID(chatJid.replace(/@.*/, '')),
|
|
707
|
-
messageID: d.messageId || '',
|
|
708
|
-
reaction: d.reaction || '',
|
|
709
|
-
senderID: utils.formatID(senderJid.replace(/@.*/, '')),
|
|
710
|
-
offlineThreadingId: null,
|
|
711
|
-
timestamp: Date.now(),
|
|
712
|
-
isGroup: false,
|
|
713
|
-
});
|
|
714
|
-
continue;
|
|
720
|
+
// ready / reconnected / disconnected / deviceDataChanged / raw → ignore silently
|
|
715
721
|
}
|
|
716
722
|
|
|
717
|
-
// ready / reconnected / disconnected / deviceDataChanged / raw → ignore silently
|
|
718
|
-
|
|
719
723
|
} catch (pollErr) {
|
|
720
724
|
if (!ctx._e2eePollingActive) break;
|
|
721
725
|
utils.warn("E2EE", "pollEvents error:", pollErr && pollErr.message ? pollErr.message : pollErr);
|
package/src/e2ee/bridge.js
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
* Lightweight E2EE bridge — delegates to the `meta-messenger.js` npm package.
|
|
5
5
|
*
|
|
6
6
|
* Our postinstall replaces meta-messenger.js's binary with our TLS-fixed build
|
|
7
|
-
* so that prekey HTTPS requests succeed in sandboxed environments (Replit, Docker
|
|
7
|
+
* so that prekey HTTPS requests succeed in sandboxed environments (Replit, Docker,
|
|
8
|
+
* Railway, etc.). We also patch at runtime (ensureTlsFixedBinary) so the fix
|
|
9
|
+
* applies even when postinstall was skipped (npm ci --ignore-scripts, Railway
|
|
10
|
+
* production installs, etc.).
|
|
8
11
|
*
|
|
9
12
|
* Key design decisions:
|
|
10
13
|
* - enableE2EE: false → we call connectE2EE() explicitly; no auto-start race
|
|
@@ -12,12 +15,83 @@
|
|
|
12
15
|
* - devicePath defaults to ".nkxfca_e2ee_device.json" in cwd when not provided
|
|
13
16
|
*/
|
|
14
17
|
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
// ── Runtime binary patcher ────────────────────────────────────────────────────
|
|
22
|
+
// Ensures the TLS-fixed binary is in place before meta-messenger.js is loaded.
|
|
23
|
+
// Runs once; safe to call multiple times.
|
|
24
|
+
|
|
25
|
+
let _patchDone = false;
|
|
26
|
+
|
|
27
|
+
function _platformKey() {
|
|
28
|
+
const p = process.platform;
|
|
29
|
+
const a = process.arch;
|
|
30
|
+
if (p === 'linux' && a === 'x64') return 'linux-x64-gnu';
|
|
31
|
+
if (p === 'linux' && a === 'arm64') return 'linux-arm64-gnu';
|
|
32
|
+
if (p === 'darwin' && a === 'x64') return 'darwin-x64';
|
|
33
|
+
if (p === 'darwin' && a === 'arm64') return 'darwin-arm64';
|
|
34
|
+
if (p === 'win32' && a === 'x64') return 'win32-x64-msvc';
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function _binaryExt() {
|
|
39
|
+
if (process.platform === 'win32') return '.dll';
|
|
40
|
+
if (process.platform === 'darwin') return '.dylib';
|
|
41
|
+
return '.so';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function ensureTlsFixedBinary() {
|
|
45
|
+
if (_patchDone) return;
|
|
46
|
+
_patchDone = true;
|
|
47
|
+
|
|
48
|
+
if (process.env.NKXFCA_SKIP_POSTINSTALL === 'true') return;
|
|
49
|
+
|
|
50
|
+
const key = _platformKey();
|
|
51
|
+
if (!key) return;
|
|
52
|
+
|
|
53
|
+
const ext = _binaryExt();
|
|
54
|
+
// bridge.js lives at src/e2ee/bridge.js → prebuilt/ is two levels up
|
|
55
|
+
const ourBin = path.join(__dirname, '..', '..', 'prebuilt', key, 'messagix' + ext);
|
|
56
|
+
if (!fs.existsSync(ourBin)) return;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
const pkgJson = require.resolve('meta-messenger.js/package.json');
|
|
60
|
+
const buildDir = path.join(path.dirname(pkgJson), 'build');
|
|
61
|
+
const mmBin = path.join(buildDir, 'messagix' + ext);
|
|
62
|
+
|
|
63
|
+
fs.mkdirSync(buildDir, { recursive: true });
|
|
64
|
+
|
|
65
|
+
// Only copy if sizes differ (avoids unnecessary writes on every boot)
|
|
66
|
+
let needsCopy = true;
|
|
67
|
+
if (fs.existsSync(mmBin)) {
|
|
68
|
+
needsCopy = fs.statSync(ourBin).size !== fs.statSync(mmBin).size;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (needsCopy) {
|
|
72
|
+
fs.copyFileSync(ourBin, mmBin);
|
|
73
|
+
fs.chmodSync(mmBin, 0o755);
|
|
74
|
+
console.log('[nkxfca] runtime patch: applied TLS-fixed binary (' + key + ')');
|
|
75
|
+
}
|
|
76
|
+
} catch (err) {
|
|
77
|
+
// Non-fatal — warn so the user knows why E2EE may not work
|
|
78
|
+
console.warn('[nkxfca] runtime patch failed (' + err.message + '). E2EE TLS errors may occur in sandboxed environments.');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
15
84
|
let _Client = null;
|
|
16
85
|
let _loadError = null;
|
|
17
86
|
|
|
18
87
|
function loadClient() {
|
|
19
88
|
if (_Client) return _Client;
|
|
20
89
|
if (_loadError) throw _loadError;
|
|
90
|
+
|
|
91
|
+
// Patch binary BEFORE the first require('meta-messenger.js') so the
|
|
92
|
+
// TLS-fixed .so/.dylib/.dll is loaded by Node rather than the original.
|
|
93
|
+
ensureTlsFixedBinary();
|
|
94
|
+
|
|
21
95
|
try {
|
|
22
96
|
const mm = require('meta-messenger.js');
|
|
23
97
|
_Client = mm.Client || mm.default?.Client;
|