feat(horse-racing): 新增赛马列表/取消下注/开赛权限限制 + 修复退还异常保护 + 文档同步
This commit is contained in:
@@ -1,215 +1,223 @@
|
|||||||
# Group Horse Racing 插件
|
# Group Horse Racing 插件
|
||||||
|
|
||||||
群赛马插件 - 一个支持群组内多人参与的赛马游戏,集成积分系统和下注功能。
|
群赛马插件 - 一个支持群组内多人参与的赛马游戏,集成积分系统和下注功能。
|
||||||
|
|
||||||
## 功能概述
|
## 功能概述
|
||||||
|
|
||||||
这是一个完整的群组赛马游戏系统,支持以下核心功能:
|
这是一个完整的群组赛马游戏系统,支持以下核心功能:
|
||||||
|
|
||||||
- **马匹报名**:用户可以报名参加赛马比赛
|
- **马匹报名**:用户可以报名参加赛马比赛
|
||||||
- **下注系统**:支持用户对任意参赛马匹进行下注(含自己的马)
|
- **下注系统**:支持用户对任意参赛马匹进行下注(含自己的马)
|
||||||
- **自动比赛**:基于随机算法的赛马进程模拟
|
- **自动比赛**:基于随机算法的赛马进程模拟
|
||||||
- **积分集成**:与 danding_points 积分系统无缝集成
|
- **积分集成**:与 danding_points 积分系统无缝集成
|
||||||
- **自动撤回**:支持配置消息自动撤回时间
|
- **自动撤回**:支持配置消息自动撤回时间
|
||||||
- **多房间支持**:支持群组和私聊两种模式
|
- **多房间支持**:支持群组和私聊两种模式
|
||||||
|
|
||||||
## 命令列表
|
## 命令列表
|
||||||
|
|
||||||
### 基础命令
|
### 基础命令
|
||||||
|
|
||||||
| 命令 | 说明 | 示例 |
|
| 命令 | 说明 | 示例 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `/赛马报名 <马匹名>` | 报名参加赛马比赛。若不输入则复用上次马名或使用昵称 | `/赛马报名 绝地赤兔` |
|
| `/赛马报名 <马匹名>` | 报名参加赛马比赛。若不输入则复用上次马名或使用昵称 | `/赛马报名 绝地赤兔` |
|
||||||
| `/赛马取消报名` | 取消报名并退还下注(限开始前) | `/赛马取消报名` |
|
| `/赛马取消报名` | 取消报名并退还下注(限开始前) | `/赛马取消报名` |
|
||||||
| `/赛马下注 <序号|马名> <金额>` | 为马匹下注 | `/赛马下注 01 100` |
|
| `/赛马下注 <序号|马名> <金额>` | 为马匹下注 | `/赛马下注 01 100` |
|
||||||
| `/赛马开赛` | 开始比赛(需要至少2匹马) | `/赛马开赛` |
|
| `/赛马取消下注` | 取消本人所有下注并退还积分(限开始前) | `/赛马取消下注` |
|
||||||
| `/赛马帮助` | 显示帮助信息 | `/赛马帮助` |
|
| `/赛马赔率` | 查看当前赔率和下注池 | `/赛马赔率` |
|
||||||
|
| `/赛马列表` | 查看当前报名马匹列表 | `/赛马列表` |
|
||||||
## 配置说明
|
| `/赛马开赛` | 开始比赛(需参赛者或管理员,至少2匹马) | `/赛马开赛` |
|
||||||
|
| `/赛马帮助` | 显示帮助信息 | `/赛马帮助` |
|
||||||
### 环境变量
|
|
||||||
|
## 配置说明
|
||||||
所有配置项都可通过环境变量设置,前缀为 `GROUP_HORSE_RACING_`
|
|
||||||
|
### 环境变量
|
||||||
```python
|
|
||||||
# 测试模式
|
所有配置项都可通过环境变量设置,前缀为 `GROUP_HORSE_RACING_`
|
||||||
GROUP_HORSE_RACING_TEST_MODE=false
|
|
||||||
|
```python
|
||||||
# 测试用户ID集合
|
# 测试模式
|
||||||
GROUP_HORSE_RACING_TESTERS=[]
|
GROUP_HORSE_RACING_TEST_MODE=false
|
||||||
|
|
||||||
# 测试群组ID集合
|
# 测试用户ID集合
|
||||||
GROUP_HORSE_RACING_TEST_GROUPS=[]
|
GROUP_HORSE_RACING_TESTERS=[]
|
||||||
|
|
||||||
# 允许的群组ID集合
|
# 测试群组ID集合
|
||||||
GROUP_HORSE_RACING_ALLOWED_GROUPS=[]
|
GROUP_HORSE_RACING_TEST_GROUPS=[]
|
||||||
```
|
|
||||||
|
# 允许的群组ID集合
|
||||||
### 游戏配置
|
GROUP_HORSE_RACING_ALLOWED_GROUPS=[]
|
||||||
|
```
|
||||||
| 配置项 | 默认值 | 说明 |
|
|
||||||
|--------|--------|------|
|
### 游戏配置
|
||||||
| `PARTICIPANT_REWARD` | 50 | 参赛者奖励积分 |
|
|
||||||
| `CHAMPION_REWARD` | 200 | 冠军奖励积分 |
|
| 配置项 | 默认值 | 说明 |
|
||||||
| `MIN_BET` | 10 | 最小下注积分 |
|
|--------|--------|------|
|
||||||
| `MIN_ODDS` | 1.2 | 最小赔率 |
|
| `PARTICIPANT_REWARD` | 20 | 参赛者奖励积分 |
|
||||||
| `RACE_DISTANCE` | 100 | 比赛距离 |
|
| `CHAMPION_REWARD` | 150 | 冠军奖励积分 |
|
||||||
| `RACE_TICK_INTERVAL` | 5 | 比赛更新间隔(秒) |
|
| `MIN_BET` | 10 | 最小下注积分 |
|
||||||
|
| `MIN_ODDS` | 1.2 | 最小赔率 |
|
||||||
### 消息撤回配置
|
| `RACE_DISTANCE` | 100 | 比赛距离 |
|
||||||
|
| `RACE_TICK_INTERVAL` | 5 | 比赛更新间隔(秒) |
|
||||||
可配置不同类型消息的自动撤回时间(秒,0表示不撤回):
|
|
||||||
|
### 消息撤回配置
|
||||||
```python
|
|
||||||
MESSAGE_RECALL = {
|
可配置不同类型消息的自动撤回时间(秒,0表示不撤回):
|
||||||
"race_update": 30, # 比赛更新消息
|
|
||||||
"registration": 180, # 报名确认消息
|
```python
|
||||||
"bet_confirm": 180, # 下注确认消息
|
MESSAGE_RECALL = {
|
||||||
"cancel_confirm": 60, # 取消确认消息
|
"race_update": 30, # 比赛更新消息
|
||||||
"error": 60, # 错误消息
|
"registration": 180, # 报名确认消息
|
||||||
"race_result": 0, # 比赛结果(不撤回)
|
"bet_confirm": 180, # 下注确认消息
|
||||||
"leaderboard": 0, # 排行榜(不撤回)
|
"cancel_confirm": 60, # 取消确认消息
|
||||||
"help": 0, # 帮助信息(不撤回)
|
"error": 60, # 错误消息
|
||||||
"odds_display": 0, # 赔率显示(不撤回)
|
"race_result": 0, # 比赛结果(不撤回)
|
||||||
}
|
"leaderboard": 0, # 排行榜(不撤回)
|
||||||
```
|
"help": 0, # 帮助信息(不撤回)
|
||||||
|
"odds_display": 0, # 赔率显示(不撤回)
|
||||||
### 数据库配置
|
}
|
||||||
|
```
|
||||||
```python
|
|
||||||
RACE_DB_FILE = "data/group_horse_racing/race.db"
|
### 数据库配置
|
||||||
```
|
|
||||||
|
```python
|
||||||
## 核心模块
|
RACE_DB_FILE = "data/group_horse_racing/race.db"
|
||||||
|
```
|
||||||
### models.py
|
|
||||||
|
## 核心模块
|
||||||
定义了游戏中的数据模型:
|
|
||||||
|
### models.py
|
||||||
- **RoomState**:房间状态枚举(WAITING、RUNNING、FINISHED、INTERRUPTED)
|
|
||||||
- **HorseState**:马匹状态枚举(READY、RACING、FINISHED)
|
定义了游戏中的数据模型:
|
||||||
- **Horse**:马匹数据类,包含所有者ID、名称、位置、状态
|
|
||||||
- **Bet**:下注数据类,包含用户ID、马匹名称、下注金额
|
- **RoomState**:房间状态枚举(WAITING、RUNNING、FINISHED、INTERRUPTED)
|
||||||
- **Room**:房间数据类,管理马匹、下注、比赛状态
|
- **HorseState**:马匹状态枚举(READY、RACING、FINISHED)
|
||||||
- **RaceResult**:比赛结果数据类,记录比赛统计信息
|
- **Horse**:马匹数据类,包含所有者ID、名称、位置、状态
|
||||||
|
- **Bet**:下注数据类,包含用户ID、马匹名称、下注金额
|
||||||
### race_engine.py
|
- **Room**:房间数据类,管理马匹、下注、比赛状态
|
||||||
|
- **RaceResult**:比赛结果数据类,记录比赛统计信息
|
||||||
比赛引擎,负责比赛逻辑:
|
|
||||||
|
### race_engine.py
|
||||||
- **start_race()**:启动比赛循环
|
|
||||||
- **_race_loop()**:主比赛循环,每个tick更新马匹位置
|
比赛引擎,负责比赛逻辑:
|
||||||
- **_determine_champion()**:确定冠军(处理平局情况)
|
|
||||||
|
- **start_race()**:启动比赛循环
|
||||||
比赛采用高斯分布随机算法,每个tick马匹随机前进一定距离。
|
- **_race_loop()**:主比赛循环,每个tick更新马匹位置
|
||||||
|
- **_determine_champion()**:确定冠军(处理平局情况)
|
||||||
### points_service.py
|
|
||||||
|
比赛采用高斯分布随机算法,每个tick马匹随机前进一定距离。
|
||||||
积分服务,与 danding_points 插件集成:
|
|
||||||
|
### points_service.py
|
||||||
- **spend_bet_points()**:扣除下注积分(支持重试)
|
|
||||||
- **refund_bet_points()**:退还下注积分
|
积分服务,与 danding_points 插件集成:
|
||||||
- **payout_winnings()**:支付中奖积分
|
|
||||||
- **reward_participant()**:奖励参赛者
|
- **spend_bet_points()**:扣除下注积分(支持重试)
|
||||||
- **reward_champion()**:奖励冠军
|
- **refund_bet_points()**:退还下注积分
|
||||||
- **get_balance()**:获取用户余额
|
- **payout_winnings()**:支付中奖积分
|
||||||
|
- **reward_participant()**:奖励参赛者
|
||||||
### message_service.py
|
- **reward_champion()**:奖励冠军
|
||||||
|
- **get_balance()**:获取用户余额
|
||||||
消息服务,处理消息发送和自动撤回:
|
|
||||||
|
### message_service.py
|
||||||
- **send_with_recall()**:发送消息并根据配置自动撤回
|
|
||||||
- **_schedule_recall()**:异步调度消息撤回
|
消息服务,处理消息发送和自动撤回:
|
||||||
- **clear_pending_recalls()**:清除待撤回消息任务
|
|
||||||
|
- **send_with_recall()**:发送消息并根据配置自动撤回
|
||||||
### room_store.py
|
- **_schedule_recall()**:异步调度消息撤回
|
||||||
|
- **clear_pending_recalls()**:清除待撤回消息任务
|
||||||
房间存储,管理比赛房间的生命周期:
|
|
||||||
|
### room_store.py
|
||||||
- 支持并发访问控制(使用asyncio.Lock)
|
|
||||||
- 房间持久化存储
|
房间存储,管理比赛房间的生命周期:
|
||||||
- 房间清理和过期处理
|
|
||||||
|
- 支持并发访问控制(使用asyncio.Lock)
|
||||||
### commands.py
|
- 房间持久化存储
|
||||||
|
- 房间清理和过期处理
|
||||||
命令处理器,实现所有用户命令:
|
|
||||||
|
### commands.py
|
||||||
- **handle_register()**:处理报名命令
|
|
||||||
- **handle_start()**:处理开赛命令
|
命令处理器,实现所有用户命令:
|
||||||
- **handle_help()**:处理帮助命令
|
|
||||||
|
- **handle_register()**:处理报名命令
|
||||||
## 使用流程
|
- **handle_cancel()**:处理取消报名命令
|
||||||
|
- **handle_bet()**:处理下注命令
|
||||||
### 基本游戏流程
|
- **handle_cancel_bet()**:处理取消下注命令
|
||||||
|
- **handle_odds()**:处理赔率显示命令
|
||||||
1. **报名阶段**
|
- **handle_race_list()**:处理马匹列表命令
|
||||||
- 用户执行 `/赛马报名` 命令
|
- **handle_start()**:处理开赛命令(仅参赛者或管理员可操作)
|
||||||
- 系统检查权限和房间容量(最多8匹马)
|
- **handle_help()**:处理帮助命令
|
||||||
- 成功报名
|
|
||||||
|
## 使用流程
|
||||||
2. **下注阶段**
|
|
||||||
- 用户可对参赛马匹进行下注
|
### 基本游戏流程
|
||||||
- 下注金额需满足最小下注要求
|
|
||||||
- 下注积分从用户账户扣除
|
1. **报名阶段**
|
||||||
- 可以给自己的马下注
|
- 用户执行 `/赛马报名` 命令
|
||||||
|
- 系统检查权限和房间容量(最多8匹马)
|
||||||
3. **比赛阶段**
|
- 成功报名
|
||||||
- 房主执行 `/赛马开赛` 命令
|
|
||||||
- 系统启动比赛引擎
|
2. **下注阶段**
|
||||||
- 每个tick更新马匹位置
|
- 用户可对参赛马匹进行下注
|
||||||
- 首先到达终点的马匹为冠军
|
- 下注金额需满足最小下注要求
|
||||||
|
- 下注积分从用户账户扣除
|
||||||
4. **结算阶段**
|
- 可以给自己的马下注
|
||||||
- 冠军获得冠军奖励
|
|
||||||
- 中奖用户获得下注奖金(下注金额 × 赔率)
|
3. **比赛阶段**
|
||||||
- 比赛结果保存到数据库
|
- 房主执行 `/赛马开赛` 命令
|
||||||
|
- 系统启动比赛引擎
|
||||||
## 权限控制
|
- 每个tick更新马匹位置
|
||||||
|
- 首先到达终点的马匹为冠军
|
||||||
插件支持两种权限模式:
|
|
||||||
|
4. **结算阶段**
|
||||||
### 测试模式(TEST_MODE=true)
|
- 冠军获得冠军奖励
|
||||||
|
- 中奖用户获得下注奖金(下注金额 × 赔率)
|
||||||
- 仅允许 `TEST_GROUPS` 中的群组使用
|
- 比赛结果保存到数据库
|
||||||
- 仅允许 `TESTERS` 中的用户在私聊中使用
|
|
||||||
|
## 权限控制
|
||||||
### 正常模式(TEST_MODE=false)
|
|
||||||
|
插件支持两种权限模式:
|
||||||
- 仅允许 `ALLOWED_GROUPS` 中的群组使用
|
|
||||||
- 私聊中禁用
|
### 测试模式(TEST_MODE=true)
|
||||||
|
|
||||||
## 依赖关系
|
- 仅允许 `TEST_GROUPS` 中的群组使用
|
||||||
|
- 仅允许 `TESTERS` 中的用户在私聊中使用
|
||||||
- **必需**:`danding_bot.plugins.danding_points` - 积分系统插件
|
|
||||||
|
### 正常模式(TEST_MODE=false)
|
||||||
## 数据存储
|
|
||||||
|
- 仅允许 `ALLOWED_GROUPS` 中的群组使用
|
||||||
比赛数据存储在SQLite数据库中:
|
- 私聊中禁用
|
||||||
|
|
||||||
- 位置:`data/group_horse_racing/race.db`
|
## 依赖关系
|
||||||
- 存储内容:比赛历史、结果统计、用户数据
|
|
||||||
|
- **必需**:`danding_bot.plugins.danding_points` - 积分系统插件
|
||||||
## 并发控制
|
|
||||||
|
## 数据存储
|
||||||
- 使用 `asyncio.Lock` 保证房间操作的线程安全
|
|
||||||
- 支持多个房间同时进行比赛
|
比赛数据存储在SQLite数据库中:
|
||||||
- 每个房间有独立的锁和异步任务
|
|
||||||
|
- 位置:`data/group_horse_racing/race.db`
|
||||||
## 错误处理
|
- 存储内容:比赛历史、结果统计、用户数据
|
||||||
|
|
||||||
- 权限检查:无权限时返回错误提示
|
## 并发控制
|
||||||
- 房间检查:房间不存在或已满时返回错误
|
|
||||||
- 参赛人数检查:少于2匹马时无法开赛
|
- 使用 `asyncio.Lock` 保证房间操作的线程安全
|
||||||
- 积分检查:积分不足时下注失败
|
- 支持多个房间同时进行比赛
|
||||||
|
- 每个房间有独立的锁和异步任务
|
||||||
## 测试
|
|
||||||
|
## 错误处理
|
||||||
项目包含 `test_commands.py` 用于测试各项功能。
|
|
||||||
|
- 权限检查:无权限时返回错误提示
|
||||||
## 扩展建议
|
- 房间检查:房间不存在或已满时返回错误
|
||||||
|
- 参赛人数检查:少于2匹马时无法开赛
|
||||||
- 支持更多赛马属性(速度、耐力等)
|
- 积分检查:积分不足时下注失败
|
||||||
- 实现赔率动态计算
|
|
||||||
- 添加排行榜功能
|
## 测试
|
||||||
- 支持马匹升级系统
|
|
||||||
- 实现更复杂的下注规则
|
项目包含 `test_commands.py` 用于测试各项功能。
|
||||||
|
|
||||||
|
## 扩展建议
|
||||||
|
|
||||||
|
- 支持更多赛马属性(速度、耐力等)
|
||||||
|
- 实现赔率动态计算
|
||||||
|
- 添加排行榜功能
|
||||||
|
- 支持马匹升级系统
|
||||||
|
- 实现更复杂的下注规则
|
||||||
|
|||||||
@@ -444,6 +444,57 @@ async def handle_cancel(bot: Bot, event: Event):
|
|||||||
bet_cmd = on_command("赛马下注", priority=5)
|
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()
|
@bet_cmd.handle()
|
||||||
async def handle_bet(bot: Bot, event: Event):
|
async def handle_bet(bot: Bot, event: Event):
|
||||||
"""Handle bet placement."""
|
"""Handle bet placement."""
|
||||||
@@ -530,17 +581,43 @@ async def handle_odds(bot: Bot, event: Event):
|
|||||||
await odds_cmd.finish("\n".join(lines))
|
await odds_cmd.finish("\n".join(lines))
|
||||||
|
|
||||||
|
|
||||||
|
race_list_cmd = on_command("赛马列表", priority=5)
|
||||||
|
|
||||||
|
|
||||||
|
@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 = on_command("赛马开赛", priority=5)
|
||||||
|
|
||||||
|
|
||||||
@start_cmd.handle()
|
@start_cmd.handle()
|
||||||
async def handle_start(bot: Bot, event: Event):
|
async def handle_start(bot: Bot, event: Event):
|
||||||
"""Handle race start."""
|
"""Handle race start - only participants or admins can start."""
|
||||||
if not await check_access(bot, event):
|
if not await check_access(bot, event):
|
||||||
await start_cmd.finish("无权限访问此功能")
|
await start_cmd.finish("无权限访问此功能")
|
||||||
return
|
return
|
||||||
|
|
||||||
scope = get_scope(event)
|
scope = get_scope(event)
|
||||||
|
user_id = get_event_id(event)
|
||||||
lock = room_store.get_lock(scope)
|
lock = room_store.get_lock(scope)
|
||||||
|
|
||||||
async with lock:
|
async with lock:
|
||||||
@@ -557,6 +634,24 @@ async def handle_start(bot: Bot, event: Event):
|
|||||||
await start_cmd.finish("至少需要2匹马才能开赛")
|
await start_cmd.finish("至少需要2匹马才能开赛")
|
||||||
return
|
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
|
# Set all horses to racing state
|
||||||
for horse in room.horses.values():
|
for horse in room.horses.values():
|
||||||
horse.state = HorseState.RACING
|
horse.state = HorseState.RACING
|
||||||
@@ -581,7 +676,9 @@ async def handle_help(bot: Bot, event: Event):
|
|||||||
/赛马报名 - 复用上次绑定的马名,若无则使用群昵称
|
/赛马报名 - 复用上次绑定的马名,若无则使用群昵称
|
||||||
/赛马取消报名 - 取消报名并退还下注
|
/赛马取消报名 - 取消报名并退还下注
|
||||||
/赛马下注 <序号|马匹名> <金额> - 下注
|
/赛马下注 <序号|马匹名> <金额> - 下注
|
||||||
|
/赛马取消下注 - 取消本人在当前房间的所有下注并退还积分
|
||||||
/赛马赔率 - 查看当前赔率和下注池
|
/赛马赔率 - 查看当前赔率和下注池
|
||||||
|
/赛马列表 - 查看当前报名马匹列表
|
||||||
/赛马开赛 - 开始比赛(至少2匹马)
|
/赛马开赛 - 开始比赛(至少2匹马)
|
||||||
/赛马帮助 - 显示此帮助
|
/赛马帮助 - 显示此帮助
|
||||||
|
|
||||||
@@ -589,8 +686,10 @@ async def handle_help(bot: Bot, event: Event):
|
|||||||
• 最低下注金额:{config.MIN_BET} 积分
|
• 最低下注金额:{config.MIN_BET} 积分
|
||||||
• 参赛马匹上限:8匹
|
• 参赛马匹上限:8匹
|
||||||
• 开赛要求:至少2匹马报名
|
• 开赛要求:至少2匹马报名
|
||||||
|
• 手动开赛权限:仅当前参赛者或群管理员可操作
|
||||||
|
|
||||||
💰 奖励机制:
|
💰 奖励机制:
|
||||||
|
• 参赛奖励:参赛者均可获得 {config.PARTICIPANT_REWARD} 积分
|
||||||
• 冠军马主:获得 {config.CHAMPION_REWARD} 积分
|
• 冠军马主:获得 {config.CHAMPION_REWARD} 积分
|
||||||
• 下注中奖:下注金额 × 赔率
|
• 下注中奖:下注金额 × 赔率
|
||||||
|
|
||||||
@@ -602,7 +701,7 @@ async def handle_help(bot: Bot, event: Event):
|
|||||||
🎮 游戏流程:
|
🎮 游戏流程:
|
||||||
1️⃣ 玩家报名并绑定马匹名
|
1️⃣ 玩家报名并绑定马匹名
|
||||||
2️⃣ 玩家可以给任意马匹下注
|
2️⃣ 玩家可以给任意马匹下注
|
||||||
3️⃣ 满足开赛条件后,任意玩家可开赛
|
3️⃣ 满足开赛后,由参赛者或管理员开赛
|
||||||
4️⃣ 比赛实时进行,定期播报进度
|
4️⃣ 比赛实时进行,定期播报进度
|
||||||
5️⃣ 比赛结束后结算积分和奖金"""
|
5️⃣ 比赛结束后结算积分和奖金"""
|
||||||
await help_cmd.finish(help_text)
|
await help_cmd.finish(help_text)
|
||||||
|
|||||||
Reference in New Issue
Block a user