diff --git a/README.md b/README.md index 07237bfa4..4e8c1a829 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@
- - +
diff --git a/astrbot/core/pipeline/waking_check/stage.py b/astrbot/core/pipeline/waking_check/stage.py index 6fb3378ca..5c5163938 100644 --- a/astrbot/core/pipeline/waking_check/stage.py +++ b/astrbot/core/pipeline/waking_check/stage.py @@ -4,7 +4,7 @@ from astrbot import logger from typing import Union, AsyncGenerator from astrbot.core.platform.astr_message_event import AstrMessageEvent from astrbot.core.message.message_event_result import MessageEventResult, MessageChain -from astrbot.core.message.components import At, AtAll +from astrbot.core.message.components import At, AtAll, Reply from astrbot.core.star.star_handler import star_handlers_registry, EventType from astrbot.core.star.star import star_map from astrbot.core.star.filter.permission import PermissionTypeFilter @@ -80,11 +80,19 @@ class WakingCheckStage(Stage): event.message_str = event.message_str[len(wake_prefix) :].strip() break if not is_wake: - # 检查是否有 at 消息 + # 检查是否有at消息 / at全体成员消息 / 引用了bot的消息 for message in messages: - if (isinstance(message, At) and ( - str(message.qq) == str(event.get_self_id()) - )) or (isinstance(message, AtAll) and not self.ignore_at_all): + if ( + ( + isinstance(message, At) + and (str(message.qq) == str(event.get_self_id())) + ) + or (isinstance(message, AtAll) and not self.ignore_at_all) + or ( + isinstance(message, Reply) + and str(message.sender_id) == str(event.get_self_id()) + ) + ): is_wake = True event.is_wake = True wake_prefix = "" diff --git a/astrbot/core/platform/sources/dingtalk/dingtalk_event.py b/astrbot/core/platform/sources/dingtalk/dingtalk_event.py index 4834032f5..c2188dc36 100644 --- a/astrbot/core/platform/sources/dingtalk/dingtalk_event.py +++ b/astrbot/core/platform/sources/dingtalk/dingtalk_event.py @@ -32,31 +32,31 @@ class DingtalkMessageEvent(AstrMessageEvent): ) elif isinstance(segment, Comp.Image): markdown_str = "" - if segment.file and segment.file.startswith("file:///"): - logger.warning( - "dingtalk only support url image, not: " + segment.file - ) - continue - elif segment.file and segment.file.startswith("http"): - markdown_str += f"\n\n" - elif segment.file and segment.file.startswith("base64://"): - logger.warning("dingtalk only support url image, not base64") - continue - else: - logger.warning( - "dingtalk only support url image, not: " + segment.file - ) - continue - ret = await asyncio.get_event_loop().run_in_executor( - None, - client.reply_markdown, - "😄", - markdown_str, - self.message_obj.raw_message, - ) - logger.debug(f"send image: {ret}") + try: + if not segment.file: + logger.warning("钉钉图片 segment 缺少 file 字段,跳过") + continue + if segment.file.startswith(("http://", "https://")): + image_url = segment.file + else: + image_url = await segment.register_to_file_service() + markdown_str = f"\n\n" + + ret = await asyncio.get_event_loop().run_in_executor( + None, + client.reply_markdown, + "😄", + markdown_str, + self.message_obj.raw_message, + ) + logger.debug(f"send image: {ret}") + + except Exception as e: + logger.error(f"钉钉图片处理失败: {e}") + logger.warning(f"跳过图片发送: {image_path}") + continue async def send(self, message: MessageChain): await self.send_with_client(self.client, message) await super().send(message) diff --git a/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py b/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py index 3cddeccce..58e3c9b19 100644 --- a/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +++ b/astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py @@ -2,6 +2,7 @@ import asyncio import base64 import json import os +import traceback import time from typing import Optional @@ -158,7 +159,6 @@ class WeChatPadProAdapter(Platform): os.makedirs(data_dir, exist_ok=True) with open(self.credentials_file, "w") as f: json.dump(credentials, f) - logger.info("成功保存 WeChatPadPro 凭据。") except Exception as e: logger.error(f"保存 WeChatPadPro 凭据失败: {e}") @@ -166,6 +166,8 @@ class WeChatPadProAdapter(Platform): """ 检查 WeChatPadPro 设备是否在线。 """ + if not self.auth_key: + return False url = f"{self.base_url}/login/GetLoginStatus" params = {"key": self.auth_key} @@ -184,12 +186,16 @@ class WeChatPadProAdapter(Platform): logger.info("WeChatPadPro 设备不在线。") return False else: - logger.error(f"未知的在线状态: {login_state:}") + logger.error(f"未知的在线状态: {response_data}") return False # Code == 300 为微信退出状态。 elif response.status == 200 and response_data.get("Code") == 300: logger.info("WeChatPadPro 设备已退出。") return False + elif response.status == 200 and response_data.get("Code") == -2: + # 该链接不存在 + self.auth_key = None + return False else: logger.error( f"检查在线状态失败: {response.status}, {response_data}" @@ -201,6 +207,7 @@ class WeChatPadProAdapter(Platform): return False except Exception as e: logger.error(f"检查在线状态时发生错误: {e}") + logger.error(traceback.format_exc()) return False async def generate_auth_key(self): @@ -224,7 +231,7 @@ class WeChatPadProAdapter(Platform): and len(response_data["Data"]) > 0 ): self.auth_key = response_data["Data"][0] - logger.info("成功获取授权码") + logger.info(f"成功获取授权码 {self.auth_key[:8]}...") else: logger.error( f"生成授权码成功但未找到授权码: {response_data}" @@ -250,7 +257,6 @@ class WeChatPadProAdapter(Platform): try: async with session.post(url, params=params, json=payload) as response: response_data = await response.json() - # 修正成功判断条件和数据提取路径 if response.status == 200 and response_data.get("Code") == 200: # 二维码地址在 Data.QrCodeUrl 字段中 if response_data.get("Data") and response_data["Data"].get( @@ -262,6 +268,13 @@ class WeChatPadProAdapter(Platform): f"获取登录二维码成功但未找到二维码地址: {response_data}" ) return None + elif "该 key 无效" in response_data.get("Text"): + logger.error( + "授权码无效,已经清除。请重新启动 AstrBot 或者本消息适配器。原因也可能是 WeChatPadPro 的 MySQL 服务没有启动成功,请检查 WeChatPadPro 服务的日志。" + ) + self.auth_key = None + self.save_credentials() + return None else: logger.error( f"获取登录二维码失败: {response.status}, {response_data}" @@ -354,7 +367,7 @@ class WeChatPadProAdapter(Platform): while True: try: async with websockets.connect(ws_url) as websocket: - logger.info("WebSocket 连接成功。") + logger.debug("WebSocket 连接成功。") # 设置空闲超时重连 wait_time = ( self.active_message_poll_interval @@ -369,7 +382,7 @@ class WeChatPadProAdapter(Platform): # logger.debug(message) # 不显示原始消息内容 asyncio.create_task(self.handle_websocket_message(message)) except asyncio.TimeoutError: - logger.warning(f"WebSocket 连接空闲超过 {wait_time} s") + logger.debug(f"WebSocket 连接空闲超过 {wait_time} s") break except websockets.exceptions.ConnectionClosedOK: logger.info("WebSocket 连接正常关闭。") @@ -492,7 +505,7 @@ class WeChatPadProAdapter(Platform): # 对于群聊,session_id 可以是群聊 ID 或发送者 ID + 群聊 ID (如果 unique_session 为 True) if self.unique_session: - abm.session_id = f"{from_user_name}_{to_user_name}" + abm.session_id = f"{from_user_name}#{abm.sender.user_id}" else: abm.session_id = from_user_name @@ -631,7 +644,11 @@ class WeChatPadProAdapter(Platform): # wechatpadpro 的格式: