- 系统任务保留刚需三项:登录巡检、消息计数入库、媒体补偿处理;移除新闻/Epic/排行/PDF/秀人维护等业务型系统任务定义\n- 新增 daily_news、epic_free、daily_ranking、sehuatang_push 四个插件,将原系统业务任务改为插件可调度动作\n- 扩展 xiuren_image 插件调度动作,新增秀人下载、绅士R15下载、图片缓存更新三项维护任务\n- 新增系统任务到插件任务的幂等迁移逻辑:按旧 job_key 映射到插件 action,同步 trigger_type/trigger_config/enabled,并通过 payload 标记防止反复覆盖\n- 在 Robot 启动流程中接入迁移执行与重载,并清理已迁移的历史系统任务记录,避免后台双份维护\n- 扩展插件调度数据库操作:支持按 plugin_name + action_key 精确查询,便于迁移与对账
110 lines
4.1 KiB
Python
110 lines
4.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Awaitable, Callable, Dict, List
|
|
|
|
from db.system_job_db import SystemJobDBOperator
|
|
from utils.decorator.async_job import async_job
|
|
|
|
|
|
def get_system_job_definitions(robot) -> List[Dict[str, Any]]:
|
|
"""系统任务定义(业务函数映射)。
|
|
|
|
说明:这里只维护“任务 key 与业务函数”的绑定关系;
|
|
调度时间、启停状态全部从数据库 t_system_jobs 读取。
|
|
"""
|
|
return [
|
|
{
|
|
"job_key": "message_count_to_db",
|
|
"name": "消息计数入库",
|
|
"description": "每天 02:30 将 Redis 消息计数写入 SQLite",
|
|
"trigger_type": "at_times",
|
|
"trigger_config": {"time_list": ["02:30"]},
|
|
"handler": robot.message_count_to_db,
|
|
},
|
|
{
|
|
"job_key": "login_check",
|
|
"name": "登录状态巡检",
|
|
"description": "每天 14:43 执行登录二次校验",
|
|
"trigger_type": "at_times",
|
|
"trigger_config": {"time_list": ["14:43"]},
|
|
"handler": robot.login_twice_auto_auth,
|
|
},
|
|
{
|
|
"job_key": "process_pending_images",
|
|
"name": "待下载图片补偿处理",
|
|
"description": "每 5 分钟处理一次待下载图片/表情,避免数据库锁竞争",
|
|
"trigger_type": "every_seconds",
|
|
"trigger_config": {"seconds": 300},
|
|
"handler": _build_process_pending_images_handler(robot),
|
|
},
|
|
]
|
|
|
|
def _build_process_pending_images_handler(robot) -> Callable[[], Awaitable[None]]:
|
|
async def _handler():
|
|
if hasattr(robot, "message_storage") and robot.message_storage:
|
|
await robot.message_storage.process_pending_images(minutes_ago=10, batch_size=20)
|
|
|
|
return _handler
|
|
|
|
|
|
class SystemJobLoader:
|
|
"""系统任务加载器:从数据库读取调度配置并注册到 async_job。"""
|
|
|
|
def __init__(self, robot, system_job_db: SystemJobDBOperator):
|
|
self.robot = robot
|
|
self.db = system_job_db
|
|
self._job_defs = {item["job_key"]: item for item in get_system_job_definitions(robot)}
|
|
self._registered_job_ids: List[str] = []
|
|
|
|
def init_and_load(self):
|
|
self.db.init_tables()
|
|
self._seed_defaults()
|
|
self.reload_from_db()
|
|
|
|
def _seed_defaults(self):
|
|
for item in self._job_defs.values():
|
|
existed = self.db.get_job(item["job_key"])
|
|
if existed:
|
|
continue
|
|
self.db.upsert_job(
|
|
{
|
|
"job_key": item["job_key"],
|
|
"name": item["name"],
|
|
"description": item.get("description", ""),
|
|
"trigger_type": item["trigger_type"],
|
|
"trigger_config": item["trigger_config"],
|
|
"enabled": True,
|
|
}
|
|
)
|
|
|
|
def reload_from_db(self):
|
|
# 每次重载前先补齐默认任务,避免误删后无法恢复
|
|
self._seed_defaults()
|
|
|
|
# 先移除当前注册任务,避免重复调度
|
|
for job_id in self._registered_job_ids:
|
|
async_job.remove_job(job_id)
|
|
self._registered_job_ids = []
|
|
|
|
jobs = self.db.list_jobs()
|
|
for row in jobs:
|
|
job_key = row.get("job_key")
|
|
if not row.get("enabled", 1):
|
|
continue
|
|
definition = self._job_defs.get(job_key)
|
|
if not definition:
|
|
logger.warning(f"系统任务 {job_key} 在代码中无处理器,已跳过注册")
|
|
continue
|
|
|
|
handler = definition["handler"]
|
|
job_id = async_job.register_callable(
|
|
func=handler,
|
|
trigger_type=row.get("trigger_type", definition["trigger_type"]),
|
|
trigger_config=row.get("trigger_config", definition["trigger_config"]),
|
|
job_name=row.get("name") or definition["name"],
|
|
description=row.get("description") or definition.get("description", ""),
|
|
job_key=job_key,
|
|
)
|
|
self._registered_job_ids.append(job_id)
|