From 75a28037101fca2e412332cfee98e1dd84f0c682 Mon Sep 17 00:00:00 2001 From: lxfight <1686540385@qq.com> Date: Sat, 21 Jun 2025 20:12:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=B8=85=E7=A9=BA=E4=BA=A4=E4=BA=92?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E7=9A=84=20message=5Fstr=EF=BC=8C=E7=A1=AE?= =?UTF-8?q?=E4=BF=9D=E4=BB=85=E4=B8=93=E9=97=A8=E6=8C=87=E4=BB=A4=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=99=A8=E5=93=8D=E5=BA=94=EF=BC=9B=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E5=9B=BE=E7=89=87=E6=9D=A5?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复了@激活机器人时,指令无法正确处理的问题 - 修复了base64 图片无法发送的问题 注意:本次提交的代码功能还需要针对全部功能进行一次系统完整的测试,计划与6月22日下午完成。 --- .../discord/discord_platform_adapter.py | 32 ++++++- .../sources/discord/discord_platform_event.py | 92 +++++++++++++------ 2 files changed, 94 insertions(+), 30 deletions(-) diff --git a/astrbot/core/platform/sources/discord/discord_platform_adapter.py b/astrbot/core/platform/sources/discord/discord_platform_adapter.py index 3fab55e2..c83279f7 100644 --- a/astrbot/core/platform/sources/discord/discord_platform_adapter.py +++ b/astrbot/core/platform/sources/discord/discord_platform_adapter.py @@ -118,7 +118,9 @@ class DiscordPlatformAdapter(Platform): interaction.channel, interaction.guild_id ) - abm.message_str = data["content"] + # 对于交互事件,message_str 通常没有意义,且可能导致被闲聊等通用插件错误响应。 + # 将其清空,以确保只有专门的指令处理器会响应。 + abm.message_str = "" abm.sender = MessageMember( user_id=str(interaction.user.id), nickname=interaction.user.display_name ) @@ -137,12 +139,32 @@ class DiscordPlatformAdapter(Platform): """将普通消息转换为 AstrBotMessage""" message = data["message"] is_mentioned = data.get("is_mentioned", False) - clean_content = data.get("clean_content", message.content) + + content = message.content + + # 如果机器人被@,移除@部分 + if ( + is_mentioned + and self.client + and self.client.user + and self.client.user in message.mentions + ): + # 构建机器人的@字符串,格式为 <@USER_ID> 或 <@!USER_ID> + mention_str = f"<@{self.client.user.id}>" + mention_str_nickname = ( + f"<@!{self.client.user.id}>" # 有些客户端会使用带!的格式 + ) + + if content.startswith(mention_str): + content = content[len(mention_str) :].lstrip() + elif content.startswith(mention_str_nickname): + content = content[len(mention_str_nickname) :].lstrip() + abm = AstrBotMessage() abm.type, abm.group_id = self._determine_message_type(message.channel) - abm.message_str = clean_content if is_mentioned else message.content + abm.message_str = content abm.sender = MessageMember( user_id=str(message.author.id), nickname=message.author.display_name ) @@ -190,7 +212,9 @@ class DiscordPlatformAdapter(Platform): # 如果是被@的消息,设置为唤醒状态 if ( - hasattr(message.raw_message, "mentions") + self.client + and self.client.user + and hasattr(message.raw_message, "mentions") and self.client.user in message.raw_message.mentions ): message_event.is_wake = True diff --git a/astrbot/core/platform/sources/discord/discord_platform_event.py b/astrbot/core/platform/sources/discord/discord_platform_event.py index 653b84da..bdd707b7 100644 --- a/astrbot/core/platform/sources/discord/discord_platform_event.py +++ b/astrbot/core/platform/sources/discord/discord_platform_event.py @@ -92,44 +92,84 @@ class DiscordPlatformEvent(AstrMessageEvent): for i in message.chain: # 遍历消息链 if isinstance(i, Plain): # 如果是文字类型的 plain_text_parts.append(i.text) - elif isinstance(i, Image): # 如果是图片类型的 + elif isinstance(i, Image): + logger.debug(f"[Discord] 开始处理 Image 组件: {i}") try: + filename = getattr(i, "filename", None) + file_content = getattr(i, "file", None) + + if not file_content: + logger.warning(f"[Discord] Image 组件没有 file 属性: {i}") + continue + discord_file = None - # 优先使用组件指定的filename,否则从路径推断,最后使用默认值 - filename = i.filename - async def process_local_path(p_str: str) -> Optional[discord.File]: - nonlocal filename - path = Path(p_str) - if not await asyncio.to_thread(path.exists): - logger.warning(f"[Discord] 图片文件不存在: {p_str}") - return None + # 1. URL + if file_content.startswith("http"): + logger.debug(f"[Discord] 处理 URL 图片: {file_content}") + embed = discord.Embed().set_image(url=file_content) + embeds.append(embed) + continue - if not filename: # 如果没有指定filename,则从路径推断 - filename = path.name + # 2. File URI + elif file_content.startswith("file:///"): + logger.debug(f"[Discord] 处理 File URI: {file_content}") + path = Path(file_content[8:]) + if await asyncio.to_thread(path.exists): + file_bytes = await asyncio.to_thread(path.read_bytes) + discord_file = discord.File( + BytesIO(file_bytes), filename=filename or path.name + ) + else: + logger.warning(f"[Discord] 图片文件不存在: {path}") - file_bytes = await asyncio.to_thread(path.read_bytes) - return discord.File(BytesIO(file_bytes), filename=filename) - - if i.file.startswith("file:///"): - discord_file = await process_local_path(i.file[8:]) - elif i.file.startswith("http"): - downloaded_path_str = await download_image_by_url(i.file) - if downloaded_path_str: - discord_file = await process_local_path(downloaded_path_str) - elif i.file.startswith("base64://"): - img_bytes = base64.b64decode(i.file.split("base64://")[1]) + # 3. Base64 URI + elif file_content.startswith("base64://"): + logger.debug("[Discord] 处理 Base64 URI") + b64_data = file_content.split("base64://", 1)[1] + missing_padding = len(b64_data) % 4 + if missing_padding: + b64_data += "=" * (4 - missing_padding) + img_bytes = base64.b64decode(b64_data) discord_file = discord.File( BytesIO(img_bytes), filename=filename or "image.png" ) - else: # Treat as a local path - discord_file = await process_local_path(i.file) + + # 4. 裸 Base64 或本地路径 + else: + try: + logger.debug("[Discord] 尝试作为裸 Base64 处理") + b64_data = file_content + missing_padding = len(b64_data) % 4 + if missing_padding: + b64_data += "=" * (4 - missing_padding) + img_bytes = base64.b64decode(b64_data) + discord_file = discord.File( + BytesIO(img_bytes), filename=filename or "image.png" + ) + except (ValueError, TypeError, base64.binascii.Error): + logger.debug( + f"[Discord] 裸 Base64 解码失败,作为本地路径处理: {file_content}" + ) + path = Path(file_content) + if await asyncio.to_thread(path.exists): + file_bytes = await asyncio.to_thread(path.read_bytes) + discord_file = discord.File( + BytesIO(file_bytes), filename=filename or path.name + ) + else: + logger.warning(f"[Discord] 图片文件不存在: {path}") if discord_file: files.append(discord_file) - except Exception as e: - logger.warning(f"[Discord] 处理图片失败: {i.file}, 错误: {e}") + except Exception: + # 使用 getattr 来安全地访问 i.file,以防 i 本身就是问题 + file_info = getattr(i, "file", "未知") + logger.error( + f"[Discord] 处理图片时发生未知严重错误: {file_info}", + exc_info=True, + ) elif isinstance(i, File): try: file_path_str = await i.get_file()