diff --git a/app/api/endpoints/plugin.py b/app/api/endpoints/plugin.py index 02d64044..7a7c4208 100644 --- a/app/api/endpoints/plugin.py +++ b/app/api/endpoints/plugin.py @@ -84,7 +84,10 @@ def set_plugin_config(plugin_id: str, conf: dict, """ 根据插件ID获取插件配置信息 """ + # 保存配置 PluginManager().save_plugin_config(plugin_id, conf) + # 重新生效插件 + PluginManager().reload_plugin(plugin_id, conf) return schemas.Response(success=True) diff --git a/app/core/plugin.py b/app/core/plugin.py index 77a92f6b..1e4c09d0 100644 --- a/app/core/plugin.py +++ b/app/core/plugin.py @@ -66,6 +66,14 @@ class PluginManager(metaclass=Singleton): except Exception as err: logger.error(f"加载插件 {plugin_id} 出错:{err} - {traceback.format_exc()}") + def reload_plugin(self, plugin_id: str, conf: dict): + """ + 重新加载插件 + """ + if not self._running_plugins.get(plugin_id): + return + self._running_plugins[plugin_id].init_plugin(conf) + def stop(self): """ 停止 diff --git a/app/plugins/autosignin/__init__.py b/app/plugins/autosignin/__init__.py index 7bac0019..945b1ff2 100644 --- a/app/plugins/autosignin/__init__.py +++ b/app/plugins/autosignin/__init__.py @@ -7,6 +7,7 @@ from typing import Any, List, Dict, Tuple from urllib.parse import urljoin from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger from ruamel.yaml import CommentedMap from app import schemas @@ -57,6 +58,13 @@ class AutoSignIn(_PluginBase): # 加载的模块 _site_schema: list = [] + # 配置属性 + _enabled: bool = False + _cron: str = "" + _notify: bool = False + _queue_cnt: int = 5 + _sign_sites: list = [] + def init_plugin(self, config: dict = None): self.sites = SitesHelper() self.event = EventManager() @@ -64,24 +72,44 @@ class AutoSignIn(_PluginBase): # 停止现有任务 self.stop_service() + # 配置 + if config: + self._enabled = config.get("enabled") + self._cron = config.get("cron") + self._notify = config.get("notify") + self._queue_cnt = config.get("queue_cnt") + self._sign_sites = config.get("sign_sites") + # 加载模块 - self._site_schema = ModuleHelper.load('app.plugins.autosignin.sites', - filter_func=lambda _, obj: hasattr(obj, 'match')) + if self._enabled: - # 定时服务 - self._scheduler = BackgroundScheduler(timezone=settings.TZ) - triggers = TimerUtils.random_scheduler(num_executions=2, - begin_hour=9, - end_hour=23, - max_interval=12 * 60, - min_interval=6 * 60) - for trigger in triggers: - self._scheduler.add_job(self.sign_in, "cron", hour=trigger.hour, minute=trigger.minute) + self._site_schema = ModuleHelper.load('app.plugins.autosignin.sites', + filter_func=lambda _, obj: hasattr(obj, 'match')) - # 启动任务 - if self._scheduler.get_jobs(): - self._scheduler.print_jobs() - self._scheduler.start() + # 定时服务 + self._scheduler = BackgroundScheduler(timezone=settings.TZ) + + if self._cron: + try: + self._scheduler.add_job(func=self.sign_in, + trigger=CronTrigger.from_crontab(self._cron)) + except Exception as err: + logger.error(f"定时任务配置错误:{err}") + else: + # 随机时间 + triggers = TimerUtils.random_scheduler(num_executions=2, + begin_hour=9, + end_hour=23, + max_interval=12 * 60, + min_interval=6 * 60) + for trigger in triggers: + self._scheduler.add_job(self.sign_in, "cron", + hour=trigger.hour, minute=trigger.minute) + + # 启动任务 + if self._scheduler.get_jobs(): + self._scheduler.print_jobs() + self._scheduler.start() @staticmethod def get_command() -> List[Dict[str, Any]]: @@ -197,22 +225,6 @@ class AutoSignIn(_PluginBase): } } ] - }, - { - 'component': 'VCol', - 'props': { - 'cols': 12, - 'md': 6 - }, - 'content': [ - { - 'component': 'VTextField', - 'props': { - 'model': 'retry_keyword', - 'label': '重试关键字' - } - } - ] } ] }, @@ -235,26 +247,6 @@ class AutoSignIn(_PluginBase): ] } ] - }, - { - 'component': 'VRow', - 'content': [ - { - 'component': 'VCol', - 'content': [ - { - 'component': 'VSelect', - 'props': { - 'chips': True, - 'multiple': True, - 'model': 'special_sites', - 'label': '特殊站点', - 'items': site_options - } - } - ] - } - ] } ] } @@ -263,9 +255,7 @@ class AutoSignIn(_PluginBase): "notify": True, "cron": "1 9,18 * * *", "queue_cnt": 5, - "retry_keyword": "", - "sign_sites": [], - "special_sites": [] + "sign_sites": [] } def get_page(self) -> List[dict]: @@ -283,13 +273,17 @@ class AutoSignIn(_PluginBase): logger.info("收到远程签到命令,开始执行签到任务 ...") # 查询签到站点 sign_sites = [site for site in self.sites.get_indexers() if not site.get("public")] + # 过滤掉没有选中的站点 + if self._sign_sites: + sign_sites = [site for site in sign_sites if site.get("id") in self._sign_sites] + if not sign_sites: logger.info("没有需要签到的站点") return # 执行签到 logger.info("开始执行签到任务 ...") - with ThreadPool(min(len(sign_sites), 5)) as p: + with ThreadPool(min(len(sign_sites), self._queue_cnt)) as p: status = p.map(self.signin_site, sign_sites) if status: @@ -302,8 +296,9 @@ class AutoSignIn(_PluginBase): "status": s[1] } for s in status]) # 发送通知 - self.chain.post_message(Notification(title="站点自动签到", - text="\n".join([f'【{s[0]}】{s[1]}' for s in status if s]))) + if self._notify: + self.chain.post_message(Notification(title="站点自动签到", + text="\n".join([f'【{s[0]}】{s[1]}' for s in status if s]))) else: logger.error("站点签到任务失败!") diff --git a/app/plugins/sitestatistic/__init__.py b/app/plugins/sitestatistic/__init__.py index df1273da..c7d555ce 100644 --- a/app/plugins/sitestatistic/__init__.py +++ b/app/plugins/sitestatistic/__init__.py @@ -5,6 +5,7 @@ from typing import Optional, Any, List, Dict, Tuple import requests from apscheduler.schedulers.background import BackgroundScheduler +from apscheduler.triggers.cron import CronTrigger from ruamel.yaml import CommentedMap from app import schemas @@ -55,37 +56,64 @@ class SiteStatistic(_PluginBase): # 私有属性 sites = None - _scheduler: BackgroundScheduler = None - _MAX_CONCURRENCY: int = 10 + _scheduler = None _last_update_time: Optional[datetime] = None _sites_data: dict = {} _site_schema: List[ISiteUserInfo] = None - def init_plugin(self, config: dict = None): - # 加载模块 - self._site_schema = ModuleHelper.load('app.plugins.sitestatistic.siteuserinfo', - filter_func=lambda _, obj: hasattr(obj, 'schema')) - self._site_schema.sort(key=lambda x: x.order) - # 站点管理 - self.sites = SitesHelper() - # 站点上一次更新时间 - self._last_update_time = None - # 站点数据 - self._sites_data = {} - # 定时服务 - self._scheduler = BackgroundScheduler(timezone=settings.TZ) - triggers = TimerUtils.random_scheduler(num_executions=1, - begin_hour=0, - end_hour=1, - min_interval=1, - max_interval=60) - for trigger in triggers: - self._scheduler.add_job(self.refresh_all_site_data, "cron", hour=trigger.hour, minute=trigger.minute) + # 配置属性 + _enabled: bool = False + _cron: str = "" + _notify: bool = False + _queue_cnt: int = 5 + _statistic_sites: list = [] - # 启动任务 - if self._scheduler.get_jobs(): - self._scheduler.print_jobs() - self._scheduler.start() + def init_plugin(self, config: dict = None): + + # 停止现有任务 + self.stop_service() + + # 配置 + if config: + self._enabled = config.get("enabled") + self._cron = config.get("cron") + self._notify = config.get("notify") + self._queue_cnt = config.get("queue_cnt") + self._statistic_sites = config.get("statistic_sites") + + if self._enabled: + # 加载模块 + self._site_schema = ModuleHelper.load('app.plugins.sitestatistic.siteuserinfo', + filter_func=lambda _, obj: hasattr(obj, 'schema')) + self._site_schema.sort(key=lambda x: x.order) + # 站点管理 + self.sites = SitesHelper() + # 站点上一次更新时间 + self._last_update_time = None + # 站点数据 + self._sites_data = {} + # 定时服务 + self._scheduler = BackgroundScheduler(timezone=settings.TZ) + if self._cron: + try: + self._scheduler.add_job(func=self.refresh_all_site_data, + trigger=CronTrigger.from_crontab(self._cron)) + except Exception as err: + logger.error(f"定时任务配置错误:{err}") + else: + triggers = TimerUtils.random_scheduler(num_executions=1, + begin_hour=0, + end_hour=1, + min_interval=1, + max_interval=60) + for trigger in triggers: + self._scheduler.add_job(self.refresh_all_site_data, "cron", + hour=trigger.hour, minute=trigger.minute) + + # 启动任务 + if self._scheduler.get_jobs(): + self._scheduler.print_jobs() + self._scheduler.start() @staticmethod def get_command() -> List[Dict[str, Any]]: @@ -241,7 +269,17 @@ class SiteStatistic(_PluginBase): pass def stop_service(self): - pass + """ + 退出插件 + """ + try: + if self._scheduler: + self._scheduler.remove_all_jobs() + if self._scheduler.running: + self._scheduler.shutdown() + self._scheduler = None + except Exception as e: + logger.error("退出插件失败:%s" % str(e)) def __build_class(self, html_text: str) -> Any: for site_schema in self._site_schema: @@ -471,12 +509,14 @@ class SiteStatistic(_PluginBase): else: refresh_sites = [site for site in self.sites.get_indexers() if site.get("name") in specify_sites] + # 过滤掉未选中的站点 + refresh_sites = [site for site in refresh_sites if site.get("id") in self._statistic_sites] if not refresh_sites: return # 并发刷新 - with ThreadPool(min(len(refresh_sites), self._MAX_CONCURRENCY)) as p: + with ThreadPool(min(len(refresh_sites), self._queue_cnt)) as p: p.map(self.__refresh_site_data, refresh_sites) # 获取今天的日期 @@ -487,35 +527,36 @@ class SiteStatistic(_PluginBase): self._last_update_time = datetime.now() # 通知刷新完成 - messages = [] - # 按照上传降序排序 - sites = self._sites_data.keys() - uploads = [self._sites_data[site].get("upload") or 0 for site in sites] - downloads = [self._sites_data[site].get("download") or 0 for site in sites] - data_list = sorted(list(zip(sites, uploads, downloads)), - key=lambda x: x[1], - reverse=True) - # 总上传 - incUploads = 0 - # 总下载 - incDownloads = 0 - for data in data_list: - site = data[0] - upload = int(data[1]) - download = int(data[2]) - if upload > 0 or download > 0: - incUploads += int(upload) - incDownloads += int(download) - messages.append(f"【{site}】\n" - f"上传量:{StringUtils.str_filesize(upload)}\n" - f"下载量:{StringUtils.str_filesize(download)}\n" - f"————————————") + if self._notify: + messages = [] + # 按照上传降序排序 + sites = self._sites_data.keys() + uploads = [self._sites_data[site].get("upload") or 0 for site in sites] + downloads = [self._sites_data[site].get("download") or 0 for site in sites] + data_list = sorted(list(zip(sites, uploads, downloads)), + key=lambda x: x[1], + reverse=True) + # 总上传 + incUploads = 0 + # 总下载 + incDownloads = 0 + for data in data_list: + site = data[0] + upload = int(data[1]) + download = int(data[2]) + if upload > 0 or download > 0: + incUploads += int(upload) + incDownloads += int(download) + messages.append(f"【{site}】\n" + f"上传量:{StringUtils.str_filesize(upload)}\n" + f"下载量:{StringUtils.str_filesize(download)}\n" + f"————————————") - if incDownloads or incUploads: - messages.insert(0, f"【汇总】\n" - f"总上传:{StringUtils.str_filesize(incUploads)}\n" - f"总下载:{StringUtils.str_filesize(incDownloads)}\n" - f"————————————") - self.chain.post_message(Notification(title="站点数据统计", text="\n".join(messages))) + if incDownloads or incUploads: + messages.insert(0, f"【汇总】\n" + f"总上传:{StringUtils.str_filesize(incUploads)}\n" + f"总下载:{StringUtils.str_filesize(incDownloads)}\n" + f"————————————") + self.chain.post_message(Notification(title="站点数据统计", text="\n".join(messages))) logger.info("站点数据刷新完成")