Files
DanDingNoneBot/danding_bot/plugins/danding_sign_in/service.py
Mr.Xia 9ee5545cac 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
2026-08-16 09:02:11 +08:00

67 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""独立签到插件 - 签到编排。
先记录签到再发放积分(与抽卡签到 `onmyoji_gacha/sign_in.py` 同模式),
避免重复签到时重复发积分xapi `POST /bot/gacha/sign-in` 负责判断当天
是否已签到(与抽卡/WPF 共用 `bot_gacha_daily_sign_in` 表去重),只有它
确认首次签到后,才调用 `/bot/points/add` 发积分。
"""
from __future__ import annotations
import logging
import random
from typing import Any, Dict, Optional, Protocol
logger = logging.getLogger(__name__)
class SignInRecorder(Protocol):
async def record_sign_in(self, user_id: str, points_awarded: int) -> Optional[Dict[str, Any]]:
"""记录每日签到,返回 xapi data含 signed_already失败返回 None。"""
class PointsAwarder(Protocol):
async def add_points(self, user_id: str, amount: int, source: str, reason: str) -> tuple[bool, int]:
"""发放积分,返回(是否成功, 新余额)。"""
async def perform_daily_sign_in(
*,
sign_in_api: SignInRecorder,
points_api: PointsAwarder,
user_id: str,
points_min: int,
points_max: int,
source: str,
reason: str,
) -> Dict[str, Any]:
"""执行每日签到。
返回 dict
- {"success": True, "already_signed": False, "points": int, "balance": int} 首次签到成功
- {"success": True, "already_signed": True, "points": int, "sign_date": str} 当天已签到
- {"success": False, "message": str} 失败
"""
low, high = sorted((points_min, points_max))
points = random.randint(low, high)
sign_result = await sign_in_api.record_sign_in(user_id, points)
if sign_result is None:
return {"success": False, "message": "签到服务暂不可用,请稍后再试"}
if sign_result.get("signed_already"):
# 今天已签到(抽卡/WPF/命令任一入口先到先得),不重复发积分
return {
"success": True,
"already_signed": True,
"points": int(sign_result.get("points_awarded", 0) or 0),
"sign_date": str(sign_result.get("sign_date", "")),
}
ok, balance = await points_api.add_points(user_id, points, source, reason)
if not ok:
logger.error("签到记录成功但积分发放失败 user_id=%s points=%s", user_id, points)
return {"success": False, "message": "积分发放失败,请稍后再试"}
return {"success": True, "already_signed": False, "points": points, "balance": balance}