From 48d07af574a0ffda72b6a2150fa118532c77568a Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Thu, 3 Apr 2025 11:50:12 +0800 Subject: [PATCH 01/14] =?UTF-8?q?feature(fix=3F):=20=E5=9C=A8=E5=8F=91?= =?UTF-8?q?=E9=80=81=E6=B6=88=E6=81=AF=E4=B9=8B=E5=89=8D=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=B6=88=E6=81=AF=E5=86=85=E5=AE=B9=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E4=B8=BA=E7=A9=BA,=20=E4=B8=8D=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E5=8F=91=E9=80=81=E7=A9=BA=E6=B6=88=E6=81=AF,=20=E4=BB=A5?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=AF=A5=E6=B6=88=E6=81=AF=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E4=B8=8D=E6=94=AF=E6=8C=81=E6=9F=A5=E7=9C=8B=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?gemini=E8=BF=94=E5=9B=9E=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/pipeline/respond/stage.py | 21 +++++++++++++++++++ .../core/provider/sources/gemini_source.py | 6 +++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index a43f0b32d..202a3b8aa 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -72,6 +72,20 @@ class RespondStage(Stage): # random return random.uniform(self.interval[0], self.interval[1]) + async def _is_empty_message_chain(self, chain): + """检查消息链是否为空 + + Args: + chain (MessageChain): 消息链 + """ + for comp in chain: + if isinstance(comp, Plain): + if comp.text.strip(): + return False + else: + return True + return True + async def process( self, event: AstrMessageEvent ) -> Union[None, AsyncGenerator[None, None]]: @@ -82,6 +96,13 @@ class RespondStage(Stage): if len(result.chain) > 0: await event._pre_send() + # 检查消息链是否为空 + if await self._is_empty_message_chain(result.chain): + logger.info("消息为空,跳过发送阶段") + event.clear_result() + event.stop_event() + return + if self.enable_seg and ( (self.only_llm_result and result.is_llm_result()) or not self.only_llm_result diff --git a/astrbot/core/provider/sources/gemini_source.py b/astrbot/core/provider/sources/gemini_source.py index 3233b3453..cc34784c5 100644 --- a/astrbot/core/provider/sources/gemini_source.py +++ b/astrbot/core/provider/sources/gemini_source.py @@ -127,7 +127,7 @@ class ProviderGoogleGenAI(Provider): if message["role"] == "user": if isinstance(message["content"], str): if not message["content"]: - message["content"] = "" + message["content"] = "" google_genai_conversation.append( {"role": "user", "parts": [{"text": message["content"]}]} @@ -138,7 +138,7 @@ class ProviderGoogleGenAI(Provider): for part in message["content"]: if part["type"] == "text": if not part["text"]: - part["text"] = "" + part["text"] = "" parts.append({"text": part["text"]}) elif part["type"] == "image_url": parts.append( @@ -156,7 +156,7 @@ class ProviderGoogleGenAI(Provider): elif message["role"] == "assistant": if "content" in message: if not message["content"]: - message["content"] = "" + message["content"] = "" google_genai_conversation.append( {"role": "model", "parts": [{"text": message["content"]}]} ) From 1c1a65b637f6a0eaa1b22f3e63948fe651159ca1 Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Sat, 5 Apr 2025 00:21:28 +0800 Subject: [PATCH 02/14] =?UTF-8?q?fix:=20=E5=85=A8=E9=83=A8=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=AE=B5=E7=9A=84=E6=A3=80=E9=AA=8C=E5=BC=84=E5=A5=BD?= =?UTF-8?q?=E4=BA=86!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/pipeline/respond/stage.py | 86 ++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 5 deletions(-) diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index 202a3b8aa..bdb9a8434 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -11,7 +11,36 @@ from astrbot.core import logger from astrbot.core.message.message_event_result import BaseMessageComponent from astrbot.core.star.star_handler import star_handlers_registry, EventType from astrbot.core.star.star import star_map -from astrbot.core.message.components import Plain, Reply, At +from astrbot.core.message.components import ( + Plain, + Face, + Record, + Video, + At, + AtAll, + RPS, + Dice, + Shake, + Anonymous, + Share, + Contact, + Location, + Music, + Image, + Reply, + RedBag, + Poke, + Forward, + Node, + Nodes, + Xml, + Json, + CardImage, + TTS, + Unknown, + File, + WechatEmoji, +) @register_stage @@ -72,18 +101,65 @@ class RespondStage(Stage): # random return random.uniform(self.interval[0], self.interval[1]) - async def _is_empty_message_chain(self, chain): + async def _is_empty_message_chain(self, chain: list[BaseMessageComponent]): """检查消息链是否为空 Args: - chain (MessageChain): 消息链 + chain (list[BaseMessageComponent]): 包含消息对象的列表 """ + if not chain: + return True + + # 组件类型到其非空判断函数的映射 + component_validators = { + Plain: lambda comp: bool( + comp.text and comp.text.strip() + ), # 纯文本消息需要strip + Face: lambda comp: comp.id is not None, # QQ表情 + Record: lambda comp: bool(comp.file), # 语音 + Video: lambda comp: bool(comp.file), # 视频 + At: lambda comp: bool(comp.qq) or bool(comp.name), # @ + AtAll: lambda comp: True, # @所有人 + RPS: lambda comp: True, # 不知道是啥(未完成) + Dice: lambda comp: True, # 骰子(未完成) + Shake: lambda comp: True, # 摇一摇(未完成) + Anonymous: lambda comp: True, # 匿名(未完成) + Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享 + Contact: lambda comp: True, # 联系人(未完成) + Location: lambda comp: bool(comp.lat and comp.lon), # 位置 + Music: lambda comp: bool(comp._type) + and bool(comp.url) + and bool(comp.audio), # 音乐 + Image: lambda comp: bool(comp.file), # 图片 + Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复 + RedBag: lambda comp: bool(comp.title), # 红包 + Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳 + Forward: lambda comp: bool(comp.id and comp.id.strip()), # 转发 + Node: lambda comp: bool(comp.name) + and comp.uin != 0 + and bool(comp.content), # 一个转发节点 + Nodes: lambda comp: bool(comp.nodes), # 多个转发节点 + Xml: lambda comp: bool(comp.data and comp.data.strip()), # XML + Json: lambda comp: bool(comp.data), # JSON + CardImage: lambda comp: bool(comp.file), # 卡片图片 + TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成 + Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息 + File: lambda comp: bool(comp.file), # 文件 + WechatEmoji: lambda comp: bool(comp.md5), # 微信表情 + } + for comp in chain: - if isinstance(comp, Plain): - if comp.text.strip(): + comp_type = type(comp) + + # 检查组件类型是否在字典中 + if comp_type in component_validators: + if component_validators[comp_type](comp): return False else: + logger.error(f"消息链中包含非消息组件: {comp}, 停止事件传播") return True + + # 如果所有组件都为空 return True async def process( From 3ebd2f746f304180366de96cd8c1f7edb210201e Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Sat, 5 Apr 2025 00:51:52 +0800 Subject: [PATCH 03/14] =?UTF-8?q?feature:=20=E6=B7=BB=E5=8A=A0=E6=8F=92?= =?UTF-8?q?=E4=BB=B6=E5=B7=A5=E5=85=B7=E7=B1=BB,=20=E6=9A=82=E6=97=B6?= =?UTF-8?q?=E8=BF=99=E4=B9=88=E5=A4=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/star/star_tools.py | 135 ++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 astrbot/core/star/star_tools.py diff --git a/astrbot/core/star/star_tools.py b/astrbot/core/star/star_tools.py new file mode 100644 index 000000000..f83cf979a --- /dev/null +++ b/astrbot/core/star/star_tools.py @@ -0,0 +1,135 @@ +from typing import Union, Awaitable, List +from asyncio import Queue +from astrbot.core.message.components import BaseMessageComponent +from astrbot.core.message.message_event_result import MessageChain +from astrbot.api.platform import MessageType, MessageMember, AstrBotMessage +from astrbot.core.platform.astr_message_event import MessageSesion +from astrbot.core.star.context import Context +from astrbot.core.star.filter.command import CommandFilter +from astrbot.core.star.filter.regex import RegexFilter +from astrbot.core.star import star_handlers_registry, StarHandlerMetadata, EventType +from astrbot.core import sp + + +class StarTools: + """ + 提供给插件使用的便捷工具函数集合 + 这些方法封装了一些常用操作,使插件开发更加简单便捷! + """ + + def __init__(self, context: Context): + """ + 初始化StarTools实例 + + Args: + context(Context): 暴露给插件的上下文 + """ + self._context = context + + async def send_message( + self, session: Union[str, MessageSesion], message_chain: MessageChain + ) -> bool: + """ + 根据session(unified_msg_origin)主动发送消息 + + Args: + session: 消息会话。通过event.session或者event.unified_msg_origin获取 + message_chain: 消息链 + + Returns: + bool: 是否找到匹配的平台 + + Raises: + ValueError: 当session为字符串且解析失败时抛出 + + Note: + qq_official(QQ官方API平台)不支持此方法 + """ + return await self._context.send_message(session, message_chain) + + async def create_message( + self, + type: str, + self_id: str, + session_id: str, + message_id: str, + sender: MessageMember, + message: List[BaseMessageComponent], + message_str: str, + raw_message: object, + group_id: str = "", + ): + """ + 创建一个AstrBot消息对象 + + Args: + type (str): 消息类型 + self_id (str): 机器人自身ID + session_id (str): 会话ID(通常为用户ID)(QQ号, 群号等) + message_id (str): 消息ID + sender (MessageMember): 发送者信息 + message (List[BaseMessageComponent]): 消息组件列表 + message_str (str): 消息字符串 + raw_message (object): 原始消息对象 + group_id (str, optional): 群组ID, 如果为私聊则为空. Defaults to "". + + Returns: + AstrBotMessage: 创建的消息对象 + """ + abm = AstrBotMessage() + abm.type = type + abm.self_id = self_id + abm.session_id = session_id + abm.message_id = message_id + abm.sender = sender + abm.message = message + abm.message_str = message_str + abm.raw_message = raw_message + abm.group_id = group_id + return abm + + # todo: 添加构造event的方法 + # todo: 添加找到对应平台并提交对应事件的方法 + + def activate_llm_tool(self, name: str) -> bool: + """ + 激活一个已经注册的函数调用工具 + 注册的工具默认是激活状态 + + Args: + name (str): 工具名称 + """ + return self._context.activate_llm_tool(name) + + def deactivate_llm_tool(self, name: str) -> bool: + """ + 停用一个已经注册的函数调用工具 + + Args: + name (str): 工具名称 + """ + return self._context.deactivate_llm_tool(name) + + def register_llm_tool( + self, name: str, func_args: list, desc: str, func_obj: Awaitable + ) -> None: + """ + 为函数调用(function-calling/tools-use)添加工具 + + Args: + name (str): 工具名称 + func_args (list): 函数参数列表 + desc (str): 工具描述 + func_obj (Awaitable): 函数对象,必须是异步函数 + """ + self._context.register_llm_tool(name, func_args, desc, func_obj) + + def unregister_llm_tool(self, name: str) -> None: + """ + 删除一个函数调用工具 + 如果再要启用,需要重新注册 + + Args: + name (str): 工具名称 + """ + self._context.unregister_llm_tool(name) From 60352307aae12122da81354434edb307a4a55f1c Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Sat, 5 Apr 2025 01:11:10 +0800 Subject: [PATCH 04/14] =?UTF-8?q?fix:=20=E9=87=8D=E7=94=9F=E4=B9=8B?= =?UTF-8?q?=E6=88=91=E8=A6=81=E8=8B=A6=E8=AF=BB=E8=AE=BE=E8=AE=A1=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F,=20=E7=BB=88=E4=BA=8E=E7=9F=A5=E9=81=93=E6=80=8E?= =?UTF-8?q?=E4=B9=88=E6=95=B4=E4=BA=86=E5=93=88=E5=93=88=E5=93=88:=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E9=9D=99=E6=80=81=E7=B1=BB=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E9=9B=86=E5=90=88,=20=E5=B9=B6=E4=B8=94?= =?UTF-8?q?=E6=AD=A3=E7=A1=AE=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/star/__init__.py | 2 ++ astrbot/core/star/star_tools.py | 47 +++++++++++++++++++++------------ 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/astrbot/core/star/__init__.py b/astrbot/core/star/__init__.py index b1bd5de81..37ca73664 100644 --- a/astrbot/core/star/__init__.py +++ b/astrbot/core/star/__init__.py @@ -4,12 +4,14 @@ from .context import Context from astrbot.core.provider import Provider from astrbot.core.utils.command_parser import CommandParserMixin from astrbot.core import html_renderer +from astrbot.core.star.star_tools import StarTools class Star(CommandParserMixin): """所有插件(Star)的父类,所有插件都应该继承于这个类""" def __init__(self, context: Context): + StarTools.initialize(context) self.context = context async def text_to_image(self, text: str, return_url=True) -> str: diff --git a/astrbot/core/star/star_tools.py b/astrbot/core/star/star_tools.py index f83cf979a..284423840 100644 --- a/astrbot/core/star/star_tools.py +++ b/astrbot/core/star/star_tools.py @@ -1,4 +1,4 @@ -from typing import Union, Awaitable, List +from typing import Union, Awaitable, List, Optional, ClassVar from asyncio import Queue from astrbot.core.message.components import BaseMessageComponent from astrbot.core.message.message_event_result import MessageChain @@ -17,17 +17,20 @@ class StarTools: 这些方法封装了一些常用操作,使插件开发更加简单便捷! """ - def __init__(self, context: Context): + _context: ClassVar[Optional[Context]] = None + + def initialize(cls, context: Context) -> None: """ - 初始化StarTools实例 + 初始化StarTools,设置context引用 Args: - context(Context): 暴露给插件的上下文 + context: 暴露给插件的上下文 """ - self._context = context + cls._context = context + @classmethod async def send_message( - self, session: Union[str, MessageSesion], message_chain: MessageChain + cls, session: Union[str, MessageSesion], message_chain: MessageChain ) -> bool: """ 根据session(unified_msg_origin)主动发送消息 @@ -45,10 +48,11 @@ class StarTools: Note: qq_official(QQ官方API平台)不支持此方法 """ - return await self._context.send_message(session, message_chain) + return await cls._context.send_message(session, message_chain) + @classmethod async def create_message( - self, + cls, type: str, self_id: str, session_id: str, @@ -88,10 +92,16 @@ class StarTools: abm.group_id = group_id return abm - # todo: 添加构造event的方法 + # todo: 添加构造事件的方法 + # async def create_event( + # self, platform: str, umo: str, sender_id: str, session_id: str + # ): + # platform = self._context.get_platform(platform) + # todo: 添加找到对应平台并提交对应事件的方法 - def activate_llm_tool(self, name: str) -> bool: + @classmethod + def activate_llm_tool(cls, name: str) -> bool: """ 激活一个已经注册的函数调用工具 注册的工具默认是激活状态 @@ -99,19 +109,21 @@ class StarTools: Args: name (str): 工具名称 """ - return self._context.activate_llm_tool(name) + return cls._context.activate_llm_tool(name) - def deactivate_llm_tool(self, name: str) -> bool: + @classmethod + def deactivate_llm_tool(cls, name: str) -> bool: """ 停用一个已经注册的函数调用工具 Args: name (str): 工具名称 """ - return self._context.deactivate_llm_tool(name) + return cls._context.deactivate_llm_tool(name) + @classmethod def register_llm_tool( - self, name: str, func_args: list, desc: str, func_obj: Awaitable + cls, name: str, func_args: list, desc: str, func_obj: Awaitable ) -> None: """ 为函数调用(function-calling/tools-use)添加工具 @@ -122,9 +134,10 @@ class StarTools: desc (str): 工具描述 func_obj (Awaitable): 函数对象,必须是异步函数 """ - self._context.register_llm_tool(name, func_args, desc, func_obj) + cls._context.register_llm_tool(name, func_args, desc, func_obj) - def unregister_llm_tool(self, name: str) -> None: + @classmethod + def unregister_llm_tool(cls, name: str) -> None: """ 删除一个函数调用工具 如果再要启用,需要重新注册 @@ -132,4 +145,4 @@ class StarTools: Args: name (str): 工具名称 """ - self._context.unregister_llm_tool(name) + cls._context.unregister_llm_tool(name) From ee86b68470390ae7c080a03aa597b29befd32006 Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Sat, 5 Apr 2025 01:15:56 +0800 Subject: [PATCH 05/14] =?UTF-8?q?fix:=20=E6=BC=8F=E5=8A=A0classmethod?= =?UTF-8?q?=E4=BA=86!?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/star/star_tools.py | 1 + 1 file changed, 1 insertion(+) diff --git a/astrbot/core/star/star_tools.py b/astrbot/core/star/star_tools.py index 284423840..bc7ead53e 100644 --- a/astrbot/core/star/star_tools.py +++ b/astrbot/core/star/star_tools.py @@ -19,6 +19,7 @@ class StarTools: _context: ClassVar[Optional[Context]] = None + @classmethod def initialize(cls, context: Context) -> None: """ 初始化StarTools,设置context引用 From 8a366964bb1943598d705244654f120d64ead0a5 Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Sat, 5 Apr 2025 11:52:51 +0800 Subject: [PATCH 06/14] =?UTF-8?q?feature:=20=E5=A2=9E=E5=8A=A0=E6=97=B6?= =?UTF-8?q?=E5=8C=BA=E8=AE=BE=E7=BD=AE=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 7 +++++++ packages/astrbot/main.py | 32 +++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index b77ad94bc..b62f839ae 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -47,6 +47,7 @@ DEFAULT_CONFIG = { "web_search_link": False, "identifier": False, "datetime_system_prompt": True, + "timezone": "Asia/Shanghai", "default_personality": "default", "prompt_prefix": "", "max_context_length": -1, @@ -977,6 +978,12 @@ CONFIG_METADATA_2 = { "obvious_hint": True, "hint": "启用后,会在系统提示词中加上当前机器的日期时间。", }, + "timezone": { + "description": "时区设置", + "type": "string", + "obvious_hint": True, + "hint": "时区设置。请填写时区名称,如 Asia/Shanghai。", + }, "default_personality": { "description": "默认采用的人格情景的名称", "type": "string", diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index 3887fc929..207067a9d 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -22,8 +22,8 @@ from astrbot.core.config.default import VERSION from .long_term_memory import LongTermMemory from astrbot.core import logger from astrbot.api.message_components import Plain, Image, Reply - from typing import Union +import pytz @star.register( @@ -39,6 +39,7 @@ class Main(star.Star): self.prompt_prefix = cfg["provider_settings"]["prompt_prefix"] self.identifier = cfg["provider_settings"]["identifier"] self.enable_datetime = cfg["provider_settings"]["datetime_system_prompt"] + self.timezone = cfg["provider_settings"]["timezone"] self.ltm = None if ( @@ -969,7 +970,8 @@ UID: {user_id} 此 ID 可用于设置管理员。 if len(l) == 1: message.set_result( MessageEventResult() - .message(f"""[Persona] + .message( + f"""[Persona] - 人格情景列表: `/persona list` - 设置人格情景: `/persona 人格` @@ -980,7 +982,8 @@ UID: {user_id} 此 ID 可用于设置管理员。 当前对话 {curr_cid_title} 的人格情景: {curr_persona_name} 配置人格情景请前往管理面板-配置页 -""") +""" + ) .use_t2i(False) ) elif l[1] == "list": @@ -1190,11 +1193,26 @@ UID: {user_id} 此 ID 可用于设置管理员。 user_info = f"\n[User ID: {user_id}, Nickname: {user_nickname}]\n" req.prompt = user_info + req.prompt + # 启用附加时间戳 if self.enable_datetime: - # Including timezone - current_time = ( - datetime.datetime.now().astimezone().strftime("%Y-%m-%d %H:%M (%Z)") - ) + # 启用时区 + if self.timezone: + try: + tz = pytz.timezone(self.timezone) + now = datetime.datetime.now(tz) + current_time = now.strftime("%Y-%m-%d %H:%M (%Z)") + except Exception as e: + logger.error(f"时区设置错误: {e}, 使用本地时区") + current_time = ( + datetime.datetime.now() + .astimezone() + .strftime("%Y-%m-%d %H:%M (%Z)") + ) + # 未启用时区 + else: + current_time = ( + datetime.datetime.now().astimezone().strftime("%Y-%m-%d %H:%M (%Z)") + ) req.system_prompt += f"\nCurrent datetime: {current_time}\n" if req.conversation: From be022c48942d04cda677cbf8e4e6b131bd8d8385 Mon Sep 17 00:00:00 2001 From: anka <1350989414@qq.com> Date: Sat, 5 Apr 2025 11:55:25 +0800 Subject: [PATCH 07/14] fix: add StarTools to api --- astrbot/api/star/__init__.py | 8 ++------ astrbot/core/star/__init__.py | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/astrbot/api/star/__init__.py b/astrbot/api/star/__init__.py index 630786de3..1b33923fe 100644 --- a/astrbot/api/star/__init__.py +++ b/astrbot/api/star/__init__.py @@ -2,11 +2,7 @@ from astrbot.core.star.register import ( register_star as register, # 注册插件(Star) ) -from astrbot.core.star import Context, Star +from astrbot.core.star import Context, Star, StarTools from astrbot.core.star.config import * -__all__ = [ - "register", - "Context", - "Star", -] +__all__ = ["register", "Context", "Star", "StarTools"] diff --git a/astrbot/core/star/__init__.py b/astrbot/core/star/__init__.py index 37ca73664..ec1ee655b 100644 --- a/astrbot/core/star/__init__.py +++ b/astrbot/core/star/__init__.py @@ -29,4 +29,4 @@ class Star(CommandParserMixin): pass -__all__ = ["Star", "StarMetadata", "PluginManager", "Context", "Provider"] +__all__ = ["Star", "StarMetadata", "PluginManager", "Context", "Provider", "StarTools"] From c2b687212c1b605d014dc08d525c82702c7e9705 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sat, 5 Apr 2025 16:51:06 +0800 Subject: [PATCH 08/14] cleanup --- astrbot/core/pipeline/respond/stage.py | 109 ++++++++++--------------- astrbot/core/star/star_tools.py | 7 +- 2 files changed, 42 insertions(+), 74 deletions(-) diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index bdb9a8434..e41e61e28 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -2,6 +2,7 @@ import random import asyncio import math import traceback +import astrbot.core.message.components as Comp from typing import Union, AsyncGenerator from ..stage import register_stage, Stage from ..context import PipelineContext @@ -11,36 +12,6 @@ from astrbot.core import logger from astrbot.core.message.message_event_result import BaseMessageComponent from astrbot.core.star.star_handler import star_handlers_registry, EventType from astrbot.core.star.star import star_map -from astrbot.core.message.components import ( - Plain, - Face, - Record, - Video, - At, - AtAll, - RPS, - Dice, - Shake, - Anonymous, - Share, - Contact, - Location, - Music, - Image, - Reply, - RedBag, - Poke, - Forward, - Node, - Nodes, - Xml, - Json, - CardImage, - TTS, - Unknown, - File, - WechatEmoji, -) @register_stage @@ -91,7 +62,7 @@ class RespondStage(Stage): async def _calc_comp_interval(self, comp: BaseMessageComponent) -> float: """分段回复 计算间隔时间""" if self.interval_method == "log": - if isinstance(comp, Plain): + if isinstance(comp, Comp.Plain): wc = await self._word_cnt(comp.text) i = math.log(wc + 1, self.log_base) return random.uniform(i, i + 0.5) @@ -112,40 +83,40 @@ class RespondStage(Stage): # 组件类型到其非空判断函数的映射 component_validators = { - Plain: lambda comp: bool( + Comp.Plain: lambda comp: bool( comp.text and comp.text.strip() ), # 纯文本消息需要strip - Face: lambda comp: comp.id is not None, # QQ表情 - Record: lambda comp: bool(comp.file), # 语音 - Video: lambda comp: bool(comp.file), # 视频 - At: lambda comp: bool(comp.qq) or bool(comp.name), # @ - AtAll: lambda comp: True, # @所有人 - RPS: lambda comp: True, # 不知道是啥(未完成) - Dice: lambda comp: True, # 骰子(未完成) - Shake: lambda comp: True, # 摇一摇(未完成) - Anonymous: lambda comp: True, # 匿名(未完成) - Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享 - Contact: lambda comp: True, # 联系人(未完成) - Location: lambda comp: bool(comp.lat and comp.lon), # 位置 - Music: lambda comp: bool(comp._type) + Comp.Face: lambda comp: comp.id is not None, # QQ表情 + Comp.Record: lambda comp: bool(comp.file), # 语音 + Comp.Video: lambda comp: bool(comp.file), # 视频 + Comp.At: lambda comp: bool(comp.qq) or bool(comp.name), # @ + Comp.AtAll: lambda comp: True, # @所有人 + Comp.RPS: lambda comp: True, # 不知道是啥(未完成) + Comp.Dice: lambda comp: True, # 骰子(未完成) + Comp.Shake: lambda comp: True, # 摇一摇(未完成) + Comp.Anonymous: lambda comp: True, # 匿名(未完成) + Comp.Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享 + Comp.Contact: lambda comp: True, # 联系人(未完成) + Comp.Location: lambda comp: bool(comp.lat and comp.lon), # 位置 + Comp.Music: lambda comp: bool(comp._type) and bool(comp.url) and bool(comp.audio), # 音乐 - Image: lambda comp: bool(comp.file), # 图片 - Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复 - RedBag: lambda comp: bool(comp.title), # 红包 - Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳 - Forward: lambda comp: bool(comp.id and comp.id.strip()), # 转发 - Node: lambda comp: bool(comp.name) + Comp.Image: lambda comp: bool(comp.file), # 图片 + Comp.Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复 + Comp.RedBag: lambda comp: bool(comp.title), # 红包 + Comp.Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳 + Comp.Forward: lambda comp: bool(comp.id and comp.id.strip()), # 转发 + Comp.Node: lambda comp: bool(comp.name) and comp.uin != 0 and bool(comp.content), # 一个转发节点 - Nodes: lambda comp: bool(comp.nodes), # 多个转发节点 - Xml: lambda comp: bool(comp.data and comp.data.strip()), # XML - Json: lambda comp: bool(comp.data), # JSON - CardImage: lambda comp: bool(comp.file), # 卡片图片 - TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成 - Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息 - File: lambda comp: bool(comp.file), # 文件 - WechatEmoji: lambda comp: bool(comp.md5), # 微信表情 + Comp.Nodes: lambda comp: bool(comp.nodes), # 多个转发节点 + Comp.Xml: lambda comp: bool(comp.data and comp.data.strip()), # XML + Comp.Json: lambda comp: bool(comp.data), # JSON + Comp.CardImage: lambda comp: bool(comp.file), # 卡片图片 + Comp.TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成 + Comp.Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息 + Comp.File: lambda comp: bool(comp.file), # 文件 + Comp.WechatEmoji: lambda comp: bool(comp.md5), # 微信表情 } for comp in chain: @@ -156,8 +127,7 @@ class RespondStage(Stage): if component_validators[comp_type](comp): return False else: - logger.error(f"消息链中包含非消息组件: {comp}, 停止事件传播") - return True + logger.info(f"空内容检查: 无法识别的组件类型: {comp_type.__name__}") # 如果所有组件都为空 return True @@ -173,11 +143,14 @@ class RespondStage(Stage): await event._pre_send() # 检查消息链是否为空 - if await self._is_empty_message_chain(result.chain): - logger.info("消息为空,跳过发送阶段") - event.clear_result() - event.stop_event() - return + try: + if await self._is_empty_message_chain(result.chain): + logger.info("消息为空,跳过发送阶段") + event.clear_result() + event.stop_event() + return + except Exception as e: + logger.warning(f"空内容检查异常: {e}") if self.enable_seg and ( (self.only_llm_result and result.is_llm_result()) @@ -186,13 +159,13 @@ class RespondStage(Stage): decorated_comps = [] if self.reply_with_mention: for comp in result.chain: - if isinstance(comp, At): + if isinstance(comp, Comp.At): decorated_comps.append(comp) result.chain.remove(comp) break if self.reply_with_quote: for comp in result.chain: - if isinstance(comp, Reply): + if isinstance(comp, Comp.Reply): decorated_comps.append(comp) result.chain.remove(comp) break diff --git a/astrbot/core/star/star_tools.py b/astrbot/core/star/star_tools.py index bc7ead53e..68468e353 100644 --- a/astrbot/core/star/star_tools.py +++ b/astrbot/core/star/star_tools.py @@ -1,14 +1,9 @@ from typing import Union, Awaitable, List, Optional, ClassVar -from asyncio import Queue from astrbot.core.message.components import BaseMessageComponent from astrbot.core.message.message_event_result import MessageChain -from astrbot.api.platform import MessageType, MessageMember, AstrBotMessage +from astrbot.api.platform import MessageMember, AstrBotMessage from astrbot.core.platform.astr_message_event import MessageSesion from astrbot.core.star.context import Context -from astrbot.core.star.filter.command import CommandFilter -from astrbot.core.star.filter.regex import RegexFilter -from astrbot.core.star import star_handlers_registry, StarHandlerMetadata, EventType -from astrbot.core import sp class StarTools: From dc1f222cd2ee643f6034de7e415ed50f560568b6 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sat, 5 Apr 2025 17:27:46 +0800 Subject: [PATCH 09/14] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8=20zoneinfo=20?= =?UTF-8?q?=E6=9B=BF=E4=BB=A3=20tzinfo;=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E6=97=B6=E5=8C=BA(=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E9=BB=98=E8=AE=A4=E6=97=B6=E5=8C=BA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 14 +++++++------- packages/astrbot/main.py | 23 ++++++++++------------- packages/reminder/main.py | 22 +++++++++++++++------- 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index b62f839ae..7ad1ecc07 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -47,7 +47,6 @@ DEFAULT_CONFIG = { "web_search_link": False, "identifier": False, "datetime_system_prompt": True, - "timezone": "Asia/Shanghai", "default_personality": "default", "prompt_prefix": "", "max_context_length": -1, @@ -99,6 +98,7 @@ DEFAULT_CONFIG = { "plugin_repo_mirror": "", "knowledge_db": {}, "persona": [], + "timezone": "", } @@ -978,12 +978,6 @@ CONFIG_METADATA_2 = { "obvious_hint": True, "hint": "启用后,会在系统提示词中加上当前机器的日期时间。", }, - "timezone": { - "description": "时区设置", - "type": "string", - "obvious_hint": True, - "hint": "时区设置。请填写时区名称,如 Asia/Shanghai。", - }, "default_personality": { "description": "默认采用的人格情景的名称", "type": "string", @@ -1179,6 +1173,12 @@ CONFIG_METADATA_2 = { "type": "string", "hint": "启用后,会以添加环境变量的方式设置代理。格式为 `http://ip:port`", }, + "timezone": { + "description": "时区", + "type": "string", + "obvious_hint": True, + "hint": "时区设置。请填写 IANA 时区名称, 如 Asia/Shanghai, 为空时使用系统默认时区。所有时区请查看: https://data.iana.org/time-zones/tzdb-2021a/zone1970.tab", + }, "log_level": { "description": "控制台日志级别", "type": "string", diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index 207067a9d..8d421ed6d 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -3,6 +3,7 @@ import datetime import builtins import traceback import re +import zoneinfo import astrbot.api.star as star import astrbot.api.event.filter as filter from astrbot.api.event import AstrMessageEvent, MessageEventResult @@ -23,7 +24,6 @@ from .long_term_memory import LongTermMemory from astrbot.core import logger from astrbot.api.message_components import Plain, Image, Reply from typing import Union -import pytz @star.register( @@ -39,8 +39,11 @@ class Main(star.Star): self.prompt_prefix = cfg["provider_settings"]["prompt_prefix"] self.identifier = cfg["provider_settings"]["identifier"] self.enable_datetime = cfg["provider_settings"]["datetime_system_prompt"] - self.timezone = cfg["provider_settings"]["timezone"] - + self.timezone = cfg.get("timezone", "Asia/Shanghai") + if not self.timezone: + # 系统默认时区 + self.timezone = None + logger.info(f"Timezone set to: {self.timezone}") self.ltm = None if ( self.context.get_config()["provider_ltm_settings"]["group_icl_enable"] @@ -1195,21 +1198,15 @@ UID: {user_id} 此 ID 可用于设置管理员。 # 启用附加时间戳 if self.enable_datetime: - # 启用时区 + current_time = None if self.timezone: + # 启用时区 try: - tz = pytz.timezone(self.timezone) - now = datetime.datetime.now(tz) + now = datetime.datetime.now(zoneinfo.ZoneInfo(self.timezone)) current_time = now.strftime("%Y-%m-%d %H:%M (%Z)") except Exception as e: logger.error(f"时区设置错误: {e}, 使用本地时区") - current_time = ( - datetime.datetime.now() - .astimezone() - .strftime("%Y-%m-%d %H:%M (%Z)") - ) - # 未启用时区 - else: + if not current_time: current_time = ( datetime.datetime.now().astimezone().strftime("%Y-%m-%d %H:%M (%Z)") ) diff --git a/packages/reminder/main.py b/packages/reminder/main.py index e3f6c0a97..5b9b32588 100644 --- a/packages/reminder/main.py +++ b/packages/reminder/main.py @@ -2,6 +2,7 @@ import os import json import datetime import uuid +import zoneinfo import astrbot.api.star as star from astrbot.api.event import filter from apscheduler.schedulers.asyncio import AsyncIOScheduler @@ -17,7 +18,11 @@ class Main(star.Star): def __init__(self, context: star.Context) -> None: self.context = context - self.scheduler = AsyncIOScheduler(timezone="Asia/Shanghai") + self.timezone = self.context.get_config().get("timezone", "Asia/Shanghai") + if not self.timezone: + self.timezone = None + self.scheduler = AsyncIOScheduler(timezone=self.timezone) + self.tzinfo = zoneinfo.ZoneInfo(self.timezone) if self.timezone else None # set and load config if not os.path.exists("data/astrbot-reminder.json"): @@ -65,10 +70,10 @@ class Main(star.Star): def check_is_outdated(self, reminder: dict): """Check if the reminder is outdated.""" if "datetime" in reminder: - return ( - datetime.datetime.strptime(reminder["datetime"], "%Y-%m-%d %H:%M") - < datetime.datetime.now() - ) + reminder_time = datetime.datetime.strptime( + reminder["datetime"], "%Y-%m-%d %H:%M" + ).replace(tzinfo=self.tzinfo) + return reminder_time < datetime.datetime.now(self.tzinfo) return False async def _save_data(self): @@ -171,12 +176,15 @@ class Main(star.Star): reminders = self.reminder_data.get(unified_msg_origin, []) if not reminders: return [] - now = datetime.datetime.now() + now = datetime.datetime.now(self.tzinfo) upcoming_reminders = [ reminder for reminder in reminders if "datetime" not in reminder - or datetime.datetime.strptime(reminder["datetime"], "%Y-%m-%d %H:%M") >= now + or datetime.datetime.strptime( + reminder["datetime"], "%Y-%m-%d %H:%M" + ).replace(tzinfo=self.tzinfo) + >= now ] return upcoming_reminders From c20f4f5adf5cfd143e2389138ccbfcb59a1026c8 Mon Sep 17 00:00:00 2001 From: Raven95676 Date: Sat, 5 Apr 2025 21:03:02 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E5=88=A0=E9=99=A4=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=80=BC=EF=BC=8C=E8=B0=83=E6=95=B4logger=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/astrbot/main.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index 8d421ed6d..013dcb0f7 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -39,11 +39,12 @@ class Main(star.Star): self.prompt_prefix = cfg["provider_settings"]["prompt_prefix"] self.identifier = cfg["provider_settings"]["identifier"] self.enable_datetime = cfg["provider_settings"]["datetime_system_prompt"] - self.timezone = cfg.get("timezone", "Asia/Shanghai") + self.timezone = cfg.get("timezone") if not self.timezone: # 系统默认时区 self.timezone = None - logger.info(f"Timezone set to: {self.timezone}") + else: + logger.info(f"Timezone set to: {self.timezone}") self.ltm = None if ( self.context.get_config()["provider_ltm_settings"]["group_icl_enable"] From 43b6297b5d9391a4cb0102b7829ff61f78ba0629 Mon Sep 17 00:00:00 2001 From: Raven95676 Date: Sat, 5 Apr 2025 21:08:52 +0800 Subject: [PATCH 11/14] =?UTF-8?q?reminder=E5=B0=86=E6=97=B6=E5=8C=BA?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=A7=BB=E5=85=A5try=E5=9D=97,=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=B8=BAself.timezone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/reminder/main.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/reminder/main.py b/packages/reminder/main.py index 5b9b32588..6b1b8f3e8 100644 --- a/packages/reminder/main.py +++ b/packages/reminder/main.py @@ -18,11 +18,15 @@ class Main(star.Star): def __init__(self, context: star.Context) -> None: self.context = context - self.timezone = self.context.get_config().get("timezone", "Asia/Shanghai") + self.timezone = self.context.get_config().get("timezone") if not self.timezone: self.timezone = None + try: + self.timezone = zoneinfo.ZoneInfo(self.timezone) if self.timezone else None + except Exception as e: + logger.error(f"时区设置错误: {e}, 使用本地时区") + self.timezone = None self.scheduler = AsyncIOScheduler(timezone=self.timezone) - self.tzinfo = zoneinfo.ZoneInfo(self.timezone) if self.timezone else None # set and load config if not os.path.exists("data/astrbot-reminder.json"): @@ -72,8 +76,8 @@ class Main(star.Star): if "datetime" in reminder: reminder_time = datetime.datetime.strptime( reminder["datetime"], "%Y-%m-%d %H:%M" - ).replace(tzinfo=self.tzinfo) - return reminder_time < datetime.datetime.now(self.tzinfo) + ).replace(tzinfo=self.timezone) + return reminder_time < datetime.datetime.now(self.timezone) return False async def _save_data(self): @@ -176,14 +180,14 @@ class Main(star.Star): reminders = self.reminder_data.get(unified_msg_origin, []) if not reminders: return [] - now = datetime.datetime.now(self.tzinfo) + now = datetime.datetime.now(self.timezone) upcoming_reminders = [ reminder for reminder in reminders if "datetime" not in reminder or datetime.datetime.strptime( reminder["datetime"], "%Y-%m-%d %H:%M" - ).replace(tzinfo=self.tzinfo) + ).replace(tzinfo=self.timezone) >= now ] return upcoming_reminders From e54eaab842267eba26d8acbead0748cf3eab1746 Mon Sep 17 00:00:00 2001 From: Raven95676 Date: Sat, 5 Apr 2025 21:19:53 +0800 Subject: [PATCH 12/14] =?UTF-8?q?=E5=B0=86=E9=AA=8C=E8=AF=81=E5=99=A8?= =?UTF-8?q?=E5=AD=97=E5=85=B8=E7=A7=BB=E5=88=B0=E7=B1=BB=E7=BA=A7=E5=88=AB?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E9=87=8D=E5=A4=8D=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/pipeline/respond/stage.py | 74 ++++++++++++-------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/astrbot/core/pipeline/respond/stage.py b/astrbot/core/pipeline/respond/stage.py index e41e61e28..86c165945 100644 --- a/astrbot/core/pipeline/respond/stage.py +++ b/astrbot/core/pipeline/respond/stage.py @@ -16,6 +16,38 @@ from astrbot.core.star.star import star_map @register_stage class RespondStage(Stage): + # 组件类型到其非空判断函数的映射 + _component_validators = { + Comp.Plain: lambda comp: bool(comp.text and comp.text.strip()), # 纯文本消息需要strip + Comp.Face: lambda comp: comp.id is not None, # QQ表情 + Comp.Record: lambda comp: bool(comp.file), # 语音 + Comp.Video: lambda comp: bool(comp.file), # 视频 + Comp.At: lambda comp: bool(comp.qq) or bool(comp.name), # @ + Comp.AtAll: lambda comp: True, # @所有人 + Comp.RPS: lambda comp: True, # 不知道是啥(未完成) + Comp.Dice: lambda comp: True, # 骰子(未完成) + Comp.Shake: lambda comp: True, # 摇一摇(未完成) + Comp.Anonymous: lambda comp: True, # 匿名(未完成) + Comp.Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享 + Comp.Contact: lambda comp: True, # 联系人(未完成) + Comp.Location: lambda comp: bool(comp.lat and comp.lon), # 位置 + Comp.Music: lambda comp: bool(comp._type) and bool(comp.url) and bool(comp.audio), # 音乐 + Comp.Image: lambda comp: bool(comp.file), # 图片 + Comp.Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复 + Comp.RedBag: lambda comp: bool(comp.title), # 红包 + Comp.Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳 + Comp.Forward: lambda comp: bool(comp.id and comp.id.strip()), # 转发 + Comp.Node: lambda comp: bool(comp.name) and comp.uin != 0 and bool(comp.content), # 一个转发节点 + Comp.Nodes: lambda comp: bool(comp.nodes), # 多个转发节点 + Comp.Xml: lambda comp: bool(comp.data and comp.data.strip()), # XML + Comp.Json: lambda comp: bool(comp.data), # JSON + Comp.CardImage: lambda comp: bool(comp.file), # 卡片图片 + Comp.TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成 + Comp.Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息 + Comp.File: lambda comp: bool(comp.file), # 文件 + Comp.WechatEmoji: lambda comp: bool(comp.md5), # 微信表情 + } + async def initialize(self, ctx: PipelineContext): self.ctx = ctx @@ -81,50 +113,12 @@ class RespondStage(Stage): if not chain: return True - # 组件类型到其非空判断函数的映射 - component_validators = { - Comp.Plain: lambda comp: bool( - comp.text and comp.text.strip() - ), # 纯文本消息需要strip - Comp.Face: lambda comp: comp.id is not None, # QQ表情 - Comp.Record: lambda comp: bool(comp.file), # 语音 - Comp.Video: lambda comp: bool(comp.file), # 视频 - Comp.At: lambda comp: bool(comp.qq) or bool(comp.name), # @ - Comp.AtAll: lambda comp: True, # @所有人 - Comp.RPS: lambda comp: True, # 不知道是啥(未完成) - Comp.Dice: lambda comp: True, # 骰子(未完成) - Comp.Shake: lambda comp: True, # 摇一摇(未完成) - Comp.Anonymous: lambda comp: True, # 匿名(未完成) - Comp.Share: lambda comp: bool(comp.url) and bool(comp.title), # 分享 - Comp.Contact: lambda comp: True, # 联系人(未完成) - Comp.Location: lambda comp: bool(comp.lat and comp.lon), # 位置 - Comp.Music: lambda comp: bool(comp._type) - and bool(comp.url) - and bool(comp.audio), # 音乐 - Comp.Image: lambda comp: bool(comp.file), # 图片 - Comp.Reply: lambda comp: bool(comp.id) and comp.sender_id is not None, # 回复 - Comp.RedBag: lambda comp: bool(comp.title), # 红包 - Comp.Poke: lambda comp: comp.id != 0 and comp.qq != 0, # 戳一戳 - Comp.Forward: lambda comp: bool(comp.id and comp.id.strip()), # 转发 - Comp.Node: lambda comp: bool(comp.name) - and comp.uin != 0 - and bool(comp.content), # 一个转发节点 - Comp.Nodes: lambda comp: bool(comp.nodes), # 多个转发节点 - Comp.Xml: lambda comp: bool(comp.data and comp.data.strip()), # XML - Comp.Json: lambda comp: bool(comp.data), # JSON - Comp.CardImage: lambda comp: bool(comp.file), # 卡片图片 - Comp.TTS: lambda comp: bool(comp.text and comp.text.strip()), # 语音合成 - Comp.Unknown: lambda comp: bool(comp.text and comp.text.strip()), # 未知消息 - Comp.File: lambda comp: bool(comp.file), # 文件 - Comp.WechatEmoji: lambda comp: bool(comp.md5), # 微信表情 - } - for comp in chain: comp_type = type(comp) # 检查组件类型是否在字典中 - if comp_type in component_validators: - if component_validators[comp_type](comp): + if comp_type in self._component_validators: + if self._component_validators[comp_type](comp): return False else: logger.info(f"空内容检查: 无法识别的组件类型: {comp_type.__name__}") From 79d38f959709c1c20094fa63bcb68ebf34061215 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sun, 6 Apr 2025 22:36:31 +0800 Subject: [PATCH 13/14] =?UTF-8?q?=F0=9F=93=A6release:=20v3.5.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 2 +- changelogs/v3.5.2.md | 31 +++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 changelogs/v3.5.2.md diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index 2c4b60d2b..00e27c15c 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -2,7 +2,7 @@ 如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。 """ -VERSION = "3.5.1" +VERSION = "3.5.2" DB_PATH = "data/data_v3.db" # 默认配置 diff --git a/changelogs/v3.5.2.md b/changelogs/v3.5.2.md new file mode 100644 index 000000000..cbc12af9f --- /dev/null +++ b/changelogs/v3.5.2.md @@ -0,0 +1,31 @@ +# What's Changed + +> 📢 在升级前,请完整阅读本次更新日志。 + +## ✨ 新增的功能 + +1. 安装完插件后自动弹出插件仓库 README 对话框 @zhx8702 +4. 支持阿里云百炼 TTS@Soulter +5. 支持 Telegram MarkdownV2 渲染 @Soulter +6. 支持 钉钉 Markdown 渲染 @Soulter +6. 增加对 Gemini 系列模型的输入安全设置参数支持 @AliveGh0st +7. 支持手动设置时区以应对容器、国外用户的时区问题 @anka-afk @Raven95676 @Soulter +8. 插件市场显示帮助按钮 @Soulter + +## 🎈 功能性优化 + +1. WebUI 的日志通信使用 SSE 替代 Websockets @Soulter +2. 在发送消息之前统一检查消息内容是否为空, 不允许发送空消息, 以解决该消息内容不支持查看以及 Gemini 返回 `` 问题 @anka-afk +3. 更新 Dify 平台链接为官方域名 by @Captain-Slacker-OwO +4. 人格 prompt 输入框支持调节高度 @Soulter + +## 🐛 修复的 Bug + +1. 将最多携带对话数量修改回 `-1` 时出现报错 #1074 @anka-afk +2. 修复无法识别到函数调用异常的问题 by @Soulter +3. 修复 aiocqhttp 适配器下空白 plain 导致的 `the object is not a proper segment chain` 报错问题 @Soulter +4. 修复阿里百炼应用无法多轮会话的问题 @Soulter + +## 🧩 新增的插件 + +待补充 From 062af1ac08b7936b5a869d30954407a95057ac24 Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Mon, 7 Apr 2025 10:38:03 +0800 Subject: [PATCH 14/14] =?UTF-8?q?=F0=9F=8E=88=20perf:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=20WebUI=20=E6=97=A5=E5=BF=97=E9=94=99=E8=AF=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/stores/common.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/dashboard/src/stores/common.js b/dashboard/src/stores/common.js index 640389ddc..49f55f5db 100644 --- a/dashboard/src/stores/common.js +++ b/dashboard/src/stores/common.js @@ -62,14 +62,23 @@ export const useCommonStore = defineStore({ } const text = decoder.decode(value); - const lines = text.split('\n'); + const lines = text.split('\n\n'); lines.forEach(line => { if (line.startsWith('data:')) { const data = line.substring(5).trim(); - // {"type":"log","data":"[2021-08-01 00:00:00] INFO: Hello, world!"} - - let data_json = JSON.parse(data) + let data_json = {} + try { + data_json = JSON.parse(data); + } catch (e) { + console.error('Invalid JSON:', data); + data_json = { + type: 'log', + data: data, + level: 'INFO', + time: new Date().toISOString(), + } + } if (data_json.type === 'log') { // let log = data_json.data this.log_cache.push(data_json);