233 lines
6.9 KiB
Python
233 lines
6.9 KiB
Python
"""
|
||
插件基类模块
|
||
|
||
提供插件的基础功能:
|
||
- 生命周期钩子(on_load, on_enable, on_disable, on_unload, on_reload)
|
||
- 定时任务管理
|
||
- 依赖声明
|
||
- 插件元数据
|
||
"""
|
||
|
||
from abc import ABC
|
||
from enum import Enum
|
||
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
||
|
||
from loguru import logger
|
||
|
||
from .decorators import scheduler, add_job_safe, remove_job_safe
|
||
|
||
if TYPE_CHECKING:
|
||
from utils.plugin_manager import PluginManager
|
||
|
||
|
||
class PluginState(Enum):
|
||
"""插件状态"""
|
||
UNLOADED = "unloaded" # 未加载
|
||
LOADED = "loaded" # 已加载(未启用)
|
||
ENABLED = "enabled" # 已启用
|
||
DISABLED = "disabled" # 已禁用
|
||
ERROR = "error" # 错误状态
|
||
|
||
|
||
class PluginBase(ABC):
|
||
"""
|
||
插件基类
|
||
|
||
生命周期:
|
||
1. __init__() - 构造函数(同步)
|
||
2. on_load() - 加载时调用(异步,可访问其他插件)
|
||
3. async_init() - 异步初始化(异步,加载配置、资源等)
|
||
4. on_enable() - 启用时调用(异步,注册定时任务)
|
||
5. on_disable() - 禁用时调用(异步,清理定时任务)
|
||
6. on_unload() - 卸载时调用(异步,释放资源)
|
||
7. on_reload() - 重载前调用(异步,保存状态)
|
||
|
||
使用示例:
|
||
class MyPlugin(PluginBase):
|
||
description = "我的插件"
|
||
author = "作者"
|
||
version = "1.0.0"
|
||
dependencies = ["AIChat"] # 依赖的插件
|
||
load_priority = 60 # 加载优先级
|
||
|
||
async def on_load(self, plugin_manager):
|
||
# 可以访问其他插件
|
||
aichat = plugin_manager.plugins.get("AIChat")
|
||
|
||
async def async_init(self):
|
||
# 加载配置、初始化资源
|
||
self.config = load_config()
|
||
|
||
async def on_enable(self, bot):
|
||
await super().on_enable(bot) # 注册定时任务
|
||
# 额外的启用逻辑
|
||
|
||
async def on_disable(self):
|
||
await super().on_disable() # 清理定时任务
|
||
# 额外的禁用逻辑
|
||
|
||
async def on_unload(self):
|
||
# 释放资源、关闭连接
|
||
await self.close_connections()
|
||
|
||
async def on_reload(self) -> dict:
|
||
# 返回需要保存的状态
|
||
return {"counter": self.counter}
|
||
|
||
async def restore_state(self, state: dict):
|
||
# 重载后恢复状态
|
||
self.counter = state.get("counter", 0)
|
||
"""
|
||
|
||
# ==================== 插件元数据 ====================
|
||
|
||
description: str = "暂无描述"
|
||
author: str = "未知"
|
||
version: str = "1.0.0"
|
||
|
||
# 插件依赖(填写依赖的插件类名列表)
|
||
dependencies: List[str] = []
|
||
|
||
# 加载优先级(数值越大越先加载,默认50)
|
||
load_priority: int = 50
|
||
|
||
# ==================== 实例属性 ====================
|
||
|
||
def __init__(self):
|
||
self.enabled = False
|
||
self.state = PluginState.UNLOADED
|
||
self._scheduled_jobs: set = set()
|
||
self._bot = None
|
||
self._plugin_manager: Optional["PluginManager"] = None
|
||
self._saved_state: Dict[str, Any] = {}
|
||
|
||
# ==================== 生命周期钩子 ====================
|
||
|
||
async def on_load(self, plugin_manager: "PluginManager"):
|
||
"""
|
||
插件加载时调用
|
||
|
||
此时其他依赖的插件已经加载完成,可以安全访问。
|
||
|
||
Args:
|
||
plugin_manager: 插件管理器实例
|
||
"""
|
||
self._plugin_manager = plugin_manager
|
||
self.state = PluginState.LOADED
|
||
logger.debug(f"[{self.__class__.__name__}] on_load 调用")
|
||
|
||
async def async_init(self):
|
||
"""
|
||
插件异步初始化
|
||
|
||
用于加载配置、初始化资源等耗时操作。
|
||
在 on_load 之后、on_enable 之前调用。
|
||
"""
|
||
pass
|
||
|
||
async def on_enable(self, bot=None):
|
||
"""
|
||
插件启用时调用
|
||
|
||
注册定时任务、启动后台服务等。
|
||
|
||
Args:
|
||
bot: WechatHookClient 实例
|
||
"""
|
||
self._bot = bot
|
||
self.enabled = True
|
||
self.state = PluginState.ENABLED
|
||
|
||
# 注册定时任务
|
||
for method_name in dir(self):
|
||
method = getattr(self, method_name)
|
||
if hasattr(method, '_is_scheduled'):
|
||
job_id = getattr(method, '_job_id')
|
||
trigger = getattr(method, '_schedule_trigger')
|
||
trigger_args = getattr(method, '_schedule_args')
|
||
|
||
add_job_safe(scheduler, job_id, method, bot, trigger, **trigger_args)
|
||
self._scheduled_jobs.add(job_id)
|
||
|
||
if self._scheduled_jobs:
|
||
logger.success(f"插件 {self.__class__.__name__} 已加载定时任务: {self._scheduled_jobs}")
|
||
|
||
async def on_disable(self):
|
||
"""
|
||
插件禁用时调用
|
||
|
||
清理定时任务、停止后台服务等。
|
||
"""
|
||
self.enabled = False
|
||
self.state = PluginState.DISABLED
|
||
|
||
# 移除定时任务
|
||
for job_id in self._scheduled_jobs:
|
||
remove_job_safe(scheduler, job_id)
|
||
|
||
if self._scheduled_jobs:
|
||
logger.info(f"已卸载定时任务: {self._scheduled_jobs}")
|
||
self._scheduled_jobs.clear()
|
||
|
||
async def on_unload(self):
|
||
"""
|
||
插件卸载时调用
|
||
|
||
释放资源、关闭连接、保存数据等。
|
||
在 on_disable 之后调用。
|
||
"""
|
||
self.state = PluginState.UNLOADED
|
||
self._bot = None
|
||
self._plugin_manager = None
|
||
logger.debug(f"[{self.__class__.__name__}] on_unload 调用")
|
||
|
||
async def on_reload(self) -> Dict[str, Any]:
|
||
"""
|
||
插件重载前调用
|
||
|
||
返回需要在重载后恢复的状态数据。
|
||
|
||
Returns:
|
||
需要保存的状态字典
|
||
"""
|
||
logger.debug(f"[{self.__class__.__name__}] on_reload 调用")
|
||
return {}
|
||
|
||
async def restore_state(self, state: Dict[str, Any]):
|
||
"""
|
||
重载后恢复状态
|
||
|
||
Args:
|
||
state: on_reload 返回的状态字典
|
||
"""
|
||
self._saved_state = state
|
||
logger.debug(f"[{self.__class__.__name__}] 状态已恢复: {list(state.keys())}")
|
||
|
||
# ==================== 辅助方法 ====================
|
||
|
||
def get_plugin(self, plugin_name: str) -> Optional["PluginBase"]:
|
||
"""
|
||
获取其他插件实例
|
||
|
||
Args:
|
||
plugin_name: 插件类名
|
||
|
||
Returns:
|
||
插件实例,不存在返回 None
|
||
"""
|
||
if self._plugin_manager:
|
||
return self._plugin_manager.plugins.get(plugin_name)
|
||
return None
|
||
|
||
def get_bot(self):
|
||
"""获取 bot 实例"""
|
||
return self._bot
|
||
|
||
@property
|
||
def plugin_name(self) -> str:
|
||
"""获取插件名称"""
|
||
return self.__class__.__name__
|
||
|
||
def __repr__(self) -> str:
|
||
return f"<{self.__class__.__name__} v{self.version} state={self.state.value}>"
|