【积分商店】onmyoji_gacha 新增积分商店与兑换抽卡命令

This commit is contained in:
2026-08-15 20:50:38 +08:00
parent cb94794e38
commit 7f3f744af5
6 changed files with 331 additions and 160 deletions

View File

@@ -1,20 +1,20 @@
import os
import logging
import random
from nonebot import get_driver, on_command, on_startswith
import os
import logging
import random
from nonebot import get_driver, on_command, on_startswith
from nonebot.adapters.onebot.v11 import Bot, GroupMessageEvent, MessageEvent, Message
from nonebot.adapters.onebot.v11.message import MessageSegment
from nonebot.typing import T_State
from nonebot.rule import Rule
from pathlib import Path
from .config import Config
from .gacha import GachaSystem
from .utils import format_sign_in_message, format_user_mention, get_image_path
from .sign_in import award_daily_sign_in_points
from .api_utils import process_ssr_sp_reward, process_achievement_reward
from . import web_api
from danding_bot.plugins.danding_points import points_api
from .config import Config
from .gacha import GachaSystem
from .utils import format_sign_in_message, format_user_mention, get_image_path
from .sign_in import award_daily_sign_in_points
from .api_utils import process_ssr_sp_reward, process_achievement_reward
from . import web_api
from danding_bot.plugins.danding_points import points_api
# 创建Config实例
config = Config()
@@ -28,24 +28,26 @@ DAILY_STATS_COMMANDS = config.DAILY_STATS_COMMANDS
TRIPLE_GACHA_COMMANDS = config.TRIPLE_GACHA_COMMANDS
ACHIEVEMENT_COMMANDS = config.ACHIEVEMENT_COMMANDS
INTRO_COMMANDS = config.INTRO_COMMANDS
SHOP_COMMANDS = config.SHOP_COMMANDS
EXCHANGE_COMMANDS = config.EXCHANGE_COMMANDS
DAILY_LIMIT = config.DAILY_LIMIT
gacha_system = GachaSystem()
logger = logging.getLogger(__name__)
gacha_system = GachaSystem()
logger = logging.getLogger(__name__)
SIGN_IN_MIN_POINTS = 1
SIGN_IN_MAX_POINTS = 100
SIGN_IN_SOURCE = "gacha_sign"
SIGN_IN_REASON = "抽卡签到"
@get_driver().on_startup
async def load_gacha_shikigami_data() -> None:
"""启动时从 xapi 拉取式神基础数据缓存。"""
try:
await gacha_system.data_manager.refresh_shikigami_data()
except Exception:
logger.exception("启动拉取抽卡式神缓存失败")
SIGN_IN_REASON = "抽卡签到"
@get_driver().on_startup
async def load_gacha_shikigami_data() -> None:
"""启动时从 xapi 拉取式神基础数据缓存。"""
try:
await gacha_system.data_manager.refresh_shikigami_data()
except Exception:
logger.exception("启动拉取抽卡式神缓存失败")
# 检查是否允许使用功能的规则
def check_permission() -> Rule:
@@ -63,38 +65,38 @@ def check_permission() -> Rule:
return Rule(_checker)
async def try_handle_daily_sign_in(matcher, user_id: str, user_name: str) -> None:
"""处理抽卡成功后的每日签到,不影响主流程"""
try:
points = random.randint(SIGN_IN_MIN_POINTS, SIGN_IN_MAX_POINTS)
success, new_balance, status = await award_daily_sign_in_points(
data_manager=gacha_system.data_manager,
points_api=points_api,
user_id=user_id,
points=points,
source=SIGN_IN_SOURCE,
reason=SIGN_IN_REASON,
)
if not success:
if status == "points_failed":
logger.error("抽卡签到记录成功但积分发放失败 user_id=%s points=%s", user_id, points)
else:
logger.info("抽卡签到未发放积分 user_id=%s status=%s", user_id, status)
return
await matcher.send(format_sign_in_message(user_id, user_name, points, new_balance))
except Exception:
logger.exception("处理抽卡签到失败 user_id=%s", user_id)
async def claim_achievement_after_reward(user_id: str, achievement_id: str, reward_success: bool) -> None:
"""成就奖励发放成功后标记 xapi reward_claimed。"""
if not reward_success:
return
claimed = await gacha_system.data_manager.claim_achievement_reward(user_id, achievement_id)
if not claimed:
logger.warning("成就奖励已发放但 claim 标记失败 user_id=%s achievement_id=%s", user_id, achievement_id)
async def try_handle_daily_sign_in(matcher, user_id: str, user_name: str) -> None:
"""处理抽卡成功后的每日签到,不影响主流程"""
try:
points = random.randint(SIGN_IN_MIN_POINTS, SIGN_IN_MAX_POINTS)
success, new_balance, status = await award_daily_sign_in_points(
data_manager=gacha_system.data_manager,
points_api=points_api,
user_id=user_id,
points=points,
source=SIGN_IN_SOURCE,
reason=SIGN_IN_REASON,
)
if not success:
if status == "points_failed":
logger.error("抽卡签到记录成功但积分发放失败 user_id=%s points=%s", user_id, points)
else:
logger.info("抽卡签到未发放积分 user_id=%s status=%s", user_id, status)
return
await matcher.send(format_sign_in_message(user_id, user_name, points, new_balance))
except Exception:
logger.exception("处理抽卡签到失败 user_id=%s", user_id)
async def claim_achievement_after_reward(user_id: str, achievement_id: str, reward_success: bool) -> None:
"""成就奖励发放成功后标记 xapi reward_claimed。"""
if not reward_success:
return
claimed = await gacha_system.data_manager.claim_achievement_reward(user_id, achievement_id)
if not claimed:
logger.warning("成就奖励已发放但 claim 标记失败 user_id=%s achievement_id=%s", user_id, achievement_id)
# 注册抽卡命令,添加权限检查规则
gacha_matcher = on_command("抽卡", aliases=set(GACHA_COMMANDS), priority=10, rule=check_permission())
@@ -105,7 +107,7 @@ async def handle_gacha(bot: Bot, event: MessageEvent, state: T_State):
user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
# 执行抽卡
result = await gacha_system.draw(user_id)
result = await gacha_system.draw(user_id)
if not result["success"]:
await gacha_matcher.finish(format_user_mention(user_id, user_name) + "" + result["message"])
@@ -146,9 +148,9 @@ async def handle_gacha(bot: Bot, event: MessageEvent, state: T_State):
has_manual_rewards = False
for achievement_id in unlocked_achievements:
# 尝试自动发放成就奖励
auto_success, reward_msg = await process_achievement_reward(user_id, achievement_id)
await claim_achievement_after_reward(user_id, achievement_id, auto_success)
# 尝试自动发放成就奖励
auto_success, reward_msg = await process_achievement_reward(user_id, achievement_id)
await claim_achievement_after_reward(user_id, achievement_id, auto_success)
# 检查是否是重复奖励
if "_repeat_" in achievement_id:
@@ -181,7 +183,7 @@ async def handle_gacha(bot: Bot, event: MessageEvent, state: T_State):
msg.append("💰 未自动发放的奖励请联系管理员\n")
# 添加成就进度提示
achievement_data = await gacha_system.get_user_achievements(user_id)
achievement_data = await gacha_system.get_user_achievements(user_id)
if achievement_data["success"]:
progress = achievement_data["progress"]
consecutive_days = progress.get("consecutive_days", 0)
@@ -222,7 +224,7 @@ async def handle_gacha(bot: Bot, event: MessageEvent, state: T_State):
msg.append(f"💔 已达月见黑级别:{no_ssr_streak}次 🌚\n")
# 添加剩余次数和概率信息
msg.append(f"\n📊 今日剩余抽卡次数:{draws_left}/{DAILY_LIMIT}\n")
msg.append(f"\n📊 今日剩余抽卡次数:{draws_left}\n")
msg.append(gacha_system.get_probability_text())
# 如果抽到了SSR或SP处理奖励发放
@@ -246,13 +248,13 @@ async def handle_gacha(bot: Bot, event: MessageEvent, state: T_State):
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:
logger.debug("通知管理员失败: %s", e)
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:
logger.debug("通知管理员失败: %s", e)
# 注册查询命令,添加权限检查规则
stats_matcher = on_command("我的抽卡", aliases=set(STATS_COMMANDS), priority=5, rule=check_permission())
@@ -272,7 +274,7 @@ async def handle_stats(bot: Bot, event: MessageEvent, state: T_State):
user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
# 获取用户统计
stats = await gacha_system.get_user_stats(user_id)
stats = await gacha_system.get_user_stats(user_id)
if not stats["success"]:
await stats_matcher.finish(format_user_mention(user_id, user_name) + " " + stats["message"])
@@ -314,7 +316,7 @@ async def handle_triple_gacha(bot: Bot, event: MessageEvent, state: T_State):
user_name = event.sender.card or event.sender.nickname or "未知用户"
# 执行三连抽
result = await gacha_system.triple_draw(user_id)
result = await gacha_system.triple_draw(user_id)
if not result["success"]:
await triple_gacha_matcher.finish(f"{result['message']}")
@@ -358,9 +360,9 @@ async def handle_triple_gacha(bot: Bot, event: MessageEvent, state: T_State):
has_manual_rewards = False
for achievement_id in unlocked_achievements:
# 尝试自动发放成就奖励
auto_success, reward_msg = await process_achievement_reward(user_id, achievement_id)
await claim_achievement_after_reward(user_id, achievement_id, auto_success)
# 尝试自动发放成就奖励
auto_success, reward_msg = await process_achievement_reward(user_id, achievement_id)
await claim_achievement_after_reward(user_id, achievement_id, auto_success)
# 检查是否是重复奖励
if "_repeat_" in achievement_id:
@@ -393,7 +395,7 @@ async def handle_triple_gacha(bot: Bot, event: MessageEvent, state: T_State):
msg.append("💰 未自动发放的奖励请联系管理员\n")
# 添加成就进度提示
achievement_data = await gacha_system.get_user_achievements(user_id)
achievement_data = await gacha_system.get_user_achievements(user_id)
if achievement_data["success"]:
progress = achievement_data["progress"]
consecutive_days = progress.get("consecutive_days", 0)
@@ -435,7 +437,7 @@ async def handle_triple_gacha(bot: Bot, event: MessageEvent, state: T_State):
# 添加剩余次数
draws_left = result["draws_left"]
msg.append(f"\n📊 今日剩余抽卡次数:{draws_left}/{DAILY_LIMIT}")
msg.append(f"\n📊 今日剩余抽卡次数:{draws_left}")
# 如果抽到SSR/SP处理奖励发放
if ssr_count > 0:
@@ -463,7 +465,7 @@ async def handle_achievement(bot: Bot, event: MessageEvent, state: T_State):
user_name = event.sender.card or event.sender.nickname or "未知用户"
# 获取用户成就信息
result = await gacha_system.get_user_achievements(user_id)
result = await gacha_system.get_user_achievements(user_id)
if not result["success"]:
await achievement_matcher.finish(f"{result['message']}")
@@ -547,6 +549,61 @@ async def handle_achievement(bot: Bot, event: MessageEvent, state: T_State):
await achievement_matcher.finish(msg)
# 注册积分商店命令
shop_matcher = on_command("积分商店", aliases=set(SHOP_COMMANDS), priority=5, rule=check_permission())
@shop_matcher.handle()
async def handle_shop(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
data = await gacha_system.data_manager.get_shop_products(user_id)
if data is None:
await shop_matcher.finish("❌ 积分商店暂时不可用,请稍后再试")
products = data.get("products", [])
msg = Message()
msg.append(f"🛒 {format_user_mention(user_id, user_name)} 的积分商店:\n\n")
msg.append(f"💰 Bot积分余额{data.get('balance', 0)}\n")
msg.append(
f"🎲 今日剩余抽卡:{data.get('draws_left', 0)}"
f"(其中积分兑换 {data.get('extra_draws_today', 0)} 次)\n\n"
)
msg.append("📦 在售商品:\n")
for product in products:
msg.append(f"{product.get('name')}{product.get('price_points', 0)} 积分\n")
msg.append(f" {product.get('description', '')}\n")
if products:
msg.append(f"\n发送「{EXCHANGE_COMMANDS[0]}」即可兑换")
await shop_matcher.finish(msg)
# 注册兑换命令
shop_exchange_matcher = on_command("兑换抽卡", aliases=set(EXCHANGE_COMMANDS), priority=5, rule=check_permission())
@shop_exchange_matcher.handle()
async def handle_shop_exchange(bot: Bot, event: MessageEvent, state: T_State):
"""用 Bot 积分兑换一次当日抽卡机会(当前目录唯一商品 extra_draw"""
user_id = str(event.user_id)
user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
result = await gacha_system.data_manager.exchange_shop_product(user_id, "extra_draw")
if result is None:
await shop_exchange_matcher.finish("❌ 兑换服务暂时不可用,请稍后再试")
if not result.get("success"):
await shop_exchange_matcher.finish(
format_user_mention(user_id, user_name) + "" + str(result.get("message", "兑换失败"))
)
msg = Message()
msg.append(f"{format_user_mention(user_id, user_name)} 兑换成功!\n\n")
msg.append("🎁 获得:抽卡机会 ×1今日有效\n")
msg.append(f"💰 积分余额:{result.get('balance', 0)}\n")
msg.append(f"📊 今日剩余抽卡:{result.get('draws_left', 0)}\n")
msg.append(f"\n发送「{GACHA_COMMANDS[0]}」立即使用吧~")
await shop_exchange_matcher.finish(msg)
# 注册查询抽卡指令,支持@用户查询功能
query_matcher = on_command("查询抽卡", aliases={"查询抽奖"}, priority=5, rule=check_permission())
@@ -582,7 +639,7 @@ async def handle_query(bot: Bot, event: MessageEvent, state: T_State):
target_user_name = event.sender.card if isinstance(event, GroupMessageEvent) else event.sender.nickname
# 获取用户统计
stats = await gacha_system.get_user_stats(target_user_id)
stats = await gacha_system.get_user_stats(target_user_id)
# 构建响应消息
msg = Message()
@@ -644,7 +701,7 @@ rank_matcher = on_startswith(("抽卡排行","抽卡榜"), priority=1, rule=chec
@rank_matcher.handle()
async def handle_rank(bot: Bot, event: MessageEvent, state: T_State):
# 获取排行榜数据
rank_data = await gacha_system.get_rank_list()
rank_data = await gacha_system.get_rank_list()
if not rank_data:
await rank_matcher.finish("暂无抽卡排行榜数据")
@@ -689,7 +746,7 @@ async def handle_rank(bot: Bot, event: MessageEvent, state: T_State):
@daily_stats_matcher.handle()
async def handle_daily_stats(bot: Bot, event: MessageEvent, state: T_State):
"""处理今日抽卡统计命令"""
result = await gacha_system.get_daily_stats()
result = await gacha_system.get_daily_stats()
if not result["success"]:
await daily_stats_matcher.finish(f"{result['message']}")
@@ -768,6 +825,8 @@ async def handle_intro(bot: Bot, event: MessageEvent, state: T_State):
msg += f"• 查询成就:{', '.join(ACHIEVEMENT_COMMANDS)}\n"
msg += "• 查询抽卡/查询抽奖:查询自己的或@他人的抽卡数据\n"
msg += "• 抽卡排行/抽卡榜查看SSR/SP排行榜\n"
msg += f"• 积分商店:查看可兑换商品({'/'.join(SHOP_COMMANDS)}\n"
msg += f"• 兑换抽卡:用积分兑换额外抽卡机会({'/'.join(EXCHANGE_COMMANDS)}\n"
msg += "\n"
# 成就系统