修复:升级 Pydantic v2 兼容性,修复插件加载错误
- 更新 danding_points 配置使用 pydantic_settings.BaseSettings 和 SettingsConfigDict - 更新 onmyoji_gacha 配置使用 pydantic_settings.BaseSettings - 修复 danding_qqpush 配置加载使用 model_validate 替代 parse_obj - 添加 group_horse_racing 插件的详细 README 文档 这些修复解决了 Pydantic v2 迁移中的导入错误和 API 变更问题。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,26 +1,31 @@
|
|||||||
from pydantic import BaseSettings, validator
|
from pydantic import field_validator
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class Config(BaseSettings):
|
class Config(BaseSettings):
|
||||||
"""Points system configuration."""
|
"""Points system configuration."""
|
||||||
|
|
||||||
|
model_config = SettingsConfigDict(
|
||||||
|
env_prefix="DANDING_",
|
||||||
|
case_sensitive=True,
|
||||||
|
extra="ignore",
|
||||||
|
)
|
||||||
|
|
||||||
POINTS_DB_FILE: str = "data/danding_points/points.db"
|
POINTS_DB_FILE: str = "data/danding_points/points.db"
|
||||||
POINTS_MAX_BALANCE: int = 0 # 0 = unlimited
|
POINTS_MAX_BALANCE: int = 0 # 0 = unlimited
|
||||||
POINTS_MAX_PER_OPERATION: int = 0 # 0 = unlimited
|
POINTS_MAX_PER_OPERATION: int = 0 # 0 = unlimited
|
||||||
POINTS_LOG_RETENTION_DAYS: int = 365
|
POINTS_LOG_RETENTION_DAYS: int = 365
|
||||||
|
|
||||||
class Config:
|
@field_validator("POINTS_MAX_BALANCE", "POINTS_MAX_PER_OPERATION", "POINTS_LOG_RETENTION_DAYS")
|
||||||
env_prefix = "DANDING_"
|
@classmethod
|
||||||
case_sensitive = True
|
|
||||||
|
|
||||||
@validator("POINTS_MAX_BALANCE", "POINTS_MAX_PER_OPERATION", "POINTS_LOG_RETENTION_DAYS")
|
|
||||||
def validate_non_negative(cls, v):
|
def validate_non_negative(cls, v):
|
||||||
if v < 0:
|
if v < 0:
|
||||||
raise ValueError("Value must be non-negative")
|
raise ValueError("Value must be non-negative")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@validator("POINTS_DB_FILE")
|
@field_validator("POINTS_DB_FILE")
|
||||||
|
@classmethod
|
||||||
def validate_db_path(cls, v):
|
def validate_db_path(cls, v):
|
||||||
if not v:
|
if not v:
|
||||||
raise ValueError("Database file path cannot be empty")
|
raise ValueError("Database file path cannot be empty")
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
|
|
||||||
|
|
||||||
# 加载配置
|
# 加载配置
|
||||||
plugin_config = Config.parse_obj(get_driver().config)
|
plugin_config = Config.model_validate(get_driver().config.dict())
|
||||||
|
|
||||||
|
|
||||||
def register_routes():
|
def register_routes():
|
||||||
|
|||||||
212
danding_bot/plugins/group_horse_racing/README.md
Normal file
212
danding_bot/plugins/group_horse_racing/README.md
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
# Group Horse Racing 插件
|
||||||
|
|
||||||
|
群赛马插件 - 一个支持群组内多人参与的赛马游戏,集成积分系统和下注功能。
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
这是一个完整的群组赛马游戏系统,支持以下核心功能:
|
||||||
|
|
||||||
|
- **马匹报名**:用户可以报名参加赛马比赛
|
||||||
|
- **下注系统**:支持用户对参赛马匹进行下注
|
||||||
|
- **自动比赛**:基于随机算法的赛马进程模拟
|
||||||
|
- **积分集成**:与 danding_points 积分系统无缝集成
|
||||||
|
- **自动撤回**:支持配置消息自动撤回时间
|
||||||
|
- **多房间支持**:支持群组和私聊两种模式
|
||||||
|
|
||||||
|
## 命令列表
|
||||||
|
|
||||||
|
### 基础命令
|
||||||
|
|
||||||
|
| 命令 | 说明 | 示例 |
|
||||||
|
|------|------|------|
|
||||||
|
| `/赛马报名` | 报名参加赛马比赛 | `/赛马报名` |
|
||||||
|
| `/赛马开赛` | 开始比赛(需要至少2匹马) | `/赛马开赛` |
|
||||||
|
| `/赛马帮助` | 显示帮助信息 | `/赛马帮助` |
|
||||||
|
|
||||||
|
## 配置说明
|
||||||
|
|
||||||
|
### 环境变量
|
||||||
|
|
||||||
|
所有配置项都可通过环境变量设置,前缀为 `GROUP_HORSE_RACING_`
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 测试模式
|
||||||
|
GROUP_HORSE_RACING_TEST_MODE=false
|
||||||
|
|
||||||
|
# 测试用户ID集合
|
||||||
|
GROUP_HORSE_RACING_TESTERS=[]
|
||||||
|
|
||||||
|
# 测试群组ID集合
|
||||||
|
GROUP_HORSE_RACING_TEST_GROUPS=[]
|
||||||
|
|
||||||
|
# 允许的群组ID集合
|
||||||
|
GROUP_HORSE_RACING_ALLOWED_GROUPS=[]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 游戏配置
|
||||||
|
|
||||||
|
| 配置项 | 默认值 | 说明 |
|
||||||
|
|--------|--------|------|
|
||||||
|
| `PARTICIPANT_REWARD` | 50 | 参赛者奖励积分 |
|
||||||
|
| `CHAMPION_REWARD` | 200 | 冠军奖励积分 |
|
||||||
|
| `MIN_BET` | 10 | 最小下注积分 |
|
||||||
|
| `MIN_ODDS` | 1.2 | 最小赔率 |
|
||||||
|
| `RACE_DISTANCE` | 100 | 比赛距离 |
|
||||||
|
| `RACE_TICK_INTERVAL` | 5 | 比赛更新间隔(秒) |
|
||||||
|
|
||||||
|
### 消息撤回配置
|
||||||
|
|
||||||
|
可配置不同类型消息的自动撤回时间(秒,0表示不撤回):
|
||||||
|
|
||||||
|
```python
|
||||||
|
MESSAGE_RECALL = {
|
||||||
|
"race_update": 30, # 比赛更新消息
|
||||||
|
"registration": 180, # 报名确认消息
|
||||||
|
"bet_confirm": 180, # 下注确认消息
|
||||||
|
"cancel_confirm": 60, # 取消确认消息
|
||||||
|
"error": 60, # 错误消息
|
||||||
|
"race_result": 0, # 比赛结果(不撤回)
|
||||||
|
"leaderboard": 0, # 排行榜(不撤回)
|
||||||
|
"help": 0, # 帮助信息(不撤回)
|
||||||
|
"odds_display": 0, # 赔率显示(不撤回)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据库配置
|
||||||
|
|
||||||
|
```python
|
||||||
|
RACE_DB_FILE = "data/group_horse_racing/race.db"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心模块
|
||||||
|
|
||||||
|
### models.py
|
||||||
|
|
||||||
|
定义了游戏中的数据模型:
|
||||||
|
|
||||||
|
- **RoomState**:房间状态枚举(WAITING、RUNNING、FINISHED、INTERRUPTED)
|
||||||
|
- **HorseState**:马匹状态枚举(READY、RACING、FINISHED)
|
||||||
|
- **Horse**:马匹数据类,包含所有者ID、名称、位置、状态
|
||||||
|
- **Bet**:下注数据类,包含用户ID、马匹名称、下注金额
|
||||||
|
- **Room**:房间数据类,管理马匹、下注、比赛状态
|
||||||
|
- **RaceResult**:比赛结果数据类,记录比赛统计信息
|
||||||
|
|
||||||
|
### race_engine.py
|
||||||
|
|
||||||
|
比赛引擎,负责比赛逻辑:
|
||||||
|
|
||||||
|
- **start_race()**:启动比赛循环
|
||||||
|
- **_race_loop()**:主比赛循环,每个tick更新马匹位置
|
||||||
|
- **_determine_champion()**:确定冠军(处理平局情况)
|
||||||
|
|
||||||
|
比赛采用高斯分布随机算法,每个tick马匹随机前进一定距离。
|
||||||
|
|
||||||
|
### points_service.py
|
||||||
|
|
||||||
|
积分服务,与 danding_points 插件集成:
|
||||||
|
|
||||||
|
- **spend_bet_points()**:扣除下注积分(支持重试)
|
||||||
|
- **refund_bet_points()**:退还下注积分
|
||||||
|
- **payout_winnings()**:支付中奖积分
|
||||||
|
- **reward_participant()**:奖励参赛者
|
||||||
|
- **reward_champion()**:奖励冠军
|
||||||
|
- **get_balance()**:获取用户余额
|
||||||
|
|
||||||
|
### message_service.py
|
||||||
|
|
||||||
|
消息服务,处理消息发送和自动撤回:
|
||||||
|
|
||||||
|
- **send_with_recall()**:发送消息并根据配置自动撤回
|
||||||
|
- **_schedule_recall()**:异步调度消息撤回
|
||||||
|
- **clear_pending_recalls()**:清除待撤回消息任务
|
||||||
|
|
||||||
|
### room_store.py
|
||||||
|
|
||||||
|
房间存储,管理比赛房间的生命周期:
|
||||||
|
|
||||||
|
- 支持并发访问控制(使用asyncio.Lock)
|
||||||
|
- 房间持久化存储
|
||||||
|
- 房间清理和过期处理
|
||||||
|
|
||||||
|
### commands.py
|
||||||
|
|
||||||
|
命令处理器,实现所有用户命令:
|
||||||
|
|
||||||
|
- **handle_register()**:处理报名命令
|
||||||
|
- **handle_start()**:处理开赛命令
|
||||||
|
- **handle_help()**:处理帮助命令
|
||||||
|
|
||||||
|
## 使用流程
|
||||||
|
|
||||||
|
### 基本游戏流程
|
||||||
|
|
||||||
|
1. **报名阶段**
|
||||||
|
- 用户执行 `/赛马报名` 命令
|
||||||
|
- 系统检查权限和房间容量(最多8匹马)
|
||||||
|
- 成功报名后获得参赛奖励
|
||||||
|
|
||||||
|
2. **下注阶段**
|
||||||
|
- 用户可对参赛马匹进行下注
|
||||||
|
- 下注金额需满足最小下注要求
|
||||||
|
- 下注积分从用户账户扣除
|
||||||
|
|
||||||
|
3. **比赛阶段**
|
||||||
|
- 房主执行 `/赛马开赛` 命令
|
||||||
|
- 系统启动比赛引擎
|
||||||
|
- 每个tick更新马匹位置
|
||||||
|
- 首先到达终点的马匹为冠军
|
||||||
|
|
||||||
|
4. **结算阶段**
|
||||||
|
- 冠军获得冠军奖励
|
||||||
|
- 下注冠军的用户获得中奖积分
|
||||||
|
- 比赛结果保存到数据库
|
||||||
|
|
||||||
|
## 权限控制
|
||||||
|
|
||||||
|
插件支持两种权限模式:
|
||||||
|
|
||||||
|
### 测试模式(TEST_MODE=true)
|
||||||
|
|
||||||
|
- 仅允许 `TEST_GROUPS` 中的群组使用
|
||||||
|
- 仅允许 `TESTERS` 中的用户在私聊中使用
|
||||||
|
|
||||||
|
### 正常模式(TEST_MODE=false)
|
||||||
|
|
||||||
|
- 仅允许 `ALLOWED_GROUPS` 中的群组使用
|
||||||
|
- 私聊中禁用
|
||||||
|
|
||||||
|
## 依赖关系
|
||||||
|
|
||||||
|
- **必需**:`danding_bot.plugins.danding_points` - 积分系统插件
|
||||||
|
|
||||||
|
## 数据存储
|
||||||
|
|
||||||
|
比赛数据存储在SQLite数据库中:
|
||||||
|
|
||||||
|
- 位置:`data/group_horse_racing/race.db`
|
||||||
|
- 存储内容:比赛历史、结果统计、用户数据
|
||||||
|
|
||||||
|
## 并发控制
|
||||||
|
|
||||||
|
- 使用 `asyncio.Lock` 保证房间操作的线程安全
|
||||||
|
- 支持多个房间同时进行比赛
|
||||||
|
- 每个房间有独立的锁和异步任务
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
- 权限检查:无权限时返回错误提示
|
||||||
|
- 房间检查:房间不存在或已满时返回错误
|
||||||
|
- 参赛人数检查:少于2匹马时无法开赛
|
||||||
|
- 积分检查:积分不足时下注失败
|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
项目包含 `test_commands.py` 用于测试各项功能。
|
||||||
|
|
||||||
|
## 扩展建议
|
||||||
|
|
||||||
|
- 支持更多赛马属性(速度、耐力等)
|
||||||
|
- 实现赔率动态计算
|
||||||
|
- 添加排行榜功能
|
||||||
|
- 支持马匹升级系统
|
||||||
|
- 实现更复杂的下注规则
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
from pydantic import BaseSettings
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
import os
|
import os
|
||||||
|
|
||||||
class Config(BaseSettings):
|
class Config(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(extra="ignore")
|
||||||
|
|
||||||
# 抽卡概率配置
|
# 抽卡概率配置
|
||||||
RARITY_PROBABILITY: dict = {
|
RARITY_PROBABILITY: dict = {
|
||||||
"R": 78.75,
|
"R": 78.75,
|
||||||
@@ -9,18 +11,18 @@ class Config(BaseSettings):
|
|||||||
"SSR": 1.0,
|
"SSR": 1.0,
|
||||||
"SP": 0.25
|
"SP": 0.25
|
||||||
}
|
}
|
||||||
|
|
||||||
# 每日抽卡限制
|
# 每日抽卡限制
|
||||||
DAILY_LIMIT: int = 3
|
DAILY_LIMIT: int = 3
|
||||||
|
|
||||||
# 数据文件路径
|
# 数据文件路径
|
||||||
DB_FILE: str = "data/onmyoji_gacha/gacha.db"
|
DB_FILE: str = "data/onmyoji_gacha/gacha.db"
|
||||||
DAILY_DRAWS_FILE: str = "data/onmyoji_gacha/daily_draws.json" # 保留用于迁移
|
DAILY_DRAWS_FILE: str = "data/onmyoji_gacha/daily_draws.json" # 保留用于迁移
|
||||||
USER_STATS_FILE: str = "data/onmyoji_gacha/user_stats.json" # 保留用于迁移
|
USER_STATS_FILE: str = "data/onmyoji_gacha/user_stats.json" # 保留用于迁移
|
||||||
|
|
||||||
# 式神图片目录
|
# 式神图片目录
|
||||||
SHIKIGAMI_IMG_DIR: str = "data/chouka/"
|
SHIKIGAMI_IMG_DIR: str = "data/chouka/"
|
||||||
|
|
||||||
# 触发指令
|
# 触发指令
|
||||||
GACHA_COMMANDS: list = ["抽卡","抽奖", "召唤"]
|
GACHA_COMMANDS: list = ["抽卡","抽奖", "召唤"]
|
||||||
STATS_COMMANDS: list = ["我的抽卡","我的抽奖", "我的图鉴"]
|
STATS_COMMANDS: list = ["我的抽卡","我的抽奖", "我的图鉴"]
|
||||||
@@ -28,7 +30,7 @@ class Config(BaseSettings):
|
|||||||
TRIPLE_GACHA_COMMANDS: list = ["三连", "三连抽"]
|
TRIPLE_GACHA_COMMANDS: list = ["三连", "三连抽"]
|
||||||
ACHIEVEMENT_COMMANDS: list = ["查询成就", "抽卡成就"]
|
ACHIEVEMENT_COMMANDS: list = ["查询成就", "抽卡成就"]
|
||||||
INTRO_COMMANDS: list = ["抽卡介绍", "抽卡说明", "抽卡帮助"]
|
INTRO_COMMANDS: list = ["抽卡介绍", "抽卡说明", "抽卡帮助"]
|
||||||
|
|
||||||
# 成就系统配置
|
# 成就系统配置
|
||||||
ACHIEVEMENTS: dict = {
|
ACHIEVEMENTS: dict = {
|
||||||
"consecutive_days_30_1": {
|
"consecutive_days_30_1": {
|
||||||
@@ -99,20 +101,17 @@ class Config(BaseSettings):
|
|||||||
"type": "no_ssr_streak"
|
"type": "no_ssr_streak"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# 权限配置
|
# 权限配置
|
||||||
ALLOWED_GROUP_ID: int = 621016172
|
ALLOWED_GROUP_ID: int = 621016172
|
||||||
ALLOWED_USER_ID: int = 1424473282
|
ALLOWED_USER_ID: int = 1424473282
|
||||||
|
|
||||||
# 特殊概率用户配置
|
# 特殊概率用户配置
|
||||||
SPECIAL_PROBABILITY_USERS: list = ["1424473282"] # 100%抽到SSR或SP的用户列表
|
SPECIAL_PROBABILITY_USERS: list = ["1424473282"] # 100%抽到SSR或SP的用户列表
|
||||||
|
|
||||||
# Web后台管理配置
|
# Web后台管理配置
|
||||||
WEB_ADMIN_TOKEN: str = os.getenv("WEB_ADMIN_TOKEN", "onmyoji_admin_token_2024")
|
WEB_ADMIN_TOKEN: str = os.getenv("WEB_ADMIN_TOKEN", "onmyoji_admin_token_2024")
|
||||||
WEB_ADMIN_PORT: int = int(os.getenv("WEB_ADMIN_PORT", "8080"))
|
WEB_ADMIN_PORT: int = int(os.getenv("WEB_ADMIN_PORT", "8080"))
|
||||||
|
|
||||||
# 时区
|
# 时区
|
||||||
TIMEZONE: str = "Asia/Shanghai"
|
TIMEZONE: str = "Asia/Shanghai"
|
||||||
|
|
||||||
class Config:
|
|
||||||
extra = "ignore"
|
|
||||||
Reference in New Issue
Block a user