This commit is contained in:
qsc
2025-12-25 21:40:23 +08:00
parent dbe2aa489c
commit 694eb90e5b
2 changed files with 109 additions and 138 deletions
+102 -131
View File
@@ -3,6 +3,8 @@ from pathlib import Path
import json
from typing import Callable, Awaitable, Optional
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
from astrbot.api.event import filter, AstrMessageEvent, MessageEventResult, MessageChain
from astrbot.api.star import Context, Star, register, StarTools
@@ -11,35 +13,31 @@ from astrbot.api import AstrBotConfig
from .jx3_service import JX3Service
class AsyncTask:
"""
异步后台任务类(asyncio版)
- 支持后台异步循环执行
- 支持 start/stop
- 支持设定间隔
基于 APScheduler 的后台异步监控任务管理类
"""
def __init__(self, context: Context, config: AstrBotConfig, jx3fun: JX3Service):
"""
Args:
coro: 异步任务函数
interval: 每次执行间隔(秒)
auto_start: 是否自动启动
"""
self.context = context
self.conf = config
self.jx3fun = jx3fun
self.file_path = StarTools.get_data_dir("astrbot_plugin_jx3") / "local_async.json"
self._file_lock = asyncio.Lock()
self.scheduler = AsyncIOScheduler()
self.tasks = {} # 存储 task_id 对应的状态信息
logger.info(f"获取后台数据缓存文件路径成功:{self.file_path}")
"""===================== 本地读写 ====================="""
async def set_local_data(self, key: str, value):
"""异步安全写入本地 JSON"""
async with self._file_lock:
try:
self.file_path.parent.mkdir(parents=True, exist_ok=True)
if not self.file_path.exists():
local_data = {}
else:
@@ -47,147 +45,120 @@ class AsyncTask:
local_data = json.load(f)
local_data[key] = value
with open(self.file_path, 'w', encoding='utf-8') as f:
json.dump(local_data, f, ensure_ascii=False, indent=4)
logger.debug(f"后台数据写入完成: {key}--{value}")
except Exception as e:
logger.error(f"数据写入文件失败:{e}")
async def get_local_data(self, key: str, default=None):
"""异步安全读取本地 JSON"""
async with self._file_lock:
try:
if not self.file_path.exists():
return default
with open(self.file_path, 'r', encoding='utf-8') as f:
local_data = json.load(f)
value = local_data.get(key, default)
logger.debug(f"读取数据成功: {key}--{value}")
return value
return local_data.get(key, default)
except Exception as e:
logger.error(f"读取数据文件失败:{e}")
return default
"""===================== 通用后台任务 ====================="""
async def _cycle_common(self,fetch_func, conf: dict, state: dict, namefun: str, local_key: str):
"""后台程序"""
# 调用函数获取最新数据
async def _job_common(self, fetch_func, task_key: str, namefun: str):
state = self.tasks[task_key]
try:
state["state_old"] = await self.get_local_data(local_key)
except Exception as e:
logger.error(f"获取{namefun}本地缓存数据失败: {e}")
# 判断推送功能是否启用
if conf["enable"]:
logger.info(f"{namefun}功能开启")
else:
logger.info(f"{namefun}功能关闭")
return
# 循环启用
while conf["enable"]:
try:
# 获取最新状态
data = await fetch_func()
state["state_new"] = data["status"]
logger.debug(f"{namefun}功能循环中,上次询问状态:{state['state_old']},本次询问状态:{state['state_new']}")
# 判断状态是否变化
if state["state_old"] != state["state_new"]:
logger.info(f"{namefun}功能循环中,上次询问状态:{state['state_old']},本次询问状态:{state['state_new']}")
# 构建消息
message_chain = MessageChain().message(data.get("data"))
# 推送消息
if conf["umos"]:
for umo in conf["umos"]:
await self.context.send_message(umo, message_chain)
# 状态储存本地
await self.set_local_data(local_key, state["state_new"])
# 最新状态覆盖以前状态
data = await fetch_func()
state["state_new"] = data["status"]
if state["state_old"] != state["state_new"]:
message_chain = MessageChain().message(data.get("data"))
for umo in state["umos"]:
await self.context.send_message(umo, message_chain)
await self.set_local_data(task_key, state["state_new"])
state["state_old"] = state["state_new"]
except Exception as e:
logger.error(f"{namefun}循环异常: {e}")
await asyncio.sleep(conf["time"])
async def _get_conf(self, conf: dict, state: dict, namefun: str) -> str:
"""获取功能配置信息"""
return_msg = f"{namefun}后台状态:{conf['enable']}\n"
return_msg += f"周期询问时间:{conf['time']}\n"
return_msg += f"上次询问状态:{state['state_old']}\n"
return_msg += f"推送会话列表:\n{conf['umos']}"
return return_msg
async def cycle_kfjk(self):
"""开服监控后台程序"""
# 获取配置信息
conf = self.conf.get("kfjk", {})
self.kfjk_conf = {
"enable": conf.get("enable", True),
"time": conf.get("time", 60),
"umos": conf.get("umos", []),
}
# 状态记录
self.kfjk = {
"state_old": False,
"state_new": False
}
await self._cycle_common(
fetch_func=lambda: self.jx3fun.kaifu("梦江南"),
conf=self.kfjk_conf,
state=self.kfjk,
namefun="开服监控",
local_key="kfjk"
)
async def cycle_xwzx(self):
"""最新新闻资讯后台程序"""
# 获取配置信息
conf = self.conf.get("xwzx", {})
self.xwzx_conf = {
"enable": conf.get("enable", True),
"time": conf.get("time", 280),
"umos": conf.get("umos", []),
}
# 状态记录
self.xwzx = {
"state_old": 0,
"state_new": 0
}
# 后台进程开启
await self._cycle_common(
fetch_func=lambda: self.jx3fun.xinwei(),
conf=self.xwzx_conf,
state=self.xwzx,
namefun="新闻资讯",
local_key="xwzx"
)
async def get_kfjk_conf(self) -> str:
"""获取开服监控配置信息"""
try:
return_msg = await self._get_conf(self.kfjk_conf, self.kfjk, "开服监控")
except Exception as e:
return_msg = f"获取后台配置状态失败:{e}"
return return_msg
logger.error(f"{namefun}后台任务执行异常: {e}")
async def get_xwzx_conf(self) -> str:
"""获取开服监控配置信息"""
"""===================== 初始化任务 ====================="""
async def init_tasks(self):
settings = [
("kfjk", "开服监控", lambda: self.jx3fun.kaifu("梦江南")),
("xwzx", "新闻资讯", lambda: self.jx3fun.xinwei()),
]
for key, name, fetch in settings:
conf = self.conf.get(key, {})
state_old = await self.get_local_data(key, default=False)
self.tasks[key] = {
"enable": conf.get("enable", True),
"interval": conf.get("time", 60),
"umos": conf.get("umos", []),
"state_old": state_old,
"state_new": state_old
}
if self.tasks[key]["enable"]:
self._add_scheduler(key, name, fetch)
if not self.scheduler.running:
self.scheduler.start()
logger.info("后台监控调度器已启动")
"""===================== 调度操作 ====================="""
def _add_scheduler(self, key, namefun, fetch_func):
if self.scheduler.get_job(key):
self.scheduler.remove_job(key)
interval = self.tasks[key]["interval"]
self.scheduler.add_job(
func=self._job_common,
trigger=IntervalTrigger(seconds=interval),
id=key,
args=[fetch_func, key, namefun]
)
logger.info(f"{namefun}后台任务启动成功,周期:{interval}s")
def stop_all_tasks(self):
"""
停止并移除所有任务
"""
try:
return_msg = await self._get_conf(self.xwzx_conf, self.xwzx, "新闻资讯")
self.scheduler.remove_all_jobs()
for key in self.tasks:
self.tasks[key]["enable"] = False
logger.info("已停止全部后台任务")
except Exception as e:
return_msg = f"获取后台配置状态失败:{e}"
return return_msg
logger.error(f"停止全部后台任务失败:{e}")
async def destroy(self):
"""
销毁整个调度器,适合插件卸载/重启时调用
"""
try:
self.stop_all_tasks()
if self.scheduler.running:
self.scheduler.shutdown(wait=False)
logger.info("后台调度器已销毁")
except Exception as e:
logger.error(f"销毁调度器失败:{e}")
async def get_task_info(self, key: str) -> str:
try:
t = self.tasks[key]
return (
f"功能:{key}\n"
f"启用:{t['enable']}\n"
f"周期:{t['interval']}\n"
f"旧状态:{t['state_old']}\n"
f"推送对象:{t['umos']}"
)
except Exception as e:
return f"读取后台配置失败:{e}"