跳转到正文

配置持久化与恢复 ​

运行时设置保存在 data/config.json。core/config/persistence.py 是所有 JSON 配置写入口共用的持久化边界,Telegram、WebUI、频道调度和投票管理器都应通过该边界修改配置。

选择写入接口 ​

  • update_config_fields(fields):只替换明确提供的顶层字段,适合 /setaicfg 等字段设置。每次操作都从最新完整配置读取,未知字段和其他业务设置保持原值。
  • read_config() 配合 save_config_changes(snapshot):严格读取后修改并提交差异,适合频道、调度、投票、转发等包含增删操作的配置。字典中的显式删除会提交到磁盘;不同路径的并发修改会合并,同一字段或数组发生冲突时拒绝保存,调用方应重新读取。
  • save_config(config):明确的完整替换接口,不执行合并。保留有意删除字段的语义,同时阻断三字段 AI 草稿覆盖和异常大量顶层字段删除。普通业务设置应使用前两种接口。
  • load_config():保留兼容的只读容错行为。读取失败会返回空字典形态的无效快照,该结果不能提交修改。不要通过 dict(snapshot) 或 .copy() 丢弃写入基线。

WebUI 使用 deps.get_config() 和 deps.write_config(),保存成功后才安排热重载。不要重新引入直接 open(..., "w") 或 json.dump() 写入完整配置的路径。

保存与失败行为 ​

事务使用线程锁和操作系统文件锁覆盖读取、候选校验、备份和替换。锁文件为 config.json.lock;运行期间不要删除或替换该文件。配置必须已存在、可解析且通过业务校验;首次安装仍应从示例配置创建完整文件。

写入使用同目录临时文件、flush()、fsync() 和 os.replace()。Windows 在同卷私有暂存子目录中写入,保留现有文件的 DACL 和继承保护,新备份禁止继承外层目录的宽松读取权限。读取失败、候选校验失败、备份失败或替换失败都会向调用方报错,不把空配置当作写入起点。持久化错误消息不包含配置值。

/setaicfg 只提交明确输入的 AI 字段;/skip 在提交时保留该字段的最新值,/cancel 不写文件。保存失败保留当前用户草稿,可重新发送 Model 重试或取消。API Key 的展示完全遮蔽,输入文本和异常原文不写入日志。

恢复版本与保留策略 ​

  • config.json.last_valid 保存可恢复的有效配置。启动不会覆盖已有且有效的该文件。
  • config.json.backups/ 保存按 UTC 时间命名的独立 JSON 快照,文件内容不原地覆盖,最多保留最近 30 个版本。相同的连续快照不会因反复重启产生新版本。
  • 覆盖现有配置前必须成功保存有效恢复点。重载只将校验通过的版本纳入历史;坏 JSON 或异常大量字段丢失不会被固化为有效备份。
  • 热重载验证失败会在同一文件锁内恢复已有有效快照;内存快照不可用时尝试历史版本。损坏的备份会跳过,不将其直接复制回配置文件。
  • 备份包含实际凭据,新建历史目录和文件使用所有者权限,并已加入 Git 忽略列表。限制生产目录访问,不将备份上传到 Issue 或日志。

历史版本不是永久外部备份。仍应通过服务器备份系统保存重要设置;磁盘损坏或历史版本全部失效时,本地恢复机制不能重新生成已丢失的数据。

已发生字段丢失时 ​

  1. 停止机器人及其他写配置的进程。将当前 config.json、config.json.last_valid、历史目录和相关日志复制到单独的受限事故目录,保留现场。
  2. 查找事故前的完整快照。先检查 JSON 可解析、顶层为对象,并确认频道、调度、投票、转发及未知扩展字段仍然存在。不要把凭据原文输出到终端或公开记录。
  3. 保留独立副本后,将选定的完整历史快照复制为 data/config.json,然后重启并检查业务设置及热重载状态。启动阶段发现异常大量字段删除会拒绝接受该文件,以保留现有恢复点。
  4. 对旧版 /setaicfg 处理过的凭据,检查服务器历史日志中的原文输入记录;限制日志访问并轮换可能已经暴露的 API Key。修复不会远程清理服务器日志,也不会替换外部供应商的凭据。

如果事故发生在本修复之前,且当前配置、last_valid 和其他副本都只剩 AI 三字段,本修复无法找回原来的频道与调度值。需要从服务器快照、独立备份或原始配置记录恢复。

基于 AGPL-3.0 许可证发布