feat(command): optimize command registration event handling

This commit is contained in:
InfinityPacer
2024-10-23 02:26:11 +08:00
parent 5c5182941f
commit ab32d3347d
5 changed files with 234 additions and 131 deletions
+7
View File
@@ -3,6 +3,7 @@ from typing import Annotated, Any, List, Optional
from fastapi import APIRouter, Depends, Header from fastapi import APIRouter, Depends, Header
from app import schemas from app import schemas
from app.chain.command import CommandChain
from app.core.config import settings from app.core.config import settings
from app.core.plugin import PluginManager from app.core.plugin import PluginManager
from app.core.security import verify_apikey, verify_token from app.core.security import verify_apikey, verify_token
@@ -210,6 +211,8 @@ def install(plugin_id: str,
PluginManager().reload_plugin(plugin_id) PluginManager().reload_plugin(plugin_id)
# 注册插件服务 # 注册插件服务
Scheduler().update_plugin_job(plugin_id) Scheduler().update_plugin_job(plugin_id)
# 注册菜单命令
CommandChain().init_commands(plugin_id)
# 注册插件API # 注册插件API
register_plugin_api(plugin_id) register_plugin_api(plugin_id)
return schemas.Response(success=True) return schemas.Response(success=True)
@@ -276,6 +279,8 @@ def reset_plugin(plugin_id: str,
PluginManager().reload_plugin(plugin_id) PluginManager().reload_plugin(plugin_id)
# 注册插件服务 # 注册插件服务
Scheduler().update_plugin_job(plugin_id) Scheduler().update_plugin_job(plugin_id)
# 注册菜单命令
CommandChain().init_commands(plugin_id)
# 注册插件API # 注册插件API
register_plugin_api(plugin_id) register_plugin_api(plugin_id)
return schemas.Response(success=True) return schemas.Response(success=True)
@@ -302,6 +307,8 @@ def set_plugin_config(plugin_id: str, conf: dict,
PluginManager().init_plugin(plugin_id, conf) PluginManager().init_plugin(plugin_id, conf)
# 注册插件服务 # 注册插件服务
Scheduler().update_plugin_job(plugin_id) Scheduler().update_plugin_job(plugin_id)
# 注册菜单命令
CommandChain().init_commands(plugin_id)
# 注册插件API # 注册插件API
register_plugin_api(plugin_id) register_plugin_api(plugin_id)
return schemas.Response(success=True) return schemas.Response(success=True)
+220 -130
View File
@@ -1,5 +1,6 @@
import threading
import traceback import traceback
from typing import Any, Union, Dict from typing import Any, Union, Dict, Optional
from app.chain import ChainBase from app.chain import ChainBase
from app.chain.download import DownloadChain from app.chain.download import DownloadChain
@@ -8,13 +9,13 @@ from app.chain.subscribe import SubscribeChain
from app.chain.system import SystemChain from app.chain.system import SystemChain
from app.chain.transfer import TransferChain from app.chain.transfer import TransferChain
from app.core.config import settings from app.core.config import settings
from app.core.event import Event as ManagerEvent, eventmanager from app.core.event import Event as ManagerEvent, eventmanager, Event
from app.core.plugin import PluginManager from app.core.plugin import PluginManager
from app.helper.message import MessageHelper from app.helper.message import MessageHelper
from app.log import logger from app.log import logger
from app.scheduler import Scheduler from app.scheduler import Scheduler
from app.schemas import Notification from app.schemas import Notification
from app.schemas.types import EventType, MessageChannel from app.schemas.types import EventType, MessageChannel, ChainEventType
from app.utils.object import ObjectUtils from app.utils.object import ObjectUtils
from app.utils.singleton import Singleton from app.utils.singleton import Singleton
@@ -23,133 +24,213 @@ class CommandChain(ChainBase, metaclass=Singleton):
""" """
全局命令管理,消费事件 全局命令管理,消费事件
""" """
# 内建命令 # 注册的命令集合
_registered_commands = {}
# 所有命令集合
_commands = {} _commands = {}
# 内建命令集合
_preset_commands = {
"/cookiecloud": {
"id": "cookiecloud",
"type": "scheduler",
"description": "同步站点",
"category": "站点"
},
"/sites": {
"func": SiteChain().remote_list,
"description": "查询站点",
"category": "站点",
"data": {}
},
"/site_cookie": {
"func": SiteChain().remote_cookie,
"description": "更新站点Cookie",
"data": {}
},
"/site_enable": {
"func": SiteChain().remote_enable,
"description": "启用站点",
"data": {}
},
"/site_disable": {
"func": SiteChain().remote_disable,
"description": "禁用站点",
"data": {}
},
"/mediaserver_sync": {
"id": "mediaserver_sync",
"type": "scheduler",
"description": "同步媒体服务器",
"category": "管理"
},
"/subscribes": {
"func": SubscribeChain().remote_list,
"description": "查询订阅",
"category": "订阅",
"data": {}
},
"/subscribe_refresh": {
"id": "subscribe_refresh",
"type": "scheduler",
"description": "刷新订阅",
"category": "订阅"
},
"/subscribe_search": {
"id": "subscribe_search",
"type": "scheduler",
"description": "搜索订阅",
"category": "订阅"
},
"/subscribe_delete": {
"func": SubscribeChain().remote_delete,
"description": "删除订阅",
"data": {}
},
"/subscribe_tmdb": {
"id": "subscribe_tmdb",
"type": "scheduler",
"description": "订阅元数据更新"
},
"/downloading": {
"func": DownloadChain().remote_downloading,
"description": "正在下载",
"category": "管理",
"data": {}
},
"/transfer": {
"id": "transfer",
"type": "scheduler",
"description": "下载文件整理",
"category": "管理"
},
"/redo": {
"func": TransferChain().remote_transfer,
"description": "手动整理",
"data": {}
},
"/clear_cache": {
"func": SystemChain().remote_clear_cache,
"description": "清理缓存",
"category": "管理",
"data": {}
},
"/restart": {
"func": SystemChain().restart,
"description": "重启系统",
"category": "管理",
"data": {}
},
"/version": {
"func": SystemChain().version,
"description": "当前版本",
"category": "管理",
"data": {}
}
}
# 插件命令集合
_plugin_commands = {}
# 其他命令集合
_other_commands = {}
def __init__(self): def __init__(self):
# 插件管理器 # 插件管理器
super().__init__() super().__init__()
# 初始化锁
self._rlock = threading.RLock()
# 插件管理
self.pluginmanager = PluginManager() self.pluginmanager = PluginManager()
# 定时服务管理 # 定时服务管理
self.scheduler = Scheduler() self.scheduler = Scheduler()
# 消息管理器 # 消息管理器
self.messagehelper = MessageHelper() self.messagehelper = MessageHelper()
# 内置命令:标准参数 arg_str: str, channel: MessageChannel, userid: Union[str, int] = None, source: str = None # 初始化命令
# 其中 arg_str 为用户输入的参数,channel 为消息渠道,userid 为用户IDsource 为消息来源,arg_str 可选 self.init_commands()
self._commands = {
"/cookiecloud": { def init_commands(self, pid: Optional[str] = None) -> None:
"id": "cookiecloud", """
"type": "scheduler", 初始化菜单命令
"description": "同步站点", """
"category": "站点" if settings.DEV:
}, logger.debug("Development mode active. Skipping command initialization.")
"/sites": { return
"func": SiteChain().remote_list,
"description": "查询站点", with self._rlock:
"category": "站点", logger.debug("Acquired lock for initializing commands.")
"data": {} self._plugin_commands = self.__build_plugin_commands()
}, self._commands = {
"/site_cookie": { **self._preset_commands,
"func": SiteChain().remote_cookie, **self._plugin_commands,
"description": "更新站点Cookie", **self._other_commands
"data": {}
},
"/site_enable": {
"func": SiteChain().remote_enable,
"description": "启用站点",
"data": {}
},
"/site_disable": {
"func": SiteChain().remote_disable,
"description": "禁用站点",
"data": {}
},
"/mediaserver_sync": {
"id": "mediaserver_sync",
"type": "scheduler",
"description": "同步媒体服务器",
"category": "管理"
},
"/subscribes": {
"func": SubscribeChain().remote_list,
"description": "查询订阅",
"category": "订阅",
"data": {}
},
"/subscribe_refresh": {
"id": "subscribe_refresh",
"type": "scheduler",
"description": "刷新订阅",
"category": "订阅"
},
"/subscribe_search": {
"id": "subscribe_search",
"type": "scheduler",
"description": "搜索订阅",
"category": "订阅"
},
"/subscribe_delete": {
"func": SubscribeChain().remote_delete,
"description": "删除订阅",
"data": {}
},
"/subscribe_tmdb": {
"id": "subscribe_tmdb",
"type": "scheduler",
"description": "订阅元数据更新"
},
"/downloading": {
"func": DownloadChain().remote_downloading,
"description": "正在下载",
"category": "管理",
"data": {}
},
"/transfer": {
"id": "transfer",
"type": "scheduler",
"description": "下载文件整理",
"category": "管理"
},
"/redo": {
"func": TransferChain().remote_transfer,
"description": "手动整理",
"data": {}
},
"/clear_cache": {
"func": SystemChain().remote_clear_cache,
"description": "清理缓存",
"category": "管理",
"data": {}
},
"/restart": {
"func": SystemChain().restart,
"description": "重启系统",
"category": "管理",
"data": {}
},
"/version": {
"func": SystemChain().version,
"description": "当前版本",
"category": "管理",
"data": {}
} }
}
# 汇总插件命令 # 触发事件允许可以拦截和调整命令
plugin_commands = self.pluginmanager.get_plugin_commands() event, initial_commands = self.__trigger_register_commands_event()
for command in plugin_commands:
self.register( # 如果事件返回有效的 event_data,使用事件中调整后的命令
cmd=command.get('cmd'), if event and event.event_data:
func=self.send_plugin_event, initial_commands = event.event_data.get("commands") or {}
desc=command.get('desc'), logger.debug(f"Registering command count from event: {len(initial_commands)}")
category=command.get('category'), else:
data={ logger.debug(f"Registering initial command count: {len(initial_commands)}")
'etype': command.get('event'),
'data': command.get('data') # 对比调整后的命令与当前命令
if initial_commands == self._registered_commands:
logger.debug("Command set unchanged, skipping broadcast registration.")
else:
logger.debug("Command set has changed, Updating and broadcasting new commands.")
self._registered_commands = initial_commands
super().register_commands(commands=initial_commands)
def __trigger_register_commands_event(self) -> (Optional[Event], dict):
"""
触发事件,允许调整命令数据
"""
def add_commands(source, command_type):
"""
添加命令集合
"""
for cmd, command in source.items():
command_data = {
"type": command_type,
"description": command.get("description"),
"category": command.get("category")
} }
) # 如果有 pid,则添加到命令数据中
# 广播注册命令菜单 plugin_id = command.get("pid")
if not settings.DEV: if plugin_id:
self.register_commands(commands=self.get_commands()) command_data["pid"] = plugin_id
commands[cmd] = command_data
# 触发事件允许可以拦截和调整命令
commands = {}
add_commands(self._preset_commands, "preset")
add_commands(self._plugin_commands, "plugin")
add_commands(self._other_commands, "other")
event_data = {
"commands": commands
}
return eventmanager.send_event(ChainEventType.CommandRegister, event_data), commands
def __build_plugin_commands(self) -> Dict[str, dict]:
"""
构建插件命令
"""
plugin_commands = {}
for command in self.pluginmanager.get_plugin_commands():
cmd = command.get("cmd")
if cmd:
plugin_commands[cmd] = {
"pid": command.get("pid"),
"func": self.send_plugin_event,
"description": command.get("desc"),
"category": command.get("category"),
"data": {
"etype": command.get("event"),
"data": command.get("data")
}
}
return plugin_commands
def __run_command(self, command: Dict[str, any], data_str: str = "", def __run_command(self, command: Dict[str, any], data_str: str = "",
channel: MessageChannel = None, source: str = None, userid: Union[str, int] = None): channel: MessageChannel = None, source: str = None, userid: Union[str, int] = None):
@@ -211,24 +292,25 @@ class CommandChain(ChainBase, metaclass=Singleton):
""" """
return self._commands return self._commands
def register(self, cmd: str, func: Any, data: dict = None,
desc: str = None, category: str = None) -> None:
"""
注册命令
"""
self._commands[cmd] = {
"func": func,
"description": desc,
"category": category,
"data": data or {}
}
def get(self, cmd: str) -> Any: def get(self, cmd: str) -> Any:
""" """
获取命令 获取命令
""" """
return self._commands.get(cmd, {}) return self._commands.get(cmd, {})
def register(self, cmd: str, func: Any, data: dict = None,
desc: str = None, category: str = None) -> None:
"""
注册单个命令
"""
# 单独调用的,统一注册到其他
self._other_commands[cmd] = {
"func": func,
"description": desc,
"category": category,
"data": data or {}
}
def execute(self, cmd: str, data_str: str = "", def execute(self, cmd: str, data_str: str = "",
channel: MessageChannel = None, source: str = None, channel: MessageChannel = None, source: str = None,
userid: Union[str, int] = None) -> None: userid: Union[str, int] = None) -> None:
@@ -286,3 +368,11 @@ class CommandChain(ChainBase, metaclass=Singleton):
if self.get(cmd): if self.get(cmd):
self.execute(cmd=cmd, data_str=args, self.execute(cmd=cmd, data_str=args,
channel=event_channel, source=event_source, userid=event_user) channel=event_channel, source=event_source, userid=event_user)
@eventmanager.register(EventType.ModuleReload)
def module_reload_event(self, event: ManagerEvent) -> None:
"""
注册模块重载事件
"""
# 发生模块重载时,重新注册命令
self.init_commands()
+1 -1
View File
@@ -129,7 +129,7 @@ class EventManager(metaclass=Singleton):
for handler in handlers.values() for handler in handlers.values()
) )
def send_event(self, etype: Union[EventType, ChainEventType], data: Optional[Dict] = None, def send_event(self, etype: Union[EventType, ChainEventType], data: Optional[Union[Dict, ChainEventData]] = None,
priority: int = DEFAULT_EVENT_PRIORITY) -> Optional[Event]: priority: int = DEFAULT_EVENT_PRIORITY) -> Optional[Event]:
""" """
发送事件,根据事件类型决定是广播事件还是链式事件 发送事件,根据事件类型决定是广播事件还是链式事件
+2
View File
@@ -66,6 +66,8 @@ class ChainEventType(Enum):
AuthVerification = "auth.verification" AuthVerification = "auth.verification"
# 认证拦截请求 # 认证拦截请求
AuthIntercept = "auth.intercept" AuthIntercept = "auth.intercept"
# 命令注册请求
CommandRegister = "command.register"
# 系统配置Key字典 # 系统配置Key字典
+4
View File
@@ -1,5 +1,6 @@
import asyncio import asyncio
from app.chain.command import CommandChain
from app.core.plugin import PluginManager from app.core.plugin import PluginManager
from app.log import logger from app.log import logger
from app.scheduler import Scheduler from app.scheduler import Scheduler
@@ -13,6 +14,7 @@ async def init_plugins_async():
loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
plugin_manager = PluginManager() plugin_manager = PluginManager()
scheduler = Scheduler() scheduler = Scheduler()
command = CommandChain()
sync_plugins = await loop.run_in_executor(None, plugin_manager.sync) sync_plugins = await loop.run_in_executor(None, plugin_manager.sync)
if not sync_plugins: if not sync_plugins:
return return
@@ -22,6 +24,8 @@ async def init_plugins_async():
plugin_manager.init_config() plugin_manager.init_config()
# 插件启动后注册后台任务 # 插件启动后注册后台任务
scheduler.init_plugin_jobs() scheduler.init_plugin_jobs()
# 插件启动后注册菜单命令
command.init_commands()
# 插件启动后注册插件API # 插件启动后注册插件API
register_plugin_api() register_plugin_api()
logger.info("所有插件初始化完成") logger.info("所有插件初始化完成")