Compare commits
13 Commits
three
...
task/add_c
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f3c21c3d6 | |||
| b07b044035 | |||
| 9e16774d2f | |||
| 17da2e6111 | |||
| 1f10799562 | |||
| cef684f64b | |||
| 7962852685 | |||
| cc97374e98 | |||
| a62d5d66b7 | |||
| 19cde88acf | |||
| 6f05ca98f1 | |||
| 5a8a4ac026 | |||
| 12aac846cc |
@@ -122,6 +122,24 @@ WPSBotGame/
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
## 2025-10-31_10:27:59
|
||||
- 已修改:
|
||||
- games/alchemy.py(修复冒险任务完成后自动删除状态导致奖励丢失的bug)
|
||||
- 更改:
|
||||
1. **Bug修复**:修复冒险任务完成后奖励丢失问题
|
||||
- **问题**:在 `_perform_alchemy` 中,当检测到冒险任务已完成时,代码会自动删除冒险状态(`self.db.delete_game_state`),但没有发放奖励,导致用户奖励丢失
|
||||
- **修复**:移除自动删除逻辑,改为提示用户先使用 `.adventure` 回收奖励
|
||||
- **修改前**:冒险完成后自动删除状态,允许炼金(导致奖励丢失)
|
||||
- **修改后**:冒险完成后提示用户先回收奖励,不允许炼金,确保奖励只能通过 `.adventure` 命令回收
|
||||
2. **行为变更**:
|
||||
- 冒险进行中:提示剩余时间,不允许炼金(保持不变)
|
||||
- 冒险已完成:提示先回收奖励,不允许炼金(修复后)
|
||||
- 用户使用 `.adventure`:发放奖励并删除状态(保持不变)
|
||||
- 状态已删除:可以正常炼金(保持不变)
|
||||
- 原因:修复冒险任务完成后自动删除状态导致奖励丢失的严重bug,确保用户必须先主动回收奖励才能继续其他操作
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
# 详细实施记录
|
||||
|
||||
## 文件修改清单
|
||||
@@ -150,7 +168,7 @@ WPSBotGame/
|
||||
- 使用 `get_game_state(0, user_id, 'adventure')` 查询状态
|
||||
- 如果存在状态:
|
||||
* 计算剩余时间
|
||||
* 如果已完成:自动删除状态,允许继续
|
||||
* 如果已完成:提示用户先使用 `.adventure` 回收奖励,不允许炼金(2025-10-31修复:避免奖励丢失,移除自动删除逻辑)
|
||||
* 如果未完成:返回错误消息,显示剩余时间(X分Y秒)
|
||||
- 异常处理:捕获状态数据异常,自动清理损坏状态
|
||||
|
||||
@@ -206,7 +224,7 @@ state_data = {
|
||||
### 游戏互斥机制
|
||||
- 炼金前检查:查询冒险状态
|
||||
- 如果冒险进行中:返回错误,显示剩余时间
|
||||
- 如果冒险已完成:自动清理状态,允许炼金
|
||||
- 如果冒险已完成:提示用户先使用 `.adventure` 回收奖励,不允许炼金(修复后:确保奖励不会丢失)
|
||||
- 状态异常:自动清理,允许继续操作
|
||||
|
||||
# 最终审查
|
||||
|
||||
506
.tasks/2025-10-30_1_add_casino_games.md
Normal file
506
.tasks/2025-10-30_1_add_casino_games.md
Normal file
@@ -0,0 +1,506 @@
|
||||
# 背景
|
||||
文件名:2025-10-30_1_add_casino_games.md
|
||||
创建于:2025-10-30_15:16:56
|
||||
创建者:admin
|
||||
主分支:main
|
||||
任务分支:task/add_casino_games_2025-01-14_1
|
||||
Yolo模式:Off
|
||||
|
||||
# 任务描述
|
||||
在项目中新增赌场常见游戏,支持多个玩家下注等功能,使用积分系统模拟真实的赌场环境。
|
||||
|
||||
要求:
|
||||
- 指令格式:`.赌场 <游戏类型> <参数>`
|
||||
- 采用模块化设计,便于扩展多种赌场游戏
|
||||
- 支持多人同时下注
|
||||
- 集成现有积分系统
|
||||
- 记录下注和收益数据
|
||||
|
||||
# 项目概览
|
||||
基于WPS协作开放平台的自定义机器人游戏系统。使用FastAPI + SQLite架构,已有完善的积分系统和多款游戏(五子棋、成语接龙等)。需要通过模块化设计添加赌场游戏功能。
|
||||
|
||||
# 分析
|
||||
|
||||
## 现有系统分析
|
||||
1. **积分系统**:已实现 `add_points()` 和 `consume_points()` 方法
|
||||
2. **游戏基类**:`BaseGame` 提供统一的接口
|
||||
3. **路由系统**:通过 `CommandParser` 解析指令,在 `callback.py` 中路由
|
||||
4. **数据库**:SQLite,已有用户表、游戏状态表、统计表
|
||||
|
||||
## 需要新增的内容
|
||||
1. **数据库表**:
|
||||
- `casino_bets`:记录所有下注
|
||||
- `casino_results`:记录游戏结果和结算
|
||||
- `casino_games`:记录游戏房间(可选)
|
||||
|
||||
2. **游戏模块**:
|
||||
- `games/casino.py`:主赌场模块
|
||||
- 第一期支持:大小游戏
|
||||
- 第二期计划:轮盘、二十一点等
|
||||
|
||||
3. **指令映射**:
|
||||
- `.赌场` -> casino 游戏类型
|
||||
- 子指令:`轮盘`、`大小`、`21点` 等
|
||||
|
||||
## 设计要点
|
||||
1. **模块化设计**:每种赌场游戏作为独立类
|
||||
2. **下注流程**:创建房间 -> 玩家下注 -> 结算 -> 分发奖励
|
||||
3. **安全性**:下注前检查积分,结算时原子性操作
|
||||
4. **多玩家支持**:以 chat_id 为单位创建游戏房间
|
||||
|
||||
# 提议的解决方案
|
||||
|
||||
## 指令设计
|
||||
采用 `.赌场 <游戏类型> <操作> <参数>` 的模块化结构:
|
||||
|
||||
### 大小游戏
|
||||
- **庄家开启游戏**:`.赌场 大小 open <最小下注> <最大下注> <赔率>`
|
||||
- 示例:`.赌场 大小 open 10 100 2.0` (最小10分,最大100分,赔率2.0倍)
|
||||
- **玩家下注**:`.赌场 大小 bet <大小/小> <下注金额>`
|
||||
- 示例:`.赌场 大小 bet 大 50` (下注50分压大)
|
||||
- 示例:`.赌场 大小 bet 小 30` (下注30分压小)
|
||||
- **查看状态**:`.赌场 大小 status`
|
||||
- **庄家结算**:`.赌场 大小 settle <结果>`
|
||||
- 示例:`.赌场 大小 settle 大` (开大)
|
||||
- 示例:`.赌场 大小 settle 小` (开小)
|
||||
|
||||
### 轮盘游戏(二期实现)
|
||||
- 暂不实现,等大小游戏完善后再扩展
|
||||
|
||||
### 21点游戏(二期实现)
|
||||
- 暂不实现,等大小游戏完善后再扩展
|
||||
|
||||
## 游戏流程
|
||||
1. 庄家开启游戏(指定下注限额和赔率参数)
|
||||
2. 玩家下注(可多人同时参与)
|
||||
3. 庄家确认结算(手动触发结果)
|
||||
4. 系统自动分发奖励
|
||||
|
||||
## 数据库设计
|
||||
|
||||
### 新增表:casino_bets(下注记录表)
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS casino_bets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
bet_type TEXT NOT NULL, -- '大' 或 '小'
|
||||
amount INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL, -- 赔率
|
||||
status TEXT DEFAULT 'pending', -- pending/settled/cancelled
|
||||
result TEXT, -- 游戏结果
|
||||
win_amount INTEGER, -- 赢得金额
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER,
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
)
|
||||
```
|
||||
|
||||
### 新增表:casino_sessions(游戏会话表)
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS casino_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
banker_id INTEGER NOT NULL, -- 庄家ID
|
||||
min_bet INTEGER NOT NULL,
|
||||
max_bet INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL,
|
||||
house_fee REAL DEFAULT 0.05, -- 抽水率,默认5%
|
||||
status TEXT DEFAULT 'open', -- open/settling/closed
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER,
|
||||
UNIQUE(chat_id, game_type, status)
|
||||
)
|
||||
```
|
||||
|
||||
### 索引
|
||||
- `casino_bets(chat_id, game_type, status)` - 快速查询待结算下注
|
||||
- `casino_sessions(chat_id, game_type, status)` - 快速查询活跃游戏
|
||||
|
||||
## 方案对比
|
||||
|
||||
### 方案A:纯数据库表方案(推荐)
|
||||
**优点**:
|
||||
- 数据结构清晰,便于查询统计
|
||||
- 支持历史记录追踪
|
||||
- 并发安全,利用数据库事务
|
||||
- 易于扩展复杂查询
|
||||
|
||||
**缺点**:
|
||||
- 需要维护额外的表结构
|
||||
- 稍微复杂一些
|
||||
|
||||
**决策**:采用此方案
|
||||
|
||||
### 方案B:game_states + JSON方案
|
||||
**优点**:
|
||||
- 复用现有系统
|
||||
- 实现简单
|
||||
|
||||
**缺点**:
|
||||
- 难以进行复杂统计查询
|
||||
- JSON解析性能较差
|
||||
- 数据格式不够规范化
|
||||
|
||||
## 核心实现细节
|
||||
|
||||
### 1. 游戏流程控制
|
||||
- **开启游戏**:检查是否已有活跃游戏,同一chat_id只能有一个进行中的游戏
|
||||
- **下注限制**:检查session状态、下注金额范围、玩家积分
|
||||
- **结算控制**:只有庄家可以结算,结算后自动关闭session
|
||||
|
||||
### 2. 下注流程
|
||||
1. 检查是否有活跃的session
|
||||
2. 检查下注金额是否符合min/max限制
|
||||
3. 检查用户积分是否充足
|
||||
4. 扣除下注金额(consume_points)
|
||||
5. 记录下注到casino_bets表
|
||||
|
||||
### 3. 结算流程
|
||||
1. 验证是否为庄家操作
|
||||
2. 查询所有pending状态的下注
|
||||
3. 计算每个玩家的输赢
|
||||
4. 使用数据库事务确保原子性:
|
||||
- 更新bets状态
|
||||
- 发放/扣除积分
|
||||
- 更新session状态
|
||||
5. 返回结算报告
|
||||
|
||||
### 4. 抽水机制
|
||||
- **抽水率**:5%(可配置,存储在session.house_fee中)
|
||||
- **抽水时机**:从玩家的赢得金额中扣除
|
||||
- **抽水归属**:归系统所有(不返还给庄家)
|
||||
- **计算方式**:
|
||||
- 玩家赢得 = 下注金额 × 赔率
|
||||
- 实际发放 = 赢得金额 × (1 - 抽水率)
|
||||
- 抽水金额 = 赢得金额 × 抽水率
|
||||
|
||||
### 5. 错误处理
|
||||
- 下注时积分不足:给出明确提示
|
||||
- 重复下注:允许(可下多注)
|
||||
- 非法下注金额:给出范围提示
|
||||
- 非庄家尝试结算:拒绝
|
||||
|
||||
## 安全性
|
||||
- 下注前检查积分
|
||||
- 结算时使用数据库事务保证原子性
|
||||
- 抽水机制保护庄家(虽然抽水归系统)
|
||||
- 验证庄家身份
|
||||
- 防止重复结算
|
||||
|
||||
# 详细实施计划
|
||||
|
||||
## 文件1: core/database.py
|
||||
|
||||
### 修改函数: init_tables()
|
||||
在现有表创建之后(约第130行),添加赌场相关表的创建:
|
||||
|
||||
位置:在 `user_points` 表创建之后(约第130行)添加
|
||||
|
||||
```python
|
||||
# 赌场下注记录表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS casino_bets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
bet_type TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL,
|
||||
status TEXT DEFAULT 'pending',
|
||||
result TEXT,
|
||||
win_amount INTEGER,
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER,
|
||||
FOREIGN KEY (user_id) REFERENCES users (user_id)
|
||||
)
|
||||
""")
|
||||
|
||||
# 赌场游戏会话表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS casino_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
banker_id INTEGER NOT NULL,
|
||||
min_bet INTEGER NOT NULL,
|
||||
max_bet INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL,
|
||||
house_fee REAL DEFAULT 0.05,
|
||||
status TEXT DEFAULT 'open',
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER,
|
||||
UNIQUE(chat_id, game_type, status)
|
||||
)
|
||||
""")
|
||||
|
||||
# 创建索引
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_casino_bets
|
||||
ON casino_bets(chat_id, game_type, status)
|
||||
""")
|
||||
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_casino_sessions
|
||||
ON casino_sessions(chat_id, game_type, status)
|
||||
""")
|
||||
```
|
||||
|
||||
### 新增函数: create_casino_session()
|
||||
函数签名:
|
||||
```python
|
||||
def create_casino_session(self, chat_id: int, game_type: str, banker_id: int,
|
||||
min_bet: int, max_bet: int, multiplier: float,
|
||||
house_fee: float = 0.05) -> int:
|
||||
```
|
||||
|
||||
功能:创建新的赌场游戏会话,返回session_id
|
||||
|
||||
### 新增函数: get_active_casino_session()
|
||||
函数签名:
|
||||
```python
|
||||
def get_active_casino_session(self, chat_id: int, game_type: str) -> Optional[Dict]:
|
||||
```
|
||||
|
||||
功能:获取活跃的游戏会话
|
||||
|
||||
### 新增函数: create_casino_bet()
|
||||
函数签名:
|
||||
```python
|
||||
def create_casino_bet(self, chat_id: int, game_type: str, user_id: int,
|
||||
bet_type: str, amount: int, multiplier: float) -> int:
|
||||
```
|
||||
|
||||
功能:创建下注记录,返回bet_id
|
||||
|
||||
### 新增函数: get_pending_bets()
|
||||
函数签名:
|
||||
```python
|
||||
def get_pending_bets(self, chat_id: int, game_type: str) -> List[Dict]:
|
||||
```
|
||||
|
||||
功能:获取待结算的下注列表
|
||||
|
||||
### 新增函数: settle_casino_bets()
|
||||
函数签名:
|
||||
```python
|
||||
def settle_casino_bets(self, chat_id: int, game_type: str, result: str,
|
||||
banker_id: int) -> Dict:
|
||||
```
|
||||
|
||||
功能:结算所有下注,返回结算详情字典(winners, losers, total_win等)
|
||||
|
||||
### 新增函数: close_casino_session()
|
||||
函数签名:
|
||||
```python
|
||||
def close_casino_session(self, chat_id: int, game_type: str):
|
||||
```
|
||||
|
||||
功能:关闭游戏会话
|
||||
|
||||
## 文件2: games/casino.py(新建)
|
||||
|
||||
### 类: CasinoGame
|
||||
继承自 `BaseGame`
|
||||
|
||||
### 方法: __init__()
|
||||
初始化数据库连接
|
||||
|
||||
### 方法: async handle(command, chat_id, user_id) -> str
|
||||
主处理函数,解析指令并调用相应的处理方法
|
||||
|
||||
解析逻辑:
|
||||
- 提取命令参数,格式:`.赌场 <游戏类型> <操作> <参数>`
|
||||
- 识别游戏类型(第一期只支持"大小")
|
||||
- 分发到相应的处理方法
|
||||
|
||||
### 方法: async _handle_bigsmall(command, args, chat_id, user_id) -> str
|
||||
处理大小游戏的各种操作
|
||||
|
||||
支持的操作:
|
||||
- open: 开启游戏
|
||||
- bet: 下注
|
||||
- status: 查看状态
|
||||
- settle: 结算
|
||||
|
||||
### 方法: async _open_bigsmall(args, chat_id, user_id) -> str
|
||||
庄家开启大小游戏
|
||||
|
||||
参数解析:`<最小下注> <最大下注> <赔率>`
|
||||
参数验证和限制
|
||||
|
||||
### 方法: async _bet_bigsmall(args, chat_id, user_id) -> str
|
||||
玩家下注
|
||||
|
||||
参数解析:`<大小/小> <下注金额>`
|
||||
检查session、金额范围、用户积分
|
||||
|
||||
### 方法: async _status_bigsmall(chat_id, game_type) -> str
|
||||
查看当前游戏状态
|
||||
|
||||
### 方法: async _settle_bigsmall(args, chat_id, user_id) -> str
|
||||
庄家结算游戏
|
||||
|
||||
参数解析:`<大/小>`
|
||||
验证庄家身份,结算所有下注
|
||||
|
||||
### 方法: get_help() -> str
|
||||
返回帮助信息
|
||||
|
||||
## 文件3: utils/parser.py
|
||||
|
||||
### 修改: COMMAND_MAP
|
||||
添加赌场指令映射:
|
||||
|
||||
```python
|
||||
# 赌场系统
|
||||
'.赌场': 'casino',
|
||||
'.casino': 'casino',
|
||||
```
|
||||
|
||||
## 文件4: routers/callback.py
|
||||
|
||||
### 修改: async handle_command()
|
||||
在AI对话系统之后(约第209行)添加:
|
||||
|
||||
```python
|
||||
# 赌场系统
|
||||
if game_type == 'casino':
|
||||
from games.casino import CasinoGame
|
||||
game = CasinoGame()
|
||||
return await game.handle(command, chat_id, user_id)
|
||||
```
|
||||
|
||||
## 文件5: games/base.py
|
||||
|
||||
### 修改: get_help_message()
|
||||
在积分赠送系统之后添加赌场游戏帮助:
|
||||
|
||||
```python
|
||||
### 🎰 赌场系统
|
||||
- `.赌场 大小 open <最小> <最大> <赔率>` - 庄家开启大小游戏
|
||||
- `.赌场 大小 bet <大/小> <金额>` - 下注
|
||||
- `.赌场 大小 status` - 查看状态
|
||||
- `.赌场 大小 settle <大/小>` - 庄家结算
|
||||
```
|
||||
|
||||
## 实施清单
|
||||
|
||||
1. 修改 `core/database.py` 的 `init_tables()` 方法,添加赌场表创建和索引
|
||||
2. 在 `core/database.py` 中添加 `create_casino_session()` 方法
|
||||
3. 在 `core/database.py` 中添加 `get_active_casino_session()` 方法
|
||||
4. 在 `core/database.py` 中添加 `create_casino_bet()` 方法
|
||||
5. 在 `core/database.py` 中添加 `get_pending_bets()` 方法
|
||||
6. 在 `core/database.py` 中添加 `settle_casino_bets()` 方法
|
||||
7. 在 `core/database.py` 中添加 `close_casino_session()` 方法
|
||||
8. 创建文件 `games/casino.py`,定义 `CasinoGame` 类
|
||||
9. 在 `games/casino.py` 中实现 `__init__()` 方法
|
||||
10. 在 `games/casino.py` 中实现 `async handle()` 方法
|
||||
11. 在 `games/casino.py` 中实现 `async _handle_bigsmall()` 方法
|
||||
12. 在 `games/casino.py` 中实现 `async _open_bigsmall()` 方法
|
||||
13. 在 `games/casino.py` 中实现 `async _bet_bigsmall()` 方法
|
||||
14. 在 `games/casino.py` 中实现 `async _status_bigsmall()` 方法
|
||||
15. 在 `games/casino.py` 中实现 `async _settle_bigsmall()` 方法
|
||||
16. 在 `games/casino.py` 中实现 `get_help()` 方法
|
||||
17. 修改 `utils/parser.py`,在 COMMAND_MAP 中添加赌场指令映射
|
||||
18. 修改 `routers/callback.py`,在 `handle_command()` 中添加赌场路由
|
||||
19. 修改 `games/base.py`,在 `get_help_message()` 中添加赌场帮助信息
|
||||
20. 测试所有功能点,确保无错误
|
||||
|
||||
# 当前执行步骤:"2. 详细技术规划完成,等待进入实现阶段"
|
||||
|
||||
# 任务进度
|
||||
[2025-10-30_15:16:56]
|
||||
- 已修改:创建任务文件 `.tasks/2025-10-30_1_add_casino_games.md`
|
||||
- 更改:创建任务分支 `task/add_casino_games_2025-01-14_1` 和任务文件
|
||||
- 原因:按照RIPER-5协议建立工作基础
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-30_16:30:00](预估时间)
|
||||
- 已修改:完成详细技术规划
|
||||
- 更改:设计数据库表结构、游戏流程、抽水机制等细节
|
||||
- 原因:为实施阶段提供详细技术规范
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-30_16:07:57]
|
||||
- 已修改:core/database.py, games/casino.py, utils/parser.py, routers/callback.py, games/base.py
|
||||
- 更改:完成所有实施步骤1-19
|
||||
- 添加赌场表创建和索引
|
||||
- 实现6个数据库方法(create_casino_session, get_active_casino_session, create_casino_bet, get_pending_bets, settle_casino_bets, close_casino_session)
|
||||
- 创建完整的CasinoGame类,实现大小游戏所有功能
|
||||
- 注册指令映射和路由
|
||||
- 添加帮助信息
|
||||
- 原因:按照详细实施计划完成全部功能开发
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-30_17:20:00](预估时间)
|
||||
- 已修改:games/casino.py
|
||||
- 更改:修改结算逻辑,从庄家指定结果改为系统随机生成
|
||||
- 移除庄家输入种子/结果的参数
|
||||
- 使用random.random()生成随机结果(50%大/50%小)
|
||||
- 更新帮助信息,settle命令不再需要参数
|
||||
- 原因:用户反馈庄家不应该能够操控游戏结果,庄家也是玩家
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-30_17:26:19]
|
||||
- 已修改:games/casino.py, games/base.py
|
||||
- 更改:添加庄家放弃游戏功能
|
||||
- 新增_cancel_bigsmall()方法处理放弃逻辑
|
||||
- 放弃时返还所有玩家下注
|
||||
- 关闭会话并标记下注为cancelled
|
||||
- 添加cancel命令支持(cancel/放弃/关闭)
|
||||
- 更新帮助信息和base.py中的帮助
|
||||
- 原因:用户要求庄家可以放弃本轮游戏
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-31_11:35:18]
|
||||
- 已修改:core/database.py
|
||||
- 更改:扩展数据库支持轮盘和21点游戏
|
||||
- 添加列存在性检查辅助方法(_column_exists, _add_column_if_not_exists)
|
||||
- 扩展casino_sessions表:添加current_phase和blackjack_multiplier字段(兼容性检查)
|
||||
- 扩展casino_bets表:添加bet_category、bet_number、bet_value、hand_status字段(兼容性检查)
|
||||
- 创建casino_blackjack_hands表:存储21点游戏手牌数据
|
||||
- 修改create_casino_session():支持单场限制检查(get_any_active_casino_session)和新字段
|
||||
- 扩展create_casino_bet():支持轮盘和21点专用字段参数
|
||||
- 添加21点手牌管理方法:create_blackjack_hand、get_blackjack_hand、update_blackjack_hand、get_all_blackjack_hands
|
||||
- 原因:为轮盘和21点游戏提供数据库支持,确保字段分离和向后兼容
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-31_15:15:08]
|
||||
- 已修改:core/database.py
|
||||
- 更改:修复大小游戏结算时的UNIQUE约束冲突问题
|
||||
- 移除casino_sessions表的UNIQUE(chat_id, game_type, status)约束
|
||||
- 原因:status='closed'时需要允许多条历史记录,UNIQUE约束阻止了结算时更新status
|
||||
- 添加兼容性迁移逻辑:检测旧版本表结构,自动重建表以移除UNIQUE约束
|
||||
- 迁移时复制所有历史数据,处理外键关系(临时禁用/启用外键检查)
|
||||
- 单场限制通过应用层逻辑(get_any_active_casino_session)保证
|
||||
- 原因:用户测试大小游戏结算时遇到"UNIQUE constraint failed"错误
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
[2025-10-31_15:15:08]
|
||||
- 已修改:core/database.py
|
||||
- 更改:修复21点游戏结算逻辑问题
|
||||
- 修正losers统计逻辑:将条件从`not player_is_busted and player_points != banker_points`改为`player_points != banker_points`
|
||||
- 原因:原条件排除了爆牌玩家,导致爆牌玩家未被统计到losers列表
|
||||
- 修正数据库更新逻辑:明确区分三种情况
|
||||
- 赢家:发放奖励并更新数据库
|
||||
- 平局(player_points == banker_points):已返还下注,更新数据库
|
||||
- 输家(else分支,包括爆牌和点数小于庄家):更新数据库
|
||||
- 改进结果字符串显示:包含玩家和庄家的状态信息(爆牌、黑杰克等)
|
||||
- 例如:"庄家19点 vs 玩家爆牌" 或 "庄家19点 vs 玩家20点(黑杰克)"
|
||||
- 原因:用户测试21点游戏时发现3人游戏中只有1个赢家被结算,1个爆牌玩家和1个平局玩家未被结算
|
||||
- 阻碍因素:无
|
||||
- 状态:成功
|
||||
|
||||
# 最终审查
|
||||
待完成
|
||||
933
core/database.py
933
core/database.py
@@ -54,6 +54,37 @@ class Database:
|
||||
raise
|
||||
return self._conn
|
||||
|
||||
def _column_exists(self, table_name: str, column_name: str) -> bool:
|
||||
"""检查表中列是否存在
|
||||
|
||||
Args:
|
||||
table_name: 表名
|
||||
column_name: 列名
|
||||
|
||||
Returns:
|
||||
是否存在
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute(f"PRAGMA table_info({table_name})")
|
||||
columns = [row[1] for row in cursor.fetchall()]
|
||||
return column_name in columns
|
||||
|
||||
def _add_column_if_not_exists(self, table_name: str, column_name: str, column_def: str):
|
||||
"""安全地添加列(如果不存在)
|
||||
|
||||
Args:
|
||||
table_name: 表名
|
||||
column_name: 列名
|
||||
column_def: 列定义(如 "INTEGER" 或 "TEXT DEFAULT ''")
|
||||
"""
|
||||
if not self._column_exists(table_name, column_name):
|
||||
try:
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute(f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_def}")
|
||||
logger.info(f"为表 {table_name} 添加列 {column_name}")
|
||||
except Exception as e:
|
||||
logger.warning(f"添加列失败: {e}")
|
||||
|
||||
def init_tables(self):
|
||||
"""初始化数据库表"""
|
||||
cursor = self.conn.cursor()
|
||||
@@ -129,6 +160,167 @@ class Database:
|
||||
)
|
||||
""")
|
||||
|
||||
# 赌场下注记录表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS casino_bets (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
bet_type TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL,
|
||||
status TEXT DEFAULT 'pending',
|
||||
result TEXT,
|
||||
win_amount INTEGER,
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER,
|
||||
FOREIGN KEY (user_id) REFERENCES users (user_id)
|
||||
)
|
||||
""")
|
||||
|
||||
# 赌场游戏会话表
|
||||
# 注意:移除了UNIQUE(chat_id, game_type, status)约束
|
||||
# 因为status='closed'时需要允许多条历史记录
|
||||
# 单场限制通过应用层逻辑(get_any_active_casino_session)保证
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS casino_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
banker_id INTEGER NOT NULL,
|
||||
min_bet INTEGER NOT NULL,
|
||||
max_bet INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL,
|
||||
house_fee REAL DEFAULT 0.05,
|
||||
status TEXT DEFAULT 'open',
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER
|
||||
)
|
||||
""")
|
||||
|
||||
# 迁移:如果表已存在且有UNIQUE约束,需要重建表
|
||||
# 检查是否已有旧表(通过检查是否有UNIQUE约束的索引)
|
||||
try:
|
||||
# 尝试查询表结构,检查是否有UNIQUE约束相关的索引
|
||||
cursor.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='casino_sessions'")
|
||||
old_sql = cursor.fetchone()
|
||||
if old_sql and 'UNIQUE(chat_id, game_type, status)' in old_sql[0]:
|
||||
# 需要重建表以移除UNIQUE约束
|
||||
logger.info("检测到旧版本的casino_sessions表,需要重建以移除UNIQUE约束")
|
||||
# 禁用外键检查(SQLite默认可能未启用,但为了安全)
|
||||
cursor.execute("PRAGMA foreign_keys=OFF")
|
||||
# 创建临时表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS casino_sessions_new (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
chat_id INTEGER NOT NULL,
|
||||
game_type TEXT NOT NULL,
|
||||
banker_id INTEGER NOT NULL,
|
||||
min_bet INTEGER NOT NULL,
|
||||
max_bet INTEGER NOT NULL,
|
||||
multiplier REAL NOT NULL,
|
||||
house_fee REAL DEFAULT 0.05,
|
||||
status TEXT DEFAULT 'open',
|
||||
created_at INTEGER NOT NULL,
|
||||
settled_at INTEGER,
|
||||
current_phase TEXT DEFAULT 'betting',
|
||||
blackjack_multiplier REAL DEFAULT 1.5
|
||||
)
|
||||
""")
|
||||
# 复制所有数据(包括历史记录)
|
||||
# 检查旧表是否有新字段
|
||||
old_columns = [row[1] for row in cursor.execute("PRAGMA table_info(casino_sessions)").fetchall()]
|
||||
has_current_phase = 'current_phase' in old_columns
|
||||
has_blackjack_multiplier = 'blackjack_multiplier' in old_columns
|
||||
|
||||
if has_current_phase and has_blackjack_multiplier:
|
||||
# 旧表已有新字段,直接复制所有列
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_sessions_new
|
||||
SELECT id, chat_id, game_type, banker_id, min_bet, max_bet,
|
||||
multiplier, house_fee, status, created_at, settled_at,
|
||||
current_phase, blackjack_multiplier
|
||||
FROM casino_sessions
|
||||
""")
|
||||
elif has_current_phase:
|
||||
# 只有current_phase字段
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_sessions_new
|
||||
SELECT id, chat_id, game_type, banker_id, min_bet, max_bet,
|
||||
multiplier, house_fee, status, created_at, settled_at,
|
||||
current_phase, 1.5 as blackjack_multiplier
|
||||
FROM casino_sessions
|
||||
""")
|
||||
elif has_blackjack_multiplier:
|
||||
# 只有blackjack_multiplier字段
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_sessions_new
|
||||
SELECT id, chat_id, game_type, banker_id, min_bet, max_bet,
|
||||
multiplier, house_fee, status, created_at, settled_at,
|
||||
'betting' as current_phase, blackjack_multiplier
|
||||
FROM casino_sessions
|
||||
""")
|
||||
else:
|
||||
# 都没有新字段,使用默认值
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_sessions_new
|
||||
SELECT id, chat_id, game_type, banker_id, min_bet, max_bet,
|
||||
multiplier, house_fee, status, created_at, settled_at,
|
||||
'betting' as current_phase, 1.5 as blackjack_multiplier
|
||||
FROM casino_sessions
|
||||
""")
|
||||
# 删除旧表
|
||||
cursor.execute("DROP TABLE casino_sessions")
|
||||
# 重命名新表
|
||||
cursor.execute("ALTER TABLE casino_sessions_new RENAME TO casino_sessions")
|
||||
# 重新启用外键检查
|
||||
cursor.execute("PRAGMA foreign_keys=ON")
|
||||
logger.info("成功重建casino_sessions表,移除UNIQUE约束")
|
||||
except Exception as e:
|
||||
# 如果迁移失败,记录日志但不影响正常运行
|
||||
logger.warning(f"迁移casino_sessions表时出现错误(可能表结构已更新): {e}")
|
||||
|
||||
# 21点手牌表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS casino_blackjack_hands (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id INTEGER NOT NULL,
|
||||
user_id INTEGER NOT NULL,
|
||||
hand_data TEXT NOT NULL,
|
||||
hand_status TEXT DEFAULT 'playing',
|
||||
created_at INTEGER NOT NULL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
FOREIGN KEY (session_id) REFERENCES casino_sessions(id)
|
||||
)
|
||||
""")
|
||||
|
||||
# 创建索引
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_casino_bets
|
||||
ON casino_bets(chat_id, game_type, status)
|
||||
""")
|
||||
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_casino_sessions
|
||||
ON casino_sessions(chat_id, game_type, status)
|
||||
""")
|
||||
|
||||
cursor.execute("""
|
||||
CREATE INDEX IF NOT EXISTS idx_casino_blackjack_hands
|
||||
ON casino_blackjack_hands(session_id, user_id)
|
||||
""")
|
||||
|
||||
# 兼容性检查:为casino_sessions表添加新字段
|
||||
self._add_column_if_not_exists('casino_sessions', 'current_phase', "TEXT DEFAULT 'betting'")
|
||||
self._add_column_if_not_exists('casino_sessions', 'blackjack_multiplier', 'REAL DEFAULT 1.5')
|
||||
|
||||
# 兼容性检查:为casino_bets表添加新字段(轮盘和21点专用)
|
||||
self._add_column_if_not_exists('casino_bets', 'bet_category', "TEXT")
|
||||
self._add_column_if_not_exists('casino_bets', 'bet_number', 'INTEGER')
|
||||
self._add_column_if_not_exists('casino_bets', 'bet_value', "TEXT")
|
||||
self._add_column_if_not_exists('casino_bets', 'hand_status', "TEXT")
|
||||
|
||||
logger.info("数据库表初始化完成")
|
||||
|
||||
# ===== 用户相关操作 =====
|
||||
@@ -220,6 +412,23 @@ class Database:
|
||||
logger.error(f"更新用户名失败: user_id={user_id}, username={username}, error={e}", exc_info=True)
|
||||
return False
|
||||
|
||||
def get_user_display_name(self, user_id: int) -> str:
|
||||
"""获取用户显示名称
|
||||
如果用户已注册(username不为None),返回用户名;否则返回"用户{user_id}"
|
||||
|
||||
Args:
|
||||
user_id: 用户ID
|
||||
|
||||
Returns:
|
||||
用户显示名称(用户名或"用户{user_id}")
|
||||
"""
|
||||
user_dict = self.get_or_create_user(user_id)
|
||||
username = user_dict.get('username')
|
||||
if username:
|
||||
return username
|
||||
else:
|
||||
return f"用户{user_id}"
|
||||
|
||||
# ===== 游戏状态相关操作 =====
|
||||
|
||||
def get_game_state(self, chat_id: int, user_id: int, game_type: str) -> Optional[Dict]:
|
||||
@@ -587,6 +796,730 @@ class Database:
|
||||
rows = cursor.fetchall()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
# ===== 赌场相关操作 =====
|
||||
|
||||
def get_any_active_casino_session(self, chat_id: int) -> Optional[Dict]:
|
||||
"""获取任意活跃的游戏会话(用于单场限制检查)
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
|
||||
Returns:
|
||||
会话信息字典或None
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT * FROM casino_sessions
|
||||
WHERE chat_id = ? AND status = 'open'
|
||||
ORDER BY id DESC LIMIT 1
|
||||
""", (chat_id,))
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row:
|
||||
return dict(row)
|
||||
return None
|
||||
|
||||
def create_casino_session(self, chat_id: int, game_type: str, banker_id: int,
|
||||
min_bet: int, max_bet: int, multiplier: float,
|
||||
house_fee: float = 0.05, current_phase: str = 'betting',
|
||||
blackjack_multiplier: float = 1.5) -> int:
|
||||
"""创建新的赌场游戏会话
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
banker_id: 庄家ID
|
||||
min_bet: 最小下注金额
|
||||
max_bet: 最大下注金额
|
||||
multiplier: 赔率
|
||||
house_fee: 抽水率
|
||||
current_phase: 当前阶段(默认'betting')
|
||||
blackjack_multiplier: 21点黑杰克倍数(默认1.5)
|
||||
|
||||
Returns:
|
||||
session_id
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
|
||||
# 检查是否已有活跃的会话(单场限制:同一chat_id只能有一个活跃游戏)
|
||||
existing = self.get_any_active_casino_session(chat_id)
|
||||
if existing:
|
||||
# 如果已有活跃游戏,返回其ID(保持向后兼容,但实际应该在应用层阻止)
|
||||
return existing['id']
|
||||
|
||||
# 检查是否已有相同game_type的活跃会话(向后兼容)
|
||||
cursor.execute("""
|
||||
SELECT id FROM casino_sessions
|
||||
WHERE chat_id = ? AND game_type = ? AND status = 'open'
|
||||
""", (chat_id, game_type))
|
||||
existing_same_type = cursor.fetchone()
|
||||
if existing_same_type:
|
||||
return existing_same_type['id']
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_sessions
|
||||
(chat_id, game_type, banker_id, min_bet, max_bet, multiplier, house_fee,
|
||||
status, created_at, current_phase, blackjack_multiplier)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, 'open', ?, ?, ?)
|
||||
""", (chat_id, game_type, banker_id, min_bet, max_bet, multiplier, house_fee,
|
||||
current_time, current_phase, blackjack_multiplier))
|
||||
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_active_casino_session(self, chat_id: int, game_type: str) -> Optional[Dict]:
|
||||
"""获取活跃的游戏会话
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
|
||||
Returns:
|
||||
会话信息字典或None
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT * FROM casino_sessions
|
||||
WHERE chat_id = ? AND game_type = ? AND status = 'open'
|
||||
ORDER BY id DESC LIMIT 1
|
||||
""", (chat_id, game_type))
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row:
|
||||
return dict(row)
|
||||
return None
|
||||
|
||||
def create_casino_bet(self, chat_id: int, game_type: str, user_id: int,
|
||||
bet_type: str, amount: int, multiplier: float,
|
||||
bet_category: str = None, bet_number: int = None,
|
||||
bet_value: str = None, hand_status: str = None) -> int:
|
||||
"""创建下注记录
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
user_id: 用户ID
|
||||
bet_type: 下注类型(大小游戏:"大"/"小";21点:"标准"等)
|
||||
amount: 下注金额
|
||||
multiplier: 赔率
|
||||
bet_category: 下注类别(轮盘游戏:["数字"/"颜色"/"奇偶"/"大小"/"区间"])
|
||||
bet_number: 数字下注(轮盘游戏:0-36)
|
||||
bet_value: 下注值(轮盘游戏:如"红色"、"奇数"、"1-12"等)
|
||||
hand_status: 手牌状态(21点游戏:["playing"/"stood"/"busted"/"blackjack"])
|
||||
|
||||
Returns:
|
||||
bet_id
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_bets
|
||||
(chat_id, game_type, user_id, bet_type, amount, multiplier, status, created_at,
|
||||
bet_category, bet_number, bet_value, hand_status)
|
||||
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, ?, ?, ?)
|
||||
""", (chat_id, game_type, user_id, bet_type, amount, multiplier, current_time,
|
||||
bet_category, bet_number, bet_value, hand_status))
|
||||
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_pending_bets(self, chat_id: int, game_type: str) -> List[Dict]:
|
||||
"""获取待结算的下注列表
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
|
||||
Returns:
|
||||
下注列表
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT * FROM casino_bets
|
||||
WHERE chat_id = ? AND game_type = ? AND status = 'pending'
|
||||
ORDER BY created_at ASC
|
||||
""", (chat_id, game_type))
|
||||
rows = cursor.fetchall()
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
def settle_casino_bets(self, chat_id: int, game_type: str, result: str,
|
||||
banker_id: int, **kwargs) -> Dict:
|
||||
"""结算所有下注(根据游戏类型分发到不同结算方法)
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
result: 游戏结果(大小游戏:"大"/"小";轮盘:数字字符串;21点:特殊格式)
|
||||
banker_id: 庄家ID
|
||||
**kwargs: 其他参数(轮盘:result_number;21点:hands_dict等)
|
||||
|
||||
Returns:
|
||||
结算详情字典
|
||||
"""
|
||||
if game_type == '大小':
|
||||
return self._settle_bigsmall_bets(chat_id, game_type, result, banker_id)
|
||||
elif game_type == '轮盘':
|
||||
result_number = kwargs.get('result_number', int(result) if result.isdigit() else 0)
|
||||
return self._settle_roulette_bets(chat_id, game_type, result_number, banker_id)
|
||||
elif game_type == '21点':
|
||||
hands_dict = kwargs.get('hands_dict', {})
|
||||
return self._settle_blackjack_bets(chat_id, game_type, hands_dict, banker_id)
|
||||
else:
|
||||
# 兼容旧的大小游戏逻辑
|
||||
return self._settle_bigsmall_bets(chat_id, game_type, result, banker_id)
|
||||
|
||||
def _settle_bigsmall_bets(self, chat_id: int, game_type: str, result: str,
|
||||
banker_id: int) -> Dict:
|
||||
"""结算大小游戏下注
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
result: 游戏结果("大" 或 "小")
|
||||
banker_id: 庄家ID
|
||||
|
||||
Returns:
|
||||
结算详情字典
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
|
||||
# 获取活跃会话
|
||||
session = self.get_active_casino_session(chat_id, game_type)
|
||||
if not session:
|
||||
raise ValueError("没有活跃的游戏会话")
|
||||
|
||||
if session['banker_id'] != banker_id:
|
||||
raise ValueError("只有庄家可以结算游戏")
|
||||
|
||||
# 获取所有待结算下注
|
||||
bets = self.get_pending_bets(chat_id, game_type)
|
||||
|
||||
winners = []
|
||||
losers = []
|
||||
total_win = 0
|
||||
|
||||
# 计算输赢
|
||||
for bet in bets:
|
||||
is_win = (bet['bet_type'] == result)
|
||||
if is_win:
|
||||
# 计算赢得金额
|
||||
win_amount = int(bet['amount'] * bet['multiplier'])
|
||||
# 扣除抽水
|
||||
house_fee = session['house_fee']
|
||||
actual_win = int(win_amount * (1 - house_fee))
|
||||
|
||||
winners.append({
|
||||
'user_id': bet['user_id'],
|
||||
'amount': bet['amount'],
|
||||
'win_amount': actual_win,
|
||||
'bet_id': bet['id']
|
||||
})
|
||||
total_win += actual_win
|
||||
else:
|
||||
losers.append({
|
||||
'user_id': bet['user_id'],
|
||||
'amount': bet['amount'],
|
||||
'bet_id': bet['id']
|
||||
})
|
||||
|
||||
# 使用事务确保原子性
|
||||
try:
|
||||
# 更新下注状态
|
||||
for bet in bets:
|
||||
is_win = (bet['bet_type'] == result)
|
||||
if is_win:
|
||||
win_amount = int(bet['amount'] * bet['multiplier'])
|
||||
actual_win = int(win_amount * (1 - session['house_fee']))
|
||||
|
||||
# 发放奖励
|
||||
self.add_points(bet['user_id'], actual_win, 'casino_win',
|
||||
f"赌场游戏{game_type}赢得")
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, win_amount = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result, actual_win, current_time, bet['id']))
|
||||
else:
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result, current_time, bet['id']))
|
||||
|
||||
# 关闭会话
|
||||
cursor.execute("""
|
||||
UPDATE casino_sessions
|
||||
SET status = 'closed', settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (current_time, session['id']))
|
||||
|
||||
return {
|
||||
'winners': winners,
|
||||
'losers': losers,
|
||||
'total_win': total_win,
|
||||
'result': result
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"结算失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
def _settle_roulette_bets(self, chat_id: int, game_type: str, result_number: int,
|
||||
banker_id: int) -> Dict:
|
||||
"""结算轮盘游戏下注
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
result_number: 结果数字(0-36)
|
||||
banker_id: 庄家ID
|
||||
|
||||
Returns:
|
||||
结算详情字典
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
|
||||
session = self.get_active_casino_session(chat_id, game_type)
|
||||
if not session:
|
||||
raise ValueError("没有活跃的游戏会话")
|
||||
|
||||
if session['banker_id'] != banker_id:
|
||||
raise ValueError("只有庄家可以结算游戏")
|
||||
|
||||
bets = self.get_pending_bets(chat_id, game_type)
|
||||
winners = []
|
||||
losers = []
|
||||
total_win = 0
|
||||
|
||||
# 轮盘数字对应的颜色(欧式轮盘,0为绿色)
|
||||
roulette_red = {1, 3, 5, 7, 9, 12, 14, 16, 18, 19, 21, 23, 25, 27, 30, 32, 34, 36}
|
||||
result_color = '绿色' if result_number == 0 else ('红色' if result_number in roulette_red else '黑色')
|
||||
result_odd_even = None if result_number == 0 else ('奇数' if result_number % 2 == 1 else '偶数')
|
||||
result_big_small = None if result_number == 0 else ('小' if 1 <= result_number <= 18 else '大')
|
||||
|
||||
result_range = None
|
||||
if 1 <= result_number <= 12:
|
||||
result_range = '1-12'
|
||||
elif 13 <= result_number <= 24:
|
||||
result_range = '13-24'
|
||||
elif 25 <= result_number <= 36:
|
||||
result_range = '25-36'
|
||||
|
||||
for bet in bets:
|
||||
is_win = False
|
||||
multiplier = bet['multiplier']
|
||||
|
||||
bet_category = bet.get('bet_category')
|
||||
if bet_category == '数字':
|
||||
if bet.get('bet_number') == result_number:
|
||||
is_win = True
|
||||
elif bet_category == '颜色':
|
||||
if bet.get('bet_value') == result_color:
|
||||
is_win = True
|
||||
elif bet_category == '奇偶':
|
||||
if result_odd_even and bet.get('bet_value') == result_odd_even:
|
||||
is_win = True
|
||||
elif bet_category == '大小':
|
||||
if result_big_small and bet.get('bet_value') == result_big_small:
|
||||
is_win = True
|
||||
elif bet_category == '区间':
|
||||
if result_range and bet.get('bet_value') == result_range:
|
||||
is_win = True
|
||||
|
||||
if is_win:
|
||||
win_amount = int(bet['amount'] * multiplier)
|
||||
house_fee = session['house_fee']
|
||||
actual_win = int(win_amount * (1 - house_fee))
|
||||
winners.append({
|
||||
'user_id': bet['user_id'],
|
||||
'amount': bet['amount'],
|
||||
'win_amount': actual_win,
|
||||
'bet_id': bet['id']
|
||||
})
|
||||
total_win += actual_win
|
||||
else:
|
||||
losers.append({
|
||||
'user_id': bet['user_id'],
|
||||
'amount': bet['amount'],
|
||||
'bet_id': bet['id']
|
||||
})
|
||||
|
||||
try:
|
||||
result_str = str(result_number)
|
||||
for bet in bets:
|
||||
is_win = False
|
||||
multiplier = bet['multiplier']
|
||||
|
||||
bet_category = bet.get('bet_category')
|
||||
if bet_category == '数字':
|
||||
if bet.get('bet_number') == result_number:
|
||||
is_win = True
|
||||
elif bet_category == '颜色':
|
||||
if bet.get('bet_value') == result_color:
|
||||
is_win = True
|
||||
elif bet_category == '奇偶':
|
||||
if result_odd_even and bet.get('bet_value') == result_odd_even:
|
||||
is_win = True
|
||||
elif bet_category == '大小':
|
||||
if result_big_small and bet.get('bet_value') == result_big_small:
|
||||
is_win = True
|
||||
elif bet_category == '区间':
|
||||
if result_range and bet.get('bet_value') == result_range:
|
||||
is_win = True
|
||||
|
||||
if is_win:
|
||||
win_amount = int(bet['amount'] * multiplier)
|
||||
actual_win = int(win_amount * (1 - session['house_fee']))
|
||||
self.add_points(bet['user_id'], actual_win, 'casino_win',
|
||||
f"赌场游戏{game_type}赢得")
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, win_amount = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result_str, actual_win, current_time, bet['id']))
|
||||
else:
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result_str, current_time, bet['id']))
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE casino_sessions
|
||||
SET status = 'closed', settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (current_time, session['id']))
|
||||
|
||||
return {
|
||||
'winners': winners,
|
||||
'losers': losers,
|
||||
'total_win': total_win,
|
||||
'result': result_str,
|
||||
'result_number': result_number
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"结算失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
def _settle_blackjack_bets(self, chat_id: int, game_type: str, hands_dict: Dict,
|
||||
banker_id: int) -> Dict:
|
||||
"""结算21点游戏下注
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
hands_dict: 手牌字典 {user_id: {'cards': [2,3,4], 'status': 'stood'}, ...}
|
||||
banker_id: 庄家ID
|
||||
|
||||
Returns:
|
||||
结算详情字典
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
|
||||
session = self.get_active_casino_session(chat_id, game_type)
|
||||
if not session:
|
||||
raise ValueError("没有活跃的游戏会话")
|
||||
|
||||
if session['banker_id'] != banker_id:
|
||||
raise ValueError("只有庄家可以结算游戏")
|
||||
|
||||
bets = self.get_pending_bets(chat_id, game_type)
|
||||
banker_hand = hands_dict.get(0, {}) # 0表示庄家
|
||||
banker_cards = banker_hand.get('cards', [])
|
||||
banker_status = banker_hand.get('status', 'stood')
|
||||
banker_points = self._calculate_blackjack_points(banker_cards)
|
||||
banker_is_busted = banker_status == 'busted'
|
||||
banker_is_blackjack = banker_status == 'blackjack'
|
||||
|
||||
winners = []
|
||||
losers = []
|
||||
total_win = 0
|
||||
|
||||
for bet in bets:
|
||||
user_id = bet['user_id']
|
||||
player_hand = hands_dict.get(user_id, {})
|
||||
player_cards = player_hand.get('cards', [])
|
||||
player_status = player_hand.get('status', 'stood')
|
||||
player_points = self._calculate_blackjack_points(player_cards)
|
||||
player_is_busted = player_status == 'busted'
|
||||
player_is_blackjack = player_status == 'blackjack'
|
||||
|
||||
is_win = False
|
||||
multiplier = bet['multiplier']
|
||||
|
||||
if player_is_busted:
|
||||
is_win = False
|
||||
elif player_is_blackjack:
|
||||
if banker_is_blackjack:
|
||||
# 双方都是黑杰克,平局(返还下注)
|
||||
is_win = False
|
||||
self.add_points(user_id, bet['amount'], 'casino_blackjack_push',
|
||||
"21点游戏平局,返还下注")
|
||||
else:
|
||||
# 玩家黑杰克,赢得1.5倍
|
||||
is_win = True
|
||||
multiplier = session.get('blackjack_multiplier', 1.5)
|
||||
elif banker_is_busted:
|
||||
is_win = True
|
||||
elif player_points > banker_points:
|
||||
is_win = True
|
||||
elif player_points == banker_points:
|
||||
# 平局,返还下注
|
||||
is_win = False
|
||||
self.add_points(user_id, bet['amount'], 'casino_blackjack_push',
|
||||
"21点游戏平局,返还下注")
|
||||
|
||||
if is_win:
|
||||
win_amount = int(bet['amount'] * multiplier)
|
||||
house_fee = session['house_fee']
|
||||
actual_win = int(win_amount * (1 - house_fee))
|
||||
winners.append({
|
||||
'user_id': user_id,
|
||||
'amount': bet['amount'],
|
||||
'win_amount': actual_win,
|
||||
'bet_id': bet['id']
|
||||
})
|
||||
total_win += actual_win
|
||||
elif player_points != banker_points:
|
||||
# 输家:包括爆牌或点数小于庄家
|
||||
losers.append({
|
||||
'user_id': user_id,
|
||||
'amount': bet['amount'],
|
||||
'bet_id': bet['id']
|
||||
})
|
||||
|
||||
try:
|
||||
for bet in bets:
|
||||
user_id = bet['user_id']
|
||||
player_hand = hands_dict.get(user_id, {})
|
||||
player_cards = player_hand.get('cards', [])
|
||||
player_status = player_hand.get('status', 'stood')
|
||||
player_points = self._calculate_blackjack_points(player_cards)
|
||||
player_is_busted = player_status == 'busted'
|
||||
player_is_blackjack = player_status == 'blackjack'
|
||||
|
||||
is_win = False
|
||||
multiplier = bet['multiplier']
|
||||
|
||||
if player_is_busted:
|
||||
is_win = False
|
||||
elif player_is_blackjack:
|
||||
if banker_is_blackjack:
|
||||
is_win = False
|
||||
self.add_points(user_id, bet['amount'], 'casino_blackjack_push',
|
||||
"21点游戏平局,返还下注")
|
||||
else:
|
||||
is_win = True
|
||||
multiplier = session.get('blackjack_multiplier', 1.5)
|
||||
elif banker_is_busted:
|
||||
is_win = True
|
||||
elif player_points > banker_points:
|
||||
is_win = True
|
||||
elif player_points == banker_points:
|
||||
is_win = False
|
||||
self.add_points(user_id, bet['amount'], 'casino_blackjack_push',
|
||||
"21点游戏平局,返还下注")
|
||||
|
||||
# 生成结果字符串,包含玩家状态信息
|
||||
if player_is_busted:
|
||||
player_desc = "爆牌"
|
||||
elif player_is_blackjack:
|
||||
player_desc = f"{player_points}点(黑杰克)"
|
||||
else:
|
||||
player_desc = f"{player_points}点"
|
||||
|
||||
banker_desc = "爆牌" if banker_is_busted else (f"{banker_points}点(黑杰克)" if banker_is_blackjack else f"{banker_points}点")
|
||||
result_str = f"庄家{banker_desc} vs 玩家{player_desc}"
|
||||
|
||||
if is_win:
|
||||
win_amount = int(bet['amount'] * multiplier)
|
||||
actual_win = int(win_amount * (1 - session['house_fee']))
|
||||
self.add_points(user_id, actual_win, 'casino_win',
|
||||
f"赌场游戏{game_type}赢得")
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, win_amount = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result_str, actual_win, current_time, bet['id']))
|
||||
elif player_points == banker_points:
|
||||
# 平局,已返还下注(在上面的逻辑中已处理)
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result_str, current_time, bet['id']))
|
||||
else:
|
||||
# 输家:包括爆牌或点数小于庄家
|
||||
cursor.execute("""
|
||||
UPDATE casino_bets
|
||||
SET status = 'settled', result = ?, settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (result_str, current_time, bet['id']))
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE casino_sessions
|
||||
SET status = 'closed', settled_at = ?
|
||||
WHERE id = ?
|
||||
""", (current_time, session['id']))
|
||||
|
||||
return {
|
||||
'winners': winners,
|
||||
'losers': losers,
|
||||
'total_win': total_win,
|
||||
'result': f"庄家{banker_points}点"
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"结算失败: {e}", exc_info=True)
|
||||
raise
|
||||
|
||||
def _calculate_blackjack_points(self, cards: List[int]) -> int:
|
||||
"""计算21点手牌点数
|
||||
|
||||
Args:
|
||||
cards: 手牌列表,A=1,J/Q/K=10,其他为本身值(1-13,其中11=J, 12=Q, 13=K)
|
||||
|
||||
Returns:
|
||||
点数
|
||||
"""
|
||||
points = 0
|
||||
ace_count = 0
|
||||
|
||||
for card in cards:
|
||||
if card == 1:
|
||||
ace_count += 1
|
||||
points += 11
|
||||
elif card >= 11:
|
||||
points += 10
|
||||
else:
|
||||
points += card
|
||||
|
||||
# 处理A的1/11选择
|
||||
while points > 21 and ace_count > 0:
|
||||
points -= 10
|
||||
ace_count -= 1
|
||||
|
||||
return points
|
||||
|
||||
def close_casino_session(self, chat_id: int, game_type: str):
|
||||
"""关闭游戏会话
|
||||
|
||||
Args:
|
||||
chat_id: 会话ID
|
||||
game_type: 游戏类型
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE casino_sessions
|
||||
SET status = 'closed', settled_at = ?
|
||||
WHERE chat_id = ? AND game_type = ? AND status = 'open'
|
||||
""", (current_time, chat_id, game_type))
|
||||
|
||||
# ===== 21点手牌管理 =====
|
||||
|
||||
def create_blackjack_hand(self, session_id: int, user_id: int,
|
||||
hand_data: List[int], hand_status: str = 'playing') -> int:
|
||||
"""创建21点手牌记录
|
||||
|
||||
Args:
|
||||
session_id: 会话ID
|
||||
user_id: 用户ID(0表示庄家)
|
||||
hand_data: 手牌列表
|
||||
hand_status: 手牌状态
|
||||
|
||||
Returns:
|
||||
hand_id
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
hand_data_json = json.dumps(hand_data)
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO casino_blackjack_hands
|
||||
(session_id, user_id, hand_data, hand_status, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?)
|
||||
""", (session_id, user_id, hand_data_json, hand_status, current_time, current_time))
|
||||
|
||||
return cursor.lastrowid
|
||||
|
||||
def get_blackjack_hand(self, session_id: int, user_id: int) -> Optional[Dict]:
|
||||
"""获取21点手牌
|
||||
|
||||
Args:
|
||||
session_id: 会话ID
|
||||
user_id: 用户ID
|
||||
|
||||
Returns:
|
||||
手牌信息字典或None
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT * FROM casino_blackjack_hands
|
||||
WHERE session_id = ? AND user_id = ?
|
||||
ORDER BY id DESC LIMIT 1
|
||||
""", (session_id, user_id))
|
||||
row = cursor.fetchone()
|
||||
|
||||
if row:
|
||||
hand_dict = dict(row)
|
||||
hand_dict['hand_data'] = json.loads(hand_dict['hand_data'])
|
||||
return hand_dict
|
||||
return None
|
||||
|
||||
def update_blackjack_hand(self, session_id: int, user_id: int,
|
||||
hand_data: List[int], hand_status: str):
|
||||
"""更新21点手牌
|
||||
|
||||
Args:
|
||||
session_id: 会话ID
|
||||
user_id: 用户ID
|
||||
hand_data: 手牌列表
|
||||
hand_status: 手牌状态
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
current_time = int(time.time())
|
||||
hand_data_json = json.dumps(hand_data)
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE casino_blackjack_hands
|
||||
SET hand_data = ?, hand_status = ?, updated_at = ?
|
||||
WHERE session_id = ? AND user_id = ?
|
||||
""", (hand_data_json, hand_status, current_time, session_id, user_id))
|
||||
|
||||
def get_all_blackjack_hands(self, session_id: int) -> Dict[int, Dict]:
|
||||
"""获取所有21点手牌(用于结算)
|
||||
|
||||
Args:
|
||||
session_id: 会话ID
|
||||
|
||||
Returns:
|
||||
手牌字典 {user_id: {'cards': [...], 'status': ...}, ...}
|
||||
"""
|
||||
cursor = self.conn.cursor()
|
||||
cursor.execute("""
|
||||
SELECT * FROM casino_blackjack_hands
|
||||
WHERE session_id = ?
|
||||
ORDER BY user_id, id DESC
|
||||
""", (session_id,))
|
||||
rows = cursor.fetchall()
|
||||
|
||||
hands_dict = {}
|
||||
for row in rows:
|
||||
hand_dict = dict(row)
|
||||
user_id = hand_dict['user_id']
|
||||
# 只保留最新的手牌(每个用户)
|
||||
if user_id not in hands_dict:
|
||||
hands_dict[user_id] = {
|
||||
'cards': json.loads(hand_dict['hand_data']),
|
||||
'status': hand_dict['hand_status']
|
||||
}
|
||||
|
||||
return hands_dict
|
||||
|
||||
def close(self):
|
||||
"""关闭数据库连接"""
|
||||
if self._conn:
|
||||
|
||||
@@ -92,9 +92,9 @@ class AlchemyGame(BaseGame):
|
||||
end_time = start_time + cost_time * 60
|
||||
remaining_seconds = end_time - current_time
|
||||
|
||||
# 如果冒险已完成,自动清理状态,允许炼金
|
||||
# 如果冒险已完成,提示用户先回收奖励,不允许炼金
|
||||
if remaining_seconds <= 0:
|
||||
self.db.delete_game_state(0, user_id, 'adventure')
|
||||
return f"❌ 你有待回收的冒险奖励!\n\n💡 请先使用 `.adventure` 回收冒险奖励后再进行炼金。"
|
||||
else:
|
||||
# 冒险未完成,返回错误提示
|
||||
remaining_minutes = remaining_seconds // 60
|
||||
|
||||
@@ -113,6 +113,31 @@ def get_help_message() -> str:
|
||||
- `.ai <问题>` - 向AI提问(支持多用户对话,等待10秒后回答)
|
||||
- `.aiconfig host=xxx port=xxx model=xxx` - 配置Ollama服务地址和模型
|
||||
|
||||
### 🎰 赌场系统
|
||||
**大小游戏**:
|
||||
- `.赌场 大小 open <最小> <最大> <赔率>` - 庄家开启大小游戏
|
||||
- `.赌场 大小 bet <大/小> <金额>` - 下注
|
||||
- `.赌场 大小 status` - 查看状态
|
||||
- `.赌场 大小 settle` - 庄家结算(系统随机)
|
||||
- `.赌场 大小 cancel` - 庄家放弃游戏(返还下注)
|
||||
|
||||
**轮盘游戏**:
|
||||
- `.赌场 轮盘 open <最小> <最大>` - 庄家开启轮盘游戏
|
||||
- `.赌场 轮盘 bet <类型> <选项> <金额>` - 下注(数字/颜色/奇偶/大小/区间)
|
||||
- `.赌场 轮盘 status` - 查看状态
|
||||
- `.赌场 轮盘 settle` - 庄家结算(系统随机0-36)
|
||||
- `.赌场 轮盘 cancel` - 庄家放弃游戏(返还下注)
|
||||
|
||||
**21点游戏**:
|
||||
- `.赌场 21点 open <最小> <最大> [黑杰克倍数]` - 庄家开启21点游戏
|
||||
- `.赌场 21点 bet <金额>` - 下注
|
||||
- `.赌场 21点 deal` - 庄家发牌
|
||||
- `.赌场 21点 hit` - 玩家要牌
|
||||
- `.赌场 21点 stand` - 玩家停牌
|
||||
- `.赌场 21点 status` - 查看状态
|
||||
- `.赌场 21点 settle` - 庄家结算
|
||||
- `.赌场 21点 cancel` - 庄家放弃游戏(返还下注)
|
||||
|
||||
### 其他
|
||||
- `.help` - 显示帮助
|
||||
- `.stats` - 查看个人统计
|
||||
|
||||
1473
games/casino.py
Normal file
1473
games/casino.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -82,12 +82,10 @@ class GiftGame(BaseGame):
|
||||
receiver_id = user['user_id']
|
||||
|
||||
# 获取接收者名称用于显示
|
||||
receiver_user = self.db.get_or_create_user(receiver_id)
|
||||
receiver_name = receiver_user.get('username', f"用户{receiver_id}")
|
||||
receiver_name = self.db.get_user_display_name(receiver_id)
|
||||
|
||||
# 获取发送者名称用于显示
|
||||
sender_user = self.db.get_or_create_user(sender_id)
|
||||
sender_name = sender_user.get('username', f"用户{sender_id}")
|
||||
sender_name = self.db.get_user_display_name(sender_id)
|
||||
|
||||
# 验证参数
|
||||
if points <= 0:
|
||||
@@ -184,7 +182,7 @@ class GiftGame(BaseGame):
|
||||
|
||||
for record in records:
|
||||
timestamp = datetime.fromtimestamp(record['created_at']).strftime('%m-%d %H:%M')
|
||||
receiver_name = record.get('receiver_name', f"用户{record['receiver_id']}")
|
||||
receiver_name = self.db.get_user_display_name(record['receiver_id'])
|
||||
points = record['points']
|
||||
message = record.get('message', '')
|
||||
|
||||
@@ -214,7 +212,7 @@ class GiftGame(BaseGame):
|
||||
|
||||
for record in records:
|
||||
timestamp = datetime.fromtimestamp(record['created_at']).strftime('%m-%d %H:%M')
|
||||
sender_name = record.get('sender_name', f"用户{record['sender_id']}")
|
||||
sender_name = self.db.get_user_display_name(record['sender_id'])
|
||||
points = record['points']
|
||||
message = record.get('message', '')
|
||||
|
||||
|
||||
@@ -379,10 +379,12 @@ class IdiomGame(BaseGame):
|
||||
text += f"**当前链长**:{state_data['chain_length']}\n\n"
|
||||
|
||||
user_count = state_data['participants'][str(user_id)]
|
||||
text += f"@用户{user_id} 成功次数:{user_count}\n\n"
|
||||
user_display_name = self.db.get_user_display_name(user_id)
|
||||
text += f"@{user_display_name} 成功次数:{user_count}\n\n"
|
||||
|
||||
if mentioned_user_id:
|
||||
text += f"已指定 @用户{mentioned_user_id} 接龙\n\n"
|
||||
mentioned_display_name = self.db.get_user_display_name(mentioned_user_id)
|
||||
text += f"已指定 @{mentioned_display_name} 接龙\n\n"
|
||||
else:
|
||||
text += "任何人都可以接龙\n\n"
|
||||
|
||||
@@ -416,7 +418,8 @@ class IdiomGame(BaseGame):
|
||||
state_data['next_user_id'] = next_user_id
|
||||
self.db.save_game_state(chat_id, 0, 'idiom', state_data)
|
||||
|
||||
return f"✅ 已指定 @用户{next_user_id} 接龙"
|
||||
next_user_display_name = self.db.get_user_display_name(next_user_id)
|
||||
return f"✅ 已指定 @{next_user_display_name} 接龙"
|
||||
|
||||
def _reject_idiom(self, chat_id: int, user_id: int, idiom: str) -> str:
|
||||
"""裁判拒绝词语
|
||||
@@ -509,7 +512,8 @@ class IdiomGame(BaseGame):
|
||||
|
||||
# 下一位
|
||||
if state_data.get('next_user_id'):
|
||||
text += f"**下一位**:@用户{state_data['next_user_id']}\n\n"
|
||||
next_user_display_name = self.db.get_user_display_name(state_data['next_user_id'])
|
||||
text += f"**下一位**:@{next_user_display_name}\n\n"
|
||||
else:
|
||||
text += f"**下一位**:任何人都可以接龙\n\n"
|
||||
|
||||
@@ -522,7 +526,8 @@ class IdiomGame(BaseGame):
|
||||
reverse=True
|
||||
)
|
||||
for idx, (uid, count) in enumerate(sorted_participants[:5], 1):
|
||||
text += f"{idx}. @用户{uid} - {count}次\n"
|
||||
user_display_name = self.db.get_user_display_name(int(uid))
|
||||
text += f"{idx}. @{user_display_name} - {count}次\n"
|
||||
text += "\n"
|
||||
|
||||
# 最近成语
|
||||
@@ -588,7 +593,8 @@ class IdiomGame(BaseGame):
|
||||
reverse=True
|
||||
)
|
||||
for idx, (uid, count) in enumerate(sorted_participants, 1):
|
||||
text += f"{idx}. @用户{uid} - {count}次\n"
|
||||
user_display_name = self.db.get_user_display_name(int(uid))
|
||||
text += f"{idx}. @{user_display_name} - {count}次\n"
|
||||
# 更新统计
|
||||
try:
|
||||
for _ in range(count):
|
||||
|
||||
@@ -126,7 +126,7 @@ class PointsGame(BaseGame):
|
||||
for i, user in enumerate(leaderboard):
|
||||
rank = i + 1
|
||||
medal = medals[i] if i < len(medals) else "🏅"
|
||||
username = user.get('username', f"用户{user['user_id']}")
|
||||
username = self.db.get_user_display_name(user['user_id'])
|
||||
points = user.get('points', 0)
|
||||
|
||||
text += f"{medal} **第 {rank} 名** {username}\n"
|
||||
|
||||
@@ -208,6 +208,12 @@ async def handle_command(game_type: str, command: str,
|
||||
game = AIChatGame()
|
||||
return await game.handle(command, chat_id, user_id)
|
||||
|
||||
# 赌场系统
|
||||
if game_type == 'casino':
|
||||
from games.casino import CasinoGame
|
||||
game = CasinoGame()
|
||||
return await game.handle(command, chat_id, user_id)
|
||||
|
||||
# 未知游戏类型
|
||||
logger.warning(f"未知游戏类型: {game_type}")
|
||||
return "❌ 未知的游戏类型"
|
||||
|
||||
@@ -80,6 +80,10 @@ class CommandParser:
|
||||
# 统计
|
||||
'.stats': 'stats',
|
||||
'.统计': 'stats',
|
||||
|
||||
# 赌场系统
|
||||
'.赌场': 'casino',
|
||||
'.casino': 'casino',
|
||||
}
|
||||
|
||||
# 机器人名称模式(用于从@消息中提取)
|
||||
|
||||
Reference in New Issue
Block a user