海豹(SealDice)单文件 JS 插件:从比赛网站拉赛程 → 群内选对局 → @ 选手开局 → 引用结算截图上传成绩(人工审核)→ 比赛结束后用纯文本展示接下来的比赛。插件通过 WebSocket 实时接收控制器状态(自动重连),对接「三角占领 · 赛时控制器」的成绩上传协议。
.ts start(群管理):从比赛网站(competition_web)拉取当前进行中比赛的赛程, 列出候选对局(对局 ID + 双方单位名 + 成员 QQ);- 群管理发送
.ts start <对局ID>选择该场 → bot @ 全部选手并直接开局 (开局前自动随机选边:调用后端POST /api/bot/matches/{id}/randomize-sides交换掠夺者/守护者阵营,随后POST /api/init自动记录秘钥,不对外展示),发棋盘截图; - 选手引用(回复)结算截图 + 触发词(默认「上传成绩」)→ image-recognizer 识别 → bot 发【成绩待确认】消息 → 群管理引用回复 确认 / 修改 / 拒绝 → 确认后才上传;
- 插件加载即连接控制器
/ws状态通道(WebSocket,指数退避自动重连);控制器后端 在最后一分钟自动开始超时判定,超时后经/ws推送game_over,插件自动结束对局 并用纯文本展示接下来的比赛(最多 4 场 + 余量提示);.ts stop同样展示; .ts reconnect可手动重连控制器 WS(断线连续 3 次会在对局群提示,恢复后自动继续);- 选手身份不再手动绑定:从赛程名单匹配
QQ → 昵称 → 阵营(掠夺者=participant_a,守护者=participant_b),QQ 由选手在网站个人资料里自己填。
image-recognizer(作者错误,≥ 1.0.0)——本插件通过@depends声明;- image-recognizer 自身依赖
ob11网络连接依赖(拉取被引用消息图片)与AI骰娘4(图片模型识别,需开启「图片模型」并配置如glm-4v-plus); - 比赛网站(competition_web,需含赛程只读接口,见下文「后端运行人需要做什么」);
- 赛时控制器服务(
competition_web/demo,需已实现/api/v1/results协议与/api/init、/api/end、/api/state、/ws状态通道); - aiplugin4-backends 的
web-read后端(默认http://127.0.0.1:46799,MCP/mcp+screenshot_url工具),用于截取控制器网页。
- 上传
triangle-score-plugin.js到海豹 WebUI → 扩展功能 → JS 扩展,点击「重载 JS」; - 确保 image-recognizer 已加载(
image-recognizer.js或image-recognizer-cy2.js,二选一); - 在插件设置中填入:
siteUrl:比赛网站地址(机器人可访问,如http://<网站服务器>:8000);siteToken:比赛网站机器人令牌(对应后端.env的BOT_API_TOKEN),.ts start开局前自动随机选边时使用;后端未配置或留空则跳过自动选边;competitionId:比赛 ID,留空自动使用「当前进行中」的比赛;controllerUrl:赛时控制器地址(如http://<控制器服务器>:8001);screenshotUrl:web-read 后端地址(默认http://127.0.0.1:46799), 配置了 token 时再填screenshotToken;debugLog:调试日志开关(默认1)。截图/WS 状态详情会打印到海豹日志, 排查「截图失败」「比赛不结束」「WS 断线」时保持开启,稳定后可以改0;- 秘钥不需要配置:
.ts start开局时自动获取并存储。
.ts help 查看帮助
.ts status 查看配置 / 当前对局状态
.ts start [对局ID] 拉取赛程 → 展示候选;带 ID 直接选择开局(仅群管理以上)
.ts stop 结束比赛并展示接下来的比赛(仅群管理以上)
.ts reconnect 手动重连控制器 WebSocket(仅群管理以上)
.ts board 查看控制器当前比分/占领情况
.ts tasks 查看本局 21 个任务格的歌曲列表
.ts shot 截取控制器网页当前画面
- 群管理发送
.ts start,bot 拉取赛程并列出候选对局:对局ID 1 | 第1轮:掠夺者 阿晴(QQ:1001) vs 守护者 小澜(QQ:1002); - 群管理发送
.ts start 1选择该场(ID 见上一步候选列表); - bot 自动随机选边(后端
BOT_API_TOKEN+ 插件siteToken;未配置时跳过并提示), 随后 @ 已填 QQ 的选手并直接开局(POST /api/init自动记录秘钥、发送棋盘截图); 未填 QQ 的一侧会收到警告,仍可强制开局,但该侧结算截图无法自动匹配身份上传; - 比赛进行中,群管理可
引用结算截图 + 上传成绩; - 比赛时间到:控制器后台在最后一分钟自动开始超时判定,超时后经
/ws推送game_over,插件自动结束对局并用纯文本展示接下来的比赛 (如「第2轮 局7:掠夺者「X」 vs 守护者「Y」」,最多 4 场);.ts stop同样展示。
- 选手引用(回复)一张结算截图,消息文本填「上传成绩」(触发词可在插件设置中修改);
- bot 识别后不会直接上传,而是发出一条【成绩待确认 · 编号 xxxx】消息;
- 群管理(管理员 / 群主 / 骰主)引用该确认消息回复:
确认—— 按识别到的成绩上传;修改 <字段> <值>—— 修改后再上传,可改score / tp / miss / bad / good / mm / fc, 例:修改 score 991420、修改 miss 0 bad 0 good 1、修改 tp 95.5;拒绝—— 作废该成绩;
- 上传成功后 bot 返回结果文字 + 控制器网页截图;截图失败回退纯文本。
待审成绩 15 分钟未处理自动作废;同群同一选手同一阵营同时只允许一条待审成绩(不同选手可各自上传, 互不阻塞)。上传被拒绝或网络失败时待审成绩会保留(确认/修改分支不会先删后传), 群管理可重新引用确认或继续修改。
限制:当前版本同一时刻只支持一个群跑一局(对局 / 秘钥均为全局单实例),
.ts start会拒绝在已有对局的群之外再开一局;审核待审成绩时会校验其群归属, 其他群不能代审;.ts stop也只会清空对局所在群的秘钥与状态。
开局提示:选手未在网站个人资料填写 QQ 时开局不拦截,仅警告(无法 @ 与自动匹配身份, 需选手补填后重新开局)。控制器 WS 若连续 3 次重连失败,bot 会在对局群提示控制器 可能失联;恢复后自动重连并清零失败计数(也可用
.ts reconnect手动重连)。
- 状态通道是 WebSocket(
new WebSocket,海豹 1.5.1+ 提供),不依赖常驻轮询定时器: 插件加载(onLoad)即尝试连接控制器/ws,连接句柄与重连状态挂在全局 (globalThis.__tsWsState),断线用单次setTimeout指数退避重连 (1s → 2s → 4s … 封顶 30s),不存在会被重载打断的常驻循环; - 热重载不打断进行中的比赛:对局身份、秘钥、待审成绩全部存在
ext.storage, 重载后 onLoad 幂等地确认 WS 连接,海豹已关闭的旧连接会被自动重建, 正在进行的对局照常上传、审核和结束;插件加载时会在海豹日志打印恢复信息 (检测到进行中对局…控制器 WS:已连接/重连中),可据此确认重载后比赛已接续; - 待审成绩 15 分钟未审自动作废(在 WS 状态更新 / 上传交互时惰性清理)。
- 赛程接口(网站侧,公开只读):
GET {siteUrl}/api/schedule/current或GET {siteUrl}/api/competitions/{id}/schedule, 返回{competition, matches[]},每局含participant_a/b {type, name, qqs}; - 状态通道(控制器侧):
WS {controllerUrl}/ws。连接后立即收到一帧{"type":"state_update", ...完整状态},之后每次状态变更(含超时game_over:true) 都会推送;超时判定由控制器后端后台任务驱动,插件不再调用/api/tick轮询; - 上传端点:
POST {controllerUrl}/api/v1/results - 请求头:
X-Match-Token(比赛令牌)、X-Team-Token(按选手阵营选择) - 请求体:
{ api_version, client_msg_id, team, player:{id,name}, song:{name,level,type}, result:{score,tp,mm,full_combo,miss,bad,good} } - 响应
outcome:occupied(占领成功)/l1_holder(L1 挑战成功)/l1_challenged_lost(L1 未超过)/already_occupied(已被占领)等
- 导入歌曲库:控制器启动后执行
POST {controllerUrl}/api/songs,请求体为 JSON{"songs": [{"name": "歌名", "type": "Glitch|Chaos|Hard", "level": "难度"}]}(也可以在控制器网页里导入),否则.ts start开局时会提示先导入歌曲库; - 比赛网站(competition_web)需更新到含赛程只读接口的版本:
git pull后重启网站服务(8000 端口),验证GET {siteUrl}/api/schedule/current(或指定比赛GET {siteUrl}/api/competitions/{id}/schedule)可访问;比赛需处于ongoing状态且选手已批准报名、对局已排定(进入 ongoing 时引擎自动生成); - 选手填 QQ:参赛选手在网站「个人资料」填写 QQ(机器人靠它 @ 与匹配身份);
- 秘钥不需要人工获取:插件
.ts start开局时会调/api/init自动拿并保存。 - 控制器需重启:升级到带
/ws状态通道 + 后台超时看门狗的版本后,需要 重启控制器进程(不是热更新)才生效;重启后用WS {controllerUrl}/ws连接测试,能收到state_update帧即正常。
协议完整定义见「三角占领 · 赛时控制器」项目内的成绩上传协议文档。