⚡ 添加进程及生命周期管理器,添加轻雪框架支持
This commit is contained in:
@ -1,6 +0,0 @@
|
||||
import abc
|
||||
|
||||
|
||||
class Bot(abc.ABC):
|
||||
def __init__(self):
|
||||
pass
|
@ -1,10 +0,0 @@
|
||||
"""exception模块包含了liteyuki运行中的所有错误
|
||||
"""
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class LiteyukiException(BaseException):
|
||||
"""Liteyuki的异常基类。"""
|
||||
def __str__(self) -> str:
|
||||
return self.__repr__()
|
@ -18,17 +18,6 @@ __plugin_meta__ = PluginMetadata(
|
||||
|
||||
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")))
|
@ -16,9 +16,11 @@ 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")
|
||||
@ -92,7 +94,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(
|
||||
@ -281,7 +285,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}")
|
||||
|
||||
|
||||
@ -371,7 +374,7 @@ async def every_day_update():
|
||||
if result:
|
||||
await broadcast_to_superusers(f"Liteyuki updated: ```\n{logs}\n```")
|
||||
nonebot.logger.info(f"Liteyuki updated: {logs}")
|
||||
Reloader.reload(5)
|
||||
ProcessingManager.restart()
|
||||
else:
|
||||
nonebot.logger.info(logs)
|
||||
|
||||
|
@ -2,24 +2,27 @@ 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/liteyuki_main",
|
||||
"src/plugins",
|
||||
"src/utils",
|
||||
)
|
||||
src_excludes_extensions = (
|
||||
"pyc",
|
||||
"pyc",
|
||||
)
|
||||
|
||||
res_directories = (
|
||||
"src/resources",
|
||||
"resources",
|
||||
"src/resources",
|
||||
"resources",
|
||||
)
|
||||
|
||||
nonebot.logger.info("Liteyuki Reload enabled, watching for file changes...")
|
||||
@ -35,7 +38,7 @@ if get_config("debug", False):
|
||||
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()
|
||||
liteyuki_bot.restart()
|
||||
|
||||
|
||||
class ResourceModifiedHandler(FileSystemEventHandler):
|
||||
|
@ -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
|
||||
@ -29,3 +32,33 @@ async def load_plugins():
|
||||
nonebot.plugin.load_plugins("plugins")
|
||||
else:
|
||||
nonebot.logger.info("Safe mode is on, no plugin loaded.")
|
||||
|
||||
|
||||
@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("重启后")
|
||||
|
@ -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("Loaded plugins:", len(get_loaded_plugins()))
|
@ -1,4 +1,6 @@
|
||||
from nonebot.plugin import PluginMetadata
|
||||
from .npm import *
|
||||
from .rpm import *
|
||||
|
||||
__author__ = "snowykami"
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
|
@ -0,0 +1,69 @@
|
||||
# npm update/upgrade
|
||||
# npm search
|
||||
# npm install/uninstall
|
||||
# npm list
|
||||
from nonebot import require
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
|
||||
from nonebot_plugin_alconna import (
|
||||
on_alconna,
|
||||
Alconna,
|
||||
Args,
|
||||
MultiVar,
|
||||
Subcommand,
|
||||
Option
|
||||
)
|
||||
|
||||
"""包管理器alc"""
|
||||
npm_alc = on_alconna(
|
||||
aliases={"插件", "nonebot-plugin-manager"},
|
||||
command=Alconna(
|
||||
"npm",
|
||||
Subcommand(
|
||||
"list",
|
||||
Args["page", int, 1]["num", int, 10],
|
||||
alias={"ls", "列表", "列出"},
|
||||
dest="list installed plugins",
|
||||
help_text="列出已安装插件",
|
||||
),
|
||||
Subcommand(
|
||||
"search",
|
||||
Args["keywords", MultiVar(str)],
|
||||
alias=["s", "搜索"],
|
||||
dest="search plugins",
|
||||
help_text="搜索本地商店插件,需自行更新",
|
||||
),
|
||||
Subcommand(
|
||||
"install",
|
||||
Args["package_name", str],
|
||||
alias=["i", "安装"],
|
||||
dest="install plugin",
|
||||
help_text="安装插件",
|
||||
),
|
||||
Subcommand(
|
||||
"uninstall",
|
||||
Args["package_name", str],
|
||||
alias=["u", "卸载"],
|
||||
dest="uninstall plugin",
|
||||
help_text="卸载插件",
|
||||
),
|
||||
Subcommand(
|
||||
"update",
|
||||
alias={"更新"},
|
||||
dest="update local store index",
|
||||
help_text="更新本地索引库",
|
||||
),
|
||||
Subcommand(
|
||||
"upgrade",
|
||||
Args["package_name", str],
|
||||
Option(
|
||||
"package_name",
|
||||
Args["package_name", str, None], # Optional
|
||||
),
|
||||
alias={"升级"},
|
||||
dest="upgrade all plugins without package name",
|
||||
help_text="升级插件",
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -1,6 +1,28 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import aiofiles
|
||||
from pydantic import BaseModel
|
||||
|
||||
from src.utils.base.config import get_config
|
||||
from src.utils.io import fetch
|
||||
|
||||
|
||||
class Session:
|
||||
def __init__(self, session_type: str, session_id: int | str):
|
||||
self.session_type = session_type
|
||||
self.session_id = session_id
|
||||
|
||||
|
||||
async def update_local_store_index() -> list[str]:
|
||||
"""
|
||||
更新本地插件索引库
|
||||
Returns:
|
||||
新增插件包名列表list[str]
|
||||
"""
|
||||
url = "https://registry.nonebot.dev/plugins.json"
|
||||
save_file = Path(get_config("data_path"), "data/liteyuki") / "pacman/plugins.json"
|
||||
raw_text = await fetch(url)
|
||||
data = json.loads(raw_text)
|
||||
with aiofiles.open(save_file, "w") as f:
|
||||
await f.write(raw_text)
|
||||
|
@ -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()
|
@ -32,6 +32,7 @@ class BasicConfig(BaseModel):
|
||||
command_start: list[str] = ["/", ""]
|
||||
nickname: list[str] = [f"LiteyukiBot-{random_hex_string(6)}"]
|
||||
satori: SatoriConfig = SatoriConfig()
|
||||
data_path: str = "data/liteyuki"
|
||||
|
||||
|
||||
def load_from_yaml(file: str) -> dict:
|
||||
@ -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
|
||||
|
@ -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("Auto report enabled")
|
||||
|
||||
@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}GB",
|
||||
"memory_used" : f"{psutil.virtual_memory().used / 1024 ** 3:.2f}GB",
|
||||
"memory_bot" : f"{psutil.Process(os.getpid()).memory_info().rss / 1024 ** 2:.2f}MB",
|
||||
"disk" : f"{psutil.disk_usage('/').total / 1024 ** 3:.2f}GB"
|
||||
}
|
||||
|
||||
def bug_report(self, content: str):
|
||||
@ -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")
|
||||
else:
|
||||
nonebot.logger.error(f"Heartbeat failed: {await resp.text()}")
|
||||
|
||||
|
||||
liteyuki_api = LiteyukiAPI()
|
||||
nonebot.logger.error(f"Heartbeat failed: {await resp.text()}")
|
@ -1,61 +0,0 @@
|
||||
import threading
|
||||
from multiprocessing import get_context
|
||||
|
||||
import nonebot
|
||||
from nonebot import logger
|
||||
|
||||
reboot_grace_time_limit: int = 20
|
||||
|
||||
_nb_run = nonebot.run
|
||||
|
||||
|
||||
class Reloader:
|
||||
event: threading.Event = None
|
||||
|
||||
@classmethod
|
||||
def reload(cls, delay: int = 0):
|
||||
if cls.event is None:
|
||||
raise RuntimeError()
|
||||
if delay > 0:
|
||||
threading.Timer(delay, function=cls.event.set).start()
|
||||
return
|
||||
cls.event.set()
|
||||
|
||||
|
||||
def _run(ev: threading.Event, *args, **kwargs):
|
||||
Reloader.event = ev
|
||||
_nb_run(*args, **kwargs)
|
||||
|
||||
|
||||
def run(*args, **kwargs):
|
||||
should_exit = False
|
||||
ctx = get_context("spawn")
|
||||
while not should_exit:
|
||||
event = ctx.Event()
|
||||
process = ctx.Process(
|
||||
target=_run,
|
||||
args=(
|
||||
event,
|
||||
*args,
|
||||
),
|
||||
kwargs=kwargs,
|
||||
)
|
||||
process.start()
|
||||
while not should_exit:
|
||||
if event.wait(1):
|
||||
logger.info("Receive reboot event")
|
||||
process.terminate()
|
||||
process.join(reboot_grace_time_limit)
|
||||
if process.is_alive():
|
||||
logger.warning(
|
||||
f"Cannot shutdown gracefully in {reboot_grace_time_limit} second, force kill process."
|
||||
)
|
||||
process.kill()
|
||||
break
|
||||
elif process.is_alive():
|
||||
continue
|
||||
else:
|
||||
should_exit = True
|
||||
|
||||
|
||||
nonebot.run = run
|
@ -9,7 +9,6 @@ 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.")
|
||||
return
|
||||
if config.get("satori", {'enable': False}).get("enable", False):
|
||||
|
@ -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()
|
29
src/utils/io/file.py
Normal file
29
src/utils/io/file.py
Normal file
@ -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()
|
12
src/utils/io/net.py
Normal file
12
src/utils/io/net.py
Normal file
@ -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()
|
Reference in New Issue
Block a user