11
This commit is contained in:
+35
-13
@@ -1,5 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import aiofiles
|
||||||
|
|
||||||
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
||||||
from apscheduler.triggers.interval import IntervalTrigger
|
from apscheduler.triggers.interval import IntervalTrigger
|
||||||
@@ -36,17 +37,22 @@ class AsyncTask:
|
|||||||
async with self._file_lock:
|
async with self._file_lock:
|
||||||
try:
|
try:
|
||||||
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
self.file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
if not self.file_path.exists():
|
if not self.file_path.exists():
|
||||||
local_data = {}
|
local_data = {}
|
||||||
else:
|
else:
|
||||||
with open(self.file_path, 'r', encoding='utf-8') as f:
|
async with aiofiles.open(self.file_path, "r", encoding="utf-8") as f:
|
||||||
local_data = json.load(f)
|
content = await f.read()
|
||||||
|
local_data = json.loads(content) if content else {}
|
||||||
|
|
||||||
local_data[key] = value
|
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)
|
|
||||||
|
|
||||||
except Exception as e:
|
async with aiofiles.open(self.file_path, "w", encoding="utf-8") as f:
|
||||||
|
await f.write(
|
||||||
|
json.dumps(local_data, ensure_ascii=False, indent=4)
|
||||||
|
)
|
||||||
|
|
||||||
|
except (OSError, json.JSONDecodeError) as e:
|
||||||
logger.error(f"数据写入文件失败:{e}")
|
logger.error(f"数据写入文件失败:{e}")
|
||||||
|
|
||||||
async def get_local_data(self, key: str, default=None):
|
async def get_local_data(self, key: str, default=None):
|
||||||
@@ -54,10 +60,16 @@ class AsyncTask:
|
|||||||
try:
|
try:
|
||||||
if not self.file_path.exists():
|
if not self.file_path.exists():
|
||||||
return default
|
return default
|
||||||
with open(self.file_path, 'r', encoding='utf-8') as f:
|
|
||||||
local_data = json.load(f)
|
async with aiofiles.open(self.file_path, "r", encoding="utf-8") as f:
|
||||||
|
content = await f.read()
|
||||||
|
if not content:
|
||||||
|
return default
|
||||||
|
local_data = json.loads(content)
|
||||||
|
|
||||||
return local_data.get(key, default)
|
return local_data.get(key, default)
|
||||||
except Exception as e:
|
|
||||||
|
except (OSError, json.JSONDecodeError) as e:
|
||||||
logger.error(f"读取数据文件失败:{e}")
|
logger.error(f"读取数据文件失败:{e}")
|
||||||
return default
|
return default
|
||||||
|
|
||||||
@@ -65,9 +77,14 @@ class AsyncTask:
|
|||||||
|
|
||||||
async def _job_common(self, fetch_func, task_key: str, namefun: str):
|
async def _job_common(self, fetch_func, task_key: str, namefun: str):
|
||||||
state = self.tasks[task_key]
|
state = self.tasks[task_key]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
data = await fetch_func()
|
data = await fetch_func()
|
||||||
state["state_new"] = data["status"]
|
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise ValueError("fetch_func 返回数据不是 dict")
|
||||||
|
|
||||||
|
state["state_new"] = data.get("status")
|
||||||
|
|
||||||
if state["state_old"] != state["state_new"]:
|
if state["state_old"] != state["state_new"]:
|
||||||
message_chain = MessageChain().message(data.get("data"))
|
message_chain = MessageChain().message(data.get("data"))
|
||||||
@@ -78,8 +95,15 @@ class AsyncTask:
|
|||||||
await self.set_local_data(task_key, state["state_new"])
|
await self.set_local_data(task_key, state["state_new"])
|
||||||
state["state_old"] = state["state_new"]
|
state["state_old"] = state["state_new"]
|
||||||
|
|
||||||
|
except asyncio.CancelledError:
|
||||||
|
# 调度器 shutdown 时的正常路径
|
||||||
|
raise
|
||||||
|
|
||||||
|
except (KeyError, TypeError, ValueError) as e:
|
||||||
|
logger.error(f"{namefun} 数据结构异常: {e}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{namefun}后台任务执行异常: {e}")
|
logger.exception(f"{namefun} 后台任务执行异常")
|
||||||
|
|
||||||
"""===================== 初始化任务 ====================="""
|
"""===================== 初始化任务 ====================="""
|
||||||
|
|
||||||
@@ -137,13 +161,11 @@ class AsyncTask:
|
|||||||
logger.error(f"停止全部后台任务失败:{e}")
|
logger.error(f"停止全部后台任务失败:{e}")
|
||||||
|
|
||||||
async def destroy(self):
|
async def destroy(self):
|
||||||
"""
|
|
||||||
销毁整个调度器,适合插件卸载/重启时调用
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
self.stop_all_tasks()
|
self.stop_all_tasks()
|
||||||
if self.scheduler.running:
|
if self.scheduler.running:
|
||||||
self.scheduler.shutdown(wait=False)
|
self.scheduler.shutdown(wait=False)
|
||||||
|
self.tasks.clear()
|
||||||
logger.info("后台调度器已销毁")
|
logger.info("后台调度器已销毁")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"销毁调度器失败:{e}")
|
logger.error(f"销毁调度器失败:{e}")
|
||||||
|
|||||||
+7
-15
@@ -1,27 +1,19 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from datetime import datetime,date
|
from datetime import datetime,date
|
||||||
|
import aiofiles
|
||||||
|
|
||||||
def load_template(template_name):
|
async def load_template(template_name: str) -> str:
|
||||||
"""
|
"""
|
||||||
从模板文件加载模板内容
|
异步加载模板内容(非阻塞)
|
||||||
|
|
||||||
Args:
|
|
||||||
template_name: 模板文件名(不带路径)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: 模板内容
|
|
||||||
"""
|
"""
|
||||||
# 获取模板文件路径
|
|
||||||
plugin_dir = Path(__file__).parent.parent
|
plugin_dir = Path(__file__).parent.parent
|
||||||
template_path = plugin_dir / "templates" / template_name
|
template_path = plugin_dir / "templates" / template_name
|
||||||
|
|
||||||
# 检查文件是否存在
|
|
||||||
if not template_path.exists():
|
if not template_path.exists():
|
||||||
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
raise FileNotFoundError(f"模板文件不存在: {template_path}")
|
||||||
|
|
||||||
# 读取模板内容
|
async with aiofiles.open(template_path, "r", encoding="utf-8") as f:
|
||||||
with open(template_path, 'r', encoding='utf-8') as f:
|
return await f.read()
|
||||||
return f.read()
|
|
||||||
|
|
||||||
|
|
||||||
def gold_to_string(gold_amount):
|
def gold_to_string(gold_amount):
|
||||||
|
|||||||
+21
-14
@@ -33,6 +33,13 @@ class JX3Service:
|
|||||||
logger.info(f"获取配置ticket成功。{self.ticket}")
|
logger.info(f"获取配置ticket成功。{self.ticket}")
|
||||||
|
|
||||||
|
|
||||||
|
async def close(self):
|
||||||
|
"""释放底层 APIClient 资源"""
|
||||||
|
if self._api:
|
||||||
|
await self._api.close()
|
||||||
|
self._api = None
|
||||||
|
|
||||||
|
|
||||||
def _init_return_data(self) -> Dict[str, Any]:
|
def _init_return_data(self) -> Dict[str, Any]:
|
||||||
"""初始化标准的返回数据结构"""
|
"""初始化标准的返回数据结构"""
|
||||||
return {
|
return {
|
||||||
@@ -96,7 +103,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("helps.html")
|
return_data["temp"] = await load_template("helps.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -198,7 +205,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("richangyuche.html")
|
return_data["temp"] = await load_template("richangyuche.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -233,7 +240,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("xingxiashijian.html")
|
return_data["temp"] = await load_template("xingxiashijian.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -361,7 +368,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("qufuzhuangtai.html")
|
return_data["temp"] = await load_template("qufuzhuangtai.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -450,7 +457,7 @@ class JX3Service:
|
|||||||
return return_data
|
return return_data
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("jinjia.html")
|
return_data["temp"] = await load_template("jinjia.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -489,7 +496,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 加载模板
|
# 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("qiyuliebiao.html")
|
return_data["temp"] = await load_template("qiyuliebiao.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -528,7 +535,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 5. 加载模板
|
# 5. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("wujia.html")
|
return_data["temp"] = await load_template("wujia.html")
|
||||||
return_data["code"] = 200
|
return_data["code"] = 200
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
@@ -578,7 +585,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 5. 模板渲染
|
# 5. 模板渲染
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("jiaoyihang.html")
|
return_data["temp"] = await load_template("jiaoyihang.html")
|
||||||
return_data["code"] = 200
|
return_data["code"] = 200
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
@@ -661,7 +668,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 4. 加载模板
|
# 4. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("yanhuan.html")
|
return_data["temp"] = await load_template("yanhuan.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -697,7 +704,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 4. 加载模板
|
# 4. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("dilujilu.html")
|
return_data["temp"] = await load_template("dilujilu.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -732,7 +739,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 4. 加载模板
|
# 4. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("tuanduizhaomu.html")
|
return_data["temp"] = await load_template("tuanduizhaomu.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -772,7 +779,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 4. 加载模板
|
# 4. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("zhanji.html")
|
return_data["temp"] = await load_template("zhanji.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -820,7 +827,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 4. 加载模板
|
# 4. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("juesheqiyu.html")
|
return_data["temp"] = await load_template("juesheqiyu.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
@@ -853,7 +860,7 @@ class JX3Service:
|
|||||||
|
|
||||||
# 4. 加载模板
|
# 4. 加载模板
|
||||||
try:
|
try:
|
||||||
return_data["temp"] = load_template("zhengyingpaimai.html")
|
return_data["temp"] = await load_template("zhengyingpaimai.html")
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
logger.error(f"加载模板失败: {e}")
|
logger.error(f"加载模板失败: {e}")
|
||||||
return_data["msg"] = "系统错误:模板文件不存在"
|
return_data["msg"] = "系统错误:模板文件不存在"
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ class APIClient:
|
|||||||
"""关闭 Session"""
|
"""关闭 Session"""
|
||||||
if self._session and not self._session.closed:
|
if self._session and not self._session.closed:
|
||||||
await self._session.close()
|
await self._session.close()
|
||||||
|
self._session = None
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
await self.get_session()
|
await self.get_session()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
import pathlib
|
import pathlib
|
||||||
|
import asyncio
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
@@ -47,26 +48,29 @@ class Jx3ApiPlugin(Star):
|
|||||||
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
"""可选择实现异步的插件初始化方法,当实例化该插件类之后会自动调用该方法。"""
|
||||||
# --- 调用函数完成检查和复制 ---
|
# --- 调用函数完成检查和复制 ---
|
||||||
try:
|
try:
|
||||||
self.file_local_data = self.check_and_copy_db(
|
loop = asyncio.get_running_loop()
|
||||||
local_data_dir=self.local_data_dir,
|
self.file_local_data = await loop.run_in_executor(
|
||||||
db_filename="local_async.json",
|
None,
|
||||||
default_db_dir=self.data_file_path
|
self.check_and_copy_db,
|
||||||
|
self.local_data_dir,
|
||||||
|
"local_async.json",
|
||||||
|
self.data_file_path
|
||||||
)
|
)
|
||||||
except FileNotFoundError as e:
|
except FileNotFoundError as e:
|
||||||
# 处理默认文件丢失的严重错误
|
|
||||||
logger.critical(f"插件初始化失败:{e}")
|
logger.critical(f"插件初始化失败:{e}")
|
||||||
raise # 中断初始化
|
raise
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.jx3fun = JX3Service(self.api_config,self.conf)
|
self.jx3fun = JX3Service(self.api_config, self.conf)
|
||||||
self.at = AsyncTask(self.context, self.conf, self.jx3fun)
|
self.at = AsyncTask(self.context, self.conf, self.jx3fun)
|
||||||
await self.at.init_tasks()
|
await self.at.init_tasks()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.at.destroy()
|
if hasattr(self, "at"):
|
||||||
logger.error(f"功能示例初始化失败: {e}")
|
await self.at.destroy()
|
||||||
return
|
logger.error(f"功能模块初始化失败: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
logger.info("jx3api异步插件初始化完成")
|
logger.info("jx3api 异步插件初始化完成")
|
||||||
|
|
||||||
|
|
||||||
def check_and_copy_db(self, local_data_dir: Union[str, Path], db_filename: str, default_db_dir: Union[str, Path]) -> pathlib.Path:
|
def check_and_copy_db(self, local_data_dir: Union[str, Path], db_filename: str, default_db_dir: Union[str, Path]) -> pathlib.Path:
|
||||||
@@ -501,8 +505,11 @@ class Jx3ApiPlugin(Star):
|
|||||||
|
|
||||||
async def terminate(self):
|
async def terminate(self):
|
||||||
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
"""可选择实现异步的插件销毁方法,当插件被卸载/停用时会调用。"""
|
||||||
# 关闭数据库连接
|
if self.at:
|
||||||
|
await self.at.destroy()
|
||||||
# 后台z周期进程销毁
|
self.at = None
|
||||||
await self.at.destroy()
|
|
||||||
|
if self.jx3fun:
|
||||||
|
await self.jx3fun.close()
|
||||||
|
self.jx3fun = None
|
||||||
logger.info("jx3api插件已卸载/停用")
|
logger.info("jx3api插件已卸载/停用")
|
||||||
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
aiomysql
|
aiomysql
|
||||||
matplotlib
|
matplotlib
|
||||||
|
aiofiles
|
||||||
Reference in New Issue
Block a user