From 750470d6774e5257f27b848a53c34dd090e8c402 Mon Sep 17 00:00:00 2001 From: SXP-Simon Date: Sat, 21 Feb 2026 16:54:34 +0800 Subject: [PATCH] =?UTF-8?q?fix(onebot):=20=E8=A7=A3=E5=86=B3=E4=BA=86?= =?UTF-8?q?=E8=B7=A8=E5=AE=B9=E5=99=A8=E8=B7=AF=E5=BE=84=E8=AE=BF=E9=97=AE?= =?UTF-8?q?=E9=9A=BE=E9=A2=98=EF=BC=9A=20=E7=94=B1=E4=BA=8E=E6=82=A8?= =?UTF-8?q?=E7=9A=84=20AstrBot=20=E5=92=8C=20NapCat=20=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E5=9C=A8=E4=B8=8D=E5=90=8C=E7=9A=84=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E6=88=96=E7=8E=AF=E5=A2=83=E4=B8=AD=EF=BC=8CNapCat=20?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E7=9B=B4=E6=8E=A5=E8=AE=BF=E9=97=AE=20AstrBo?= =?UTF-8?q?t=20=E7=94=9F=E6=88=90=E7=9A=84=E6=9C=AC=E5=9C=B0=E4=B8=B4?= =?UTF-8?q?=E6=97=B6=E6=96=87=E4=BB=B6=EF=BC=88=E5=A6=82=20//AstrBot/data/?= =?UTF-8?q?temp/...=EF=BC=89=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=8A=A5=E9=94=99?= =?UTF-8?q?=20ENOENT:=20no=20such=20file=20or=20directory=E3=80=82=20?= =?UTF-8?q?=E7=8E=B0=E5=9C=A8=EF=BC=8C=E6=8F=92=E4=BB=B6=E5=9C=A8=E8=B0=83?= =?UTF-8?q?=E7=94=A8=20OneBot=20=E5=8F=91=E9=80=81=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E6=88=96=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E5=89=8D=EF=BC=8C?= =?UTF-8?q?=E4=BC=9A=E8=87=AA=E5=8A=A8=E5=B0=86=E6=9C=AC=E5=9C=B0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E8=BD=AC=E4=B8=BA=20Base64=20=E7=BC=96=E7=A0=81?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E3=80=82=E8=BF=99=E6=A0=B7=EF=BC=8C=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=95=B0=E6=8D=AE=E5=B0=86=E7=9B=B4=E6=8E=A5=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E5=8D=8F=E8=AE=AE=E4=BC=A0=E8=BE=93=EF=BC=8C=E5=BD=BB?= =?UTF-8?q?=E5=BA=95=E6=91=86=E8=84=B1=E4=BA=86=E5=AF=B9=E7=89=A9=E7=90=86?= =?UTF-8?q?=E7=A3=81=E7=9B=98=E8=B7=AF=E5=BE=84=E5=85=B1=E4=BA=AB=E7=9A=84?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/adapters/onebot_adapter.py | 65 +++++++++++++++++-- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/infrastructure/platform/adapters/onebot_adapter.py b/src/infrastructure/platform/adapters/onebot_adapter.py index 2715031..d3e7536 100644 --- a/src/infrastructure/platform/adapters/onebot_adapter.py +++ b/src/infrastructure/platform/adapters/onebot_adapter.py @@ -6,6 +6,7 @@ OneBot v11 平台适配器 import asyncio import base64 +import os from datetime import datetime, timedelta from typing import Any @@ -453,7 +454,18 @@ class OneBotAdapter(PlatformAdapter): elif image_path.startswith("base64://"): file_str = image_path else: - file_str = f"file:///{image_path}" + # 尝试转换为 Base64,解决跨容器路径不一致导致的 1200 错误 + b64_str = await self._get_base64_from_file(image_path) + if b64_str: + logger.debug( + f"已将本地图片转换为 Base64 发送: {image_path[:50]}..." + ) + file_str = b64_str + else: + logger.warning( + f"无法读取本地图片,将尝试使用原始路径回退: {image_path}" + ) + file_str = f"file:///{image_path}" message.append({"type": "image", "data": {"file": file_str}}) @@ -485,11 +497,17 @@ class OneBotAdapter(PlatformAdapter): bool: 上传任务启动是否成功 """ try: + # 同样优先使用 Base64 发送文件,适配跨容器环境 + file_str = await self._get_base64_from_file(file_path) + if not file_str: + logger.warning(f"无法读取本地文件进行 Base64 编码: {file_path}") + file_str = file_path # 回退 + await self.bot.call_action( "upload_group_file", group_id=int(group_id), - file=file_path, - name=filename or file_path.replace("\\", "/").split("/")[-1], + file=file_str, + name=filename or os.path.basename(file_path), ) return True except Exception as e: @@ -612,6 +630,31 @@ class OneBotAdapter(PlatformAdapter): except Exception: return None + async def _get_base64_from_file(self, file_path: str) -> str | None: + """ + 读取本地文件并返回 Base64 编码字符串。 + + Args: + file_path: 本地文件绝对路径 + + Returns: + str | None: base64://... 格式的字符串,读取失败返回 None + """ + try: + import os + + if not os.path.exists(file_path): + logger.error(f"文件不存在,无法读取 Base64: {file_path}") + return None + + with open(file_path, "rb") as f: + data = f.read() + b64 = base64.b64encode(data).decode("utf-8") + return f"base64://{b64}" + except Exception as e: + logger.error(f"读取文件并转换 Base64 失败: {e}") + return None + # ==================== IAvatarRepository 实现 ==================== async def get_user_avatar_url( @@ -863,7 +906,7 @@ class OneBotAdapter(PlatformAdapter): """ try: # 如果没有 album_id,尝试获取该群的第一个相册作为默认目标 - # 注意:某些 API (如 upload_image_to_qun_album) 强制要求 album_id + # 注意:某些 API (如 upload_image_to_qun_album) 强制要求 album_id 且不能为空 if not album_id: albums = await self.get_group_album_list(group_id) if albums: @@ -875,12 +918,22 @@ class OneBotAdapter(PlatformAdapter): f"未指定有效的相册,自动选择默认相册 ID: {album_id}" ) + if not album_id: + logger.info( + f"群 {group_id} 未找到任何有效相册,且未指定相册名,跳过相册上传以防止后端错误。" + ) + return False + + # 尝试转换为 Base64 以保证跨容器成功率 (兼容性取决于 OneBot 实现) + b64_file = await self._get_base64_from_file(image_path) + upload_path = b64_file if b64_file else image_path + # 策略 1: 尝试 upload_image_to_qun_album (参考 astrbot_plugin_qun_album) try: # 注意:NapCat 的某些实现强制要求 album_id 字段存在 params = { "group_id": int(group_id), - "file": image_path, + "file": upload_path, "album_id": str(album_id or ""), # 保证字段存在 } if album_name: @@ -902,7 +955,7 @@ class OneBotAdapter(PlatformAdapter): params = { "group_id": int(group_id), - "file": image_path, + "file": upload_path, "album_id": str(album_id or ""), }