550 lines
19 KiB
Markdown
550 lines
19 KiB
Markdown
# JWeBot
|
||
|
||
[中文](#中文说明) | [English](#english)
|
||
|
||
## 中文说明
|
||
|
||
JWeBot 是运行于 Linux 的单群微信自动回复系统。它通过本地 OCR
|
||
观察选定的微信窗口,将消息写入持久化 Edge 账本,调用经过审核的命令插件,
|
||
并通过带视觉确认的 GUI 发送链路回复。
|
||
|
||
当前版本:`alpha 0.6.10.1015`。
|
||
|
||
0.6.1 新增本地上下文总结、人物与别名索引、单人虚拟金币游戏,并增加
|
||
两秒画面复核以补抓遗漏的消息变化。命令和游戏管理规则见
|
||
[0.6.1 插件说明](docs/local-plugins-0.6.1.md)。
|
||
0.6.2 为 DeepSeek 流式回复增加 30 秒总时限,避免未结束的上游连接长期
|
||
占满生成 worker;21 点开局金额明确作为本局下注,并兼容 OCR 丢失空格。
|
||
21 点牌面和结算以当前玩家展示名开头。快速 ACK、重试和严格顺序投递
|
||
规则保持不变。
|
||
0.6.3 将彩票改为群内多人轮次,加入报名、状态、主持人开奖和取消;
|
||
抽奖、彩票、21 点统一使用 `Jacky #N:...` 局号并支持 `/游戏记录`。
|
||
完整玩法见[多人彩票与游戏记录](docs/group-lottery-0.6.3.md)。
|
||
0.6.4 新增 2~8 人 CS Major 自走棋:职业选手卡池、属性/定位/战术
|
||
克制、小组单循环、1v4 与 2v3 淘汰赛,以及 BO3/BO5 的 MR15 加时赛。
|
||
完整规则见[CS Major 自走棋设计稿](docs/cs-major-autochess-design.md)。
|
||
0.6.5 将 AI 测试模式扩展到双人:两名真人可一键补入 AI 战队 A、B,
|
||
两支 AI 独立运营。
|
||
0.6.6 重构自走棋流程:开赛后三轮选秀,第一轮从固定 10 人教练池中
|
||
选教练,第二、三轮选选手;随后进入训练赛、小组赛、挑战、淘汰赛和
|
||
总决赛。主持消息会拆分为多条回复;所有需要推进的阶段由相关真人发送
|
||
`/结束回合` 自动进入下一步,已淘汰玩家不会再收到赛前准备提示。
|
||
0.6.7 继续强化文字自走棋体验:卡池扩充到 100+ 人、教练库扩充到
|
||
18 人;训练赛改为按参赛人数生成的真实对阵轮次;单图比分由 MR15
|
||
逐回合模拟产生,不再使用固定比分档;赛事叙事素材扩充到 120+ 条,
|
||
D 牌、锁店、调阵容和战术窗口会在轮次之间自然提示。
|
||
0.6.8 增加旧版自走棋 active 状态兼容:如果群里残留 0.6.6/0.6.7
|
||
之前创建的未完成赛事,新的 `/自走棋开始` 会原子归档旧档案并开启新版
|
||
赛季,不需要维护人员手工改库。
|
||
0.6.9 将 CS Major 自走棋的公开游戏数据拆出到包内 JSON:选手卡池、
|
||
教练池、叙事素材、商店概率、经济奖励、选秀节奏、羁绊阈值、赛制和
|
||
MR15/加时参数都由数据文件驱动,启动时会校验字段、ID 和素材数量。
|
||
同版新增虚拟金币转账:`/转账 对方 金额` 会在同一事务内扣款入账,
|
||
收款人需先发送 `/金币` 初始化账户。
|
||
0.6.10 新增虚拟金币贷款:`/贷款 [金额]` 最多借入 500 金币,`/还款`
|
||
可按金额或全部归还;每个账户同一时间最多一笔未还贷款。
|
||
|
||
### 运行流程
|
||
|
||
```text
|
||
微信窗口
|
||
-> 自适应 XDamage 截图
|
||
-> 本地 OCR 与群指纹校验
|
||
-> 持久化 Edge 账本
|
||
-> 已审核的本地插件
|
||
-> DeepSeek 直接对话
|
||
-> 可选云端智能体
|
||
-> 带优先级的 GUI 队列
|
||
-> 输入草稿、发送与视觉确认
|
||
```
|
||
|
||
运行时绑定一个可见群聊。插件不能选择群聊、操作 GUI、读取环境密钥、
|
||
打开任意网络连接、执行 shell 命令或直接访问 SQLite。
|
||
|
||
### 快速开始
|
||
|
||
运行环境:
|
||
|
||
- Linux 与 systemd 用户服务;
|
||
- 已登录的桌面微信,以及 X11/VNC 显示会话 `:1`;
|
||
- Python 3.9 或更新版本;
|
||
- `xdotool`、`scrot`、`xclip`、OpenCV、Pillow、NumPy 和 RapidOCR;
|
||
- Steam 功能所需的本机回环 Mihomo HTTP 代理。
|
||
|
||
创建并激活私有配置:
|
||
|
||
```sh
|
||
./tools/jwebot-config.py init personal --activate
|
||
```
|
||
|
||
编辑 `~/.config/jwebot/profiles/personal/` 下的配置,然后重新激活并验证:
|
||
|
||
```sh
|
||
./tools/jwebot-config.py activate personal
|
||
PATH="$PWD/.venv/bin:$PATH" ./tools/check-local.sh
|
||
```
|
||
|
||
服务读取激活时生成的配置,不直接读取源 profile。激活过程先校验配置,
|
||
再原子生成以下文件:
|
||
|
||
```text
|
||
~/.config/jwebot/current/generated/
|
||
runtime.env
|
||
root.env
|
||
private-context.json
|
||
notification.json
|
||
manifest.json
|
||
```
|
||
|
||
导入、脱敏导出、旧配置迁移、调试归档和 root 部署设置见
|
||
[配置系统说明](config/README.md);完整安装步骤见
|
||
[从零部署指南](docs/runbooks/zero-to-running.md)。
|
||
|
||
### 常用命令
|
||
|
||
内置命令清单由经过审核的插件描述文件生成:
|
||
|
||
```text
|
||
/功能列表
|
||
/上下文 [100|200|300](/context)
|
||
/人物列表 [页码](/people);/人物 [名称或别名](/person)
|
||
/金币(/wallet);/抽奖 [金额](/draw)
|
||
/转账 对方 金额(/transfer name amount)
|
||
/贷款 [金额](/loan amount);/还款 [金额|全部](/repay)
|
||
/彩票 开始|报名|当前状态|开奖|取消(/lottery)
|
||
/21点 开始 [1~500];开局后 /21点 要牌|停牌|状态|结束
|
||
/游戏记录 [页码](/gamehistory)
|
||
/游戏帮助(/games)
|
||
/自走棋开始|报名|开赛|状态|档案
|
||
/自走棋加AI战队(两名或三名真人测试模式)
|
||
/选1~6(教练轮为 /选1~10);/刷新;/锁定
|
||
/战术 火力|稳定|残局;/阵容;/上阵 备战编号 [首发位];/出售 备战编号;/结束回合
|
||
/丢骰子 [NdM]
|
||
/猜数字 开始 [上限]
|
||
/石头剪刀布 [开始|加入|出|状态|结算|结束]
|
||
/记住 内容
|
||
/人物记忆 显示名
|
||
/扫码排行
|
||
/复读榜
|
||
/查记录 关键词
|
||
/状态
|
||
/version
|
||
/debug
|
||
/steam [在线|游戏|全部|刷新]
|
||
/steam时长 游戏名[、玩家名]
|
||
```
|
||
|
||
Steam 时长查询允许省略空格,以兼容 OCR。指定玩家时使用明确的分隔符:
|
||
|
||
```text
|
||
/steam 时长 Dota 2
|
||
/steam时长Dota2
|
||
/steam时长Dota2、玩家名
|
||
/steam时长Dota2人=玩家名
|
||
```
|
||
|
||
Steam 优先使用当前私有 profile 中的人物 SteamID 映射,显示对应的群内名称,
|
||
并支持群内别名。配置了人物名单时,查询仅覆盖名单内账号;未配置名单时,
|
||
才在运行时发现配置账号的公开好友列表。游戏名和玩家名支持模糊匹配;
|
||
全员查询限制并发数,指定玩家只查询该账号的游戏库。所有 Steam 请求必须
|
||
经过配置的本机回环代理。源码不包含真实 SteamID、人物名称、别名或固定名单。
|
||
|
||
### 独立配置
|
||
|
||
每个 profile 按插件配置包组织:
|
||
|
||
```text
|
||
manifest.toml
|
||
components/
|
||
bot.toml
|
||
providers.toml
|
||
runtime.toml
|
||
debug.toml
|
||
notification.toml
|
||
deployment.toml
|
||
context/
|
||
people.json
|
||
secrets/
|
||
runtime.env
|
||
overrides/
|
||
runtime.env
|
||
root.env
|
||
```
|
||
|
||
真实配置放在 Git 外:目录权限为 `0700`,文件权限为 `0600`。
|
||
事故、调试、遥测和迁移后的 root 操作资料放在
|
||
`~/.local/state/jwebot/profiles/<profile>/`。
|
||
字段和配置流程见 [配置指南](docs/configuration-guide.md)。
|
||
|
||
### 插件与 SDK
|
||
|
||
公共 SDK 位于 `packages/jwebot-sdk/`,最小示例位于
|
||
`examples/hello-command/`。插件注册在启动期完成,运行前冻结命令所有权。
|
||
|
||
创建插件:
|
||
|
||
```sh
|
||
./tools/create-plugin.py weather-command /weather \
|
||
--author example \
|
||
--description "Query approved weather data" \
|
||
--help-entry "/weather city"
|
||
```
|
||
|
||
校验、打包并签名:
|
||
|
||
```sh
|
||
./tools/validate-plugin.py extensions/friends/weather-command/plugin.toml
|
||
./tools/package-plugin.py extensions/friends/weather-command --output /tmp/release
|
||
./tools/sign-release.py /tmp/release/*.release.json \
|
||
--private-key /protected/path/publisher \
|
||
--publisher example
|
||
```
|
||
|
||
签名完成后,按 [文档目录](docs/) 中的扩展发布与激活流程安装。
|
||
|
||
### 部署
|
||
|
||
新机器请按 [从零部署指南](docs/runbooks/zero-to-running.md) 操作。
|
||
已有安装使用协调部署脚本;切换前会保留旧运行时、用户服务、生成配置、
|
||
SQLite 状态和 systemd 服务结构:
|
||
|
||
```sh
|
||
sudo ./deploy/scripts/deploy-jwebot-production.sh /path/to/JWeBot jwebot
|
||
```
|
||
|
||
部署必须通过微信窗口就绪、队列清空、Edge 健康、SQLite 完整性和自适应
|
||
录制服务重启稳定性检查。旧 OCR 服务保留安装,但保持禁用。
|
||
|
||
### 仓库结构
|
||
|
||
| 路径 | 用途 |
|
||
|---|---|
|
||
| `packages/jwebot-config/` | 配置校验、生成、导入导出、激活与旧配置迁移。 |
|
||
| `packages/jwebot-sdk/` | 权限受限的公共插件接口。 |
|
||
| `packages/jwebot-kernel/` | 路由、账本、策略、发布校验与 Edge 运行时。 |
|
||
| `packages/jwebot-extension-host/` | 隔离运行已签名扩展的进程宿主。 |
|
||
| `packages/jwebot-official-extensions/` | 已审核的命令引擎与生成适配器。 |
|
||
| `extensions/official/` | 官方 service-profile 适配器。 |
|
||
| `extensions/jwebot/` | 内置命令插件。 |
|
||
| `extensions/friends/` | 贡献者插件目录。 |
|
||
| `config/` | 公开模板与 schema,不存放真实配置。 |
|
||
| `deploy/` | 初始化、协调部署与 systemd 单元。 |
|
||
| `docs/` | 架构、SDK、配置与运行指南。 |
|
||
| `tests/` | 单元、行为一致性、打包、安全与部署契约测试。 |
|
||
| `tools/` | 校验、打包、签名、激活、隐私扫描与配置工具。 |
|
||
|
||
根目录 Python 文件是部署服务使用的兼容入口,实际实现位于 `packages/`。
|
||
|
||
### 项目进度
|
||
|
||
```text
|
||
独立配置与私有资料迁移 [####################] 100%
|
||
插件所有权与 SDK 边界 [####################] 100%
|
||
Steam 状态与时长查询 [####################] 100%
|
||
本地单元与行为一致性 [####################] 100%
|
||
从零部署自动化 [################----] 80%
|
||
跨发行版 staging 验证 [########------------] 40%
|
||
```
|
||
|
||
进度描述仓库中已完成、可测试的交付内容。实际运行效果仍取决于目标桌面、
|
||
微信版本、OCR 模型、代理和操作者提供的凭据。
|
||
|
||
### 验证与隐私
|
||
|
||
运行完整本地检查:
|
||
|
||
```sh
|
||
PATH="$PWD/.venv/bin:$PATH" ./tools/check-local.sh
|
||
```
|
||
|
||
检查当前文件及全部 Git 对象历史:
|
||
|
||
```sh
|
||
./tools/check-secrets.py
|
||
./tools/check-secrets.py --history
|
||
```
|
||
|
||
扫描器只报告类别、路径、行号、对象哈希和单向指纹,不输出匹配到的敏感值。
|
||
不要提交已填写的 profile、生成环境、人物上下文、截图、OCR 历史、调试内容、
|
||
事故归档、SQLite 文件、发布者私钥、API 密钥、SteamID 或真实主机路径。
|
||
|
||
仓库只提供公开模板与 schema。默认配置导出会脱敏;私有导出需要显式指定
|
||
`--include-private`,导出结果应继续保存在 Git 外。
|
||
|
||
### 许可证
|
||
|
||
JWeBot 使用 Apache License 2.0,见 [LICENSE](LICENSE)。
|
||
|
||
---
|
||
|
||
## English
|
||
|
||
JWeBot is a single-group WeChat automation runtime for Linux. It observes one
|
||
selected WeChat window through local OCR, routes messages through a durable
|
||
Edge ledger, runs reviewed command plugins, and sends replies through a
|
||
visually confirmed GUI bridge.
|
||
|
||
Current version:
|
||
|
||
```text
|
||
alpha 0.6.10.1015
|
||
```
|
||
|
||
## Runtime Flow
|
||
|
||
```text
|
||
WeChat window
|
||
-> adaptive XDamage capture
|
||
-> local OCR and group fingerprint gate
|
||
-> durable Edge ledger
|
||
-> reviewed local plugin
|
||
-> DeepSeek direct chat
|
||
-> optional cloud agent
|
||
-> priority GUI queue
|
||
-> guarded draft/send/visual confirmation
|
||
```
|
||
|
||
The runtime is deliberately bound to one visible group. Plugins cannot select a
|
||
group, access the GUI, read environment secrets, open arbitrary sockets, invoke
|
||
shell commands, or access raw SQLite state.
|
||
|
||
## Quick Start
|
||
|
||
Requirements:
|
||
|
||
- Linux with systemd user services;
|
||
- a working desktop WeChat session on X11/VNC display `:1`;
|
||
- Python 3.9 or newer;
|
||
- `xdotool`, `scrot`, `xclip`, OpenCV, Pillow, NumPy, and RapidOCR;
|
||
- an optional loopback Mihomo HTTP proxy for Steam.
|
||
|
||
Create a private configuration profile:
|
||
|
||
```sh
|
||
./tools/jwebot-config.py init personal --activate
|
||
```
|
||
|
||
Edit files under:
|
||
|
||
```text
|
||
~/.config/jwebot/profiles/personal/
|
||
```
|
||
|
||
Then compile and validate:
|
||
|
||
```sh
|
||
./tools/jwebot-config.py activate personal
|
||
PATH="$PWD/.venv/bin:$PATH" ./tools/check-local.sh
|
||
```
|
||
|
||
The source profile is never read directly by services. Activation validates and
|
||
atomically generates:
|
||
|
||
```text
|
||
~/.config/jwebot/current/generated/
|
||
runtime.env
|
||
root.env
|
||
private-context.json
|
||
notification.json
|
||
manifest.json
|
||
```
|
||
|
||
See [config/README.md](config/README.md) for import, redacted export, legacy
|
||
migration, debug archives, and root deployment settings.
|
||
|
||
## Commands
|
||
|
||
The built-in command inventory is generated from reviewed plugin descriptors.
|
||
Common commands include:
|
||
|
||
```text
|
||
/功能列表
|
||
/丢骰子 [NdM]
|
||
/猜数字 开始 [上限]
|
||
/石头剪刀布 [开始|加入|出|状态|结算|结束]
|
||
/记住 内容
|
||
/人物记忆 显示名
|
||
/扫码排行
|
||
/复读榜
|
||
/查记录 关键词
|
||
/状态
|
||
/version
|
||
/debug
|
||
/steam [在线|游戏|全部|刷新]
|
||
/steam时长 游戏名[、玩家名]
|
||
```
|
||
|
||
OCR whitespace is optional for Steam playtime queries. A player selector uses
|
||
the final separator shown below:
|
||
|
||
```text
|
||
/steam 时长 Dota 2
|
||
/steam时长Dota2
|
||
/steam时长Dota2、玩家名
|
||
/steam时长Dota2人=玩家名
|
||
```
|
||
|
||
Version 0.6.1 adds local `/context 100|200|300` extractive summaries,
|
||
`/people` and `/person` lookup, and `/wallet`, `/draw`, `/lottery`, `/blackjack`
|
||
single-player virtual coin games. Chinese aliases share the same implementations.
|
||
Accounts receive 1000 virtual coins once; funds, game state and event replay
|
||
results commit together. `/transfer name amount` moves virtual coins between
|
||
initialized group-scoped accounts in one ledger transaction. OCR display aliases
|
||
identify group-scoped people; `/loan amount` and `/repay` manage one capped
|
||
virtual coin loan per account. Native WeChat account IDs are unavailable. See
|
||
[plugin rules and capture reconciliation](docs/local-plugins-0.6.1.md).
|
||
Version 0.6.2 adds a 30-second total deadline to direct DeepSeek streams so an
|
||
unfinished upstream response cannot occupy both generation workers for about
|
||
15 minutes. Blackjack start amounts are the current round's stake; compact OCR
|
||
forms are accepted, while hit/stand remain explicit follow-up commands.
|
||
Blackjack hand and settlement replies start with the current player's configured
|
||
display label. ACKs, retries, and ordered final delivery remain unchanged.
|
||
Version 0.6.3 replaces instant lottery tickets with persistent multiplayer
|
||
group rounds, adds indexed personal game history, and raises blackjack stakes
|
||
to 1-500 coins. See [multiplayer lottery and game records](docs/group-lottery-0.6.3.md).
|
||
|
||
Steam prefers private per-person SteamID mappings from the active profile and
|
||
uses the corresponding group display IDs and aliases. Without a private roster,
|
||
it discovers the configured account's public friend list at runtime. No real
|
||
SteamID, display name, alias, or fixed roster is stored in source. Game and
|
||
player names support fuzzy matching; all-player requests use bounded
|
||
concurrency, while player-specific requests read only the selected account's
|
||
game library. Every request must use the configured loopback proxy.
|
||
|
||
## Configuration Profiles
|
||
|
||
Each profile is organized like a plugin package:
|
||
|
||
```text
|
||
manifest.toml
|
||
components/
|
||
bot.toml
|
||
providers.toml
|
||
runtime.toml
|
||
debug.toml
|
||
notification.toml
|
||
deployment.toml
|
||
context/
|
||
people.json
|
||
secrets/
|
||
runtime.env
|
||
overrides/
|
||
runtime.env
|
||
root.env
|
||
```
|
||
|
||
Real profiles live outside Git with `0700` directories and `0600` files.
|
||
Incidents, debug sessions, telemetry, and migrated root material live under
|
||
`~/.local/state/jwebot/profiles/<profile>/`.
|
||
|
||
## Plugins And SDK
|
||
|
||
The public SDK is in `packages/jwebot-sdk/`. A minimal extension is available
|
||
at `examples/hello-command/`.
|
||
|
||
Create a plugin:
|
||
|
||
```sh
|
||
./tools/create-plugin.py weather-command /weather \
|
||
--author example \
|
||
--description "Query approved weather data" \
|
||
--help-entry "/weather city"
|
||
```
|
||
|
||
Validate, package, sign, and activate:
|
||
|
||
```sh
|
||
./tools/validate-plugin.py extensions/friends/weather-command/plugin.toml
|
||
./tools/package-plugin.py extensions/friends/weather-command --output /tmp/release
|
||
./tools/sign-release.py /tmp/release/*.release.json \
|
||
--private-key /protected/path/publisher \
|
||
--publisher example
|
||
```
|
||
|
||
Extension registration is dynamic only during startup and freezes before
|
||
runtime routing.
|
||
|
||
## Deployment
|
||
|
||
For a new machine, follow
|
||
[docs/runbooks/zero-to-running.md](docs/runbooks/zero-to-running.md).
|
||
|
||
For an existing installation, the coordinated deployer preserves the previous
|
||
runtime, user units, generated configuration, SQLite state, and systemd
|
||
topology before cutover:
|
||
|
||
```sh
|
||
sudo ./deploy/scripts/deploy-jwebot-production.sh /path/to/JWeBot jwebot
|
||
```
|
||
|
||
The deployer refuses cutover unless the ready-window, empty-queue, Edge health,
|
||
SQLite integrity, and adaptive restart-stability gates pass. Legacy OCR units
|
||
remain installed but disabled.
|
||
|
||
## Repository Map
|
||
|
||
| Path | Purpose |
|
||
|---|---|
|
||
| `packages/jwebot-config/` | Profile validation, generation, import/export, activation, and legacy migration. |
|
||
| `packages/jwebot-sdk/` | Capability-limited public plugin contracts. |
|
||
| `packages/jwebot-kernel/` | Routing, ledger, policy, release verification, and Edge runtime. |
|
||
| `packages/jwebot-extension-host/` | Isolated signed-extension process host. |
|
||
| `packages/jwebot-official-extensions/` | Reviewed command engine and generated adapters. |
|
||
| `extensions/official/` | Official service-profile adapters. |
|
||
| `extensions/jwebot/` | Built-in reviewed command plugins. |
|
||
| `extensions/friends/` | Contributor plugin area. |
|
||
| `config/` | Public templates and schemas only. |
|
||
| `deploy/` | Bootstrap, coordinated deployment, and systemd units. |
|
||
| `docs/` | Architecture, SDK, configuration, and runbooks. |
|
||
| `tests/` | Unit, parity, packaging, safety, and deployment contract tests. |
|
||
| `tools/` | Validation, packaging, signing, activation, scanning, and config CLI. |
|
||
|
||
The root Python files are compatibility entrypoints used by deployed services;
|
||
implementation packages remain under `packages/`.
|
||
|
||
## Project Status
|
||
|
||
```text
|
||
Config profiles and private migration [####################] 100%
|
||
Plugin ownership and SDK boundaries [####################] 100%
|
||
Steam status and playtime modes [####################] 100%
|
||
Local unit and parity coverage [####################] 100%
|
||
Zero-to-running automation [################----] 80%
|
||
Cross-distribution staging [########------------] 40%
|
||
```
|
||
|
||
Percentages describe completed, testable deliverables in this repository.
|
||
Production readiness still depends on the target desktop, WeChat build, OCR
|
||
models, proxy, and operator-provided credentials.
|
||
|
||
## Verification
|
||
|
||
Run the complete local gate:
|
||
|
||
```sh
|
||
PATH="$PWD/.venv/bin:$PATH" ./tools/check-local.sh
|
||
```
|
||
|
||
Run privacy scans explicitly:
|
||
|
||
```sh
|
||
./tools/check-secrets.py
|
||
./tools/check-secrets.py --history
|
||
```
|
||
|
||
The scanner reports only category, path, line, object hash, and a one-way value
|
||
fingerprint. It never prints the matched value.
|
||
|
||
## Privacy
|
||
|
||
Do not commit populated profiles, generated environments, private context,
|
||
screenshots, OCR history, debug payloads, incident archives, SQLite files,
|
||
publisher private keys, API keys, SteamIDs, or host-specific paths.
|
||
|
||
The repository includes safe templates and schemas. Default profile export is
|
||
redacted; private export requires an explicit `--include-private` flag.
|
||
|
||
## License
|
||
|
||
JWeBot is licensed under Apache License 2.0. See [LICENSE](LICENSE).
|