# vmess-domain-rotator 一个用于选择优选目标并写出运行时文件的工具集,当前支持两套独立模式: - 云服务器模式:调用远程 API,选择优选域名,写入 `runtime/`,可自动提交到 `runtime-state` - 本地路由器模式:调用本地 `cfst`,选择优选 IP,写入 `cfip_runtime/`;HTTP 暴露推荐使用项目目录下的完整 BusyBox `httpd` 这两套模式已经彻底拆开: - 服务器模式使用 [`config.server.json`](./config.server.json) - 本地 `cfst` 模式使用 [`config.router.json`](./config.router.json) - BusyBox 路由器脚本使用 [`router_local.conf`](./router_local.conf) 旧的 `config.json` / `config.example.json` 已废弃,不再使用。 ## 1. 目录与入口 核心脚本: - `scripts/domain_updater.py` 统一的 Python 主入口,支持 `api` 和 `cfst_local` 两种 `source.type` - `scripts/run_update_and_commit.sh` 服务器模式入口,执行 updater 并按配置同步运行时文件到 `runtime-state` - `scripts/install_debian.sh` Debian/Ubuntu 一键安装 systemd service + timer - `scripts/uninstall_debian.sh` 卸载 systemd service + timer - `scripts/router_local_update.sh` BusyBox `sh` 路由器入口,执行 `cfst` 并写出 `cfip_runtime` - `scripts/router_local_http.sh` BusyBox `sh` 路由器 HTTP 暴露入口,优先使用 BusyBox `httpd` 暴露运行时文件 - `scripts/update_vmess_links.py` 可选工具,用运行时文件里的值批量替换 `vmess://` 节点 配置文件: - `config.server.json` 服务器模式配置,输出默认是: - `runtime/current_domain.txt` - `runtime/current_domain.json` - `runtime/state.json` - `runtime/substore_vars.json` - `config.router.json` 本地 `cfst` 模式配置,输出默认是: - `cfip_runtime/current_ip.txt` - `cfip_runtime/current_ip.json` - `cfip_runtime/state.json` - `cfip_runtime/substore_vars.json` - `router_local.conf` BusyBox 路由器脚本配置,给 `router_local_update.sh` / `router_local_http.sh` 使用 ## 2. 共用设计 两套模式共用同一套“输出抽象”: - 文本值文件:当前选中的值 - JSON 文件:当前结果 - state 文件:上次可用值和状态 - export vars 文件:给外部系统消费 关键点: - 输出文件名和输出目录都从配置读取,不再在 shell 脚本里硬编码 `runtime/current_domain.txt` - `run_update_and_commit.sh` 会先解析配置里的输出路径,再决定同步哪些文件 - 只有服务器模式默认集成 git commit/push;路由器模式不做 git 操作 - `domain_updater.py` 会按 `--config` 所在目录解析相对路径 ## 3. 模式选择 ### 3.1 云服务器模式 适用场景: - 有 `python3` - 有网络访问目标 API - 需要 systemd 定时执行 - 需要自动提交 `runtime-state` 入口: - 手动执行:`python3 scripts/domain_updater.py --config config.server.json` - 定时执行:`bash scripts/run_update_and_commit.sh config.server.json` - Debian 安装:`sudo bash scripts/install_debian.sh --config config.server.json` ### 3.2 本地 `cfst` 模式 适用场景: - 在本机、Mac、Linux 上已有可执行的 `cfst` - 希望由 `domain_updater.py` 直接调用本地 `cfst` - 不需要 BusyBox 专用脚本 入口: - `python3 scripts/domain_updater.py --config config.router.json` ### 3.3 BusyBox 路由器模式 适用场景: - 路由器没有 Python - 只有 BusyBox `sh` / `awk` / `sed` 等基础工具即可执行更新脚本 - HTTP 暴露推荐使用项目目录下的完整 BusyBox,例如 `BUSYBOX_BIN="./busybox_armv7l"`,脚本会调用它的 `httpd` applet;系统自带精简 `nc` 不够 入口: - 更新结果:`sh scripts/router_local_update.sh ./router_local.conf` - 暴露 HTTP:`sh scripts/router_local_http.sh ./router_local.conf` ## 4. 云服务器模式部署 ### 4.1 前置条件 - Debian/Ubuntu - `git clone` 后在仓库目录执行 - 远程仓库已配置,例如 `origin` - 运行用户对仓库有读写权限 - 机器上可访问 `config.server.json` 中的 API ### 4.2 核心配置文件 服务器模式固定使用 [`config.server.json`](./config.server.json)。 它当前默认行为: - `source.type = "api"` - 通过 `api.url` 拉取候选结果 - 通过 `parser`、`record_mapping`、`record_filter`、`scoring` 选择目标 - 写出到 `runtime/` 你通常只需要修改: - `api.url` - `api.headers` - `parser` - `record_mapping` - `record_filter` - `scoring` - `healthcheck` ### 4.3 本地手动跑通 先做语法检查: ```bash env PYTHONPYCACHEPREFIX=/tmp/pycache python3 -m py_compile scripts/domain_updater.py ``` 执行一次: ```bash python3 scripts/domain_updater.py --config config.server.json ``` 查看结果: ```bash cat runtime/current_domain.txt cat runtime/current_domain.json cat runtime/state.json cat runtime/substore_vars.json ``` 如果你只想看脚本解析出的输出路径: ```bash python3 scripts/domain_updater.py --config config.server.json --print-output-settings ``` ### 4.4 自动提交到 runtime-state 服务器模式提交脚本: ```bash bash scripts/run_update_and_commit.sh config.server.json ``` 它会执行: 1. 调用 `domain_updater.py` 2. 从配置解析出运行时文件路径 3. 比较本次“选中值”和 `runtime-state` 分支上次记录 4. 值未变化时跳过 commit/push 5. 值变化时同步配置定义的输出文件并提交 强制提交: ```bash bash scripts/run_update_and_commit.sh --force-commit config.server.json ``` 或: ```bash GIT_FORCE_COMMIT=1 bash scripts/run_update_and_commit.sh config.server.json ``` ### 4.5 Debian 一键安装 推荐命令: ```bash sudo bash scripts/install_debian.sh --config config.server.json ``` 默认行为: - 使用当前 `sudo` 前的用户作为 service 用户 - 定时周期 `1h` - 自动 push 开启 - 目标分支 `runtime-state` 常用参数: ```bash sudo bash scripts/install_debian.sh \ --config config.server.json \ --interval 10min \ --git-push 1 \ --git-push-remote origin ``` 如果用 token: ```bash sudo bash scripts/install_debian.sh \ --config config.server.json \ --git-http-username \ --git-http-token-file /root/.config/vmess-token \ --git-use-credential-store 1 ``` ### 4.6 安装后验证 ```bash sudo systemctl status vmess-domain-rotator.timer sudo systemctl status vmess-domain-rotator.service sudo systemctl start vmess-domain-rotator.service sudo journalctl -u vmess-domain-rotator.service -n 120 --no-pager ``` 成功时通常应看到: - updater 输出 JSON - `committed output changes on runtime-state` - `pushed to origin/runtime-state` ### 4.7 卸载 ```bash sudo bash scripts/uninstall_debian.sh ``` 保留认证文件: ```bash sudo bash scripts/uninstall_debian.sh --keep-auth-files ``` ## 5. 本地 `cfst` 模式部署 ### 5.1 适用环境 - 有 `python3` - 有可执行的 `cfst` - 不依赖 BusyBox - 希望继续复用 `domain_updater.py` 的统一输出逻辑 ### 5.2 核心配置文件 本模式使用 [`config.router.json`](./config.router.json)。 它当前默认指向: - `./cfst_darwin_arm64/cfst` - 结果文件 `./cfst_darwin_arm64/result.csv` - 输出目录 `./cfip_runtime` 你通常只需要修改: - `cfst_local.work_dir` - `cfst_local.binary` - `cfst_local.run_args` - `cfst_local.result_file` - `cfst_local.columns` - `output.*` ### 5.3 手动执行 ```bash python3 scripts/domain_updater.py --config config.router.json ``` 查看结果: ```bash cat cfip_runtime/current_ip.txt cat cfip_runtime/current_ip.json cat cfip_runtime/state.json cat cfip_runtime/substore_vars.json ``` 查看解析出的输出路径: ```bash python3 scripts/domain_updater.py --config config.router.json --print-output-settings ``` ### 5.4 配置说明 `cfst_local` 关键项: - `work_dir` `cfst` 工作目录 - `binary` `cfst` 可执行文件路径 - `run_args` 执行参数数组,例如 `-f ip.txt -o result.csv -p 10` - `result_file` `cfst` 输出结果文件 - `skip_run` 为 `true` 时不执行 `cfst`,只解析现有结果文件 - `columns` CSV 列映射,默认按: - `0` IP - `1` 已发送 - `2` 已接收 - `3` 丢包率 - `4` 平均延迟 - `5` 下载速度 - `6` 地区 ## 6. BusyBox 路由器模式部署 ### 6.1 适用环境 - 路由器没有 Python - 已准备对应架构的 `cfst` - 已准备完整 BusyBox,例如本项目默认使用 `busybox_armv7l` - HTTP 暴露使用完整 BusyBox 的 `httpd` applet,不使用 ASUS 固件自带 `/usr/sbin/httpd` 架构以目标路由器实际输出为准: ```bash uname -m ``` `cfst` 和完整 BusyBox 都必须匹配路由器架构。ASUS RT-AC68U 示例里,完整 BusyBox 文件名使用 `busybox_armv7l`。 ### 6.2 路由器目录建议 示例(ASUS RT-AC68U 推荐放在 `/jffs/vmess`): ```text /jffs/vmess/ ├── busybox_armv7l ├── router_local.conf ├── scripts/ │ ├── router_local_update.sh │ └── router_local_http.sh └── cfst/ ├── cfst ├── ip.txt └── result.csv ``` 其中: - `router_local.conf` 指定 `CFST_WORK_DIR`、输出目录、HTTP 端口、完整 BusyBox 路径等 - `busybox_armv7l` 是你下载的完整 BusyBox,HTTP 服务脚本会优先调用它,而不是系统自带 BusyBox/ASUS `httpd` - `cfst/` 放路由器架构对应的 `cfst` ### 6.3 配置 router_local.conf 当前仓库里的 [`router_local.conf`](./router_local.conf) 就是 BusyBox 配置文件。 最重要的字段: - `CFST_WORK_DIR` `cfst` 所在目录 - `CFST_BIN` `cfst` 可执行文件名,一般是 `./cfst` - `CFST_IP_FILE` 输入 IP 列表 - `CFST_RESULT_FILE` `cfst` 结果文件 - `RUNTIME_DIR` 运行时输出目录,默认 `./cfip_runtime` - `VALUE_TEXT_FILE` 当前值文本文件,默认 `current_ip.txt` - `VALUE_JSON_FILE` 当前值 JSON 文件,默认 `current_ip.json` - `BUSYBOX_BIN` 完整 BusyBox 二进制路径,默认 `./busybox_armv7l`;HTTP 脚本会优先调用它的 `httpd` applet,避免误用 ASUS `/usr/sbin/httpd` - `HTTP_PORT` 局域网 HTTP 监听端口,默认 `8080` ### 6.4 手动更新一次 ```bash sh scripts/router_local_update.sh ./router_local.conf ``` 成功时会输出类似: ```text [router-local] selected ip: x.x.x.x ``` 生成文件默认在: - `cfip_runtime/current_ip.txt` - `cfip_runtime/index.html`(复制当前 IP 文本,供 `/` 访问) - `cfip_runtime/current_ip.json` - `cfip_runtime/state.json` - `cfip_runtime/substore_vars.json` ### 6.5 暴露到局域网 ```bash sh scripts/router_local_http.sh ./router_local.conf ``` 默认会监听: ```text 0.0.0.0:8080 ``` 这个脚本不再使用系统自带的精简 `nc` 或 ASUS `/usr/sbin/httpd`。它会读取 `router_local.conf` 里的 `BUSYBOX_BIN`,优先调用项目目录下完整 BusyBox 的 `httpd` applet: ```bash ./busybox_armv7l httpd -f -p 8080 -h ./cfip_runtime ``` 如果你的 BusyBox applet 列表里没有 `httpd`,当前脚本会明确报错。ASUS 固件自带的 `/usr/sbin/httpd` 通常是路由器管理后台服务,不是 BusyBox 静态文件服务器;即使支持 `-p` 端口参数,也不代表支持 `-h ./cfip_runtime` 这类目录发布。 可访问路径: - `/`(由 `index.html` 返回当前 IP 文本) - `/current_ip.txt` - `/current_ip.json` - `/state.json` - `/substore_vars.json` 例如局域网内访问: ```bash curl http://192.168.50.1:8080/current_ip.json curl http://192.168.50.1:8080/current_ip.txt ``` ### 6.6 ASUS RT-AC68U services-start 自启动 如果路由器使用 `/jffs/scripts/services-start` 统一启动任务,可以用 `cru` 注册定时更新和 watchdog。这个小节是 ASUS RT-AC68U / KoolShare 风格固件专用示例,假设项目放在 `/jffs/vmess`,完整 BusyBox 位于 `/jffs/vmess/busybox_armv7l`。 部署前建议确认: ```sh chmod +x /jffs/vmess/busybox_armv7l chmod +x /jffs/scripts/services-start /jffs/vmess/busybox_armv7l --list | grep '^httpd$' ``` `router_local.conf` 至少需要包含: ```sh BUSYBOX_BIN="./busybox_armv7l" HTTP_PORT="8080" ``` 把 `/jffs/scripts/services-start` 写成: ```sh #!/bin/sh /koolshare/bin/ks-services-start.sh VMESS_DIR="/jffs/vmess" CONFIG="./router_local.conf" UPDATE_LOG="/tmp/router_local_update.log" HTTP_LOG="/tmp/router_http.log" start_vmess_http() { if ps | grep -v grep | grep -q 'router_local_http.sh'; then return 0 fi cd "$VMESS_DIR" || exit 1 nohup sh scripts/router_local_http.sh "$CONFIG" >> "$HTTP_LOG" 2>&1 & } cd "$VMESS_DIR" || exit 1 sh scripts/router_local_update.sh "$CONFIG" >> "$UPDATE_LOG" 2>&1 cru d vmess_rotate cru a vmess_rotate "0 */3 * * * cd $VMESS_DIR && sh scripts/router_local_update.sh $CONFIG >> $UPDATE_LOG 2>&1" sleep 10 start_vmess_http cru d vmess_watchdog cru a vmess_watchdog "*/5 * * * * if ! ps | grep -v grep | grep -q 'router_local_http.sh'; then cd $VMESS_DIR && nohup sh scripts/router_local_http.sh $CONFIG >> $HTTP_LOG 2>&1 & fi" ``` `router_local_http.sh` 会保留 wrapper 进程并等待子进程里的完整 BusyBox `httpd`,所以 watchdog 用 `router_local_http.sh` 作为进程关键字可以正常判断服务是否仍在运行。示例里先 `cru d` 再 `cru a`,避免 `services-start` 被重复触发后留下旧的定时任务定义;启动 HTTP 前也会先检查进程,避免重复监听同一个端口。 ### 6.7 定时执行 可以用 BusyBox `crond` 定时更新,例如每 15 分钟执行一次: ```cron */15 * * * * cd /tmp/home/root/vmess-domain-rotator && sh scripts/router_local_update.sh ./router_local.conf >> /tmp/router_local_update.log 2>&1 ``` ### 6.8 手动常驻 HTTP HTTP 服务如果需要手动后台启动: ```bash cd /tmp/home/root/vmess-domain-rotator nohup sh scripts/router_local_http.sh ./router_local.conf >/tmp/router_http.log 2>&1 & ``` ## 7. VMess 链接批量替换 如果下游要消费“当前值文件”,可使用 `scripts/update_vmess_links.py`。 服务器模式示例: ```bash python3 scripts/update_vmess_links.py \ --input ./nodes.txt \ --output ./nodes.updated.txt \ --domain-file ./runtime/current_domain.txt ``` 路由器/本地 `cfst` 模式示例: ```bash python3 scripts/update_vmess_links.py \ --input ./nodes.txt \ --output ./nodes.updated.txt \ --domain-file ./cfip_runtime/current_ip.txt ``` 仅替换匹配名称的节点: ```bash python3 scripts/update_vmess_links.py \ --input ./nodes.txt \ --output ./nodes.updated.txt \ --domain-file ./runtime/current_domain.txt \ --name-regex "(argo|cf|vm)" ``` ## 8. 常用运维命令 服务器模式: ```bash sudo systemctl status vmess-domain-rotator.timer sudo systemctl status vmess-domain-rotator.service sudo systemctl start vmess-domain-rotator.service sudo journalctl -u vmess-domain-rotator.service -f git log runtime-state --oneline -n 20 ``` 路由器模式: ```bash sh scripts/router_local_update.sh ./router_local.conf sh scripts/router_local_http.sh ./router_local.conf cat cfip_runtime/current_ip.txt cat cfip_runtime/current_ip.json ``` ## 9. 注意事项 1. 服务器模式和路由器模式的配置不要混用。 2. `run_update_and_commit.sh` 设计目标是服务器模式;路由器模式默认不走 git 提交。 3. 服务器模式下,service 用户和 git 凭证用户必须一致,否则会出现 `terminal prompts disabled`。 4. `credential.helper store` 是明文存储,只适合可控服务器。 5. BusyBox 路由器模式下,`router_local_update.sh` 只依赖常见基础 applet;HTTP 暴露会优先调用 `BUSYBOX_BIN` 指向的完整 BusyBox `httpd`,不要依赖 ASUS `/usr/sbin/httpd` 或系统精简 `nc`。 6. `state.json` 需要持久化,否则 fallback 不可用。