mirror of
https://github.com/langbot-app/LangBot.git
synced 2025-11-25 19:37:36 +08:00
feat: 基本插件加载功能
This commit is contained in:
10
main.py
10
main.py
@@ -117,9 +117,16 @@ def main(first_time_init=False):
|
||||
timeout=config.process_message_timeout, retry=config.retry_times,
|
||||
first_time_init=first_time_init)
|
||||
|
||||
# 加载插件
|
||||
import pkg.plugin.host
|
||||
pkg.plugin.host.load_plugins()
|
||||
|
||||
pkg.plugin.host.initialize_plugins()
|
||||
|
||||
if first_time_init: # 不是热重载之后的启动,则不启动新的bot线程
|
||||
|
||||
import mirai.exceptions
|
||||
|
||||
def run_bot_wrapper():
|
||||
global known_exception_caught
|
||||
try:
|
||||
@@ -201,6 +208,9 @@ def stop():
|
||||
import pkg.qqbot.manager
|
||||
import pkg.openai.session
|
||||
try:
|
||||
import pkg.plugin.host
|
||||
pkg.plugin.host.unload_plugins()
|
||||
|
||||
qqbot_inst = pkg.utils.context.get_qqbot_manager()
|
||||
assert isinstance(qqbot_inst, pkg.qqbot.manager.QQBotManager)
|
||||
|
||||
|
||||
0
pkg/plugin/__init__.py
Normal file
0
pkg/plugin/__init__.py
Normal file
93
pkg/plugin/host.py
Normal file
93
pkg/plugin/host.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# 插件管理模块
|
||||
import logging
|
||||
import importlib
|
||||
import pkgutil
|
||||
import sys
|
||||
|
||||
import pkg.utils.context as context
|
||||
|
||||
__plugins__ = {}
|
||||
"""{
|
||||
"example": {
|
||||
"name": "example",
|
||||
"description": "example",
|
||||
"version": "0.0.1",
|
||||
"author": "RockChinQ",
|
||||
"class": <class 'plugins.example.ExamplePlugin'>,
|
||||
"hooks": {
|
||||
"person_message": [
|
||||
<function ExamplePlugin.person_message at 0x0000020E1D1B8D38>
|
||||
]
|
||||
},
|
||||
"instance": None
|
||||
}
|
||||
}"""
|
||||
|
||||
|
||||
def walk_plugin_path(module, prefix=''):
|
||||
"""遍历插件路径"""
|
||||
for item in pkgutil.iter_modules(module.__path__):
|
||||
if item.ispkg:
|
||||
walk_plugin_path(__import__(module.__name__ + '.' + item.name, fromlist=['']), prefix + item.name + '.')
|
||||
else:
|
||||
logging.info('加载模块: {}'.format(prefix + item.name))
|
||||
|
||||
importlib.import_module(module.__name__ + '.' + item.name)
|
||||
|
||||
|
||||
def load_plugins():
|
||||
""" 加载插件 """
|
||||
logging.info("加载插件")
|
||||
PluginHost()
|
||||
walk_plugin_path(__import__('plugins'))
|
||||
|
||||
logging.debug(__plugins__)
|
||||
|
||||
|
||||
def initialize_plugins():
|
||||
""" 初始化插件 """
|
||||
logging.info("初始化插件")
|
||||
for plugin in __plugins__.values():
|
||||
try:
|
||||
plugin['instance'] = plugin["class"]()
|
||||
except:
|
||||
logging.error("插件{}初始化时发生错误: {}".format(plugin['name'], sys.exc_info()))
|
||||
|
||||
|
||||
def unload_plugins():
|
||||
""" 卸载插件 """
|
||||
for plugin in __plugins__.values():
|
||||
if plugin['instance'] is not None:
|
||||
if not hasattr(plugin['instance'], '__del__'):
|
||||
logging.warning("插件{}没有定义析构函数".format(plugin['name']))
|
||||
else:
|
||||
try:
|
||||
plugin['instance'].__del__()
|
||||
logging.info("卸载插件: {}".format(plugin['name']))
|
||||
except:
|
||||
logging.error("插件{}卸载时发生错误: {}".format(plugin['name'], sys.exc_info()))
|
||||
|
||||
|
||||
def emit(event: str, **kwargs):
|
||||
""" 触发事件 """
|
||||
for plugin in __plugins__.values():
|
||||
for hook in plugin['hooks'].get(event, []):
|
||||
try:
|
||||
kwargs['plugin_host'] = context.get_plugin_host()
|
||||
hook(plugin['instance'], **kwargs)
|
||||
except:
|
||||
logging.error("插件{}触发事件{}时发生错误: {}".format(plugin['name'], event, sys.exc_info()))
|
||||
|
||||
|
||||
class PluginHost:
|
||||
"""插件宿主"""
|
||||
|
||||
def __init__(self):
|
||||
context.set_plugin_host(self)
|
||||
|
||||
def prevent_default(self):
|
||||
"""阻止默认行为"""
|
||||
|
||||
def get_runtime_context(self) -> context:
|
||||
"""获取运行时上下文"""
|
||||
return context
|
||||
89
pkg/plugin/models.py
Normal file
89
pkg/plugin/models.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import logging
|
||||
|
||||
import pkg.plugin.host as host
|
||||
|
||||
__current_registering_plugin__ = ""
|
||||
|
||||
import pkg.utils.context
|
||||
|
||||
PersonMessage = "person_message"
|
||||
GroupMessage = "group_message"
|
||||
PersonNormalMessage = "person_normal_message"
|
||||
PersonCommand = "person_command"
|
||||
GroupNormalMessage = "group_normal_message"
|
||||
GroupCommand = "group_command"
|
||||
SessionFirstMessage = "session_first_message"
|
||||
SessionReset = "session_reset"
|
||||
SessionExpired = "session_expired"
|
||||
KeyExceeded = "key_exceeded"
|
||||
KeySwitched = "key_switched"
|
||||
|
||||
|
||||
class Plugin:
|
||||
|
||||
host: host.PluginHost
|
||||
"""插件宿主,提供插件的一些基础功能"""
|
||||
|
||||
@classmethod
|
||||
def on(cls, event):
|
||||
"""事件处理器装饰器
|
||||
|
||||
:param
|
||||
event: 事件类型
|
||||
:return:
|
||||
None
|
||||
"""
|
||||
|
||||
def wrapper(func):
|
||||
plugin_hooks = host.__plugins__[__current_registering_plugin__]["hooks"]
|
||||
|
||||
if event not in plugin_hooks:
|
||||
plugin_hooks[event] = []
|
||||
plugin_hooks[event].append(func)
|
||||
|
||||
host.__plugins__[__current_registering_plugin__]["hooks"] = plugin_hooks
|
||||
|
||||
return func
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def register(name: str, description: str, version: str, author: str):
|
||||
"""注册插件, 此函数作为装饰器使用
|
||||
|
||||
Args:
|
||||
name (str): 插件名称
|
||||
description (str): 插件描述
|
||||
version (str): 插件版本
|
||||
author (str): 插件作者
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
global __current_registering_plugin__
|
||||
|
||||
__current_registering_plugin__ = name
|
||||
|
||||
host.__plugins__[name] = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"version": version,
|
||||
"author": author,
|
||||
"hooks": {}
|
||||
}
|
||||
|
||||
def wrapper(cls: Plugin):
|
||||
cls.name = name
|
||||
cls.description = description
|
||||
cls.version = version
|
||||
cls.author = author
|
||||
cls.host = pkg.utils.context.get_plugin_host()
|
||||
|
||||
# 存到插件列表
|
||||
host.__plugins__[name]["class"] = cls
|
||||
|
||||
logging.info("插件注册完成: n='{}', d='{}', v={}, a='{}' ({})".format(name, description, version, author, cls))
|
||||
|
||||
return cls
|
||||
|
||||
return wrapper
|
||||
@@ -16,6 +16,9 @@ import pkg.qqbot.filter
|
||||
import pkg.qqbot.process as processor
|
||||
import pkg.utils.context
|
||||
|
||||
import pkg.plugin.host as plugin_host
|
||||
import pkg.plugin.models as plugin_models
|
||||
|
||||
|
||||
# 并行运行
|
||||
def go(func, args=()):
|
||||
@@ -155,6 +158,8 @@ class QQBotManager:
|
||||
|
||||
# 私聊消息处理
|
||||
def on_person_message(self, event: MessageEvent):
|
||||
plugin_host.emit(plugin_models.PersonMessage)
|
||||
|
||||
reply = ''
|
||||
|
||||
if event.sender.id == self.bot.qq:
|
||||
|
||||
@@ -6,6 +6,7 @@ context = {
|
||||
},
|
||||
'logger_handler': None,
|
||||
'config': None,
|
||||
'plugin_host': None,
|
||||
}
|
||||
|
||||
|
||||
@@ -39,3 +40,11 @@ def set_qqbot_manager(inst):
|
||||
|
||||
def get_qqbot_manager():
|
||||
return context['inst']['qqbot.manager.QQBotManager']
|
||||
|
||||
|
||||
def set_plugin_host(inst):
|
||||
context['plugin_host'] = inst
|
||||
|
||||
|
||||
def get_plugin_host():
|
||||
return context['plugin_host']
|
||||
|
||||
@@ -32,6 +32,7 @@ def reload_all(notify=True):
|
||||
importlib.reload(__import__('config'))
|
||||
importlib.reload(__import__('main'))
|
||||
importlib.reload(__import__('banlist'))
|
||||
importlib.reload(__import__('plugins'))
|
||||
pkg.utils.context.context = context
|
||||
|
||||
# 执行启动流程
|
||||
|
||||
Reference in New Issue
Block a user