forked from bot/app
✨ 新增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
|
ENV TZ Asia/Shanghai
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
nonebot:
|
nonebot:
|
||||||
host: 127.0.0.1
|
host: 127.0.0.1
|
||||||
port: 20216
|
port: 20216
|
||||||
superusers: ["1234"]
|
|
||||||
command_start: ["", "/"]
|
command_start: ["", "/"]
|
||||||
nickname: [ "liteyuki-dev" ]
|
nickname: [ "liteyuki-dev" ]
|
||||||
default_language: zh
|
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:
|
class LiteyukiBot:
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
print_logo()
|
||||||
global _BOT_INSTANCE
|
global _BOT_INSTANCE
|
||||||
_BOT_INSTANCE = self # 引用
|
_BOT_INSTANCE = self # 引用
|
||||||
|
|
||||||
self.lifespan = Lifespan()
|
|
||||||
|
|
||||||
self.config: dict[str, Any] = kwargs
|
self.config: dict[str, Any] = kwargs
|
||||||
|
|
||||||
self.init(**self.config) # 初始化
|
self.init(**self.config) # 初始化
|
||||||
|
logger.info("Liteyuki is initializing...")
|
||||||
|
|
||||||
|
self.lifespan = Lifespan()
|
||||||
|
|
||||||
self.process_manager: ProcessManager = ProcessManager(bot=self)
|
self.process_manager: ProcessManager = ProcessManager(bot=self)
|
||||||
self.loop = asyncio.new_event_loop()
|
self.loop = asyncio.new_event_loop()
|
||||||
@ -41,19 +43,7 @@ class LiteyukiBot:
|
|||||||
self.stop_event = threading.Event()
|
self.stop_event = threading.Event()
|
||||||
self.call_restart_count = 0
|
self.call_restart_count = 0
|
||||||
|
|
||||||
print("\033[34m" + r"""
|
|
||||||
__ ______ ________ ________ __ __ __ __ __ __ ______
|
|
||||||
/ | / |/ |/ |/ \ / |/ | / |/ | / |/ |
|
|
||||||
$$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/
|
|
||||||
$$ | $$ | $$ | $$ |__ $$ \/$$/ $$ | $$ |$$ |/$$/ $$ |
|
|
||||||
$$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ |
|
|
||||||
$$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \ $$ |
|
|
||||||
$$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
|
||||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
|
||||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|
||||||
""" + "\033[0m")
|
|
||||||
load_plugins("liteyuki/plugins") # 加载轻雪插件
|
load_plugins("liteyuki/plugins") # 加载轻雪插件
|
||||||
logger.info("Liteyuki is initializing...")
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
@ -75,7 +65,6 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self.call_restart_count < 1:
|
if self.call_restart_count < 1:
|
||||||
executable = sys.executable
|
executable = sys.executable
|
||||||
args = sys.argv
|
args = sys.argv
|
||||||
@ -103,16 +92,10 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|||||||
Returns:
|
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()) # 重启前钩子
|
if name is not None:
|
||||||
self.loop.create_task(self.lifespan.before_shutdown()) # 停止前钩子
|
|
||||||
|
|
||||||
# if name:
|
|
||||||
# self.process_manager.terminate(name)
|
|
||||||
# else:
|
|
||||||
# self.process_manager.terminate_all()
|
|
||||||
if name:
|
|
||||||
chan_active = get_channel(f"{name}-active")
|
chan_active = get_channel(f"{name}-active")
|
||||||
chan_active.send(1)
|
chan_active.send(1)
|
||||||
else:
|
else:
|
||||||
@ -158,17 +141,6 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|||||||
"""
|
"""
|
||||||
return self.lifespan.on_after_start(func)
|
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):
|
def on_after_shutdown(self, func: LIFESPAN_FUNC):
|
||||||
"""
|
"""
|
||||||
注册停止后的函数:未实现
|
注册停止后的函数:未实现
|
||||||
@ -180,9 +152,20 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|||||||
"""
|
"""
|
||||||
return self.lifespan.on_after_shutdown(func)
|
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:
|
Args:
|
||||||
func:
|
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):
|
def on_after_restart(self, func: LIFESPAN_FUNC):
|
||||||
"""
|
"""
|
||||||
@ -228,13 +211,14 @@ def get_bot() -> Optional[LiteyukiBot]:
|
|||||||
LiteyukiBot: 当前的轻雪实例
|
LiteyukiBot: 当前的轻雪实例
|
||||||
"""
|
"""
|
||||||
if IS_MAIN_PROCESS:
|
if IS_MAIN_PROCESS:
|
||||||
|
if _BOT_INSTANCE is None:
|
||||||
|
raise RuntimeError("Liteyuki instance not initialized.")
|
||||||
return _BOT_INSTANCE
|
return _BOT_INSTANCE
|
||||||
else:
|
else:
|
||||||
# 从多进程上下文中获取
|
raise RuntimeError("Can't get bot instance in sub process.")
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def get_config(key: str, default: Any = None) -> Any:
|
def get_config(key: str = None, default: Any = None) -> Any:
|
||||||
"""
|
"""
|
||||||
获取配置
|
获取配置
|
||||||
Args:
|
Args:
|
||||||
@ -244,7 +228,9 @@ def get_config(key: str, default: Any = None) -> Any:
|
|||||||
Returns:
|
Returns:
|
||||||
Any: 配置值
|
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:
|
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:
|
Returns:
|
||||||
Any: 配置值
|
Any: 配置值
|
||||||
"""
|
"""
|
||||||
if key in _BOT_INSTANCE.config:
|
if key in get_bot().config:
|
||||||
return _BOT_INSTANCE.config[key]
|
return get_bot().config[key]
|
||||||
for compat_key in compat_keys:
|
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.")
|
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
|
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._before_start_funcs: list[LIFESPAN_FUNC] = []
|
||||||
self._after_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._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_restart_funcs: list[LIFESPAN_FUNC] = []
|
||||||
|
|
||||||
self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
|
self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = []
|
||||||
@ -73,7 +73,7 @@ class Lifespan:
|
|||||||
self._after_start_funcs.append(func)
|
self._after_start_funcs.append(func)
|
||||||
return 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:
|
Args:
|
||||||
@ -81,7 +81,7 @@ class Lifespan:
|
|||||||
Returns:
|
Returns:
|
||||||
LIFESPAN_FUNC:
|
LIFESPAN_FUNC:
|
||||||
"""
|
"""
|
||||||
self._before_shutdown_funcs.append(func)
|
self._before_process_shutdown_funcs.append(func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||||
@ -97,7 +97,7 @@ class Lifespan:
|
|||||||
self._after_shutdown_funcs.append(func)
|
self._after_shutdown_funcs.append(func)
|
||||||
return 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:
|
Args:
|
||||||
@ -105,7 +105,7 @@ class Lifespan:
|
|||||||
Returns:
|
Returns:
|
||||||
LIFESPAN_FUNC:
|
LIFESPAN_FUNC:
|
||||||
"""
|
"""
|
||||||
self._before_restart_funcs.append(func)
|
self._before_process_restart_funcs.append(func)
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC:
|
||||||
@ -147,13 +147,13 @@ class Lifespan:
|
|||||||
logger.debug("Running after_start functions")
|
logger.debug("Running after_start functions")
|
||||||
await self._run_funcs(self._after_start_funcs)
|
await self._run_funcs(self._after_start_funcs)
|
||||||
|
|
||||||
async def before_shutdown(self) -> None:
|
async def before_process_shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
停止前
|
停止前
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
logger.debug("Running before_shutdown functions")
|
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:
|
async def after_shutdown(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -163,13 +163,13 @@ class Lifespan:
|
|||||||
logger.debug("Running after_shutdown functions")
|
logger.debug("Running after_shutdown functions")
|
||||||
await self._run_funcs(self._after_shutdown_funcs)
|
await self._run_funcs(self._after_shutdown_funcs)
|
||||||
|
|
||||||
async def before_restart(self) -> None:
|
async def before_process_restart(self) -> None:
|
||||||
"""
|
"""
|
||||||
重启前
|
重启前
|
||||||
Returns:
|
Returns:
|
||||||
"""
|
"""
|
||||||
logger.debug("Running before_restart functions")
|
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:
|
async def after_restart(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -53,7 +53,6 @@ class ProcessManager:
|
|||||||
should_exit = False
|
should_exit = False
|
||||||
while not should_exit:
|
while not should_exit:
|
||||||
chan_active = get_channel(f"{name}-active")
|
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],
|
process = Process(target=self.targets[name][0], args=self.targets[name][1],
|
||||||
kwargs=self.targets[name][2])
|
kwargs=self.targets[name][2])
|
||||||
self.processes[name] = process
|
self.processes[name] = process
|
||||||
@ -62,15 +61,19 @@ class ProcessManager:
|
|||||||
# 0退出 1重启
|
# 0退出 1重启
|
||||||
data = chan_active.receive()
|
data = chan_active.receive()
|
||||||
if data == 1:
|
if data == 1:
|
||||||
|
# 重启
|
||||||
|
if self.is_process_alive(name):
|
||||||
logger.info(f"Restarting process {name}")
|
logger.info(f"Restarting process {name}")
|
||||||
asyncio.run(self.bot.lifespan.before_shutdown())
|
asyncio.run(self.bot.lifespan.before_process_shutdown())
|
||||||
asyncio.run(self.bot.lifespan.before_restart())
|
asyncio.run(self.bot.lifespan.before_process_restart())
|
||||||
self.terminate(name)
|
self.terminate(name)
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
logger.warning(f"Process {name} is not restartable, cannot restart.")
|
||||||
|
|
||||||
elif data == 0:
|
elif data == 0:
|
||||||
logger.info(f"Stopping process {name}")
|
logger.info(f"Stopping process {name}")
|
||||||
asyncio.run(self.bot.lifespan.before_shutdown())
|
asyncio.run(self.bot.lifespan.before_process_shutdown())
|
||||||
should_exit = True
|
should_exit = True
|
||||||
self.terminate(name)
|
self.terminate(name)
|
||||||
else:
|
else:
|
||||||
@ -129,3 +132,17 @@ class ProcessManager:
|
|||||||
def terminate_all(self):
|
def terminate_all(self):
|
||||||
for name in self.targets:
|
for name in self.targets:
|
||||||
self.terminate(name)
|
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
|
||||||
|
|
||||||
|
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):
|
||||||
"""
|
"""
|
||||||
import watchdog
|
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
|
@Software: PyCharm
|
||||||
"""
|
"""
|
||||||
import sys
|
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日志格式
|
||||||
debug_format: str = (
|
debug_format: str = (
|
||||||
@ -50,35 +36,26 @@ def get_format(level: str) -> str:
|
|||||||
return default_format
|
return default_format
|
||||||
|
|
||||||
|
|
||||||
logger = loguru.logger.bind()
|
|
||||||
|
|
||||||
|
|
||||||
def init_log(config: dict):
|
def init_log(config: dict):
|
||||||
"""
|
"""
|
||||||
在语言加载完成后执行
|
在语言加载完成后执行
|
||||||
Returns:
|
Returns:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
global logger
|
|
||||||
|
|
||||||
logger.remove()
|
logger.remove()
|
||||||
logger.add(
|
logger.add(
|
||||||
sys.stdout,
|
sys.stdout,
|
||||||
level=0,
|
level=0,
|
||||||
diagnose=False,
|
diagnose=False,
|
||||||
filter=default_filter,
|
|
||||||
format=get_format(config.get("log_level", "INFO")),
|
format=get_format(config.get("log_level", "INFO")),
|
||||||
)
|
)
|
||||||
show_icon = config.get("log_icon", True)
|
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")
|
init_log(config={})
|
||||||
# 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}")
|
|
||||||
|
@ -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 import get_bot
|
||||||
from liteyuki.comm import Channel, set_channel
|
from liteyuki.comm import Channel, set_channel
|
||||||
from liteyuki.core import IS_MAIN_PROCESS
|
from liteyuki.core import IS_MAIN_PROCESS
|
||||||
|
|
||||||
from .nb_utils import adapter_manager, driver_manager
|
from .nb_utils import adapter_manager, driver_manager
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="NoneBot2启动器",
|
name="NoneBot2启动器",
|
||||||
)
|
)
|
||||||
|
|
||||||
liteyuki = get_bot()
|
|
||||||
|
|
||||||
|
|
||||||
def nb_run(chan_active: "Channel", chan_passive: "Channel", **kwargs):
|
def nb_run(chan_active: "Channel", chan_passive: "Channel", **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -50,6 +49,11 @@ def nb_run(chan_active: "Channel", chan_passive: "Channel", **kwargs):
|
|||||||
|
|
||||||
|
|
||||||
if IS_MAIN_PROCESS:
|
if IS_MAIN_PROCESS:
|
||||||
|
from .dev_reloader import *
|
||||||
|
|
||||||
|
liteyuki = get_bot()
|
||||||
|
|
||||||
|
|
||||||
@liteyuki.on_after_start
|
@liteyuki.on_after_start
|
||||||
def start_run_nonebot():
|
def start_run_nonebot():
|
||||||
liteyuki.process_manager.add_target(name="nonebot", target=nb_run, args=(), kwargs=liteyuki.config)
|
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
|
from liteyuki.config import load_config_in_default
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
bot = LiteyukiBot(**load_config_in_default())
|
bot = LiteyukiBot(**load_config_in_default(no_waring=True))
|
||||||
bot.run()
|
bot.run()
|
||||||
|
@ -21,6 +21,7 @@ pytz~=2024.1
|
|||||||
PyYAML~=6.0.1
|
PyYAML~=6.0.1
|
||||||
pillow~=10.0.0
|
pillow~=10.0.0
|
||||||
starlette~=0.36.3
|
starlette~=0.36.3
|
||||||
|
toml~=0.10.2
|
||||||
loguru~=0.7.2
|
loguru~=0.7.2
|
||||||
importlib_metadata~=7.0.2
|
importlib_metadata~=7.0.2
|
||||||
requests~=2.31.0
|
requests~=2.31.0
|
||||||
|
@ -2,8 +2,6 @@ from nonebot.plugin import PluginMetadata
|
|||||||
|
|
||||||
from .core import *
|
from .core import *
|
||||||
from .loader import *
|
from .loader import *
|
||||||
from .dev import *
|
|
||||||
|
|
||||||
__author__ = "snowykami"
|
__author__ = "snowykami"
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="轻雪核心插件",
|
name="轻雪核心插件",
|
||||||
@ -18,6 +16,5 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
|
|
||||||
from ..utils.base.language import Language, get_default_lang_code
|
from ..utils.base.language import Language, get_default_lang_code
|
||||||
|
|
||||||
|
|
||||||
sys_lang = 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")))
|
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()
|
init_log()
|
||||||
|
|
||||||
driver = get_driver()
|
driver = get_driver()
|
||||||
liteyuki_bot = get_bot()
|
|
||||||
|
|
||||||
|
|
||||||
@driver.on_startup
|
@driver.on_startup
|
||||||
|
@ -29,13 +29,12 @@ def _():
|
|||||||
logger.info("生命周期监控器:准备启动")
|
logger.info("生命周期监控器:准备启动")
|
||||||
|
|
||||||
|
|
||||||
@bot.on_before_shutdown
|
@bot.on_before_process_shutdown
|
||||||
def _():
|
def _():
|
||||||
print(get_channel("main"))
|
|
||||||
logger.info("生命周期监控器:准备停止")
|
logger.info("生命周期监控器:准备停止")
|
||||||
|
|
||||||
|
|
||||||
@bot.on_before_restart
|
@bot.on_before_process_restart
|
||||||
def _():
|
def _():
|
||||||
logger.info("生命周期监控器:准备重启")
|
logger.info("生命周期监控器:准备重启")
|
||||||
|
|
||||||
|
@ -21,4 +21,3 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
"default_enable" : True,
|
"default_enable" : True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ import os
|
|||||||
|
|
||||||
from pydantic import Field
|
from pydantic import Field
|
||||||
|
|
||||||
from .data import Database, LiteModel, Database
|
from .data import Database, LiteModel
|
||||||
print("导入数据库模块")
|
|
||||||
DATA_PATH = "data/liteyuki"
|
DATA_PATH = "data/liteyuki"
|
||||||
user_db: Database = Database(os.path.join(DATA_PATH, "users.ldb"))
|
user_db: Database = Database(os.path.join(DATA_PATH, "users.ldb"))
|
||||||
group_db: Database = Database(os.path.join(DATA_PATH, "groups.ldb"))
|
group_db: Database = Database(os.path.join(DATA_PATH, "groups.ldb"))
|
||||||
|
Loading…
Reference in New Issue
Block a user