refactor: onmyoji gacha plugin overhaul (gacha-refactor)

This commit is contained in:
2026-05-03 09:55:15 +08:00
parent 9a8cb3ad6d
commit 0312c79c9d
20 changed files with 2699 additions and 2450 deletions

View 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