diff --git a/.gitignore b/.gitignore
index d8bc4873..7a9bad78 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
.venv/
.idea/
.cache/
+
node_modules/
data/
db/
@@ -11,19 +12,23 @@ __pycache__/
*.pyd
*.pyw
/plugins/
+
+#config
+config/
+!config/default.yml
_config.yml
config.yml
config.example.yml
-compile.bat
-src/resources/templates/latest-debug.html
+
# vuepress
.github
-test.py
-line_count.py
+# mupy
mypy.ini
# nuitka
+compile.bat
+src/resources/templates/latest-debug.html
main.build/
main.dist/
main.exe
diff --git a/liteyuki/plugins/process_manager/__init__.py b/config/default.yml
similarity index 100%
rename from liteyuki/plugins/process_manager/__init__.py
rename to config/default.yml
diff --git a/docs/README.md b/docs/README.md
index c2f32ee9..c18d881e 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -9,7 +9,7 @@ bgImageDark:
bgImageStyle:
background-attachment: fixed
heroText: LiteyukiBot
-tagline: LiteyukiBot 轻雪机器人,基于NoneBot2构建的综合应用型聊天机器人
+tagline: LiteyukiBot 轻雪机器人,综合性的机器人应用及管理框架
actions:
- text: 快速部署
@@ -30,9 +30,9 @@ highlights:
background-repeat: repeat
background-size: initial
features:
- - title: 基于NoneBot2
+ - title: 支持多种框架
icon: robot
- details: 拥有良好的生态支持
+ details: 兼容nonebot,melobot等,拥有良好的生态支持
link: https://nonebot.dev/
- title: 便捷管理
diff --git a/docs/deployment/config.md b/docs/deployment/config.md
index 57a96fe5..385c070d 100644
--- a/docs/deployment/config.md
+++ b/docs/deployment/config.md
@@ -8,8 +8,11 @@ tag:
- 部署
---
-首次运行后生成`config.yml`,你可以修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers`及`nickname`字段即可
+轻雪支持`yaml`、`json`和`toml`作为配置文件,取决于你个人的喜好
+首次运行后生成`config.yml`和`config`目录,你可修改配置项后重启轻雪,绝大多数情况下,你只需要修改`superusers`及`nickname`字段即可
+
+启动时会加载项目目录下`config.yml/yaml/json/toml`和`config`目录下的所有配置文件,你可在`config`目录下创建多个配置文件,轻雪会自动合并这些配置文件
## **基础配置项**
```yaml
diff --git a/liteyuki/__init__.py b/liteyuki/__init__.py
index 4ace66e1..1e195c2e 100644
--- a/liteyuki/__init__.py
+++ b/liteyuki/__init__.py
@@ -1,6 +1,7 @@
from liteyuki.bot import (
LiteyukiBot,
- get_bot
+ get_bot,
+ get_config
)
from liteyuki.comm import (
diff --git a/liteyuki/bot/__init__.py b/liteyuki/bot/__init__.py
index e89d8a3a..63363c92 100644
--- a/liteyuki/bot/__init__.py
+++ b/liteyuki/bot/__init__.py
@@ -4,21 +4,22 @@ import platform
import sys
import threading
import time
-from typing import Any, Optional
+from typing import Any, Iterable, Optional
from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer
from liteyuki.bot.lifespan import (LIFESPAN_FUNC, Lifespan)
+from liteyuki.comm import get_channel
from liteyuki.core import IS_MAIN_PROCESS
from liteyuki.core.manager import ProcessManager
-from liteyuki.core.spawn_process import mb_run, nb_run
from liteyuki.log import init_log, logger
from liteyuki.plugin import load_plugins
__all__ = [
"LiteyukiBot",
- "get_bot"
+ "get_bot",
+ "get_config",
]
@@ -26,15 +27,18 @@ class LiteyukiBot:
def __init__(self, *args, **kwargs):
global _BOT_INSTANCE
_BOT_INSTANCE = self # 引用
- self.config: dict[str, Any] = kwargs
- self.init(**self.config) # 初始化
- self.lifespan: Lifespan = Lifespan()
+ self.lifespan = Lifespan()
+
+ self.config: dict[str, Any] = kwargs
+
+ self.init(**self.config) # 初始化
self.process_manager: ProcessManager = ProcessManager(bot=self)
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.loop_thread = threading.Thread(target=self.loop.run_forever, daemon=True)
+ self.stop_event = threading.Event()
self.call_restart_count = 0
print("\033[34m" + r"""
@@ -48,58 +52,22 @@ $$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
""" + "\033[0m")
+ load_plugins("liteyuki/plugins") # 加载轻雪插件
+ logger.info("Liteyuki is initializing...")
def run(self):
- load_plugins("liteyuki/plugins") # 加载轻雪插件
-
+ """
+ 启动逻辑
+ """
self.loop_thread.start() # 启动事件循环
asyncio.run(self.lifespan.before_start()) # 启动前钩子
- self.process_manager.add_target("nonebot", nb_run, **self.config)
- self.process_manager.start("nonebot")
-
- self.process_manager.add_target("melobot", mb_run, **self.config)
- self.process_manager.start("melobot")
-
asyncio.run(self.lifespan.after_start()) # 启动后钩子
-
- self.start_watcher() # 启动文件监视器
+ self.start_watcher() # 启动文件监视器,后续准备插件化
+ self.keep_running()
def start_watcher(self):
- if self.config.get("debug", False):
-
- src_directories = (
- "liteyuki",
- "src/liteyuki_main",
- "src/liteyuki_plugins",
- "src/nonebot_plugins",
- "src/utils",
- )
- src_excludes_extensions = (
- "pyc",
- )
-
- logger.debug("Liteyuki Reload enabled, watching for file changes...")
- restart = self.restart_process
-
- 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...")
- restart()
-
- code_modified_handler = CodeModifiedHandler()
-
- observer = Observer()
- for directory in src_directories:
- observer.schedule(code_modified_handler, directory, recursive=True)
- observer.start()
+ pass
def restart(self, delay: int = 0):
"""
@@ -135,15 +103,22 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
Returns:
"""
- logger.info("Stopping LiteyukiBot...")
+ logger.info(f"Stopping process {name}...")
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:
- self.process_manager.terminate(name)
+ chan_active = get_channel(f"{name}-active")
+ chan_active.send(1)
else:
- self.process_manager.terminate_all()
+ for process_name in self.process_manager.processes:
+ chan_active = get_channel(f"{process_name}-active")
+ chan_active.send(1)
def init(self, *args, **kwargs):
"""
@@ -239,6 +214,9 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
"""
return self.lifespan.on_after_nonebot_init(func)
+ def keep_running(self):
+ self.stop_event.wait()
+
_BOT_INSTANCE: Optional[LiteyukiBot] = None
@@ -254,3 +232,37 @@ def get_bot() -> Optional[LiteyukiBot]:
else:
# 从多进程上下文中获取
pass
+
+
+def get_config(key: str, default: Any = None) -> Any:
+ """
+ 获取配置
+ Args:
+ key: 配置键
+ default: 默认值
+
+ Returns:
+ Any: 配置值
+ """
+ return _BOT_INSTANCE.config.get(key, default)
+
+
+def get_config_with_compat(key: str, compat_keys: tuple[str], default: Any = None) -> Any:
+ """
+ 获取配置,兼容旧版本
+ Args:
+ key: 配置键
+ compat_keys: 兼容键
+ default: 默认值
+
+ Returns:
+ Any: 配置值
+ """
+ if key in _BOT_INSTANCE.config:
+ return _BOT_INSTANCE.config[key]
+ for compat_key in compat_keys:
+ if compat_key in _BOT_INSTANCE.config:
+ logger.warning(f"Config key {compat_key} will be deprecated, use {key} instead.")
+ return _BOT_INSTANCE.config[compat_key]
+ return default
+
diff --git a/liteyuki/comm/channel.py b/liteyuki/comm/channel.py
index ec22014c..eae83da7 100644
--- a/liteyuki/comm/channel.py
+++ b/liteyuki/comm/channel.py
@@ -35,13 +35,14 @@ _callback_funcs: dict[str, ON_RECEIVE_FUNC] = {}
class Channel:
"""
- 通道类,用于进程间通信,进程内不可用,仅限主进程和子进程之间通信
+ 通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
"""
def __init__(self, _id: str):
- self.main_send_conn, self.sub_receive_conn = Pipe()
- self.sub_send_conn, self.main_receive_conn = Pipe()
+ # self.main_send_conn, self.sub_receive_conn = Pipe()
+ # self.sub_send_conn, self.main_receive_conn = Pipe()
+ self.conn_send, self.conn_recv = Pipe()
self._closed = False
self._on_main_receive_funcs: list[str] = []
self._on_sub_receive_funcs: list[str] = []
@@ -61,12 +62,7 @@ class Channel:
"""
if self._closed:
raise RuntimeError("Cannot send to a closed channel")
- if IS_MAIN_PROCESS:
- print("主进程发送数据:", data)
- self.main_send_conn.send(data)
- else:
- print("子进程发送数据:", data)
- self.sub_send_conn.send(data)
+ self.conn_send.send(data)
def receive(self) -> Any:
"""
@@ -77,14 +73,7 @@ class Channel:
raise RuntimeError("Cannot receive from a closed channel")
while True:
- # 判断receiver是否为None或者receiver是否等于接收者,是则接收数据,否则不动数据
- if IS_MAIN_PROCESS:
- data = self.main_receive_conn.recv()
- print("主进程接收数据:", data)
- else:
- data = self.sub_receive_conn.recv()
- print("子进程接收数据:", data)
-
+ data = self.conn_recv.recv()
return data
def close(self):
@@ -92,10 +81,8 @@ class Channel:
关闭通道
"""
self._closed = True
- self.sub_receive_conn.close()
- self.main_send_conn.close()
- self.sub_send_conn.close()
- self.main_receive_conn.close()
+ self.conn_send.close()
+ self.conn_recv.close()
def on_receive(self, filter_func: Optional[FILTER_FUNC] = None) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]:
"""
@@ -158,7 +145,7 @@ class Channel:
"""
self.is_main_receive_loop_running = True
while not self._closed:
- data = self.main_receive_conn.recv()
+ data = self.conn_recv.recv()
self._run_on_main_receive_funcs(data)
def _start_sub_receive_loop(self):
@@ -167,7 +154,7 @@ class Channel:
"""
self.is_sub_receive_loop_running = True
while not self._closed:
- data = self.sub_receive_conn.recv()
+ data = self.conn_recv.recv()
self._run_on_sub_receive_funcs(data)
def __iter__(self):
@@ -188,6 +175,8 @@ def set_channel(name: str, channel: Channel):
name: 通道名称
channel: 通道实例
"""
+ if not isinstance(channel, Channel):
+ raise TypeError(f"channel must be an instance of Channel, {type(channel)} found")
_channel[name] = channel
@@ -198,7 +187,7 @@ def set_channels(channels: dict[str, Channel]):
channels: 通道名称
"""
for name, channel in channels.items():
- _channel[name] = channel
+ set_channel(name, channel)
def get_channel(name: str) -> Optional[Channel]:
diff --git a/liteyuki/config.py b/liteyuki/config.py
index 4fcb8d44..b50adc5e 100644
--- a/liteyuki/config.py
+++ b/liteyuki/config.py
@@ -1,12 +1,24 @@
+"""
+该模块用于常用配置文件的加载
+多配置文件编写原则:
+1.尽量不要冲突: 一个键不要多次出现
+2.分工明确: 每个配置文件给一个或一类服务提供配置
+3.扁平化编写: 配置文件尽量扁平化,不要出现过多的嵌套
+4.注意冲突时的优先级: 项目目录下的配置文件优先级高于config目录下的配置文件
+5.请不要将需要动态加载的内容写入配置文件,你应该使用其他储存方式
+"""
+import copy
+import json
import os
-from typing import List
+from typing import Any, List
-import nonebot
+import toml
import yaml
from pydantic import BaseModel
+from liteyuki import logger
-config = {} # 主进程全局配置,确保加载后读取
+_SUPPORTED_CONFIG_FORMATS = (".yaml", ".yml", ".json", ".toml")
class SatoriNodeConfig(BaseModel):
@@ -32,18 +44,100 @@ class BasicConfig(BaseModel):
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")
- with open(file, "w", encoding="utf-8") as f:
- yaml.dump(BasicConfig().dict(), f, default_flow_style=False)
+def flat_config(config: dict[str, Any]) -> dict[str, Any]:
+ """
+ 扁平化配置文件
- with open(file, "r", encoding="utf-8") as f:
- 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")
- conf = BasicConfig().dict()
- return conf
+ {a:{b:{c:1}}} -> {"a.b.c": 1}
+ Args:
+ config: 配置项目
+
+ Returns:
+ 扁平化后的配置文件,但也包含原有的键值对
+ """
+ new_config = copy.deepcopy(config)
+ for key, value in config.items():
+ if isinstance(value, dict):
+ for k, v in flat_config(value).items():
+ new_config[f"{key}.{k}"] = v
+ return new_config
+
+
+def load_from_yaml(file: str) -> dict[str, Any]:
+ """
+ Load config from yaml file
+
+ """
+ logger.debug(f"Loading YAML config from {file}")
+ config = yaml.safe_load(open(file, "r", encoding="utf-8"))
+ return flat_config(config if config is not None else {})
+
+
+def load_from_json(file: str) -> dict[str, Any]:
+ """
+ Load config from json file
+ """
+ logger.debug(f"Loading JSON config from {file}")
+ config = json.load(open(file, "r", encoding="utf-8"))
+ return flat_config(config if config is not None else {})
+
+
+def load_from_toml(file: str) -> dict[str, Any]:
+ """
+ Load config from toml file
+ """
+ logger.debug(f"Loading TOML config from {file}")
+ config = toml.load(open(file, "r", encoding="utf-8"))
+ return flat_config(config if config is not None else {})
+
+
+def load_from_files(*files: str, no_warning: bool = False) -> dict[str, Any]:
+ """
+ 从指定文件加载配置项,会自动识别文件格式
+ 默认执行扁平化选项
+ """
+ config = {}
+ for file in files:
+ if os.path.exists(file):
+ if file.endswith((".yaml", "yml")):
+ config.update(load_from_yaml(file))
+ elif file.endswith(".json"):
+ config.update(load_from_json(file))
+ elif file.endswith(".toml"):
+ config.update(load_from_toml(file))
+ else:
+ if not no_warning:
+ logger.warning(f"Unsupported config file format: {file}")
+ else:
+ if not no_warning:
+ logger.warning(f"Config file not found: {file}")
+ return config
+
+
+def load_configs_from_dirs(*directories: str, no_waring: bool = False) -> dict[str, Any]:
+ """
+ 从目录下加载配置文件,不递归
+ 按照读取文件的优先级反向覆盖
+ 默认执行扁平化选项
+ """
+ config = {}
+ for directory in directories:
+ if not os.path.exists(directory):
+ if not no_waring:
+ logger.warning(f"Directory not found: {directory}")
+ continue
+ for file in os.listdir(directory):
+ if file.endswith(_SUPPORTED_CONFIG_FORMATS):
+ config.update(load_from_files(os.path.join(directory, file), no_warning=no_waring))
+ return config
+
+
+def load_config_in_default(no_waring: bool = False) -> dict[str, Any]:
+ """
+ 从一个标准的轻雪项目加载配置文件
+ 项目目录下的config.*和config目录下的所有配置文件
+ 项目目录下的配置文件优先
+ """
+ config = load_configs_from_dirs("config", no_waring=no_waring)
+ config.update(load_from_files("config.yaml", "config.toml", "config.json", "config.yml", no_warning=no_waring))
+ return config
diff --git a/liteyuki/core/__init__.py b/liteyuki/core/__init__.py
index f95f8146..8f4d9278 100644
--- a/liteyuki/core/__init__.py
+++ b/liteyuki/core/__init__.py
@@ -1,6 +1,5 @@
import multiprocessing
-from .spawn_process import *
from .manager import *
__all__ = [
diff --git a/liteyuki/core/manager.py b/liteyuki/core/manager.py
index 06d62eee..9cb368a8 100644
--- a/liteyuki/core/manager.py
+++ b/liteyuki/core/manager.py
@@ -36,13 +36,6 @@ class ProcessManager:
self.targets: dict[str, tuple[callable, tuple, dict]] = {}
self.processes: dict[str, Process] = {}
- set_channels({
- "nonebot-active" : Channel(_id="nonebot-active"),
- "melobot-active" : Channel(_id="melobot-active"),
- "nonebot-passive": Channel(_id="nonebot-passive"),
- "melobot-passive": Channel(_id="melobot-passive"),
- })
-
def start(self, name: str, delay: int = 0):
"""
开启后自动监控进程,并添加到进程字典中
@@ -61,7 +54,7 @@ class ProcessManager:
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=(chan_active, chan_passive, *self.targets[name][1]),
+ process = Process(target=self.targets[name][0], args=self.targets[name][1],
kwargs=self.targets[name][2])
self.processes[name] = process
process.start()
@@ -88,9 +81,30 @@ class ProcessManager:
else:
threading.Thread(target=_start).start()
- def add_target(self, name: str, target, *args, **kwargs):
+ def add_target(self, name: str, target, args: tuple = (), kwargs=None):
+ """
+ 添加进程
+ Args:
+ name: 进程名,用于获取和唯一标识
+ target: 进程函数
+ args: 进程函数参数
+ kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive
+ """
+ if kwargs is None:
+ kwargs = {}
+ chan_active = Channel(_id=f"{name}-active")
+ chan_passive = Channel(_id=f"{name}-passive")
+ kwargs["chan_active"] = chan_active
+ kwargs["chan_passive"] = chan_passive
self.targets[name] = (target, args, kwargs)
+ set_channels(
+ {
+ f"{name}-active" : chan_active,
+ f"{name}-passive": chan_passive
+ }
+ )
+
def join(self):
for name, process in self.targets:
process.join()
diff --git a/liteyuki/core/spawn_process.py b/liteyuki/core/spawn_process.py
deleted file mode 100644
index 040392aa..00000000
--- a/liteyuki/core/spawn_process.py
+++ /dev/null
@@ -1,56 +0,0 @@
-from typing import Optional, TYPE_CHECKING
-
-import nonebot
-
-from liteyuki.core.nb import adapter_manager, driver_manager
-from liteyuki.comm.channel import set_channel
-
-if TYPE_CHECKING:
- from liteyuki.comm.channel import Channel
-
-timeout_limit: int = 20
-
-"""导出对象,用于主进程与nonebot通信"""
-_channels = {}
-
-
-def nb_run(chan_active: "Channel", chan_passive: "Channel", *args, **kwargs):
- """
- 初始化NoneBot并运行在子进程
- Args:
-
- chan_active:
- chan_passive:
- **kwargs:
-
- Returns:
-
- """
- set_channel("nonebot-active", chan_active)
- set_channel("nonebot-passive", chan_passive)
- nonebot.init(**kwargs)
- driver_manager.init(config=kwargs)
- adapter_manager.init(kwargs)
- adapter_manager.register()
- nonebot.load_plugin("src.liteyuki_main")
- nonebot.run()
-
-
-def mb_run(chan_active: "Channel", chan_passive: "Channel", *args, **kwargs):
- """
- 初始化MeloBot并运行在子进程
- Args:
- chan_active
- chan_passive
- *args:
- **kwargs:
-
- Returns:
-
- """
- set_channel("melobot-active", chan_active)
- set_channel("melobot-passive", chan_passive)
-
- # bot = MeloBot(__name__)
- # bot.init(AbstractConnector(cd_time=0))
- # bot.run()
diff --git a/liteyuki/dev/__init__.py b/liteyuki/dev/__init__.py
new file mode 100644
index 00000000..893c1703
--- /dev/null
+++ b/liteyuki/dev/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+"""
+该模块用于存放一些开发工具
+"""
diff --git a/liteyuki/dev/observer.py b/liteyuki/dev/observer.py
new file mode 100644
index 00000000..8143c7fa
--- /dev/null
+++ b/liteyuki/dev/observer.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+"""
+
+"""
+import watchdog
\ No newline at end of file
diff --git a/liteyuki/plugin/load.py b/liteyuki/plugin/load.py
index e073c19c..a997f1e2 100644
--- a/liteyuki/plugin/load.py
+++ b/liteyuki/plugin/load.py
@@ -44,8 +44,12 @@ def load_plugin(module_path: str | Path) -> Optional[Plugin]:
module_name=module_path,
metadata=module.__dict__.get("__plugin_metadata__", None)
)
+ display_name = module.__name__.split(".")[-1]
+ if module.__dict__.get("__plugin_meta__"):
+ metadata: "PluginMetadata" = module.__dict__["__plugin_meta__"]
+ display_name = f"{metadata.name}({module.__name__.split('.')[-1]})"
logger.opt(colors=True).success(
- f'Succeeded to load liteyuki plugin "{module.__name__.split(".")[-1]}"'
+ f'Succeeded to load liteyuki plugin "{display_name}"'
)
return _plugins[module.__name__]
diff --git a/liteyuki/plugins/code_watchdog/__init__.py b/liteyuki/plugins/code_watchdog/__init__.py
new file mode 100644
index 00000000..7500e8ab
--- /dev/null
+++ b/liteyuki/plugins/code_watchdog/__init__.py
@@ -0,0 +1,21 @@
+# -*- 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")
diff --git a/liteyuki/plugins/code_watchdog/observer.py b/liteyuki/plugins/code_watchdog/observer.py
new file mode 100644
index 00000000..c081c854
--- /dev/null
+++ b/liteyuki/plugins/code_watchdog/observer.py
@@ -0,0 +1,44 @@
+# -*- 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()
\ No newline at end of file
diff --git a/liteyuki/plugins/nonebot_launcher/__init__.py b/liteyuki/plugins/nonebot_launcher/__init__.py
new file mode 100644
index 00000000..ee90b158
--- /dev/null
+++ b/liteyuki/plugins/nonebot_launcher/__init__.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/8/11 下午5:24
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : __init__.py.py
+@Software: PyCharm
+"""
+
+import nonebot
+
+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):
+ """
+ 初始化NoneBot并运行在子进程
+ Args:
+
+ chan_active:
+ chan_passive:
+ **kwargs:
+
+ Returns:
+ """
+ # 给子进程传递通道对象
+ set_channel("nonebot-active", chan_active)
+ set_channel("nonebot-passive", chan_passive)
+
+ kwargs.update(kwargs.get("nonebot", {})) # nonebot配置优先
+ nonebot.init(**kwargs)
+
+ driver_manager.init(config=kwargs)
+ adapter_manager.init(kwargs)
+ adapter_manager.register()
+ nonebot.load_plugin("src.liteyuki_main")
+ nonebot.run()
+
+
+if IS_MAIN_PROCESS:
+ @liteyuki.on_after_start
+ def start_run_nonebot():
+ liteyuki.process_manager.add_target(name="nonebot", target=nb_run, args=(), kwargs=liteyuki.config)
+ liteyuki.process_manager.start("nonebot")
diff --git a/liteyuki/core/nb/adapter_manager/__init__.py b/liteyuki/plugins/nonebot_launcher/nb_utils/adapter_manager/__init__.py
similarity index 100%
rename from liteyuki/core/nb/adapter_manager/__init__.py
rename to liteyuki/plugins/nonebot_launcher/nb_utils/adapter_manager/__init__.py
diff --git a/liteyuki/core/nb/adapter_manager/onebot.py b/liteyuki/plugins/nonebot_launcher/nb_utils/adapter_manager/onebot.py
similarity index 100%
rename from liteyuki/core/nb/adapter_manager/onebot.py
rename to liteyuki/plugins/nonebot_launcher/nb_utils/adapter_manager/onebot.py
diff --git a/liteyuki/core/nb/adapter_manager/satori.py b/liteyuki/plugins/nonebot_launcher/nb_utils/adapter_manager/satori.py
similarity index 100%
rename from liteyuki/core/nb/adapter_manager/satori.py
rename to liteyuki/plugins/nonebot_launcher/nb_utils/adapter_manager/satori.py
diff --git a/liteyuki/core/nb/driver_manager/__init__.py b/liteyuki/plugins/nonebot_launcher/nb_utils/driver_manager/__init__.py
similarity index 100%
rename from liteyuki/core/nb/driver_manager/__init__.py
rename to liteyuki/plugins/nonebot_launcher/nb_utils/driver_manager/__init__.py
diff --git a/liteyuki/core/nb/driver_manager/auto_set_env.py b/liteyuki/plugins/nonebot_launcher/nb_utils/driver_manager/auto_set_env.py
similarity index 100%
rename from liteyuki/core/nb/driver_manager/auto_set_env.py
rename to liteyuki/plugins/nonebot_launcher/nb_utils/driver_manager/auto_set_env.py
diff --git a/liteyuki/core/nb/driver_manager/defines.py b/liteyuki/plugins/nonebot_launcher/nb_utils/driver_manager/defines.py
similarity index 100%
rename from liteyuki/core/nb/driver_manager/defines.py
rename to liteyuki/plugins/nonebot_launcher/nb_utils/driver_manager/defines.py
diff --git a/liteyuki/plugins/plugins_loader/__init__.py b/liteyuki/plugins/plugins_loader/__init__.py
new file mode 100644
index 00000000..e8f7010d
--- /dev/null
+++ b/liteyuki/plugins/plugins_loader/__init__.py
@@ -0,0 +1,20 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/8/11 下午10:02
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : __init__.py.py
+@Software: PyCharm
+"""
+from liteyuki.plugin import PluginMetadata, load_plugins
+
+__plugin_meta__ = PluginMetadata(
+ name="外部轻雪插件加载器",
+ version="0.1.0",
+ author="snowykami",
+ description="插件加载器,用于加载轻雪原生插件"
+)
+
+load_plugins("src/liteyuki_plugins")
diff --git a/liteyuki/plugins/what_litaco/__init__.py b/liteyuki/plugins/what_litaco/__init__.py
deleted file mode 100644
index ffce5f45..00000000
--- a/liteyuki/plugins/what_litaco/__init__.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
-
-@Time : 2024/7/25 上午2:28
-@Author : snowykami
-@Email : snowykami@outlook.com
-@File : __init__.py
-@Software: PyCharm
-"""
-# -*- coding: utf-8 -*-
-#
-# Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
-#
-# @Time : 2024/7/22 上午11:25
-# @Author : snowykami
-# @Email : snowykami@outlook.com
-# @File : asa.py
-# @Software: PyCharm
\ No newline at end of file
diff --git a/main.py b/main.py
index 7efe0279..a2a83b1f 100644
--- a/main.py
+++ b/main.py
@@ -1,6 +1,9 @@
+"""
+启动脚本,会执行一些启动的操作,比如加载配置文件,初始化 bot 实例等。
+"""
from liteyuki import LiteyukiBot
-from liteyuki.config import load_from_yaml
+from liteyuki.config import load_config_in_default
if __name__ == "__main__":
- bot = LiteyukiBot(**load_from_yaml("config.yml"))
+ bot = LiteyukiBot(**load_config_in_default())
bot.run()
diff --git a/src/liteyuki_plugins/README.md b/src/liteyuki_plugins/README.md
deleted file mode 100644
index aba0376d..00000000
--- a/src/liteyuki_plugins/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# 说明
-
-此目录为**轻雪插件**目录,非其他插件目录。
\ No newline at end of file
diff --git a/liteyuki/plugins/lifespan_monitor.py b/src/liteyuki_plugins/lifespan_monitor.py
similarity index 71%
rename from liteyuki/plugins/lifespan_monitor.py
rename to src/liteyuki_plugins/lifespan_monitor.py
index f49d5bca..ca222bc8 100644
--- a/liteyuki/plugins/lifespan_monitor.py
+++ b/src/liteyuki_plugins/lifespan_monitor.py
@@ -8,20 +8,22 @@
# @File : asa.py
# @Software: PyCharm
import asyncio
+import multiprocessing
from liteyuki.plugin import PluginMetadata
from liteyuki import get_bot, logger
from liteyuki.comm.channel import get_channel
__plugin_meta__ = PluginMetadata(
- name="lifespan_monitor",
+ name="生命周期日志",
)
bot = get_bot()
-nbp_chan = get_channel("nonebot-passive")
-mbp_chan = get_channel("melobot-passive")
+# nbp_chan = get_channel("nonebot-passive")
+# mbp_chan = get_channel("melobot-passive")
+
@bot.on_before_start
def _():
logger.info("生命周期监控器:准备启动")
@@ -41,15 +43,3 @@ def _():
@bot.on_after_start
def _():
logger.info("生命周期监控器:启动完成")
-
-
-@bot.on_after_start
-async def _():
- logger.info("生命周期监控器:启动完成")
-
-
-
-# @mbp_chan.on_receive()
-# @nbp_chan.on_receive()
-# async def _(data):
-# print("主进程收到数据", data)
diff --git a/src/liteyuki_plugins/process_manager/__init__.py b/src/liteyuki_plugins/process_manager/__init__.py
new file mode 100644
index 00000000..dcdbd336
--- /dev/null
+++ b/src/liteyuki_plugins/process_manager/__init__.py
@@ -0,0 +1,9 @@
+from liteyuki.plugin import PluginMetadata
+
+
+__plugin_meta__ = PluginMetadata(
+ name="进程管理器",
+ version="0.1.0",
+ author="snowykami",
+ description="进程管理器,用于管理子进程"
+)
\ No newline at end of file
diff --git a/liteyuki/plugins/reloader_monitor.py b/src/liteyuki_plugins/reboot.py
similarity index 71%
rename from liteyuki/plugins/reloader_monitor.py
rename to src/liteyuki_plugins/reboot.py
index 4900e215..ddb31053 100644
--- a/liteyuki/plugins/reloader_monitor.py
+++ b/src/liteyuki_plugins/reboot.py
@@ -2,9 +2,9 @@
"""
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
-@Time : 2024/8/10 下午5:18
+@Time : 2024/8/11 下午8:22
@Author : snowykami
@Email : snowykami@outlook.com
-@File : reloader_monitor.py
+@File : reloader.py.py
@Software: PyCharm
-"""
\ No newline at end of file
+"""
diff --git a/liteyuki/plugins/register_service.py b/src/liteyuki_plugins/register_service.py
similarity index 100%
rename from liteyuki/plugins/register_service.py
rename to src/liteyuki_plugins/register_service.py
diff --git a/liteyuki/plugins/resource_loader/__init__.py b/src/liteyuki_plugins/resource_loader/__init__.py
similarity index 100%
rename from liteyuki/plugins/resource_loader/__init__.py
rename to src/liteyuki_plugins/resource_loader/__init__.py
diff --git a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py
index 386f3c63..63e21d69 100644
--- a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py
+++ b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py
@@ -16,12 +16,4 @@ __plugin_meta__ = PluginMetadata(
"toggleable" : True,
"default_enable": True,
}
-)
-
-# chan = get_channel("nonebot-passive")
-#
-#
-# @chan.on_receive()
-# async def _(d):
-# print("CRT子进程接收到数据:", d)
-# chan.send("CRT子进程已接收到数据")
+)
\ No newline at end of file
diff --git a/tests/test_config_load.py b/tests/test_config_load.py
new file mode 100644
index 00000000..d4c30397
--- /dev/null
+++ b/tests/test_config_load.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+"""
+Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
+
+@Time : 2024/8/11 下午11:07
+@Author : snowykami
+@Email : snowykami@outlook.com
+@File : test_config_load.py
+@Software: PyCharm
+"""
+import json
+import os
+import sys
+
+sys.path.insert(0, os.getcwd())
+from liteyuki.config import load_config_in_default
+
+
+def test_default_load():
+ config = load_config_in_default()
+ print(json.dumps(config, indent=4, ensure_ascii=False))
diff --git a/tests/test_core.py b/tests/test_core.py
index 81da20e8..b8983b04 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -1,4 +1,4 @@
-from src.liteyuki import LiteyukiBot
+from liteyuki import LiteyukiBot
if __name__ == "__main__":
lyb = LiteyukiBot()
diff --git a/tests/test_dll.py b/tests/test_dll.py
deleted file mode 100644
index 8d443788..00000000
--- a/tests/test_dll.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
-
-@Time : 2024/8/7 下午11:44
-@Author : snowykami
-@Email : snowykami@outlook.com
-@File : test_dll.py
-@Software: PyCharm
-"""
-from src.utils.extension import load_lib
-
-
-a = load_lib("src/libs/ly_api")
-
-a.Register("sss", "sss", 64, "sss", "sss")
\ No newline at end of file