mirror of
https://github.com/LiteyukiStudio/LiteyukiBot.git
synced 2024-11-11 02:57:23 +08:00
🐛 fix 通道无法在进程内传递消息的问题
This commit is contained in:
parent
c3fc5d429b
commit
37b8d969b1
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,6 +1,7 @@
|
|||||||
.venv/
|
.venv/
|
||||||
.idea/
|
.idea/
|
||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
node_modules/
|
node_modules/
|
||||||
data/
|
data/
|
||||||
db/
|
db/
|
||||||
@ -11,19 +12,23 @@ __pycache__/
|
|||||||
*.pyd
|
*.pyd
|
||||||
*.pyw
|
*.pyw
|
||||||
/plugins/
|
/plugins/
|
||||||
|
|
||||||
|
#config
|
||||||
|
config/
|
||||||
|
!config/default.yml
|
||||||
_config.yml
|
_config.yml
|
||||||
config.yml
|
config.yml
|
||||||
config.example.yml
|
config.example.yml
|
||||||
compile.bat
|
|
||||||
src/resources/templates/latest-debug.html
|
|
||||||
# vuepress
|
# vuepress
|
||||||
.github
|
.github
|
||||||
|
|
||||||
test.py
|
# mupy
|
||||||
line_count.py
|
|
||||||
mypy.ini
|
mypy.ini
|
||||||
|
|
||||||
# nuitka
|
# nuitka
|
||||||
|
compile.bat
|
||||||
|
src/resources/templates/latest-debug.html
|
||||||
main.build/
|
main.build/
|
||||||
main.dist/
|
main.dist/
|
||||||
main.exe
|
main.exe
|
||||||
|
@ -9,7 +9,7 @@ bgImageDark:
|
|||||||
bgImageStyle:
|
bgImageStyle:
|
||||||
background-attachment: fixed
|
background-attachment: fixed
|
||||||
heroText: LiteyukiBot
|
heroText: LiteyukiBot
|
||||||
tagline: LiteyukiBot 轻雪机器人,基于NoneBot2构建的综合应用型聊天机器人
|
tagline: LiteyukiBot 轻雪机器人,综合性的机器人应用及管理框架
|
||||||
|
|
||||||
actions:
|
actions:
|
||||||
- text: 快速部署
|
- text: 快速部署
|
||||||
@ -30,9 +30,9 @@ highlights:
|
|||||||
background-repeat: repeat
|
background-repeat: repeat
|
||||||
background-size: initial
|
background-size: initial
|
||||||
features:
|
features:
|
||||||
- title: 基于NoneBot2
|
- title: 支持多种框架
|
||||||
icon: robot
|
icon: robot
|
||||||
details: 拥有良好的生态支持
|
details: 兼容nonebot,melobot等,拥有良好的生态支持
|
||||||
link: https://nonebot.dev/
|
link: https://nonebot.dev/
|
||||||
|
|
||||||
- title: 便捷管理
|
- title: 便捷管理
|
||||||
|
@ -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
|
```yaml
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from liteyuki.bot import (
|
from liteyuki.bot import (
|
||||||
LiteyukiBot,
|
LiteyukiBot,
|
||||||
get_bot
|
get_bot,
|
||||||
|
get_config
|
||||||
)
|
)
|
||||||
|
|
||||||
from liteyuki.comm import (
|
from liteyuki.comm import (
|
||||||
|
@ -4,21 +4,22 @@ import platform
|
|||||||
import sys
|
import sys
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from typing import Any, Optional
|
from typing import Any, Iterable, Optional
|
||||||
|
|
||||||
from watchdog.events import FileSystemEventHandler
|
from watchdog.events import FileSystemEventHandler
|
||||||
from watchdog.observers import Observer
|
from watchdog.observers import Observer
|
||||||
|
|
||||||
from liteyuki.bot.lifespan import (LIFESPAN_FUNC, Lifespan)
|
from liteyuki.bot.lifespan import (LIFESPAN_FUNC, Lifespan)
|
||||||
|
from liteyuki.comm import get_channel
|
||||||
from liteyuki.core import IS_MAIN_PROCESS
|
from liteyuki.core import IS_MAIN_PROCESS
|
||||||
from liteyuki.core.manager import ProcessManager
|
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.log import init_log, logger
|
||||||
from liteyuki.plugin import load_plugins
|
from liteyuki.plugin import load_plugins
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"LiteyukiBot",
|
"LiteyukiBot",
|
||||||
"get_bot"
|
"get_bot",
|
||||||
|
"get_config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -26,15 +27,18 @@ class LiteyukiBot:
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
global _BOT_INSTANCE
|
global _BOT_INSTANCE
|
||||||
_BOT_INSTANCE = self # 引用
|
_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.process_manager: ProcessManager = ProcessManager(bot=self)
|
||||||
self.loop = asyncio.new_event_loop()
|
self.loop = asyncio.new_event_loop()
|
||||||
asyncio.set_event_loop(self.loop)
|
asyncio.set_event_loop(self.loop)
|
||||||
self.loop_thread = threading.Thread(target=self.loop.run_forever, daemon=True)
|
self.loop_thread = threading.Thread(target=self.loop.run_forever, daemon=True)
|
||||||
|
self.stop_event = threading.Event()
|
||||||
self.call_restart_count = 0
|
self.call_restart_count = 0
|
||||||
|
|
||||||
print("\033[34m" + r"""
|
print("\033[34m" + r"""
|
||||||
@ -48,58 +52,22 @@ $$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \__$$ |$$ |$$ \ _$$ |_
|
|||||||
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
$$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |
|
||||||
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
$$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
||||||
""" + "\033[0m")
|
""" + "\033[0m")
|
||||||
|
load_plugins("liteyuki/plugins") # 加载轻雪插件
|
||||||
|
logger.info("Liteyuki is initializing...")
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
load_plugins("liteyuki/plugins") # 加载轻雪插件
|
"""
|
||||||
|
启动逻辑
|
||||||
|
"""
|
||||||
self.loop_thread.start() # 启动事件循环
|
self.loop_thread.start() # 启动事件循环
|
||||||
asyncio.run(self.lifespan.before_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()) # 启动后钩子
|
asyncio.run(self.lifespan.after_start()) # 启动后钩子
|
||||||
|
self.start_watcher() # 启动文件监视器,后续准备插件化
|
||||||
self.start_watcher() # 启动文件监视器
|
self.keep_running()
|
||||||
|
|
||||||
def start_watcher(self):
|
def start_watcher(self):
|
||||||
if self.config.get("debug", False):
|
pass
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
def restart(self, delay: int = 0):
|
def restart(self, delay: int = 0):
|
||||||
"""
|
"""
|
||||||
@ -135,15 +103,22 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|||||||
Returns:
|
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()) # 重启前钩子
|
||||||
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:
|
||||||
self.process_manager.terminate(name)
|
chan_active = get_channel(f"{name}-active")
|
||||||
|
chan_active.send(1)
|
||||||
else:
|
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):
|
def init(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -239,6 +214,9 @@ $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/
|
|||||||
"""
|
"""
|
||||||
return self.lifespan.on_after_nonebot_init(func)
|
return self.lifespan.on_after_nonebot_init(func)
|
||||||
|
|
||||||
|
def keep_running(self):
|
||||||
|
self.stop_event.wait()
|
||||||
|
|
||||||
|
|
||||||
_BOT_INSTANCE: Optional[LiteyukiBot] = None
|
_BOT_INSTANCE: Optional[LiteyukiBot] = None
|
||||||
|
|
||||||
@ -254,3 +232,37 @@ def get_bot() -> Optional[LiteyukiBot]:
|
|||||||
else:
|
else:
|
||||||
# 从多进程上下文中获取
|
# 从多进程上下文中获取
|
||||||
pass
|
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
|
||||||
|
|
||||||
|
@ -35,13 +35,14 @@ _callback_funcs: dict[str, ON_RECEIVE_FUNC] = {}
|
|||||||
|
|
||||||
class Channel:
|
class Channel:
|
||||||
"""
|
"""
|
||||||
通道类,用于进程间通信,进程内不可用,仅限主进程和子进程之间通信
|
通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者
|
||||||
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
|
有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, _id: str):
|
def __init__(self, _id: str):
|
||||||
self.main_send_conn, self.sub_receive_conn = Pipe()
|
# self.main_send_conn, self.sub_receive_conn = Pipe()
|
||||||
self.sub_send_conn, self.main_receive_conn = Pipe()
|
# self.sub_send_conn, self.main_receive_conn = Pipe()
|
||||||
|
self.conn_send, self.conn_recv = Pipe()
|
||||||
self._closed = False
|
self._closed = False
|
||||||
self._on_main_receive_funcs: list[str] = []
|
self._on_main_receive_funcs: list[str] = []
|
||||||
self._on_sub_receive_funcs: list[str] = []
|
self._on_sub_receive_funcs: list[str] = []
|
||||||
@ -61,12 +62,7 @@ class Channel:
|
|||||||
"""
|
"""
|
||||||
if self._closed:
|
if self._closed:
|
||||||
raise RuntimeError("Cannot send to a closed channel")
|
raise RuntimeError("Cannot send to a closed channel")
|
||||||
if IS_MAIN_PROCESS:
|
self.conn_send.send(data)
|
||||||
print("主进程发送数据:", data)
|
|
||||||
self.main_send_conn.send(data)
|
|
||||||
else:
|
|
||||||
print("子进程发送数据:", data)
|
|
||||||
self.sub_send_conn.send(data)
|
|
||||||
|
|
||||||
def receive(self) -> Any:
|
def receive(self) -> Any:
|
||||||
"""
|
"""
|
||||||
@ -77,14 +73,7 @@ class Channel:
|
|||||||
raise RuntimeError("Cannot receive from a closed channel")
|
raise RuntimeError("Cannot receive from a closed channel")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
# 判断receiver是否为None或者receiver是否等于接收者,是则接收数据,否则不动数据
|
data = self.conn_recv.recv()
|
||||||
if IS_MAIN_PROCESS:
|
|
||||||
data = self.main_receive_conn.recv()
|
|
||||||
print("主进程接收数据:", data)
|
|
||||||
else:
|
|
||||||
data = self.sub_receive_conn.recv()
|
|
||||||
print("子进程接收数据:", data)
|
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
@ -92,10 +81,8 @@ class Channel:
|
|||||||
关闭通道
|
关闭通道
|
||||||
"""
|
"""
|
||||||
self._closed = True
|
self._closed = True
|
||||||
self.sub_receive_conn.close()
|
self.conn_send.close()
|
||||||
self.main_send_conn.close()
|
self.conn_recv.close()
|
||||||
self.sub_send_conn.close()
|
|
||||||
self.main_receive_conn.close()
|
|
||||||
|
|
||||||
def on_receive(self, filter_func: Optional[FILTER_FUNC] = None) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]:
|
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
|
self.is_main_receive_loop_running = True
|
||||||
while not self._closed:
|
while not self._closed:
|
||||||
data = self.main_receive_conn.recv()
|
data = self.conn_recv.recv()
|
||||||
self._run_on_main_receive_funcs(data)
|
self._run_on_main_receive_funcs(data)
|
||||||
|
|
||||||
def _start_sub_receive_loop(self):
|
def _start_sub_receive_loop(self):
|
||||||
@ -167,7 +154,7 @@ class Channel:
|
|||||||
"""
|
"""
|
||||||
self.is_sub_receive_loop_running = True
|
self.is_sub_receive_loop_running = True
|
||||||
while not self._closed:
|
while not self._closed:
|
||||||
data = self.sub_receive_conn.recv()
|
data = self.conn_recv.recv()
|
||||||
self._run_on_sub_receive_funcs(data)
|
self._run_on_sub_receive_funcs(data)
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
@ -188,6 +175,8 @@ def set_channel(name: str, channel: Channel):
|
|||||||
name: 通道名称
|
name: 通道名称
|
||||||
channel: 通道实例
|
channel: 通道实例
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(channel, Channel):
|
||||||
|
raise TypeError(f"channel must be an instance of Channel, {type(channel)} found")
|
||||||
_channel[name] = channel
|
_channel[name] = channel
|
||||||
|
|
||||||
|
|
||||||
@ -198,7 +187,7 @@ def set_channels(channels: dict[str, Channel]):
|
|||||||
channels: 通道名称
|
channels: 通道名称
|
||||||
"""
|
"""
|
||||||
for name, channel in channels.items():
|
for name, channel in channels.items():
|
||||||
_channel[name] = channel
|
set_channel(name, channel)
|
||||||
|
|
||||||
|
|
||||||
def get_channel(name: str) -> Optional[Channel]:
|
def get_channel(name: str) -> Optional[Channel]:
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
|
"""
|
||||||
|
该模块用于常用配置文件的加载
|
||||||
|
多配置文件编写原则:
|
||||||
|
1.尽量不要冲突: 一个键不要多次出现
|
||||||
|
2.分工明确: 每个配置文件给一个或一类服务提供配置
|
||||||
|
3.扁平化编写: 配置文件尽量扁平化,不要出现过多的嵌套
|
||||||
|
4.注意冲突时的优先级: 项目目录下的配置文件优先级高于config目录下的配置文件
|
||||||
|
5.请不要将需要动态加载的内容写入配置文件,你应该使用其他储存方式
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import List
|
from typing import Any, List
|
||||||
|
|
||||||
import nonebot
|
import toml
|
||||||
import yaml
|
import yaml
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from liteyuki import logger
|
||||||
|
|
||||||
config = {} # 主进程全局配置,确保加载后读取
|
_SUPPORTED_CONFIG_FORMATS = (".yaml", ".yml", ".json", ".toml")
|
||||||
|
|
||||||
|
|
||||||
class SatoriNodeConfig(BaseModel):
|
class SatoriNodeConfig(BaseModel):
|
||||||
@ -32,18 +44,100 @@ class BasicConfig(BaseModel):
|
|||||||
data_path: str = "data/liteyuki"
|
data_path: str = "data/liteyuki"
|
||||||
|
|
||||||
|
|
||||||
def load_from_yaml(file: str) -> dict:
|
def flat_config(config: dict[str, Any]) -> dict[str, Any]:
|
||||||
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)
|
|
||||||
|
|
||||||
with open(file, "r", encoding="utf-8") as f:
|
{a:{b:{c:1}}} -> {"a.b.c": 1}
|
||||||
conf = yaml.load(f, Loader=yaml.FullLoader)
|
Args:
|
||||||
config = conf
|
config: 配置项目
|
||||||
if conf is None:
|
|
||||||
nonebot.logger.warning(f"Config file {file} is empty, use default config. please modify it and restart")
|
Returns:
|
||||||
conf = BasicConfig().dict()
|
扁平化后的配置文件,但也包含原有的键值对
|
||||||
return conf
|
"""
|
||||||
|
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
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
from .spawn_process import *
|
|
||||||
from .manager import *
|
from .manager import *
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
|
@ -36,13 +36,6 @@ class ProcessManager:
|
|||||||
self.targets: dict[str, tuple[callable, tuple, dict]] = {}
|
self.targets: dict[str, tuple[callable, tuple, dict]] = {}
|
||||||
self.processes: dict[str, Process] = {}
|
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):
|
def start(self, name: str, delay: int = 0):
|
||||||
"""
|
"""
|
||||||
开启后自动监控进程,并添加到进程字典中
|
开启后自动监控进程,并添加到进程字典中
|
||||||
@ -61,7 +54,7 @@ class ProcessManager:
|
|||||||
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")
|
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])
|
kwargs=self.targets[name][2])
|
||||||
self.processes[name] = process
|
self.processes[name] = process
|
||||||
process.start()
|
process.start()
|
||||||
@ -88,9 +81,30 @@ class ProcessManager:
|
|||||||
else:
|
else:
|
||||||
threading.Thread(target=_start).start()
|
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)
|
self.targets[name] = (target, args, kwargs)
|
||||||
|
|
||||||
|
set_channels(
|
||||||
|
{
|
||||||
|
f"{name}-active" : chan_active,
|
||||||
|
f"{name}-passive": chan_passive
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def join(self):
|
def join(self):
|
||||||
for name, process in self.targets:
|
for name, process in self.targets:
|
||||||
process.join()
|
process.join()
|
||||||
|
@ -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()
|
|
4
liteyuki/dev/__init__.py
Normal file
4
liteyuki/dev/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
该模块用于存放一些开发工具
|
||||||
|
"""
|
5
liteyuki/dev/observer.py
Normal file
5
liteyuki/dev/observer.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
|
||||||
|
"""
|
||||||
|
import watchdog
|
@ -44,8 +44,12 @@ def load_plugin(module_path: str | Path) -> Optional[Plugin]:
|
|||||||
module_name=module_path,
|
module_name=module_path,
|
||||||
metadata=module.__dict__.get("__plugin_metadata__", None)
|
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(
|
logger.opt(colors=True).success(
|
||||||
f'Succeeded to load liteyuki plugin "<y>{module.__name__.split(".")[-1]}</y>"'
|
f'Succeeded to load liteyuki plugin "<y>{display_name}</y>"'
|
||||||
)
|
)
|
||||||
return _plugins[module.__name__]
|
return _plugins[module.__name__]
|
||||||
|
|
||||||
|
21
liteyuki/plugins/code_watchdog/__init__.py
Normal file
21
liteyuki/plugins/code_watchdog/__init__.py
Normal file
@ -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")
|
44
liteyuki/plugins/code_watchdog/observer.py
Normal file
44
liteyuki/plugins/code_watchdog/observer.py
Normal file
@ -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()
|
56
liteyuki/plugins/nonebot_launcher/__init__.py
Normal file
56
liteyuki/plugins/nonebot_launcher/__init__.py
Normal file
@ -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")
|
20
liteyuki/plugins/plugins_loader/__init__.py
Normal file
20
liteyuki/plugins/plugins_loader/__init__.py
Normal file
@ -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")
|
@ -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
|
|
7
main.py
7
main.py
@ -1,6 +1,9 @@
|
|||||||
|
"""
|
||||||
|
启动脚本,会执行一些启动的操作,比如加载配置文件,初始化 bot 实例等。
|
||||||
|
"""
|
||||||
from liteyuki import LiteyukiBot
|
from liteyuki import LiteyukiBot
|
||||||
from liteyuki.config import load_from_yaml
|
from liteyuki.config import load_config_in_default
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
bot = LiteyukiBot(**load_from_yaml("config.yml"))
|
bot = LiteyukiBot(**load_config_in_default())
|
||||||
bot.run()
|
bot.run()
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
# 说明
|
|
||||||
|
|
||||||
此目录为**轻雪插件**目录,非其他插件目录。
|
|
@ -8,20 +8,22 @@
|
|||||||
# @File : asa.py
|
# @File : asa.py
|
||||||
# @Software: PyCharm
|
# @Software: PyCharm
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import multiprocessing
|
||||||
|
|
||||||
from liteyuki.plugin import PluginMetadata
|
from liteyuki.plugin import PluginMetadata
|
||||||
from liteyuki import get_bot, logger
|
from liteyuki import get_bot, logger
|
||||||
from liteyuki.comm.channel import get_channel
|
from liteyuki.comm.channel import get_channel
|
||||||
|
|
||||||
__plugin_meta__ = PluginMetadata(
|
__plugin_meta__ = PluginMetadata(
|
||||||
name="lifespan_monitor",
|
name="生命周期日志",
|
||||||
)
|
)
|
||||||
|
|
||||||
bot = get_bot()
|
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
|
@bot.on_before_start
|
||||||
def _():
|
def _():
|
||||||
logger.info("生命周期监控器:准备启动")
|
logger.info("生命周期监控器:准备启动")
|
||||||
@ -41,15 +43,3 @@ def _():
|
|||||||
@bot.on_after_start
|
@bot.on_after_start
|
||||||
def _():
|
def _():
|
||||||
logger.info("生命周期监控器:启动完成")
|
logger.info("生命周期监控器:启动完成")
|
||||||
|
|
||||||
|
|
||||||
@bot.on_after_start
|
|
||||||
async def _():
|
|
||||||
logger.info("生命周期监控器:启动完成")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# @mbp_chan.on_receive()
|
|
||||||
# @nbp_chan.on_receive()
|
|
||||||
# async def _(data):
|
|
||||||
# print("主进程收到数据", data)
|
|
9
src/liteyuki_plugins/process_manager/__init__.py
Normal file
9
src/liteyuki_plugins/process_manager/__init__.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from liteyuki.plugin import PluginMetadata
|
||||||
|
|
||||||
|
|
||||||
|
__plugin_meta__ = PluginMetadata(
|
||||||
|
name="进程管理器",
|
||||||
|
version="0.1.0",
|
||||||
|
author="snowykami",
|
||||||
|
description="进程管理器,用于管理子进程"
|
||||||
|
)
|
@ -2,9 +2,9 @@
|
|||||||
"""
|
"""
|
||||||
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved
|
||||||
|
|
||||||
@Time : 2024/8/10 下午5:18
|
@Time : 2024/8/11 下午8:22
|
||||||
@Author : snowykami
|
@Author : snowykami
|
||||||
@Email : snowykami@outlook.com
|
@Email : snowykami@outlook.com
|
||||||
@File : reloader_monitor.py
|
@File : reloader.py.py
|
||||||
@Software: PyCharm
|
@Software: PyCharm
|
||||||
"""
|
"""
|
@ -17,11 +17,3 @@ __plugin_meta__ = PluginMetadata(
|
|||||||
"default_enable": True,
|
"default_enable": True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# chan = get_channel("nonebot-passive")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @chan.on_receive()
|
|
||||||
# async def _(d):
|
|
||||||
# print("CRT子进程接收到数据:", d)
|
|
||||||
# chan.send("CRT子进程已接收到数据")
|
|
||||||
|
21
tests/test_config_load.py
Normal file
21
tests/test_config_load.py
Normal file
@ -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))
|
@ -1,4 +1,4 @@
|
|||||||
from src.liteyuki import LiteyukiBot
|
from liteyuki import LiteyukiBot
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
lyb = LiteyukiBot()
|
lyb = LiteyukiBot()
|
||||||
|
@ -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")
|
|
Loading…
Reference in New Issue
Block a user