qq-bot-roost 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/README.md CHANGED
@@ -8,9 +8,9 @@
8
8
 
9
9
  <img src="https://img.shields.io/badge/Node.js-22.13%2B-339933?logo=node.js&logoColor=white" alt="Node.js 22.13+">
10
10
  <img src="https://img.shields.io/badge/dependencies-0-brightgreen" alt="零依赖">
11
- <img src="https://img.shields.io/badge/tests-796%20passed-brightgreen" alt="测试 796 项">
11
+ <img src="https://img.shields.io/badge/tests-797%20passed-brightgreen" alt="测试 797 项">
12
12
  <img src="https://img.shields.io/badge/plugins-JS%20%7C%20Python%20%7C%20Go%20%7C%20any-blue" alt="多语言插件">
13
- <img src="https://img.shields.io/badge/version-1.0.4-blueviolet" alt="版本 1.0.4">
13
+ <img src="https://img.shields.io/badge/version-1.0.5-blueviolet" alt="版本 1.0.5">
14
14
  <img src="https://img.shields.io/badge/license-Non--Commercial-red" alt="禁止商用">
15
15
  <a href="https://github.com/tdcq9594/qq-bot-roost/actions"><img src="https://img.shields.io/github/actions/workflow/status/tdcq9594/qq-bot-roost/ci.yml?branch=master" alt="CI"></a>
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qq-bot-roost",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "description": "栖 Roost:零依赖的 QQ 官方机器人框架,填 AppID/AppSecret 即连,插件可写任意语言",
6
6
  "license": "SEE LICENSE IN LICENSE",
package/src/identity.js CHANGED
@@ -727,14 +727,24 @@ export class IdentityStore {
727
727
  */
728
728
  this.flushing = true;
729
729
  try {
730
- // 写前重读:别的机器人进程可能刚写过
731
- const diskMtime = await this.#mtime();
732
- if (diskMtime && diskMtime !== this.mtimeMs) {
733
- const raw = await readJsonSafe(this.file, (m) => log.error(m));
734
- const disk = this.#normalizeRoot(raw);
735
- this.data = this.#merge(this.data, disk);
736
- this.#rebuildIndexes();
737
- }
730
+ /**
731
+ * 写前重读:别的机器人进程可能刚写过。
732
+ *
733
+ * **必须无条件重读,不能拿 mtime 做「磁盘变没变」的判断。**
734
+ * `mtimeMs` 只有毫秒精度(Windows 上尤其粗),同一毫秒里连着两次落盘
735
+ * mtime 一模一样 → 判定「磁盘没变」→ 不重读 → 用内存里的旧状态整份覆盖,
736
+ * 别的进程刚做的解绑 / 停用就被翻回去了。
737
+ * 这不是假想:CI 的 Windows + Node 22 上两条「旧进程不许复活解绑」的
738
+ * 用例稳定复现,第一次报 21 号、重跑报 35 号——同一毫秒内谁先谁后看运气。
739
+ *
740
+ * 代价可控:落盘本来就有 300ms 节流、只在 `dirty` 时跑,多读一个小 JSON
741
+ * 可以忽略;文件不存在 / 读坏时 `readJsonSafe` 返回 null,归一化成空表,
742
+ * 合并结果仍是内存这份,不会丢。
743
+ */
744
+ const raw = await readJsonSafe(this.file, (m) => log.error(m));
745
+ const disk = this.#normalizeRoot(raw);
746
+ this.data = this.#merge(this.data, disk);
747
+ this.#rebuildIndexes();
738
748
 
739
749
  this.data.version = DATA_VERSION;
740
750
  this.data.updatedAt = Date.now();