feat: support plugins

This commit is contained in:
lanvent
2023-03-12 11:53:06 +08:00
parent 9e07703eb1
commit 0fcf0824dc
8 changed files with 220 additions and 50 deletions

9
plugins/__init__.py Normal file
View File

@@ -0,0 +1,9 @@
from .plugin_manager import PluginManager
from .event import *
from .plugin import *
instance = PluginManager()
register = instance.register
# load_plugins = instance.load_plugins
# emit_event = instance.emit_event

49
plugins/event.py Normal file
View File

@@ -0,0 +1,49 @@
# encoding:utf-8
from enum import Enum
class Event(Enum):
# ON_RECEIVE_MESSAGE = 1 # 收到消息
ON_HANDLE_CONTEXT = 2 # 处理消息前
"""
e_context = { "channel": 消息channel, "context" : 本次消息的context, "reply" : 目前的回复,初始为空 }
"""
ON_DECORATE_REPLY = 3 # 得到回复后准备装饰
"""
e_context = { "channel": 消息channel, "context" : 本次消息的context, "reply" : 目前的回复 }
"""
ON_SEND_REPLY = 4 # 发送回复前
"""
e_context = { "channel": 消息channel, "context" : 本次消息的context, "reply" : 目前的回复 }
"""
# AFTER_SEND_REPLY = 5 # 发送回复后
class EventAction(Enum):
CONTINUE = 1 # 事件未结束,继续交给下个插件处理,如果没有下个插件,则交付给默认的事件处理逻辑
BREAK = 2 # 事件结束,不再给下个插件处理,交付给默认的事件处理逻辑
BREAK_PASS = 3 # 事件结束,不再给下个插件处理,不交付给默认的事件处理逻辑
class EventContext:
def __init__(self, event, econtext=dict()):
self.event = event
self.econtext = econtext
self.action = EventAction.CONTINUE
def __getitem__(self, key):
return self.econtext[key]
def __setitem__(self, key, value):
self.econtext[key] = value
def __delitem__(self, key):
del self.econtext[key]
def is_pass(self):
return self.action == EventAction.BREAK_PASS

3
plugins/plugin.py Normal file
View File

@@ -0,0 +1,3 @@
class Plugin:
def __init__(self):
self.handlers = {}

89
plugins/plugin_manager.py Normal file
View File

@@ -0,0 +1,89 @@
# encoding:utf-8
import importlib
import json
import os
from common.singleton import singleton
from .event import *
from .plugin import *
from common.log import logger
@singleton
class PluginManager:
def __init__(self):
self.plugins = {}
self.listening_plugins = {}
self.instances = {}
def register(self, name: str, desc: str, version: str, author: str):
def wrapper(plugincls):
self.plugins[name] = plugincls
plugincls.name = name
plugincls.desc = desc
plugincls.version = version
plugincls.author = author
plugincls.enabled = True
logger.info("Plugin %s registered" % name)
return plugincls
return wrapper
def save_config(self, pconf):
with open("plugins/plugins.json", "w", encoding="utf-8") as f:
json.dump(pconf, f, indent=4, ensure_ascii=False)
def load_config(self):
logger.info("Loading plugins config...")
plugins_dir = "plugins"
for plugin_name in os.listdir(plugins_dir):
plugin_path = os.path.join(plugins_dir, plugin_name)
if os.path.isdir(plugin_path):
# 判断插件是否包含main.py文件
main_module_path = os.path.join(plugin_path, "main.py")
if os.path.isfile(main_module_path):
# 导入插件的main
import_path = "{}.{}.main".format(plugins_dir, plugin_name)
main_module = importlib.import_module(import_path)
modified = False
if os.path.exists("plugins/plugins.json"):
with open("plugins/plugins.json", "r", encoding="utf-8") as f:
pconf = json.load(f)
else:
modified = True
pconf = {"plugins": []}
for name, plugincls in self.plugins.items():
if name not in [plugin["name"] for plugin in pconf["plugins"]]:
modified = True
logger.info("Plugin %s not found in pconfig, adding to pconfig..." % name)
pconf["plugins"].append({"name": name, "enabled": True})
if modified:
self.save_config(pconf)
return pconf
def load_plugins(self):
pconf = self.load_config()
for plugin in pconf["plugins"]:
name = plugin["name"]
enabled = plugin["enabled"]
self.plugins[name].enabled = enabled
for name, plugincls in self.plugins.items():
if plugincls.enabled:
if name not in self.instances:
instance = plugincls()
self.instances[name] = instance
for event in instance.handlers:
if event not in self.listening_plugins:
self.listening_plugins[event] = []
self.listening_plugins[event].append(name)
def emit_event(self, e_context: EventContext, *args, **kwargs):
if e_context.event in self.listening_plugins:
for name in self.listening_plugins[e_context.event]:
if e_context.action == EventAction.CONTINUE:
logger.debug("Plugin %s triggered by event %s" % (name,e_context.event))
instance = self.instances[name]
instance.handlers[e_context.event](e_context, *args, **kwargs)
return e_context