新增赌场系统

This commit is contained in:
2025-10-30 16:11:50 +08:00
parent 4bddd4339f
commit 12aac846cc
6 changed files with 1072 additions and 0 deletions

View File

@@ -0,0 +1,443 @@
# 背景
文件名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纯数据库表方案推荐
**优点**
- 数据结构清晰,便于查询统计
- 支持历史记录追踪
- 并发安全,利用数据库事务
- 易于扩展复杂查询
**缺点**
- 需要维护额外的表结构
- 稍微复杂一些
**决策**:采用此方案
### 方案Bgame_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类实现大小游戏所有功能
- 注册指令映射和路由
- 添加帮助信息
- 原因:按照详细实施计划完成全部功能开发
- 阻碍因素:无
- 状态:成功
# 最终审查
待完成

View File

@@ -129,6 +129,54 @@ 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)
)
""")
# 赌场游戏会话表
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)
""")
logger.info("数据库表初始化完成")
# ===== 用户相关操作 =====
@@ -587,6 +635,225 @@ class Database:
rows = cursor.fetchall()
return [dict(row) for row in rows]
# ===== 赌场相关操作 =====
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:
"""创建新的赌场游戏会话
Args:
chat_id: 会话ID
game_type: 游戏类型
banker_id: 庄家ID
min_bet: 最小下注金额
max_bet: 最大下注金额
multiplier: 赔率
house_fee: 抽水率
Returns:
session_id
"""
cursor = self.conn.cursor()
current_time = int(time.time())
# 检查是否已有活跃的会话
cursor.execute("""
SELECT id FROM casino_sessions
WHERE chat_id = ? AND game_type = ? AND status = 'open'
""", (chat_id, game_type))
existing = cursor.fetchone()
if existing:
return existing['id']
cursor.execute("""
INSERT INTO casino_sessions
(chat_id, game_type, banker_id, min_bet, max_bet, multiplier, house_fee, status, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, 'open', ?)
""", (chat_id, game_type, banker_id, min_bet, max_bet, multiplier, house_fee, current_time))
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) -> int:
"""创建下注记录
Args:
chat_id: 会话ID
game_type: 游戏类型
user_id: 用户ID
bet_type: 下注类型
amount: 下注金额
multiplier: 赔率
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)
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)
""", (chat_id, game_type, user_id, bet_type, amount, multiplier, current_time))
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) -> 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 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))
def close(self):
"""关闭数据库连接"""
if self._conn:

View File

@@ -113,6 +113,12 @@ def get_help_message() -> str:
- `.ai <问题>` - 向AI提问支持多用户对话等待10秒后回答
- `.aiconfig host=xxx port=xxx model=xxx` - 配置Ollama服务地址和模型
### 🎰 赌场系统
- `.赌场 大小 open <最小> <最大> <赔率>` - 庄家开启大小游戏
- `.赌场 大小 bet <大/小> <金额>` - 下注
- `.赌场 大小 status` - 查看状态
- `.赌场 大小 settle <大/小>` - 庄家结算
### 其他
- `.help` - 显示帮助
- `.stats` - 查看个人统计

346
games/casino.py Normal file
View File

@@ -0,0 +1,346 @@
"""赌场游戏模块"""
import logging
from games.base import BaseGame
from utils.parser import CommandParser
from core.database import get_db
logger = logging.getLogger(__name__)
class CasinoGame(BaseGame):
"""赌场游戏"""
def __init__(self):
"""初始化游戏"""
super().__init__()
self.db = get_db()
async def handle(self, command: str, chat_id: int, user_id: int) -> str:
"""处理赌场指令
Args:
command: 完整指令
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
try:
# 提取参数
_, args = CommandParser.extract_command_args(command)
args = args.strip().lower()
# 没有参数,显示帮助
if not args:
return self.get_help()
# 解析第一个参数为游戏类型
parts = args.split(maxsplit=1)
game_type = parts[0]
sub_args = parts[1] if len(parts) > 1 else ""
# 根据游戏类型分发
if game_type == '大小':
return await self._handle_bigsmall(sub_args, chat_id, user_id)
elif game_type in ['help', '帮助']:
return self.get_help()
else:
return f"❌ 暂不支持的游戏类型: {game_type}\n\n支持的类型:大小"
except Exception as e:
logger.error(f"处理赌场指令错误: {e}", exc_info=True)
return f"❌ 处理指令出错: {str(e)}"
async def _handle_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""处理大小游戏
Args:
args: 子命令和参数
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
if not args:
return self._get_bigsmall_help()
# 解析子命令
parts = args.split(maxsplit=1)
action = parts[0].lower()
sub_args = parts[1] if len(parts) > 1 else ""
if action in ['open', '开启', '开始']:
return await self._open_bigsmall(sub_args, chat_id, user_id)
elif action in ['bet', '下注', '']:
return await self._bet_bigsmall(sub_args, chat_id, user_id)
elif action in ['status', '状态', '查看']:
return await self._status_bigsmall(chat_id)
elif action in ['settle', '结算', '开奖']:
return await self._settle_bigsmall(sub_args, chat_id, user_id)
elif action in ['help', '帮助']:
return self._get_bigsmall_help()
else:
return f"❌ 未知命令: {action}\n\n{self._get_bigsmall_help()}"
async def _open_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""庄家开启大小游戏
Args:
args: 参数字符串 "<最小下注> <最大下注> <赔率>"
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
try:
# 解析参数
parts = args.split()
if len(parts) != 3:
return "❌ 参数格式错误!\n\n正确格式:`.赌场 大小 open <最小下注> <最大下注> <赔率>`\n\n示例:`.赌场 大小 open 10 100 2.0`"
try:
min_bet = int(parts[0])
max_bet = int(parts[1])
multiplier = float(parts[2])
except ValueError:
return "❌ 参数必须是数字!"
# 参数验证
if min_bet <= 0 or max_bet <= 0:
return "❌ 下注金额必须大于0"
if min_bet > max_bet:
return "❌ 最小下注不能大于最大下注!"
if multiplier <= 0:
return "❌ 赔率必须大于0"
if max_bet > 10000:
return "❌ 最大下注不能超过10000分"
# 检查是否已有活跃游戏
existing = self.db.get_active_casino_session(chat_id, '大小')
if existing:
return "⚠️ 当前已有进行中的大小游戏,请先结算后再开启新游戏。"
# 创建游戏会话
session_id = self.db.create_casino_session(
chat_id=chat_id,
game_type='大小',
banker_id=user_id,
min_bet=min_bet,
max_bet=max_bet,
multiplier=multiplier,
house_fee=0.05 # 固定5%抽水
)
text = f"## 🎰 大小游戏已开启\n\n"
text += f"**庄家**:用户{user_id}\n\n"
text += f"**最小下注**{min_bet}\n\n"
text += f"**最大下注**{max_bet}\n\n"
text += f"**赔率**{multiplier}\n\n"
text += f"**抽水率**5%\n\n"
text += "---\n\n"
text += "💡 提示:玩家可以使用 `.赌场 大小 bet <大/小> <金额>` 进行下注"
return text
except Exception as e:
logger.error(f"开启游戏失败: {e}", exc_info=True)
return f"❌ 开启游戏失败: {str(e)}"
async def _bet_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""玩家下注
Args:
args: 参数字符串 "<大/小> <下注金额>"
chat_id: 会话ID
user_id: 用户ID
Returns:
回复消息
"""
try:
# 解析参数
parts = args.split()
if len(parts) != 2:
return "❌ 参数格式错误!\n\n正确格式:`.赌场 大小 bet <大/小> <下注金额>`\n\n示例:`.赌场 大小 bet 大 50`"
bet_type = parts[0]
try:
amount = int(parts[1])
except ValueError:
return "❌ 下注金额必须是数字!"
# 验证下注类型
if bet_type not in ['', '']:
return f"❌ 下注类型错误!只支持'''',您输入的是:{bet_type}"
# 检查是否有活跃的会话
session = self.db.get_active_casino_session(chat_id, '大小')
if not session:
return "❌ 当前没有进行中的游戏,请等待庄家开启游戏。"
# 验证下注金额
if amount < session['min_bet']:
return f"❌ 下注金额太小!最小下注:{session['min_bet']}"
if amount > session['max_bet']:
return f"❌ 下注金额太大!最大下注:{session['max_bet']}"
# 检查用户积分
user_points = self.db.get_user_points(user_id)
if user_points['points'] < amount:
return f"❌ 积分不足!需要 {amount} 分,当前可用 {user_points['points']}"
# 扣除积分
if not self.db.consume_points(user_id, amount, 'casino_bet', f"大小游戏下注{bet_type}"):
return "❌ 扣除积分失败!"
# 记录下注
bet_id = self.db.create_casino_bet(
chat_id=chat_id,
game_type='大小',
user_id=user_id,
bet_type=bet_type,
amount=amount,
multiplier=session['multiplier']
)
# 获取更新后的积分
updated_points = self.db.get_user_points(user_id)
text = f"## 🎲 下注成功\n\n"
text += f"**下注类型**{bet_type}\n\n"
text += f"**下注金额**{amount}\n\n"
text += f"**赔率**{session['multiplier']}\n\n"
text += f"**剩余积分**{updated_points['points']}\n\n"
text += "---\n\n"
text += "💡 等待庄家结算结果..."
return text
except Exception as e:
logger.error(f"下注失败: {e}", exc_info=True)
return f"❌ 下注失败: {str(e)}"
async def _status_bigsmall(self, chat_id: int) -> str:
"""查看当前游戏状态
Args:
chat_id: 会话ID
Returns:
状态消息
"""
session = self.db.get_active_casino_session(chat_id, '大小')
if not session:
return "❌ 当前没有进行中的大小游戏"
# 获取所有待结算下注
bets = self.db.get_pending_bets(chat_id, '大小')
# 统计下注情况
bet_big = sum(b['amount'] for b in bets if b['bet_type'] == '')
bet_small = sum(b['amount'] for b in bets if b['bet_type'] == '')
text = f"## 🎰 大小游戏状态\n\n"
text += f"**庄家**:用户{session['banker_id']}\n\n"
text += f"**最小下注**{session['min_bet']}\n\n"
text += f"**最大下注**{session['max_bet']}\n\n"
text += f"**赔率**{session['multiplier']}\n\n"
text += "---\n\n"
text += f"**当前下注统计**\n"
text += f"- 压大:{bet_big} 分({len([b for b in bets if b['bet_type'] == ''])}注)\n"
text += f"- 压小:{bet_small} 分({len([b for b in bets if b['bet_type'] == ''])}注)\n"
text += f"- 总下注:{len(bets)}\n\n"
text += "---\n\n"
text += "💡 庄家可以使用 `.赌场 大小 settle <大/小>` 结算"
return text
async def _settle_bigsmall(self, args: str, chat_id: int, user_id: int) -> str:
"""庄家结算游戏
Args:
args: 结果 "<大/小>"
chat_id: 会话ID
user_id: 用户ID
Returns:
结算结果消息
"""
try:
if not args:
return "❌ 请指定结算结果!\n\n正确格式:`.赌场 大小 settle <大/小>`"
result = args.strip()
if result not in ['', '']:
return f"❌ 结果类型错误!只支持'''',您输入的是:{result}"
# 结算
settlement = self.db.settle_casino_bets(chat_id, '大小', result, user_id)
# 构建结算报告
text = f"## 🎰 大小游戏结算\n\n"
text += f"**结算结果**{result}\n\n"
text += "---\n\n"
text += f"**赢家**{len(settlement['winners'])}\n"
text += f"**输家**{len(settlement['losers'])}\n\n"
# 显示赢家详情
if settlement['winners']:
text += "**赢家明细**\n"
for winner in settlement['winners']:
text += f"- 用户{winner['user_id']}: 下注{winner['amount']}分,赢得{winner['win_amount']}\n"
text += "\n"
# 显示输家详情
if settlement['losers']:
text += "**输家明细**\n"
for loser in settlement['losers']:
text += f"- 用户{loser['user_id']}: 下注{loser['amount']}\n"
text += "\n"
text += "---\n\n"
text += "✅ 游戏已结束,欢迎再次游戏!"
return text
except ValueError as e:
return f"{str(e)}"
except Exception as e:
logger.error(f"结算失败: {e}", exc_info=True)
return f"❌ 结算失败: {str(e)}"
def _get_bigsmall_help(self) -> str:
"""获取大小游戏帮助信息"""
return """## 🎰 大小游戏帮助
### 庄家命令
- `.赌场 大小 open <最小> <最大> <赔率>` - 开启游戏
- 示例:`.赌场 大小 open 10 100 2.0`
### 玩家命令
- `.赌场 大小 bet <大/小> <金额>` - 下注
- 示例:`.赌场 大小 bet 大 50`
- 示例:`.赌场 大小 bet 小 30`
### 通用命令
- `.赌场 大小 status` - 查看当前状态
- `.赌场 大小 settle <大/小>` - 庄家结算(仅庄家)
### 游戏规则
- 玩家下注后积分立即扣除
- 结算后根据结果发放奖励
- 系统抽水5%
- 赔率由庄家设置
"""
def get_help(self) -> str:
"""获取帮助信息"""
return self._get_bigsmall_help()

View File

@@ -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 "❌ 未知的游戏类型"

View File

@@ -80,6 +80,10 @@ class CommandParser:
# 统计
'.stats': 'stats',
'.统计': 'stats',
# 赌场系统
'.赌场': 'casino',
'.casino': 'casino',
}
# 机器人名称模式(用于从@消息中提取)