From 7d28e2e2aae15dbc962053e9998655094b4d8d97 Mon Sep 17 00:00:00 2001 From: ninemine <1371605831@qq.com> Date: Tue, 28 Oct 2025 17:49:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=B8=BA=E5=8F=91=E8=B5=B7/?= =?UTF-8?q?=E6=8E=A5=E5=8F=97=E5=AF=B9=E5=B1=80=E4=BB=A5=E9=81=BF=E5=BC=80?= =?UTF-8?q?at=E6=97=A0=E6=B3=95=E8=8E=B7=E5=8F=96id=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .tasks/2025-10-28_1_gomoku.md | 24 ++++- games/base.py | 3 +- games/gomoku.py | 192 ++++++++++++++++++++++------------ routers/callback.py | 1 + 4 files changed, 149 insertions(+), 71 deletions(-) diff --git a/.tasks/2025-10-28_1_gomoku.md b/.tasks/2025-10-28_1_gomoku.md index a15dee8..fa19b4b 100644 --- a/.tasks/2025-10-28_1_gomoku.md +++ b/.tasks/2025-10-28_1_gomoku.md @@ -228,7 +228,29 @@ if game_type == 'gomoku': - 改进错误提示,显示实际接收到的参数内容 - 将 callback.py 中的消息内容日志级别从 DEBUG 改为 INFO,便于追踪 - 原因:进一步诊断用户ID识别失败的问题,添加调试信息帮助定位问题 -- 阻碍因素:需要用户测试并提供日志输出来确定实际的消息格式 +- 阻碍因素:WPS callback不提供被@用户的ID信息 +- 状态:不成功 + +## [2025-10-28 17:55:00] +- 已修改: + - games/gomoku.py - 重构游戏发起机制,从@用户改为挑战-接受模式 + - games/base.py - 更新全局帮助信息 + - routers/callback.py - 添加完整callback数据日志 +- 更改: + - **核心架构变更**:从"@用户发起对战"改为"挑战-接受"机制 + - 新增 `_create_challenge()` 方法 - 用户发起挑战 + - 新增 `_accept_challenge()` 方法 - 其他用户接受挑战 + - 新增 `_cancel_challenge()` 方法 - 取消自己的挑战 + - 删除 `_parse_opponent()` 方法(不再需要) + - 删除 `_start_game()` 方法(由新方法替代) + - 更新游戏池数据结构,添加 `challenges` 列表 + - 更新所有帮助信息和错误提示 + - 指令变更: + - `.gomoku challenge` / `.gomoku start` - 发起挑战 + - `.gomoku accept` / `.gomoku join` - 接受挑战 + - `.gomoku cancel` - 取消挑战 +- 原因:WPS callback消息内容中@用户只是文本形式(如"@揭英飙"),不包含user_id,无法实现@用户发起对战 +- 阻碍因素:无 - 状态:未确认 # 最终审查 diff --git a/games/base.py b/games/base.py index bcf48e4..f8a3ac5 100644 --- a/games/base.py +++ b/games/base.py @@ -74,7 +74,8 @@ def get_help_message() -> str: - `.idiom blacklist` - 查看黑名单 ### ⚫ 五子棋 -- `.gomoku @对手` - 发起对战 +- `.gomoku challenge` - 发起挑战 +- `.gomoku accept` - 接受挑战 - `.gomoku A1` - 落子 - `.gomoku show` - 显示棋盘 - `.gomoku resign` - 认输 diff --git a/games/gomoku.py b/games/gomoku.py index d25fb43..ff5d639 100644 --- a/games/gomoku.py +++ b/games/gomoku.py @@ -54,6 +54,18 @@ class GomokuGame(BaseGame): if action in ['help', '帮助']: return self.get_help() + # 发起挑战 + if action in ['challenge', 'start', '挑战', '开始']: + return self._create_challenge(chat_id, user_id) + + # 接受挑战 + if action in ['accept', 'join', '接受', '加入']: + return self._accept_challenge(chat_id, user_id) + + # 取消挑战 + if action in ['cancel', '取消']: + return self._cancel_challenge(chat_id, user_id) + # 列出所有对战 if action in ['list', '列表', '查看']: return self._list_games(chat_id) @@ -75,48 +87,14 @@ class GomokuGame(BaseGame): if coord is not None: return self._make_move(chat_id, user_id, action) - # 尝试解析为@对手(开始游戏) - opponent_id = self._parse_opponent(args) - logger.info(f"五子棋指令解析 - opponent_id: {opponent_id}") - if opponent_id is not None: - return self._start_game(chat_id, user_id, opponent_id) - # 未识别的指令 logger.warning(f"五子棋未识别的指令 - args: {args}") - return f"❌ 未识别的指令:{args}\n\n💡 提示:\n- 要开始游戏,请使用 `.gomoku` 然后@对手\n- 要落子,请使用 `.gomoku A1`(坐标格式)\n- 要查看帮助,请使用 `.gomoku help`" + return f"❌ 未识别的指令:{args}\n\n💡 提示:\n- 发起挑战:`.gomoku challenge`\n- 接受挑战:`.gomoku accept`\n- 落子:`.gomoku A1`\n- 查看帮助:`.gomoku help`" except Exception as e: logger.error(f"处理五子棋指令错误: {e}", exc_info=True) return f"❌ 处理指令出错: {str(e)}" - def _parse_opponent(self, args: str) -> Optional[int]: - """解析@对手的用户ID - - Args: - args: 参数字符串 - - Returns: - 用户ID或None - """ - # WPS格式: 或其他变体 - # 尝试多种格式 - patterns = [ - r']*>', # - r']*>', # - r'user_id="(\d+)"', # 直接查找 user_id="xxx" - r'user_id=\'(\d+)\'', # 直接查找 user_id='xxx' - ] - - for pattern in patterns: - match = re.search(pattern, args) - if match: - try: - return int(match.group(1)) - except ValueError: - pass - - return None - def _get_game_pool(self, chat_id: int) -> Dict[str, Any]: """获取游戏池 @@ -163,56 +141,93 @@ class GomokuGame(BaseGame): return None - def _start_game(self, chat_id: int, user_id: int, opponent_id: int) -> str: - """开始新游戏 + def _create_challenge(self, chat_id: int, user_id: int) -> str: + """创建挑战 Args: chat_id: 会话ID user_id: 发起者ID - opponent_id: 对手ID Returns: 提示消息 """ - # 检查是否与自己对战 - if user_id == opponent_id: - return "❌ 不能和自己下棋哦!" - # 获取游戏池 pool = self._get_game_pool(chat_id) games = pool.get("games", []) + challenges = pool.get("challenges", []) + + # 检查用户是否已经在对战中 + user_game = self._find_user_game(chat_id, user_id) + if user_game: + return "⚠️ 你已经在对战中!\n\n输入 `.gomoku show` 查看棋盘" + + # 检查用户是否已经发起了挑战 + for challenge in challenges: + if challenge["challenger_id"] == user_id: + return "⚠️ 你已经发起了一个挑战!\n\n等待其他人接受,或输入 `.gomoku cancel` 取消挑战" + + # 创建挑战 + current_time = int(time.time()) + challenge = { + "challenger_id": user_id, + "created_at": current_time + } + + challenges.append(challenge) + pool["challenges"] = challenges + self._save_game_pool(chat_id, pool) + + text = f"## 🎯 五子棋挑战\n\n" + text += f" 发起了五子棋挑战!\n\n" + text += f"💡 想要应战吗?输入 `.gomoku accept` 接受挑战" + + return text + + def _accept_challenge(self, chat_id: int, user_id: int) -> str: + """接受挑战 + + Args: + chat_id: 会话ID + user_id: 接受者ID + + Returns: + 提示消息 + """ + # 获取游戏池 + pool = self._get_game_pool(chat_id) + games = pool.get("games", []) + challenges = pool.get("challenges", []) + + if not challenges: + return "⚠️ 当前没有挑战可以接受\n\n输入 `.gomoku challenge` 发起挑战" + + # 检查用户是否已经在对战中 + user_game = self._find_user_game(chat_id, user_id) + if user_game: + return "⚠️ 你已经在对战中!\n\n输入 `.gomoku show` 查看棋盘" + + # 获取最新的挑战 + challenge = challenges[-1] + challenger_id = challenge["challenger_id"] + + # 不能接受自己的挑战 + if challenger_id == user_id: + return "❌ 不能接受自己的挑战!" # 检查是否已达到最大并发数 active_games = [g for g in games if g["status"] == "playing"] if len(active_games) >= self.max_concurrent_games: return f"⚠️ 当前聊天已有 {len(active_games)} 局对战,已达到最大并发数限制" - # 检查这两个用户是否已经在对战 - for game in active_games: - players = {game["player_black"], game["player_white"]} - if user_id in players and opponent_id in players: - return "⚠️ 你们已经有一局正在进行的对战了!\n\n输入 `.gomoku show` 查看棋盘" - - # 检查用户是否已经在其他对战中 - user_game = self._find_user_game(chat_id, user_id) - if user_game: - opponent = user_game["player_white"] if user_game["player_black"] == user_id else user_game["player_black"] - return f"⚠️ 你已经在与 对战中!\n\n输入 `.gomoku show` 查看棋盘" - - opponent_game = self._find_user_game(chat_id, opponent_id) - if opponent_game: - other = opponent_game["player_white"] if opponent_game["player_black"] == opponent_id else opponent_game["player_black"] - return f"⚠️ 对手已经在与 对战中!" - - # 创建新游戏 + # 创建游戏 current_time = int(time.time()) - game_id = f"p{user_id}_p{opponent_id}_{current_time}" + game_id = f"p{challenger_id}_p{user_id}_{current_time}" new_game = { "game_id": game_id, - "player_black": user_id, # 发起者执黑(先手) - "player_white": opponent_id, # 对手执白(后手) - "current_player": user_id, # 黑方先手 + "player_black": challenger_id, # 挑战者执黑(先手) + "player_white": user_id, # 接受者执白(后手) + "current_player": challenger_id, # 黑方先手 "board": logic.create_empty_board(), "status": "playing", "winner": None, @@ -223,13 +238,18 @@ class GomokuGame(BaseGame): } games.append(new_game) + + # 移除已接受的挑战 + challenges.remove(challenge) + pool["games"] = games + pool["challenges"] = challenges self._save_game_pool(chat_id, pool) text = f"## ⚫ 五子棋对战开始!\n\n" - text += f"**黑方(先手)**: ⚫\n\n" - text += f"**白方(后手)**: ⚪\n\n" - text += f"**轮到**: ⚫\n\n" + text += f"**黑方(先手)**: ⚫\n\n" + text += f"**白方(后手)**: ⚪\n\n" + text += f"**轮到**: ⚫\n\n" text += "💡 提示:\n" text += "- 黑方有禁手规则(三三、四四、长连禁手)\n" text += "- 输入 `.gomoku A1` 在A1位置落子\n" @@ -237,6 +257,37 @@ class GomokuGame(BaseGame): return text + def _cancel_challenge(self, chat_id: int, user_id: int) -> str: + """取消挑战 + + Args: + chat_id: 会话ID + user_id: 用户ID + + Returns: + 提示消息 + """ + # 获取游戏池 + pool = self._get_game_pool(chat_id) + challenges = pool.get("challenges", []) + + # 查找用户的挑战 + user_challenge = None + for challenge in challenges: + if challenge["challenger_id"] == user_id: + user_challenge = challenge + break + + if not user_challenge: + return "⚠️ 你没有发起挑战" + + # 移除挑战 + challenges.remove(user_challenge) + pool["challenges"] = challenges + self._save_game_pool(chat_id, pool) + + return "✅ 已取消挑战" + def _make_move(self, chat_id: int, user_id: int, coord: str) -> str: """落子 @@ -477,12 +528,14 @@ class GomokuGame(BaseGame): return """## ⚫ 五子棋 ### 基础用法 -- `.gomoku @对手` - 向对手发起对战 +- `.gomoku challenge` - 发起挑战 +- `.gomoku accept` - 接受挑战 - `.gomoku A1` - 在A1位置落子 - `.gomoku show` - 显示当前棋盘 - `.gomoku resign` - 认输 ### 其他指令 +- `.gomoku cancel` - 取消自己的挑战 - `.gomoku list` - 列出所有进行中的对战 - `.gomoku stats` - 查看个人战绩 @@ -502,7 +555,8 @@ class GomokuGame(BaseGame): ### 示例 ``` -.gomoku @123456 # 向用户123456发起对战 +.gomoku challenge # 发起挑战 +.gomoku accept # 接受挑战 .gomoku H8 # 在中心位置落子 .gomoku show # 查看棋盘 .gomoku resign # 认输 diff --git a/routers/callback.py b/routers/callback.py index 1223c52..a7c74dd 100644 --- a/routers/callback.py +++ b/routers/callback.py @@ -29,6 +29,7 @@ async def callback_receive(request: Request): data = await request.json() logger.info(f"收到消息: chatid={data.get('chatid')}, creator={data.get('creator')}") logger.info(f"消息内容: {data.get('content')}") + logger.info(f"完整callback数据: {data}") # 验证请求 try: