diff --git a/routers/callback.py b/routers/callback.py index b4c2321..8688883 100644 --- a/routers/callback.py +++ b/routers/callback.py @@ -1,5 +1,6 @@ """Callback路由处理""" import logging +import re from fastapi import APIRouter, Request from fastapi.responses import JSONResponse @@ -48,6 +49,15 @@ async def callback_receive(request: Request): game_type, command = parse_result logger.info(f"识别指令: game_type={game_type}, command={command}") + # 检查是否包含 @s 参数(私聊标志) + use_private_url = False + # 使用正则表达式匹配独立的 @s 参数(前后有空格或字符串边界) + if re.search(r'\s+@s\s+|\s+@s$|^@s\s+|^@s$', command): + use_private_url = True + # 从命令中移除 @s 参数,保持其他参数不变 + command = re.sub(r'\s+@s(\s+|$)|^@s\s+', ' ', command).strip() + logger.info(f"检测到 @s 参数,将优先使用个人URL发送反馈,清理后的命令: {command}") + # 检查限流 rate_limiter = get_rate_limiter() if not rate_limiter.is_allowed(): @@ -75,21 +85,75 @@ async def callback_receive(request: Request): # 发送回复 if response_text: - sender = get_message_sender() - - # AI 对话:统一按 Markdown 发送(按任务决策) - if game_type == 'ai_chat': - try: - await sender.send_markdown(response_text) - except Exception as send_md_err: - logger.error(f"发送Markdown消息失败,改用文本发送: {send_md_err}") - await sender.send_text(response_text) - else: - # 其他模块保持原有启发式:以 # 开头视为 Markdown,否则文本 - if response_text.startswith('#'): - await sender.send_markdown(response_text) + # 如果使用了 @s 参数,优先发送到个人URL + if use_private_url: + db = get_db() + user_webhook_url = db.get_user_webhook_url(callback_data.creator) + + if user_webhook_url: + # 有个人URL,发送到个人URL + from utils.message import send_private_message + # 判断消息类型 + if game_type == 'ai_chat': + msg_type = 'markdown' + elif response_text.startswith('#'): + msg_type = 'markdown' + else: + msg_type = 'text' + + success = await send_private_message( + user_id=callback_data.creator, + content=response_text, + msg_type=msg_type + ) + if not success: + # 如果私聊发送失败,回退到主URL + logger.warning(f"个人URL发送失败,回退到主URL: user_id={callback_data.creator}") + sender = get_message_sender() + if game_type == 'ai_chat': + try: + await sender.send_markdown(response_text) + except Exception as send_md_err: + logger.error(f"发送Markdown消息失败,改用文本发送: {send_md_err}") + await sender.send_text(response_text) + else: + if response_text.startswith('#'): + await sender.send_markdown(response_text) + else: + await sender.send_text(response_text) + # 成功发送到个人URL,不向主URL发送 else: - await sender.send_text(response_text) + # 没有个人URL,回退到主URL + logger.info(f"用户 {callback_data.creator} 没有注册个人URL,使用主URL发送") + sender = get_message_sender() + if game_type == 'ai_chat': + try: + await sender.send_markdown(response_text) + except Exception as send_md_err: + logger.error(f"发送Markdown消息失败,改用文本发送: {send_md_err}") + await sender.send_text(response_text) + else: + if response_text.startswith('#'): + await sender.send_markdown(response_text) + else: + await sender.send_text(response_text) + else: + # 没有 @s 参数,正常发送到主URL + sender = get_message_sender() + + # AI 对话:统一按 Markdown 发送(按任务决策) + if game_type == 'ai_chat': + try: + await sender.send_markdown(response_text) + except Exception as send_md_err: + logger.error(f"发送Markdown消息失败,改用文本发送: {send_md_err}") + await sender.send_text(response_text) + else: + # 其他模块保持原有启发式:以 # 开头视为 Markdown,否则文本 + if response_text.startswith('#'): + await sender.send_markdown(response_text) + else: + await sender.send_text(response_text) return JSONResponse({"result": "ok"})