From e1d0473627141bea9aec987ca37ba20ad653fcd4 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 3 Mar 2024 11:18:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=8F=92=E4=BB=B6=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E5=90=8E=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/main.py | 3 +- api/plugin.py | 74 +++++++++++++++++++++++++++++++++++++++-- blcsdk/__init__.py | 2 +- config.py | 2 ++ data/config.example.ini | 4 +++ services/plugin.py | 8 +++++ 6 files changed, 88 insertions(+), 5 deletions(-) diff --git a/api/main.py b/api/main.py index 3bdb50f..be9d7a6 100644 --- a/api/main.py +++ b/api/main.py @@ -46,7 +46,8 @@ class ServerInfoHandler(api.base.ApiHandler): 'config': { 'enableTranslate': cfg.enable_translate, 'enableUploadFile': cfg.enable_upload_file, - 'loaderUrl': cfg.loader_url + 'loaderUrl': cfg.loader_url, + 'enableAdminPlugins': cfg.enable_admin_plugins, } }) diff --git a/api/plugin.py b/api/plugin.py index 87b46c0..daafb42 100644 --- a/api/plugin.py +++ b/api/plugin.py @@ -10,6 +10,7 @@ import tornado.websocket import api.base import api.chat import blcsdk.models as models +import config import services.avatar import services.chat import services.plugin @@ -17,7 +18,71 @@ import services.plugin logger = logging.getLogger(__name__) -class _PluginHandlerBase(api.base.ApiHandler): +class _AdminHandlerBase(api.base.ApiHandler): + def prepare(self): + cfg = config.get_config() + if not cfg.enable_admin_plugins: + raise tornado.web.HTTPError(403) + + super().prepare() + + def _get_plugin(self): + plugin_id = self.json_args.get('pluginId', None) + if not isinstance(plugin_id, str) or plugin_id == '': + raise tornado.web.MissingArgumentError('pluginId') + plugin = services.plugin.get_plugin(plugin_id) + if plugin is None: + raise tornado.web.HTTPError(404, 'no plugin, plugin_id=%s', plugin_id) + return plugin + + +# 不继承_AdminHandlerBase,为了忽略enable_admin_plugins +class PluginsHandler(api.base.ApiHandler): + async def get(self): + plugin_dicts = [] + for plugin in services.plugin.iter_plugins(): + plugin_cfg = plugin.config + plugin_dicts.append({ + 'id': plugin.id, + 'name': plugin_cfg.name, + 'version': plugin_cfg.version, + 'author': plugin_cfg.author, + 'description': plugin_cfg.description, + 'enabled': plugin.enabled, + 'isStarted': plugin.is_started, + 'isConnected': plugin.is_connected, + }) + self.write({'plugins': plugin_dicts}) + + +class EnableHandler(_AdminHandlerBase): + async def post(self): + enabled = bool(self.json_args.get('enabled', False)) + + plugin = self._get_plugin() + msg = '' + try: + plugin.enabled = enabled + except services.plugin.StartTooFrequently as e: + msg = str(e) + plugin.enabled = False + except services.plugin.StartPluginError as e: + msg = str(e) + self.write({ + 'enabled': plugin.enabled, + 'msg': msg + }) + + +class OpenAdminUiHandler(_AdminHandlerBase): + async def post(self): + plugin = self._get_plugin() + plugin.send_cmd_data(models.Command.OPEN_PLUGIN_ADMIN_UI, {}) + self.write({}) + + +class _PluginApiHandlerBase(api.base.ApiHandler): + """给插件用的接口""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.plugin: Optional[services.plugin.Plugin] = None @@ -46,7 +111,7 @@ def make_message_body(cmd, data, extra: Optional[dict] = None): return json.dumps(body).encode('utf-8') -class PluginWsHandler(_PluginHandlerBase, tornado.websocket.WebSocketHandler): +class PluginWsHandler(_PluginApiHandlerBase, tornado.websocket.WebSocketHandler): HEARTBEAT_INTERVAL = 10 RECEIVE_TIMEOUT = HEARTBEAT_INTERVAL + 5 @@ -161,7 +226,7 @@ class PluginWsHandler(_PluginHandlerBase, tornado.websocket.WebSocketHandler): self.close() -class RoomsHandler(api.base.ApiHandler): +class RoomsHandler(_PluginApiHandlerBase): async def get(self): rooms = [ { @@ -174,6 +239,9 @@ class RoomsHandler(api.base.ApiHandler): ROUTES = [ + (r'/api/plugin/plugins', PluginsHandler), + (r'/api/plugin/enable_plugin', EnableHandler), + (r'/api/plugin/open_admin_ui', OpenAdminUiHandler), (r'/api/plugin/websocket', PluginWsHandler), (r'/api/plugin/rooms', RoomsHandler), ] diff --git a/blcsdk/__init__.py b/blcsdk/__init__.py index e5a674e..2de51a8 100644 --- a/blcsdk/__init__.py +++ b/blcsdk/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = '0.0.1' +__version__ = '0.0.1' # TODO UID改成open ID后再改成正式版 from .handlers import * from .client import * diff --git a/config.py b/config.py index 94f0c3a..817f4ea 100644 --- a/config.py +++ b/config.py @@ -63,6 +63,7 @@ class AppConfig: self.loader_url = '' self.open_browser_at_startup = True self.enable_upload_file = True + self.enable_admin_plugins = True self.fetch_avatar_max_queue_size = 4 self.avatar_cache_size = 10000 @@ -114,6 +115,7 @@ class AppConfig: self.loader_url = app_section.get('loader_url', self.loader_url) self.open_browser_at_startup = app_section.getboolean('open_browser_at_startup', self.open_browser_at_startup) self.enable_upload_file = app_section.getboolean('enable_upload_file', self.enable_upload_file) + self.enable_admin_plugins = app_section.getboolean('enable_admin_plugins', self.enable_admin_plugins) self.fetch_avatar_max_queue_size = app_section.getint( 'fetch_avatar_max_queue_size', self.fetch_avatar_max_queue_size diff --git a/data/config.example.ini b/data/config.example.ini index 9ae384f..aa55142 100644 --- a/data/config.example.ini +++ b/data/config.example.ini @@ -28,6 +28,10 @@ open_browser_at_startup = true # Enable uploading custom emote file enable_upload_file = true +# 允许管理插件 +# Enable administration for plugins +enable_admin_plugins = true + # 获取头像最大队列长度 # Maximum queue length for fetching avatar diff --git a/services/plugin.py b/services/plugin.py index b84b662..0c4484e 100644 --- a/services/plugin.py +++ b/services/plugin.py @@ -72,6 +72,10 @@ def iter_plugins() -> Iterable['Plugin']: return _plugins.values() +def get_plugin(plugin_id): + return _plugins.get(plugin_id, None) + + def get_plugin_by_token(token): if token == '': return None @@ -156,6 +160,10 @@ class Plugin: def id(self): return self._id + @property + def config(self): + return self._config + @property def enabled(self): return self._config.enabled