@dongdev/fca-unofficial 2.0.29 → 2.0.31

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/CHANGELOG.md CHANGED
@@ -122,3 +122,9 @@ Too lazy to write changelog, sorry! (will write changelog in the next release, t
122
122
 
123
123
  ## v2.0.28 - 2025-10-18
124
124
  - Hotfix / auto bump
125
+
126
+ ## v2.0.29 - 2025-10-19
127
+ - Hotfix / auto bump
128
+
129
+ ## v2.0.30 - 2025-10-19
130
+ - Hotfix / auto bump
@@ -1,147 +1,120 @@
1
- const fs = require('fs');
2
- const path = require('path');
3
- const os = require('os');
4
- const https = require('https');
5
- const { spawn } = require('child_process');
6
-
7
- const PACKAGE_NAME = 'fca-unofficial';
8
- // Không thử cài đặt lại trong vòng 10 phút sau lần update gần nhất
9
- const LOCK_TTL_MS = 10 * 60 * 1000;
10
- // Không check quá 1 lần mỗi 12 giờ
11
- const CHECK_TTL_MS = 12 * 60 * 60 * 1000;
12
-
13
- const lockPath = path.join(os.tmpdir(), 'fca-unofficial-update.lock');
14
- const statePath = path.join(os.tmpdir(), 'fca-unofficial-update-state.json');
15
-
16
- function readJSON(p) {
17
- try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
18
- }
19
- function writeJSON(p, obj) {
20
- try { fs.writeFileSync(p, JSON.stringify(obj)); } catch { }
1
+ // func/checkUpdate.js
2
+ const logger = require("./logger");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const { exec } = require("child_process");
6
+ const pkgName = "@dongdev/fca-unofficial";
7
+
8
+ const TEMP_DIR = path.join(process.cwd(), "temp");
9
+ const LOCK_FILE = path.join(TEMP_DIR, ".fca-update-lock.json");
10
+ const RESTART_COOLDOWN_MS = 10 * 60 * 1000;
11
+
12
+ function execPromise(cmd) {
13
+ return new Promise((resolve, reject) => {
14
+ exec(cmd, { cwd: process.cwd() }, (error, stdout, stderr) => {
15
+ if (error) return reject({ error, stderr });
16
+ resolve({ stdout, stderr });
17
+ });
18
+ });
21
19
  }
22
20
 
23
- function isLockFresh() {
24
- try {
25
- const st = fs.statSync(lockPath);
26
- return (Date.now() - st.mtimeMs) < LOCK_TTL_MS;
27
- } catch { return false; }
28
- }
29
- function acquireLock() {
30
- if (isLockFresh()) return false;
31
- try { fs.writeFileSync(lockPath, String(Date.now())); return true; } catch { return false; }
32
- }
33
- function releaseLock() {
34
- try { fs.unlinkSync(lockPath); } catch { }
21
+ function ensureTemp() {
22
+ try { fs.mkdirSync(TEMP_DIR, { recursive: true }); } catch { }
35
23
  }
36
24
 
37
- function semverCompare(a, b) {
38
- const pa = String(a).replace(/^v/, '').split('.').map(n => parseInt(n || 0, 10));
39
- const pb = String(b).replace(/^v/, '').split('.').map(n => parseInt(n || 0, 10));
40
- for (let i = 0; i < 3; i++) {
41
- const da = pa[i] || 0, db = pb[i] || 0;
42
- if (da > db) return 1;
43
- if (da < db) return -1;
44
- }
45
- return 0;
25
+ function readLock() {
26
+ try { return JSON.parse(fs.readFileSync(LOCK_FILE, "utf8")); } catch { return null; }
46
27
  }
47
28
 
48
- function getLatestVersion(pkgName) {
49
- return new Promise((resolve, reject) => {
50
- const url = `https://registry.npmjs.org/${encodeURIComponent(pkgName)}/latest`;
51
- https.get(url, res => {
52
- let d = '';
53
- res.setEncoding('utf8');
54
- res.on('data', c => d += c);
55
- res.on('end', () => {
56
- try {
57
- const j = JSON.parse(d);
58
- resolve(j.version || null);
59
- } catch (e) { reject(e); }
60
- });
61
- }).on('error', reject);
62
- });
29
+ function writeLock(data) {
30
+ ensureTemp();
31
+ try { fs.writeFileSync(LOCK_FILE, JSON.stringify(data)); } catch { }
63
32
  }
64
33
 
65
- function runNpmInstallLatest(pkgName, cwd) {
66
- return new Promise((resolve, reject) => {
67
- const bin = process.platform === 'win32' ? 'npm.cmd' : 'npm';
68
- const child = spawn(bin, ['i', `${pkgName}@latest`], {
69
- cwd,
70
- stdio: 'inherit',
71
- env: process.env
72
- });
73
- child.on('exit', code => {
74
- if (code === 0) resolve();
75
- else reject(new Error(`npm install exited with ${code}`));
76
- });
77
- child.on('error', reject);
78
- });
34
+ function clearLock() {
35
+ try { fs.unlinkSync(LOCK_FILE); } catch { }
79
36
  }
80
37
 
81
- // options: { cwd, pkg, restart }
82
- async function checkUpdate(options = {}) {
83
- if (global.__FCA_UPDATE_CHECKED__) return;
84
- global.__FCA_UPDATE_CHECKED__ = true;
85
-
86
- const pkg = options.pkg || PACKAGE_NAME;
87
- const cwd = options.cwd || process.cwd();
88
- const shouldRestart = options.restart !== false;
89
-
90
- // Giới hạn tần suất check
91
- const state = readJSON(statePath) || {};
92
- const now = Date.now();
93
- if (state.lastCheck && now - state.lastCheck < CHECK_TTL_MS) return;
94
- state.lastCheck = now;
95
- writeJSON(statePath, state);
96
-
97
- // Đọc version hiện tại
98
- let currentVersion = null;
99
- const installedPkgJson = path.join(cwd, 'node_modules', pkg, 'package.json');
100
- const rootPkgJson = path.join(cwd, 'package.json');
101
-
102
- const installed = readJSON(installedPkgJson);
103
- if (installed && installed.version) {
104
- currentVersion = installed.version;
105
- } else {
106
- // fallback: nếu lib đang là chính dự án
107
- const root = readJSON(rootPkgJson);
108
- if (root && root.name === pkg && root.version) {
109
- currentVersion = root.version;
110
- }
38
+ function getInstalledVersion() {
39
+ try {
40
+ const p = require.resolve(`${pkgName}/package.json`, { paths: [process.cwd(), __dirname] });
41
+ return JSON.parse(fs.readFileSync(p, "utf8")).version;
42
+ } catch {
43
+ return null;
111
44
  }
112
- if (!currentVersion) return; // Không xác định được version hiện tại => bỏ
45
+ }
113
46
 
114
- let latestVersion = null;
47
+ async function getInstalledVersionByNpm() {
115
48
  try {
116
- latestVersion = await getLatestVersion(pkg);
49
+ const { stdout } = await execPromise(`npm ls ${pkgName} --json --depth=0`);
50
+ const json = JSON.parse(stdout || "{}");
51
+ const v = json?.dependencies?.[pkgName]?.version;
52
+ return v || null;
117
53
  } catch {
118
- return; // network lỗi => bỏ qua, tránh spam
54
+ return null;
119
55
  }
120
- if (!latestVersion) return;
56
+ }
57
+
58
+ async function _checkAndUpdateVersionImpl() {
59
+ const lock = readLock();
60
+ if (lock && Date.now() - (lock.ts || 0) < RESTART_COOLDOWN_MS) {
61
+ logger("Skip auto-update due to recent attempt", "info");
62
+ return;
63
+ }
64
+
65
+ logger("Checking version...", "info");
66
+ const latest = (await execPromise(`npm view ${pkgName} version`)).stdout.trim();
121
67
 
122
- // Nếu đã mới nhất => thôi
123
- if (semverCompare(currentVersion, latestVersion) >= 0) return;
68
+ let installed = getInstalledVersion();
69
+ if (!installed) installed = await getInstalledVersionByNpm();
70
+
71
+ if (installed && installed === latest) {
72
+ clearLock();
73
+ logger(`You're already on the latest version - ${latest}`, "info");
74
+ return;
75
+ }
76
+
77
+ if (lock && lock.latest === latest && Date.now() - (lock.ts || 0) < RESTART_COOLDOWN_MS) {
78
+ logger("Update already attempted recently, skipping restart loop", "info");
79
+ return;
80
+ }
124
81
 
125
- // Đã lock mới => bỏ để tránh loop
126
- if (!acquireLock()) return;
82
+ logger(`New version available (${latest}). Current version (${installed || "not installed"}). Updating...`, "info");
127
83
 
128
84
  try {
129
- await runNpmInstallLatest(pkg, cwd);
130
-
131
- // Sau khi cài xong, chỉ restart đúng 1 lần
132
- if (shouldRestart) {
133
- if (global.__FCA_RESTARTING__) return;
134
- global.__FCA_RESTARTING__ = true;
135
- // Dùng exit code riêng để process manager có thể phân biệt
136
- setTimeout(() => process.exit(222), 300);
137
- }
85
+ const { stderr } = await execPromise(`npm i ${pkgName}@latest`);
86
+ if (stderr) logger(stderr, "error");
138
87
  } catch (e) {
139
- // Không restart khi cài thất bại để tránh loop
140
- } finally {
141
- // Không xóa lock ngay để tránh ngay-lập-tức retry sau restart
142
- // Lock TTL sẽ tự hết hạn
143
- // releaseLock(); // cố tình KHÔNG gọi
88
+ logger(`Error running npm install: ${e.error || e}. Trying to install from GitHub...`, "error");
89
+ try {
90
+ const { stderr } = await execPromise("npm i https://github.com/Donix-VN/fca-unofficial");
91
+ if (stderr) logger(stderr, "error");
92
+ } catch (gitErr) {
93
+ writeLock({ ts: Date.now(), latest, status: "failed" });
94
+ logger(`Error installing from GitHub: ${gitErr.error || gitErr}`, "error");
95
+ throw (gitErr.error || gitErr);
96
+ }
97
+ }
98
+
99
+ let after = getInstalledVersion();
100
+ if (!after) after = await getInstalledVersionByNpm();
101
+
102
+ if (after && after === latest) {
103
+ writeLock({ ts: Date.now(), latest, status: "updated" });
104
+ logger(`Updated fca to the latest version: ${latest}, Restart to apply`, "info");
105
+ process.exit(1);
106
+ } else {
107
+ writeLock({ ts: Date.now(), latest, status: "mismatch" });
108
+ logger(`Installed but version mismatch (have: ${after || "unknown"}, want: ${latest}). Skip restart to avoid loop`, "error");
109
+ }
110
+ }
111
+
112
+ function checkAndUpdateVersion(callback) {
113
+ if (typeof callback === "function") {
114
+ _checkAndUpdateVersionImpl().then(() => callback(null)).catch(err => callback(err));
115
+ return;
144
116
  }
117
+ return _checkAndUpdateVersionImpl();
145
118
  }
146
119
 
147
- module.exports = checkUpdate;
120
+ module.exports = { checkAndUpdateVersion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongdev/fca-unofficial",
3
- "version": "2.0.29",
3
+ "version": "2.0.31",
4
4
  "description": "Unofficial Facebook Chat API for Node.js - Interact with Facebook Messenger programmatically",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -3,67 +3,44 @@
3
3
  const log = require("npmlog");
4
4
  const { getFrom } = require("../../utils/constants");
5
5
  const { get } = require("../../utils/request")
6
- module.exports = function(defaultFuncs, api, ctx) {
6
+ const { getType } = require("../../utils/format");
7
+ module.exports = function (defaultFuncs, api, ctx) {
7
8
  return function refreshFb_dtsg(obj, callback) {
8
- let resolveFunc, rejectFunc;
9
- const returnPromise = new Promise((resolve, reject) => {
10
- resolveFunc = resolve;
11
- rejectFunc = reject;
12
- });
13
- if (
14
- getType(obj) === "Function" ||
15
- getType(obj) === "AsyncFunction"
16
- ) {
9
+ if (typeof obj === "function") {
17
10
  callback = obj;
18
11
  obj = {};
19
12
  }
20
13
  if (!obj) obj = {};
21
14
  if (getType(obj) !== "Object") {
22
- throw Error(
23
- "The first parameter must be an object or a callback function"
24
- );
15
+ throw new CustomError("The first parameter must be an object or a callback function");
25
16
  }
17
+ let resolveFunc, rejectFunc;
18
+ const returnPromise = new Promise((resolve, reject) => {
19
+ resolveFunc = resolve;
20
+ rejectFunc = reject;
21
+ });
26
22
  if (!callback) {
27
- callback = (err, data) => (err ? rejectFunc(err) : resolveFunc(data));
23
+ callback = (err, data) => err ? rejectFunc(err) : resolveFunc(data);
28
24
  }
29
25
  if (Object.keys(obj).length === 0) {
30
- get(
31
- "https://www.facebook.com/",
32
- ctx.jar,
33
- null,
34
- ctx.globalOptions,
35
- {
36
- noRef: true
37
- }
38
- )
39
- .then(resData => {
40
- const fb_dtsg = getFrom(
41
- resData.body,
42
- '["DTSGInitData",[],{"token":"',
43
- '","'
44
- );
45
- const jazoest = getFrom(resData.body, "jazoest=", '",');
46
- if (!fb_dtsg) {
47
- throw Error(
48
- "Could not find fb_dtsg in HTML after requesting Facebook."
49
- );
50
- }
51
- ctx.fb_dtsg = fb_dtsg;
52
- ctx.jazoest = jazoest;
53
- callback(null, {
54
- data: { fb_dtsg, jazoest },
55
- message: "Refreshed fb_dtsg and jazoest"
56
- });
57
- })
58
- .catch(err => {
59
- log.error("refreshFb_dtsg", err);
60
- callback(err);
26
+ get("https://www.facebook.com/", ctx.jar, null, ctx.globalOptions, { noRef: true }).then(({ data }) => {
27
+ const fb_dtsg = getFrom(data, '["DTSGInitData",[],{"token":"', '","');
28
+ const jazoest = getFrom(data, "jazoest=", '",');
29
+ if (!fb_dtsg) throw new Error("Could not find fb_dtsg in HTML after requesting Facebook.");
30
+ Object.assign(ctx, { fb_dtsg, jazoest });
31
+ callback(null, {
32
+ data: { fb_dtsg, jazoest },
33
+ message: "Refreshed fb_dtsg and jazoest",
61
34
  });
35
+ }).catch(err => {
36
+ console.error("refreshFb_dtsg", err);
37
+ callback(err);
38
+ });
62
39
  } else {
63
40
  Object.assign(ctx, obj);
64
41
  callback(null, {
65
42
  data: obj,
66
- message: "Refreshed " + Object.keys(obj).join(", ")
43
+ message: `Refreshed ${Object.keys(obj).join(", ")}`,
67
44
  });
68
45
  }
69
46
  return returnPromise;