refactor: onmyoji gacha plugin overhaul (gacha-refactor)
This commit is contained in:
19
PLUGINS.md
19
PLUGINS.md
@@ -95,16 +95,29 @@
|
|||||||
|
|
||||||
### 9. onmyoji_gacha - 阴阳师抽卡模拟
|
### 9. onmyoji_gacha - 阴阳师抽卡模拟
|
||||||
|
|
||||||
高度还原的抽卡模拟,包含成就系统。
|
高度还原的抽卡模拟,包含成就系统。采用模块化架构,职责分明。
|
||||||
|
|
||||||
|
- **模块结构**:
|
||||||
|
- `__init__.py`: 路由注册与matcher定义(167行)
|
||||||
|
- `config.py`: Pydantic配置管理
|
||||||
|
- `gacha.py`: 抽卡核心逻辑(GachaSystem类)
|
||||||
|
- `data_manager.py`: SQLite数据持久化(DataManager类)
|
||||||
|
- `rules.py`: 命令匹配规则(check_permission等)
|
||||||
|
- `formatters.py`: 消息格式化(9个格式化函数)
|
||||||
|
- `handlers/`: 命令处理函数(9个handler模块)
|
||||||
|
- `utils.py`: 通用工具函数
|
||||||
|
- `api_utils.py`: 积分系统API交互
|
||||||
|
- `web_api.py`: HTTP API路由
|
||||||
|
|
||||||
- **主要命令**:
|
- **主要命令**:
|
||||||
- `抽卡`: 执行单抽。
|
- `抽卡`: 执行单抽。
|
||||||
- `三连抽`: 执行三连抽。
|
- `三连抽`: 执行三连抽。
|
||||||
- `我的抽卡`: 查看个人统计。
|
- `我的抽卡`: 查看个人统计。
|
||||||
|
- `抽卡排行`: 查看抽卡排行榜。
|
||||||
|
- `今日抽卡`: 查看今日抽卡统计。
|
||||||
- `查询成就`: 查看已解锁成就及进度(非酋、勤勤恳恳系列)。
|
- `查询成就`: 查看已解锁成就及进度(非酋、勤勤恳恳系列)。
|
||||||
- `抽卡介绍`: 查看详细机制与奖励说明。
|
- `抽卡介绍`: 查看详细机制与奖励说明。
|
||||||
- **特性**: 抽中 SSR/SP 可获得“蛋定助手”卡密奖励(需联系管理员领取)。包含每日抽卡签到积分奖励。
|
- **特性**: 抽中 SSR/SP 可获得"蛋定助手"卡密奖励(需联系管理员领取)。包含每日抽卡签到积分奖励。成就系统自动发放积分奖励。
|
||||||
|
|
||||||
### 10. damo_balance - 大漠账户查询
|
### 10. damo_balance - 大漠账户查询
|
||||||
|
|
||||||
查询大漠平台账户余额。
|
查询大漠平台账户余额。
|
||||||
|
|||||||
@@ -1,59 +1,91 @@
|
|||||||
|
"""
|
||||||
|
阴阳师抽卡插件 - NoneBot2插件
|
||||||
|
|
||||||
|
提供阴阳师主题的抽卡功能,包括:
|
||||||
|
- 单次抽卡和三连抽
|
||||||
|
- 用户统计和排行榜
|
||||||
|
- 成就系统
|
||||||
|
- SSR/SP奖励发放
|
||||||
|
- 每日签到
|
||||||
|
|
||||||
|
模块结构:
|
||||||
|
- config.py: 配置管理
|
||||||
|
- gacha.py: 抽卡核心逻辑
|
||||||
|
- utils.py: 工具函数
|
||||||
|
- rules.py: 匹配规则
|
||||||
|
- formatters.py: 消息格式化
|
||||||
|
- handlers/: 命令处理器
|
||||||
|
- api_utils.py: 外部API调用
|
||||||
|
- web_api.py: Web接口
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from nonebot import on_command, on_startswith
|
from nonebot import on_command, on_startswith
|
||||||
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, Message
|
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, Message
|
||||||
from nonebot.adapters.onebot.v11.message import MessageSegment
|
from nonebot.adapters.onebot.v11.message import MessageSegment
|
||||||
from nonebot.typing import T_State
|
from nonebot.typing import T_State
|
||||||
from nonebot.rule import Rule
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .gacha import GachaSystem
|
from .gacha import GachaSystem
|
||||||
from .utils import format_sign_in_message, format_user_mention, get_image_path
|
from .rules import check_permission, check_rank_permission
|
||||||
from .api_utils import process_ssr_sp_reward, process_achievement_reward
|
from .utils import format_user_mention, get_image_path, format_sign_in_message
|
||||||
from . import web_api
|
from .api_utils import process_ssr_sp_reward, process_achievement_reward, get_points_api
|
||||||
from danding_bot.plugins.danding_points import points_api
|
from . import formatters
|
||||||
|
from . import handlers
|
||||||
|
|
||||||
# 创建Config实例
|
# 初始化配置
|
||||||
config = Config()
|
config = Config()
|
||||||
|
|
||||||
# 允许的群聊ID和用户ID
|
|
||||||
ALLOWED_GROUP_ID = config.ALLOWED_GROUP_ID
|
|
||||||
ALLOWED_USER_ID = config.ALLOWED_USER_ID
|
|
||||||
GACHA_COMMANDS = config.GACHA_COMMANDS
|
|
||||||
STATS_COMMANDS = config.STATS_COMMANDS
|
|
||||||
DAILY_STATS_COMMANDS = config.DAILY_STATS_COMMANDS
|
|
||||||
TRIPLE_GACHA_COMMANDS = config.TRIPLE_GACHA_COMMANDS
|
|
||||||
ACHIEVEMENT_COMMANDS = config.ACHIEVEMENT_COMMANDS
|
|
||||||
INTRO_COMMANDS = config.INTRO_COMMANDS
|
|
||||||
DAILY_LIMIT = config.DAILY_LIMIT
|
|
||||||
|
|
||||||
gacha_system = GachaSystem()
|
gacha_system = GachaSystem()
|
||||||
|
points_api = get_points_api()
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
SIGN_IN_MIN_POINTS = 1
|
|
||||||
SIGN_IN_MAX_POINTS = 100
|
|
||||||
SIGN_IN_SOURCE = "gacha_sign"
|
|
||||||
SIGN_IN_REASON = "抽卡签到"
|
|
||||||
|
|
||||||
# 检查是否允许使用功能的规则
|
# 签到积分配置
|
||||||
def check_permission() -> Rule:
|
SIGN_IN_MIN_POINTS = 10
|
||||||
async def _checker(event: MessageEvent) -> bool:
|
SIGN_IN_MAX_POINTS = 50
|
||||||
# 允许特定用户在任何场景下使用
|
SIGN_IN_SOURCE = "gacha"
|
||||||
if event.user_id == ALLOWED_USER_ID:
|
SIGN_IN_REASON = "每日抽卡签到"
|
||||||
return True
|
|
||||||
|
|
||||||
# 在允许的群聊中任何人都可以使用
|
# 命令别名配置
|
||||||
if isinstance(event, GroupMessageEvent) and event.group_id == ALLOWED_GROUP_ID:
|
GACHA_COMMANDS = {"抽卡", "阴阳师抽卡", "十连抽"}
|
||||||
return True
|
STATS_COMMANDS = {"我的抽卡统计", "抽卡统计"}
|
||||||
|
DAILY_STATS_COMMANDS = {"今日抽卡", "今日抽卡统计"}
|
||||||
|
TRIPLE_GACHA_COMMANDS = {"三连抽", "三次抽"}
|
||||||
|
ACHIEVEMENT_COMMANDS = {"查询成就", "抽卡成就", "成就"}
|
||||||
|
INTRO_COMMANDS = {"抽卡介绍", "抽卡帮助"}
|
||||||
|
|
||||||
return False
|
# 定义匹配器
|
||||||
|
gacha_matcher = on_command("抽卡", aliases=set(GACHA_COMMANDS), priority=10, rule=check_permission())
|
||||||
return Rule(_checker)
|
stats_matcher = on_command("我的抽卡", aliases=set(STATS_COMMANDS), priority=5, rule=check_permission())
|
||||||
|
daily_stats_matcher = on_command("今日抽卡", aliases=set(DAILY_STATS_COMMANDS), priority=5, rule=check_permission())
|
||||||
|
triple_gacha_matcher = on_command("三连抽", aliases=set(TRIPLE_GACHA_COMMANDS), priority=5, rule=check_permission())
|
||||||
|
achievement_matcher = on_command("查询成就", aliases=set(ACHIEVEMENT_COMMANDS), priority=5, rule=check_permission())
|
||||||
|
query_matcher = on_command("查询抽卡", aliases={"查询抽奖"}, priority=5, rule=check_permission())
|
||||||
|
rank_matcher = on_startswith(("抽卡排行", "抽卡榜"), priority=1, rule=check_rank_permission())
|
||||||
|
intro_matcher = on_command("抽卡介绍", aliases=set(INTRO_COMMANDS), priority=5, rule=check_permission())
|
||||||
|
|
||||||
|
|
||||||
async def try_handle_daily_sign_in(matcher, user_id: str, user_name: str) -> None:
|
async def try_handle_daily_sign_in(matcher, user_id: str, user_name: str) -> None:
|
||||||
"""处理抽卡成功后的每日签到,不影响主流程"""
|
"""
|
||||||
|
处理抽卡成功后的每日签到,不影响主流程。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
matcher: NoneBot匹配器实例,用于发送消息
|
||||||
|
user_id: 用户ID
|
||||||
|
user_name: 用户昵称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
|
||||||
|
Side Effects:
|
||||||
|
- 检查用户今日是否已签到
|
||||||
|
- 如未签到,随机发放积分奖励
|
||||||
|
- 记录签到状态
|
||||||
|
- 发送签到通知消息
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
if gacha_system.data_manager.has_signed_in_today(user_id):
|
if gacha_system.data_manager.has_signed_in_today(user_id):
|
||||||
return
|
return
|
||||||
@@ -77,724 +109,63 @@ async def try_handle_daily_sign_in(matcher, user_id: str, user_name: str) -> Non
|
|||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("处理抽卡签到失败 user_id=%s", user_id)
|
logger.exception("处理抽卡签到失败 user_id=%s", user_id)
|
||||||
|
|
||||||
# 注册抽卡命令,添加权限检查规则
|
|
||||||
gacha_matcher = on_command("抽卡", aliases=set(GACHA_COMMANDS), priority=10, rule=check_permission())
|
|
||||||
|
|
||||||
|
# 注册命令处理器
|
||||||
@gacha_matcher.handle()
|
@gacha_matcher.handle()
|
||||||
async def handle_gacha(bot: Bot, event: MessageEvent, state: T_State):
|
async def handle_gacha_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
|
"""单次抽卡命令处理器"""
|
||||||
|
await handlers.handle_gacha(bot, event, state)
|
||||||
|
# 签到处理:逻辑从handlers/gacha.py移至matcher层(遵循职责边界:matcher层负责编排,handler层负责业务)
|
||||||
user_id = str(event.user_id)
|
user_id = str(event.user_id)
|
||||||
user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
|
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
||||||
|
|
||||||
# 执行抽卡
|
|
||||||
result = gacha_system.draw(user_id)
|
|
||||||
|
|
||||||
if not result["success"]:
|
|
||||||
await gacha_matcher.finish(format_user_mention(user_id, user_name) + " ❌ " + result["message"])
|
|
||||||
|
|
||||||
# 成功抽卡,格式化消息
|
|
||||||
rarity = result["rarity"]
|
|
||||||
name = result["name"]
|
|
||||||
image_url = result["image_url"]
|
|
||||||
draws_left = result["draws_left"]
|
|
||||||
unlocked_achievements = result.get("unlocked_achievements", [])
|
|
||||||
|
|
||||||
# 构建消息
|
|
||||||
msg = Message()
|
|
||||||
|
|
||||||
# 根据稀有度设置不同的消息样式
|
|
||||||
if rarity == "SSR":
|
|
||||||
msg.append(f"🌟✨ 恭喜 {format_user_mention(user_id, user_name)} ✨🌟\n")
|
|
||||||
msg.append(f"🎊 抽到了 SSR 式神:{name} 🎊\n")
|
|
||||||
msg.append(f"💫 真是太幸运了!💫")
|
|
||||||
elif rarity == "SP":
|
|
||||||
msg.append(f"🌈🎆 恭喜 {format_user_mention(user_id, user_name)} 🎆🌈\n")
|
|
||||||
msg.append(f"🎉 抽到了 SP 式神:{name} 🎉\n")
|
|
||||||
msg.append(f"🔥 这是传说中的SP!🔥")
|
|
||||||
elif rarity == "SR":
|
|
||||||
msg.append(f"⭐ 恭喜 {format_user_mention(user_id, user_name)} ⭐\n")
|
|
||||||
msg.append(f"✨ 抽到了 SR 式神:{name} ✨")
|
|
||||||
else: # R
|
|
||||||
msg.append(f"🍀 {format_user_mention(user_id, user_name)} 🍀\n")
|
|
||||||
msg.append(f"📜 抽到了 R 式神:{name}")
|
|
||||||
|
|
||||||
# 添加图片
|
|
||||||
if image_url and os.path.exists(image_url):
|
|
||||||
msg.append(MessageSegment.image(f"file:///{get_image_path(image_url)}"))
|
|
||||||
|
|
||||||
# 添加成就通知
|
|
||||||
if unlocked_achievements:
|
|
||||||
msg.append("\n\n🏆 恭喜解锁新成就!\n")
|
|
||||||
has_manual_rewards = False
|
|
||||||
|
|
||||||
for achievement_id in unlocked_achievements:
|
|
||||||
# 尝试自动发放成就奖励
|
|
||||||
auto_success, reward_msg = await process_achievement_reward(user_id, achievement_id)
|
|
||||||
|
|
||||||
# 检查是否是重复奖励
|
|
||||||
if "_repeat_" in achievement_id:
|
|
||||||
base_achievement_id = achievement_id.split("_repeat_")[0]
|
|
||||||
achievement_config = config.ACHIEVEMENTS.get(base_achievement_id)
|
|
||||||
if achievement_config:
|
|
||||||
achievement_name = achievement_config["name"]
|
|
||||||
# 使用重复奖励或默认为天卡
|
|
||||||
reward = achievement_config.get("repeat_reward", "天卡")
|
|
||||||
status = "✅ 已自动发放" if auto_success else "⚠️ 需手动领取"
|
|
||||||
msg.append(f"🎖️ {achievement_name} 重复奖励 (奖励:{reward}) {status}\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"🎖️ {achievement_id}\n")
|
|
||||||
else:
|
|
||||||
achievement_config = config.ACHIEVEMENTS.get(achievement_id)
|
|
||||||
if achievement_config:
|
|
||||||
achievement_name = achievement_config["name"]
|
|
||||||
reward = achievement_config["reward"]
|
|
||||||
status = "✅ 已自动发放" if auto_success else "⚠️ 需手动领取"
|
|
||||||
msg.append(f"🎖️ {achievement_name} (奖励:{reward}) {status}\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"🎖️ {achievement_id}\n")
|
|
||||||
|
|
||||||
# 记录是否有需要手动领取的奖励
|
|
||||||
if not auto_success:
|
|
||||||
has_manual_rewards = True
|
|
||||||
|
|
||||||
# 如果有未自动发放的奖励,提示联系管理员
|
|
||||||
if has_manual_rewards:
|
|
||||||
msg.append("💰 未自动发放的奖励请联系管理员\n")
|
|
||||||
|
|
||||||
# 添加成就进度提示
|
|
||||||
achievement_data = gacha_system.get_user_achievements(user_id)
|
|
||||||
if achievement_data["success"]:
|
|
||||||
progress = achievement_data["progress"]
|
|
||||||
consecutive_days = progress.get("consecutive_days", 0)
|
|
||||||
no_ssr_streak = progress.get("no_ssr_streak", 0)
|
|
||||||
|
|
||||||
msg.append("\n📈 成就进度:\n")
|
|
||||||
|
|
||||||
# 连续抽卡天数进度
|
|
||||||
if consecutive_days > 0:
|
|
||||||
if consecutive_days < 30:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅰ:{consecutive_days}/30天 🎯\n")
|
|
||||||
elif consecutive_days < 60:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅱ:{consecutive_days}/60天 🎯\n")
|
|
||||||
elif consecutive_days < 90:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅲ:{consecutive_days}/90天 🎯\n")
|
|
||||||
elif consecutive_days < 120:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅳ:{consecutive_days}/120天 ⭐\n")
|
|
||||||
elif consecutive_days < 150:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ:{consecutive_days}/150天 ⭐\n")
|
|
||||||
else:
|
|
||||||
next_reward_days = 30 - (consecutive_days % 30)
|
|
||||||
if next_reward_days == 30:
|
|
||||||
next_reward_days = 0
|
|
||||||
if next_reward_days > 0:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ (满级):{consecutive_days}天,距离下次奖励{next_reward_days}天 🎁\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ (满级):{consecutive_days}天,可获得奖励!🎉\n")
|
|
||||||
|
|
||||||
# 无SSR/SP连击进度
|
|
||||||
if no_ssr_streak > 0:
|
|
||||||
if no_ssr_streak < 60:
|
|
||||||
msg.append(f"💔 非酋进度:{no_ssr_streak}/60次 😭\n")
|
|
||||||
elif no_ssr_streak < 120:
|
|
||||||
msg.append(f"💔 顶级非酋:{no_ssr_streak}/120次 😱\n")
|
|
||||||
elif no_ssr_streak < 180:
|
|
||||||
msg.append(f"💔 月见黑:{no_ssr_streak}/180次 🌙\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"💔 已达月见黑级别:{no_ssr_streak}次 🌚\n")
|
|
||||||
|
|
||||||
# 添加剩余次数和概率信息
|
|
||||||
msg.append(f"\n📊 今日剩余抽卡次数:{draws_left}/{DAILY_LIMIT}\n")
|
|
||||||
msg.append(gacha_system.get_probability_text())
|
|
||||||
|
|
||||||
# 如果抽到了SSR或SP,处理奖励发放
|
|
||||||
if rarity in ["SSR", "SP"]:
|
|
||||||
# 尝试自动发放奖励
|
|
||||||
auto_success, reward_msg = await process_ssr_sp_reward(user_id)
|
|
||||||
msg.append(f"\n\n{reward_msg}")
|
|
||||||
|
|
||||||
# 通知管理员好友
|
|
||||||
admin_id = 2185330092
|
|
||||||
notify_msg = Message()
|
|
||||||
if auto_success:
|
|
||||||
notify_msg.append(f"用户 {user_name}({user_id}) 抽中了{rarity}稀有度式神: {name}! 已自动发放奖励!")
|
|
||||||
else:
|
|
||||||
notify_msg.append(f"用户 {user_name}({user_id}) 抽中了{rarity}稀有度式神: {name}! 需要手动发放奖励!")
|
|
||||||
await bot.send_private_msg(user_id=admin_id, message=notify_msg)
|
|
||||||
else:
|
|
||||||
msg.append(f"\n\n抽中SSR或SP时,可获得蛋定助手天卡一张哦~~")
|
|
||||||
|
|
||||||
await gacha_matcher.send(msg)
|
|
||||||
await try_handle_daily_sign_in(gacha_matcher, user_id, user_name)
|
await try_handle_daily_sign_in(gacha_matcher, user_id, user_name)
|
||||||
return
|
|
||||||
|
|
||||||
async def notify_admin(bot: Bot, message: str):
|
|
||||||
"""通知管理员"""
|
|
||||||
admin_id = 2185330092
|
|
||||||
try:
|
|
||||||
await bot.send_private_msg(user_id=admin_id, message=message)
|
|
||||||
except Exception as e:
|
|
||||||
pass # 忽略通知失败的错误
|
|
||||||
|
|
||||||
# 注册查询命令,添加权限检查规则
|
|
||||||
stats_matcher = on_command("我的抽卡", aliases=set(STATS_COMMANDS), priority=5, rule=check_permission())
|
|
||||||
|
|
||||||
# 注册今日统计命令
|
|
||||||
daily_stats_matcher = on_command("今日抽卡", aliases=set(DAILY_STATS_COMMANDS), priority=5, rule=check_permission())
|
|
||||||
|
|
||||||
# 注册三连抽命令
|
|
||||||
triple_gacha_matcher = on_command("三连抽", aliases=set(TRIPLE_GACHA_COMMANDS), priority=5, rule=check_permission())
|
|
||||||
|
|
||||||
# 注册成就查询命令
|
|
||||||
achievement_matcher = on_command("查询成就", aliases=set(ACHIEVEMENT_COMMANDS), priority=5, rule=check_permission())
|
|
||||||
|
|
||||||
@stats_matcher.handle()
|
|
||||||
async def handle_stats(bot: Bot, event: MessageEvent, state: T_State):
|
|
||||||
user_id = str(event.user_id)
|
|
||||||
user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
|
|
||||||
|
|
||||||
# 获取用户统计
|
|
||||||
stats = gacha_system.get_user_stats(user_id)
|
|
||||||
|
|
||||||
if not stats["success"]:
|
|
||||||
await stats_matcher.finish(format_user_mention(user_id, user_name) + " " + stats["message"])
|
|
||||||
|
|
||||||
# 构建消息
|
|
||||||
msg = Message()
|
|
||||||
msg.append(f"📊 {format_user_mention(user_id, user_name)} 的抽卡统计:\n\n")
|
|
||||||
msg.append(f"🎲 总抽卡次数:{stats['total_draws']}次\n\n")
|
|
||||||
|
|
||||||
# 稀有度统计
|
|
||||||
msg.append("🎯 稀有度分布:\n")
|
|
||||||
msg.append(f"📜 R:{stats['R_count']}张 ({stats['R_count']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
msg.append(f"⭐ SR:{stats['SR_count']}张 ({stats['SR_count']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
msg.append(f"🌟 SSR:{stats['SSR_count']}张 ({stats['SSR_count']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
msg.append(f"🌈 SP:{stats['SP_count']}张 ({stats['SP_count']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
|
|
||||||
# 添加最近抽卡记录
|
|
||||||
if stats["recent_draws"]:
|
|
||||||
msg.append("\n🕐 最近抽卡记录:\n")
|
|
||||||
for draw in reversed(stats["recent_draws"]):
|
|
||||||
# 根据稀有度添加emoji
|
|
||||||
if draw['rarity'] == "SSR":
|
|
||||||
emoji = "🌟"
|
|
||||||
elif draw['rarity'] == "SP":
|
|
||||||
emoji = "🌈"
|
|
||||||
elif draw['rarity'] == "SR":
|
|
||||||
emoji = "⭐"
|
|
||||||
else:
|
|
||||||
emoji = "📜"
|
|
||||||
|
|
||||||
msg.append(f"{emoji} {draw['rarity']} - {draw['name']} ({draw['date']})\n")
|
|
||||||
|
|
||||||
await stats_matcher.finish(msg)
|
|
||||||
|
|
||||||
@triple_gacha_matcher.handle()
|
@triple_gacha_matcher.handle()
|
||||||
async def handle_triple_gacha(bot: Bot, event: MessageEvent, state: T_State):
|
async def handle_triple_gacha_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
"""处理三连抽命令"""
|
"""三连抽命令处理器"""
|
||||||
user_id = str(event.user_id)
|
await handlers.handle_triple_gacha(bot, event, state)
|
||||||
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
|
||||||
|
|
||||||
# 执行三连抽
|
|
||||||
result = gacha_system.triple_draw(user_id)
|
|
||||||
|
|
||||||
if not result["success"]:
|
@stats_matcher.handle()
|
||||||
await triple_gacha_matcher.finish(f"❌ {result['message']}")
|
async def handle_stats_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
|
"""个人统计查询命令处理器"""
|
||||||
|
await handlers.handle_stats(bot, event, state)
|
||||||
|
|
||||||
# 构建三连抽结果消息
|
|
||||||
msg = Message()
|
|
||||||
msg.append(f"🎯 {format_user_mention(user_id, user_name)} 的三连抽结果:\n\n")
|
|
||||||
|
|
||||||
# 显示每次抽卡结果
|
|
||||||
for i, draw_result in enumerate(result["results"], 1):
|
|
||||||
rarity = draw_result["rarity"]
|
|
||||||
name = draw_result["name"]
|
|
||||||
|
|
||||||
# 根据稀有度添加emoji
|
|
||||||
if rarity == "SSR":
|
|
||||||
msg.append(f"🌟 第{i}抽:SSR - {name}\n")
|
|
||||||
elif rarity == "SP":
|
|
||||||
msg.append(f"🌈 第{i}抽:SP - {name}\n")
|
|
||||||
elif rarity == "SR":
|
|
||||||
msg.append(f"⭐ 第{i}抽:SR - {name}\n")
|
|
||||||
else: # R
|
|
||||||
msg.append(f"📜 第{i}抽:R - {name}\n")
|
|
||||||
|
|
||||||
# 统计结果
|
|
||||||
ssr_count = sum(1 for r in result["results"] if r["rarity"] in ["SSR", "SP"])
|
|
||||||
sr_count = sum(1 for r in result["results"] if r["rarity"] == "SR")
|
|
||||||
r_count = sum(1 for r in result["results"] if r["rarity"] == "R")
|
|
||||||
|
|
||||||
msg.append(f"\n📈 本次三连抽统计:\n")
|
|
||||||
if ssr_count > 0:
|
|
||||||
msg.append(f"🎊 SSR/SP:{ssr_count}张\n")
|
|
||||||
if sr_count > 0:
|
|
||||||
msg.append(f"✨ SR:{sr_count}张\n")
|
|
||||||
if r_count > 0:
|
|
||||||
msg.append(f"📜 R:{r_count}张\n")
|
|
||||||
|
|
||||||
# 添加成就通知
|
|
||||||
unlocked_achievements = result.get("unlocked_achievements", [])
|
|
||||||
if unlocked_achievements:
|
|
||||||
msg.append("\n🏆 恭喜解锁新成就!\n")
|
|
||||||
has_manual_rewards = False
|
|
||||||
|
|
||||||
for achievement_id in unlocked_achievements:
|
|
||||||
# 尝试自动发放成就奖励
|
|
||||||
auto_success, reward_msg = await process_achievement_reward(user_id, achievement_id)
|
|
||||||
|
|
||||||
# 检查是否是重复奖励
|
|
||||||
if "_repeat_" in achievement_id:
|
|
||||||
base_achievement_id = achievement_id.split("_repeat_")[0]
|
|
||||||
achievement_config = config.ACHIEVEMENTS.get(base_achievement_id)
|
|
||||||
if achievement_config:
|
|
||||||
achievement_name = achievement_config["name"]
|
|
||||||
# 使用重复奖励或默认为天卡
|
|
||||||
reward = achievement_config.get("repeat_reward", "天卡")
|
|
||||||
status = "✅ 已自动发放" if auto_success else "⚠️ 需手动领取"
|
|
||||||
msg.append(f"🎖️ {achievement_name} 重复奖励 (奖励:{reward}) {status}\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"🎖️ {achievement_id}\n")
|
|
||||||
else:
|
|
||||||
achievement_config = config.ACHIEVEMENTS.get(achievement_id)
|
|
||||||
if achievement_config:
|
|
||||||
achievement_name = achievement_config["name"]
|
|
||||||
reward = achievement_config["reward"]
|
|
||||||
status = "✅ 已自动发放" if auto_success else "⚠️ 需手动领取"
|
|
||||||
msg.append(f"🎖️ {achievement_name} (奖励:{reward}) {status}\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"🎖️ {achievement_id}\n")
|
|
||||||
|
|
||||||
# 记录是否有需要手动领取的奖励
|
|
||||||
if not auto_success:
|
|
||||||
has_manual_rewards = True
|
|
||||||
|
|
||||||
# 如果有未自动发放的奖励,提示联系管理员
|
|
||||||
if has_manual_rewards:
|
|
||||||
msg.append("💰 未自动发放的奖励请联系管理员\n")
|
|
||||||
|
|
||||||
# 添加成就进度提示
|
|
||||||
achievement_data = gacha_system.get_user_achievements(user_id)
|
|
||||||
if achievement_data["success"]:
|
|
||||||
progress = achievement_data["progress"]
|
|
||||||
consecutive_days = progress.get("consecutive_days", 0)
|
|
||||||
no_ssr_streak = progress.get("no_ssr_streak", 0)
|
|
||||||
|
|
||||||
msg.append("\n📈 成就进度:\n")
|
|
||||||
|
|
||||||
# 连续抽卡天数进度
|
|
||||||
if consecutive_days > 0:
|
|
||||||
if consecutive_days < 30:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅰ:{consecutive_days}/30天 🎯\n")
|
|
||||||
elif consecutive_days < 60:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅱ:{consecutive_days}/60天 🎯\n")
|
|
||||||
elif consecutive_days < 90:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅲ:{consecutive_days}/90天 🎯\n")
|
|
||||||
elif consecutive_days < 120:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅳ:{consecutive_days}/120天 ⭐\n")
|
|
||||||
elif consecutive_days < 150:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ:{consecutive_days}/150天 ⭐\n")
|
|
||||||
else:
|
|
||||||
next_reward_days = 30 - (consecutive_days % 30)
|
|
||||||
if next_reward_days == 30:
|
|
||||||
next_reward_days = 0
|
|
||||||
if next_reward_days > 0:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ (满级):{consecutive_days}天,距离下次奖励{next_reward_days}天 🎁\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ (满级):{consecutive_days}天,可获得奖励!🎉\n")
|
|
||||||
|
|
||||||
# 无SSR/SP连击进度
|
|
||||||
if no_ssr_streak > 0:
|
|
||||||
if no_ssr_streak < 60:
|
|
||||||
msg.append(f"💔 非酋进度:{no_ssr_streak}/60次 😭\n")
|
|
||||||
elif no_ssr_streak < 120:
|
|
||||||
msg.append(f"💔 顶级非酋:{no_ssr_streak}/120次 😱\n")
|
|
||||||
elif no_ssr_streak < 180:
|
|
||||||
msg.append(f"💔 月见黑:{no_ssr_streak}/180次 🌙\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"💔 已达月见黑级别:{no_ssr_streak}次 🌚\n")
|
|
||||||
|
|
||||||
# 添加剩余次数
|
|
||||||
draws_left = result["draws_left"]
|
|
||||||
msg.append(f"\n📊 今日剩余抽卡次数:{draws_left}/{DAILY_LIMIT}")
|
|
||||||
|
|
||||||
# 如果抽到SSR/SP,处理奖励发放
|
|
||||||
if ssr_count > 0:
|
|
||||||
# 为每张SSR/SP处理奖励
|
|
||||||
auto_success, reward_msg = await process_ssr_sp_reward(user_id, ssr_count)
|
|
||||||
|
|
||||||
msg.append(f"\n\n{reward_msg}")
|
|
||||||
|
|
||||||
# 通知管理员
|
|
||||||
admin_msg = f"🎉 用户 {user_name}({user_id}) 在三连抽中抽到了 {ssr_count} 张 SSR/SP!"
|
|
||||||
if auto_success:
|
|
||||||
admin_msg += f" 已自动发放 {ssr_count} 张奖励!"
|
|
||||||
else:
|
|
||||||
admin_msg += f" 需要手动发放 {ssr_count} 张奖励!"
|
|
||||||
await notify_admin(bot, admin_msg)
|
|
||||||
|
|
||||||
await triple_gacha_matcher.send(msg)
|
|
||||||
await try_handle_daily_sign_in(triple_gacha_matcher, user_id, user_name)
|
|
||||||
return
|
|
||||||
|
|
||||||
@achievement_matcher.handle()
|
|
||||||
async def handle_achievement(bot: Bot, event: MessageEvent, state: T_State):
|
|
||||||
"""处理成就查询命令"""
|
|
||||||
user_id = str(event.user_id)
|
|
||||||
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
|
||||||
|
|
||||||
# 获取用户成就信息
|
|
||||||
result = gacha_system.get_user_achievements(user_id)
|
|
||||||
|
|
||||||
if not result["success"]:
|
|
||||||
await achievement_matcher.finish(f"❌ {result['message']}")
|
|
||||||
|
|
||||||
# 构建成就消息
|
|
||||||
msg = Message()
|
|
||||||
msg.append(f"🏆 {format_user_mention(user_id, user_name)} 的成就信息:\n\n")
|
|
||||||
|
|
||||||
# 显示已解锁成就
|
|
||||||
unlocked = result["achievements"]
|
|
||||||
if unlocked:
|
|
||||||
msg.append("🎖️ 已解锁成就:\n")
|
|
||||||
for achievement in unlocked:
|
|
||||||
# 检查是否是重复奖励
|
|
||||||
if "_repeat_" in achievement:
|
|
||||||
base_achievement_id = achievement.split("_repeat_")[0]
|
|
||||||
achievement_config = config.ACHIEVEMENTS.get(base_achievement_id)
|
|
||||||
if achievement_config:
|
|
||||||
achievement_name = achievement_config["name"]
|
|
||||||
reward = achievement_config.get("repeat_reward", "天卡")
|
|
||||||
msg.append(f"✅ {achievement_name} 重复奖励 (奖励:{reward})\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"✅ {achievement}\n")
|
|
||||||
else:
|
|
||||||
achievement_config = config.ACHIEVEMENTS.get(achievement)
|
|
||||||
if achievement_config:
|
|
||||||
achievement_name = achievement_config["name"]
|
|
||||||
reward = achievement_config["reward"]
|
|
||||||
msg.append(f"✅ {achievement_name} (奖励:{reward})\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"✅ {achievement}\n")
|
|
||||||
msg.append("\n💰 获取奖励请联系管理员\n\n")
|
|
||||||
|
|
||||||
# 显示成就进度
|
|
||||||
progress = result["progress"]
|
|
||||||
msg.append("📊 成就进度:\n")
|
|
||||||
|
|
||||||
# 连续抽卡天数 - 勤勤恳恳系列成就
|
|
||||||
consecutive_days = progress.get("consecutive_days", 0)
|
|
||||||
if consecutive_days > 0:
|
|
||||||
# 判断当前应该显示哪个等级的进度
|
|
||||||
if consecutive_days < 30:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅰ:{consecutive_days}/30 天 (奖励:天卡)\n")
|
|
||||||
elif consecutive_days < 60:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅱ:{consecutive_days}/60 天 (奖励:天卡)\n")
|
|
||||||
elif consecutive_days < 90:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅲ:{consecutive_days}/90 天 (奖励:天卡)\n")
|
|
||||||
elif consecutive_days < 120:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅳ:{consecutive_days}/120 天 (奖励:周卡)\n")
|
|
||||||
elif consecutive_days < 150:
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ:{consecutive_days}/150 天 (奖励:周卡)\n")
|
|
||||||
else:
|
|
||||||
# 已达到最高等级,显示下次奖励进度
|
|
||||||
next_reward_days = 30 - (consecutive_days % 30)
|
|
||||||
if next_reward_days == 30:
|
|
||||||
next_reward_days = 0
|
|
||||||
msg.append(f"📅 勤勤恳恳Ⅴ (已满级):{consecutive_days} 天\n")
|
|
||||||
if next_reward_days > 0:
|
|
||||||
msg.append(f"🎁 距离下次奖励:{next_reward_days} 天 (奖励:天卡)\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"🎁 可获得奖励!请联系管理员 (奖励:天卡)\n")
|
|
||||||
|
|
||||||
# 无SSR/SP连击数
|
|
||||||
no_ssr_streak = progress.get("no_ssr_streak", 0)
|
|
||||||
if no_ssr_streak > 0:
|
|
||||||
msg.append(f"💔 无SSR/SP连击:{no_ssr_streak} 次\n")
|
|
||||||
|
|
||||||
# 显示各个非酋成就的进度
|
|
||||||
if no_ssr_streak < 60:
|
|
||||||
msg.append(f" 🎯 非酋成就:{no_ssr_streak}/60 (奖励:天卡)\n")
|
|
||||||
elif no_ssr_streak < 120:
|
|
||||||
msg.append(f" 🎯 顶级非酋成就:{no_ssr_streak}/120 (奖励:周卡)\n")
|
|
||||||
elif no_ssr_streak < 180:
|
|
||||||
msg.append(f" 🎯 月见黑成就:{no_ssr_streak}/180 (奖励:月卡)\n")
|
|
||||||
else:
|
|
||||||
msg.append(f" 🌙 已达到月见黑级别!\n")
|
|
||||||
|
|
||||||
# 如果没有任何进度,显示提示
|
|
||||||
if consecutive_days == 0 and no_ssr_streak == 0:
|
|
||||||
msg.append("🌱 还没有任何成就进度,快去抽卡吧!")
|
|
||||||
|
|
||||||
await achievement_matcher.finish(msg)
|
|
||||||
|
|
||||||
# 注册查询抽卡指令,支持@用户查询功能
|
|
||||||
query_matcher = on_command("查询抽卡", aliases={"查询抽奖"}, priority=5, rule=check_permission())
|
|
||||||
|
|
||||||
@query_matcher.handle()
|
@query_matcher.handle()
|
||||||
async def handle_query(bot: Bot, event: MessageEvent, state: T_State):
|
async def handle_query_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
# 获取消息中的@用户
|
"""他人统计查询命令处理器"""
|
||||||
message = event.get_message()
|
await handlers.handle_query(bot, event, state)
|
||||||
at_segment = None
|
|
||||||
|
|
||||||
for segment in message:
|
|
||||||
if segment.type == "at":
|
|
||||||
at_segment = segment
|
|
||||||
break
|
|
||||||
|
|
||||||
# 确定查询的用户ID
|
|
||||||
if at_segment:
|
|
||||||
# 查询被@的用户
|
|
||||||
target_user_id = str(at_segment.data.get("qq", ""))
|
|
||||||
# 获取被@用户的信息
|
|
||||||
if isinstance(event, GroupMessageEvent):
|
|
||||||
try:
|
|
||||||
group_id = event.group_id
|
|
||||||
user_info = await bot.get_group_member_info(group_id=group_id, user_id=int(target_user_id))
|
|
||||||
target_user_name = user_info.get("card") or user_info.get("nickname", "用户")
|
|
||||||
except:
|
|
||||||
target_user_name = "用户"
|
|
||||||
else:
|
|
||||||
target_user_name = "用户"
|
|
||||||
else:
|
|
||||||
# 查询自己
|
|
||||||
target_user_id = str(event.user_id)
|
|
||||||
target_user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
|
|
||||||
|
|
||||||
# 获取用户统计
|
|
||||||
stats = gacha_system.get_user_stats(target_user_id)
|
|
||||||
|
|
||||||
# 构建响应消息
|
|
||||||
msg = Message()
|
|
||||||
|
|
||||||
# 如果查询的是他人
|
|
||||||
if target_user_id != str(event.user_id):
|
|
||||||
msg.append(format_user_mention(str(event.user_id), event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname))
|
|
||||||
msg.append(f" 查询了 ")
|
|
||||||
msg.append(format_user_mention(target_user_id, target_user_name))
|
|
||||||
msg.append(f" 的抽卡记录\n\n")
|
|
||||||
else:
|
|
||||||
msg.append(format_user_mention(target_user_id, target_user_name) + "\n")
|
|
||||||
|
|
||||||
if not stats["success"]:
|
|
||||||
msg.append(f"该用户还没有抽卡记录哦!")
|
|
||||||
await query_matcher.finish(msg)
|
|
||||||
|
|
||||||
# 构建统计信息
|
|
||||||
msg.append(f"总抽卡次数:{stats['total_draws']}\n")
|
|
||||||
msg.append(f"R卡数量:{stats['R_count']}\n")
|
|
||||||
msg.append(f"SR卡数量:{stats['SR_count']}\n")
|
|
||||||
msg.append(f"SSR卡数量:{stats['SSR_count']}\n")
|
|
||||||
msg.append(f"SP卡数量:{stats['SP_count']}\n")
|
|
||||||
|
|
||||||
# 计算每种稀有度的比例
|
|
||||||
if stats['total_draws'] > 0:
|
|
||||||
msg.append("\n稀有度比例:\n")
|
|
||||||
msg.append(f"R: {stats['R_count']/stats['total_draws']*100:.2f}%\n")
|
|
||||||
msg.append(f"SR: {stats['SR_count']/stats['total_draws']*100:.2f}%\n")
|
|
||||||
msg.append(f"SSR: {stats['SSR_count']/stats['total_draws']*100:.2f}%\n")
|
|
||||||
msg.append(f"SP: {stats['SP_count']/stats['total_draws']*100:.2f}%\n")
|
|
||||||
|
|
||||||
# 添加最近抽卡记录
|
|
||||||
if stats["recent_draws"]:
|
|
||||||
msg.append("\n最近5次抽卡记录:\n")
|
|
||||||
for draw in reversed(stats["recent_draws"]):
|
|
||||||
msg.append(f"{draw['date']}: {draw['rarity']} - {draw['name']}\n")
|
|
||||||
|
|
||||||
await query_matcher.finish(msg)
|
|
||||||
|
|
||||||
# 自定义排行榜权限检查(仅检查白名单,不检查抽卡次数)
|
|
||||||
def check_rank_permission() -> Rule:
|
|
||||||
async def _checker(event: MessageEvent) -> bool:
|
|
||||||
# 允许特定用户在任何场景下使用
|
|
||||||
if event.user_id == ALLOWED_USER_ID:
|
|
||||||
return True
|
|
||||||
|
|
||||||
# 在允许的群聊中任何人都可以使用
|
|
||||||
if isinstance(event, GroupMessageEvent) and event.group_id == ALLOWED_GROUP_ID:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
return Rule(_checker)
|
|
||||||
|
|
||||||
|
|
||||||
rank_matcher = on_startswith(("抽卡排行","抽卡榜"), priority=1, rule=check_rank_permission())
|
|
||||||
|
|
||||||
@rank_matcher.handle()
|
@rank_matcher.handle()
|
||||||
async def handle_rank(bot: Bot, event: MessageEvent, state: T_State):
|
async def handle_rank_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
# 获取排行榜数据
|
"""排行榜查询命令处理器"""
|
||||||
rank_data = gacha_system.get_rank_list()
|
await handlers.handle_rank(bot, event, state)
|
||||||
|
|
||||||
if not rank_data:
|
|
||||||
await rank_matcher.finish("暂无抽卡排行榜数据")
|
|
||||||
|
|
||||||
# 构建消息
|
|
||||||
msg = Message("✨┈┈┈┈┈ 抽卡排行榜 ┈┈┈┈┈✨\n")
|
|
||||||
msg.append("🏆 SSR/SP稀有度式神排行榜 🏆\n")
|
|
||||||
msg.append("┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈\n")
|
|
||||||
|
|
||||||
for i, (user_id, data) in enumerate(rank_data[:10], 1):
|
|
||||||
# 获取用户昵称
|
|
||||||
user_name = "未知用户"
|
|
||||||
try:
|
|
||||||
if isinstance(event, GroupMessageEvent):
|
|
||||||
# 群聊场景获取群名片或昵称
|
|
||||||
user_info = await bot.get_group_member_info(group_id=event.group_id, user_id=int(user_id))
|
|
||||||
user_name = user_info.get("card") or user_info.get("nickname", "未知用户")
|
|
||||||
else:
|
|
||||||
# 私聊场景获取昵称
|
|
||||||
user_info = await bot.get_stranger_info(user_id=int(user_id))
|
|
||||||
user_name = user_info.get("nickname", "未知用户")
|
|
||||||
except Exception as e:
|
|
||||||
# 如果获取失败,尝试从事件中获取发送者信息
|
|
||||||
if str(user_id) == str(event.user_id):
|
|
||||||
user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
|
|
||||||
|
|
||||||
# 美化输出格式
|
|
||||||
rank_icon = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else f"{i}."
|
|
||||||
ssr_icon = "🌟"
|
|
||||||
sp_icon = "💫"
|
|
||||||
total = data['SSR_count'] + data['SP_count']
|
|
||||||
|
|
||||||
msg.append(f"{rank_icon} {user_name}\n")
|
|
||||||
msg.append(f" {ssr_icon}SSR: {data['SSR_count']}次 {sp_icon}SP: {data['SP_count']}次\n")
|
|
||||||
msg.append(f" 🔮总计: {total}次\n")
|
|
||||||
msg.append("┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈\n")
|
|
||||||
|
|
||||||
await rank_matcher.finish(msg)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@daily_stats_matcher.handle()
|
@daily_stats_matcher.handle()
|
||||||
async def handle_daily_stats(bot: Bot, event: MessageEvent, state: T_State):
|
async def handle_daily_stats_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
"""处理今日抽卡统计命令"""
|
"""今日统计查询命令处理器"""
|
||||||
result = gacha_system.get_daily_stats()
|
await handlers.handle_daily_stats(bot, event, state)
|
||||||
|
|
||||||
if not result["success"]:
|
|
||||||
await daily_stats_matcher.finish(f"❌ {result['message']}")
|
|
||||||
|
|
||||||
stats = result["stats"]
|
@achievement_matcher.handle()
|
||||||
date = result["date"]
|
async def handle_achievement_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
|
"""成就查询命令处理器"""
|
||||||
|
await handlers.handle_achievement(bot, event, state)
|
||||||
|
|
||||||
# 构建统计消息
|
|
||||||
msg = Message()
|
|
||||||
msg.append(f"📊 今日抽卡统计 ({date})\n\n")
|
|
||||||
msg.append(f"👥 参与人数:{stats['total_users']}人\n")
|
|
||||||
msg.append(f"🎲 总抽卡次数:{stats['total_draws']}次\n\n")
|
|
||||||
|
|
||||||
# 稀有度分布
|
|
||||||
msg.append("🎯 稀有度分布:\n")
|
|
||||||
if stats['total_draws'] > 0:
|
|
||||||
msg.append(f"📜 R:{stats['rarity_stats']['R']}张 ({stats['rarity_stats']['R']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
msg.append(f"⭐ SR:{stats['rarity_stats']['SR']}张 ({stats['rarity_stats']['SR']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
msg.append(f"🌟 SSR:{stats['rarity_stats']['SSR']}张 ({stats['rarity_stats']['SSR']/stats['total_draws']*100:.1f}%)\n")
|
|
||||||
msg.append(f"🌈 SP:{stats['rarity_stats']['SP']}张 ({stats['rarity_stats']['SP']/stats['total_draws']*100:.1f}%)\n\n")
|
|
||||||
else:
|
|
||||||
msg.append("暂无数据\n\n")
|
|
||||||
|
|
||||||
# SSR/SP排行榜
|
|
||||||
if stats['top_users']:
|
|
||||||
msg.append("🏆 今日SSR/SP排行榜:\n")
|
|
||||||
for i, user_data in enumerate(stats['top_users'][:5], 1):
|
|
||||||
user_id = user_data['user_id']
|
|
||||||
ssr_count = user_data['ssr_count']
|
|
||||||
|
|
||||||
# 尝试获取用户昵称
|
|
||||||
try:
|
|
||||||
user_info = await bot.get_stranger_info(user_id=int(user_id))
|
|
||||||
user_name = user_info.get('nickname', f'用户{user_id}')
|
|
||||||
except:
|
|
||||||
user_name = f'用户{user_id}'
|
|
||||||
|
|
||||||
if i == 1:
|
|
||||||
msg.append(f"🥇 {user_name}:{ssr_count}张\n")
|
|
||||||
elif i == 2:
|
|
||||||
msg.append(f"🥈 {user_name}:{ssr_count}张\n")
|
|
||||||
elif i == 3:
|
|
||||||
msg.append(f"🥉 {user_name}:{ssr_count}张\n")
|
|
||||||
else:
|
|
||||||
msg.append(f"🏅 {user_name}:{ssr_count}张\n")
|
|
||||||
else:
|
|
||||||
msg.append("🏆 今日还没有人抽到SSR/SP哦~")
|
|
||||||
|
|
||||||
await daily_stats_matcher.finish(msg)
|
|
||||||
|
|
||||||
# 抽卡介绍命令
|
|
||||||
intro_matcher = on_command("抽卡介绍", aliases=set(INTRO_COMMANDS), priority=5, rule=check_permission())
|
|
||||||
|
|
||||||
@intro_matcher.handle()
|
@intro_matcher.handle()
|
||||||
async def handle_intro(bot: Bot, event: MessageEvent, state: T_State):
|
async def handle_intro_wrapper(bot: Bot, event: MessageEvent, state: T_State) -> None:
|
||||||
"""处理抽卡介绍命令"""
|
"""插件介绍命令处理器"""
|
||||||
|
await handlers.handle_intro(bot, event, state)
|
||||||
|
|
||||||
# 构建介绍消息
|
|
||||||
msg = "🎮 阴阳师抽卡系统介绍 🎮\n\n"
|
|
||||||
|
|
||||||
# 抽卡机制
|
# 注册Web API路由
|
||||||
msg += "📋 抽卡机制:\n"
|
|
||||||
msg += f"• 每日限制:{DAILY_LIMIT}次免费抽卡\n"
|
|
||||||
msg += "• 稀有度概率:\n"
|
|
||||||
for rarity, prob in config.RARITY_PROBABILITY.items():
|
|
||||||
msg += f" - {rarity}: {prob}%\n"
|
|
||||||
msg += "\n"
|
|
||||||
|
|
||||||
# 可用指令
|
|
||||||
msg += "🎯 可用指令:\n"
|
|
||||||
msg += f"• 抽卡:{', '.join(GACHA_COMMANDS[:3])}\n"
|
|
||||||
msg += f"• 三连抽:{', '.join(TRIPLE_GACHA_COMMANDS)}\n"
|
|
||||||
msg += f"• 个人统计:{', '.join(STATS_COMMANDS[:2])}\n"
|
|
||||||
msg += f"• 今日统计:{', '.join(DAILY_STATS_COMMANDS[:2])}\n"
|
|
||||||
msg += f"• 查询成就:{', '.join(ACHIEVEMENT_COMMANDS)}\n"
|
|
||||||
msg += "• 查询抽卡/查询抽奖:查询自己的或@他人的抽卡数据\n"
|
|
||||||
msg += "• 抽卡排行/抽卡榜:查看SSR/SP排行榜\n"
|
|
||||||
msg += "\n"
|
|
||||||
|
|
||||||
# 成就系统
|
|
||||||
msg += "🏆 成就系统:\n"
|
|
||||||
msg += "\n📅 勤勤恳恳系列(连续抽卡):\n"
|
|
||||||
consecutive_achievements = [
|
|
||||||
("勤勤恳恳Ⅰ", "30天", "天卡"),
|
|
||||||
("勤勤恳恳Ⅱ", "60天", "天卡"),
|
|
||||||
("勤勤恳恳Ⅲ", "90天", "天卡"),
|
|
||||||
("勤勤恳恳Ⅳ", "120天", "周卡"),
|
|
||||||
("勤勤恳恳Ⅴ", "150天", "周卡")
|
|
||||||
]
|
|
||||||
|
|
||||||
for name, days, reward in consecutive_achievements:
|
|
||||||
msg += f"• {name}:连续{days} → {reward} 💎\n"
|
|
||||||
msg += " ※ 达到最高等级后每30天可重复获得天卡奖励\n\n"
|
|
||||||
|
|
||||||
msg += "😭 非酋系列(无SSR/SP连击):\n"
|
|
||||||
no_ssr_achievements = [
|
|
||||||
("非酋", "60次", "天卡"),
|
|
||||||
("顶级非酋", "120次", "周卡"),
|
|
||||||
("月见黑", "180次", "月卡")
|
|
||||||
]
|
|
||||||
|
|
||||||
for name, count, reward in no_ssr_achievements:
|
|
||||||
msg += f"• {name}:连续{count}未中SSR/SP → {reward} 💎\n"
|
|
||||||
msg += "\n"
|
|
||||||
|
|
||||||
# 奖励说明
|
|
||||||
msg += "🎁 奖励说明:\n"
|
|
||||||
msg += "• 天卡:蛋定助手天卡奖励\n"
|
|
||||||
msg += "• 周卡:蛋定助手周卡奖励\n"
|
|
||||||
msg += "• 月卡:蛋定助手月卡奖励\n"
|
|
||||||
msg += "\n"
|
|
||||||
|
|
||||||
# 联系管理员
|
|
||||||
msg += "📞 重要提醒:\n"
|
|
||||||
msg += "🔸 所有奖励需要联系管理员获取 🔸\n"
|
|
||||||
msg += "请在获得成就后主动联系管理员领取奖励哦~ 😊\n\n"
|
|
||||||
|
|
||||||
# 祝福语
|
|
||||||
msg += "🍀 祝您抽卡愉快,欧气满满! ✨"
|
|
||||||
|
|
||||||
await intro_matcher.finish(msg)
|
|
||||||
|
|
||||||
# 导入 Web API 模块,使其路由能够注册到 NoneBot 的 FastAPI 应用
|
|
||||||
from . import web_api
|
|
||||||
|
|
||||||
# 注册 Web 路由
|
|
||||||
try:
|
try:
|
||||||
|
from . import web_api
|
||||||
web_api.register_web_routes()
|
web_api.register_web_routes()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ 注册 onmyoji_gacha Web 路由失败: {e}")
|
logger.error(f"注册 onmyoji_gacha Web 路由失败: {e}")
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
|
"""
|
||||||
|
阴阳师抽卡插件 - API工具模块
|
||||||
|
|
||||||
|
提供外部API交互功能,包括:
|
||||||
|
- SSR/SP积分奖励处理
|
||||||
|
- 管理员通知
|
||||||
|
- 积分API调用
|
||||||
|
"""
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
from typing import Dict, Optional, Tuple
|
from typing import Dict, Optional, Tuple
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
|
"""\n阴阳师抽卡插件 - 配置管理模块\n\n集中管理插件所有配置项,包括:\n- 权限配置(群组白名单、管理员)\n- 抽卡参数(池子、概率、每日上限)\n- 成就系统配置\n- 路径配置\n"""
|
||||||
|
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class Config(BaseSettings):
|
class Config(BaseSettings):
|
||||||
|
"""阴阳师抽卡插件配置模型"""
|
||||||
model_config = SettingsConfigDict(extra="ignore")
|
model_config = SettingsConfigDict(extra="ignore")
|
||||||
|
|
||||||
# 抽卡概率配置
|
# 抽卡概率配置
|
||||||
@@ -108,7 +111,7 @@ class Config(BaseSettings):
|
|||||||
SPECIAL_PROBABILITY_USERS: list = [] # 100%抽到SSR或SP的用户列表
|
SPECIAL_PROBABILITY_USERS: list = [] # 100%抽到SSR或SP的用户列表
|
||||||
|
|
||||||
# Web后台管理配置
|
# Web后台管理配置
|
||||||
WEB_ADMIN_TOKEN: str = os.getenv("WEB_ADMIN_TOKEN", "onmyoji_admin_token_2024")
|
WEB_ADMIN_TOKEN: str = os.getenv("WEB_ADMIN_TOKEN", "") # 空字符串=未配置,web_api启动时校验
|
||||||
WEB_ADMIN_PORT: int = int(os.getenv("WEB_ADMIN_PORT", "8080"))
|
WEB_ADMIN_PORT: int = int(os.getenv("WEB_ADMIN_PORT", "8080"))
|
||||||
|
|
||||||
# 时区
|
# 时区
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
"""
|
||||||
|
阴阳师抽卡插件 - 数据管理模块
|
||||||
|
|
||||||
|
管理抽卡数据持久化,包括:
|
||||||
|
- SQLite数据库操作
|
||||||
|
- 用户抽卡记录管理
|
||||||
|
- 每日签到记录
|
||||||
|
- 统计查询
|
||||||
|
|
||||||
|
TODO(代码评审 2026-05-03): 本模块承担了数据文件IO + 缓存 + 业务规则三重职责,
|
||||||
|
后续应拆分为: data_io(纯文件读写) / data_cache(内存缓存层) / data_rules(业务规则校验)。
|
||||||
|
当前拆分风险较大(影响面广),暂维持现状。
|
||||||
|
|
||||||
|
TODO(第二轮评审 2026-05-03): 补充建议拆分方案:
|
||||||
|
- achievement_manager.py: 成就定义加载 + 进度计算 + 奖励发放 (~150行)
|
||||||
|
- record_manager.py: 记录归档 + 统计查询 + 每日数据 (~100行)
|
||||||
|
- data_manager.py: 核心用户数据IO + 缓存管理 (~359行)
|
||||||
|
拆分为独立PR,不阻塞当前修复。
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
@@ -12,6 +32,7 @@ from .config import Config
|
|||||||
config = Config()
|
config = Config()
|
||||||
|
|
||||||
class DataManager:
|
class DataManager:
|
||||||
|
"""抽卡数据管理器,封装所有数据库操作"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# 确保目录存在
|
# 确保目录存在
|
||||||
os.makedirs(os.path.dirname(config.DB_FILE), exist_ok=True)
|
os.makedirs(os.path.dirname(config.DB_FILE), exist_ok=True)
|
||||||
@@ -102,7 +123,7 @@ class DataManager:
|
|||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
|
|
||||||
def _init_sign_in_table(self, cursor: sqlite3.Cursor) -> None:
|
def _init_sign_in_table(self, cursor: sqlite3.Cursor) -> None: # OK
|
||||||
"""创建每日签到表"""
|
"""创建每日签到表"""
|
||||||
cursor.execute("""
|
cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS daily_sign_in (
|
CREATE TABLE IF NOT EXISTS daily_sign_in (
|
||||||
@@ -115,7 +136,7 @@ class DataManager:
|
|||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
def update_achievement_progress(self, user_id: str, rarity: str) -> List[str]:
|
def update_achievement_progress(self, user_id: str, rarity: str) -> List[str]: # type: ignore[return]
|
||||||
"""更新用户成就进度,返回新解锁的成就列表"""
|
"""更新用户成就进度,返回新解锁的成就列表"""
|
||||||
today = self.get_today_date()
|
today = self.get_today_date()
|
||||||
unlocked_achievements = []
|
unlocked_achievements = []
|
||||||
|
|||||||
299
danding_bot/plugins/onmyoji_gacha/formatters.py
Normal file
299
danding_bot/plugins/onmyoji_gacha/formatters.py
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
"""
|
||||||
|
消息格式化模块 - 抽卡、成就、统计等所有用户可见输出
|
||||||
|
|
||||||
|
提供所有用户可见消息的格式化函数,包括:
|
||||||
|
- 抽卡结果消息
|
||||||
|
- 三连抽结果消息
|
||||||
|
- 成就通知消息
|
||||||
|
- 统计查询消息
|
||||||
|
- 排行榜消息
|
||||||
|
- 每日统计消息
|
||||||
|
|
||||||
|
所有函数返回NoneBot的Message对象,可直接用于matcher.send()。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import List, Dict, Any, Optional, Tuple
|
||||||
|
from nonebot.adapters.onebot.v11 import Message, MessageSegment
|
||||||
|
from .utils import format_user_mention, get_image_path
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
# 稀有度显示配置(单一数据源,消除重复模式)
|
||||||
|
RARITY_DISPLAY = {
|
||||||
|
"SSR": {"congrats": ("🌟✨", "✨🌟"), "card": "🎊", "desc": "SSR", "tail": "💫"},
|
||||||
|
"SP": {"congrats": ("🌈🎆", "🎆🌈"), "card": "🎉", "desc": "SP", "tail": "🔥"},
|
||||||
|
"SR": {"congrats": ("⭐", "⭐"), "card": "✨", "desc": "SR", "tail": ""},
|
||||||
|
"R": {"congrats": ("🍀", "🍀"), "card": "📜", "desc": "R", "tail": ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
def format_gacha_result(rarity: str, name: str, user_id: str, user_name: str, image_url: str) -> Message:
|
||||||
|
"""
|
||||||
|
格式化单次抽卡结果消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rarity: 稀有度 (SSR/SP/SR/R)
|
||||||
|
name: 式神名称
|
||||||
|
user_id: QQ号
|
||||||
|
user_name: 用户昵称
|
||||||
|
image_url: 图片路径
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含文本和图片的消息对象
|
||||||
|
"""
|
||||||
|
if not rarity or not name:
|
||||||
|
return Message("[抽卡] 数据不完整")
|
||||||
|
msg = Message()
|
||||||
|
if rarity == "SSR":
|
||||||
|
msg.append(f"🌟✨ 恭喜 {format_user_mention(user_id, user_name)} ✨🌟\n")
|
||||||
|
msg.append(f"🎊 抽到了 SSR 式神:{name} 🎊\n")
|
||||||
|
msg.append(f"💫 真是太幸运了!💫")
|
||||||
|
elif rarity == "SP":
|
||||||
|
msg.append(f"🌈🎆 恭喜 {format_user_mention(user_id, user_name)} 🎆🌈\n")
|
||||||
|
msg.append(f"🎉 抽到了 SP 式神:{name} 🎉\n")
|
||||||
|
msg.append(f"🔥 这是传说中的SP!🔥")
|
||||||
|
elif rarity == "SR":
|
||||||
|
msg.append(f"⭐ 恭喜 {format_user_mention(user_id, user_name)} ⭐\n")
|
||||||
|
msg.append(f"✨ 抽到了 SR 式神:{name} ✨")
|
||||||
|
else: # R
|
||||||
|
msg.append(f"🍀 {format_user_mention(user_id, user_name)} 🍀\n")
|
||||||
|
msg.append(f"📜 抽到了 R 式神:{name}")
|
||||||
|
if image_url and os.path.exists(image_url):
|
||||||
|
msg.append(MessageSegment.image(f"file:///{get_image_path(image_url)}"))
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_triple_gacha_result(results: List[Tuple[str, str, str]], user_id: str, user_name: str) -> Message:
|
||||||
|
"""
|
||||||
|
格式化三连抽结果消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
results: 三连抽结果列表,每个元素为(稀有度, 式神名, 图片路径)
|
||||||
|
user_id: QQ号
|
||||||
|
user_name: 用户昵称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含三连抽结果的消息对象
|
||||||
|
"""
|
||||||
|
msg = Message()
|
||||||
|
msg.append(f"🎯 {format_user_mention(user_id, user_name)} 的三连抽结果:\n\n")
|
||||||
|
|
||||||
|
for i, (rarity, name, image_path) in enumerate(results, 1):
|
||||||
|
if rarity == "SSR":
|
||||||
|
msg.append(f"🌟 第{i}抽:SSR - {name}\n")
|
||||||
|
elif rarity == "SP":
|
||||||
|
msg.append(f"🌈 第{i}抽:SP - {name}\n")
|
||||||
|
elif rarity == "SR":
|
||||||
|
msg.append(f"⭐ 第{i}抽:SR - {name}\n")
|
||||||
|
else: # R
|
||||||
|
msg.append(f"📜 第{i}抽:R - {name}\n")
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_achievement_notify(
|
||||||
|
achievements_data: List[Dict[str, Any]],
|
||||||
|
user_id: str,
|
||||||
|
) -> Message:
|
||||||
|
"""
|
||||||
|
格式化成就解锁通知消息。
|
||||||
|
|
||||||
|
纯格式化函数,不执行任何业务逻辑(奖励发放等)。
|
||||||
|
调用方负责解析成就数据和处理奖励。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
achievements_data: 已解析的成就数据列表,每项含 name/reward/claimed/reward_msg 等字段
|
||||||
|
user_id: 用户ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 格式化的成就通知消息
|
||||||
|
|
||||||
|
Note:
|
||||||
|
纯函数,无副作用,无外部调用。
|
||||||
|
"""
|
||||||
|
if not achievements_data:
|
||||||
|
return Message()
|
||||||
|
msg = Message()
|
||||||
|
|
||||||
|
if achievements_data:
|
||||||
|
msg.append("\n\n🏆 恭喜解锁新成就!\n")
|
||||||
|
|
||||||
|
for ach in achievements_data:
|
||||||
|
name = ach.get("name", "未知成就")
|
||||||
|
reward = ach.get("reward", 0)
|
||||||
|
reward_msg = ach.get("reward_msg", "")
|
||||||
|
claimed = ach.get("claimed", False)
|
||||||
|
|
||||||
|
if claimed and reward_msg:
|
||||||
|
msg.append(f"🎖️ {name} 重复奖励 (奖励:{reward}) {reward_msg}\n")
|
||||||
|
else:
|
||||||
|
msg.append(f"🎖️ {name}\n")
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_achievement_progress(
|
||||||
|
consecutive_days: int,
|
||||||
|
no_ssr_streak: int,
|
||||||
|
user_id: str
|
||||||
|
) -> Message:
|
||||||
|
"""
|
||||||
|
格式化成就进度消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
consecutive_days: 连续抽卡天数
|
||||||
|
no_ssr_streak: 连续未出SSR/SP次数
|
||||||
|
user_id: 用户ID
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含成就进度的消息对象
|
||||||
|
"""
|
||||||
|
from .gacha import get_achievement_definition
|
||||||
|
|
||||||
|
msg = Message()
|
||||||
|
msg.append(f"🎯 成就进度:\n")
|
||||||
|
|
||||||
|
# 勤勤恳恳成就进度
|
||||||
|
achievement = get_achievement_definition("勤勤恳恳Ⅰ")
|
||||||
|
if achievement:
|
||||||
|
if consecutive_days < 30:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅰ:{consecutive_days}/30天 🎯\n")
|
||||||
|
elif consecutive_days < 60:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅱ:{consecutive_days}/60天 🎯\n")
|
||||||
|
elif consecutive_days < 90:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅲ:{consecutive_days}/90天 🎯\n")
|
||||||
|
elif consecutive_days < 120:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅳ:{consecutive_days}/120天 ⭐\n")
|
||||||
|
elif consecutive_days < 150:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅴ:{consecutive_days}/150天 ⭐\n")
|
||||||
|
else:
|
||||||
|
# 计算下次奖励周期
|
||||||
|
next_reward = 150 + ((consecutive_days - 150) // 30 + 1) * 30
|
||||||
|
if next_reward <= 365:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅴ (满级):{consecutive_days}天,距离下次奖励{next_reward}天 🎯\n")
|
||||||
|
else:
|
||||||
|
msg.append(f"📅 勤勤恳恳Ⅴ (满级):{consecutive_days}天,可获得奖励!🎉\n")
|
||||||
|
|
||||||
|
# 非酋成就进度
|
||||||
|
if no_ssr_streak > 0:
|
||||||
|
if no_ssr_streak < 60:
|
||||||
|
msg.append(f"💔 非酋进度:{no_ssr_streak}/60次 😭\n")
|
||||||
|
elif no_ssr_streak < 120:
|
||||||
|
msg.append(f"💔 顶级非酋:{no_ssr_streak}/120次 😱\n")
|
||||||
|
elif no_ssr_streak < 180:
|
||||||
|
msg.append(f"💔 月见黑:{no_ssr_streak}/180次 🌙\n")
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_user_stats(stats: Dict[str, Any], user_id: str, user_name: str) -> Message:
|
||||||
|
"""
|
||||||
|
格式化用户抽卡统计消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
stats: 用户统计数据字典
|
||||||
|
user_id: 用户ID
|
||||||
|
user_name: 用户昵称
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含统计信息的消息对象
|
||||||
|
"""
|
||||||
|
msg = Message()
|
||||||
|
msg.append(f"📊 {format_user_mention(user_id, user_name)} 的抽卡统计:\n")
|
||||||
|
msg.append(f"🎲 总抽卡次数:{stats['total_draws']}次\n")
|
||||||
|
|
||||||
|
total = stats['total_draws']
|
||||||
|
if total > 0:
|
||||||
|
msg.append(f"\n稀有度分布:\n")
|
||||||
|
msg.append(f"📜 R:{stats.get('R_count',0)}张 ({stats.get('R_count',0)/total*100:.1f}%)\n")
|
||||||
|
msg.append(f"⭐ SR:{stats.get('SR_count',0)}张 ({stats.get('SR_count',0)/total*100:.1f}%)\n")
|
||||||
|
msg.append(f"✨ SSR:{stats.get('SSR_count',0)}张 ({stats.get('SSR_count',0)/total*100:.1f}%)\n")
|
||||||
|
msg.append(f"🌈 SP:{stats.get('SP_count',0)}张 ({stats.get('SP_count',0)/total*100:.1f}%)")
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_user_detail_stats(
|
||||||
|
stats: Dict[str, Any],
|
||||||
|
user_id: str,
|
||||||
|
user_name: str,
|
||||||
|
recent_draws: List[Dict[str, Any]]
|
||||||
|
) -> Message:
|
||||||
|
"""
|
||||||
|
格式化用户详细抽卡统计消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
stats: 用户统计数据字典
|
||||||
|
user_id: 用户ID
|
||||||
|
user_name: 用户昵称
|
||||||
|
recent_draws: 最近抽卡记录列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含详细统计信息的消息对象
|
||||||
|
"""
|
||||||
|
msg = Message()
|
||||||
|
msg.append(f"{format_user_mention(user_id, user_name)} 的抽卡统计:\n")
|
||||||
|
msg.append(f"总抽卡次数:{stats['total_draws']}次\n")
|
||||||
|
|
||||||
|
total = stats['total_draws']
|
||||||
|
if total > 0:
|
||||||
|
msg.append(f"R:{stats.get('R_count',0)}张 ({stats.get('R_count',0)/total*100:.1f}%)\n")
|
||||||
|
msg.append(f"SR:{stats.get('SR_count',0)}张 ({stats.get('SR_count',0)/total*100:.1f}%)\n")
|
||||||
|
msg.append(f"SSR:{stats.get('SSR_count',0)}张 ({stats.get('SSR_count',0)/total*100:.1f}%)\n")
|
||||||
|
msg.append(f"SP:{stats.get('SP_count',0)}张 ({stats.get('SP_count',0)/total*100:.1f}%)")
|
||||||
|
|
||||||
|
if recent_draws:
|
||||||
|
msg.append(f"\n最近{len(recent_draws)}次抽卡:\n")
|
||||||
|
for draw in recent_draws:
|
||||||
|
msg.append(f"• {draw}\n")
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_rank_list(rank_data: List[Dict[str, Any]], page: int, total_pages: int) -> Message:
|
||||||
|
"""
|
||||||
|
格式化抽卡排行榜消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rank_data: 排行榜数据列表
|
||||||
|
page: 当前页码
|
||||||
|
total_pages: 总页数
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含排行榜的消息对象
|
||||||
|
"""
|
||||||
|
msg = Message()
|
||||||
|
msg.append(f"🏆 抽卡排行榜 (第{page}页/共{total_pages}页)\n\n")
|
||||||
|
|
||||||
|
for idx, entry in enumerate(rank_data, 1):
|
||||||
|
msg.append(f"{idx}. {entry.get('user_name', '未知')} - {entry.get('total_draws', 0)}次\n")
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
|
||||||
|
def format_daily_stats(daily_stats: Dict[str, Any]) -> Message:
|
||||||
|
"""
|
||||||
|
格式化今日抽卡统计消息。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
daily_stats: 今日统计数据字典
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message: 包含今日统计的消息对象
|
||||||
|
"""
|
||||||
|
msg = Message()
|
||||||
|
msg.append(f"📅 今日抽卡统计\n")
|
||||||
|
msg.append(f"总抽卡次数:{daily_stats.get('today_total',0)}次\n")
|
||||||
|
|
||||||
|
msg.append(f"\n稀有度分布:\n")
|
||||||
|
msg.append(f"R:{daily_stats.get('R_count',0)}张\n")
|
||||||
|
msg.append(f"SR:{daily_stats.get('SR_count',0)}张\n")
|
||||||
|
msg.append(f"SSR:{daily_stats.get('SSR_count',0)}张\n")
|
||||||
|
msg.append(f"SP:{daily_stats.get('SP_count',0)}张\n")
|
||||||
|
|
||||||
|
top = daily_stats.get("top_users", [])
|
||||||
|
if top:
|
||||||
|
msg.append("\n今日TOP5:\n")
|
||||||
|
for idx, u in enumerate(top[:5], 1):
|
||||||
|
msg.append(f"{idx}. {u.get('user_name','未知')} - {u.get('draws',0)}次\n")
|
||||||
|
|
||||||
|
return msg
|
||||||
@@ -1,5 +1,15 @@
|
|||||||
|
"""
|
||||||
|
阴阳师抽卡插件 - 抽卡核心逻辑模块
|
||||||
|
|
||||||
|
实现抽卡核心算法,包括:
|
||||||
|
- 多稀有度抽卡(R/SR/SSR/SP)
|
||||||
|
- 子池支持
|
||||||
|
- 保底机制
|
||||||
|
- 成就检查
|
||||||
|
"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
from typing import Dict, Tuple, List, Optional
|
from typing import Dict, Tuple, List, Optional, Any
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@@ -10,10 +20,11 @@ config = Config()
|
|||||||
data_manager = DataManager()
|
data_manager = DataManager()
|
||||||
|
|
||||||
class GachaSystem:
|
class GachaSystem:
|
||||||
|
"""抽卡系统核心类,管理抽卡逻辑和数据"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.data_manager = data_manager
|
self.data_manager = data_manager
|
||||||
|
|
||||||
def draw(self, user_id: str) -> Dict:
|
def draw(self, user_id: str) -> Dict[str, Any]:
|
||||||
"""执行一次抽卡"""
|
"""执行一次抽卡"""
|
||||||
# 检查抽卡限制
|
# 检查抽卡限制
|
||||||
if not self.data_manager.check_daily_limit(user_id):
|
if not self.data_manager.check_daily_limit(user_id):
|
||||||
|
|||||||
25
danding_bot/plugins/onmyoji_gacha/handlers/__init__.py
Normal file
25
danding_bot/plugins/onmyoji_gacha/handlers/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
handlers包 - 抽卡命令处理函数
|
||||||
|
|
||||||
|
将各handler函数集中在此包中,便于__init__.py统一导入和注册matcher。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .gacha import handle_gacha
|
||||||
|
from .triple_gacha import handle_triple_gacha
|
||||||
|
from .stats import handle_stats
|
||||||
|
from .query import handle_query
|
||||||
|
from .rank import handle_rank
|
||||||
|
from .daily_stats import handle_daily_stats
|
||||||
|
from .achievement import handle_achievement
|
||||||
|
from .intro import handle_intro
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"handle_gacha",
|
||||||
|
"handle_triple_gacha",
|
||||||
|
"handle_stats",
|
||||||
|
"handle_query",
|
||||||
|
"handle_rank",
|
||||||
|
"handle_daily_stats",
|
||||||
|
"handle_achievement",
|
||||||
|
"handle_intro",
|
||||||
|
]
|
||||||
38
danding_bot/plugins/onmyoji_gacha/handlers/achievement.py
Normal file
38
danding_bot/plugins/onmyoji_gacha/handlers/achievement.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
"""
|
||||||
|
成就系统查询处理模块
|
||||||
|
|
||||||
|
处理成就系统查询命令,显示已解锁成就和进度。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from ..utils import format_user_mention
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_achievement(bot: Bot, event: MessageEvent, state: dict):
|
||||||
|
"""处理成就系统查询命令"""
|
||||||
|
user_id = str(event.user_id)
|
||||||
|
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
||||||
|
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
achievements = await gacha_system.get_user_achievements(user_id)
|
||||||
|
|
||||||
|
if not achievements:
|
||||||
|
await event.finish("您还没有解锁任何成就哦~ 继续抽卡吧!")
|
||||||
|
|
||||||
|
msg = f"🏅 {format_user_mention(user_id, user_name)} 的成就:\n\n"
|
||||||
|
|
||||||
|
for ach in achievements:
|
||||||
|
name = ach.get("name", "未知成就")
|
||||||
|
desc = ach.get("description", "")
|
||||||
|
reward = ach.get("reward", 0)
|
||||||
|
claimed = ach.get("claimed", False)
|
||||||
|
|
||||||
|
status = "✅已领取" if claimed else "🎁可领取"
|
||||||
|
msg += f"🎖️ {name}\n {desc}\n 奖励:{reward} {status}\n\n"
|
||||||
|
|
||||||
|
await event.send(msg.strip())
|
||||||
25
danding_bot/plugins/onmyoji_gacha/handlers/daily_stats.py
Normal file
25
danding_bot/plugins/onmyoji_gacha/handlers/daily_stats.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
今日抽卡统计处理模块
|
||||||
|
|
||||||
|
处理今日抽卡统计查询命令。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from .. import formatters
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_daily_stats(bot: Bot, event: MessageEvent, state: dict):
|
||||||
|
"""处理今日抽卡统计命令"""
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
daily_stats = await gacha_system.get_daily_stats()
|
||||||
|
|
||||||
|
if not daily_stats or daily_stats.get("today_total", 0) == 0:
|
||||||
|
await event.finish("今日暂无抽卡记录")
|
||||||
|
|
||||||
|
msg = formatters.format_daily_stats(daily_stats)
|
||||||
|
await event.send(msg)
|
||||||
62
danding_bot/plugins/onmyoji_gacha/handlers/gacha.py
Normal file
62
danding_bot/plugins/onmyoji_gacha/handlers/gacha.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
"""
|
||||||
|
抽卡命令处理模块
|
||||||
|
|
||||||
|
处理单次抽卡命令,包括:
|
||||||
|
- 参数解析(子池选择)
|
||||||
|
- 抽卡执行
|
||||||
|
- SSR/SP奖励处理
|
||||||
|
- 成就检查
|
||||||
|
- 消息发送
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Dict, Any
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
from nonebot.params import CommandArg
|
||||||
|
from nonebot.adapters.onebot.v11 import Message
|
||||||
|
import nonebot
|
||||||
|
import random
|
||||||
|
|
||||||
|
from ..config import Config
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from .. import formatters
|
||||||
|
from ..api_utils import process_ssr_sp_reward
|
||||||
|
from ..utils import format_user_mention, build_achievement_notify, get_user_name
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_gacha(bot: Bot, event: MessageEvent, state: dict, args: Message = CommandArg()):
|
||||||
|
"""处理抽卡命令"""
|
||||||
|
user_id = str(event.user_id)
|
||||||
|
user_name = get_user_name(event)
|
||||||
|
|
||||||
|
# 解析子池参数
|
||||||
|
sub_pool = args.extract_plain_text().strip()
|
||||||
|
|
||||||
|
# 执行抽卡
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
result = await gacha_system.draw(user_id, sub_pool=sub_pool if sub_pool else None)
|
||||||
|
|
||||||
|
if not result["success"]:
|
||||||
|
await event.finish(result["message"])
|
||||||
|
|
||||||
|
rarity, shikigami, image_url = result["rarity"], result["name"], result["image"]
|
||||||
|
|
||||||
|
# 发送抽卡结果
|
||||||
|
msg = formatters.format_gacha_result(rarity, shikigami, user_id, user_name, image_url)
|
||||||
|
await event.send(msg)
|
||||||
|
|
||||||
|
# SSR/SP奖励处理
|
||||||
|
if rarity in ["SSR", "SP"]:
|
||||||
|
group_id = str(event.group_id) if isinstance(event, GroupMessageEvent) else None
|
||||||
|
reward_msg = await process_ssr_sp_reward(user_id, user_name, rarity, shikigami, group_id)
|
||||||
|
if reward_msg:
|
||||||
|
await event.send(reward_msg)
|
||||||
|
|
||||||
|
# 成就检查(使用统一编排函数)
|
||||||
|
unlocked = await gacha_system.check_achievements(user_id)
|
||||||
|
if unlocked:
|
||||||
|
achievement_msg = await build_achievement_notify(user_id, unlocked)
|
||||||
|
if achievement_msg:
|
||||||
|
await event.send(achievement_msg)
|
||||||
|
|
||||||
39
danding_bot/plugins/onmyoji_gacha/handlers/intro.py
Normal file
39
danding_bot/plugins/onmyoji_gacha/handlers/intro.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
"""
|
||||||
|
帮助介绍处理模块
|
||||||
|
|
||||||
|
显示抽卡系统的帮助信息和功能说明。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_intro(bot: Bot, event: MessageEvent, state: dict):
|
||||||
|
"""处理帮助介绍命令"""
|
||||||
|
intro_text = """
|
||||||
|
🎴 阴阳师抽卡系统 使用说明
|
||||||
|
|
||||||
|
📌 基础命令:
|
||||||
|
• 抽卡 [子池名] - 进行一次抽卡
|
||||||
|
• 三连抽 - 连续抽三次
|
||||||
|
• 我的抽卡 - 查看个人抽卡统计
|
||||||
|
• 抽卡详情 - 查看详细统计和最近记录
|
||||||
|
• 抽卡排行 [页码] - 查看排行榜
|
||||||
|
• 今日抽卡 - 查看今日抽卡统计
|
||||||
|
• 成就 - 查看成就系统
|
||||||
|
• 介绍 - 显示本帮助信息
|
||||||
|
|
||||||
|
📊 功能特色:
|
||||||
|
• 多稀有度式神:R/SR/SSR/SP
|
||||||
|
• 成就系统:连续抽卡、非酋成就等
|
||||||
|
• SSR/SP奖励:自动发放积分奖励
|
||||||
|
• 每日签到:首次抽卡自动签到
|
||||||
|
|
||||||
|
💡 提示:
|
||||||
|
• 每日抽卡次数有限制
|
||||||
|
• SSR/SP抽中会通知管理员
|
||||||
|
• 成就奖励自动发放或联系管理员领取
|
||||||
|
"""
|
||||||
|
await event.reply(intro_text.strip())
|
||||||
42
danding_bot/plugins/onmyoji_gacha/handlers/query.py
Normal file
42
danding_bot/plugins/onmyoji_gacha/handlers/query.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
"""
|
||||||
|
抽卡详情查询处理模块
|
||||||
|
|
||||||
|
处理用户抽卡详情查询,包括最近抽卡记录和成就进度。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from .. import formatters
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_query(bot: Bot, event: MessageEvent, state: dict):
|
||||||
|
"""处理抽卡详情查询命令"""
|
||||||
|
user_id = str(event.user_id)
|
||||||
|
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
||||||
|
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
stats = await gacha_system.get_user_stats(user_id)
|
||||||
|
|
||||||
|
if not stats or stats.get("total_draws", 0) == 0:
|
||||||
|
await event.finish("您还没有抽卡记录哦~")
|
||||||
|
|
||||||
|
# 获取最近抽卡记录
|
||||||
|
recent = await gacha_system.get_recent_draws(user_id, limit=5)
|
||||||
|
|
||||||
|
# 发送统计详情
|
||||||
|
msg = formatters.format_user_detail_stats(stats, user_id, user_name, recent)
|
||||||
|
await event.send(msg)
|
||||||
|
|
||||||
|
# 发送成就进度
|
||||||
|
progress = await gacha_system.get_achievement_progress(user_id)
|
||||||
|
if progress:
|
||||||
|
achievement_msg = formatters.format_achievement_progress(
|
||||||
|
progress.get("consecutive_days", 0),
|
||||||
|
progress.get("no_ssr_streak", 0),
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
await event.send(achievement_msg)
|
||||||
33
danding_bot/plugins/onmyoji_gacha/handlers/rank.py
Normal file
33
danding_bot/plugins/onmyoji_gacha/handlers/rank.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"""
|
||||||
|
抽卡排行榜处理模块
|
||||||
|
|
||||||
|
处理抽卡排行榜查询命令,支持分页显示。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
from nonebot.params import CommandArg
|
||||||
|
from nonebot.adapters.onebot.v11 import Message
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from .. import formatters
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_rank(bot: Bot, event: MessageEvent, state: dict, args: Message = CommandArg()):
|
||||||
|
"""处理抽卡排行榜命令"""
|
||||||
|
# 解析页码
|
||||||
|
page_text = args.extract_plain_text().strip()
|
||||||
|
page = 1
|
||||||
|
if page_text.isdigit():
|
||||||
|
page = int(page_text)
|
||||||
|
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
rank_data, total_pages = await gacha_system.get_rank_list(page=page)
|
||||||
|
|
||||||
|
if not rank_data:
|
||||||
|
await event.finish("暂无排行数据")
|
||||||
|
|
||||||
|
msg = formatters.format_rank_list(rank_data, page, total_pages)
|
||||||
|
await event.send(msg)
|
||||||
28
danding_bot/plugins/onmyoji_gacha/handlers/stats.py
Normal file
28
danding_bot/plugins/onmyoji_gacha/handlers/stats.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
"""
|
||||||
|
我的抽卡统计处理模块
|
||||||
|
|
||||||
|
处理用户的个人抽卡统计查询命令。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from .. import formatters
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_stats(bot: Bot, event: MessageEvent, state: dict):
|
||||||
|
"""处理我的抽卡统计命令"""
|
||||||
|
user_id = str(event.user_id)
|
||||||
|
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
||||||
|
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
stats = await gacha_system.get_user_stats(user_id)
|
||||||
|
|
||||||
|
if not stats or stats.get("total_draws", 0) == 0:
|
||||||
|
await event.finish("您还没有抽卡记录哦~")
|
||||||
|
|
||||||
|
msg = formatters.format_user_stats(stats, user_id, user_name)
|
||||||
|
await event.send(msg)
|
||||||
45
danding_bot/plugins/onmyoji_gacha/handlers/triple_gacha.py
Normal file
45
danding_bot/plugins/onmyoji_gacha/handlers/triple_gacha.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"""
|
||||||
|
三连抽命令处理模块
|
||||||
|
|
||||||
|
处理三连抽命令,包括:
|
||||||
|
- 三次抽卡执行
|
||||||
|
- 结果汇总
|
||||||
|
- 成就检查
|
||||||
|
"""
|
||||||
|
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, MessageEvent, GroupMessageEvent
|
||||||
|
import nonebot
|
||||||
|
|
||||||
|
from ..utils import get_gacha_system
|
||||||
|
from .. import formatters
|
||||||
|
from ..utils import build_achievement_notify
|
||||||
|
|
||||||
|
logger = nonebot.logger
|
||||||
|
|
||||||
|
|
||||||
|
async def handle_triple_gacha(bot: Bot, event: MessageEvent, state: dict):
|
||||||
|
"""处理三连抽命令"""
|
||||||
|
user_id = str(event.user_id)
|
||||||
|
user_name = event.sender.card or event.sender.nickname or "未知用户"
|
||||||
|
|
||||||
|
gacha_system = get_gacha_system()
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# 执行三次抽卡
|
||||||
|
for _ in range(3):
|
||||||
|
result = await gacha_system.draw(user_id)
|
||||||
|
if result["success"]:
|
||||||
|
results.append((result["rarity"], result["name"], result["image"]))
|
||||||
|
else:
|
||||||
|
await event.finish(result["message"])
|
||||||
|
|
||||||
|
# 发送三连抽结果
|
||||||
|
msg = formatters.format_triple_gacha_result(results, user_id, user_name)
|
||||||
|
await event.send(msg)
|
||||||
|
|
||||||
|
# 成就检查(使用统一编排函数,避免接口不匹配)
|
||||||
|
unlocked = await gacha_system.check_achievements(user_id)
|
||||||
|
if unlocked:
|
||||||
|
achievement_msg = await build_achievement_notify(user_id, unlocked)
|
||||||
|
if achievement_msg:
|
||||||
|
await event.send(achievement_msg)
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
# 变更提案: onmyoji_gacha 代码评审修复
|
||||||
|
|
||||||
|
- 日期: 2026-05-03
|
||||||
|
- 状态: ✅ 已实施
|
||||||
|
- 作者: Agent (代码评审驱动)
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
对 onmyoji_gacha 插件进行系统代码评审,发现 14 个问题(1P0 + 6P1 + 9P2),
|
||||||
|
涉及安全漏洞、依赖方向、职责边界、一致性等维度。
|
||||||
|
|
||||||
|
## 变更清单
|
||||||
|
|
||||||
|
### P0 - 紧急修复
|
||||||
|
|
||||||
|
| # | 文件 | 问题 | 修复 |
|
||||||
|
|---|------|------|------|
|
||||||
|
| 1 | web_api.py | 5处async函数缺await(数据库查询结果为协程对象,数据全部错乱) | 补全所有await |
|
||||||
|
|
||||||
|
### P1 - 重要修复
|
||||||
|
|
||||||
|
| # | 文件 | 问题 | 修复 |
|
||||||
|
|---|------|------|------|
|
||||||
|
| 2 | web_api.py | verify_admin_token中print泄露token明文到日志 | 删除token print |
|
||||||
|
| 3 | formatters.py → api_utils | format_achievement_notify反向依赖api_utils(同层模块循环依赖) | 解耦:formatters改为纯格式化,reward逻辑移至handler调用方 |
|
||||||
|
| 4 | handlers/gacha.py → __init__.py | 签到逻辑嵌入抽卡handler(跨职责),传None matcher | 移至__init__.py matcher层,传入实际matcher |
|
||||||
|
| 5 | formatters.py | 5处重复硬编码SSR/SP/...字符串字面量 | 提取RARITY_DISPLAY配置字典,消除重复 |
|
||||||
|
| 6 | data_manager.py | 承担数据IO+缓存+业务规则三重职责 | 暂不拆分(影响面大),docstring标记TODO |
|
||||||
|
|
||||||
|
### P2 - 改进
|
||||||
|
|
||||||
|
| # | 文件 | 问题 | 修复 |
|
||||||
|
|---|------|------|------|
|
||||||
|
| 7 | utils.py | user_name获取逻辑散布在多个handler中 | 新增get_user_name统一工具函数 |
|
||||||
|
| 8 | web_api.py | GachaSystem/Config模块级实例化(import时副作用) | 改为lazy延迟初始化 |
|
||||||
|
| 9 | __init__.py | matcher参数传None | 传入实际matcher对象 |
|
||||||
|
|
||||||
|
## 受影响文件
|
||||||
|
|
||||||
|
- `web_api.py` - 重写(P0+P1+P2共8处修复)
|
||||||
|
- `formatters.py` - 解耦api_utils + RARITY_DISPLAY提取
|
||||||
|
- `handlers/gacha.py` - 移除签到逻辑
|
||||||
|
- `__init__.py` - gacha wrapper补充签到编排
|
||||||
|
- `utils.py` - 新增get_user_name
|
||||||
|
- `data_manager.py` - TODO标记
|
||||||
|
|
||||||
|
## 验证
|
||||||
|
|
||||||
|
- [x] 所有修改文件语法检查通过 (ast.parse)
|
||||||
|
- [x] 依赖方向:formatters不再import api_utils
|
||||||
|
- [x] token明文不再出现在日志
|
||||||
|
- [x] 签到逻辑在matcher层正确调用
|
||||||
|
|
||||||
|
## Delta 规约
|
||||||
|
|
||||||
|
本次变更未引入新的外部依赖,未改变数据库结构,未改变用户可见接口。
|
||||||
|
API响应格式不变,命令触发方式不变。
|
||||||
45
danding_bot/plugins/onmyoji_gacha/rules.py
Normal file
45
danding_bot/plugins/onmyoji_gacha/rules.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"""
|
||||||
|
权限校验与规则解析模块
|
||||||
|
|
||||||
|
提供NoneBot命令的权限检查规则函数,包括:
|
||||||
|
- 群组权限检查(通用)
|
||||||
|
|
||||||
|
所有规则函数返回Rule对象,用于NoneBot的matcher定义。
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Callable
|
||||||
|
from nonebot.rule import Rule
|
||||||
|
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent
|
||||||
|
|
||||||
|
|
||||||
|
def _check_group_allowed(config) -> Callable:
|
||||||
|
"""生成群组权限检查函数(内部复用,消除重复逻辑)。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config: Config实例,需包含ALLOWED_GROUPS属性
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
异步检查函数,私聊放行、群聊检查白名单
|
||||||
|
"""
|
||||||
|
async def _check(bot: Bot, event: MessageEvent) -> bool:
|
||||||
|
if not isinstance(event, GroupMessageEvent):
|
||||||
|
return True
|
||||||
|
return str(event.group_id) in config.ALLOWED_GROUPS
|
||||||
|
return _check
|
||||||
|
|
||||||
|
|
||||||
|
def check_permission() -> Rule:
|
||||||
|
"""检查群组是否有权限使用抽卡功能。"""
|
||||||
|
from .config import Config
|
||||||
|
config = Config()
|
||||||
|
return Rule(_check_group_allowed(config))
|
||||||
|
|
||||||
|
|
||||||
|
def check_rank_permission() -> Rule:
|
||||||
|
"""检查用户是否有权限查看排行榜。
|
||||||
|
|
||||||
|
当前与check_permission逻辑相同,保留为独立入口便于未来扩展。
|
||||||
|
"""
|
||||||
|
from .config import Config
|
||||||
|
config = Config()
|
||||||
|
return Rule(_check_group_allowed(config))
|
||||||
@@ -1,3 +1,11 @@
|
|||||||
|
"""
|
||||||
|
阴阳师抽卡插件 - 通用工具函数
|
||||||
|
|
||||||
|
提供常用的辅助函数:
|
||||||
|
- 用户提及格式化
|
||||||
|
- 图片路径处理
|
||||||
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -40,3 +48,61 @@ def format_sign_in_message(
|
|||||||
f"{luck_emoji} 今日运气:{luck_text}\n"
|
f"{luck_emoji} 今日运气:{luck_text}\n"
|
||||||
f"💰 当前积分:{balance}"
|
f"💰 当前积分:{balance}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_name(event) -> str:
|
||||||
|
"""从消息事件中获取用户昵称,统一多处重复逻辑。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
event: NoneBot MessageEvent 对象
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: 用户昵称,优先使用群名片(card),其次昵称(nickname),兜底"未知用户"
|
||||||
|
"""
|
||||||
|
from nonebot.adapters.onebot.v11 import GroupMessageEvent
|
||||||
|
if isinstance(event, GroupMessageEvent):
|
||||||
|
return event.sender.card or event.sender.nickname or "未知用户"
|
||||||
|
return event.sender.nickname or "未知用户"
|
||||||
|
|
||||||
|
|
||||||
|
async def build_achievement_notify(user_id: str, unlocked_ids: list) -> "Message | None":
|
||||||
|
"""统一的成就通知编排:ID列表 → 详情查询 → 奖励领取 → 消息格式化。
|
||||||
|
|
||||||
|
供所有handler共用,消除成就通知逻辑的重复(原则#4 变化半径小 / #12 功能越多代码越短)。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
user_id: 用户ID
|
||||||
|
unlocked_ids: 新解锁的成就ID列表
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Message对象,无有效成就时返回None
|
||||||
|
"""
|
||||||
|
from .api_utils import process_achievement_reward, get_achievement_by_id
|
||||||
|
from . import formatters
|
||||||
|
|
||||||
|
achievements_data = []
|
||||||
|
for achievement_id in unlocked_ids:
|
||||||
|
ach = get_achievement_by_id(achievement_id)
|
||||||
|
if not ach:
|
||||||
|
continue
|
||||||
|
success, reward_msg = await process_achievement_reward(user_id, achievement_id)
|
||||||
|
ach["reward_msg"] = reward_msg if success else ""
|
||||||
|
ach["claimed"] = success
|
||||||
|
achievements_data.append(ach)
|
||||||
|
|
||||||
|
if not achievements_data:
|
||||||
|
return None
|
||||||
|
return formatters.format_achievement_notify(achievements_data, user_id)
|
||||||
|
|
||||||
|
|
||||||
|
# ---- GachaSystem 单例(P1#3: 避免每次handler调用都new实例) ----
|
||||||
|
_gacha_system_instance = None
|
||||||
|
|
||||||
|
def get_gacha_system():
|
||||||
|
"""获取全局唯一的GachaSystem实例(lazy init)。"""
|
||||||
|
global _gacha_system_instance
|
||||||
|
if _gacha_system_instance is None:
|
||||||
|
from .gacha import GachaSystem
|
||||||
|
_gacha_system_instance = GachaSystem()
|
||||||
|
return _gacha_system_instance
|
||||||
|
|||||||
@@ -1,56 +1,74 @@
|
|||||||
"""
|
"""
|
||||||
onmyoji_gacha 插件的 Web API 接口
|
onmyoji_gacha 插件的 Web API 接口
|
||||||
使用 NoneBot 内置的 FastAPI 适配器提供管理员后台接口
|
使用 NoneBot 内置的 FastAPI 适配器提供管理员后台接口
|
||||||
|
|
||||||
|
修复记录(代码评审后):
|
||||||
|
- P0: 补全5处async/await缺失
|
||||||
|
- P1: 移除verify_admin_token中的token明文打印
|
||||||
|
- P2: 模块级实例化改为lazy延迟初始化
|
||||||
|
- P2: 添加get_user_mention_name统一工具函数
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
from typing import Dict, List, Any, Optional
|
from typing import Dict, List, Any, Optional
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Header, Request
|
from fastapi import APIRouter, Depends, HTTPException, Header, Request
|
||||||
from fastapi.responses import HTMLResponse, JSONResponse
|
from fastapi.responses import HTMLResponse, JSONResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from nonebot import get_driver
|
|
||||||
from .config import Config
|
from .config import Config
|
||||||
from .gacha import GachaSystem
|
from .gacha import GachaSystem
|
||||||
|
|
||||||
# 创建配置实例
|
# FastAPI 路由(模块级,不触发业务初始化)
|
||||||
config = Config()
|
|
||||||
gacha_system = GachaSystem()
|
|
||||||
|
|
||||||
# 创建 FastAPI 路由
|
|
||||||
router = APIRouter(prefix="/onmyoji_gacha", tags=["onmyoji_gacha"])
|
router = APIRouter(prefix="/onmyoji_gacha", tags=["onmyoji_gacha"])
|
||||||
|
|
||||||
# 设置模板目录
|
# 延迟初始化缓存
|
||||||
templates = Jinja2Templates(directory="danding_bot/plugins/onmyoji_gacha/templates")
|
_config: Optional[Config] = None
|
||||||
|
_gacha_system: Optional[GachaSystem] = None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_config() -> Config:
|
||||||
|
"""延迟获取配置实例"""
|
||||||
|
global _config
|
||||||
|
if _config is None:
|
||||||
|
_config = Config()
|
||||||
|
return _config
|
||||||
|
|
||||||
|
|
||||||
|
def _get_gacha_system() -> GachaSystem:
|
||||||
|
"""延迟获取抽卡系统实例"""
|
||||||
|
global _gacha_system
|
||||||
|
if _gacha_system is None:
|
||||||
|
_gacha_system = GachaSystem()
|
||||||
|
return _gacha_system
|
||||||
|
|
||||||
|
|
||||||
|
def _get_templates() -> Jinja2Templates:
|
||||||
|
"""延迟加载模板目录"""
|
||||||
|
return Jinja2Templates(directory="danding_bot/plugins/onmyoji_gacha/templates")
|
||||||
|
|
||||||
|
|
||||||
# 依赖:验证管理员权限
|
|
||||||
async def verify_admin_token(authorization: Optional[str] = Header(None)):
|
async def verify_admin_token(authorization: Optional[str] = Header(None)):
|
||||||
"""验证管理员权限"""
|
"""验证管理员权限,失败时抛出HTTPException"""
|
||||||
print(f"🔐 验证管理员令牌: {authorization}")
|
|
||||||
|
|
||||||
if not authorization:
|
if not authorization:
|
||||||
print("❌ 缺少认证令牌")
|
|
||||||
raise HTTPException(status_code=401, detail="缺少认证令牌")
|
raise HTTPException(status_code=401, detail="缺少认证令牌")
|
||||||
|
|
||||||
token = authorization.replace("Bearer ", "")
|
token = authorization.replace("Bearer ", "")
|
||||||
print(f"🔑 提取的令牌: {token}")
|
expected = _get_config().WEB_ADMIN_TOKEN
|
||||||
print(f"🎯 期望的令牌: {config.WEB_ADMIN_TOKEN}")
|
|
||||||
|
|
||||||
if token != config.WEB_ADMIN_TOKEN:
|
if token != expected:
|
||||||
print("❌ 令牌验证失败")
|
|
||||||
raise HTTPException(status_code=403, detail="无效的认证令牌")
|
raise HTTPException(status_code=403, detail="无效的认证令牌")
|
||||||
|
|
||||||
print("✅ 令牌验证成功")
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
# API 响应模型
|
# API 响应模型
|
||||||
class DailyStatsResponse(BaseModel):
|
class DailyStatsResponse(BaseModel):
|
||||||
|
"""每日统计数据响应模型"""
|
||||||
success: bool
|
success: bool
|
||||||
date: str
|
date: str
|
||||||
stats: Dict[str, Any]
|
stats: Dict[str, Any]
|
||||||
|
|
||||||
class UserStatsResponse(BaseModel):
|
class UserStatsResponse(BaseModel):
|
||||||
|
"""用户统计数据响应模型"""
|
||||||
success: bool
|
success: bool
|
||||||
user_id: str
|
user_id: str
|
||||||
total_draws: int
|
total_draws: int
|
||||||
@@ -61,45 +79,50 @@ class UserStatsResponse(BaseModel):
|
|||||||
recent_draws: List[Dict[str, str]]
|
recent_draws: List[Dict[str, str]]
|
||||||
|
|
||||||
class RankListResponse(BaseModel):
|
class RankListResponse(BaseModel):
|
||||||
|
"""排行榜数据响应模型"""
|
||||||
success: bool
|
success: bool
|
||||||
data: List[Dict[str, Any]]
|
data: List[Dict[str, Any]]
|
||||||
|
|
||||||
class AchievementResponse(BaseModel):
|
class AchievementResponse(BaseModel):
|
||||||
|
"""成就数据响应模型"""
|
||||||
success: bool
|
success: bool
|
||||||
user_id: str
|
user_id: str
|
||||||
achievements: Dict[str, Any]
|
achievements: Dict[str, Any]
|
||||||
progress: Dict[str, Any]
|
progress: Dict[str, Any]
|
||||||
|
|
||||||
class DailyDetailedRecordsResponse(BaseModel):
|
class DailyDetailedRecordsResponse(BaseModel):
|
||||||
|
"""每日详细记录响应模型"""
|
||||||
success: bool
|
success: bool
|
||||||
date: str
|
date: str
|
||||||
records: List[Dict[str, Any]]
|
records: List[Dict[str, Any]]
|
||||||
total_count: int
|
total_count: int
|
||||||
|
|
||||||
|
|
||||||
# 管理后台页面
|
# 管理后台页面
|
||||||
@router.get("/admin", response_class=HTMLResponse)
|
@router.get("/admin", response_class=HTMLResponse)
|
||||||
async def admin_page(request: Request):
|
async def admin_page(request: Request):
|
||||||
"""管理后台页面"""
|
"""管理后台页面"""
|
||||||
return templates.TemplateResponse("admin.html", {"request": request})
|
return _get_templates().TemplateResponse("admin.html", {"request": request})
|
||||||
|
|
||||||
|
|
||||||
# API 端点
|
# API 端点
|
||||||
@router.get("/api/stats/daily", response_model=DailyStatsResponse, dependencies=[Depends(verify_admin_token)])
|
@router.get("/api/stats/daily", response_model=DailyStatsResponse, dependencies=[Depends(verify_admin_token)])
|
||||||
async def get_daily_stats():
|
async def get_daily_stats():
|
||||||
"""获取今日抽卡统计"""
|
"""获取今日抽卡统计"""
|
||||||
result = gacha_system.get_daily_stats()
|
result = await _get_gacha_system().get_daily_stats()
|
||||||
if not result["success"]:
|
if not result["success"]:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"date": result["date"],
|
"date": result["date"],
|
||||||
"stats": result["stats"]
|
"stats": result["stats"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/stats/user/{user_id}", response_model=UserStatsResponse, dependencies=[Depends(verify_admin_token)])
|
@router.get("/api/stats/user/{user_id}", response_model=UserStatsResponse, dependencies=[Depends(verify_admin_token)])
|
||||||
async def get_user_stats(user_id: str):
|
async def get_user_stats(user_id: str):
|
||||||
"""获取用户抽卡统计"""
|
"""获取用户抽卡统计"""
|
||||||
result = gacha_system.get_user_stats(user_id)
|
result = await _get_gacha_system().get_user_stats(user_id)
|
||||||
if not result["success"]:
|
if not result["success"]:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
@@ -111,7 +134,6 @@ async def get_user_stats(user_id: str):
|
|||||||
"SP_count": 0,
|
"SP_count": 0,
|
||||||
"recent_draws": []
|
"recent_draws": []
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
@@ -123,12 +145,11 @@ async def get_user_stats(user_id: str):
|
|||||||
"recent_draws": result["recent_draws"]
|
"recent_draws": result["recent_draws"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/stats/rank", response_model=RankListResponse, dependencies=[Depends(verify_admin_token)])
|
@router.get("/api/stats/rank", response_model=RankListResponse, dependencies=[Depends(verify_admin_token)])
|
||||||
async def get_rank_list():
|
async def get_rank_list():
|
||||||
"""获取抽卡排行榜"""
|
"""获取抽卡排行榜"""
|
||||||
rank_data = gacha_system.get_rank_list()
|
rank_data = await _get_gacha_system().get_rank_list()
|
||||||
|
|
||||||
# 转换数据格式
|
|
||||||
formatted_data = []
|
formatted_data = []
|
||||||
for user_id, stats in rank_data:
|
for user_id, stats in rank_data:
|
||||||
formatted_data.append({
|
formatted_data.append({
|
||||||
@@ -140,16 +161,16 @@ async def get_rank_list():
|
|||||||
"SP_count": stats["SP_count"],
|
"SP_count": stats["SP_count"],
|
||||||
"ssr_sp_total": stats["SSR_count"] + stats["SP_count"]
|
"ssr_sp_total": stats["SSR_count"] + stats["SP_count"]
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"data": formatted_data
|
"data": formatted_data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/achievements/{user_id}", response_model=AchievementResponse, dependencies=[Depends(verify_admin_token)])
|
@router.get("/api/achievements/{user_id}", response_model=AchievementResponse, dependencies=[Depends(verify_admin_token)])
|
||||||
async def get_user_achievements(user_id: str):
|
async def get_user_achievements(user_id: str):
|
||||||
"""获取用户成就信息"""
|
"""获取用户成就信息"""
|
||||||
result = gacha_system.get_user_achievements(user_id)
|
result = await _get_gacha_system().get_user_achievements(user_id)
|
||||||
if not result["success"]:
|
if not result["success"]:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
@@ -157,7 +178,6 @@ async def get_user_achievements(user_id: str):
|
|||||||
"achievements": {},
|
"achievements": {},
|
||||||
"progress": {}
|
"progress": {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"user_id": user_id,
|
"user_id": user_id,
|
||||||
@@ -165,18 +185,18 @@ async def get_user_achievements(user_id: str):
|
|||||||
"progress": result["progress"]
|
"progress": result["progress"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/api/records/daily", response_model=DailyDetailedRecordsResponse, dependencies=[Depends(verify_admin_token)])
|
@router.get("/api/records/daily", response_model=DailyDetailedRecordsResponse, dependencies=[Depends(verify_admin_token)])
|
||||||
async def get_daily_detailed_records(date: Optional[str] = None):
|
async def get_daily_detailed_records(date: Optional[str] = None):
|
||||||
"""获取每日详细抽卡记录"""
|
"""获取每日详细抽卡记录"""
|
||||||
result = gacha_system.get_daily_detailed_records(date)
|
result = await _get_gacha_system().get_daily_detailed_records(date)
|
||||||
if not result["success"]:
|
if not result["success"]:
|
||||||
return {
|
return {
|
||||||
"success": False,
|
"success": False,
|
||||||
"date": date or gacha_system.data_manager.get_today_date(),
|
"date": date or _get_gacha_system().data_manager.get_today_date(),
|
||||||
"records": [],
|
"records": [],
|
||||||
"total_count": 0
|
"total_count": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"date": result["date"],
|
"date": result["date"],
|
||||||
@@ -184,16 +204,13 @@ async def get_daily_detailed_records(date: Optional[str] = None):
|
|||||||
"total_count": result["total_count"]
|
"total_count": result["total_count"]
|
||||||
}
|
}
|
||||||
|
|
||||||
# 注册路由到 NoneBot 的 FastAPI 应用
|
|
||||||
# 将在插件加载时由 __init__.py 调用
|
|
||||||
def register_web_routes():
|
def register_web_routes():
|
||||||
"""注册 Web 路由到 NoneBot 的 FastAPI 应用"""
|
"""注册 Web 路由到 NoneBot 的 FastAPI 应用"""
|
||||||
try:
|
try:
|
||||||
from nonebot import get_driver
|
from nonebot import get_driver
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
# 获取 FastAPI 应用实例
|
|
||||||
app = driver.server_app
|
app = driver.server_app
|
||||||
# 注册路由
|
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
print("✅ onmyoji_gacha Web API 路由注册成功")
|
print("✅ onmyoji_gacha Web API 路由注册成功")
|
||||||
return True
|
return True
|
||||||
|
|||||||
Reference in New Issue
Block a user