修改为发起/接受对局以避开at无法获取id的问题
This commit is contained in:
@@ -228,7 +228,29 @@ if game_type == 'gomoku':
|
|||||||
- 改进错误提示,显示实际接收到的参数内容
|
- 改进错误提示,显示实际接收到的参数内容
|
||||||
- 将 callback.py 中的消息内容日志级别从 DEBUG 改为 INFO,便于追踪
|
- 将 callback.py 中的消息内容日志级别从 DEBUG 改为 INFO,便于追踪
|
||||||
- 原因:进一步诊断用户ID识别失败的问题,添加调试信息帮助定位问题
|
- 原因:进一步诊断用户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,无法实现@用户发起对战
|
||||||
|
- 阻碍因素:无
|
||||||
- 状态:未确认
|
- 状态:未确认
|
||||||
|
|
||||||
# 最终审查
|
# 最终审查
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ def get_help_message() -> str:
|
|||||||
- `.idiom blacklist` - 查看黑名单
|
- `.idiom blacklist` - 查看黑名单
|
||||||
|
|
||||||
### ⚫ 五子棋
|
### ⚫ 五子棋
|
||||||
- `.gomoku @对手` - 发起对战
|
- `.gomoku challenge` - 发起挑战
|
||||||
|
- `.gomoku accept` - 接受挑战
|
||||||
- `.gomoku A1` - 落子
|
- `.gomoku A1` - 落子
|
||||||
- `.gomoku show` - 显示棋盘
|
- `.gomoku show` - 显示棋盘
|
||||||
- `.gomoku resign` - 认输
|
- `.gomoku resign` - 认输
|
||||||
|
|||||||
192
games/gomoku.py
192
games/gomoku.py
@@ -54,6 +54,18 @@ class GomokuGame(BaseGame):
|
|||||||
if action in ['help', '帮助']:
|
if action in ['help', '帮助']:
|
||||||
return self.get_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', '列表', '查看']:
|
if action in ['list', '列表', '查看']:
|
||||||
return self._list_games(chat_id)
|
return self._list_games(chat_id)
|
||||||
@@ -75,48 +87,14 @@ class GomokuGame(BaseGame):
|
|||||||
if coord is not None:
|
if coord is not None:
|
||||||
return self._make_move(chat_id, user_id, action)
|
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}")
|
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:
|
except Exception as e:
|
||||||
logger.error(f"处理五子棋指令错误: {e}", exc_info=True)
|
logger.error(f"处理五子棋指令错误: {e}", exc_info=True)
|
||||||
return f"❌ 处理指令出错: {str(e)}"
|
return f"❌ 处理指令出错: {str(e)}"
|
||||||
|
|
||||||
def _parse_opponent(self, args: str) -> Optional[int]:
|
|
||||||
"""解析@对手的用户ID
|
|
||||||
|
|
||||||
Args:
|
|
||||||
args: 参数字符串
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
用户ID或None
|
|
||||||
"""
|
|
||||||
# WPS格式:<at user_id="123456"></at> 或其他变体
|
|
||||||
# 尝试多种格式
|
|
||||||
patterns = [
|
|
||||||
r'<at\s+user_id="(\d+)"[^>]*>', # <at user_id="123456"> 或 <at user_id="123456"></at>
|
|
||||||
r'<at\s+user_id=\'(\d+)\'[^>]*>', # <at user_id='123456'>
|
|
||||||
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]:
|
def _get_game_pool(self, chat_id: int) -> Dict[str, Any]:
|
||||||
"""获取游戏池
|
"""获取游戏池
|
||||||
|
|
||||||
@@ -163,56 +141,93 @@ class GomokuGame(BaseGame):
|
|||||||
|
|
||||||
return None
|
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:
|
Args:
|
||||||
chat_id: 会话ID
|
chat_id: 会话ID
|
||||||
user_id: 发起者ID
|
user_id: 发起者ID
|
||||||
opponent_id: 对手ID
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
提示消息
|
提示消息
|
||||||
"""
|
"""
|
||||||
# 检查是否与自己对战
|
|
||||||
if user_id == opponent_id:
|
|
||||||
return "❌ 不能和自己下棋哦!"
|
|
||||||
|
|
||||||
# 获取游戏池
|
# 获取游戏池
|
||||||
pool = self._get_game_pool(chat_id)
|
pool = self._get_game_pool(chat_id)
|
||||||
games = pool.get("games", [])
|
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"<at user_id=\"{user_id}\"></at> 发起了五子棋挑战!\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"]
|
active_games = [g for g in games if g["status"] == "playing"]
|
||||||
if len(active_games) >= self.max_concurrent_games:
|
if len(active_games) >= self.max_concurrent_games:
|
||||||
return f"⚠️ 当前聊天已有 {len(active_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"⚠️ 你已经在与 <at user_id=\"{opponent}\"></at> 对战中!\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"⚠️ 对手已经在与 <at user_id=\"{other}\"></at> 对战中!"
|
|
||||||
|
|
||||||
# 创建新游戏
|
|
||||||
current_time = int(time.time())
|
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 = {
|
new_game = {
|
||||||
"game_id": game_id,
|
"game_id": game_id,
|
||||||
"player_black": user_id, # 发起者执黑(先手)
|
"player_black": challenger_id, # 挑战者执黑(先手)
|
||||||
"player_white": opponent_id, # 对手执白(后手)
|
"player_white": user_id, # 接受者执白(后手)
|
||||||
"current_player": user_id, # 黑方先手
|
"current_player": challenger_id, # 黑方先手
|
||||||
"board": logic.create_empty_board(),
|
"board": logic.create_empty_board(),
|
||||||
"status": "playing",
|
"status": "playing",
|
||||||
"winner": None,
|
"winner": None,
|
||||||
@@ -223,13 +238,18 @@ class GomokuGame(BaseGame):
|
|||||||
}
|
}
|
||||||
|
|
||||||
games.append(new_game)
|
games.append(new_game)
|
||||||
|
|
||||||
|
# 移除已接受的挑战
|
||||||
|
challenges.remove(challenge)
|
||||||
|
|
||||||
pool["games"] = games
|
pool["games"] = games
|
||||||
|
pool["challenges"] = challenges
|
||||||
self._save_game_pool(chat_id, pool)
|
self._save_game_pool(chat_id, pool)
|
||||||
|
|
||||||
text = f"## ⚫ 五子棋对战开始!\n\n"
|
text = f"## ⚫ 五子棋对战开始!\n\n"
|
||||||
text += f"**黑方(先手)**:<at user_id=\"{user_id}\"></at> ⚫\n\n"
|
text += f"**黑方(先手)**:<at user_id=\"{challenger_id}\"></at> ⚫\n\n"
|
||||||
text += f"**白方(后手)**:<at user_id=\"{opponent_id}\"></at> ⚪\n\n"
|
text += f"**白方(后手)**:<at user_id=\"{user_id}\"></at> ⚪\n\n"
|
||||||
text += f"**轮到**:<at user_id=\"{user_id}\"></at> ⚫\n\n"
|
text += f"**轮到**:<at user_id=\"{challenger_id}\"></at> ⚫\n\n"
|
||||||
text += "💡 提示:\n"
|
text += "💡 提示:\n"
|
||||||
text += "- 黑方有禁手规则(三三、四四、长连禁手)\n"
|
text += "- 黑方有禁手规则(三三、四四、长连禁手)\n"
|
||||||
text += "- 输入 `.gomoku A1` 在A1位置落子\n"
|
text += "- 输入 `.gomoku A1` 在A1位置落子\n"
|
||||||
@@ -237,6 +257,37 @@ class GomokuGame(BaseGame):
|
|||||||
|
|
||||||
return text
|
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:
|
def _make_move(self, chat_id: int, user_id: int, coord: str) -> str:
|
||||||
"""落子
|
"""落子
|
||||||
|
|
||||||
@@ -477,12 +528,14 @@ class GomokuGame(BaseGame):
|
|||||||
return """## ⚫ 五子棋
|
return """## ⚫ 五子棋
|
||||||
|
|
||||||
### 基础用法
|
### 基础用法
|
||||||
- `.gomoku @对手` - 向对手发起对战
|
- `.gomoku challenge` - 发起挑战
|
||||||
|
- `.gomoku accept` - 接受挑战
|
||||||
- `.gomoku A1` - 在A1位置落子
|
- `.gomoku A1` - 在A1位置落子
|
||||||
- `.gomoku show` - 显示当前棋盘
|
- `.gomoku show` - 显示当前棋盘
|
||||||
- `.gomoku resign` - 认输
|
- `.gomoku resign` - 认输
|
||||||
|
|
||||||
### 其他指令
|
### 其他指令
|
||||||
|
- `.gomoku cancel` - 取消自己的挑战
|
||||||
- `.gomoku list` - 列出所有进行中的对战
|
- `.gomoku list` - 列出所有进行中的对战
|
||||||
- `.gomoku stats` - 查看个人战绩
|
- `.gomoku stats` - 查看个人战绩
|
||||||
|
|
||||||
@@ -502,7 +555,8 @@ class GomokuGame(BaseGame):
|
|||||||
|
|
||||||
### 示例
|
### 示例
|
||||||
```
|
```
|
||||||
.gomoku @123456 # 向用户123456发起对战
|
.gomoku challenge # 发起挑战
|
||||||
|
.gomoku accept # 接受挑战
|
||||||
.gomoku H8 # 在中心位置落子
|
.gomoku H8 # 在中心位置落子
|
||||||
.gomoku show # 查看棋盘
|
.gomoku show # 查看棋盘
|
||||||
.gomoku resign # 认输
|
.gomoku resign # 认输
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ async def callback_receive(request: Request):
|
|||||||
data = await request.json()
|
data = await request.json()
|
||||||
logger.info(f"收到消息: chatid={data.get('chatid')}, creator={data.get('creator')}")
|
logger.info(f"收到消息: chatid={data.get('chatid')}, creator={data.get('creator')}")
|
||||||
logger.info(f"消息内容: {data.get('content')}")
|
logger.info(f"消息内容: {data.get('content')}")
|
||||||
|
logger.info(f"完整callback数据: {data}")
|
||||||
|
|
||||||
# 验证请求
|
# 验证请求
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user