Files
DanDingNoneBot/danding_bot/plugins/group_horse_racing/commands/race.py
Mr.Xia fe081f43cf fix(race): 代码质量审查修复 + commands包拆分 + 赛马取消命令
- P1: bet.py赔率计算移入锁内防竞态
- P1: config.py TESTERS解析失败添加warning日志
- P2: 新增赛马取消命令(积分退还/任务取消/状态重置)
- P3: bet.py清理未使用的_send_to_scope导入
- 将commands.py拆分为commands/包(access/bet/help/race/register)
- OpenSpec变更提案: fix-race-conditions-and-logs
2026-05-02 14:33:34 +08:00

163 lines
5.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

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

import asyncio
from nonebot import on_command
from nonebot.adapters.onebot.v11 import Bot, Event
from . import (
room_store, race_engine, config, logger,
get_scope, check_access, get_event_id,
_send_to_scope, _build_race_image_message,
run_race_with_settlement, points_service,
)
from ..models import RoomState, HorseState
from nonebot.adapters.onebot.v11 import GroupMessageEvent
from ..models import RoomState
@race_list_cmd.handle()
async def handle_race_list(bot: Bot, event: Event):
"""显示当前房间所有报名马匹信息。"""
if not await check_access(bot, event):
await race_list_cmd.finish("无权限访问此功能")
return
scope = get_scope(event)
room = room_store.get_room(scope)
if not room or not room.horses:
await race_list_cmd.finish("暂无报名马匹")
return
lines = ["🏇 当前报名马匹:"]
for horse in _get_horses_in_order(room):
owner_display = await _get_user_name(bot, scope, horse.owner_id)
lines.append(f" {horse.index}. {horse.name} - 主人: {owner_display}")
lines.append(f"\n{len(room.horses)} 匹马")
await race_list_cmd.finish("\n".join(lines))
start_cmd = on_command("赛马开赛", priority=5)
@start_cmd.handle()
async def handle_start(bot: Bot, event: Event):
"""Handle race start - only participants or admins can start."""
if not await check_access(bot, event):
await start_cmd.finish("无权限访问此功能")
return
scope = get_scope(event)
user_id = get_event_id(event)
lock = room_store.get_lock(scope)
async with lock:
room = room_store.get_room(scope)
if not room:
await start_cmd.finish("房间不存在,请先报名")
return
if room.state != RoomState.WAITING:
await start_cmd.finish("比赛已经在进行中")
return
if len(room.horses) < 2:
await start_cmd.finish("至少需要2匹马才能开赛")
return
# 开赛权限限制仅参赛者或群管理员可手动开赛满8匹自动开赛不受影响
is_participant = user_id in [h.owner_id for h in room.horses.values()]
is_admin = False
if isinstance(event, GroupMessageEvent):
try:
member_info = await bot.get_group_member_info(
group_id=event.group_id,
user_id=int(user_id)
)
role = member_info.get("role", "")
is_admin = role in ("admin", "owner")
except Exception:
pass
if not is_participant and not is_admin:
await start_cmd.finish("只有参赛者或群管理员可以开赛")
return
# Set all horses to racing state
for horse in room.horses.values():
horse.state = HorseState.RACING
await start_cmd.send("比赛开始!")
# Run race in background (outside command handler)
task = asyncio.create_task(run_race_with_settlement(bot, room, scope))
race_engine.register_task(scope, task)
cancel_race_cmd = on_command("赛马取消", priority=5)
@cancel_race_cmd.handle()
async def handle_cancel_race(bot: Bot, event: Event):
"""取消当前进行的比赛,退还所有下注积分。仅参赛者或管理员可操作。"""
if not await check_access(bot, event):
await cancel_race_cmd.finish("无权限访问此功能")
return
scope = get_scope(event)
user_id = get_event_id(event)
lock = room_store.get_lock(scope)
async with lock:
room = room_store.get_room(scope)
if not room:
await cancel_race_cmd.finish("房间不存在")
return
if room.state != RoomState.RACING:
await cancel_race_cmd.finish("当前没有进行中的比赛")
return
# 权限:只有参赛者或群管理员可以取消
is_participant = user_id in [h.owner_id for h in room.horses.values()]
is_admin = False
if isinstance(event, GroupMessageEvent):
try:
member_info = await bot.get_group_member_info(
group_id=event.group_id,
user_id=int(user_id)
)
role = member_info.get("role", "")
is_admin = role in ("admin", "owner")
except Exception:
pass
if not is_participant and not is_admin:
await cancel_race_cmd.finish("只有参赛者或群管理员可以取消比赛")
return
# 停止后台比赛任务
race_engine.stop_race(scope)
# 退还所有下注积分
total_refund = 0
for bet in room.bets[:]: # 遍历副本
success, _ = await points_service.refund_bet_points(
bet.user_id, bet.amount, "比赛取退还下注"
)
if success:
total_refund += bet.amount
# 清空下注记录
room.bets.clear()
# 重置马匹状态为等待
for horse in room.horses.values():
horse.state = HorseState.WAITING
# 重置房间状态
room.state = RoomState.WAITING
room.tick_count = 0
await _send_to_scope(scope, f"🏇 比赛已取消!共退还 {total_refund} 积分。")
help_cmd = on_command("赛马帮助", priority=5)