# 独立每日签到插件(与抽卡签到共用数据) ## Goal 在 danding-bot 中新增独立「签到」插件:用户发送「签到」命令即可完成每日签到并领取积分;抽卡插件(onmyoji_gacha)的「抽卡后自动签到」保持原功能不变;两个入口与 WPF 客户端共用同一份签到数据(xapi `bot_gacha_daily_sign_in` 表,每用户每天一条,先到先得)。 ## What I already know - 签到数据唯一权威源:xapi DanDing 库 `bot_gacha_daily_sign_in` 表,`UNIQUE(bot_user_id, sign_date)`,WPF 与 QQ Bot 已共用(xapi `docs/bot-api.md` L126、`Application/Bot/gacha_router.py` L203-284、`Application/DanDing/wpf_gacha.py` L183-226)。 - QQ 抽卡签到现状:`onmyoji_gacha` 插件在抽卡/三连抽成功后调用 `POST /bot/gacha/sign-in`(Body: user/token/user_id/points_awarded → data: {success, signed_already, signed_today, sign_date, points_awarded})记录签到,再调用 `/bot/points/add` 发积分(source=`gacha_sign`,reason=`抽卡签到`,随机 1-100 分)。 - WPF 签到:`POST /DD/gacha/sign-in` 在同一事务内写签到记录 + 发 Bot 积分(`BOT_GACHA_WPF_SIGN_IN_POINTS` 默认 20,source=`gacha_sign`,reason=`抽卡签到`)。重复签到返回 `signed_already=true` 且不重复发积分。 - xapi 无 `GET /bot/gacha/sign-in` 状态查询路由(只有内部 helper),但写接口返回 `signed_already`,重复签到可直接提示"今天已签到"。 - 客户端 HTTP 封装已有现成模式:`onmyoji_gacha/data_manager.py`(DataManager._request 模式)与 `danding_points/api.py`(PointsAPI.add_points → POST /bot/points/add)。 - 权限模式:`onmyoji_gacha` 用白名单(ALLOWED_GROUP_ID=621016172 / ALLOWED_USER_ID=1424473282)的 check_permission Rule。 - 本任务不需要改 xapi(复用现有 `/bot/gacha/sign-in` + `/bot/points/add` 即可实现共用数据),不涉及跨端契约新增。 ## Requirements - [x] 新增独立签到插件(目录 `danding_bot/plugins/sign_in/`) - [x] 群聊命令「签到」(别名:每日签到、打卡),白名单权限(ALLOWED_GROUP_ID / ALLOWED_USER_ID,与抽卡一致) - [x] 命令触发签到:调用 xapi `POST /bot/gacha/sign-in` 记录签到,成功后调用 `/bot/points/add` 发放积分 - [x] 积分规则:随机 1-100(与抽卡签到一致) - [x] 积分流水 source=`gacha_sign`、reason=`抽卡签到`(沿用现有标记,与 WPF/抽卡共用事件语义) - [x] 重复签到(signed_already=true):提示"今天已签到",不发积分 - [x] onmyoji_gacha 抽卡签到保持现状,不做任何改动 - [x] 不新增「签到状态」查询命令(保持最小范围;重复签到提示利用响应自带的 sign_date/points_awarded) - [x] 更新 PLUGINS.md / README.md 文档 ## Acceptance Criteria - [x] 发送「签到」:首次签到成功,返回签到成功 + 积分 + 当前余额 - [x] 同一天再次「签到」:提示已签到,不重复发积分 - [x] 抽卡签到与新命令签到互斥(共用 daily_sign_in 去重),先到先得:先抽卡后命令 → 命令提示已签到;先命令后抽卡 → 抽卡不重复发积分(抽卡插件原有逻辑已处理) - [x] 权限:白名单外的群/用户收到无权限提示(Rule 拒绝后命令不触发) - [x] 测试覆盖(10 个 unit tests,mock xapi aiohttp 调用,全部通过) - [x] ruff lint 通过(新文件 0 违规) ## 实现状态(2026-08-16) - 已实现:`danding_bot/plugins/danding_sign_in/`(__init__/config/api/service/rules 5 文件) - 已实现:`tests/test_danding_sign_in_plugin.py`(10 用例全绿) - 已更新文档:PLUGINS.md(新增 #14 + 抽卡章节交叉引用)、README.md(新增 #8) - 测试环境:无 conda bot 环境,用 xapi env(pytest 9.0.2 + aiohttp + pydantic 2.13)跑通;无 nonebot 环境,rules.py 采用延迟导入 nonebot 保证可测 - 代码复用决策:编排逻辑(先记录后发积分)未从 onmyoji_gacha 复制,而是自包含实现同模式 —— 避免 import onmyoji_gacha 包触发其 __init__ matcher/路由注册耦合;若将来出现第 3 个签到入口,再提取共享模块(规则三) - 既有测试套件:72 通过 + 1 失败(test_danding_qqpush_article_render 中文编码问题,与本任务无关,环境性失败) ## Definition of Done (team quality bar) - Tests added/updated (unit/integration where appropriate) - Lint / typecheck / CI green - Docs/notes updated if behavior changes - Rollout/rollback considered if risky ## Technical Approach - 新插件 `danding_bot/plugins/sign_in/`,模块结构仿照现有插件惯例: - `__init__.py`:NoneBot matcher 注册(on_command("签到", aliases={每日签到, 打卡}, priority=5, rule=check_permission()))+ handler - `config.py`:Pydantic 配置(API host/token/白名单/积分区间,复用 onmyoji_gacha 配置命名惯例) - `api.py`:xapi 客户端(封装 `POST /bot/gacha/sign-in`,仿 DataManager._request 模式) - `service.py` 或直接编排:记录签到 → 发积分 → 格式化消息(先记录后发积分,避免重复发积分,复用 `onmyoji_gacha/sign_in.py` 的 award_daily_sign_in_points 模式) - 数据共用:直接调用与抽卡插件相同的 xapi 端点(`/bot/gacha/sign-in` + `/bot/points/add`),天然共用 `bot_gacha_daily_sign_in` 表,无需改动 onmyoji_gacha 插件。 - 可复用现有代码:`points_api`(danding_points/api.py 的 PointsAPI)直接 import;签到编排可直接复用 `onmyoji_gacha/sign_in.py` 的 `award_daily_sign_in_points`(或独立实现同模式)。 - 测试:`tests/test_sign_in_plugin.py`,仿 `tests/test_onmyoji_gacha_http_api.py` 的 FakeSession/FakeResponse 模式 mock aiohttp。 ## Decision (ADR-lite) **Context**: 用户需要独立签到命令,且与抽卡签到共用数据、不破坏抽卡签到。 **Decision**: 新插件复用 xapi 现有 `/bot/gacha/sign-in` + `/bot/points/add` 端点(与抽卡插件同源),数据自然共用;积分 source/reason 沿用 `gacha_sign`/`抽卡签到`;命令仅群聊 + 白名单;积分随机 1-100。 **Consequences**: 零 xapi 改动、零契约新增;抽卡插件零改动(回归风险最低);流水无法区分签到触发入口(可接受,因为每天只有一次签到);「签到状态/连续天数」查询能力暂缺,后续需要时另开任务(契约先行)。 ## Out of Scope - 不改 xapi(无新增端点) - 不做签到排行榜/连续签到统计/签到状态查询命令 - 不迁移签到数据表位置 - 不改 onmyoji_gacha 抽卡签到逻辑 ## Technical Notes - xapi 契约:`C:\Users\14244\source\repos\Danding_Webs\xapi_new_web\docs\bot-api.md` L114-126 - 表结构:`bot_gacha_daily_sign_in`(migrations/DanDing/013_create_bot_data_migration_tables.py L99-109) - 客户端参考:`danding_bot/plugins/onmyoji_gacha/data_manager.py`(_request 模式)、`danding_bot/plugins/onmyoji_gacha/sign_in.py`(先记录后发积分编排)、`danding_bot/plugins/danding_points/api.py`(PointsAPI.add_points) - 测试模式参考:`tests/test_onmyoji_gacha_http_api.py`(FakeSession/FakeResponse mock aiohttp) - 任务创建于 2026-08-16(Trellis 自动加日期前缀)