diff --git a/astrbot/core/star/__init__.py b/astrbot/core/star/__init__.py index ca4b9ddb..28cb9a90 100644 --- a/astrbot/core/star/__init__.py +++ b/astrbot/core/star/__init__.py @@ -16,4 +16,16 @@ class Star(CommandParserMixin): async def html_render(self, tmpl: str, data: dict, return_url = True) -> str: '''渲染 HTML''' - return await html_renderer.render_custom_template(tmpl, data, return_url=return_url) \ No newline at end of file + return await html_renderer.render_custom_template(tmpl, data, return_url=return_url) + + async def terminate(self): + '''当插件被禁用、重载插件时会调用这个方法''' + pass + +__all__ = [ + 'Star', + 'StarMetadata', + 'PluginManager', + 'Context', + 'Provider' +] \ No newline at end of file diff --git a/astrbot/core/star/star_manager.py b/astrbot/core/star/star_manager.py index 44e8aa56..6802f861 100644 --- a/astrbot/core/star/star_manager.py +++ b/astrbot/core/star/star_manager.py @@ -6,6 +6,7 @@ import json import traceback import yaml import logging +import asyncio from types import ModuleType from typing import List from astrbot.core.config.astrbot_config import AstrBotConfig @@ -151,11 +152,14 @@ class PluginManager: # 终止插件 if not specified_module_path: + # 重载所有插件 for smd in star_registry: - logger.debug(f"尝试终止插件 {smd.name} ...") - if hasattr(smd.star_cls, "__del__"): - smd.star_cls.__del__() - + try: + await self._terminate_plugin(smd) + except Exception as e: + logger.warning(traceback.format_exc()) + logger.warning(f"插件 {smd.name} 未被正常终止: {str(e)}, 可能会导致该插件运行不正常。") + star_handlers_registry.clear() star_map.clear() star_registry.clear() @@ -166,6 +170,12 @@ class PluginManager: # 只重载指定插件 smd = star_map.get(specified_module_path) if smd: + try: + await self._terminate_plugin(smd) + except Exception as e: + logger.warning(traceback.format_exc()) + logger.warning(f"插件 {smd.name} 未被正常终止: {str(e)}, 可能会导致该插件运行不正常。") + await self._unbind_plugin(smd.name, specified_module_path) try: del sys.modules[specified_module_path] @@ -355,6 +365,13 @@ class PluginManager: root_dir_name = plugin.root_dir_name ppath = self.plugin_store_path + # 终止插件 + try: + await self._terminate_plugin(plugin) + except Exception as e: + logger.warning(traceback.format_exc()) + logger.warning(f"插件 {plugin_name} 未被正常终止 {str(e)}, 可能会导致资源泄露等问题。") + # 从 star_registry 和 star_map 中删除 await self._unbind_plugin(plugin_name, plugin.module_path) @@ -377,6 +394,7 @@ class PluginManager: del star_handlers_registry.star_handlers_map[k] async def update_plugin(self, plugin_name: str, proxy = ""): + '''升级一个插件''' plugin = self.context.get_registered_star(plugin_name) if not plugin: raise Exception("插件不存在。") @@ -387,9 +405,20 @@ class PluginManager: await self.reload() async def turn_off_plugin(self, plugin_name: str): + ''' + 禁用一个插件。 + 调用插件的 terminate() 方法, + 将插件的 module_path 加入到 data/shared_preferences.json 的 inactivated_plugins 列表中。 + 并且同时将插件启用的 llm_tool 禁用。 + ''' plugin = self.context.get_registered_star(plugin_name) if not plugin: raise Exception("插件不存在。") + + # 调用插件的终止方法 + await self._terminate_plugin(plugin) + + # 加入到 shared_preferences 中 inactivated_plugins: list = sp.get("inactivated_plugins", []) if plugin.module_path not in inactivated_plugins: inactivated_plugins.append(plugin.module_path) @@ -408,6 +437,15 @@ class PluginManager: plugin.activated = False + async def _terminate_plugin(self, star_metadata: StarMetadata): + '''终止插件,调用插件的 terminate() 和 __del__() 方法''' + logging.info(f"正在终止插件 {star_metadata.name} ...") + + if hasattr(star_metadata.star_cls, "__del__"): + asyncio.get_event_loop().run_in_executor(star_metadata.star_cls.__del__) + else: + await star_metadata.star_cls.terminate() + async def turn_on_plugin(self, plugin_name: str): plugin = self.context.get_registered_star(plugin_name) inactivated_plugins: list = sp.get("inactivated_plugins", []) @@ -425,7 +463,6 @@ class PluginManager: plugin.activated = True - async def install_plugin_from_file(self, zip_file_path: str): dir_name = os.path.basename(zip_file_path).replace(".zip", "") dir_name = dir_name.removesuffix("-master").removesuffix("-main").lower() diff --git a/packages/astrbot/main.py b/packages/astrbot/main.py index 90f92c2c..1882c07c 100644 --- a/packages/astrbot/main.py +++ b/packages/astrbot/main.py @@ -980,19 +980,3 @@ UID: {user_id} 此 ID 可用于设置管理员。/op 授权管理员, /deo found_command.event_filters.insert(0, PermissionTypeFilter(filter.PermissionType.ADMIN if cmd_type == "admin" else filter.PermissionType.MEMBER)) yield event.plain_result(f"已将 {cmd_name} 设置为 {cmd_type} 指令") - - # @filter.command_group("kdb") - # def kdb(self): - # pass - - - # @filter.on_llm_request() - # async def on_llm_response(self, event: AstrMessageEvent, req: ProviderRequest): - # curr_kdb_name = self.context.provider_manager.curr_kdb_name - # if self.kdb_enabled and curr_kdb_name: - # mgr = self.context.knowledge_db_manager - # results = await mgr.retrive_records(curr_kdb_name, req.prompt) - # if results: - # req.system_prompt += "\nHere are documents that related to user's query: \n" - # for result in results: - # req.system_prompt += f"- {result}\n"7 diff --git a/packages/reminder/main.py b/packages/reminder/main.py index 2df153a0..8d5e836e 100644 --- a/packages/reminder/main.py +++ b/packages/reminder/main.py @@ -193,4 +193,9 @@ class Main(star.Star): async def _reminder_callback(self, unified_msg_origin: str, d: dict): '''The callback function of the reminder.''' logger.info(f"Reminder Activated: {d['text']}, created by {unified_msg_origin}") - await self.context.send_message(unified_msg_origin, MessageEventResult().message("待办提醒: \n\n" + d['text'] + "\n时间: " + d.get("datetime", "") + d.get("cron_h", ""))) \ No newline at end of file + await self.context.send_message(unified_msg_origin, MessageEventResult().message("待办提醒: \n\n" + d['text'] + "\n时间: " + d.get("datetime", "") + d.get("cron_h", ""))) + + async def terminate(self): + self.scheduler.shutdown() + await self._save_data() + logger.info("Reminder plugin terminated.")