@dongdev/fca-unofficial 2.0.28 → 2.0.29

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
@@ -119,3 +119,6 @@ Too lazy to write changelog, sorry! (will write changelog in the next release, t
119
119
 
120
120
  ## v2.0.27 - 2025-10-18
121
121
  - Hotfix / auto bump
122
+
123
+ ## v2.0.28 - 2025-10-18
124
+ - Hotfix / auto bump
@@ -1,62 +1,147 @@
1
- // func/checkUpdate.js
2
- const logger = require("./logger");
3
- const fs = require("fs");
4
- const { exec } = require("child_process");
5
- const pkgName = "@dongdev/fca-unofficial";
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
6
 
7
- function execPromise(cmd) {
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 { }
21
+ }
22
+
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 { }
35
+ }
36
+
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;
46
+ }
47
+
48
+ function getLatestVersion(pkgName) {
8
49
  return new Promise((resolve, reject) => {
9
- exec(cmd, (error, stdout, stderr) => {
10
- if (error) return reject({ error, stderr });
11
- resolve({ stdout, stderr });
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
+ });
63
+ }
64
+
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}`));
12
76
  });
77
+ child.on('error', reject);
13
78
  });
14
79
  }
15
80
 
16
- function getInstalledVersion() {
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
+ }
111
+ }
112
+ if (!currentVersion) return; // Không xác định được version hiện tại => bỏ
113
+
114
+ let latestVersion = null;
17
115
  try {
18
- const p = require.resolve(`${pkgName}/package.json`, { paths: [process.cwd(), __dirname] });
19
- return JSON.parse(fs.readFileSync(p, "utf8")).version;
116
+ latestVersion = await getLatestVersion(pkg);
20
117
  } catch {
21
- return null;
118
+ return; // network lỗi => bỏ qua, tránh spam
22
119
  }
23
- }
120
+ if (!latestVersion) return;
24
121
 
25
- async function _checkAndUpdateVersionImpl() {
26
- logger("Checking version...", "info");
27
- const latest = (await execPromise(`npm view ${pkgName} version`)).stdout.trim();
28
- const installed = getInstalledVersion();
29
- if (!installed || installed !== latest) {
30
- logger(`New version available (${latest}). Current version (${installed || "not installed"}). Updating...`, "info");
31
- try {
32
- const { stderr } = await execPromise(`npm i ${pkgName}@latest`);
33
- if (stderr) logger(stderr, "error");
34
- logger(`Updated fca to the latest version: ${latest}, Restart to apply`, "info");
35
- process.exit(1);
36
- } catch (e) {
37
- logger(`Error running npm install: ${e.error || e}. Trying to install from GitHub...`, "error");
38
- try {
39
- const { stderr } = await execPromise("npm i https://github.com/Donix-VN/fca-unofficial");
40
- if (stderr) logger(stderr, "error");
41
- logger(`Installed from GitHub successfully: ${latest}`, "info");
42
- return;
43
- } catch (gitErr) {
44
- logger(`Error installing from GitHub: ${gitErr.error || gitErr}`, "error");
45
- throw (gitErr.error || gitErr);
46
- }
47
- }
48
- } else {
49
- logger(`You're already on the latest version - ${latest}`, "info");
50
- return;
51
- }
52
- }
122
+ // Nếu đã mới nhất => thôi
123
+ if (semverCompare(currentVersion, latestVersion) >= 0) return;
53
124
 
54
- function checkAndUpdateVersion(callback) {
55
- if (typeof callback === "function") {
56
- _checkAndUpdateVersionImpl().then(() => callback(null)).catch(err => callback(err));
57
- return;
125
+ // Đã có lock mới => bỏ để tránh loop
126
+ if (!acquireLock()) return;
127
+
128
+ 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
+ }
138
+ } 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
58
144
  }
59
- return _checkAndUpdateVersionImpl();
60
145
  }
61
146
 
62
- module.exports = { checkAndUpdateVersion };
147
+ module.exports = checkUpdate;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dongdev/fca-unofficial",
3
- "version": "2.0.28",
3
+ "version": "2.0.29",
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",