import inspect import traceback import typing as T from dataclasses import dataclass from astrbot.core.config.astrbot_config import AstrBotConfig from astrbot.core.platform.astr_message_event import AstrMessageEvent from astrbot.core.star import PluginManager from astrbot.api import logger from astrbot.core.star.star_handler import star_handlers_registry, EventType from astrbot.core.star.star import star_map from astrbot.core.message.message_event_result import MessageEventResult, CommandResult @dataclass class PipelineContext: """上下文对象,包含管道执行所需的上下文信息""" astrbot_config: AstrBotConfig # AstrBot 配置对象 plugin_manager: PluginManager # 插件管理器对象 async def call_event_hook( self, event: AstrMessageEvent, hook_type: EventType, *args, ) -> bool: """调用事件钩子函数 Returns: bool: 如果事件被终止,返回 True """ platform_id = event.get_platform_id() handlers = star_handlers_registry.get_handlers_by_event_type( hook_type, platform_id=platform_id ) for handler in handlers: try: logger.debug( f"hook(on_llm_request) -> {star_map[handler.handler_module_path].name} - {handler.handler_name}" ) await handler.handler(event, *args) except BaseException: logger.error(traceback.format_exc()) if event.is_stopped(): logger.info( f"{star_map[handler.handler_module_path].name} - {handler.handler_name} 终止了事件传播。" ) return event.is_stopped() async def call_handler( self, event: AstrMessageEvent, handler: T.Awaitable, *args, **kwargs, ) -> T.AsyncGenerator[None, None]: """执行事件处理函数并处理其返回结果 该方法负责调用处理函数并处理不同类型的返回值。它支持两种类型的处理函数: 1. 异步生成器: 实现洋葱模型,每次 yield 都会将控制权交回上层 2. 协程: 执行一次并处理返回值 Args: ctx (PipelineContext): 消息管道上下文对象 event (AstrMessageEvent): 事件对象 handler (Awaitable): 事件处理函数 Returns: AsyncGenerator[None, None]: 异步生成器,用于在管道中传递控制流 """ ready_to_call = None # 一个协程或者异步生成器 trace_ = None try: ready_to_call = handler(event, *args, **kwargs) except TypeError as _: # 向下兼容 trace_ = traceback.format_exc() # 以前的 handler 会额外传入一个参数, 但是 context 对象实际上在插件实例中有一份 ready_to_call = handler(event, self.plugin_manager.context, *args, **kwargs) if inspect.isasyncgen(ready_to_call): _has_yielded = False try: async for ret in ready_to_call: # 这里逐步执行异步生成器, 对于每个 yield 返回的 ret, 执行下面的代码 # 返回值只能是 MessageEventResult 或者 None(无返回值) _has_yielded = True if isinstance(ret, (MessageEventResult, CommandResult)): # 如果返回值是 MessageEventResult, 设置结果并继续 event.set_result(ret) yield else: # 如果返回值是 None, 则不设置结果并继续 # 继续执行后续阶段 yield ret if not _has_yielded: # 如果这个异步生成器没有执行到 yield 分支 yield except Exception as e: logger.error(f"Previous Error: {trace_}") raise e elif inspect.iscoroutine(ready_to_call): # 如果只是一个协程, 直接执行 ret = await ready_to_call if isinstance(ret, (MessageEventResult, CommandResult)): event.set_result(ret) yield else: yield ret