feat(plugin): 新增极简插件开发模式

新增 SimplePlugin 基类,提供面向新手的极简插件开发方式
添加相关示例代码和文档说明
This commit is contained in:
2026-03-08 19:02:09 +08:00
parent dec1a43f28
commit 958c1df1fc
5 changed files with 430 additions and 0 deletions

217
core/plugin.py Normal file
View File

@@ -0,0 +1,217 @@
import inspect
import functools
from typing import Optional, Union, Any, Callable
from core.managers.command_manager import matcher as command_manager
from core.permission import Permission
from models.events.message import MessageEvent
class Plugin:
"""
插件基类,提供类风格的插件编写方式。
通过继承此类,可以使用装饰器在类方法上注册命令和事件处理器。
"""
def __init__(self):
self._register_handlers()
def _register_handlers(self):
"""
自动注册带有装饰器的方法。
"""
# 遍历实例的所有方法
for name, method in inspect.getmembers(self, predicate=inspect.ismethod):
# 检查是否有命令元数据
if hasattr(method, "_command_meta"):
meta = method._command_meta
# 调用 command_manager 的装饰器来注册绑定后的方法
command_manager.command(
*meta['names'],
permission=meta.get('permission'),
override_permission_check=meta.get('override_permission_check', False)
)(method)
# 检查是否有消息处理元数据
if hasattr(method, "_on_message_meta"):
command_manager.on_message()(method)
# 检查是否有通知处理元数据
if hasattr(method, "_on_notice_meta"):
meta = method._on_notice_meta
command_manager.on_notice(notice_type=meta.get('notice_type'))(method)
# 检查是否有请求处理元数据
if hasattr(method, "_on_request_meta"):
meta = method._on_request_meta
command_manager.on_request(request_type=meta.get('request_type'))(method)
async def send(self, event: MessageEvent, message: Union[str, Any]):
"""
发送消息的基础逻辑。
"""
if hasattr(event, 'reply'):
await event.reply(message)
else:
pass
async def reply(self, event: MessageEvent, message: Union[str, Any]):
"""
回复消息。
"""
await self.send(event, message)
class SimplePlugin(Plugin):
"""
面向新手的简化插件基类。
特性:
1. 自动将公共方法不以_开头注册为指令。
2. 指令名默认为方法名。
3. 自动解析参数类型。
4. 支持直接返回字符串来回复消息。
"""
def _register_handlers(self):
# 先处理带装饰器的方法
super()._register_handlers()
# 扫描普通方法并注册为指令
for name, method in inspect.getmembers(self, predicate=inspect.ismethod):
if name.startswith("_"):
continue
if hasattr(method, "_command_meta"):
continue # 已经处理过
if hasattr(method, "_on_message_meta"):
continue
if hasattr(method, "_on_notice_meta"):
continue
if hasattr(method, "_on_request_meta"):
continue
if name in dir(Plugin):
continue # 忽略基类方法
self._register_method_as_command(name, method)
def _register_method_as_command(self, name: str, method: Callable):
# 获取方法的签名
sig = inspect.signature(method)
# 包装函数
@functools.wraps(method)
async def wrapper(event: MessageEvent, args: list[str]):
try:
# 准备调用参数
call_args: list[Any] = []
# 跳过 self第一个参数应该是 event
params = list(sig.parameters.values())
if not params:
# 方法没有参数?这不应该发生,至少要有 event
await method()
return
# 绑定 event
call_args.append(event)
# 处理剩余参数
method_params = params[1:] # 除去 event
if not method_params:
# 方法不需要额外参数
pass
elif len(method_params) == 1:
# 只有一个参数,把所有 args 拼起来传给它
param = method_params[0]
if args:
str_val = " ".join(args)
val: Any = str_val
# 类型转换
if param.annotation is int:
val = int(str_val)
elif param.annotation is float:
val = float(str_val)
call_args.append(val)
elif param.default is not inspect.Parameter.empty:
call_args.append(param.default)
else:
await event.reply(f"缺少参数: {param.name}")
return
else:
# 多个参数,尝试一一对应
if len(args) < len([p for p in method_params if p.default is inspect.Parameter.empty]):
# 必填参数不足
usage = " ".join([f"<{p.name}>" for p in method_params])
await event.reply(f"参数不足。用法: /{name} {usage}")
return
for i, param in enumerate(method_params):
if i < len(args):
arg_str = args[i]
arg_val: Any = arg_str
# 简单的类型转换
try:
if param.annotation is int:
arg_val = int(arg_str)
elif param.annotation is float:
arg_val = float(arg_str)
except ValueError:
await event.reply(f"参数 {param.name} 类型错误,应为 {param.annotation.__name__}")
return
call_args.append(arg_val)
else:
call_args.append(param.default)
# 调用方法
result = await method(*call_args)
# 如果有返回值,自动回复
if result is not None:
await event.reply(str(result))
except Exception as e:
await event.reply(f"执行命令时发生错误: {str(e)}")
# 注册命令
command_manager.command(name)(wrapper)
def command(name: str, *aliases: str, permission: Optional[Permission] = None, override_permission_check: bool = False):
"""
装饰器:标记方法为命令处理器。
"""
def decorator(func):
func._command_meta = {
"names": (name,) + aliases,
"permission": permission,
"override_permission_check": override_permission_check
}
return func
return decorator
def on_message():
"""
装饰器:标记方法为通用消息处理器。
"""
def decorator(func):
func._on_message_meta = {}
return func
return decorator
def on_notice(notice_type: Optional[str] = None):
"""
装饰器:标记方法为通知处理器。
"""
def decorator(func):
func._on_notice_meta = {
"notice_type": notice_type
}
return func
return decorator
def on_request(request_type: Optional[str] = None):
"""
装饰器:标记方法为请求处理器。
"""
def decorator(func):
func._on_request_meta = {
"request_type": request_type
}
return func
return decorator