fix commands
This commit is contained in:
parent
50d71621d7
commit
01428e9f8f
@ -2,7 +2,7 @@ import traceback
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Optional, Any
|
from typing import Optional, Any
|
||||||
|
|
||||||
from app.core import Context, ModuleManager, EventManager
|
from app.core import Context, ModuleManager
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import AbstractSingleton, Singleton
|
from app.utils.singleton import AbstractSingleton, Singleton
|
||||||
|
|
||||||
@ -17,7 +17,6 @@ class ChainBase(AbstractSingleton, metaclass=Singleton):
|
|||||||
公共初始化
|
公共初始化
|
||||||
"""
|
"""
|
||||||
self.modulemanager = ModuleManager()
|
self.modulemanager = ModuleManager()
|
||||||
self.eventmanager = EventManager()
|
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def process(self, *args, **kwargs) -> Optional[Context]:
|
def process(self, *args, **kwargs) -> Optional[Context]:
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
from typing import Dict, Any
|
from typing import Any
|
||||||
|
|
||||||
from fastapi import Request
|
|
||||||
|
|
||||||
from app.chain import ChainBase
|
|
||||||
from app.chain.common import *
|
from app.chain.common import *
|
||||||
from app.chain.search import SearchChain
|
from app.chain.search import SearchChain
|
||||||
from app.core import MediaInfo, TorrentInfo, MetaInfo
|
from app.core import MediaInfo, TorrentInfo, MetaInfo, EventManager
|
||||||
from app.db.subscribes import Subscribes
|
from app.db.subscribes import Subscribes
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.types import EventType
|
from app.utils.types import EventType
|
||||||
@ -32,6 +29,7 @@ class UserMessageChain(ChainBase):
|
|||||||
self.subscribes = Subscribes()
|
self.subscribes = Subscribes()
|
||||||
self.searchchain = SearchChain()
|
self.searchchain = SearchChain()
|
||||||
self.torrent = TorrentHelper()
|
self.torrent = TorrentHelper()
|
||||||
|
self.eventmanager = EventManager()
|
||||||
|
|
||||||
def process(self, body: Any, form: Any, args: Any) -> None:
|
def process(self, body: Any, form: Any, args: Any) -> None:
|
||||||
"""
|
"""
|
||||||
@ -60,6 +58,8 @@ class UserMessageChain(ChainBase):
|
|||||||
"cmd": text
|
"cmd": text
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
self.common.post_message(title=f"正在运行,请稍候 ...", userid=userid)
|
||||||
|
|
||||||
elif text.isdigit():
|
elif text.isdigit():
|
||||||
# 缓存
|
# 缓存
|
||||||
cache_data: dict = self._user_cache.get(userid)
|
cache_data: dict = self._user_cache.get(userid)
|
||||||
@ -285,7 +285,7 @@ class UserMessageChain(ChainBase):
|
|||||||
发送媒体列表消息
|
发送媒体列表消息
|
||||||
"""
|
"""
|
||||||
self.run_module('post_medias_message',
|
self.run_module('post_medias_message',
|
||||||
title=f"共找到{total}条相关信息,请回复数字选择对应媒体(p:上一页 n:下一页)",
|
title=f"共找到{total}条相关信息,请回复数字选择对应媒体(p: 上一页 n: 下一页)",
|
||||||
items=items,
|
items=items,
|
||||||
userid=userid)
|
userid=userid)
|
||||||
|
|
||||||
@ -294,6 +294,6 @@ class UserMessageChain(ChainBase):
|
|||||||
发送种子列表消息
|
发送种子列表消息
|
||||||
"""
|
"""
|
||||||
self.run_module('post_torrents_message',
|
self.run_module('post_torrents_message',
|
||||||
title=f"共找到{total}条相关信息,请回复数字下载对应资源(0:自动选择 p:上一页 n:下一页)",
|
title=f"共找到{total}条相关信息,请回复数字下载对应资源(0: 自动选择 p: 上一页 n: 下一页)",
|
||||||
items=items,
|
items=items,
|
||||||
userid=userid)
|
userid=userid)
|
||||||
|
@ -1,24 +1,36 @@
|
|||||||
|
import traceback
|
||||||
|
from threading import Thread, Event
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from app.chain import ChainBase
|
||||||
from app.chain.cookiecloud import CookieCloudChain
|
from app.chain.cookiecloud import CookieCloudChain
|
||||||
from app.chain.douban_sync import DoubanSyncChain
|
from app.chain.douban_sync import DoubanSyncChain
|
||||||
from app.chain.subscribe import SubscribeChain
|
from app.chain.subscribe import SubscribeChain
|
||||||
from app.core import eventmanager, PluginManager, EventManager
|
from app.core import eventmanager, PluginManager, EventManager
|
||||||
from app.core.event_manager import Event
|
from app.core.event_manager import Event as ManagerEvent
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
from app.utils.types import EventType
|
from app.utils.types import EventType
|
||||||
|
|
||||||
|
|
||||||
|
class CommandChian(ChainBase):
|
||||||
|
"""
|
||||||
|
插件处理链
|
||||||
|
"""
|
||||||
|
|
||||||
|
def process(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Command(metaclass=Singleton):
|
class Command(metaclass=Singleton):
|
||||||
"""
|
"""
|
||||||
全局命令管理
|
全局命令管理,消费事件
|
||||||
"""
|
"""
|
||||||
# 内建命令
|
# 内建命令
|
||||||
_commands = {
|
_commands = {
|
||||||
"/cookiecloud": {
|
"/cookiecloud": {
|
||||||
"func": CookieCloudChain().process,
|
"func": CookieCloudChain().process,
|
||||||
"description": "同步CookieCloud的Cookie",
|
"description": "同步站点Cookie",
|
||||||
"data": {}
|
"data": {}
|
||||||
},
|
},
|
||||||
"/doubansync": {
|
"/doubansync": {
|
||||||
@ -35,9 +47,16 @@ class Command(metaclass=Singleton):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 退出事件
|
||||||
|
_event = Event()
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# 注册插件命令
|
# 事件管理器
|
||||||
plugin_commands = PluginManager().get_plugin_commands()
|
self.eventmanager = EventManager()
|
||||||
|
# 插件管理器
|
||||||
|
self.pluginmanager = PluginManager()
|
||||||
|
# 汇总插件命令
|
||||||
|
plugin_commands = self.pluginmanager.get_plugin_commands()
|
||||||
for command in plugin_commands:
|
for command in plugin_commands:
|
||||||
self.register(
|
self.register(
|
||||||
cmd=command.get('cmd'),
|
cmd=command.get('cmd'),
|
||||||
@ -48,6 +67,45 @@ class Command(metaclass=Singleton):
|
|||||||
'data': command.get('data')
|
'data': command.get('data')
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
# 处理链
|
||||||
|
self.chain = CommandChian()
|
||||||
|
# 广播注册命令
|
||||||
|
self.chain.run_module("register_commands", commands=self.get_commands())
|
||||||
|
# 消息处理线程
|
||||||
|
self._thread = Thread(target=self.__run)
|
||||||
|
# 启动事件处理线程
|
||||||
|
self._thread.start()
|
||||||
|
|
||||||
|
def __run(self):
|
||||||
|
"""
|
||||||
|
事件处理线程
|
||||||
|
"""
|
||||||
|
while not self._event.is_set():
|
||||||
|
event, handlers = self.eventmanager.get_event()
|
||||||
|
if event:
|
||||||
|
logger.info(f"处理事件:{event.event_type} - {handlers}")
|
||||||
|
for handler in handlers:
|
||||||
|
try:
|
||||||
|
names = handler.__qualname__.split(".")
|
||||||
|
if names[0] == "Command":
|
||||||
|
self.command_event(event)
|
||||||
|
else:
|
||||||
|
self.pluginmanager.run_plugin_method(names[0], names[1], event)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"事件处理出错:{str(e)} - {traceback.format_exc()}")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
停止事件处理线程
|
||||||
|
"""
|
||||||
|
self._event.set()
|
||||||
|
self._thread.join()
|
||||||
|
|
||||||
|
def get_commands(self):
|
||||||
|
"""
|
||||||
|
获取命令列表
|
||||||
|
"""
|
||||||
|
return self._commands
|
||||||
|
|
||||||
def register(self, cmd: str, func: Any, data: dict = None, desc: str = None) -> None:
|
def register(self, cmd: str, func: Any, data: dict = None, desc: str = None) -> None:
|
||||||
"""
|
"""
|
||||||
@ -83,7 +141,7 @@ class Command(metaclass=Singleton):
|
|||||||
EventManager().send_event(etype, data)
|
EventManager().send_event(etype, data)
|
||||||
|
|
||||||
@eventmanager.register(EventType.CommandExcute)
|
@eventmanager.register(EventType.CommandExcute)
|
||||||
def command_event(self, event: Event) -> None:
|
def command_event(self, event: ManagerEvent) -> None:
|
||||||
"""
|
"""
|
||||||
注册命令执行事件
|
注册命令执行事件
|
||||||
event_data: {
|
event_data: {
|
||||||
|
@ -39,6 +39,14 @@ class ModuleManager(metaclass=Singleton):
|
|||||||
self._running_modules[module_id].init_module()
|
self._running_modules[module_id].init_module()
|
||||||
logger.info(f"Moudle Loaded:{module_id}")
|
logger.info(f"Moudle Loaded:{module_id}")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
停止所有模块
|
||||||
|
"""
|
||||||
|
for _, module in self._running_modules.items():
|
||||||
|
if hasattr(module, "stop"):
|
||||||
|
module.stop()
|
||||||
|
|
||||||
def get_modules(self, method: str) -> Generator:
|
def get_modules(self, method: str) -> Generator:
|
||||||
"""
|
"""
|
||||||
获取模块列表
|
获取模块列表
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import traceback
|
import traceback
|
||||||
from threading import Thread
|
from typing import List, Any
|
||||||
from typing import Tuple, Optional, List, Any
|
|
||||||
|
|
||||||
from app.core import EventManager
|
|
||||||
from app.db.systemconfigs import SystemConfigs
|
from app.db.systemconfigs import SystemConfigs
|
||||||
from app.helper import ModuleHelper
|
from app.helper import ModuleHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
@ -14,7 +12,6 @@ class PluginManager(metaclass=Singleton):
|
|||||||
插件管理器
|
插件管理器
|
||||||
"""
|
"""
|
||||||
systemconfigs: SystemConfigs = None
|
systemconfigs: SystemConfigs = None
|
||||||
eventmanager: EventManager = None
|
|
||||||
|
|
||||||
# 插件列表
|
# 插件列表
|
||||||
_plugins: dict = {}
|
_plugins: dict = {}
|
||||||
@ -22,37 +19,17 @@ class PluginManager(metaclass=Singleton):
|
|||||||
_running_plugins: dict = {}
|
_running_plugins: dict = {}
|
||||||
# 配置Key
|
# 配置Key
|
||||||
_config_key: str = "plugin.%s"
|
_config_key: str = "plugin.%s"
|
||||||
# 事件处理线程
|
|
||||||
_thread: Thread = None
|
|
||||||
# 开关
|
|
||||||
_active: bool = False
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.init_config()
|
self.init_config()
|
||||||
|
|
||||||
def init_config(self):
|
def init_config(self):
|
||||||
self.systemconfigs = SystemConfigs()
|
self.systemconfigs = SystemConfigs()
|
||||||
self.eventmanager = EventManager()
|
|
||||||
# 停止已有插件
|
# 停止已有插件
|
||||||
self.stop()
|
self.stop()
|
||||||
# 启动插件
|
# 启动插件
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def __run(self):
|
|
||||||
"""
|
|
||||||
事件处理线程
|
|
||||||
"""
|
|
||||||
while self._active:
|
|
||||||
event, handlers = self.eventmanager.get_event()
|
|
||||||
if event:
|
|
||||||
logger.info(f"处理事件:{event.event_type} - {handlers}")
|
|
||||||
for handler in handlers:
|
|
||||||
try:
|
|
||||||
names = handler.__qualname__.split(".")
|
|
||||||
self.run_plugin_method(names[0], names[1], event)
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"事件处理出错:{str(e)} - {traceback.format_exc()}")
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""
|
"""
|
||||||
启动
|
启动
|
||||||
@ -60,21 +37,10 @@ class PluginManager(metaclass=Singleton):
|
|||||||
# 加载插件
|
# 加载插件
|
||||||
self.__load_plugins()
|
self.__load_plugins()
|
||||||
|
|
||||||
# 将事件管理器设为启动
|
|
||||||
self._active = True
|
|
||||||
self._thread = Thread(target=self.__run)
|
|
||||||
# 启动事件处理线程
|
|
||||||
self._thread.start()
|
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
停止
|
停止
|
||||||
"""
|
"""
|
||||||
# 将事件管理器设为停止
|
|
||||||
self._active = False
|
|
||||||
# 等待事件处理线程退出
|
|
||||||
if self._thread:
|
|
||||||
self._thread.join()
|
|
||||||
# 停止所有插件
|
# 停止所有插件
|
||||||
self.__stop_plugins()
|
self.__stop_plugins()
|
||||||
|
|
||||||
@ -131,37 +97,6 @@ class PluginManager(metaclass=Singleton):
|
|||||||
return {}
|
return {}
|
||||||
return self.systemconfigs.get(self._config_key % pid) or {}
|
return self.systemconfigs.get(self._config_key % pid) or {}
|
||||||
|
|
||||||
def get_plugin_page(self, pid: str) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
|
||||||
"""
|
|
||||||
获取插件额外页面数据
|
|
||||||
:return: 标题,页面内容,确定按钮响应函数
|
|
||||||
"""
|
|
||||||
if not self._running_plugins.get(pid):
|
|
||||||
return None, None, None
|
|
||||||
if not hasattr(self._running_plugins[pid], "get_page"):
|
|
||||||
return None, None, None
|
|
||||||
return self._running_plugins[pid].get_page()
|
|
||||||
|
|
||||||
def get_plugin_script(self, pid: str) -> Optional[str]:
|
|
||||||
"""
|
|
||||||
获取插件额外脚本
|
|
||||||
"""
|
|
||||||
if not self._running_plugins.get(pid):
|
|
||||||
return None
|
|
||||||
if not hasattr(self._running_plugins[pid], "get_script"):
|
|
||||||
return None
|
|
||||||
return self._running_plugins[pid].get_script()
|
|
||||||
|
|
||||||
def get_plugin_state(self, pid: str) -> Optional[bool]:
|
|
||||||
"""
|
|
||||||
获取插件状态
|
|
||||||
"""
|
|
||||||
if not self._running_plugins.get(pid):
|
|
||||||
return None
|
|
||||||
if not hasattr(self._running_plugins[pid], "get_state"):
|
|
||||||
return None
|
|
||||||
return self._running_plugins[pid].get_state()
|
|
||||||
|
|
||||||
def save_plugin_config(self, pid: str, conf: dict) -> bool:
|
def save_plugin_config(self, pid: str, conf: dict) -> bool:
|
||||||
"""
|
"""
|
||||||
保存插件配置
|
保存插件配置
|
||||||
@ -170,98 +105,6 @@ class PluginManager(metaclass=Singleton):
|
|||||||
return False
|
return False
|
||||||
return self.systemconfigs.set(self._config_key % pid, conf)
|
return self.systemconfigs.set(self._config_key % pid, conf)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def __get_plugin_color(plugin: str) -> str:
|
|
||||||
"""
|
|
||||||
获取插件的主题色
|
|
||||||
"""
|
|
||||||
if hasattr(plugin, "plugin_color") and plugin.plugin_color:
|
|
||||||
return plugin.plugin_color
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_plugins_conf(self, auth_level: int) -> dict:
|
|
||||||
"""
|
|
||||||
获取所有插件配置
|
|
||||||
"""
|
|
||||||
all_confs = {}
|
|
||||||
for pid, plugin in self._running_plugins.items():
|
|
||||||
# 基本属性
|
|
||||||
conf = {}
|
|
||||||
# 权限
|
|
||||||
if hasattr(plugin, "auth_level") \
|
|
||||||
and plugin.auth_level > auth_level:
|
|
||||||
continue
|
|
||||||
# 名称
|
|
||||||
if hasattr(plugin, "plugin_name"):
|
|
||||||
conf.update({"name": plugin.plugin_name})
|
|
||||||
# 描述
|
|
||||||
if hasattr(plugin, "plugin_desc"):
|
|
||||||
conf.update({"desc": plugin.plugin_desc})
|
|
||||||
# 版本号
|
|
||||||
if hasattr(plugin, "plugin_version"):
|
|
||||||
conf.update({"version": plugin.plugin_version})
|
|
||||||
# 图标
|
|
||||||
if hasattr(plugin, "plugin_icon"):
|
|
||||||
conf.update({"icon": plugin.plugin_icon})
|
|
||||||
# ID前缀
|
|
||||||
if hasattr(plugin, "plugin_config_prefix"):
|
|
||||||
conf.update({"prefix": plugin.plugin_config_prefix})
|
|
||||||
# 插件额外的页面
|
|
||||||
if hasattr(plugin, "get_page"):
|
|
||||||
title, _, _ = plugin.get_page()
|
|
||||||
conf.update({"page": title})
|
|
||||||
# 插件额外的脚本
|
|
||||||
if hasattr(plugin, "get_script"):
|
|
||||||
conf.update({"script": plugin.get_script()})
|
|
||||||
# 主题色
|
|
||||||
conf.update({"color": self.__get_plugin_color(plugin)})
|
|
||||||
# 配置项
|
|
||||||
conf.update({"fields": plugin.get_fields() or {}})
|
|
||||||
# 配置值
|
|
||||||
conf.update({"config": self.get_plugin_config(pid)})
|
|
||||||
# 状态
|
|
||||||
conf.update({"state": plugin.get_state()})
|
|
||||||
# 汇总
|
|
||||||
all_confs[pid] = conf
|
|
||||||
return all_confs
|
|
||||||
|
|
||||||
def get_plugin_apps(self, auth_level: int) -> dict:
|
|
||||||
"""
|
|
||||||
获取所有插件
|
|
||||||
"""
|
|
||||||
all_confs = {}
|
|
||||||
for pid, plugin in self._plugins.items():
|
|
||||||
# 基本属性
|
|
||||||
conf = {}
|
|
||||||
# 权限
|
|
||||||
if hasattr(plugin, "auth_level") \
|
|
||||||
and plugin.auth_level > auth_level:
|
|
||||||
continue
|
|
||||||
# ID
|
|
||||||
conf.update({"id": pid})
|
|
||||||
# 名称
|
|
||||||
if hasattr(plugin, "plugin_name"):
|
|
||||||
conf.update({"name": plugin.plugin_name})
|
|
||||||
# 描述
|
|
||||||
if hasattr(plugin, "plugin_desc"):
|
|
||||||
conf.update({"desc": plugin.plugin_desc})
|
|
||||||
# 版本
|
|
||||||
if hasattr(plugin, "plugin_version"):
|
|
||||||
conf.update({"version": plugin.plugin_version})
|
|
||||||
# 图标
|
|
||||||
if hasattr(plugin, "plugin_icon"):
|
|
||||||
conf.update({"icon": plugin.plugin_icon})
|
|
||||||
# 主题色
|
|
||||||
conf.update({"color": self.__get_plugin_color(plugin)})
|
|
||||||
if hasattr(plugin, "plugin_author"):
|
|
||||||
conf.update({"author": plugin.plugin_author})
|
|
||||||
# 作者链接
|
|
||||||
if hasattr(plugin, "author_url"):
|
|
||||||
conf.update({"author_url": plugin.author_url})
|
|
||||||
# 汇总
|
|
||||||
all_confs[pid] = conf
|
|
||||||
return all_confs
|
|
||||||
|
|
||||||
def get_plugin_commands(self) -> List[dict]:
|
def get_plugin_commands(self) -> List[dict]:
|
||||||
"""
|
"""
|
||||||
获取插件命令
|
获取插件命令
|
||||||
|
@ -3,6 +3,7 @@ from fastapi import FastAPI
|
|||||||
from uvicorn import Config
|
from uvicorn import Config
|
||||||
|
|
||||||
from app.api.apiv1 import api_router
|
from app.api.apiv1 import api_router
|
||||||
|
from app.command import Command
|
||||||
from app.core import settings, ModuleManager, PluginManager
|
from app.core import settings, ModuleManager, PluginManager
|
||||||
from app.db.init import init_db, update_db
|
from app.db.init import init_db, update_db
|
||||||
from app.helper.sites import SitesHelper
|
from app.helper.sites import SitesHelper
|
||||||
@ -28,6 +29,10 @@ def shutdown_server():
|
|||||||
Scheduler().stop()
|
Scheduler().stop()
|
||||||
# 停止插件
|
# 停止插件
|
||||||
PluginManager().stop()
|
PluginManager().stop()
|
||||||
|
# 停止模块
|
||||||
|
ModuleManager().stop()
|
||||||
|
# 停止事件消费
|
||||||
|
Command().stop()
|
||||||
|
|
||||||
|
|
||||||
@App.on_event("startup")
|
@App.on_event("startup")
|
||||||
@ -43,6 +48,8 @@ def start_module():
|
|||||||
SitesHelper()
|
SitesHelper()
|
||||||
# 启动定时服务
|
# 启动定时服务
|
||||||
Scheduler()
|
Scheduler()
|
||||||
|
# 启动事件消费
|
||||||
|
Command()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -225,3 +225,17 @@ class _ModuleBase(metaclass=ABCMeta):
|
|||||||
:return: 成功或失败
|
:return: 成功或失败
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def register_commands(self, commands: dict):
|
||||||
|
"""
|
||||||
|
注册命令,实现这个函数接收系统可用的命令菜单
|
||||||
|
:param commands: 命令字典
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
如果关闭时模块有服务需要停止,需要实现此方法
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
@ -23,6 +23,9 @@ class Douban(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@ class EmbyModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.emby = Emby()
|
self.emby = Emby()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MEDIASERVER", "emby"
|
return "MEDIASERVER", "emby"
|
||||||
|
|
||||||
|
@ -21,6 +21,9 @@ class FanartModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "FANART_API_KEY", True
|
return "FANART_API_KEY", True
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@ class FileTransferModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ from app.modules.filter.RuleParser import RuleParser
|
|||||||
|
|
||||||
|
|
||||||
class FilterModule(_ModuleBase):
|
class FilterModule(_ModuleBase):
|
||||||
|
|
||||||
# 规则解析器
|
# 规则解析器
|
||||||
parser: RuleParser = None
|
parser: RuleParser = None
|
||||||
|
|
||||||
@ -62,6 +63,9 @@ class FilterModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.parser = RuleParser()
|
self.parser = RuleParser()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "FILTER_RULE", True
|
return "FILTER_RULE", True
|
||||||
|
|
||||||
|
@ -23,6 +23,9 @@ class IndexerModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "INDEXER", "builtin"
|
return "INDEXER", "builtin"
|
||||||
|
|
||||||
|
@ -13,6 +13,9 @@ class JellyfinModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.jellyfin = Jellyfin()
|
self.jellyfin = Jellyfin()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MEDIASERVER", "jellyfin"
|
return "MEDIASERVER", "jellyfin"
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@ class PlexModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.plex = Plex()
|
self.plex = Plex()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MEDIASERVER", "plex"
|
return "MEDIASERVER", "plex"
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@ class QbittorrentModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.qbittorrent = Qbittorrent()
|
self.qbittorrent = Qbittorrent()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "DOWNLOADER", "qbittorrent"
|
return "DOWNLOADER", "qbittorrent"
|
||||||
|
|
||||||
|
@ -8,12 +8,14 @@ from app.modules.telegram.telegram import Telegram
|
|||||||
|
|
||||||
|
|
||||||
class TelegramModule(_ModuleBase):
|
class TelegramModule(_ModuleBase):
|
||||||
|
|
||||||
telegram: Telegram = None
|
telegram: Telegram = None
|
||||||
|
|
||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.telegram = Telegram()
|
self.telegram = Telegram()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.telegram.stop()
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MESSAGER", "telegram"
|
return "MESSAGER", "telegram"
|
||||||
|
|
||||||
@ -51,13 +53,16 @@ class TelegramModule(_ModuleBase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
# 校验token
|
||||||
|
token = args.get("token")
|
||||||
|
if not token or token != settings.API_TOKEN:
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
msg_json: dict = json.loads(body)
|
message: dict = json.loads(body)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
logger.error(f"解析Telegram消息失败:{err}")
|
logger.error(f"解析Telegram消息失败:{err}")
|
||||||
return None
|
return None
|
||||||
if msg_json:
|
if message:
|
||||||
message = msg_json.get("message", {})
|
|
||||||
text = message.get("text")
|
text = message.get("text")
|
||||||
user_id = message.get("from", {}).get("id")
|
user_id = message.get("from", {}).get("id")
|
||||||
# 获取用户名
|
# 获取用户名
|
||||||
@ -117,3 +122,10 @@ class TelegramModule(_ModuleBase):
|
|||||||
:return: 成功或失败
|
:return: 成功或失败
|
||||||
"""
|
"""
|
||||||
return self.telegram.send_torrents_msg(title=title, torrents=items, userid=userid)
|
return self.telegram.send_torrents_msg(title=title, torrents=items, userid=userid)
|
||||||
|
|
||||||
|
def register_commands(self, commands: dict):
|
||||||
|
"""
|
||||||
|
注册命令,实现这个函数接收系统可用的命令菜单
|
||||||
|
:param commands: 命令字典
|
||||||
|
"""
|
||||||
|
self.telegram.register_commands(commands)
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
from threading import Event, Thread
|
import threading
|
||||||
|
from threading import Event
|
||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from urllib.parse import urlencode
|
|
||||||
|
|
||||||
from app.core import settings, MediaInfo, TorrentInfo, Context
|
import telebot
|
||||||
|
|
||||||
|
from app.core import settings, MediaInfo, Context
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
from app.utils.http import RequestUtils
|
from app.utils.http import RequestUtils
|
||||||
from app.utils.singleton import Singleton
|
from app.utils.singleton import Singleton
|
||||||
import telebot
|
|
||||||
|
|
||||||
from app.utils.string import StringUtils
|
from app.utils.string import StringUtils
|
||||||
|
|
||||||
|
|
||||||
@ -26,16 +26,25 @@ class Telegram(metaclass=Singleton):
|
|||||||
self._telegram_chat_id = settings.TELEGRAM_CHAT_ID
|
self._telegram_chat_id = settings.TELEGRAM_CHAT_ID
|
||||||
# 初始化机器人
|
# 初始化机器人
|
||||||
if self._telegram_token and self._telegram_chat_id:
|
if self._telegram_token and self._telegram_chat_id:
|
||||||
|
# bot
|
||||||
_bot = telebot.TeleBot(self._telegram_token, parse_mode="markdown")
|
_bot = telebot.TeleBot(self._telegram_token, parse_mode="markdown")
|
||||||
|
# 记录句柄
|
||||||
self._bot = _bot
|
self._bot = _bot
|
||||||
|
|
||||||
@_bot.message_handler(func=lambda message: True)
|
@_bot.message_handler(func=lambda message: True)
|
||||||
def echo_all(message):
|
def echo_all(message):
|
||||||
RequestUtils(timeout=10).post_res(self._ds_url, json=message.json)
|
RequestUtils(timeout=10).post_res(self._ds_url, json=message.json)
|
||||||
|
|
||||||
# 启动轮询
|
def run_polling():
|
||||||
|
"""
|
||||||
|
定义线程函数来运行 infinity_polling
|
||||||
|
"""
|
||||||
_bot.infinity_polling()
|
_bot.infinity_polling()
|
||||||
|
|
||||||
|
# 启动线程来运行 infinity_polling
|
||||||
|
self._polling_thread = threading.Thread(target=run_polling)
|
||||||
|
self._polling_thread.start()
|
||||||
|
|
||||||
def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]:
|
def send_msg(self, title: str, text: str = "", image: str = "", userid: str = "") -> Optional[bool]:
|
||||||
"""
|
"""
|
||||||
发送Telegram消息
|
发送Telegram消息
|
||||||
@ -156,8 +165,25 @@ class Telegram(metaclass=Singleton):
|
|||||||
|
|
||||||
return True if ret else False
|
return True if ret else False
|
||||||
|
|
||||||
|
def register_commands(self, commands: dict):
|
||||||
|
"""
|
||||||
|
注册菜单命令
|
||||||
|
"""
|
||||||
|
if not self._bot:
|
||||||
|
return
|
||||||
|
# 设置bot命令
|
||||||
|
if commands:
|
||||||
|
self._bot.delete_my_commands()
|
||||||
|
self._bot.set_my_commands(
|
||||||
|
commands=[
|
||||||
|
telebot.types.BotCommand(cmd[1:], str(desc.get("description"))) for cmd, desc in
|
||||||
|
commands.items()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""
|
"""
|
||||||
停止Telegram消息接收服务
|
停止Telegram消息接收服务
|
||||||
"""
|
"""
|
||||||
self._bot.stop_polling()
|
self._bot.stop_polling()
|
||||||
|
self._polling_thread.join()
|
||||||
|
@ -33,6 +33,9 @@ class TheMovieDb(_ModuleBase):
|
|||||||
self.tmdb = TmdbHelper()
|
self.tmdb = TmdbHelper()
|
||||||
self.category = CategoryHelper()
|
self.category = CategoryHelper()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -14,6 +14,9 @@ class TransmissionModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.transmission = Transmission()
|
self.transmission = Transmission()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "DOWNLOADER", "transmission"
|
return "DOWNLOADER", "transmission"
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ class WechatModule(_ModuleBase):
|
|||||||
def init_module(self) -> None:
|
def init_module(self) -> None:
|
||||||
self.wechat = WeChat()
|
self.wechat = WeChat()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
pass
|
||||||
|
|
||||||
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
def init_setting(self) -> Tuple[str, Union[str, bool]]:
|
||||||
return "MESSAGER", "wechat"
|
return "MESSAGER", "wechat"
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import json
|
import json
|
||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Optional
|
from typing import Any
|
||||||
|
|
||||||
from app.chain import ChainBase
|
from app.chain import ChainBase
|
||||||
from app.core import settings, Context
|
from app.core import settings
|
||||||
from app.db import SessionLocal
|
from app.db import SessionLocal
|
||||||
from app.db.models import Base
|
from app.db.models import Base
|
||||||
from app.db.models.plugin import PluginData
|
from app.db.models.plugin import PluginData
|
||||||
@ -17,7 +17,7 @@ class PluginChian(ChainBase):
|
|||||||
插件处理链
|
插件处理链
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def process(self, *args, **kwargs) -> Optional[Context]:
|
def process(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,11 +31,8 @@ class AutoSignIn(_PluginBase):
|
|||||||
event: EventManager = None
|
event: EventManager = None
|
||||||
# 定时器
|
# 定时器
|
||||||
_scheduler = None
|
_scheduler = None
|
||||||
|
|
||||||
# 加载的模块
|
# 加载的模块
|
||||||
_site_schema: list = []
|
_site_schema: list = []
|
||||||
# 退出事件
|
|
||||||
_event = Event()
|
|
||||||
|
|
||||||
def init_plugin(self, config: dict = None):
|
def init_plugin(self, config: dict = None):
|
||||||
self.sites = SitesHelper()
|
self.sites = SitesHelper()
|
||||||
@ -70,7 +67,7 @@ class AutoSignIn(_PluginBase):
|
|||||||
:return: 命令关键字、事件、描述、附带数据
|
:return: 命令关键字、事件、描述、附带数据
|
||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
"cmd": "/pts",
|
"cmd": "/sitesignin",
|
||||||
"event": EventType.SiteSignin,
|
"event": EventType.SiteSignin,
|
||||||
"desc": "站点自动签到",
|
"desc": "站点自动签到",
|
||||||
"data": {}
|
"data": {}
|
||||||
@ -191,9 +188,7 @@ class AutoSignIn(_PluginBase):
|
|||||||
if self._scheduler:
|
if self._scheduler:
|
||||||
self._scheduler.remove_all_jobs()
|
self._scheduler.remove_all_jobs()
|
||||||
if self._scheduler.running:
|
if self._scheduler.running:
|
||||||
self._event.set()
|
|
||||||
self._scheduler.shutdown()
|
self._scheduler.shutdown()
|
||||||
self._event.clear()
|
|
||||||
self._scheduler = None
|
self._scheduler = None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("退出插件失败:%s" % str(e))
|
logger.error("退出插件失败:%s" % str(e))
|
||||||
|
@ -7,7 +7,8 @@ import requests
|
|||||||
from apscheduler.schedulers.background import BackgroundScheduler
|
from apscheduler.schedulers.background import BackgroundScheduler
|
||||||
from ruamel.yaml import CommentedMap
|
from ruamel.yaml import CommentedMap
|
||||||
|
|
||||||
from app.core import settings
|
from app.core import settings, eventmanager
|
||||||
|
from app.core.event_manager import Event
|
||||||
from app.helper import ModuleHelper
|
from app.helper import ModuleHelper
|
||||||
from app.helper.sites import SitesHelper
|
from app.helper.sites import SitesHelper
|
||||||
from app.log import logger
|
from app.log import logger
|
||||||
@ -17,6 +18,9 @@ from app.utils.http import RequestUtils
|
|||||||
from app.utils.timer import TimerUtils
|
from app.utils.timer import TimerUtils
|
||||||
|
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
|
from app.utils.types import EventType
|
||||||
|
|
||||||
warnings.filterwarnings("ignore", category=FutureWarning)
|
warnings.filterwarnings("ignore", category=FutureWarning)
|
||||||
|
|
||||||
|
|
||||||
@ -57,6 +61,19 @@ class SiteStatistic(_PluginBase):
|
|||||||
self._scheduler.print_jobs()
|
self._scheduler.print_jobs()
|
||||||
self._scheduler.start()
|
self._scheduler.start()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_command() -> dict:
|
||||||
|
"""
|
||||||
|
定义远程控制命令
|
||||||
|
:return: 命令关键字、事件、描述、附带数据
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
"cmd": "/sitestatistic",
|
||||||
|
"event": EventType.SiteStatistic,
|
||||||
|
"desc": "站点数据统计",
|
||||||
|
"data": {}
|
||||||
|
}
|
||||||
|
|
||||||
def stop_service(self):
|
def stop_service(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -221,6 +238,14 @@ class SiteStatistic(_PluginBase):
|
|||||||
title=f"站点 {site_user_info.site_name} 收到 "
|
title=f"站点 {site_user_info.site_name} 收到 "
|
||||||
f"{site_user_info.message_unread} 条新消息,请登陆查看")
|
f"{site_user_info.message_unread} 条新消息,请登陆查看")
|
||||||
|
|
||||||
|
@eventmanager.register(EventType.SiteStatistic)
|
||||||
|
def refresh(self, event: Event):
|
||||||
|
"""
|
||||||
|
刷新站点数据
|
||||||
|
"""
|
||||||
|
logger.info("开始执行站点数据刷新 ...")
|
||||||
|
self.refresh_all_site_data(force=True)
|
||||||
|
|
||||||
def refresh_all_site_data(self, force: bool = False, specify_sites: list = None):
|
def refresh_all_site_data(self, force: bool = False, specify_sites: list = None):
|
||||||
"""
|
"""
|
||||||
多线程刷新站点下载上传量,默认间隔6小时
|
多线程刷新站点下载上传量,默认间隔6小时
|
||||||
@ -228,6 +253,8 @@ class SiteStatistic(_PluginBase):
|
|||||||
if not self.sites.get_indexers():
|
if not self.sites.get_indexers():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
logger.info("开始刷新站点数据 ...")
|
||||||
|
|
||||||
with lock:
|
with lock:
|
||||||
|
|
||||||
if not force \
|
if not force \
|
||||||
@ -251,15 +278,13 @@ class SiteStatistic(_PluginBase):
|
|||||||
|
|
||||||
# 并发刷新
|
# 并发刷新
|
||||||
with ThreadPool(min(len(refresh_sites), self._MAX_CONCURRENCY)) as p:
|
with ThreadPool(min(len(refresh_sites), self._MAX_CONCURRENCY)) as p:
|
||||||
site_user_infos: List[ISiteUserInfo] = p.map(self.__refresh_site_data, refresh_sites)
|
p.map(self.__refresh_site_data, refresh_sites)
|
||||||
site_user_infos = [info for info in site_user_infos if info]
|
|
||||||
# 保存数据
|
|
||||||
for site_user_info in site_user_infos:
|
|
||||||
# 获取今天的日期
|
|
||||||
key = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
||||||
value = site_user_info.to_dict()
|
|
||||||
# 按日期保存为字典
|
|
||||||
self.save_data(key, value)
|
|
||||||
|
|
||||||
|
# 获取今天的日期
|
||||||
|
key = datetime.now().strftime('%Y-%m-%d')
|
||||||
|
# 保存数据
|
||||||
|
self.save_data(key, self._sites_data)
|
||||||
# 更新时间
|
# 更新时间
|
||||||
self._last_update_time = datetime.now()
|
self._last_update_time = datetime.now()
|
||||||
|
|
||||||
|
logger.info("站点数据刷新完成")
|
||||||
|
@ -19,6 +19,8 @@ class EventType(Enum):
|
|||||||
CommandExcute = "command.excute"
|
CommandExcute = "command.excute"
|
||||||
# 站点签到
|
# 站点签到
|
||||||
SiteSignin = "site.signin"
|
SiteSignin = "site.signin"
|
||||||
|
# 站点数据统计
|
||||||
|
SiteStatistic = "site.statistic"
|
||||||
|
|
||||||
|
|
||||||
# 系统配置Key字典
|
# 系统配置Key字典
|
||||||
|
Loading…
x
Reference in New Issue
Block a user