feat(赛马): 为马匹添加序号并优化积分结算展示
- 为 Horse 模型添加 index 字段,用于唯一标识马匹序号 - 在报名时自动分配递增序号,并在所有展示中使用固定序号排序 - 新增积分变化计算功能,在比赛结果中展示每位用户的积分变化和总结描述 - 支持通过序号或马匹名下注,优化用户交互体验 - 添加用户上次马名记忆功能,允许重复使用马名报名 - 更新测试用例以验证序号展示和积分变化功能
This commit is contained in:
@@ -84,6 +84,108 @@ def get_event_id(event: Event) -> str:
|
||||
return str(event.user_id)
|
||||
|
||||
|
||||
def _normalize_horse_name(horse_name: str) -> str:
|
||||
return horse_name.strip().casefold()
|
||||
|
||||
|
||||
def _get_horses_in_order(room: Room) -> list[Horse]:
|
||||
return sorted(room.horses.values(), key=lambda horse: horse.index)
|
||||
|
||||
|
||||
def _format_horse_label(horse: Horse) -> str:
|
||||
return f"{horse.index:02d}号 {horse.name}"
|
||||
|
||||
|
||||
def _find_user_horse(room: Room, user_id: str) -> Horse | None:
|
||||
for horse in _get_horses_in_order(room):
|
||||
if horse.owner_id == user_id:
|
||||
return horse
|
||||
return None
|
||||
|
||||
|
||||
def _find_duplicate_horse(room: Room, horse_name: str) -> Horse | None:
|
||||
normalized_name = _normalize_horse_name(horse_name)
|
||||
for horse in room.horses.values():
|
||||
if _normalize_horse_name(horse.name) == normalized_name:
|
||||
return horse
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_horse_selector(room: Room, selector: str) -> Horse | None:
|
||||
selector = selector.strip()
|
||||
if selector.isdigit():
|
||||
target_index = int(selector)
|
||||
for horse in room.horses.values():
|
||||
if horse.index == target_index:
|
||||
return horse
|
||||
return None
|
||||
|
||||
normalized_selector = _normalize_horse_name(selector)
|
||||
for horse in room.horses.values():
|
||||
if _normalize_horse_name(horse.name) == normalized_selector:
|
||||
return horse
|
||||
return None
|
||||
|
||||
|
||||
def _describe_points_delta(delta: int) -> str:
|
||||
if delta >= 300:
|
||||
return "血赚翻倍"
|
||||
if delta >= 150:
|
||||
return "大赚特赚"
|
||||
if delta > 0:
|
||||
return "小有收获"
|
||||
if delta == 0:
|
||||
return "稳住不亏"
|
||||
if delta <= -300:
|
||||
return "倾家荡产"
|
||||
if delta <= -150:
|
||||
return "伤筋动骨"
|
||||
return "略有损失"
|
||||
|
||||
|
||||
def _build_point_changes(room: Room, odds: dict[str, float]) -> tuple[dict[str, int], dict[str, str]]:
|
||||
point_changes: dict[str, int] = {}
|
||||
|
||||
for bet in room.bets:
|
||||
point_changes[bet.user_id] = point_changes.get(bet.user_id, 0) - bet.amount
|
||||
|
||||
champion = room.horses.get(room.champion_name)
|
||||
if champion:
|
||||
point_changes[champion.owner_id] = point_changes.get(champion.owner_id, 0) + config.CHAMPION_REWARD
|
||||
|
||||
for horse in room.horses.values():
|
||||
if champion and horse.owner_id != champion.owner_id:
|
||||
point_changes[horse.owner_id] = point_changes.get(horse.owner_id, 0) + config.PARTICIPANT_REWARD
|
||||
|
||||
for bet in room.bets:
|
||||
if bet.horse_name == room.champion_name:
|
||||
payout = int(bet.amount * odds.get(bet.horse_name, config.MIN_ODDS))
|
||||
point_changes[bet.user_id] = point_changes.get(bet.user_id, 0) + payout
|
||||
|
||||
point_summaries = {
|
||||
user_id: _describe_points_delta(delta)
|
||||
for user_id, delta in point_changes.items()
|
||||
}
|
||||
return point_changes, point_summaries
|
||||
|
||||
|
||||
def _format_point_change_lines(room: Room, point_changes: dict[str, int], point_summaries: dict[str, str]) -> list[str]:
|
||||
ordered_user_ids: list[str] = []
|
||||
for horse in _get_horses_in_order(room):
|
||||
if horse.owner_id not in ordered_user_ids:
|
||||
ordered_user_ids.append(horse.owner_id)
|
||||
for bet in room.bets:
|
||||
if bet.user_id not in ordered_user_ids:
|
||||
ordered_user_ids.append(bet.user_id)
|
||||
|
||||
lines = ["积分变化:"]
|
||||
for user_id in ordered_user_ids:
|
||||
delta = point_changes.get(user_id, 0)
|
||||
summary = point_summaries.get(user_id, _describe_points_delta(delta))
|
||||
lines.append(f" {user_id} {delta:+d} 积分 · {summary}")
|
||||
return lines
|
||||
|
||||
|
||||
def calculate_odds(room: Room) -> dict[str, float]:
|
||||
"""Calculate odds for each horse based on bet distribution."""
|
||||
total_bet = sum(b.amount for b in room.bets)
|
||||
@@ -98,11 +200,11 @@ def calculate_odds(room: Room) -> dict[str, float]:
|
||||
return odds
|
||||
|
||||
|
||||
async def settle_race(room: Room):
|
||||
async def settle_race(room: Room) -> RaceResult | None:
|
||||
"""Settle bets and rewards after race finishes."""
|
||||
champion = room.horses.get(room.champion_name)
|
||||
if not champion:
|
||||
return
|
||||
return None
|
||||
|
||||
# Reward champion owner
|
||||
await points_service.reward_champion(champion.owner_id)
|
||||
@@ -119,17 +221,21 @@ async def settle_race(room: Room):
|
||||
await points_service.payout_winnings(bet.user_id, bet.amount, odds.get(bet.horse_name, config.MIN_ODDS))
|
||||
|
||||
# Save race result
|
||||
point_changes, point_summaries = _build_point_changes(room, odds)
|
||||
result = RaceResult(
|
||||
race_id=str(uuid.uuid4()),
|
||||
scope=room.scope,
|
||||
champion_name=champion.name,
|
||||
champion_owner=champion.owner_id,
|
||||
participants=[h.name for h in room.horses.values()],
|
||||
participants=[h.name for h in _get_horses_in_order(room)],
|
||||
bet_distribution={name: sum(b.amount for b in room.bets if b.horse_name == name) for name in room.horses},
|
||||
duration_ticks=room.tick_count,
|
||||
completed_at=datetime.now(),
|
||||
point_changes=point_changes,
|
||||
point_change_summaries=point_summaries,
|
||||
)
|
||||
room_store.save_race_result(result)
|
||||
return result
|
||||
|
||||
|
||||
async def _send_to_scope(bot: Bot, scope: str, message: str):
|
||||
@@ -152,8 +258,7 @@ async def run_race_with_settlement(bot: Bot, room: Room, scope: str):
|
||||
"""Run race with live progress updates and settlement."""
|
||||
room.state = RoomState.RUNNING
|
||||
|
||||
# Send start message with horse list
|
||||
horse_list = "\n".join(f" {h.name}" for h in room.horses.values())
|
||||
horse_list = "\n".join(f" {_format_horse_label(h)}" for h in _get_horses_in_order(room))
|
||||
await _send_to_scope(bot, scope, f"比赛开始!\n{horse_list}")
|
||||
|
||||
# Race loop with progress updates
|
||||
@@ -177,13 +282,12 @@ async def run_race_with_settlement(bot: Bot, room: Room, scope: str):
|
||||
return
|
||||
|
||||
# Settle
|
||||
await settle_race(room)
|
||||
result = await settle_race(room)
|
||||
|
||||
# Build result message
|
||||
odds = calculate_odds(room)
|
||||
champion = room.horses.get(room.champion_name)
|
||||
result_lines = [
|
||||
f"比赛结束!冠军:{room.champion_name}",
|
||||
f"比赛结束!冠军:{_format_horse_label(champion) if champion else room.champion_name}",
|
||||
f"马主 {champion.owner_id if champion else '?'} 获得 {config.CHAMPION_REWARD} 积分",
|
||||
]
|
||||
winning_bets = [b for b in room.bets if b.horse_name == room.champion_name]
|
||||
@@ -192,6 +296,8 @@ async def run_race_with_settlement(bot: Bot, room: Room, scope: str):
|
||||
for b in winning_bets:
|
||||
payout = int(b.amount * odds.get(b.horse_name, config.MIN_ODDS))
|
||||
result_lines.append(f" {b.user_id} 下注 {b.amount} -> 获得 {payout}")
|
||||
if result:
|
||||
result_lines.extend(_format_point_change_lines(room, result.point_changes, result.point_change_summaries))
|
||||
|
||||
await _send_to_scope(bot, scope, "\n".join(result_lines))
|
||||
|
||||
@@ -213,13 +319,13 @@ async def handle_register(bot: Bot, event: Event):
|
||||
await register_cmd.finish("无权限访问此功能")
|
||||
return
|
||||
|
||||
# Parse horse name from message
|
||||
msg = str(event.get_message()).strip()
|
||||
parts = msg.split(None, 1)
|
||||
horse_name = parts[1].strip() if len(parts) > 1 else ""
|
||||
user_id = get_event_id(event)
|
||||
horse_name = parts[1].strip() if len(parts) > 1 else room_store.get_last_horse_name(user_id) or ""
|
||||
|
||||
if not horse_name:
|
||||
await register_cmd.finish("请输入马匹名:/赛马报名 <马匹名>")
|
||||
await register_cmd.finish("请输入马匹名:/赛马报名 <马匹名>。若你之前报过名,也可以直接发送 /赛马报名 复用上一次绑定的马名")
|
||||
return
|
||||
|
||||
if len(horse_name) > 10:
|
||||
@@ -227,7 +333,6 @@ async def handle_register(bot: Bot, event: Event):
|
||||
return
|
||||
|
||||
scope = get_scope(event)
|
||||
user_id = get_event_id(event)
|
||||
lock = room_store.get_lock(scope)
|
||||
|
||||
async with lock:
|
||||
@@ -243,21 +348,24 @@ async def handle_register(bot: Bot, event: Event):
|
||||
await register_cmd.finish("房间已满(最多8匹马)")
|
||||
return
|
||||
|
||||
if horse_name in room.horses:
|
||||
await register_cmd.finish(f"马匹名 \"{horse_name}\" 已被使用")
|
||||
duplicate_horse = _find_duplicate_horse(room, horse_name)
|
||||
if duplicate_horse:
|
||||
await register_cmd.finish(f"马匹名 \"{horse_name}\" 已被 {_format_horse_label(duplicate_horse)} 使用")
|
||||
return
|
||||
|
||||
# Check if user already registered
|
||||
for h in room.horses.values():
|
||||
if h.owner_id == user_id:
|
||||
await register_cmd.finish("你已经报名了,不能重复报名")
|
||||
existing_user_horse = _find_user_horse(room, user_id)
|
||||
if existing_user_horse:
|
||||
await register_cmd.finish(f"你已经报名了,当前马匹为 {_format_horse_label(existing_user_horse)}")
|
||||
return
|
||||
|
||||
# Create horse
|
||||
room.horses[horse_name] = Horse(owner_id=user_id, name=horse_name)
|
||||
horse_index = room.next_horse_index
|
||||
room.next_horse_index += 1
|
||||
room.horses[horse_name] = Horse(owner_id=user_id, name=horse_name, index=horse_index)
|
||||
room_store.set_last_horse_name(user_id, horse_name)
|
||||
|
||||
count = len(room.horses)
|
||||
await register_cmd.finish(f"报名成功!马匹 \"{horse_name}\" 已加入比赛({count}/8)")
|
||||
registered_horse = room.horses[horse_name]
|
||||
await register_cmd.finish(f"报名成功!{_format_horse_label(registered_horse)} 已加入比赛({count}/8)")
|
||||
|
||||
|
||||
cancel_cmd = on_command("赛马取消报名", priority=5)
|
||||
@@ -284,27 +392,19 @@ async def handle_cancel(bot: Bot, event: Event):
|
||||
await cancel_cmd.finish("比赛正在进行中,无法取消报名")
|
||||
return
|
||||
|
||||
# Find user's horse
|
||||
user_horse = None
|
||||
for name, horse in room.horses.items():
|
||||
if horse.owner_id == user_id:
|
||||
user_horse = name
|
||||
break
|
||||
|
||||
user_horse = _find_user_horse(room, user_id)
|
||||
if not user_horse:
|
||||
await cancel_cmd.finish("你还没有报名")
|
||||
return
|
||||
|
||||
# Refund bets on this horse
|
||||
bets_to_refund = [b for b in room.bets if b.horse_name == user_horse]
|
||||
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]
|
||||
room.bets = [b for b in room.bets if b.horse_name != user_horse.name]
|
||||
|
||||
# Remove horse
|
||||
del room.horses[user_horse]
|
||||
del room.horses[user_horse.name]
|
||||
|
||||
await cancel_cmd.finish(f"已取消报名,马匹 \"{user_horse}\" 已退出")
|
||||
await cancel_cmd.finish(f"已取消报名,{_format_horse_label(user_horse)} 已退出")
|
||||
|
||||
|
||||
bet_cmd = on_command("赛马下注", priority=5)
|
||||
@@ -317,14 +417,13 @@ async def handle_bet(bot: Bot, event: Event):
|
||||
await bet_cmd.finish("无权限访问此功能")
|
||||
return
|
||||
|
||||
# Parse arguments: /赛马下注 <马匹名> <金额>
|
||||
msg = str(event.get_message()).strip()
|
||||
parts = msg.split()
|
||||
if len(parts) < 3:
|
||||
await bet_cmd.finish("请使用:/赛马下注 <马匹名> <金额>")
|
||||
await bet_cmd.finish("请使用:/赛马下注 <序号|马匹名> <金额>")
|
||||
return
|
||||
|
||||
horse_name = parts[1]
|
||||
horse_selector = parts[1]
|
||||
try:
|
||||
amount = int(parts[2])
|
||||
except ValueError:
|
||||
@@ -349,26 +448,24 @@ async def handle_bet(bot: Bot, event: Event):
|
||||
await bet_cmd.finish("比赛正在进行中,无法下注")
|
||||
return
|
||||
|
||||
if horse_name not in room.horses:
|
||||
await bet_cmd.finish(f"马匹 \"{horse_name}\" 不存在")
|
||||
horse = _resolve_horse_selector(room, horse_selector)
|
||||
if not horse:
|
||||
await bet_cmd.finish(f"马匹序号/名称 \"{horse_selector}\" 不存在")
|
||||
return
|
||||
|
||||
# Can't bet on your own horse
|
||||
if room.horses[horse_name].owner_id == user_id:
|
||||
if horse.owner_id == user_id:
|
||||
await bet_cmd.finish("不能给自己的马下注")
|
||||
return
|
||||
|
||||
# Deduct points first
|
||||
success, balance = await points_service.spend_bet_points(user_id, amount, f"下注 {horse_name}")
|
||||
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
|
||||
|
||||
# Record bet
|
||||
room.bets.append(Bet(user_id=user_id, horse_name=horse_name, amount=amount))
|
||||
room.bets.append(Bet(user_id=user_id, horse_name=horse.name, amount=amount))
|
||||
|
||||
odds = calculate_odds(room)
|
||||
await bet_cmd.finish(f"下注成功!{horse_name} {amount}积分(当前赔率:{odds.get(horse_name, config.MIN_ODDS):.2f})")
|
||||
await bet_cmd.finish(f"下注成功!{_format_horse_label(horse)} {amount}积分(当前赔率:{odds.get(horse.name, config.MIN_ODDS):.2f})")
|
||||
|
||||
|
||||
odds_cmd = on_command("赛马赔率", priority=5)
|
||||
@@ -394,9 +491,10 @@ async def handle_odds(bot: Bot, event: Event):
|
||||
odds = calculate_odds(room)
|
||||
lines = ["当前赔率:"]
|
||||
total_bet = sum(b.amount for b in room.bets)
|
||||
for name, odd in odds.items():
|
||||
horse_bet = sum(b.amount for b in room.bets if b.horse_name == name)
|
||||
lines.append(f" {name} - {odd:.2f}倍 (总下注: {horse_bet})")
|
||||
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))
|
||||
@@ -449,7 +547,8 @@ async def handle_help(bot: Bot, event: Event):
|
||||
help_text = """赛马命令帮助:
|
||||
/赛马报名 <马匹名> - 报名参赛
|
||||
/赛马取消报名 - 取消报名并退还下注
|
||||
/赛马下注 <马匹名> <金额> - 下注(不能给自己的马下注)
|
||||
/赛马报名 - 复用上一次绑定的马名报名
|
||||
/赛马下注 <序号|马匹名> <金额> - 下注(不能给自己的马下注)
|
||||
/赛马赔率 - 查看当前赔率
|
||||
/赛马开赛 - 开始比赛(至少2匹马)
|
||||
/赛马帮助 - 显示此帮助"""
|
||||
|
||||
@@ -21,6 +21,7 @@ class HorseState(str, Enum):
|
||||
class Horse:
|
||||
owner_id: str
|
||||
name: str
|
||||
index: int = 0
|
||||
position: float = 0.0
|
||||
state: HorseState = HorseState.READY
|
||||
|
||||
@@ -41,6 +42,7 @@ class Room:
|
||||
bets: list[Bet] = field(default_factory=list)
|
||||
champion_name: Optional[str] = None
|
||||
tick_count: int = 0
|
||||
next_horse_index: int = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -53,3 +55,5 @@ class RaceResult:
|
||||
bet_distribution: dict[str, int]
|
||||
duration_ticks: int
|
||||
completed_at: datetime
|
||||
point_changes: dict[str, int] = field(default_factory=dict)
|
||||
point_change_summaries: dict[str, str] = field(default_factory=dict)
|
||||
|
||||
@@ -57,12 +57,11 @@ class RaceEngine:
|
||||
bar_width = 20
|
||||
lines = [f"【第{room.tick_count}回合】"]
|
||||
|
||||
# Sort by position descending for readability
|
||||
sorted_horses = sorted(room.horses.values(), key=lambda h: h.position, reverse=True)
|
||||
sorted_horses = sorted(room.horses.values(), key=lambda h: h.index)
|
||||
for horse in sorted_horses:
|
||||
progress = min(horse.position / distance, 1.0)
|
||||
filled = int(progress * bar_width)
|
||||
bar = "█" * filled + "░" * (bar_width - filled)
|
||||
lines.append(f" {horse.name:<6} |{bar}| {horse.position:.1f}m")
|
||||
lines.append(f" {horse.index:02d}号 {horse.name} |{bar}| {horse.position:.1f}m")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
@@ -13,6 +13,7 @@ class RoomStore:
|
||||
self.config = config
|
||||
self.rooms: dict[str, Room] = {}
|
||||
self._locks: dict[str, asyncio.Lock] = {}
|
||||
self._last_horse_names: dict[str, str] = {}
|
||||
self.db_path = Path(config.RACE_DB_FILE)
|
||||
self.db_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
self._init_db()
|
||||
@@ -72,6 +73,12 @@ class RoomStore:
|
||||
if scope in self.rooms:
|
||||
del self.rooms[scope]
|
||||
|
||||
def get_last_horse_name(self, user_id: str) -> Optional[str]:
|
||||
return self._last_horse_names.get(user_id)
|
||||
|
||||
def set_last_horse_name(self, user_id: str, horse_name: str):
|
||||
self._last_horse_names[user_id] = horse_name
|
||||
|
||||
def _save_snapshot(self, room: Room):
|
||||
"""Save room snapshot to database."""
|
||||
import json
|
||||
@@ -80,6 +87,7 @@ class RoomStore:
|
||||
name: {
|
||||
"owner_id": horse.owner_id,
|
||||
"name": horse.name,
|
||||
"index": horse.index,
|
||||
"position": horse.position,
|
||||
"state": horse.state.value,
|
||||
}
|
||||
|
||||
@@ -258,7 +258,8 @@ async def handle_test_simulate_race(bot: Bot, event: Event):
|
||||
horse_names = _generate_random_horse_names(8)
|
||||
for idx, horse_name in enumerate(horse_names, start=1):
|
||||
owner_id = f"sim_user_{idx}"
|
||||
room.horses[horse_name] = Horse(owner_id=owner_id, name=horse_name, state=HorseState.RACING)
|
||||
room.horses[horse_name] = Horse(owner_id=owner_id, name=horse_name, index=idx, state=HorseState.RACING)
|
||||
room.next_horse_index = len(horse_names) + 1
|
||||
|
||||
bet_amount = max(commands_mod.config.MIN_BET, 10)
|
||||
room.bets.append(Bet(user_id="bettor_1", horse_name=horse_names[0], amount=bet_amount))
|
||||
@@ -282,15 +283,31 @@ async def handle_test_simulate_race(bot: Bot, event: Event):
|
||||
if "比赛开始!" not in messages[0]:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:未发送开赛消息")
|
||||
return
|
||||
for idx, horse_name in enumerate(horse_names, start=1):
|
||||
if f"{idx:02d}号 {horse_name}" not in messages[0]:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:开赛名单未按序号展示")
|
||||
return
|
||||
|
||||
if not any("【第" in msg and "回合】" in msg for msg in messages):
|
||||
progress_messages = [msg for msg in messages if "【第" in msg and "回合】" in msg]
|
||||
if not progress_messages:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:未发送回合进度消息")
|
||||
return
|
||||
progress_lines = progress_messages[0].splitlines()[1:]
|
||||
if len(progress_lines) != len(horse_names):
|
||||
await test_simulate_race_cmd.send("完全模拟失败:回合进度展示条目数量不匹配")
|
||||
return
|
||||
for idx, line in enumerate(progress_lines, start=1):
|
||||
if not line.strip().startswith(f"{idx:02d}号 "):
|
||||
await test_simulate_race_cmd.send("完全模拟失败:回合进度未按报名序号固定排序")
|
||||
return
|
||||
|
||||
result_msg = messages[-1]
|
||||
if "比赛结束!冠军:" not in result_msg:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:未发送结束结算消息")
|
||||
return
|
||||
if "积分变化:" not in result_msg:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:未发送积分变化总结")
|
||||
return
|
||||
|
||||
if not fake_room_store.saved_results:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:未写入赛史结果(内存)")
|
||||
@@ -300,6 +317,12 @@ async def handle_test_simulate_race(bot: Bot, event: Event):
|
||||
if saved.champion_name not in room.horses:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:赛史冠军不在参赛马匹中")
|
||||
return
|
||||
if not saved.point_changes:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:赛史未记录积分变化")
|
||||
return
|
||||
if not saved.point_change_summaries:
|
||||
await test_simulate_race_cmd.send("完全模拟失败:赛史未记录积分变化总结")
|
||||
return
|
||||
|
||||
champion_owner_id = room.horses[saved.champion_name].owner_id
|
||||
reward_champion_calls = [c for c in fake_points_service.calls if c[0] == "reward_champion"]
|
||||
@@ -321,6 +344,7 @@ async def handle_test_simulate_race(bot: Bot, event: Event):
|
||||
f"总回合:{saved.duration_ticks}",
|
||||
f"消息条数:{len(messages)}(开赛/进度/结算均已覆盖)",
|
||||
f"结算调用:{len(fake_points_service.calls)}(冠军/参赛/下注派奖)",
|
||||
f"积分变化用户数:{len(saved.point_changes)}",
|
||||
f"过程展示:{'开启' if stream_progress else '关闭'}",
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user