feat: 支持当消息只有@bot时,下一条发送人的消息直接唤醒机器人

This commit is contained in:
Soulter
2025-03-08 19:55:24 +08:00
parent 7a0864f5c2
commit 4aa91ad599
7 changed files with 96 additions and 15 deletions
+7 -1
View File
@@ -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",
+2 -1
View File
@@ -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):
@@ -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":
+3 -6
View File
@@ -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)
+10
View File
@@ -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
+12 -4
View File
@@ -267,10 +267,14 @@ UID: {user_id} 此 ID 可用于设置管理员。/op <UID> 授权管理员, /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 <admin_id>"""
if admin_id is None:
event.set_result(MessageEventResult().message("使用方法: /op <id> 授权管理员;/deop <id> 取消管理员。可通过 /sid 获取 ID。"))
event.set_result(
MessageEventResult().message(
"使用方法: /op <id> 授权管理员;/deop <id> 取消管理员。可通过 /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 <UID> 授权管理员, /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 <sid>"""
if sid is None:
event.set_result(MessageEventResult().message("使用方法: /wl <id> 添加白名单;/dwl <id> 删除白名单。可通过 /sid 获取 ID。"))
event.set_result(
MessageEventResult().message(
"使用方法: /wl <id> 添加白名单;/dwl <id> 删除白名单。可通过 /sid 获取 ID。"
)
)
self.context.get_config()["platform_settings"]["id_whitelist"].append(sid)
self.context.get_config().save_config()
event.set_result(MessageEventResult().message("添加白名单成功。"))
+60 -2
View File
@@ -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))