koishi-plugin-tibo-reset
v0.1.11
Published
Poll Tibo Codex reset announcements and notify whitelisted Koishi groups.
Maintainers
Readme
koishi-plugin-tibo-reset
参考 koishi-plugin-zssm 的 TypeScript 项目结构构建。轮询 https://codex-resets.com/api/v1/status,向白名单群推送 Tibo 的 Codex 重置预告。
安装与配置
需要 Node.js 20.19+、Koishi 4.18.8+,以及已启用的 HTTP 服务和机器人适配器。不依赖数据库插件。
npm install
npm test
npm run build
npm pack将打包得到的 koishi-plugin-tibo-reset-*.tgz 安装到 Koishi 项目,在插件配置中启用 tibo-reset。
插件导出 Koishi Config Schema,控制台自动生成“轮询设置”“HTTP 请求”“通知内容”“群白名单”“AI 问答”“测试推送”分组表单,保存配置后由 Koishi 重载生效。
| 配置 | 默认值 | 说明 |
| --- | --- | --- |
| enabled | true | 启用插件 |
| pollInterval | 300 | 轮询间隔,秒,范围 10–86400 |
| requestTimeout | 15 | HTTP 超时,秒,范围 1–120 |
| eventTypes | 两种类型 | direct_reset 全员重置;reset_credit 发放重置卡 |
| notifyConfirmed | false | 是否同时推送确认完成/发放 |
| groups | [] | channel ID 字符串白名单;空白名单不向任何群发送 |
| testPushToken | "" | 测试批次,填写新值后模拟最新一条事件并实际推送 |
| enableQuery | true | 启用白名单群的重置问答,未配置模型密钥时不启用 |
| aiTextEndpoint | https://api.deepseek.com/v1 | 兼容 Chat Completions 的文本模型地址 |
| aiTextToken | "" | 文本模型 API key,控制台以密码字段显示 |
| aiTextModel | deepseek-chat | 必须支持 tool calling 的模型名称 |
| aiRequestTimeout | 60 | 每次问答总超时,秒,范围 5–300 |
| http400Retries | 2 | HTTP 400 的额外重试次数,范围 0–5;0 表示不重试 |
| httpRetryDelay | 1 | 重试基础间隔,秒,范围 0–30;每次翻倍,单次等待不超过 30 秒 |
Koishi YAML 配置示例:
plugins:
tibo-reset:
pollInterval: 300
requestTimeout: 15
notifyConfirmed: false
eventTypes:
- direct_reset
- reset_credit
groups:
- "987654321"
enableQuery: true
aiTextEndpoint: "https://api.deepseek.com/v1"
aiTextToken: "填写你的模型 API key"
aiTextModel: "deepseek-chat"
aiRequestTimeout: 60
http400Retries: 2
httpRetryDelay: 1每项只填写 channel ID(群号/频道 ID)字符串,不需要平台、机器人账号或 guild ID。使用 Koishi 中第一个在线机器人发送,不向其他机器人广播;没有在线机器人时保留队列,等下次轮询重试。适合单机器人环境,多机器人环境请确保第一个在线机器人可以访问所填频道。仅配置群聊目标,不要填私聊频道 ID。
从旧配置升级时,保留原有 eventTypes 和 notifyConfirmed 设置。旧 v1/v2 状态文件会迁移为 v3:保留待发送群的进度和测试批次,首次成功读取新接口时为当前记录建立基线,不补发当前快照中的旧消息。
数据结构与数据源
只请求 GET /api/v1/status,不请求旧接口、/resets 列表或其他端点。轮询与问答共用此接口。
| 字段 | 含义与用途 |
| --- | --- |
| data.latest_reset | 最新已执行重置;内部通知状态为 completed |
| data.scheduled_reset | 上游保留的预告,包含 status: scheduled、可空的 scheduled_for;标签可能未随发放更新 |
| data.active_watch | 无 ID 的 AI 预测,仅供问答参考,不主动推送,不作为官方承诺 |
| data.stats | 接口提供的统计,保留给问答使用 |
| meta.generated_at | 接口生成时间,不是重置完成时间 |
重置记录直接保留接口的 id、reset_type(regular / banked)、announced_at、英文 text 和 source(type、可选 author / url)。通知队列只额外标明记录所在状态,避免重建旧接口的标题、帖子数组、时间区间等字段。announced_at 是公告或首次观察时间,不能冒充执行完成时间。
现有配置 direct_reset 映射 regular,reset_credit 映射 banked。默认仍只推送新预告;需要接收新出现的 latest_reset,请开启 notifyConfirmed。相同 ID 已记录后,不因预告变为已执行而再次推送。
此接口是当前快照,最多包含最新执行记录和一个预告,不是完整历史。轮询间隔内被后续记录替换的事件无法通过此接口补齐。
重置问答
沿用 zssm 的 aiTextEndpoint、aiTextToken、aiTextModel 配置方式,但不自动读取 zssm 的密钥。模型必须支持 Chat Completions 的 tools、tool_choice 和 tool_calls;endpoint 可以填写基础地址,也可以直接填写完整的 /chat/completions 地址。
填写模型密钥并保存后,在白名单群消息开头 @ 当前机器人再提问,无须命令:
tibo 是否重置?codex 重置了吗?什么时候重置?重置卡发了吗?和全员重置是同一回事吗?
自动分析仅在 Koishi 的 session.stripped.atSelf 为真时触发。未 @、仅 @ 他人或全体成员、仅呼叫机器人昵称时,不调用模型或查询接口,直接交给后续中间件。此限制不影响定时轮询、群通知和本地命令行联调。
处理流程:关键词初筛 → 模型判断是否调用 query_codex_resets → 实时读取重置接口 → 模型依据英文 text 复核事件类型后生成回答。复核在工具调用后的回答阶段完成,仍是两轮模型请求。模型直接输出、没有工具调用时,一律不发送。对于明确询问密码、数据库、游戏等其他对象的重置,提示模型不调用工具;只是提到 Tibo/Codex、普通聊天或翻译任务也应保持静默。语义判断依赖所配置模型,不是精确的规则分类器。
工具无参数,每次问答只接受一次 query_codex_resets({}) 调用,不按上游类型筛选证据。返回值仅包含 checked_at、latest_reset、scheduled_reset、history、history_is_complete、active_watch 和 stats。历史中与当前记录完全相同的证据不重复发送给模型;同 ID 但状态、原文、类型、时间或来源不同的记录仍保留。原文保持完整,本地历史文件不因输出去重而删除记录。判断规则集中在 system prompt,工具描述只说明用途,返回值只携带数据。分类仅作为提示,以英文 text 为证据;指代不清或证据不足时明确表示类型不确定。复核不回写接口分类或改变通知订阅。
预告的 scheduled 标签可能滞后,并不证明尚未发放。AI 会联合当前 scheduled_reset、latest_reset 与本地历史中的英文原文,判断预告与后续执行是否属于同一批次;不同帖子 ID 或上游类型不同都不会直接否定关联。有依据时可判断已兑现或已开始发放;仅时间接近、时间已过或模糊措辞不视为完成证据。预测不代表官方承诺,过期预测会标记 expired。轮询和问答会将观察到的两类记录保存到 data/tibo-reset/history.json,按 ID 和所在字段保存,最多保留最近观察的 100 条;接口移除或替换的记录仍可作为后续判断依据。同一 ID 的预告和执行记录分别保留,重启后继续使用。首次升级前未保存的证据无法自动恢复。本地读写失败时保留原文件,降级使用实时证据并记录日志。问答不能提供完整历史或查询个人账户额度,也不会修改通知去重记录和测试批次。回答包含原帖和 Codex Resets 来源链接。
仅处理白名单群当前消息,不处理私聊、机器人消息、引用或转发内容。候选消息正文会发送到你配置的模型服务,不携带聊天历史;工具结果也会发送给该服务。普通无关键词消息不请求模型。每个用户在同一群同一时间只处理一条问题,插件总并发上限为 4,繁忙时跳过新问题。
无关问题、模型/API 失败或超时不发送占位消息、错误提示或猜测答案。未回复时交给后续 Koishi 中间件,其他插件仍可自行回复。关闭 enableQuery 或清空 aiTextToken 只停用问答,不影响轮询;关闭 enabled 则同时停用整个插件。卸载或重载会取消尚未完成的查询。
HTTP 重试与日志
重置接口(轮询、测试推送、主动问答)和模型接口(判断工具调用、生成回答)均通过统一请求层。HTTP 400 默认额外重试 2 次,即最多请求 3 次,分别等待 1 秒、2 秒。每次重试沿用原地址与请求参数,不切换代理、地区或密钥。持续的参数或服务限制错误仍会失败;401、403、429、5xx、网络错误和解析错误不在这次立即重试范围内。
问答的 aiRequestTimeout 是包含两轮模型、工具查询和重试等待的总时限,超时或卸载会立即取消等待,不再发起新请求。requestTimeout 是重置接口每次尝试的超时。轮询最终失败仍沿用原有定时检查和通知队列策略;模型请求重试不会重复执行查询工具或重复发送群回复。
所有上述 API 尝试都写入 Koishi 的 tibo-reset 日志,包括本地联调脚本。正常请求与成功记录为 info,失败、取消和重试记录为 warn。每条日志以 [api] 开头,包含请求编号、操作、方法、脱敏后的地址、尝试次数和耗时。同一次请求的重试共用 requestId,便于关联:
reset.poll:轮询及测试推送读取接口。reset.query:问答工具读取接口。reset.check:本地check:api检查。model.route:模型判断是否调用工具。model.answer:模型复核原文并回答。
phase 为 request、success、failure、retry 或 cancelled;失败时记录可取得的 HTTP 状态码,重试时记录等待毫秒数。Koishi 的 get/post 只返回解码后的正文,因此成功记录不伪造具体的 HTTP 200 状态码。地区限制等已识别错误提供安全类别(例如 location_not_supported),不原样打印可能包含密钥或聊天内容的错误正文。
日志不输出 Authorization、API key、完整请求/响应正文、群消息或模型思考内容;URL 去掉凭据、查询参数及片段,已知密钥在路径中也会脱敏。不会为记录日志而拦截其他插件或适配器内部的请求。群消息投递继续使用原有队列重试,不增加立即 HTTP 400 重发,避免重复通知。
通知与重试规则
- 首次成功读取接口只建立历史基线,不补发接口内已有预告。建立基线失败时不会发送。
- 默认同时订阅全员重置和重置卡,只通知新 ID 的
scheduled记录;开启notifyConfirmed后,也通知新 ID 的completed记录。 - 仅用接口自带
id去重并持久化。相同 ID 的文案、类型、来源 URL、预计时间或状态变化不会重复通知;同一 ID 同时出现在两个字段时优先采用已执行记录。 - 白名单为空期间仍建立基线并记录事件;后来加群不补发历史。修改类型和确认开关也不补发已记录 ID。
- 重复白名单行会去重。离线机器人、发送异常或未返回消息 ID 时,下一轮重试;成功的群不会因其他群失败而重复收到。
- 待发送队列会持久化,API 暂时不可用时仍尝试发送。移出白名单、关闭对应通知类型后,待发送通知也会取消。
- 状态文件位于 Koishi
baseDir下的data/tibo-reset/state.json,采用临时文件加原子重命名保存。损坏文件不会被自动覆盖,修复前暂停通知。请保留数据目录。 - 同一 Koishi 数据目录只运行一个此插件实例。卸载时取消 HTTP 请求并停止后续发送;正在进行的适配器发送会等待结束。
- 群平台发送与本地保存无法形成同一事务:若发送成功后进程崩溃、保存失败后重启,或适配器接收消息却返回错误,重试可能产生重复通知;不承诺严格恰好一次。
- 预告只是来源方的计划,不表示已到账。插件不根据预计时间自动判定确认。
消息排版
消息头按状态显示 [Tibo / Codex 重置预告] 或 [Tibo / Codex 重置确认]。消息头、英文时间、正文之间用空行分段,正文前后使用分隔线,来源链接独立显示:
[Tibo / Codex 重置预告]
Expected time: Sep 23, 2026, 14:59 (UTC+8)
──── Tibo ────
Ladies and gentlemen... start... your... ENGINES. We are almost Tuesday and I promised a reset for Tuesday.
────────────────
原文:https://x.com/thsottiaux/status/2102254445082116335预告使用 scheduled_for 转换为北京时间,缺失时显示 Expected time: Not specified。重置确认显示 Reset type: regular (Usage reset) 或 Reset type: banked (Reset credit),并将 announced_at 转换为 UTC+8 显示为 Announced at,不伪造实际完成时间。所有消息时间均按 UTC+8 展示,包含跨日转换。正文直接使用接口英文 text,保留原有换行、段落和完整内容,正文前显示 ──── Tibo ────,正文后显示 ────────────────;观察记录使用 ──── Observed ────,不冒充 Tibo 原帖。没有有效来源链接时省略原文链接行。
手动测试推送
- 配置好群白名单并启用插件。
- 在配置页的“测试推送”分组,将
testPushToken填为1,保存配置。 - 插件加载后立即请求接口,选取
announced_at最新的一条事件,向白名单群实际发送一次,消息开头增加独立的[测试通知] 模拟推送,非新动态。段落。 - 再次测试时,将批次改成
2、3等不同的非空值并保存。也可直接修改 YAML:testPushToken: "2"。
测试忽略正式通知的事件类型和确认开关,即使该事件已经通知过或插件首次建立基线,也可以测试。原始事件的内容、时间、状态不做伪造,正式去重记录不会为测试而删除或重置。正常轮询仍同时运行,新事件可能另有一条不带测试标记的正式通知。
测试批次及待发送队列会持久化。同一批次不会因后续轮询、修改其他配置或重启而再次触发;失败的群按轮询间隔重试,已成功的群不重复发送。接口失败、无事件或白名单为空时,批次暂不消耗,条件满足后再触发。清空批次会取消剩余测试重试,改为新批次会替换旧的待重试测试。修改后请保存配置。插件关闭时不测试。测试沿用普通消息发送的事务限制,异常崩溃窗口仍可能重复。
本地验证
npm test
npm run build
npm run check:apicheck:api 只读取真实接口、校验结构,打印一条预告样例及只读查询工具的记录摘要;不连接机器人、不调用模型、不发送群消息、不修改通知状态。
问答自动化测试使用模拟模型响应,覆盖两轮 tool calling、参数校验、静默分支、白名单、并发和取消。npm test 不会使用真实 API key、产生模型费用或发送群消息。模型的语义判断仍需通过真实调用检查。
本地模型联调
本地联调完全独立于 zssm,配置和测试集都放在本项目:
.env:保存TIBO_AI_TEXT_ENDPOINT、TIBO_AI_TEXT_TOKEN、TIBO_AI_TEXT_MODEL。已被 Git 忽略且不进入安装包。.env.example:不含密钥的配置模板。test/local-questions.json:可自行修改、添加或删除问题的测试集。
脚本默认读取项目根目录的 .env,不依赖启动命令时的工作目录,也不会读取其他项目的配置。进程中已有的 TIBO_* 同名变量优先;需要其他本地配置时可以显式指定 --env-file。正式插件仍通过 Koishi 配置页配置模型,这些环境变量仅供本地脚本使用。
本地 .env 也可设置 TIBO_HTTP_400_RETRIES 和 TIBO_HTTP_RETRY_DELAY 来调整重试次数与基础间隔(秒),省略时默认分别为 2 和 1。
# 默认预览,不调用模型或重置接口
npm run test:local -- --suite
# 真实调用指定模型及只读重置接口,测试一个问题
npm run test:local -- --real "tibo 是否重置?"
# 真实运行 test/local-questions.json 中的全部问题
npm run test:local -- --real --suite修改测试集后直接重跑即可。每项的 question 是问题;expectReply: true 表示预期回复,false 表示预期静默;只想观察实际回复时可以省略 expectReply:
[
{ "question": "什么时候重置?", "expectReply": true },
{ "question": "今天吃什么?", "expectReply": false },
{ "question": "我自己的新问题" }
]不传问题也不加 --suite 时运行测试集的第一条。直接在命令末尾指定问题时,不要同时加 --suite。
真实测试需要显式加 --real,会消耗所配置模型的额度,重试也可能产生额外费用。脚本使用插件本身的模型客户端和查询逻辑,不连接机器人,不发送群消息,不创建或修改通知状态。输出问题、工具调用次数、查询次数、耗时和最终回答,并通过 Koishi 日志记录每次 HTTP 尝试,不输出 Authorization 或密钥。重试耗尽或遇到不重试的错误时停止剩余用例,避免连续无效请求。
内置用例包括 Tibo/Codex 是否重置、什么时候重置、重置卡是否发放,以及普通聊天、数据库密码重置、Codex 概念问答和翻译。相关问题应查询后回复,无关问题应不调用工具且不回复。路由 PASS 不代表回答内容绝对正确,仍应检查时间、批次、来源确认与模型措辞是否匹配。
使用 Gemini 兼容接口时,Koishi 的“AI 问答”配置为:
aiTextEndpoint: "https://generativelanguage.googleapis.com/v1beta/openai/"
aiTextToken: "在控制台填写你的密钥"
aiTextModel: "gemini-3.8-flash"思考强度测速
插件和本地模型联调的两轮请求(工具选择、最终回答)均默认发送 reasoning_effort: "low"。
运行 npm run bench:thinking -- --real --repeats 2,使用本项目 .env 和测试集,对比未传 reasoning_effort 的默认模式与 low。脚本先探测 none;若接口接受,也会加入对比,但接受参数不等于已证明关闭思考。会产生真实模型调用费用,不发送群消息、不修改插件默认配置。
测速中的 default 指接口默认行为,会主动移除该参数,不是插件默认的 low。
各组使用相同问题、冻结的接口快照和当前时间,按问题交替运行,减少顺序影响;另行记录 HTTP 重试、失败和思考 token,缺失的 usage 字段记为未知而不是 0。reports/thinking-*.json 保存完整回答和统计,普通闲聊被本地预筛跳过的耗时不计入模型速度。小样本结果不代表长期延迟或全面的回答质量。
安装时 npm audit 报告 Koishi 的 file-type 传递依赖存在中等级别漏洞,关联框架依赖共 10 项。本插件只读取 JSON,不调用该媒体格式解析功能;未强制覆盖 Koishi 的依赖版本,部署方仍需跟进上游修复。
开发时核对了 Koishi 官方的配置构型、计时器及机器人文档:
- https://koishi.chat/guide/plugin/schema
- https://koishi.chat/api/service/timer
- https://koishi.chat/api/core/bot
