mirror of
https://github.com/LiteyukiStudio/LiteyukiBot.git
synced 2024-11-13 14:27:26 +08:00
✨ 新增observer类和开发调试器
This commit is contained in:
parent
83325e63ea
commit
c9157f0e2c
@ -1,4 +1,4 @@
|
||||
FROM python:3.11-bullseye
|
||||
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/python:3.10-slim-bullseye
|
||||
|
||||
ENV TZ Asia/Shanghai
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
nonebot:
|
||||
host: 127.0.0.1
|
||||
port: 20216
|
||||
superusers: ["1234"]
|
||||
command_start: ["", "/"]
|
||||
nickname: [ "liteyuki-dev" ]
|
||||
default_language: zh
|
||||
|
9
docs/deployment/cfg.txt
Normal file
9
docs/deployment/cfg.txt
Normal file
@ -0,0 +1,9 @@
|
||||
# note
|
||||
开发者选项
|
||||
allow_update: true # 是否允许更新
|
||||
log_level: "INFO" # 日志等级
|
||||
log_icon: true # 是否显示日志等级图标(某些控制台字体不可用)
|
||||
auto_report: true # 是否自动上报问题给轻雪服务器
|
||||
auto_update: true # 是否自动更新轻雪,每天4点检查更新
|
||||
safe_mode: false # 安全模式,开启后将不会加载任何第三方插件
|
||||
dev_mode: false # 开发者模式,开启后将会启动看门狗
|
@ -25,14 +25,16 @@ __all__ = [
|
||||
|
||||
class LiteyukiBot:
|
||||
def __init__(self, *args, **kwargs):
|
||||
print_logo()
|
||||
global _BOT_INSTANCE
|
||||
_BOT_INSTANCE = self # 引用
|
||||
|
||||
self.lifespan = Lifespan()
|
||||
|
||||
self.config: dict[str, Any] = kwargs
|
||||
|
||||
self.init(**self.config) # 初始化
|
||||
logger.info("Liteyuki is initializing...")
|
||||
|
||||
self.lifespan = Lifespan()
|
||||
|
||||
self.process_manager: ProcessManager = ProcessManager(bot=self)
|
||||
self.loop = asyncio.new_event_loop()
|
||||
@ -41,19 +43,7 @@ class LiteyukiBot:
|
||||
self.stop_event = threading.Event()
|
||||
self.call_restart_count = 0
|
||||
|
||||
print("\033[34m" + r"""
|
||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
""" + "\033[0m")
|
||||
load_plugins("liteyuki/plugins") # 加载轻雪插件
|
||||
logger.info("Liteyuki is initializing...")
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
@ -75,7 +65,6 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
if self.call_restart_count < 1:
|
||||
executable = sys.executable
|
||||
args = sys.argv
|
||||
@ -103,16 +92,10 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
Returns:
|
||||
|
||||
"""
|
||||
logger.info(f"Stopping process {name}...")
|
||||
self.loop.create_task(self.lifespan.before_process_shutdown()) # 重启前钩子
|
||||
self.loop.create_task(self.lifespan.before_process_shutdown()) # 停止前钩子
|
||||
|
||||
self.loop.create_task(self.lifespan.before_shutdown()) # 重启前钩子
|
||||
self.loop.create_task(self.lifespan.before_shutdown()) # 停止前钩子
|
||||
|
||||
# if name:
|
||||
# self.process_manager.terminate(name)
|
||||
# else:
|
||||
# self.process_manager.terminate_all()
|
||||
if name:
|
||||
if name is not None:
|
||||
chan_active = get_channel(f"{name}-active")
|
||||
chan_active.send(1)
|
||||
else:
|
||||
@ -158,17 +141,6 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
注册停止后的函数:未实现
|
||||
@ -180,9 +152,20 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
"""
|
||||
return self.lifespan.on_after_shutdown(func)
|
||||
|
||||
def on_before_restart(self, func: LIFESPAN_FUNC):
|
||||
def on_before_process_shutdown(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册重启前的函数,为子进程重启时调用
|
||||
注册进程停止前的函数,为子进程停止时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
return self.lifespan.on_before_process_shutdown(func)
|
||||
|
||||
def on_before_process_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
注册进程重启前的函数,为子进程重启时调用
|
||||
Args:
|
||||
func:
|
||||
|
||||
@ -190,7 +173,7 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
|
||||
"""
|
||||
|
||||
return self.lifespan.on_before_restart(func)
|
||||
return self.lifespan.on_before_process_restart(func)
|
||||
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC):
|
||||
"""
|
||||
@ -228,13 +211,14 @@ def get_bot() -> Optional[LiteyukiBot]:
|
||||
LiteyukiBot: 当前的轻雪实例
|
||||
"""
|
||||
if IS_MAIN_PROCESS:
|
||||
if _BOT_INSTANCE is None:
|
||||
raise RuntimeError("Liteyuki instance not initialized.")
|
||||
return _BOT_INSTANCE
|
||||
else:
|
||||
# 从多进程上下文中获取
|
||||
pass
|
||||
raise RuntimeError("Can't get bot instance in sub process.")
|
||||
|
||||
|
||||
def get_config(key: str, default: Any = None) -> Any:
|
||||
def get_config(key: str = None, default: Any = None) -> Any:
|
||||
"""
|
||||
获取配置
|
||||
Args:
|
||||
@ -244,7 +228,9 @@ def get_config(key: str, default: Any = None) -> Any:
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
return _BOT_INSTANCE.config.get(key, default)
|
||||
if key is None:
|
||||
return get_bot().config
|
||||
return get_bot().config.get(key, default)
|
||||
|
||||
|
||||
def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any = None) -> Any:
|
||||
@ -258,11 +244,24 @@ def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any = Non
|
||||
Returns:
|
||||
Any: 配置值
|
||||
"""
|
||||
if key in _BOT_INSTANCE.config:
|
||||
return _BOT_INSTANCE.config[key]
|
||||
if key in get_bot().config:
|
||||
return get_bot().config[key]
|
||||
for compat_key in compat_keys:
|
||||
if compat_key in _BOT_INSTANCE.config:
|
||||
if compat_key in get_bot().config:
|
||||
logger.warning(f"Config key {compat_key} will be deprecated, use {key} instead.")
|
||||
return _BOT_INSTANCE.config[compat_key]
|
||||
return get_bot().config[compat_key]
|
||||
return default
|
||||
|
||||
|
||||
def print_logo():
|
||||
print("\033[34m" + r"""
|
||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||
""" + "\033[0m")
|
||||
|
@ -29,10 +29,10 @@ class Lifespan:
|
||||
self._before_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_start_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._before_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._before_process_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_shutdown_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._before_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._before_process_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
self._after_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||
|
||||
self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
|
||||
@ -73,7 +73,7 @@ class Lifespan:
|
||||
self._after_start_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_before_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
def on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册停止前的函数
|
||||
Args:
|
||||
@ -81,7 +81,7 @@ class Lifespan:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_shutdown_funcs.append(func)
|
||||
self._before_process_shutdown_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
@ -97,7 +97,7 @@ class Lifespan:
|
||||
self._after_shutdown_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_before_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
def on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
"""
|
||||
注册重启时的函数
|
||||
Args:
|
||||
@ -105,7 +105,7 @@ class Lifespan:
|
||||
Returns:
|
||||
LIFESPAN_FUNC:
|
||||
"""
|
||||
self._before_restart_funcs.append(func)
|
||||
self._before_process_restart_funcs.append(func)
|
||||
return func
|
||||
|
||||
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||
@ -147,13 +147,13 @@ class Lifespan:
|
||||
logger.debug("Running after_start functions")
|
||||
await self._run_funcs(self._after_start_funcs)
|
||||
|
||||
async def before_shutdown(self) -> None:
|
||||
async def before_process_shutdown(self) -> None:
|
||||
"""
|
||||
停止前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running before_shutdown functions")
|
||||
await self._run_funcs(self._before_shutdown_funcs)
|
||||
await self._run_funcs(self._before_process_shutdown_funcs)
|
||||
|
||||
async def after_shutdown(self) -> None:
|
||||
"""
|
||||
@ -163,13 +163,13 @@ class Lifespan:
|
||||
logger.debug("Running after_shutdown functions")
|
||||
await self._run_funcs(self._after_shutdown_funcs)
|
||||
|
||||
async def before_restart(self) -> None:
|
||||
async def before_process_restart(self) -> None:
|
||||
"""
|
||||
重启前
|
||||
Returns:
|
||||
"""
|
||||
logger.debug("Running before_restart functions")
|
||||
await self._run_funcs(self._before_restart_funcs)
|
||||
await self._run_funcs(self._before_process_restart_funcs)
|
||||
|
||||
async def after_restart(self) -> None:
|
||||
"""
|
||||
|
@ -53,7 +53,6 @@ class ProcessManager:
|
||||
should_exit = False
|
||||
while not should_exit:
|
||||
chan_active = get_channel(f"{name}-active")
|
||||
chan_passive = get_channel(f"{name}-passive")
|
||||
process = Process(target=self.targets[name][0], args=self.targets[name][1],
|
||||
kwargs=self.targets[name][2])
|
||||
self.processes[name] = process
|
||||
@ -62,15 +61,19 @@ class ProcessManager:
|
||||
# 0退出 1重启
|
||||
data = chan_active.receive()
|
||||
if data == 1:
|
||||
logger.info(f"Restarting process {name}")
|
||||
asyncio.run(self.bot.lifespan.before_shutdown())
|
||||
asyncio.run(self.bot.lifespan.before_restart())
|
||||
self.terminate(name)
|
||||
break
|
||||
# 重启
|
||||
if self.is_process_alive(name):
|
||||
logger.info(f"Restarting process {name}")
|
||||
asyncio.run(self.bot.lifespan.before_process_shutdown())
|
||||
asyncio.run(self.bot.lifespan.before_process_restart())
|
||||
self.terminate(name)
|
||||
break
|
||||
else:
|
||||
logger.warning(f"Process {name} is not restartable, cannot restart.")
|
||||
|
||||
elif data == 0:
|
||||
logger.info(f"Stopping process {name}")
|
||||
asyncio.run(self.bot.lifespan.before_shutdown())
|
||||
asyncio.run(self.bot.lifespan.before_process_shutdown())
|
||||
should_exit = True
|
||||
self.terminate(name)
|
||||
else:
|
||||
@ -129,3 +132,17 @@ class ProcessManager:
|
||||
def terminate_all(self):
|
||||
for name in self.targets:
|
||||
self.terminate(name)
|
||||
|
||||
def is_process_alive(self, name: str) -> bool:
|
||||
"""
|
||||
检查进程是否存活
|
||||
Args:
|
||||
name:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
if name not in self.targets:
|
||||
raise logger.warning(f"Process {name} not found.")
|
||||
process = self.processes[name]
|
||||
return process.is_alive()
|
||||
|
@ -1,5 +1,92 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
此模块用于注册观察者函数,使用watchdog监控文件变化并重启bot
|
||||
启用该模块需要在配置文件中设置`dev_mode`为True
|
||||
"""
|
||||
import functools
|
||||
import time
|
||||
from typing import Callable, TypeAlias
|
||||
|
||||
"""
|
||||
import watchdog
|
||||
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
|
||||
from liteyuki import get_bot, get_config, logger
|
||||
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
CALLBACK_FUNC: TypeAlias = Callable[[FileSystemEvent], None] # 位置1为FileSystemEvent
|
||||
FILTER_FUNC: TypeAlias = Callable[[FileSystemEvent], bool] # 位置1为FileSystemEvent
|
||||
observer = Observer()
|
||||
|
||||
|
||||
def debounce(wait):
|
||||
"""
|
||||
防抖函数
|
||||
"""
|
||||
|
||||
def decorator(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
nonlocal last_call_time
|
||||
current_time = time.time()
|
||||
if (current_time - last_call_time) > wait:
|
||||
last_call_time = current_time
|
||||
return func(*args, **kwargs)
|
||||
|
||||
last_call_time = None
|
||||
return wrapper
|
||||
|
||||
return decorator
|
||||
|
||||
|
||||
if get_config("dev_mode", False):
|
||||
logger.debug("Liteyuki Reload enabled, watching for file changes...")
|
||||
observer.start()
|
||||
|
||||
|
||||
class CodeModifiedHandler(FileSystemEventHandler):
|
||||
"""
|
||||
Handler for code file changes
|
||||
"""
|
||||
|
||||
@debounce(1)
|
||||
def on_modified(self, event):
|
||||
raise NotImplementedError("on_modified must be implemented")
|
||||
|
||||
def on_created(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
def on_deleted(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
def on_moved(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
def on_any_event(self, event):
|
||||
self.on_modified(event)
|
||||
|
||||
|
||||
def on_file_system_event(directories: tuple[str], recursive: bool = True, event_filter: FILTER_FUNC = None) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]:
|
||||
"""
|
||||
注册文件系统变化监听器
|
||||
Args:
|
||||
directories: 监听目录们
|
||||
recursive: 是否递归监听子目录
|
||||
event_filter: 事件过滤器, 返回True则执行回调函数
|
||||
Returns:
|
||||
装饰器,装饰一个函数在接收到数据后执行
|
||||
"""
|
||||
|
||||
def decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC:
|
||||
def wrapper(event: FileSystemEvent):
|
||||
|
||||
if event_filter is not None and not event_filter(event):
|
||||
return
|
||||
func(event)
|
||||
|
||||
code_modified_handler = CodeModifiedHandler()
|
||||
code_modified_handler.on_modified = wrapper
|
||||
for directory in directories:
|
||||
observer.schedule(code_modified_handler, directory, recursive=recursive)
|
||||
|
||||
return func
|
||||
|
||||
return decorator
|
||||
|
@ -9,22 +9,8 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
@Software: PyCharm
|
||||
"""
|
||||
import sys
|
||||
import loguru
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
logger = loguru.logger
|
||||
if TYPE_CHECKING:
|
||||
# avoid sphinx autodoc resolve annotation failed
|
||||
# because loguru module do not have `Logger` class actually
|
||||
from loguru import Record
|
||||
|
||||
|
||||
def default_filter(record: "Record"):
|
||||
"""默认的日志过滤器,根据 `config.log_level` 配置改变日志等级。"""
|
||||
log_level = record["extra"].get("nonebot_log_level", "INFO")
|
||||
levelno = logger.level(log_level).no if isinstance(log_level, str) else log_level
|
||||
return record["level"].no >= levelno
|
||||
|
||||
from loguru import logger
|
||||
|
||||
# DEBUG日志格式
|
||||
debug_format: str = (
|
||||
@ -50,35 +36,26 @@ def get_format(level: str) -> str:
|
||||
return default_format
|
||||
|
||||
|
||||
logger = loguru.logger.bind()
|
||||
|
||||
|
||||
def init_log(config: dict):
|
||||
"""
|
||||
在语言加载完成后执行
|
||||
Returns:
|
||||
|
||||
"""
|
||||
global logger
|
||||
|
||||
logger.remove()
|
||||
logger.add(
|
||||
sys.stdout,
|
||||
level=0,
|
||||
diagnose=False,
|
||||
filter=default_filter,
|
||||
format=get_format(config.get("log_level", "INFO")),
|
||||
)
|
||||
show_icon = config.get("log_icon", True)
|
||||
logger.level("DEBUG", color="<blue>", icon=f"{'🐛' if show_icon else ''}DEBUG")
|
||||
logger.level("INFO", color="<normal>", icon=f"{'ℹ️' if show_icon else ''}INFO")
|
||||
logger.level("SUCCESS", color="<green>", icon=f"{'✅' if show_icon else ''}SUCCESS")
|
||||
logger.level("WARNING", color="<yellow>", icon=f"{'⚠️' if show_icon else ''}WARNING")
|
||||
logger.level("ERROR", color="<red>", icon=f"{'⭕' if show_icon else ''}ERROR")
|
||||
|
||||
# debug = lang.get("log.debug", default="==DEBUG")
|
||||
# info = lang.get("log.info", default="===INFO")
|
||||
# success = lang.get("log.success", default="SUCCESS")
|
||||
# warning = lang.get("log.warning", default="WARNING")
|
||||
# error = lang.get("log.error", default="==ERROR")
|
||||
#
|
||||
# logger.level("DEBUG", color="<blue>", icon=f"{'🐛' if show_icon else ''}{debug}")
|
||||
# logger.level("INFO", color="<normal>", icon=f"{'ℹ️' if show_icon else ''}{info}")
|
||||
# logger.level("SUCCESS", color="<green>", icon=f"{'✅' if show_icon else ''}{success}")
|
||||
# logger.level("WARNING", color="<yellow>", icon=f"{'⚠️' if show_icon else ''}{warning}")
|
||||
# logger.level("ERROR", color="<red>", icon=f"{'⭕' if show_icon else ''}{error}")
|
||||
|
||||
init_log(config={})
|
||||
|
@ -1,21 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/11 下午8:50
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : __init__.py.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from liteyuki.core import IS_MAIN_PROCESS
|
||||
from liteyuki.plugin import PluginMetadata
|
||||
|
||||
from .observer import *
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="代码热重载监视",
|
||||
)
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
config = get_config("liteyuki.reload")
|
@ -1,44 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||
|
||||
@Time : 2024/8/11 下午10:01
|
||||
@Author : snowykami
|
||||
@Email : snowykami@outlook.com
|
||||
@File : observer.py
|
||||
@Software: PyCharm
|
||||
"""
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
from liteyuki import get_config, logger, get_bot
|
||||
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
if get_config("debug", False):
|
||||
|
||||
src_directories = (
|
||||
"src/nonebot_plugins",
|
||||
"src/utils",
|
||||
)
|
||||
src_excludes_extensions = (
|
||||
"pyc",
|
||||
)
|
||||
logger.debug("Liteyuki Reload enabled, watching for file changes...")
|
||||
|
||||
class CodeModifiedHandler(FileSystemEventHandler):
|
||||
"""
|
||||
Handler for code file changes
|
||||
"""
|
||||
def on_modified(self, event):
|
||||
if event.src_path.endswith(
|
||||
src_excludes_extensions) or event.is_directory or "__pycache__" in event.src_path:
|
||||
return
|
||||
logger.info(f"{event.src_path} modified, reloading bot...")
|
||||
liteyuki_bot.restart_process("nonebot")
|
||||
|
||||
code_modified_handler = CodeModifiedHandler()
|
||||
|
||||
observer = Observer()
|
||||
for directory in src_directories:
|
||||
observer.schedule(code_modified_handler, directory, recursive=True)
|
||||
observer.start()
|
@ -15,14 +15,13 @@ from liteyuki.plugin import PluginMetadata
|
||||
from liteyuki import get_bot
|
||||
from liteyuki.comm import Channel, set_channel
|
||||
from liteyuki.core import IS_MAIN_PROCESS
|
||||
|
||||
from .nb_utils import adapter_manager, driver_manager
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="NoneBot2启动器",
|
||||
)
|
||||
|
||||
liteyuki = get_bot()
|
||||
|
||||
|
||||
def nb_run(chan_active: "Channel", chan_passive: "Channel", **kwargs):
|
||||
"""
|
||||
@ -39,7 +38,7 @@ def nb_run(chan_active: "Channel", chan_passive: "Channel", **kwargs):
|
||||
set_channel("nonebot-active", chan_active)
|
||||
set_channel("nonebot-passive", chan_passive)
|
||||
|
||||
kwargs.update(kwargs.get("nonebot", {})) # nonebot配置优先
|
||||
kwargs.update(kwargs.get("nonebot", {})) # nonebot配置优先
|
||||
nonebot.init(**kwargs)
|
||||
|
||||
driver_manager.init(config=kwargs)
|
||||
@ -50,6 +49,11 @@ def nb_run(chan_active: "Channel", chan_passive: "Channel", **kwargs):
|
||||
|
||||
|
||||
if IS_MAIN_PROCESS:
|
||||
from .dev_reloader import *
|
||||
|
||||
liteyuki = get_bot()
|
||||
|
||||
|
||||
@liteyuki.on_after_start
|
||||
def start_run_nonebot():
|
||||
liteyuki.process_manager.add_target(name="nonebot", target=nb_run, args=(), kwargs=liteyuki.config)
|
||||
|
22
liteyuki/plugins/nonebot_launcher/dev_reloader.py
Normal file
22
liteyuki/plugins/nonebot_launcher/dev_reloader.py
Normal file
@ -0,0 +1,22 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
NoneBot 开发环境重载监视器
|
||||
"""
|
||||
import os.path
|
||||
|
||||
from liteyuki.dev import observer
|
||||
from liteyuki import get_bot, logger
|
||||
from watchdog.events import FileSystemEvent
|
||||
|
||||
liteyuki = get_bot()
|
||||
|
||||
exclude_extensions = (".pyc", ".pyo")
|
||||
|
||||
|
||||
@observer.on_file_system_event(
|
||||
directories=("src/nonebot_plugins",),
|
||||
event_filter=lambda event: not event.src_path.endswith(exclude_extensions) and ("__pycache__" not in event.src_path ) and os.path.isfile(event.src_path)
|
||||
)
|
||||
def restart_nonebot_process(event: FileSystemEvent):
|
||||
logger.debug(f"File {event.src_path} changed, reloading nonebot...")
|
||||
liteyuki.restart_process("nonebot")
|
2
main.py
2
main.py
@ -5,5 +5,5 @@ from liteyuki import LiteyukiBot
|
||||
from liteyuki.config import load_config_in_default
|
||||
|
||||
if __name__ == "__main__":
|
||||
bot = LiteyukiBot(**load_config_in_default())
|
||||
bot = LiteyukiBot(**load_config_in_default(no_waring=True))
|
||||
bot.run()
|
||||
|
@ -21,6 +21,7 @@ pytz~=2024.1
|
||||
PyYAML~=6.0.1
|
||||
pillow~=10.0.0
|
||||
starlette~=0.36.3
|
||||
toml~=0.10.2
|
||||
loguru~=0.7.2
|
||||
importlib_metadata~=7.0.2
|
||||
requests~=2.31.0
|
||||
|
@ -2,8 +2,6 @@ from nonebot.plugin import PluginMetadata
|
||||
|
||||
from .core import *
|
||||
from .loader import *
|
||||
from .dev import *
|
||||
|
||||
__author__ = "snowykami"
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="轻雪核心插件",
|
||||
@ -18,6 +16,5 @@ __plugin_meta__ = PluginMetadata(
|
||||
|
||||
from ..utils.base.language import Language, get_default_lang_code
|
||||
|
||||
|
||||
sys_lang = Language(get_default_lang_code())
|
||||
nonebot.logger.info(sys_lang.get("main.current_language", LANG=sys_lang.get("language.name")))
|
@ -1,36 +0,0 @@
|
||||
import nonebot
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
from liteyuki.bot import get_bot
|
||||
from src.utils.base import reload
|
||||
from src.utils.base.config import get_config
|
||||
from src.utils.base.resource import load_resources
|
||||
|
||||
if get_config("debug", False):
|
||||
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
res_directories = (
|
||||
"src/resources",
|
||||
"resources",
|
||||
|
||||
)
|
||||
|
||||
|
||||
class ResourceModifiedHandler(FileSystemEventHandler):
|
||||
"""
|
||||
Handler for resource file changes
|
||||
"""
|
||||
|
||||
def on_modified(self, event):
|
||||
nonebot.logger.info(f"{event.src_path} modified, reloading resource...")
|
||||
load_resources()
|
||||
|
||||
|
||||
resource_modified_handle = ResourceModifiedHandler()
|
||||
|
||||
observer = Observer()
|
||||
for directory in res_directories:
|
||||
observer.schedule(resource_modified_handle, directory, recursive=True)
|
||||
observer.start()
|
@ -16,7 +16,6 @@ load_resources()
|
||||
init_log()
|
||||
|
||||
driver = get_driver()
|
||||
liteyuki_bot = get_bot()
|
||||
|
||||
|
||||
@driver.on_startup
|
||||
|
@ -29,13 +29,12 @@ def _():
|
||||
logger.info("生命周期监控器:准备启动")
|
||||
|
||||
|
||||
@bot.on_before_shutdown
|
||||
@bot.on_before_process_shutdown
|
||||
def _():
|
||||
print(get_channel("main"))
|
||||
logger.info("生命周期监控器:准备停止")
|
||||
|
||||
|
||||
@bot.on_before_restart
|
||||
@bot.on_before_process_restart
|
||||
def _():
|
||||
logger.info("生命周期监控器:准备重启")
|
||||
|
||||
|
@ -21,4 +21,3 @@ __plugin_meta__ = PluginMetadata(
|
||||
"default_enable" : True,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -2,8 +2,8 @@ import os
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .data import Database, LiteModel, Database
|
||||
print("导入数据库模块")
|
||||
from .data import Database, LiteModel
|
||||
|
||||
DATA_PATH = "data/liteyuki"
|
||||
user_db: Database = Database(os.path.join(DATA_PATH, "users.ldb"))
|
||||
group_db: Database = Database(os.path.join(DATA_PATH, "groups.ldb"))
|
||||
|
Loading…
Reference in New Issue
Block a user