@dongdev/fca-unofficial 3.0.30 → 4.0.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/LICENSE +191 -0
- package/README.md +224 -406
- package/dist/index.d.mts +1241 -0
- package/dist/index.d.ts +1241 -0
- package/dist/index.js +27749 -0
- package/dist/index.mjs +27713 -0
- package/docs/ARCHITECTURE.md +467 -0
- package/docs/DOCS.md +686 -0
- package/fca-config.example.json +33 -0
- package/package.json +33 -22
- package/test/fca.test.cjs +533 -0
- package/CHANGELOG.md +0 -293
- package/DOCS.md +0 -2712
- package/func/checkUpdate.js +0 -222
- package/func/logAdapter.js +0 -33
- package/func/logger.js +0 -48
- package/index.d.ts +0 -751
- package/index.js +0 -8
- package/module/config.js +0 -40
- package/module/login.js +0 -133
- package/module/loginHelper.js +0 -1296
- package/module/options.js +0 -44
- package/src/api/action/addExternalModule.js +0 -25
- package/src/api/action/changeAvatar.js +0 -137
- package/src/api/action/changeBio.js +0 -75
- package/src/api/action/enableAutoSaveAppState.js +0 -73
- package/src/api/action/getCurrentUserID.js +0 -7
- package/src/api/action/handleFriendRequest.js +0 -57
- package/src/api/action/logout.js +0 -76
- package/src/api/action/refreshFb_dtsg.js +0 -48
- package/src/api/action/setPostReaction.js +0 -106
- package/src/api/action/unfriend.js +0 -54
- package/src/api/http/httpGet.js +0 -46
- package/src/api/http/httpPost.js +0 -52
- package/src/api/http/postFormData.js +0 -47
- package/src/api/messaging/addUserToGroup.js +0 -68
- package/src/api/messaging/changeAdminStatus.js +0 -126
- package/src/api/messaging/changeArchivedStatus.js +0 -55
- package/src/api/messaging/changeBlockedStatus.js +0 -48
- package/src/api/messaging/changeGroupImage.js +0 -91
- package/src/api/messaging/changeNickname.js +0 -70
- package/src/api/messaging/changeThreadColor.js +0 -79
- package/src/api/messaging/changeThreadEmoji.js +0 -111
- package/src/api/messaging/createNewGroup.js +0 -88
- package/src/api/messaging/createPoll.js +0 -46
- package/src/api/messaging/createThemeAI.js +0 -98
- package/src/api/messaging/deleteMessage.js +0 -136
- package/src/api/messaging/deleteThread.js +0 -56
- package/src/api/messaging/editMessage.js +0 -68
- package/src/api/messaging/forwardAttachment.js +0 -57
- package/src/api/messaging/getEmojiUrl.js +0 -29
- package/src/api/messaging/getFriendsList.js +0 -82
- package/src/api/messaging/getMessage.js +0 -829
- package/src/api/messaging/getThemePictures.js +0 -62
- package/src/api/messaging/handleMessageRequest.js +0 -65
- package/src/api/messaging/markAsDelivered.js +0 -57
- package/src/api/messaging/markAsRead.js +0 -88
- package/src/api/messaging/markAsReadAll.js +0 -49
- package/src/api/messaging/markAsSeen.js +0 -61
- package/src/api/messaging/muteThread.js +0 -50
- package/src/api/messaging/removeUserFromGroup.js +0 -62
- package/src/api/messaging/resolvePhotoUrl.js +0 -43
- package/src/api/messaging/scheduler.js +0 -264
- package/src/api/messaging/searchForThread.js +0 -52
- package/src/api/messaging/sendMessage.js +0 -270
- package/src/api/messaging/sendTypingIndicator.js +0 -74
- package/src/api/messaging/setMessageReaction.js +0 -91
- package/src/api/messaging/setTitle.js +0 -124
- package/src/api/messaging/shareContact.js +0 -49
- package/src/api/messaging/threadColors.js +0 -128
- package/src/api/messaging/unsendMessage.js +0 -81
- package/src/api/messaging/uploadAttachment.js +0 -492
- package/src/api/socket/core/connectMqtt.js +0 -258
- package/src/api/socket/core/emitAuth.js +0 -103
- package/src/api/socket/core/getSeqID.js +0 -320
- package/src/api/socket/core/getTaskResponseData.js +0 -25
- package/src/api/socket/core/parseDelta.js +0 -377
- package/src/api/socket/detail/buildStream.js +0 -215
- package/src/api/socket/detail/constants.js +0 -28
- package/src/api/socket/listenMqtt.js +0 -377
- package/src/api/socket/middleware/index.js +0 -216
- package/src/api/threads/getThreadHistory.js +0 -664
- package/src/api/threads/getThreadInfo.js +0 -295
- package/src/api/threads/getThreadList.js +0 -293
- package/src/api/threads/getThreadPictures.js +0 -78
- package/src/api/users/getUserID.js +0 -65
- package/src/api/users/getUserInfo.js +0 -399
- package/src/api/users/getUserInfoV2.js +0 -134
- package/src/core/sendReqMqtt.js +0 -96
- package/src/database/models/index.js +0 -87
- package/src/database/models/thread.js +0 -50
- package/src/database/models/user.js +0 -46
- package/src/database/threadData.js +0 -98
- package/src/database/userData.js +0 -89
- package/src/remote/remoteClient.js +0 -123
- package/src/utils/broadcast.js +0 -51
- package/src/utils/client.js +0 -10
- package/src/utils/constants.js +0 -23
- package/src/utils/cookies.js +0 -68
- package/src/utils/format.js +0 -1174
- package/src/utils/headers.js +0 -115
- package/src/utils/loginParser.js +0 -365
- package/src/utils/messageFormat.js +0 -1173
- package/src/utils/request.js +0 -332
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"autoUpdate": true,
|
|
3
|
+
"checkUpdate": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"install": false,
|
|
6
|
+
"notifyIfCurrent": false,
|
|
7
|
+
"packageName": "@dongdev/fca-unofficial",
|
|
8
|
+
"registryUrl": "https://registry.npmjs.org",
|
|
9
|
+
"timeoutMs": 10000
|
|
10
|
+
},
|
|
11
|
+
"mqtt": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"reconnectInterval": 3600
|
|
14
|
+
},
|
|
15
|
+
"autoLogin": true,
|
|
16
|
+
"apiServer": "https://donixdev.com",
|
|
17
|
+
"apiKey": "",
|
|
18
|
+
"credentials": {
|
|
19
|
+
"email": "",
|
|
20
|
+
"password": "",
|
|
21
|
+
"twofactor": ""
|
|
22
|
+
},
|
|
23
|
+
"antiGetInfo": {
|
|
24
|
+
"AntiGetThreadInfo": false,
|
|
25
|
+
"AntiGetUserInfo": false
|
|
26
|
+
},
|
|
27
|
+
"remoteControl": {
|
|
28
|
+
"enabled": false,
|
|
29
|
+
"url": "",
|
|
30
|
+
"token": "",
|
|
31
|
+
"autoReconnect": true
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dongdev/fca-unofficial",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Unofficial Facebook Chat API for Node.js - Interact with Facebook Messenger programmatically",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"types": "index.d.ts",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"default": "./dist/index.js"
|
|
12
13
|
}
|
|
13
14
|
},
|
|
14
15
|
"files": [
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"module/",
|
|
18
|
-
"func/",
|
|
19
|
-
"src/",
|
|
20
|
-
"DOCS.md",
|
|
16
|
+
"dist/",
|
|
17
|
+
"docs/",
|
|
21
18
|
"README.md",
|
|
19
|
+
"test/",
|
|
22
20
|
"LICENSE",
|
|
23
|
-
"
|
|
21
|
+
"fca-config.example.json"
|
|
24
22
|
],
|
|
25
23
|
"scripts": {
|
|
26
|
-
"test": "
|
|
27
|
-
"
|
|
24
|
+
"test": "node ./test/fca.test.cjs",
|
|
25
|
+
"test:build": "npm run build && node ./test/fca.test.cjs",
|
|
26
|
+
"lint": "eslint .",
|
|
27
|
+
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
28
|
+
"build": "tsup src/index.ts --format cjs,esm --dts --clean --out-dir dist --external gradient-string",
|
|
29
|
+
"build:types": "tsc -p tsconfig.build.json --emitDeclarationOnly"
|
|
28
30
|
},
|
|
29
31
|
"repository": {
|
|
30
32
|
"type": "git",
|
|
@@ -46,8 +48,8 @@
|
|
|
46
48
|
"fca"
|
|
47
49
|
],
|
|
48
50
|
"author": {
|
|
49
|
-
"name": "
|
|
50
|
-
"url": "https://www.facebook.com/
|
|
51
|
+
"name": "Donix",
|
|
52
|
+
"url": "https://www.facebook.com/minhdong.dev"
|
|
51
53
|
},
|
|
52
54
|
"contributors": [
|
|
53
55
|
{
|
|
@@ -55,31 +57,40 @@
|
|
|
55
57
|
"url": "https://github.com/dongp06"
|
|
56
58
|
}
|
|
57
59
|
],
|
|
58
|
-
"license": "
|
|
60
|
+
"license": "Apache-2.0",
|
|
59
61
|
"bugs": {
|
|
60
62
|
"url": "https://github.com/dongp06/fca-unofficial/issues"
|
|
61
63
|
},
|
|
62
64
|
"homepage": "https://github.com/dongp06/fca-unofficial#readme",
|
|
63
65
|
"engines": {
|
|
64
|
-
"node": ">=
|
|
66
|
+
"node": ">=14.0.0"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|
|
67
69
|
"axios": "^1.13.5",
|
|
68
70
|
"axios-cookiejar-support": "^5.0.5",
|
|
69
71
|
"bluebird": "^3.7.2",
|
|
70
|
-
"
|
|
72
|
+
"boxen": "^8.0.1",
|
|
71
73
|
"cheerio": "^1.0.0-rc.10",
|
|
74
|
+
"cli-progress": "^3.12.0",
|
|
72
75
|
"duplexify": "^4.1.3",
|
|
73
|
-
"gradient-string": "^
|
|
76
|
+
"gradient-string": "^3.0.0",
|
|
74
77
|
"https-proxy-agent": "^4.0.0",
|
|
75
78
|
"mqtt": "^4.3.8",
|
|
79
|
+
"ora": "^9.3.0",
|
|
80
|
+
"picocolors": "^1.1.1",
|
|
76
81
|
"sequelize": "^6.37.6",
|
|
82
|
+
"sqlite3": "^5.1.7",
|
|
77
83
|
"totp-generator": "^1.0.0",
|
|
78
84
|
"ws": "^8.18.1"
|
|
79
85
|
},
|
|
80
86
|
"devDependencies": {
|
|
87
|
+
"@types/cli-progress": "^3.11.6",
|
|
88
|
+
"@types/duplexify": "^3.6.5",
|
|
89
|
+
"@types/node": "^24.6.1",
|
|
90
|
+
"@types/ws": "^8.18.1",
|
|
81
91
|
"eslint": "^8.50.0",
|
|
82
|
-
"
|
|
92
|
+
"tsup": "^8.5.0",
|
|
93
|
+
"typescript": "^5.9.3"
|
|
83
94
|
},
|
|
84
95
|
"publishConfig": {
|
|
85
96
|
"access": "public",
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("assert");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
|
|
7
|
+
const fca = require("../dist/index.js");
|
|
8
|
+
|
|
9
|
+
const tests = [];
|
|
10
|
+
const COOKIE_ATTRIBUTE_KEYS = new Set([
|
|
11
|
+
"domain",
|
|
12
|
+
"path",
|
|
13
|
+
"expires",
|
|
14
|
+
"max-age",
|
|
15
|
+
"secure",
|
|
16
|
+
"httponly",
|
|
17
|
+
"samesite",
|
|
18
|
+
"priority",
|
|
19
|
+
"partitioned"
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
function test(name, fn) {
|
|
23
|
+
tests.push({ name, fn });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function toCookiePair(name, value) {
|
|
27
|
+
const cookieName = String(name || "").trim();
|
|
28
|
+
if (!cookieName) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const cookieValue = value === undefined || value === null ? "" : String(value).trim();
|
|
33
|
+
return `${cookieName}=${cookieValue}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function dedupeCookiePairs(pairs) {
|
|
37
|
+
const latestByName = new Map();
|
|
38
|
+
|
|
39
|
+
for (const pair of pairs) {
|
|
40
|
+
const normalized = String(pair || "").trim();
|
|
41
|
+
const eqIndex = normalized.indexOf("=");
|
|
42
|
+
if (eqIndex <= 0) {
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const name = normalized.slice(0, eqIndex).trim();
|
|
47
|
+
if (!name) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
latestByName.set(name, `${name}=${normalized.slice(eqIndex + 1).trim()}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return [...latestByName.values()];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseCookieHeaderString(raw) {
|
|
58
|
+
let header = String(raw || "").trim();
|
|
59
|
+
if (!header) {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (/^cookie\s*:/i.test(header)) {
|
|
64
|
+
header = header.replace(/^cookie\s*:/i, "").trim();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const pairs = [];
|
|
68
|
+
for (const segment of header.split(/[;\r\n]+/)) {
|
|
69
|
+
const token = segment.trim();
|
|
70
|
+
const eqIndex = token.indexOf("=");
|
|
71
|
+
if (eqIndex <= 0) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const name = token.slice(0, eqIndex).trim();
|
|
76
|
+
if (!name || COOKIE_ATTRIBUTE_KEYS.has(name.toLowerCase())) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const value = token.slice(eqIndex + 1).trim().replace(/^"(.*)"$/, "$1");
|
|
81
|
+
const pair = toCookiePair(name, value);
|
|
82
|
+
if (pair) {
|
|
83
|
+
pairs.push(pair);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return dedupeCookiePairs(pairs);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parseNetscapeCookieText(raw) {
|
|
91
|
+
const pairs = [];
|
|
92
|
+
const lines = String(raw || "").split(/\r?\n/);
|
|
93
|
+
|
|
94
|
+
for (const line of lines) {
|
|
95
|
+
const trimmed = line.trim();
|
|
96
|
+
if (!trimmed) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const normalized = trimmed.replace(/^#HttpOnly_/, "");
|
|
101
|
+
if (normalized.startsWith("#")) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const columns = normalized.split("\t");
|
|
106
|
+
if (columns.length < 7) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const name = columns[5];
|
|
111
|
+
const value = columns.slice(6).join("\t");
|
|
112
|
+
const pair = toCookiePair(name, value);
|
|
113
|
+
if (pair) {
|
|
114
|
+
pairs.push(pair);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return dedupeCookiePairs(pairs);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function normalizeCookiePayload(payload) {
|
|
122
|
+
if (!payload) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (typeof payload === "string") {
|
|
127
|
+
const netscapePairs = parseNetscapeCookieText(payload);
|
|
128
|
+
if (netscapePairs.length) {
|
|
129
|
+
return netscapePairs.join("; ");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const headerPairs = parseCookieHeaderString(payload);
|
|
133
|
+
if (headerPairs.length) {
|
|
134
|
+
return headerPairs.join("; ");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (Array.isArray(payload)) {
|
|
141
|
+
const pairs = [];
|
|
142
|
+
|
|
143
|
+
for (const entry of payload) {
|
|
144
|
+
if (typeof entry === "string") {
|
|
145
|
+
const stringPairs = parseCookieHeaderString(entry);
|
|
146
|
+
if (stringPairs.length) {
|
|
147
|
+
pairs.push(...stringPairs);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const eqIndex = entry.indexOf("=");
|
|
152
|
+
if (eqIndex > 0) {
|
|
153
|
+
const pair = toCookiePair(entry.slice(0, eqIndex), entry.slice(eqIndex + 1));
|
|
154
|
+
if (pair) {
|
|
155
|
+
pairs.push(pair);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (entry && typeof entry === "object") {
|
|
162
|
+
const pair = toCookiePair(entry.name || entry.key, entry.value);
|
|
163
|
+
if (pair) {
|
|
164
|
+
pairs.push(pair);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const deduped = dedupeCookiePairs(pairs);
|
|
170
|
+
return deduped.length ? deduped.join("; ") : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (typeof payload === "object") {
|
|
174
|
+
if (payload.Cookie || payload.cookie || payload.cookies) {
|
|
175
|
+
return normalizeCookiePayload(payload.Cookie || payload.cookie || payload.cookies);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if ((payload.name || payload.key) && Object.prototype.hasOwnProperty.call(payload, "value")) {
|
|
179
|
+
return toCookiePair(payload.name || payload.key, payload.value);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const pairs = [];
|
|
183
|
+
for (const [name, value] of Object.entries(payload)) {
|
|
184
|
+
if (value === undefined || value === null || typeof value === "object") {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const pair = toCookiePair(name, value);
|
|
189
|
+
if (pair) {
|
|
190
|
+
pairs.push(pair);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const deduped = dedupeCookiePairs(pairs);
|
|
195
|
+
return deduped.length ? deduped.join("; ") : null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function loadCookieCredentials(candidate, sourceLabel) {
|
|
202
|
+
if (!fs.existsSync(candidate)) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const raw = fs.readFileSync(candidate, "utf8").trim();
|
|
207
|
+
if (!raw) {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
let payload = raw;
|
|
212
|
+
if (candidate.endsWith(".json")) {
|
|
213
|
+
payload = JSON.parse(raw);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const cookieHeader = normalizeCookiePayload(payload);
|
|
217
|
+
if (!cookieHeader) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
mode: "cookie",
|
|
223
|
+
source: sourceLabel,
|
|
224
|
+
credentials: {
|
|
225
|
+
Cookie: cookieHeader
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function readJsonFileIfValid(candidate) {
|
|
231
|
+
if (!fs.existsSync(candidate)) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const raw = fs.readFileSync(candidate, "utf8");
|
|
236
|
+
if (!raw.trim()) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
return JSON.parse(raw);
|
|
242
|
+
} catch {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function loadIntegrationCredentials() {
|
|
248
|
+
const rootDir = process.cwd();
|
|
249
|
+
const cookieCandidates = [
|
|
250
|
+
path.join(rootDir, "cookie.txt"),
|
|
251
|
+
path.join(rootDir, "cookies.txt"),
|
|
252
|
+
path.join(rootDir, "cookie.json"),
|
|
253
|
+
path.join(rootDir, "cookies.json"),
|
|
254
|
+
path.join(rootDir, "test", "cookie.txt"),
|
|
255
|
+
path.join(rootDir, "test", "cookie.json")
|
|
256
|
+
];
|
|
257
|
+
|
|
258
|
+
for (const candidate of cookieCandidates) {
|
|
259
|
+
const loaded = loadCookieCredentials(candidate, path.relative(rootDir, candidate) || candidate);
|
|
260
|
+
if (loaded) {
|
|
261
|
+
return loaded;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const appStateCandidates = [
|
|
266
|
+
path.join(rootDir, "appstate.json"),
|
|
267
|
+
path.join(rootDir, "appState.json"),
|
|
268
|
+
path.join(rootDir, "test", "appstate.json")
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
for (const candidate of appStateCandidates) {
|
|
272
|
+
const parsedAppState = readJsonFileIfValid(candidate);
|
|
273
|
+
if (!parsedAppState) {
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return {
|
|
278
|
+
mode: "appState",
|
|
279
|
+
source: path.relative(rootDir, candidate) || candidate,
|
|
280
|
+
credentials: {
|
|
281
|
+
appState: parsedAppState
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const configPath = path.join(rootDir, "fca-config.json");
|
|
287
|
+
if (fs.existsSync(configPath)) {
|
|
288
|
+
const rawConfig = readJsonFileIfValid(configPath);
|
|
289
|
+
if (!rawConfig) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const configCookiePath =
|
|
294
|
+
rawConfig.cookiePath ||
|
|
295
|
+
rawConfig.cookiesPath ||
|
|
296
|
+
rawConfig.cookieFile ||
|
|
297
|
+
rawConfig.cookiesFile ||
|
|
298
|
+
rawConfig.credentials?.cookiePath ||
|
|
299
|
+
rawConfig.credentials?.cookiesPath ||
|
|
300
|
+
rawConfig.credentials?.cookieFile ||
|
|
301
|
+
rawConfig.credentials?.cookiesFile;
|
|
302
|
+
|
|
303
|
+
if (configCookiePath) {
|
|
304
|
+
const resolvedCookiePath = path.resolve(rootDir, configCookiePath);
|
|
305
|
+
const loaded = loadCookieCredentials(
|
|
306
|
+
resolvedCookiePath,
|
|
307
|
+
path.relative(rootDir, resolvedCookiePath) || resolvedCookiePath
|
|
308
|
+
);
|
|
309
|
+
if (loaded) {
|
|
310
|
+
return loaded;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const configCookie =
|
|
315
|
+
rawConfig.Cookie ||
|
|
316
|
+
rawConfig.cookie ||
|
|
317
|
+
rawConfig.cookies ||
|
|
318
|
+
rawConfig.sessionCookie ||
|
|
319
|
+
rawConfig.credentials?.Cookie ||
|
|
320
|
+
rawConfig.credentials?.cookie ||
|
|
321
|
+
rawConfig.credentials?.cookies ||
|
|
322
|
+
rawConfig.credentials?.sessionCookie;
|
|
323
|
+
|
|
324
|
+
if (configCookie) {
|
|
325
|
+
const normalizedConfigCookie = normalizeCookiePayload(configCookie);
|
|
326
|
+
if (normalizedConfigCookie) {
|
|
327
|
+
return {
|
|
328
|
+
mode: "cookie",
|
|
329
|
+
source: "fca-config.json",
|
|
330
|
+
credentials: {
|
|
331
|
+
Cookie: normalizedConfigCookie
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const fileAppStatePath =
|
|
338
|
+
rawConfig.appStatePath ||
|
|
339
|
+
rawConfig.appstatePath ||
|
|
340
|
+
rawConfig.appStateFile ||
|
|
341
|
+
rawConfig.appstateFile;
|
|
342
|
+
|
|
343
|
+
if (fileAppStatePath) {
|
|
344
|
+
const resolvedPath = path.resolve(rootDir, fileAppStatePath);
|
|
345
|
+
const parsedAppState = readJsonFileIfValid(resolvedPath);
|
|
346
|
+
if (parsedAppState) {
|
|
347
|
+
return {
|
|
348
|
+
mode: "appState",
|
|
349
|
+
source: path.relative(rootDir, resolvedPath) || resolvedPath,
|
|
350
|
+
credentials: {
|
|
351
|
+
appState: parsedAppState
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
test("public exports are available", () => {
|
|
362
|
+
assert.strictEqual(typeof fca.login, "function");
|
|
363
|
+
assert.strictEqual(typeof fca.createFcaClient, "function");
|
|
364
|
+
assert.strictEqual(typeof fca.attachClientFacade, "function");
|
|
365
|
+
assert.strictEqual(typeof fca.resolveConfig, "function");
|
|
366
|
+
assert.strictEqual(typeof fca.loadConfig, "function");
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("resolveConfig honors legacy autoUpdate alias", () => {
|
|
370
|
+
const resolved = fca.resolveConfig({
|
|
371
|
+
autoUpdate: false,
|
|
372
|
+
checkUpdate: {
|
|
373
|
+
notifyIfCurrent: true
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
assert.strictEqual(resolved.autoUpdate, false);
|
|
378
|
+
assert.strictEqual(resolved.checkUpdate.enabled, false);
|
|
379
|
+
assert.strictEqual(resolved.checkUpdate.notifyIfCurrent, true);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("createFcaClient reuses grouped namespaces when they already exist", () => {
|
|
383
|
+
const send = () => "send-ok";
|
|
384
|
+
const getInfo = () => "thread-ok";
|
|
385
|
+
const listen = () => "listen-ok";
|
|
386
|
+
|
|
387
|
+
const api = {
|
|
388
|
+
messages: { send },
|
|
389
|
+
threads: { getInfo },
|
|
390
|
+
users: { getInfo: () => "user-ok" },
|
|
391
|
+
account: { getCurrentUserID: () => "123" },
|
|
392
|
+
realtime: { listen },
|
|
393
|
+
http: { get: () => "http-ok" },
|
|
394
|
+
scheduler: { scheduleMessage: () => "sched-ok" }
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const client = fca.createFcaClient(api);
|
|
398
|
+
|
|
399
|
+
assert.strictEqual(client.messages.send, send);
|
|
400
|
+
assert.strictEqual(client.threads.getInfo, getInfo);
|
|
401
|
+
assert.strictEqual(client.realtime.listen, listen);
|
|
402
|
+
assert.strictEqual(client.scheduler.scheduleMessage(), "sched-ok");
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
test("attachClientFacade exposes grouped namespaces on legacy api object", () => {
|
|
406
|
+
const api = {
|
|
407
|
+
sendMessage() {
|
|
408
|
+
return "legacy-send";
|
|
409
|
+
},
|
|
410
|
+
getThreadInfo() {
|
|
411
|
+
return "legacy-thread";
|
|
412
|
+
},
|
|
413
|
+
getUserInfo() {
|
|
414
|
+
return "legacy-user";
|
|
415
|
+
},
|
|
416
|
+
getCurrentUserID() {
|
|
417
|
+
return "1000";
|
|
418
|
+
},
|
|
419
|
+
listenMqtt() {
|
|
420
|
+
return "legacy-listen";
|
|
421
|
+
},
|
|
422
|
+
stopListening() {
|
|
423
|
+
return "legacy-stop";
|
|
424
|
+
},
|
|
425
|
+
stopListeningAsync() {
|
|
426
|
+
return Promise.resolve("legacy-stop-async");
|
|
427
|
+
},
|
|
428
|
+
useMiddleware() {
|
|
429
|
+
return "legacy-use";
|
|
430
|
+
},
|
|
431
|
+
removeMiddleware() {
|
|
432
|
+
return "legacy-remove";
|
|
433
|
+
},
|
|
434
|
+
clearMiddleware() {
|
|
435
|
+
return "legacy-clear";
|
|
436
|
+
},
|
|
437
|
+
listMiddleware() {
|
|
438
|
+
return [];
|
|
439
|
+
},
|
|
440
|
+
setMiddlewareEnabled() {
|
|
441
|
+
return true;
|
|
442
|
+
},
|
|
443
|
+
httpGet() {
|
|
444
|
+
return "legacy-http-get";
|
|
445
|
+
},
|
|
446
|
+
httpPost() {
|
|
447
|
+
return "legacy-http-post";
|
|
448
|
+
},
|
|
449
|
+
postFormData() {
|
|
450
|
+
return "legacy-http-form";
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
const client = fca.attachClientFacade(api);
|
|
455
|
+
|
|
456
|
+
assert.strictEqual(api.client, client);
|
|
457
|
+
assert.strictEqual(typeof api.messages.send, "function");
|
|
458
|
+
assert.strictEqual(typeof api.threads.getInfo, "function");
|
|
459
|
+
assert.strictEqual(typeof api.users.getInfo, "function");
|
|
460
|
+
assert.strictEqual(typeof api.account.getCurrentUserID, "function");
|
|
461
|
+
assert.strictEqual(typeof api.realtime.listen, "function");
|
|
462
|
+
assert.strictEqual(typeof api.http.get, "function");
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test("optional live login works when credentials are provided", async () => {
|
|
466
|
+
const integration = loadIntegrationCredentials();
|
|
467
|
+
if (!integration) {
|
|
468
|
+
return {
|
|
469
|
+
skipped: true,
|
|
470
|
+
reason: "add cookie.txt, cookies.txt, cookie.json, or cookiePath/Cookie in fca-config.json to enable real-cookie login"
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
let ctx;
|
|
475
|
+
try {
|
|
476
|
+
ctx = await fca.login(integration.credentials, {
|
|
477
|
+
online: false,
|
|
478
|
+
forceLogin: false,
|
|
479
|
+
autoMarkRead: false
|
|
480
|
+
});
|
|
481
|
+
} catch (error) {
|
|
482
|
+
return {
|
|
483
|
+
skipped: true,
|
|
484
|
+
reason: `live login failed from ${integration.source}: ${error && error.message ? error.message : String(error)}`
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
assert.ok(ctx);
|
|
489
|
+
assert.ok(ctx.api);
|
|
490
|
+
assert.strictEqual(typeof ctx.api.sendMessage, "function");
|
|
491
|
+
assert.strictEqual(typeof ctx.api.listenMqtt, "function");
|
|
492
|
+
assert.strictEqual(typeof ctx.api.client.messages.send, "function");
|
|
493
|
+
assert.ok(ctx.userID || ctx.fbid);
|
|
494
|
+
|
|
495
|
+
return {
|
|
496
|
+
mode: integration.mode,
|
|
497
|
+
source: integration.source,
|
|
498
|
+
userID: ctx.userID || ctx.fbid || null
|
|
499
|
+
};
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
async function main() {
|
|
503
|
+
let passed = 0;
|
|
504
|
+
let skipped = 0;
|
|
505
|
+
|
|
506
|
+
for (const { name, fn } of tests) {
|
|
507
|
+
try {
|
|
508
|
+
const result = await fn();
|
|
509
|
+
if (result && result.skipped) {
|
|
510
|
+
skipped += 1;
|
|
511
|
+
console.log(`SKIP ${name} - ${result.reason}`);
|
|
512
|
+
continue;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
passed += 1;
|
|
516
|
+
if (result && result.userID) {
|
|
517
|
+
console.log(`PASS ${name} (${result.mode} from ${result.source}: ${result.userID})`);
|
|
518
|
+
} else {
|
|
519
|
+
console.log(`PASS ${name}`);
|
|
520
|
+
}
|
|
521
|
+
} catch (error) {
|
|
522
|
+
console.error(`FAIL ${name}`);
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
console.log(`SUMMARY passed=${passed} skipped=${skipped} total=${tests.length}`);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
main().catch((error) => {
|
|
531
|
+
console.error(error && error.stack ? error.stack : error);
|
|
532
|
+
process.exitCode = 1;
|
|
533
|
+
});
|