diff --git a/.gitignore b/.gitignore
index f0dce34..94878a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,7 +25,7 @@ src/plugins/trimo_plugin_msctconverter/MusicPreview/assets/wav
# vuepress
.github
-pyproject.toml
+# pyproject.toml
test.py
line_count.py
diff --git a/liteyuki/__init__.py b/liteyuki/__init__.py
new file mode 100644
index 0000000..c97c271
--- /dev/null
+++ b/liteyuki/__init__.py
@@ -0,0 +1,12 @@
+from liteyuki.bot import (
+ LiteyukiBot,
+ get_bot
+)
+
+# def get_bot_instance() -> LiteyukiBot | None:
+# """
+# 获取轻雪实例
+# Returns:
+# LiteyukiBot: 当前的轻雪实例
+# """
+# return _BOT_INSTANCE
diff --git a/liteyuki/bot/__init__.py b/liteyuki/bot/__init__.py
new file mode 100644
index 0000000..04c7650
--- /dev/null
+++ b/liteyuki/bot/__init__.py
@@ -0,0 +1,270 @@
+import asyncio
+import multiprocessing
+from typing import Any, Coroutine, Optional
+
+import nonebot
+
+from liteyuki.plugin.load import load_plugin, load_plugins
+from src.utils import (
+ adapter_manager,
+ driver_manager,
+)
+from src.utils.base.log import logger
+from liteyuki.bot.lifespan import (
+ Lifespan,
+ LIFESPAN_FUNC,
+)
+from liteyuki.core.spawn_process import nb_run, ProcessingManager
+
+__all__ = [
+ "LiteyukiBot",
+ "get_bot"
+]
+
+_MAIN_PROCESS = multiprocessing.current_process().name == "MainProcess"
+
+
+class LiteyukiBot:
+ def __init__(self, *args, **kwargs):
+
+ global _BOT_INSTANCE
+ _BOT_INSTANCE = self # 引用
+ self.running = False
+ self.config: dict[str, Any] = kwargs
+ self.lifespan: Lifespan = Lifespan()
+ self.init(**self.config) # 初始化
+
+ print("\033[34m" + r"""
+ ▅▅▅▅▅▅▅▅▅▅▅▅▅▅██ ▅▅▅▅▅▅▅▅▅▅▅▅▅▅██ ██ ▅▅▅▅▅▅▅▅▅▅██
+ ▛ ██ ██ ▛ ██ ███ ██ ██
+ ██ ██ ███████████████ ██ ████████▅ ██
+ ███████████████ ██ ███ ██ ██
+ ██ ██ ▅██████████████▛ ██ ████████████
+ ██ ██ ███ ███
+ ████████████████ ██▅ ███ ██ ▅▅▅▅▅▅▅▅▅▅▅██
+ ███ █ ▜███████ ██ ███ ██ ██ ██ ██
+ ███ ███ █████▛ ██ ██ ██ ██ ██
+ ███ ██ ███ █ ██ ██ ██ ██ ██
+ ███ █████ ██████ ███ ██████████████
+""" + "\033[0m")
+
+ def run(self, *args, **kwargs):
+
+ if _MAIN_PROCESS:
+ load_plugins("liteyuki/plugins")
+ asyncio.run(self.lifespan.before_start())
+ self._run_nb_in_spawn_process(*args, **kwargs)
+ else:
+ # 子进程启动
+
+ driver_manager.init(config=self.config)
+ adapter_manager.init(self.config)
+ adapter_manager.register()
+ nonebot.load_plugin("src.liteyuki_main")
+
+ def _run_nb_in_spawn_process(self, *args, **kwargs):
+ """
+ 在新的进程中运行nonebot.run方法
+ Args:
+ *args:
+ **kwargs:
+
+ Returns:
+ """
+
+ timeout_limit: int = 20
+ should_exit = False
+
+ while not should_exit:
+ ctx = multiprocessing.get_context("spawn")
+ event = ctx.Event()
+ ProcessingManager.event = event
+ process = ctx.Process(
+ target=nb_run,
+ args=(event,) + args,
+ kwargs=kwargs,
+ )
+ process.start() # 启动进程
+
+ asyncio.run(self.lifespan.after_start())
+
+ while not should_exit:
+ if ProcessingManager.event.wait(1):
+ logger.info("接收到重启活动信息")
+ process.terminate()
+ process.join(timeout_limit)
+ if process.is_alive():
+ logger.warning(
+ f"进程 {process.pid} 在 {timeout_limit} 秒后依旧存在,强制清灭。"
+ )
+ process.kill()
+ break
+ elif process.is_alive():
+ continue
+ else:
+ should_exit = True
+
+ @staticmethod
+ def _run_coroutine(*coro: Coroutine):
+ """
+ 运行协程
+ Args:
+ coro:
+
+ Returns:
+
+ """
+ # 检测是否有现有的事件循环
+ new_loop = False
+ try:
+ loop = asyncio.get_event_loop()
+ except RuntimeError:
+ new_loop = True
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+
+ if new_loop:
+ for c in coro:
+ loop.run_until_complete(c)
+ loop.close()
+
+ else:
+ for c in coro:
+ loop.create_task(c)
+
+ @property
+ def status(self) -> int:
+ """
+ 获取轻雪状态
+ Returns:
+ int: 0:未启动 1:运行中
+ """
+ return 1 if self.running else 0
+
+ def restart(self):
+ """
+ 停止轻雪
+ Returns:
+
+ """
+ logger.info("正在停止灵温活动…")
+
+ logger.debug("正在启动 before_restart 的函数…")
+ self._run_coroutine(self.lifespan.before_restart())
+ logger.debug("正在启动 before_shutdown 的函数…")
+ self._run_coroutine(self.lifespan.before_shutdown())
+
+ ProcessingManager.restart()
+ self.running = False
+
+ def init(self, *args, **kwargs):
+ """
+ 初始化轻雪, 自动调用
+ Returns:
+
+ """
+ self.init_config()
+ self.init_logger()
+ if not _MAIN_PROCESS:
+ nonebot.init(**kwargs)
+ asyncio.run(self.lifespan.after_nonebot_init())
+
+ def init_logger(self):
+ from src.utils.base.log import init_log
+ init_log()
+
+ def init_config(self):
+ pass
+
+ def register_adapters(self, *args):
+ pass
+
+ def on_before_start(self, func: LIFESPAN_FUNC):
+ """
+ 注册启动前的函数
+ Args:
+ func:
+
+ Returns:
+
+ """
+ return self.lifespan.on_before_start(func)
+
+ def on_after_start(self, func: LIFESPAN_FUNC):
+ """
+ 注册启动后的函数
+ Args:
+ func:
+
+ Returns:
+
+ """
+ return self.lifespan.on_after_start(func)
+
+ def on_before_shutdown(self, func: LIFESPAN_FUNC):
+ """
+ 注册停止前的函数
+ Args:
+ func:
+
+ Returns:
+
+ """
+ return self.lifespan.on_before_shutdown(func)
+
+ def on_after_shutdown(self, func: LIFESPAN_FUNC):
+ """
+ 注册停止后的函数:未实现
+ Args:
+ func:
+
+ Returns:
+
+ """
+ return self.lifespan.on_after_shutdown(func)
+
+ def on_before_restart(self, func: LIFESPAN_FUNC):
+ """
+ 注册重启前的函数
+ Args:
+ func:
+
+ Returns:
+
+ """
+
+ return self.lifespan.on_before_restart(func)
+
+ def on_after_restart(self, func: LIFESPAN_FUNC):
+ """
+ 注册重启后的函数:未实现
+ Args:
+ func:
+
+ Returns:
+
+ """
+ return self.lifespan.on_after_restart(func)
+
+ def on_after_nonebot_init(self, func: LIFESPAN_FUNC):
+ """
+ 注册nonebot初始化后的函数
+ Args:
+ func:
+
+ Returns:
+
+ """
+ return self.lifespan.on_after_nonebot_init(func)
+
+
+_BOT_INSTANCE: Optional[LiteyukiBot] = None
+
+
+def get_bot() -> Optional[LiteyukiBot]:
+ """
+ 获取轻雪实例
+ Returns:
+ LiteyukiBot: 当前的轻雪实例
+ """
+ return _BOT_INSTANCE
diff --git a/liteyuki/bot/lifespan.py b/liteyuki/bot/lifespan.py
new file mode 100644
index 0000000..eec6949
--- /dev/null
+++ b/liteyuki/bot/lifespan.py
@@ -0,0 +1,181 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/7/23 下午8:24
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : lifespan.py
+@Software: PyCharm
+"""
+from typing import Any, Awaitable, Callable, TypeAlias
+
+from liteyuki.utils import is_coroutine_callable
+
+SYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Any]
+ASYNC_LIFESPAN_FUNC: TypeAlias = Callable[[], Awaitable[Any]]
+LIFESPAN_FUNC: TypeAlias = SYNC_LIFESPAN_FUNC | ASYNC_LIFESPAN_FUNC
+
+
+class Lifespan:
+ def __init__(self) -> None:
+ """
+ 轻雪生命周期管理,启动、停止、重启
+ """
+
+ self.life_flag: int = 0 # 0: 启动前,1: 启动后,2: 停止前,3: 停止后
+
+ self._before_start_funcs: list[LIFESPAN_FUNC] = []
+ self._after_start_funcs: list[LIFESPAN_FUNC] = []
+
+ self._before_shutdown_funcs: list[LIFESPAN_FUNC] = []
+ self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
+
+ self._before_restart_funcs: list[LIFESPAN_FUNC] = []
+ self._after_restart_funcs: list[LIFESPAN_FUNC] = []
+
+ self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
+
+ @staticmethod
+ async def _run_funcs(funcs: list[LIFESPAN_FUNC]) -> None:
+ """
+ 运行函数
+ Args:
+ funcs:
+ Returns:
+ """
+ for func in funcs:
+ if is_coroutine_callable(func):
+ await func()
+ else:
+ func()
+
+ def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
+ """
+ 注册启动时的函数
+ Args:
+ func:
+ Returns:
+ LIFESPAN_FUNC:
+ """
+ self._before_start_funcs.append(func)
+ return func
+
+ def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
+ """
+ 注册启动时的函数
+ Args:
+ func:
+ Returns:
+ LIFESPAN_FUNC:
+ """
+ self._after_start_funcs.append(func)
+ return func
+
+ def on_before_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
+ """
+ 注册停止前的函数
+ Args:
+ func:
+ Returns:
+ LIFESPAN_FUNC:
+ """
+ self._before_shutdown_funcs.append(func)
+ return func
+
+ def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
+ """
+ 注册停止后的函数
+ Args:
+ func:
+
+ Returns:
+ LIFESPAN_FUNC:
+
+ """
+ self._after_shutdown_funcs.append(func)
+ return func
+
+ def on_before_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
+ """
+ 注册重启时的函数
+ Args:
+ func:
+ Returns:
+ LIFESPAN_FUNC:
+ """
+ self._before_restart_funcs.append(func)
+ return func
+
+ def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
+ """
+ 注册重启后的函数
+ Args:
+ func:
+ Returns:
+ LIFESPAN_FUNC:
+ """
+ self._after_restart_funcs.append(func)
+ return func
+
+ def on_after_nonebot_init(self, func):
+ """
+ 注册 NoneBot 初始化后的函数
+ Args:
+ func:
+
+ Returns:
+
+ """
+ self._after_nonebot_init_funcs.append(func)
+ return func
+
+ async def before_start(self) -> None:
+ """
+ 启动前
+ Returns:
+ """
+ await self._run_funcs(self._before_start_funcs)
+
+ async def after_start(self) -> None:
+ """
+ 启动后
+ Returns:
+ """
+ await self._run_funcs(self._after_start_funcs)
+
+ async def before_shutdown(self) -> None:
+ """
+ 停止前
+ Returns:
+ """
+ await self._run_funcs(self._before_shutdown_funcs)
+
+ async def after_shutdown(self) -> None:
+ """
+ 停止后
+ Returns:
+ """
+ await self._run_funcs(self._after_shutdown_funcs)
+
+ async def before_restart(self) -> None:
+ """
+ 重启前
+ Returns:
+ """
+ await self._run_funcs(self._before_restart_funcs)
+
+ async def after_restart(self) -> None:
+ """
+ 重启后
+ Returns:
+
+ """
+ await self._run_funcs(self._after_restart_funcs)
+
+ async def after_nonebot_init(self) -> None:
+ """
+ NoneBot 初始化后
+ Returns:
+ """
+ await self._run_funcs(self._after_nonebot_init_funcs)
diff --git a/src/liteyuki/__init__.py b/liteyuki/config.py
similarity index 100%
rename from src/liteyuki/__init__.py
rename to liteyuki/config.py
diff --git a/liteyuki/core/__init__.py b/liteyuki/core/__init__.py
new file mode 100644
index 0000000..e8f45b2
--- /dev/null
+++ b/liteyuki/core/__init__.py
@@ -0,0 +1,3 @@
+from .spawn_process import *
+
+
diff --git a/liteyuki/core/spawn_process.py b/liteyuki/core/spawn_process.py
new file mode 100644
index 0000000..62c065e
--- /dev/null
+++ b/liteyuki/core/spawn_process.py
@@ -0,0 +1,37 @@
+import threading
+from multiprocessing import get_context, Event
+
+import nonebot
+from nonebot import logger
+
+from liteyuki.plugin.load import load_plugins
+
+timeout_limit: int = 20
+__all__ = [
+ "ProcessingManager",
+ "nb_run",
+]
+
+
+class ProcessingManager:
+ event: Event = None
+
+ @classmethod
+ def restart(cls, delay: int = 0):
+ """
+ 发送终止信号
+ Args:
+ delay: 延迟时间,默认为0,单位秒
+ Returns:
+ """
+ if cls.event is None:
+ raise RuntimeError("ProcessingManager 未初始化。")
+ if delay > 0:
+ threading.Timer(delay, function=cls.event.set).start()
+ return
+ cls.event.set()
+
+
+def nb_run(event, *args, **kwargs):
+ ProcessingManager.event = event
+ nonebot.run(*args, **kwargs)
diff --git a/src/liteyuki/exception.py b/liteyuki/exception.py
similarity index 100%
rename from src/liteyuki/exception.py
rename to liteyuki/exception.py
diff --git a/liteyuki/plugin/__init__.py b/liteyuki/plugin/__init__.py
new file mode 100644
index 0000000..485f28b
--- /dev/null
+++ b/liteyuki/plugin/__init__.py
@@ -0,0 +1,17 @@
+from liteyuki.plugin.model import Plugin, PluginMetadata
+from liteyuki.plugin.load import load_plugin, _plugins
+
+__all__ = [
+ "PluginMetadata",
+ "Plugin",
+ "load_plugin",
+]
+
+
+def get_loaded_plugins() -> dict[str, Plugin]:
+ """
+ 获取已加载的插件
+ Returns:
+ dict[str, Plugin]: 插件字典
+ """
+ return _plugins
diff --git a/liteyuki/plugin/load.py b/liteyuki/plugin/load.py
new file mode 100644
index 0000000..a34b4ad
--- /dev/null
+++ b/liteyuki/plugin/load.py
@@ -0,0 +1,78 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/7/23 下午11:59
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : load.py
+@Software: PyCharm
+"""
+import os
+import traceback
+from pathlib import Path
+from typing import Optional
+
+from nonebot import logger
+
+from liteyuki.plugin.model import Plugin, PluginMetadata
+from importlib import import_module
+
+from liteyuki.utils import path_to_module_name
+
+_plugins: dict[str, Plugin] = {}
+
+
+def load_plugin(module_path: str | Path) -> Optional[Plugin]:
+ """加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。
+
+ 参数:
+ module_path: 插件名称 `path.to.your.plugin`
+ 或插件路径 `pathlib.Path(path/to/your/plugin)`
+ """
+ module_path = path_to_module_name(Path(module_path)) if isinstance(module_path, Path) else module_path
+ try:
+ module = import_module(module_path)
+ _plugins[module.__name__] = Plugin(
+ name=module.__name__,
+ module=module,
+ module_name=module_path,
+ metadata=module.__dict__.get("__plugin_metadata__", None)
+ )
+ logger.opt(colors=True).success(
+ f'成功加载 轻雪插件 "{module.__name__.split(".")[-1]}"'
+ )
+ return _plugins[module.__name__]
+
+ except Exception as e:
+ logger.opt(colors=True).success(
+ f'未能加载 轻雪插件 "{module_path}"'
+ )
+ traceback.print_exc()
+ return None
+
+
+def load_plugins(*plugin_dir: str) -> set[Plugin]:
+ """导入文件夹下多个插件
+
+ 参数:
+ plugin_dir: 文件夹路径
+ """
+ plugins = set()
+ for dir_path in plugin_dir:
+ # 遍历每一个文件夹下的py文件和包含__init__.py的文件夹,不递归
+ for f in os.listdir(dir_path):
+ path = Path(os.path.join(dir_path, f))
+
+ module_name = None
+ if os.path.isfile(path) and f.endswith('.py') and f != '__init__.py':
+ module_name = f"{path_to_module_name(Path(dir_path))}.{f[:-3]}"
+
+ elif os.path.isdir(path) and os.path.exists(os.path.join(path, '__init__.py')):
+ module_name = path_to_module_name(path)
+
+ if module_name:
+ load_plugin(module_name)
+ if _plugins.get(module_name):
+ plugins.add(_plugins[module_name])
+ return plugins
diff --git a/liteyuki/plugin/manager.py b/liteyuki/plugin/manager.py
new file mode 100644
index 0000000..9107775
--- /dev/null
+++ b/liteyuki/plugin/manager.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/7/23 下午11:59
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : manager.py
+@Software: PyCharm
+"""
diff --git a/liteyuki/plugin/model.py b/liteyuki/plugin/model.py
new file mode 100644
index 0000000..70f8dab
--- /dev/null
+++ b/liteyuki/plugin/model.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/7/24 上午12:02
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : model.py
+@Software: PyCharm
+"""
+from types import ModuleType
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class PluginMetadata(BaseModel):
+ """
+ 轻雪插件元数据,由插件编写者提供
+ """
+ name: str
+ description: str
+ usage: str = ""
+ type: str = ""
+ homepage: str = ""
+ running_in_main: bool = True # 是否在主进程运行
+
+
+class Plugin(BaseModel):
+ """
+ 存储插件信息
+ """
+ model_config = {
+ 'arbitrary_types_allowed': True
+ }
+ name: str
+ """插件名称 例如plugin_loader"""
+ module: ModuleType
+ """插件模块对象"""
+ module_name: str
+ """点分割模块路径 例如a.b.c"""
+ metadata: Optional[PluginMetadata] = None
+
+ def __hash__(self):
+ return hash(self.module_name)
diff --git a/liteyuki/plugins/plugin_loader/__init__.py b/liteyuki/plugins/plugin_loader/__init__.py
new file mode 100644
index 0000000..f674a93
--- /dev/null
+++ b/liteyuki/plugins/plugin_loader/__init__.py
@@ -0,0 +1,41 @@
+import multiprocessing
+import time
+
+import nonebot
+from nonebot import get_driver
+
+from liteyuki.plugin import PluginMetadata
+from liteyuki import get_bot
+
+__plugin_metadata__ = PluginMetadata(
+ name="plugin_loader",
+ description="轻雪插件加载器",
+ usage="",
+ type="",
+ homepage=""
+)
+
+from src.utils import TempConfig, common_db
+
+liteyuki = get_bot()
+
+
+@liteyuki.on_after_start
+def _():
+ temp_data = common_db.where_one(TempConfig(), default=TempConfig())
+ # 储存重启计时信息
+ if temp_data.data.get("reload", False):
+ delta_time = time.time() - temp_data.data.get("reload_time", 0)
+ temp_data.data["delta_time"] = delta_time
+ common_db.save(temp_data) # 更新数据
+
+
+@liteyuki.on_before_start
+def _():
+ print("灵温正在启动")
+
+
+@liteyuki.on_after_nonebot_init
+async def _():
+ print("NoneBot初始化完成")
+ nonebot.load_plugin("src.liteyuki_main")
diff --git a/liteyuki/plugins/plugin_loader/data_source.py b/liteyuki/plugins/plugin_loader/data_source.py
new file mode 100644
index 0000000..e744bd8
--- /dev/null
+++ b/liteyuki/plugins/plugin_loader/data_source.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/7/23 下午11:21
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : data_source.py
+@Software: PyCharm
+"""
diff --git a/src/liteyuki/core/__init__.py b/liteyuki/plugins/process_manager/__init__.py
similarity index 100%
rename from src/liteyuki/core/__init__.py
rename to liteyuki/plugins/process_manager/__init__.py
diff --git a/src/liteyuki/plugin/__init__.py b/liteyuki/plugins/resource_loader/__init__.py
similarity index 100%
rename from src/liteyuki/plugin/__init__.py
rename to liteyuki/plugins/resource_loader/__init__.py
diff --git a/liteyuki/utils.py b/liteyuki/utils.py
new file mode 100644
index 0000000..af95af9
--- /dev/null
+++ b/liteyuki/utils.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+"""
+一些常用的工具类,部分来源于 nonebot 并遵循其许可进行修改
+"""
+import inspect
+from pathlib import Path
+from typing import Any, Callable
+
+
+def is_coroutine_callable(call: Callable[..., Any]) -> bool:
+ """
+ 判断是否为协程可调用对象
+ Args:
+ call: 可调用对象
+ Returns:
+ bool: 是否为协程可调用对象
+ """
+ if inspect.isroutine(call):
+ return inspect.iscoroutinefunction(call)
+ if inspect.isclass(call):
+ return False
+ func_ = getattr(call, "__call__", None)
+ return inspect.iscoroutinefunction(func_)
+
+
+def path_to_module_name(path: Path) -> str:
+ """
+ 转换路径为模块名
+ Args:
+ path: 路径a/b/c/d -> a.b.c.d
+ Returns:
+ str: 模块名
+ """
+ rel_path = path.resolve().relative_to(Path.cwd().resolve())
+ if rel_path.stem == "__init__":
+ return ".".join(rel_path.parts[:-1])
+ else:
+ return ".".join(rel_path.parts[:-1] + (rel_path.stem,))
diff --git a/main.py b/main.py
index 23b557b..44eb1a2 100644
--- a/main.py
+++ b/main.py
@@ -1,28 +1,6 @@
-import nonebot
-from src.utils import adapter_manager, driver_manager, init
-from src.utils.base.config import load_from_yaml
-from src.utils.base.data_manager import StoredConfig, common_db
-from src.utils.base.ly_api import liteyuki_api
+from liteyuki import LiteyukiBot
+from src.utils import load_from_yaml
-if __name__ == "__mp_main__":
- # Start as multiprocessing
- init()
- store_config: dict = common_db.where_one(StoredConfig(), default=StoredConfig()).config
- static_config = load_from_yaml("config.yml")
- store_config.update(static_config)
- driver_manager.init(config=store_config)
- adapter_manager.init(store_config)
- nonebot.init(**store_config)
- adapter_manager.register()
- try:
- nonebot.load_plugin("src.liteyuki_main")
- nonebot.load_from_toml("pyproject.toml")
- except BaseException as e:
- if not isinstance(e, KeyboardInterrupt):
- nonebot.logger.error(f"An error occurred: {e}, Bug will be reported automatically.")
- liteyuki_api.bug_report(str(e.__repr__()))
-
-if __name__ == "__main__":
- # Start as __main__
- from src.utils.base.reloader import Reloader
- nonebot.run()
+if __name__ in ("__main__", "__mp_main__"):
+ bot = LiteyukiBot(**load_from_yaml("config.yml"))
+ bot.run()
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..e65327c
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,58 @@
+# PEP 621 project metadata
+# See https://www.python.org/dev/peps/pep-0621/
+# This file is for project use, but don`t use with nb-cli
+# 此文件为项目所用,请不要和nb-cli一起使用以防被修改
+[tool.poetry]
+name = "ryoun-trim"
+version = "0"
+description = "based on liteyuki6"
+authors = ["金羿Eilles"]
+license = "MIT & LSO"
+package-mode = false
+
+
+[tool.poetry.dependencies]
+python = "^3.10"
+aiofiles = "~23.2.1"
+aiohttp = "~3.9.3"
+aiosqlite3 = "~0.3.0"
+colored = "~2.2.4"
+fastapi = "~0.110.0"
+GitPython = "~3.1.42"
+httpx = "~0.27.0"
+importlib_metadata = "~7.0.2"
+jieba = "~0.42.1"
+loguru = "~0.7.2"
+nb-cli = "~1.4.1"
+nonebot-adapter-onebot = "~2.4.3"
+nonebot-adapter-satori = "~0.11.5"
+nonebot-plugin-alconna = "~0.46.3"
+nonebot-plugin-apscheduler = "~0.4.0"
+nonebot-plugin-htmlrender = "~0.3.1"
+nonebot2 = { version = "~2.3.0", extras = ["fastapi", "httpx", "websockets"] }
+numpy = "<2.0.0"
+packaging = "~23.1"
+psutil = "~5.9.8"
+py-cpuinfo = "~9.0.0"
+pydantic = "~2.7.0"
+Pygments = "~2.17.2"
+python-dotenv = "~1.0.1"
+pytest = "~8.3.1"
+pytz = "~2024.1"
+PyYAML = "~6.0.1"
+requests = "~2.31.0"
+starlette = "~0.36.3"
+watchdog = "~4.0.0"
+
+
+[[tool.poetry.source]]
+name = "tuna"
+url = "https://pypi.tuna.tsinghua.edu.cn/simple"
+
+[tool.nonebot]
+
+[project.urls]
+homepage = "https://bot.liteyuki.icu"
+repository = "https://gitee.com/TriM-Organization/LiteyukiBot-TriM"
+documentation = "https://bot.liteyuki.icu"
+
diff --git a/requirements.txt b/requirements.txt
index de0b93f..da4db6d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -23,9 +23,9 @@ loguru~=0.7.2
importlib_metadata~=7.0.2
requests~=2.31.0
watchdog~=4.0.0
-pillow~=10.2.0
+pillow~=10.0.0
jieba~=0.42.1
-pip~=23.2.1
+aiosqlite3~=0.3.0
fastapi~=0.110.0
python-dotenv~=1.0.1
nonebot_plugin_session
diff --git a/src/liteyuki/bot/__init__.py b/src/liteyuki/bot/__init__.py
deleted file mode 100644
index e8e8744..0000000
--- a/src/liteyuki/bot/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-import abc
-
-
-class Bot(abc.ABC):
- def __init__(self):
- pass
\ No newline at end of file
diff --git a/src/liteyuki/plugins/process_manager/__init__.py b/src/liteyuki/plugins/process_manager/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/liteyuki/plugins/resource_loader/__init__.py b/src/liteyuki/plugins/resource_loader/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/liteyuki_main/__init__.py b/src/liteyuki_main/__init__.py
index b54fb04..20e9f3a 100644
--- a/src/liteyuki_main/__init__.py
+++ b/src/liteyuki_main/__init__.py
@@ -11,24 +11,15 @@ __plugin_meta__ = PluginMetadata(
usage="",
homepage="https://github.com/snowykami/LiteyukiBot",
extra={
- "liteyuki" : True,
- "toggleable": False,
- }
+ "liteyuki": True,
+ "toggleable": False,
+ },
)
from ..utils.base.language import Language, get_default_lang_code
-print("\033[34m" + r"""
- __ ______ ________ ________ __ __ __ __ __ __ ______
-/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
-$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
-$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
-$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
-$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
-$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
-$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
-$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
-""" + "\033[0m")
sys_lang = Language(get_default_lang_code())
-nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
+nonebot.logger.info(
+ sys_lang.get("main.current_language", LANG=sys_lang.get("language.name"))
+)
diff --git a/src/liteyuki_main/core.py b/src/liteyuki_main/core.py
index 1619560..addec68 100644
--- a/src/liteyuki_main/core.py
+++ b/src/liteyuki_main/core.py
@@ -16,9 +16,12 @@ from src.utils.base.data_manager import StoredConfig, TempConfig, common_db
from src.utils.base.language import get_user_lang
from src.utils.base.ly_typing import T_Bot, T_MessageEvent
from src.utils.message.message import MarkdownMessage as md, broadcast_to_superusers
-from src.utils.base.reloader import Reloader
+
+# from src.liteyuki.core import Reloader
from src.utils import event as event_utils, satori_utils
+from liteyuki.core import ProcessingManager
from .api import update_liteyuki
+from liteyuki.bot import get_bot
from ..utils.base.ly_function import get_function
require("nonebot_plugin_alconna")
@@ -115,7 +118,9 @@ async def _(matcher: Matcher, bot: T_Bot, event: T_MessageEvent):
)
common_db.save(temp_data)
- Reloader.reload(0)
+ # Reloader.reload(0)
+ bot = get_bot()
+ bot.restart()
@on_alconna(
@@ -314,7 +319,6 @@ async def _(result: Arparma, bot: T_Bot, event: T_MessageEvent, matcher: Matcher
result = str(e)
args_show = "\n".join("- %s: %s" % (k, v) for k, v in args_dict.items())
- print(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
await matcher.finish(f"API: {api_name}\n\nArgs: \n{args_show}\n\nResult: {result}")
@@ -376,12 +380,19 @@ async def test_for_md_image(bot: T_Bot, api: str, data: dict):
@driver.on_startup
async def on_startup():
- temp_data = common_db.where_one(TempConfig(), default=TempConfig())
- # 储存重启信息
- if temp_data.data.get("reload", False):
- delta_time = time.time() - temp_data.data.get("reload_time", 0)
- temp_data.data["delta_time"] = delta_time
- common_db.save(temp_data) # 更新数据
+ # temp_data = common_db.where_one(TempConfig(), default=TempConfig())
+ # # 储存重启信息
+ # if temp_data.data.get("reload", False):
+ # delta_time = time.time() - temp_data.data.get("reload_time", 0)
+ # temp_data.data["delta_time"] = delta_time
+ # common_db.save(temp_data) # 更新数据
+ """
+ 该部分迁移至轻雪生命周期
+ Returns:
+
+ """
+
+ pass
@driver.on_shutdown
@@ -407,7 +418,7 @@ async def _(bot: T_Bot):
if isinstance(bot, satori.Bot):
await bot.send_message(
channel_id=reload_session_id,
- message="灵温 重载耗时 %.2f 秒" % delta_time,
+ message="轻雪核心 重载耗时 {:.2f} 秒\n*此数据仅作参考,具体计时请以实际为准\n灵温 预计体感重载耗时 {:.2f} 秒".format(delta_time,time.time() - temp_data.data.get("reload_time", 0)),
)
else:
await bot.call_api(
@@ -415,7 +426,7 @@ async def _(bot: T_Bot):
message_type=reload_session_type,
user_id=reload_session_id,
group_id=reload_session_id,
- message="灵温 重载耗时 %.2f 秒" % delta_time,
+ message="轻雪核心 重载耗时 {:.2f} 秒\n*此数据仅作参考,具体计时请以实际为准\n灵温 预计体感重载耗时 {:.2f} 秒".format(delta_time,time.time() - temp_data.data.get("reload_time", 0)),
)
@@ -426,9 +437,9 @@ async def every_day_update():
result, logs = update_liteyuki()
pip.main(["install", "-r", "requirements.txt"])
if result:
- await broadcast_to_superusers(f"灵温已更新: ```\n{logs}\n```")
- nonebot.logger.info(f"灵温已更新: {logs}")
- Reloader.reload(5)
+ await broadcast_to_superusers(f"灵温已更新:```\n{logs}\n```")
+ nonebot.logger.info(f"灵温已更新:{logs}")
+ ProcessingManager.restart(3)
else:
nonebot.logger.info(logs)
diff --git a/src/liteyuki_main/dev.py b/src/liteyuki_main/dev.py
index 72eb330..052ea56 100644
--- a/src/liteyuki_main/dev.py
+++ b/src/liteyuki_main/dev.py
@@ -2,28 +2,28 @@ import nonebot
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
+from liteyuki.bot import get_bot
from src.utils.base.config import get_config
-from src.utils.base.reloader import Reloader
+from liteyuki.core import ProcessingManager
from src.utils.base.resource import load_resources
if get_config("debug", False):
+ liteyuki_bot = get_bot()
+
src_directories = (
"src/liteyuki_main",
"src/plugins",
"src/utils",
)
- src_excludes_extensions = (
- "pyc",
- )
+ src_excludes_extensions = ("pyc",)
res_directories = (
"src/resources",
"resources",
)
- nonebot.logger.info("Liteyuki Reload enabled, watching for file changes...")
-
+ nonebot.logger.info("已启用 Liteyuki Reload ,正在监测文件变动。")
class CodeModifiedHandler(FileSystemEventHandler):
"""
@@ -31,12 +31,14 @@ if get_config("debug", False):
"""
def on_modified(self, event):
- if event.src_path.endswith(
- src_excludes_extensions) or event.is_directory or "__pycache__" in event.src_path:
+ if (
+ event.src_path.endswith(src_excludes_extensions)
+ or event.is_directory
+ or "__pycache__" in event.src_path
+ ):
return
- nonebot.logger.info(f"{event.src_path} modified, reloading bot...")
- Reloader.reload()
-
+ nonebot.logger.info(f"文件 {event.src_path} 变更,正在重载…")
+ liteyuki_bot.restart()
class ResourceModifiedHandler(FileSystemEventHandler):
"""
@@ -44,10 +46,9 @@ if get_config("debug", False):
"""
def on_modified(self, event):
- nonebot.logger.info(f"{event.src_path} modified, reloading resource...")
+ nonebot.logger.info(f"资源 {event.src_path} 变更,重载资源包…")
load_resources()
-
code_modified_handler = CodeModifiedHandler()
resource_modified_handle = ResourceModifiedHandler()
diff --git a/src/liteyuki_main/loader.py b/src/liteyuki_main/loader.py
index a09393c..eb644d4 100644
--- a/src/liteyuki_main/loader.py
+++ b/src/liteyuki_main/loader.py
@@ -6,10 +6,13 @@ from src.utils.base.data_manager import InstalledPlugin, plugin_db
from src.utils.base.resource import load_resources
from src.utils.message.tools import check_for_package
+from liteyuki import get_bot
+
load_resources()
init_log()
driver = get_driver()
+liteyuki_bot = get_bot()
@driver.on_startup
@@ -18,14 +21,47 @@ async def load_plugins():
# 从数据库读取已安装的插件
if not get_config("safe_mode", False):
# 安全模式下,不加载插件
- installed_plugins: list[InstalledPlugin] = plugin_db.where_all(InstalledPlugin())
+ installed_plugins: list[InstalledPlugin] = plugin_db.where_all(
+ InstalledPlugin()
+ )
if installed_plugins:
for installed_plugin in installed_plugins:
if not check_for_package(installed_plugin.module_name):
nonebot.logger.error(
- f"{installed_plugin.module_name} not installed, but in loading database. please run `npm fixup` in chat to reinstall it.")
+ f"插件 {installed_plugin.module_name} 在加载列表中但未安装。请使用超管账户对机器人发送 `npm fixup` 以重新安装。"
+ )
else:
nonebot.load_plugin(installed_plugin.module_name)
nonebot.plugin.load_plugins("plugins")
else:
- nonebot.logger.info("Safe mode is on, no plugin loaded.")
+ nonebot.logger.info("安全模式已启动,未加载任何插件。")
+
+
+@liteyuki_bot.on_before_start
+async def _():
+ print("启动前")
+
+
+@liteyuki_bot.on_after_start
+async def _():
+ print("启动后")
+
+
+@liteyuki_bot.on_before_shutdown
+async def _():
+ print("停止前")
+
+
+@liteyuki_bot.on_after_shutdown
+async def _():
+ print("停止后")
+
+
+@liteyuki_bot.on_before_restart
+async def _():
+ print("重启前")
+
+
+@liteyuki_bot.on_after_restart
+async def _():
+ print("重启后")
diff --git a/src/plugins/liteyuki_crt_utils/__init__.py b/src/plugins/liteyuki_crt_utils/__init__.py
index 21c8066..4ef30f3 100644
--- a/src/plugins/liteyuki_crt_utils/__init__.py
+++ b/src/plugins/liteyuki_crt_utils/__init__.py
@@ -1,4 +1,7 @@
+import multiprocessing
+
from nonebot.plugin import PluginMetadata
+from liteyuki.plugin import get_loaded_plugins
from .rt_guide import *
from .crt_matchers import *
@@ -14,3 +17,5 @@ __plugin_meta__ = PluginMetadata(
"default_enable": True,
}
)
+
+print("已加载插件:", len(get_loaded_plugins()))
\ No newline at end of file
diff --git a/src/plugins/liteyuki_pacman/npm.py b/src/plugins/liteyuki_pacman/npm.py
index eb251e2..c844da8 100644
--- a/src/plugins/liteyuki_pacman/npm.py
+++ b/src/plugins/liteyuki_pacman/npm.py
@@ -554,7 +554,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, npm: Matcher):
Subcommand(
disable,
Args["group_id", str, None],
- alias=["d", "停用"],
+ alias=["d", "停用","禁用"],
),
),
permission=SUPERUSER | GROUP_OWNER | GROUP_ADMIN,
diff --git a/src/plugins/liteyuki_webdash/__init__.py b/src/plugins/webdash/__init__.py
similarity index 97%
rename from src/plugins/liteyuki_webdash/__init__.py
rename to src/plugins/webdash/__init__.py
index 6f46e9a..faab14b 100644
--- a/src/plugins/liteyuki_webdash/__init__.py
+++ b/src/plugins/webdash/__init__.py
@@ -1,21 +1,21 @@
-from nonebot.plugin import PluginMetadata
-
-from .main import *
-
-__author__ = "snowykami"
-__plugin_meta__ = PluginMetadata(
- name="网页监控面板",
- description="网页监控面板,用于查看机器人的状态和信息",
- usage=(
- "访问 127.0.0.1:port 查看机器人的状态信息\n"
- "stat msg -g|--group [group_id] 查看群的统计信息,不带参数为全群\n"
- "配置项:custom_domain,自定义域名,通常对外用,内网无需"
- ),
- type="application",
- homepage="https://github.com/snowykami/LiteyukiBot",
- extra={
- "liteyuki" : True,
- "toggleable" : False,
- "default_enable": True,
- }
-)
+from nonebot.plugin import PluginMetadata
+
+from .main import *
+
+__author__ = "snowykami"
+__plugin_meta__ = PluginMetadata(
+ name="网页监控面板",
+ description="网页监控面板,用于查看机器人的状态和信息",
+ usage=(
+ "访问 127.0.0.1:port 查看机器人的状态信息\n"
+ "stat msg -g|--group [group_id] 查看群的统计信息,不带参数为全群\n"
+ "配置项:custom_domain,自定义域名,通常对外用,内网无需"
+ ),
+ type="application",
+ homepage="https://github.com/snowykami/LiteyukiBot",
+ extra={
+ "liteyuki" : True,
+ "toggleable" : False,
+ "default_enable": True,
+ }
+)
diff --git a/src/plugins/liteyuki_webdash/common.py b/src/plugins/webdash/common.py
similarity index 96%
rename from src/plugins/liteyuki_webdash/common.py
rename to src/plugins/webdash/common.py
index 29b4ebf..b9abd37 100644
--- a/src/plugins/liteyuki_webdash/common.py
+++ b/src/plugins/webdash/common.py
@@ -1,4 +1,4 @@
-from fastapi import FastAPI
-from nonebot import get_app
-
+from fastapi import FastAPI
+from nonebot import get_app
+
app: FastAPI = get_app()
\ No newline at end of file
diff --git a/src/plugins/liteyuki_webdash/main.py b/src/plugins/webdash/main.py
similarity index 94%
rename from src/plugins/liteyuki_webdash/main.py
rename to src/plugins/webdash/main.py
index 3be44c2..40506b1 100644
--- a/src/plugins/liteyuki_webdash/main.py
+++ b/src/plugins/webdash/main.py
@@ -1,10 +1,10 @@
-from fastapi import FastAPI
-from nonebot import get_app
-from .restful_api import *
-
-
-@app.get("/ping")
-async def root():
- return {
- "message": "pong"
- }
+from fastapi import FastAPI
+from nonebot import get_app
+from .restful_api import *
+
+
+@app.get("/ping")
+async def root():
+ return {
+ "message": "pong"
+ }
diff --git a/src/plugins/liteyuki_webdash/restful_api.py b/src/plugins/webdash/restful_api.py
similarity index 95%
rename from src/plugins/liteyuki_webdash/restful_api.py
rename to src/plugins/webdash/restful_api.py
index 3235b38..873cb69 100644
--- a/src/plugins/liteyuki_webdash/restful_api.py
+++ b/src/plugins/webdash/restful_api.py
@@ -1,24 +1,24 @@
-from fastapi import FastAPI, APIRouter
-from .common import *
-
-device_info_router = APIRouter(prefix="/api/device-info")
-bot_info_router = APIRouter(prefix="/api/bot-info")
-
-
-@device_info_router.get("/")
-async def device_info():
- print("Hello Device Info")
- return {
- "message": "Hello Device Info"
- }
-
-
-@bot_info_router.get("/")
-async def bot_info():
- return {
- "message": "Hello Bot Info"
- }
-
-
-app.include_router(device_info_router)
-app.include_router(bot_info_router)
+from fastapi import FastAPI, APIRouter
+from .common import *
+
+device_info_router = APIRouter(prefix="/api/device-info")
+bot_info_router = APIRouter(prefix="/api/bot-info")
+
+
+@device_info_router.get("/")
+async def device_info():
+ print("Hello Device Info")
+ return {
+ "message": "Hello Device Info"
+ }
+
+
+@bot_info_router.get("/")
+async def bot_info():
+ return {
+ "message": "Hello Bot Info"
+ }
+
+
+app.include_router(device_info_router)
+app.include_router(bot_info_router)
diff --git a/src/test/action_test.py b/src/test/action_test.py
deleted file mode 100644
index 74d7c54..0000000
--- a/src/test/action_test.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import nonebot
-from src.utils import adapter_manager, driver_manager, init
-from src.utils.base.config import load_from_yaml
-from src.utils.base.data_manager import StoredConfig, common_db
-from src.utils.base.ly_api import liteyuki_api
-
-if __name__ == "__mp_main__":
- # Start as multiprocessing
- init()
- store_config: dict = common_db.where_one(StoredConfig(), default=StoredConfig()).config
- static_config = load_from_yaml("config.yml")
- store_config.update(static_config)
- driver_manager.init(config=store_config)
- adapter_manager.init(store_config)
- nonebot.init(**store_config)
- adapter_manager.register()
- try:
- nonebot.load_plugin("liteyuki.liteyuki_main")
- nonebot.load_from_toml("pyproject.toml")
- except BaseException as e:
- if not isinstance(e, KeyboardInterrupt):
- nonebot.logger.error(f"An error occurred: {e}, Bug will be reported automatically.")
- liteyuki_api.bug_report(str(e.__repr__()))
-
-if __name__ == "__main__":
- # Start as __main__
- from src.utils.base.reloader import Reloader
-
- nonebot.run()
diff --git a/src/utils/__init__.py b/src/utils/__init__.py
index b8502b3..a7e5e06 100644
--- a/src/utils/__init__.py
+++ b/src/utils/__init__.py
@@ -6,8 +6,8 @@ import time
import nonebot
-__NAME__ = "LiteyukiBot-TriMO"
-__VERSION__ = "6.3.3" # 60201
+__NAME__ = "尹灵温|轻雪-睿乐"
+__VERSION__ = "6.3.4" # 60201
# __VERSION_I__ = 99060303
import requests
diff --git a/src/utils/adapter_manager/satori.py b/src/utils/adapter_manager/satori.py
index c5edc80..6ddef50 100644
--- a/src/utils/adapter_manager/satori.py
+++ b/src/utils/adapter_manager/satori.py
@@ -7,14 +7,14 @@ from nonebot.adapters import satori
def init(config: dict):
if config.get("satori", None) is None:
- nonebot.logger.info("Satori config not found, skip Satori init.")
+ nonebot.logger.info("未查见 Satori 的配置文档,将跳过 Satori 初始化")
return None
satori_config = config.get("satori")
if not satori_config.get("enable", False):
- nonebot.logger.info("Satori not enabled, skip Satori init.")
+ nonebot.logger.info("未启用 Satori ,将跳过 Satori 初始化")
return None
if os.getenv("SATORI_CLIENTS", None) is not None:
- nonebot.logger.info("Satori clients already set in environment variable, skip.")
+ nonebot.logger.info("Satori 客户端已设入环境变量,跳过此步。")
os.environ["SATORI_CLIENTS"] = json.dumps(satori_config.get("hosts", []), ensure_ascii=False)
config['satori_clients'] = satori_config.get("hosts", [])
return
diff --git a/src/utils/base/config.py b/src/utils/base/config.py
index a53f8f9..84045a9 100644
--- a/src/utils/base/config.py
+++ b/src/utils/base/config.py
@@ -20,25 +20,26 @@ class SatoriNodeConfig(BaseModel):
class SatoriConfig(BaseModel):
- comment: str = "These features are still in development. Do not enable in production environment."
+ comment: str = "此皆正处于开发之中,切勿在生产环境中启用。"
enable: bool = False
hosts: List[SatoriNodeConfig] = [SatoriNodeConfig()]
class BasicConfig(BaseModel):
host: str = "127.0.0.1"
- port: int = 20216
+ port: int = 20247
superusers: list[str] = []
command_start: list[str] = ["/", ""]
- nickname: list[str] = [f"LiteyukiBot-{random_hex_string(6)}"]
+ nickname: list[str] = [f"灵温-{random_hex_string(6)}"]
satori: SatoriConfig = SatoriConfig()
+ data_path: str = "data/liteyuki"
def load_from_yaml(file: str) -> dict:
global config
nonebot.logger.debug("Loading config from %s" % file)
if not os.path.exists(file):
- nonebot.logger.warning(f"Config file {file} not found, created default config, please modify it and restart")
+ nonebot.logger.warning(f"未找到配置文件 {file} ,已创建默认配置,请修改后重启。")
with open(file, "w", encoding="utf-8") as f:
yaml.dump(BasicConfig().dict(), f, default_flow_style=False)
@@ -46,7 +47,7 @@ def load_from_yaml(file: str) -> dict:
conf = init_conf(yaml.load(f, Loader=yaml.FullLoader))
config = conf
if conf is None:
- nonebot.logger.warning(f"Config file {file} is empty, use default config. please modify it and restart")
+ nonebot.logger.warning(f"配置文件 {file} 为空,已创建默认配置,请修改后重启。")
conf = BasicConfig().dict()
return conf
@@ -95,6 +96,8 @@ def init_conf(conf: dict) -> dict:
"""
# 若command_start中无"",则添加必要命令头,开启alconna_use_command_start防止冲突
- if "" not in conf.get("command_start", []):
- conf["alconna_use_command_start"] = True
+ # 以下内容由于issue #53 被注释
+ # if "" not in conf.get("command_start", []):
+ # conf["alconna_use_command_start"] = True
return conf
+ pass
diff --git a/src/utils/base/ly_api.py b/src/utils/base/ly_api.py
index 9b8d16d..ffbf18a 100644
--- a/src/utils/base/ly_api.py
+++ b/src/utils/base/ly_api.py
@@ -20,8 +20,9 @@ class LiteyukiAPI:
self.data = json.loads(f.read())
self.liteyuki_id = self.data.get("liteyuki_id")
self.report = load_from_yaml("config.yml").get("auto_report", True)
+
if self.report:
- nonebot.logger.info("Auto bug report is enabled")
+ nonebot.logger.info("已启用自动上报")
@property
def device_info(self) -> dict:
@@ -37,10 +38,10 @@ class LiteyukiAPI:
"python" : f"{platform.python_implementation()} {platform.python_version()}",
"os" : f"{platform.system()} {platform.version()} {platform.machine()}",
"cpu" : f"{psutil.cpu_count(logical=False)}c{psutil.cpu_count()}t{psutil.cpu_freq().current}MHz",
- "memory_total": f"{psutil.virtual_memory().total / 1024 / 1024 / 1024:.2f}GB",
- "memory_used" : f"{psutil.virtual_memory().used / 1024 / 1024 / 1024:.2f}GB",
- "memory_bot" : f"{psutil.Process(os.getpid()).memory_info().rss / 1024 / 1024:.2f}MB",
- "disk" : f"{psutil.disk_usage('/').total / 1024 / 1024 / 1024:.2f}GB"
+ "memory_total": f"{psutil.virtual_memory().total / 1024 ** 3:.2f}吉字节",
+ "memory_used" : f"{psutil.virtual_memory().used / 1024 ** 3:.2f}吉字节",
+ "memory_bot" : f"{psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2:.2f}兆字节",
+ "disk" : f"{psutil.disk_usage('/').total / 1024 ** 3:.2f}吉字节"
}
def bug_report(self, content: str):
@@ -53,7 +54,7 @@ class LiteyukiAPI:
"""
if self.report:
- nonebot.logger.warning(f"Reporting bug...: {content}")
+ nonebot.logger.warning(f"正在上报查误:{content}")
url = "https://api.liteyuki.icu/bug_report"
data = {
"liteyuki_id": self.liteyuki_id,
@@ -62,11 +63,11 @@ class LiteyukiAPI:
}
resp = requests.post(url, json=data)
if resp.status_code == 200:
- nonebot.logger.success(f"Bug report sent successfully, report_id: {resp.json().get('report_id')}")
+ nonebot.logger.success(f"成功上报差误信息,报文ID为:{resp.json().get('report_id')}")
else:
- nonebot.logger.error(f"Bug report failed: {resp.text}")
+ nonebot.logger.error(f"差误上报错误:{resp.text}")
else:
- nonebot.logger.warning(f"Bug report is disabled: {content}")
+ nonebot.logger.warning(f"已禁用自动上报:{content}")
async def heartbeat_report(self):
"""
@@ -77,14 +78,11 @@ class LiteyukiAPI:
url = "https://api.liteyuki.icu/heartbeat"
data = {
"liteyuki_id": self.liteyuki_id,
- "version": __VERSION__,
+ "version" : __VERSION__,
}
async with aiohttp.ClientSession() as session:
async with session.post(url, json=data) as resp:
if resp.status == 200:
- nonebot.logger.success("Heartbeat sent successfully")
+ nonebot.logger.success("心跳成功送达。")
else:
- nonebot.logger.error(f"Heartbeat failed: {await resp.text()}")
-
-
-liteyuki_api = LiteyukiAPI()
+ nonebot.logger.error(f"休克:{await resp.text()}")
\ No newline at end of file
diff --git a/src/utils/driver_manager/auto_set_env.py b/src/utils/driver_manager/auto_set_env.py
index f3b2f20..a069580 100644
--- a/src/utils/driver_manager/auto_set_env.py
+++ b/src/utils/driver_manager/auto_set_env.py
@@ -9,13 +9,12 @@ from .defines import *
def auto_set_env(config: dict):
dotenv.load_dotenv(".env")
if os.getenv("DRIVER", None) is not None:
- print(os.getenv("DRIVER"))
- nonebot.logger.info("Driver already set in environment variable, skip auto configure.")
+ nonebot.logger.info("Driver 已设入环境变量中,将跳过自动配置环节。")
return
if config.get("satori", {'enable': False}).get("enable", False):
os.environ["DRIVER"] = get_driver_string(ASGI_DRIVER, HTTPX_DRIVER, WEBSOCKETS_DRIVER)
- nonebot.logger.info("Enable Satori, set driver to ASGI+HTTPX+WEBSOCKETS")
+ nonebot.logger.info("已启用 Satori,将 driver 设为 ASGI+HTTPX+WEBSOCKETS")
else:
os.environ["DRIVER"] = get_driver_string(ASGI_DRIVER)
- nonebot.logger.info("Disable Satori, set driver to ASGI")
+ nonebot.logger.info("已禁用 Satori,将 driver 设为 ASGI")
return
diff --git a/src/utils/network/__init__.py b/src/utils/io/__init__.py
similarity index 87%
rename from src/utils/network/__init__.py
rename to src/utils/io/__init__.py
index 62c4eac..bc30c16 100644
--- a/src/utils/network/__init__.py
+++ b/src/utils/io/__init__.py
@@ -1,15 +1,17 @@
-from aiohttp import ClientSession
-
-
-async def simple_get(url: str) -> str:
- """
- 简单异步get请求
- Args:
- url:
-
- Returns:
-
- """
- async with ClientSession() as session:
- async with session.get(url) as resp:
- return await resp.text()
+from aiohttp import ClientSession
+
+from .net import *
+from .file import *
+
+
+async def simple_get(url: str) -> str:
+ """
+ 简单异步get请求
+ Args:
+ url:
+
+ Returns:
+ """
+ async with ClientSession() as session:
+ async with session.get(url) as resp:
+ return await resp.text()
diff --git a/src/utils/io/file.py b/src/utils/io/file.py
new file mode 100644
index 0000000..c25a9c7
--- /dev/null
+++ b/src/utils/io/file.py
@@ -0,0 +1,29 @@
+import aiofiles
+
+
+async def write_file(
+ file_path: str,
+ content: str | bytes,
+ mode: str = "w"
+):
+ """
+ 写入文件
+ Args:
+ mode: 写入模式
+ file_path: 文件路径
+ content: 内容
+ """
+ async with aiofiles.open(file_path, mode) as f:
+ await f.write(content)
+
+
+async def read_file(file_path: str, mode: str = "r") -> str:
+ """
+ 读取文件
+ Args:
+ file_path: 文件路径
+ mode: 读取模式
+ Returns:
+ """
+ async with aiofiles.open(file_path, mode) as f:
+ return await f.read()
diff --git a/src/utils/io/net.py b/src/utils/io/net.py
new file mode 100644
index 0000000..c346aa4
--- /dev/null
+++ b/src/utils/io/net.py
@@ -0,0 +1,12 @@
+async def fetch(url: str) -> str:
+ """
+ 异步get请求
+ Args:
+ url:
+
+ Returns:
+
+ """
+ async with ClientSession() as session:
+ async with session.get(url) as resp:
+ return await resp.text()
diff --git a/test/test_core.py b/test/test_core.py
new file mode 100644
index 0000000..81da20e
--- /dev/null
+++ b/test/test_core.py
@@ -0,0 +1,5 @@
+from src.liteyuki import LiteyukiBot
+
+if __name__ == "__main__":
+ lyb = LiteyukiBot()
+ lyb.run()
\ No newline at end of file
diff --git a/src/liteyuki/plugins/__init__.py b/test/test_lyapi.py
similarity index 100%
rename from src/liteyuki/plugins/__init__.py
rename to test/test_lyapi.py
diff --git a/src/liteyuki/plugins/plugin_loader/__init__.py b/test/test_lyfunc.py
similarity index 100%
rename from src/liteyuki/plugins/plugin_loader/__init__.py
rename to test/test_lyfunc.py