feat(sign_in): 新增独立每日签到插件,与抽卡签到共用同一份签到数据

- 群聊命令「签到」(别名: 每日签到/打卡),白名单群聊或管理员可用
- 复用 xapi /bot/gacha/sign-in + /bot/points/add,与抽卡/WPF 共用 bot_gacha_daily_sign_in 表
- 积分随机 1-100,流水沿用 gacha_sign/抽卡签到;先到先得,重复签到提示已签到
- 新增 10 个单元测试,ruff 通过;更新 PLUGINS.md / README.md
This commit is contained in:
2026-08-16 09:02:11 +08:00
parent 7f3f744af5
commit 9ee5545cac
12 changed files with 862 additions and 1 deletions

View File

@@ -0,0 +1,84 @@
# 独立每日签到插件(与抽卡签到共用数据)
## 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` 默认 20source=`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 testsmock 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 envpytest 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-16Trellis 自动加日期前缀)