From 4aa91ad599d3a9419a438861c97260e1c18f4cfe Mon Sep 17 00:00:00 2001 From: Soulter <905617992@qq.com> Date: Sat, 8 Mar 2025 19:55:24 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=94=AF=E6=8C=81=E5=BD=93?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8F=AA=E6=9C=89@bot=E6=97=B6=EF=BC=8C?= =?UTF-8?q?=E4=B8=8B=E4=B8=80=E6=9D=A1=E5=8F=91=E9=80=81=E4=BA=BA=E7=9A=84?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E7=9B=B4=E6=8E=A5=E5=94=A4=E9=86=92=E6=9C=BA?= =?UTF-8?q?=E5=99=A8=E4=BA=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/core/config/default.py | 8 ++- astrbot/core/platform/astr_message_event.py | 3 +- .../platform/sources/telegram/tg_adapter.py | 3 +- astrbot/core/utils/session_waiter.py | 9 +-- changelogs/v3.4.36.md | 10 +++ packages/astrbot/main.py | 16 +++-- packages/session_controller/main.py | 62 ++++++++++++++++++- 7 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 changelogs/v3.4.36.md diff --git a/astrbot/core/config/default.py b/astrbot/core/config/default.py index e67cd2f1..384ae29b 100644 --- a/astrbot/core/config/default.py +++ b/astrbot/core/config/default.py @@ -2,7 +2,7 @@ 如需修改配置,请在 `data/cmd_config.json` 中修改或者在管理面板中可视化修改。 """ -VERSION = "3.4.35" +VERSION = "3.4.36" DB_PATH = "data/data_v3.db" # 默认配置 @@ -36,6 +36,7 @@ DEFAULT_CONFIG = { "content_cleanup_rule": "", }, "no_permission_reply": True, + "empty_mention_waiting": True, }, "provider": [], "provider_settings": { @@ -257,6 +258,11 @@ CONFIG_METADATA_2 = { "type": "bool", "hint": "启用后,当用户没有权限执行某个操作时,机器人会回复一条消息。", }, + "empty_mention_waiting": { + "description": "只 @ 机器人是否触发等待回复", + "type": "bool", + "hint": "启用后,当消息内容只有 @ 机器人时,会触发等待回复,在 60 秒内的该用户的任意一条消息均会唤醒机器人。这在某些平台不支持 @ 和语音/图片等消息同时发送时特别有用。", + }, "segmented_reply": { "description": "分段回复", "type": "object", diff --git a/astrbot/core/platform/astr_message_event.py b/astrbot/core/platform/astr_message_event.py index 7a25d84c..454c60f4 100644 --- a/astrbot/core/platform/astr_message_event.py +++ b/astrbot/core/platform/astr_message_event.py @@ -1,4 +1,5 @@ import abc +import asyncio from dataclasses import dataclass from .astrbot_message import AstrBotMessage from .platform_metadata import PlatformMetadata @@ -196,7 +197,7 @@ class AstrMessageEvent(abc.ABC): """ 发送消息到消息平台。 """ - await Metric.upload(msg_event_tick=1, adapter_name=self.platform_meta.name) + asyncio.create_task(Metric.upload(msg_event_tick=1, adapter_name=self.platform_meta.name)) self._has_send_oper = True async def _pre_send(self): diff --git a/astrbot/core/platform/sources/telegram/tg_adapter.py b/astrbot/core/platform/sources/telegram/tg_adapter.py index ffeff87a..dfb88232 100644 --- a/astrbot/core/platform/sources/telegram/tg_adapter.py +++ b/astrbot/core/platform/sources/telegram/tg_adapter.py @@ -141,7 +141,8 @@ class TelegramPlatformAdapter(Platform): + plain_text[entity.offset + entity.length :] ) - message.message.append(Plain(plain_text)) + if plain_text: + message.message.append(Plain(plain_text)) message.message_str = plain_text if message.message_str == "/start": diff --git a/astrbot/core/utils/session_waiter.py b/astrbot/core/utils/session_waiter.py index 7ba8ddfb..616aa17b 100644 --- a/astrbot/core/utils/session_waiter.py +++ b/astrbot/core/utils/session_waiter.py @@ -139,13 +139,10 @@ class SessionWaiter: session.session_controller.stop(e) -def session_waiter( - session_id_param: str, timeout: int = 30, record_history_chains: bool = False -): +def session_waiter(timeout: int = 30, record_history_chains: bool = False): """ 装饰器:自动将函数注册为 SessionWaiter 处理函数,并等待外部输入触发执行。 - :param session_id_param: 用于从参数中获取 session_id 的参数名称 :param timeout: 超时时间(秒) :param record_history_chain: 是否自动记录历史消息链。可以通过 controller.get_history_chains() 获取。深拷贝。 """ @@ -153,9 +150,9 @@ def session_waiter( def decorator(func: Callable[[str], Awaitable[Any]]): @functools.wraps(func) async def wrapper(*args, **kwargs): - session_id = kwargs.get(session_id_param) + session_id = kwargs.get("session_id", None) if not session_id: - raise ValueError(f"缺少 session_id 参数 '{session_id_param}'") + raise ValueError("缺少 session_id 参数") waiter = SessionWaiter(session_id, record_history_chains) return await waiter.register_wait(func, timeout) diff --git a/changelogs/v3.4.36.md b/changelogs/v3.4.36.md new file mode 100644 index 00000000..ad004b2c --- /dev/null +++ b/changelogs/v3.4.36.md @@ -0,0 +1,10 @@ +# What's Changed + +1. ✨ 新增: 支持插件会话控制 API +2. ✨ 新增: add template of LMStudio #691 +3. ⚡ 优化: Webchat 和 Gewechat 的图片、语音等主动消息发送 #710 +4. 🐛 修复: 404 error after installing plugins +5. 🐛 修复: telegram cannot handle /start #620 +6. 🐛 修复: 修复插件在带了 __del__ 之后无法被禁用和重载的问题 +7. 🐛 修复: context.get_platform() error +8. 🐛 修复: Telegram 适配器使用代理地址无法获取图片 #723 \ No newline at end of file diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index 25fefae7..be7af7ca 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -267,10 +267,14 @@ UID: {user_id} 此 ID 可用于设置管理员。/op 授权管理员, /deo @filter.permission_type(filter.PermissionType.ADMIN) @filter.command("op") - async def op(self, event: AstrMessageEvent, admin_id: str=None): + async def op(self, event: AstrMessageEvent, admin_id: str = None): """授权管理员。op """ if admin_id is None: - event.set_result(MessageEventResult().message("使用方法: /op 授权管理员;/deop 取消管理员。可通过 /sid 获取 ID。")) + event.set_result( + MessageEventResult().message( + "使用方法: /op 授权管理员;/deop 取消管理员。可通过 /sid 获取 ID。" + ) + ) return self.context.get_config()["admins_id"].append(admin_id) self.context.get_config().save_config() @@ -291,10 +295,14 @@ UID: {user_id} 此 ID 可用于设置管理员。/op 授权管理员, /deo @filter.permission_type(filter.PermissionType.ADMIN) @filter.command("wl") - async def wl(self, event: AstrMessageEvent, sid: str=None): + async def wl(self, event: AstrMessageEvent, sid: str = None): """添加白名单。wl """ if sid is None: - event.set_result(MessageEventResult().message("使用方法: /wl 添加白名单;/dwl 删除白名单。可通过 /sid 获取 ID。")) + event.set_result( + MessageEventResult().message( + "使用方法: /wl 添加白名单;/dwl 删除白名单。可通过 /sid 获取 ID。" + ) + ) self.context.get_config()["platform_settings"]["id_whitelist"].append(sid) self.context.get_config().save_config() event.set_result(MessageEventResult().message("添加白名单成功。")) diff --git a/packages/session_controller/main.py b/packages/session_controller/main.py index c2fbf644..0750e4c5 100644 --- a/packages/session_controller/main.py +++ b/packages/session_controller/main.py @@ -1,6 +1,14 @@ +import astrbot.api.message_components as Comp +import copy +from astrbot.api import logger from astrbot.api.event import AstrMessageEvent, filter from astrbot.api.star import Context, Star, register -from astrbot.core.utils.session_waiter import SessionWaiter, USER_SESSIONS +from astrbot.core.utils.session_waiter import ( + SessionWaiter, + USER_SESSIONS, + session_waiter, + SessionController, +) from sys import maxsize @@ -17,9 +25,59 @@ class Waiter(Star): def __init__(self, context: Context): super().__init__(context) + self.empty_mention_waiting = self.context.get_config()["platform_settings"][ + "empty_mention_waiting" + ] + self.wake_prefix = self.context.get_config()["wake_prefix"] + @filter.event_message_type(filter.EventMessageType.ALL, priority=maxsize) async def handle_session_control_agent(self, event: AstrMessageEvent): - session_id = event.unified_msg_origin + """会话控制代理""" + session_id = event.get_sender_id() if session_id in USER_SESSIONS: await SessionWaiter.trigger(session_id, event) event.stop_event() + + @filter.event_message_type(filter.EventMessageType.ALL, priority=maxsize - 1) + async def handle_empty_mention(self, event: AstrMessageEvent): + """实现了对只有一个 @ 的消息内容的处理""" + try: + messages = event.get_messages() + if len(messages) == 1: + if ( + isinstance(messages[0], Comp.At) + and str(messages[0].qq) == str(event.get_self_id()) + and self.empty_mention_waiting + ) or ( + isinstance(messages[0], Comp.Plain) + and messages[0].text.strip() in self.wake_prefix + ): + yield event.plain_result("想要问什么呢?😄") + + @session_waiter(60) + async def empty_mention_waiter( + controller: SessionController, event: AstrMessageEvent + ): + logger.info("empty_mention_waiter") + event.message_obj.message.insert( + 0, Comp.At(qq=event.get_self_id(), name=event.get_self_id()) + ) + new_event = copy.copy(event) + self.context.get_event_queue().put_nowait( + new_event + ) # 重新推入事件队列 + event.stop_event() + controller.stop() + + try: + await empty_mention_waiter( + event, session_id=event.get_sender_id() + ) + except TimeoutError as _: + yield event.plain_result("如果需要帮助,请再次 @ 我哦~") + except Exception as e: + yield event.plain_result("发生错误,请联系管理员: " + str(e)) + finally: + event.stop_event() + except Exception as e: + logger.error("handle_empty_mention error: " + str(e))