From 27aee22f6205f0b26e6333d1291a75305e049801 Mon Sep 17 00:00:00 2001 From: ninemine <1371605831@qq.com> Date: Wed, 29 Oct 2025 15:49:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=B3=A8=E5=86=8C=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .tasks/2025-10-29_1_add-user-register.md | 209 +++++++++++++++++++++++ core/database.py | 53 ++++++ games/gift.py | 44 +++-- routers/callback.py | 41 +++++ utils/parser.py | 4 + 5 files changed, 341 insertions(+), 10 deletions(-) create mode 100644 .tasks/2025-10-29_1_add-user-register.md diff --git a/.tasks/2025-10-29_1_add-user-register.md b/.tasks/2025-10-29_1_add-user-register.md new file mode 100644 index 0000000..666665b --- /dev/null +++ b/.tasks/2025-10-29_1_add-user-register.md @@ -0,0 +1,209 @@ +# 背景 +文件名:2025-10-29_1_add-user-register.md +创建于:2025-10-29_15:42:51 +创建者:admin +主分支:main +任务分支:main +Yolo模式:Off + +# 任务描述 +在WPS Bot Game项目中添加用户注册系统,让用户可以通过 `.register name` 命令将用户ID与名称绑定。 + +## 核心需求 +1. 用户可以通过 `.register name` 命令注册或更新自己的名称 +2. 这个名称是全局的,所有游戏和功能都可以使用 +3. 积分赠送功能需要支持通过用户名查找用户,而不仅仅是用户ID +4. 需要一个全局的用户名称解析机制 + +## 问题背景 +目前消息传递中的用户ID和用户名不一致,使用起来非常困难。比如赠送积分时指定用户ID太麻烦了。添加注册系统后,可以使用 `.gift username points` 而不是 `.gift 123456 points`。 + +# 项目概览 + +## 项目结构 +``` +WPSBotGame/ +├── app.py # FastAPI主应用 +├── config.py # 配置管理 +├── core/ +│ ├── database.py # SQLite数据库操作 +│ ├── middleware.py # 中间件 +│ └── models.py # 数据模型 +├── routers/ +│ ├── callback.py # Callback路由处理 +│ └── health.py # 健康检查 +├── games/ # 游戏模块 +│ ├── base.py # 游戏基类 +│ ├── gift.py # 积分赠送系统 +│ └── ... # 其他游戏 +└── utils/ + ├── parser.py # 指令解析 + └── message.py # 消息发送 +``` + +# 分析 + +## 当前状态 +1. `users` 表已经有 `username` 字段,但没有被充分利用 +2. `get_or_create_user()` 可以接收 username 参数,但实际调用时没有传 +3. `gift.py` 目前只能通过用户ID进行赠送 +4. 缺少通过用户名查找用户的功能 + +## 关键技术点 +1. **数据库层**: 需要添加根据 username 查找用户的方 +2. 需要添加更新用户名的功能 +3. **指令解析层**: 需要添加 `.register` 指令的识别 +4. **路由层**: 需要添加 register 类型的处理逻辑 +5. **应用层**: 需要实现注册逻辑,并修改 gift 功能支持用户名 + +# 提议的解决方案 + +## 方案概述 +1. 在 `database.py` 中添加两个方法: + - `get_user_by_name(username: str)` - 根据用户名查找用户 + - `update_user_name(user_id: int, username: str)` - 更新用户名称 +2. 在 `parser.py` 中添加 `.register` 指令映射 +3. 在 `callback.py` 中添加 register 类型的处理逻辑 +4. 修改 `gift.py` 支持通过用户名赠送积分 +5. 创建注册处理逻辑(可以单独文件或集成到 callback) + +## 设计决策 +- 用户名作为额外的查找方式,但不替代 user_id(保持数据库主键稳定) +- 用户名不强制唯一(允许相同昵称) +- 注册功能独立于游戏模块,放在顶层处理 + +# 当前执行步骤:"3. 等待用户确认" + +# 详细实施计划 + +## 文件1: core/database.py + +### 添加方法1: get_user_by_name() +```python +def get_user_by_name(self, username: str) -> Optional[Dict]: + """根据用户名查找用户 + + Args: + username: 用户名 + + Returns: + 用户信息字典,如果不存在返回None + """ +``` + +### 添加方法2: update_user_name() +```python +def update_user_name(self, user_id: int, username: str) -> bool: + """更新用户名称 + + Args: + user_id: 用户ID + username: 新用户名 + + Returns: + 是否成功 + """ +``` + +### 添加数据库索引 +在 `init_tables()` 方法中添加: +```sql +CREATE INDEX IF NOT EXISTS idx_username ON users(username) +``` + +## 文件2: utils/parser.py + +### 修改 COMMAND_MAP +在现有的 COMMAND_MAP 中添加: +```python +'.register': 'register', +'.注册': 'register', +``` + +## 文件3: routers/callback.py + +### 在 handle_command() 中添加处理 +在现有游戏类型判断后添加: +```python +# 注册系统 +if game_type == 'register': + return await handle_register_command(command, chat_id, user_id) +``` + +### 添加处理函数 +```python +async def handle_register_command(command: str, chat_id: int, user_id: int) -> str: + """处理注册命令 + + Args: + command: 完整指令 ".register name" + chat_id: 会话ID + user_id: 用户ID + + Returns: + 注册结果消息 + """ +``` + +## 文件4: games/gift.py + +### 修改 _process_gift_command() +修改参数解析逻辑,支持用户名和用户ID两种格式: +```python +def _process_gift_command(self, args: str, sender_id: int) -> str: + # 尝试解析为用户名或用户ID + parts = args.split(maxsplit=2) + + if len(parts) < 2: + return "❌ 指令格式错误..." + + # 检查第一部分是用户名还是ID + receiver_input = parts[0] + + if receiver_input.isdigit(): + # 是数字,作为用户ID处理 + receiver_id = int(receiver_input) + else: + # 是用户名,通过数据库查找 + user = self.db.get_user_by_name(receiver_input) + if not user: + return f"❌ 未找到用户: {receiver_input}" + receiver_id = user['user_id'] + + # 继续原有的赠送逻辑... +``` + +### 更新帮助信息 +在 _get_gift_help() 中更新示例: +``` +".gift username 50 生日快乐" +".gift 123456 50" (仍支持用户ID) +``` + +# 任务进度 + +## 2025-10-29_15:42:51 +- 已修改:.tasks/2025-10-29_1_add-user-register.md +- 更改:创建任务文件 +- 原因:开始实施用户注册系统 +- 阻碍因素:无 +- 状态:未确认 + +## 2025-10-29_15:46:38 +- 已修改: + - core/database.py(添加 get_user_by_name() 和 update_user_name() 方法,添加 username 索引) + - utils/parser.py(添加 .register 和 .注册 命令映射) + - routers/callback.py(添加 register 命令处理和 handle_register_command() 函数) + - games/gift.py(修改 _process_gift_command() 支持用户名查找,更新帮助信息) +- 更改: + - 数据库层:添加用户名称查询和更新功能 + - 命令解析:注册 .register 命令映射 + - 路由处理:实现用户注册逻辑 + - 赠送系统:支持通过用户名或ID进行积分赠送 +- 原因:实现用户注册系统,让用户可以使用名称替代用户ID +- 阻碍因素:无 +- 状态:未确认 + +# 最终审查 +[等待实施] + diff --git a/core/database.py b/core/database.py index 3e64e10..3323ccc 100644 --- a/core/database.py +++ b/core/database.py @@ -88,6 +88,12 @@ class Database: ON game_states(chat_id, user_id) """) + # 创建用户名索引 + cursor.execute(""" + CREATE INDEX IF NOT EXISTS idx_username + ON users(username) + """) + # 游戏统计表 cursor.execute(""" CREATE TABLE IF NOT EXISTS game_stats ( @@ -158,6 +164,53 @@ class Database: 'last_active': current_time } + def get_user_by_name(self, username: str) -> Optional[Dict]: + """根据用户名查找用户 + + Args: + username: 用户名 + + Returns: + 用户信息字典,如果不存在返回None + """ + cursor = self.conn.cursor() + cursor.execute( + "SELECT * FROM users WHERE username = ?", + (username,) + ) + row = cursor.fetchone() + + if row: + return dict(row) + return None + + def update_user_name(self, user_id: int, username: str) -> bool: + """更新用户名称 + + Args: + user_id: 用户ID + username: 新用户名 + + Returns: + 是否成功 + """ + try: + # 确保用户存在 + self.get_or_create_user(user_id) + + cursor = self.conn.cursor() + cursor.execute( + "UPDATE users SET username = ? WHERE user_id = ?", + (username, user_id) + ) + + logger.info(f"用户 {user_id} 更新名称为: {username}") + return True + + except Exception as e: + logger.error(f"更新用户名失败: user_id={user_id}, username={username}, error={e}", exc_info=True) + return False + # ===== 游戏状态相关操作 ===== def get_game_state(self, chat_id: int, user_id: int, game_type: str) -> Optional[Dict]: diff --git a/games/gift.py b/games/gift.py index 9152864..68a591c 100644 --- a/games/gift.py +++ b/games/gift.py @@ -55,18 +55,39 @@ class GiftGame(BaseGame): Returns: 处理结果消息 """ - # 解析参数:.gift [message] + # 解析参数:.gift [message] parts = args.split(maxsplit=2) if len(parts) < 2: - return "❌ 指令格式错误!\n\n正确格式:`.gift <用户ID> <积分数量> [附赠消息]`\n\n示例:\n`.gift 123 50 生日快乐`\n`.gift 456 100`" + return "❌ 指令格式错误!\n\n正确格式:`.gift <用户名或ID> <积分数量> [附赠消息]`\n\n示例:\n`.gift 张三 50 生日快乐`\n`.gift 123 50`\n`.gift 456 100`" + # 解析积分数量 try: - receiver_id = int(parts[0]) points = int(parts[1]) message = parts[2] if len(parts) > 2 else None except ValueError: - return "❌ 用户ID和积分数量必须是数字!" + return "❌ 积分数量必须是数字!" + + # 检查第一部分是用户名还是ID + receiver_input = parts[0] + + if receiver_input.isdigit(): + # 是数字,作为用户ID处理 + receiver_id = int(receiver_input) + else: + # 是用户名,通过数据库查找 + user = self.db.get_user_by_name(receiver_input) + if not user: + return f"❌ 未找到用户: {receiver_input}\n\n请确认用户名是否正确,或使用用户ID。" + receiver_id = user['user_id'] + + # 获取接收者名称用于显示 + receiver_user = self.db.get_or_create_user(receiver_id) + receiver_name = receiver_user.get('username', f"用户{receiver_id}") + + # 获取发送者名称用于显示 + sender_user = self.db.get_or_create_user(sender_id) + sender_name = sender_user.get('username', f"用户{sender_id}") # 验证参数 if points <= 0: @@ -92,8 +113,8 @@ class GiftGame(BaseGame): receiver_points_after = self.db.get_user_points(receiver_id) text = f"## 🎁 积分赠送成功!\n\n" - text += f"**赠送者**:用户{sender_id}\n\n" - text += f"**接收者**:用户{receiver_id}\n\n" + text += f"**赠送者**:{sender_name}\n\n" + text += f"**接收者**:{receiver_name}\n\n" text += f"**赠送积分**:{points} 分\n\n" if message: @@ -212,7 +233,7 @@ class GiftGame(BaseGame): """ text = f"## 🎁 积分赠送系统\n\n" text += f"### 基础用法\n" - text += f"- `.gift <用户ID> <积分数量> [附赠消息]` - 赠送积分\n" + text += f"- `.gift <用户名或ID> <积分数量> [附赠消息]` - 赠送积分\n" text += f"- `.gift stats` - 查看赠送统计\n" text += f"- `.gift sent` - 查看发送记录\n" text += f"- `.gift received` - 查看接收记录\n" @@ -226,13 +247,16 @@ class GiftGame(BaseGame): text += f"### 示例\n" text += f"```\n" - text += f".gift 123 50 生日快乐\n" - text += f".gift 456 100 感谢你的帮助\n" - text += f".gift 789 200\n" + text += f".gift 张三 50 生日快乐\n" + text += f".gift 123 50 (使用用户ID)\n" + text += f".gift 李四 100 感谢你的帮助\n" + text += f".gift 王五 200\n" text += f".gift stats\n" text += f"```\n\n" text += f"### 说明\n" + text += f"- 支持使用用户名或用户ID进行赠送\n" + text += f"- 使用用户名需要先通过 `.register` 注册名称\n" text += f"- 所有赠送都有完整记录\n" text += f"- 赠送和接收都会在积分记录中显示\n" text += f"- 赠送是单向的,无需对方确认\n" diff --git a/routers/callback.py b/routers/callback.py index 5cdae70..7b824d3 100644 --- a/routers/callback.py +++ b/routers/callback.py @@ -117,6 +117,10 @@ async def handle_command(game_type: str, command: str, from games.base import get_stats_message return get_stats_message(user_id) + # 注册系统 + if game_type == 'register': + return await handle_register_command(command, chat_id, user_id) + # 骰娘游戏 if game_type == 'dice': from games.dice import DiceGame @@ -185,3 +189,40 @@ async def handle_command(game_type: str, command: str, logger.error(f"处理游戏指令异常: {e}", exc_info=True) return f"❌ 处理指令时出错: {str(e)}" + +async def handle_register_command(command: str, chat_id: int, user_id: int) -> str: + """处理注册命令 + + Args: + command: 完整指令 ".register name" + chat_id: 会话ID + user_id: 用户ID + + Returns: + 注册结果消息 + """ + try: + # 提取参数 + _, args = CommandParser.extract_command_args(command) + args = args.strip() + + # 验证参数 + if not args: + return "❌ 请提供要注册的名称!\n\n正确格式:`.register <名称>`\n\n示例:\n`.register 张三`\n`.register 小明`" + + if len(args) > 20: + return "❌ 名称过长!最多支持20个字符。" + + # 更新用户名称 + db = get_db() + success = db.update_user_name(user_id, args) + + if success: + return f"✅ 注册成功!\n\n**您的名称**:{args}\n\n之后您可以使用这个名称参与各种游戏和功能。" + else: + return "❌ 注册失败!请稍后重试。" + + except Exception as e: + logger.error(f"处理注册指令错误: {e}", exc_info=True) + return f"❌ 处理指令出错: {str(e)}" + diff --git a/utils/parser.py b/utils/parser.py index 61bfd84..a21f466 100644 --- a/utils/parser.py +++ b/utils/parser.py @@ -63,6 +63,10 @@ class CommandParser: # 统计 '.stats': 'stats', '.统计': 'stats', + + # 用户注册系统 + '.register': 'register', + '.注册': 'register', } # 机器人名称模式(用于从@消息中提取)