Extract shared.py from commands/__init__.py to break circular dependency: - shared.py: shared variables/services/helper functions - access.py: get_scope/check_access/get_event_id (canonical source) - __init__.py: re-exports from shared.py for backward compat - register/bet/race/help: import from .shared instead of package
187 lines
6.5 KiB
Python
187 lines
6.5 KiB
Python
from nonebot import on_command
|
||
from nonebot.adapters.onebot.v11 import Bot, Event
|
||
from .shared import (
|
||
room_store, points_service, config, logger,
|
||
get_scope, check_access, get_event_id,
|
||
_resolve_horse_selector, _format_horse_label,
|
||
_get_horses_in_order, _find_user_horse,
|
||
calculate_odds,
|
||
)
|
||
from ..models import RoomState, Bet
|
||
|
||
cancel_cmd = on_command("赛马取消报名", priority=5)
|
||
|
||
|
||
@cancel_cmd.handle()
|
||
async def handle_cancel(bot: Bot, event: Event):
|
||
"""Handle cancel registration."""
|
||
if not await check_access(bot, event):
|
||
await cancel_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_cmd.finish("房间不存在")
|
||
return
|
||
|
||
if room.state != RoomState.WAITING:
|
||
await cancel_cmd.finish("比赛正在进行中,无法取消报名")
|
||
return
|
||
|
||
user_horse = _find_user_horse(room, user_id)
|
||
if not user_horse:
|
||
await cancel_cmd.finish("你还没有报名")
|
||
return
|
||
|
||
bets_to_refund = [b for b in room.bets if b.horse_name == user_horse.name]
|
||
for bet in bets_to_refund:
|
||
await points_service.refund_bet_points(bet.user_id, bet.amount, "取消报名退还下注")
|
||
room.bets = [b for b in room.bets if b.horse_name != user_horse.name]
|
||
|
||
del room.horses[user_horse.name]
|
||
|
||
await cancel_cmd.finish(f"已取消报名,{_format_horse_label(user_horse)} 已退出")
|
||
|
||
|
||
bet_cmd = on_command("赛马下注", priority=5)
|
||
|
||
|
||
cancel_bet_cmd = on_command("赛马取消下注", priority=5)
|
||
|
||
|
||
@cancel_bet_cmd.handle()
|
||
async def handle_cancel_bet(bot: Bot, event: Event):
|
||
"""Handle cancel bet - refund all bets placed by the user in current room."""
|
||
if not await check_access(bot, event):
|
||
await cancel_bet_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_bet_cmd.finish("房间不存在")
|
||
return
|
||
|
||
if room.state != RoomState.WAITING:
|
||
await cancel_bet_cmd.finish("比赛已开始,无法取消下注")
|
||
return
|
||
|
||
user_bets = [b for b in room.bets if b.user_id == user_id]
|
||
if not user_bets:
|
||
await cancel_bet_cmd.finish("你还没有下注")
|
||
return
|
||
|
||
total_refund = 0
|
||
refund_errors = []
|
||
for bet in user_bets:
|
||
try:
|
||
await points_service.refund_bet_points(bet.user_id, bet.amount, "取消下注退还")
|
||
total_refund += bet.amount
|
||
except Exception as e:
|
||
logger.error(f"退还下注失败 user={bet.user_id} amount={bet.amount}: {e}")
|
||
refund_errors.append(bet)
|
||
|
||
if refund_errors:
|
||
failed_amount = sum(b.amount for b in refund_errors)
|
||
room.bets = [b for b in room.bets if b.user_id != user_id or b in refund_errors]
|
||
await cancel_bet_cmd.finish(f"退还部分失败:成功退还 {total_refund} 积分,{len(refund_errors)} 笔退还失败({failed_amount} 积分),请联系管理员")
|
||
return
|
||
else:
|
||
room.bets = [b for b in room.bets if b.user_id != user_id]
|
||
|
||
await cancel_bet_cmd.finish(f"已取消 {len(user_bets)} 笔下注,退还 {total_refund} 积分")
|
||
|
||
|
||
@bet_cmd.handle()
|
||
async def handle_bet(bot: Bot, event: Event):
|
||
"""Handle bet placement."""
|
||
if not await check_access(bot, event):
|
||
await bet_cmd.finish("无权限访问此功能")
|
||
return
|
||
|
||
msg = str(event.get_message()).strip()
|
||
parts = msg.split()
|
||
if len(parts) < 3:
|
||
await bet_cmd.finish("请使用:/赛马下注 <序号|马匹名> <金额>")
|
||
return
|
||
|
||
horse_selector = parts[1]
|
||
try:
|
||
amount = int(parts[2])
|
||
except ValueError:
|
||
await bet_cmd.finish("金额必须是正整数")
|
||
return
|
||
|
||
if amount < config.MIN_BET:
|
||
await bet_cmd.finish(f"最低下注金额为 {config.MIN_BET}")
|
||
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 bet_cmd.finish("房间不存在,请先报名")
|
||
return
|
||
|
||
if room.state != RoomState.WAITING:
|
||
await bet_cmd.finish("比赛正在进行中,无法下注")
|
||
return
|
||
|
||
horse = _resolve_horse_selector(room, horse_selector)
|
||
if not horse:
|
||
await bet_cmd.finish(f"马匹序号/名称 \"{horse_selector}\" 不存在")
|
||
return
|
||
|
||
success, balance = await points_service.spend_bet_points(user_id, amount, f"下注 {_format_horse_label(horse)}")
|
||
if not success:
|
||
await bet_cmd.finish(f"积分不足(当前余额:{balance})")
|
||
return
|
||
|
||
room.bets.append(Bet(user_id=user_id, horse_name=horse.name, amount=amount))
|
||
odds = calculate_odds(room)
|
||
await bet_cmd.finish(f"下注成功!{_format_horse_label(horse)} {amount}积分(当前赔率:{odds.get(horse.name, config.MIN_ODDS):.2f})")
|
||
|
||
|
||
odds_cmd = on_command("赛马赔率", priority=5)
|
||
|
||
|
||
@odds_cmd.handle()
|
||
async def handle_odds(bot: Bot, event: Event):
|
||
"""Handle odds display."""
|
||
if not await check_access(bot, event):
|
||
await odds_cmd.finish("无权限访问此功能")
|
||
return
|
||
|
||
scope = get_scope(event)
|
||
room = room_store.get_room(scope)
|
||
if not room:
|
||
await odds_cmd.finish("房间不存在,请先报名")
|
||
return
|
||
|
||
if not room.horses:
|
||
await odds_cmd.finish("还没有马匹报名")
|
||
return
|
||
|
||
odds = calculate_odds(room)
|
||
lines = ["当前赔率:"]
|
||
total_bet = sum(b.amount for b in room.bets)
|
||
for horse in _get_horses_in_order(room):
|
||
odd = odds.get(horse.name, config.MIN_ODDS)
|
||
horse_bet = sum(b.amount for b in room.bets if b.horse_name == horse.name)
|
||
lines.append(f" {_format_horse_label(horse)} - {odd:.2f}倍 (总下注: {horse_bet})")
|
||
lines.append(f"总下注池: {total_bet}")
|
||
|
||
await odds_cmd.finish("\n".join(lines))
|