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: