diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index bb680846..c75e4ca7 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -42,7 +42,9 @@ jobs: - name: 生成API markdown run: |- python -m pip install pydantic - python liteyuki/mkdoc.py + python -m docs/litedoc liteyuki -o docs/api -l zh-Hans + python -m docs/litedoc liteyuki -o docs/en/api -l en + - name: 构建文档 diff --git a/docs/dev/README.md b/docs/dev/README.md deleted file mode 100644 index 947ed654..00000000 --- a/docs/dev/README.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: 开发及贡献 -index: false -icon: laptop-code -category: 开发 ---- - - \ No newline at end of file diff --git a/docs/dev/api/README.md b/docs/dev/api/README.md deleted file mode 100644 index 181ec73d..00000000 --- a/docs/dev/api/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/bot/README.md b/docs/dev/api/bot/README.md deleted file mode 100644 index 9f019444..00000000 --- a/docs/dev/api/bot/README.md +++ /dev/null @@ -1,581 +0,0 @@ ---- -title: liteyuki.bot -index: true -icon: laptop-code -category: API ---- - -### ***def*** `get_bot() -> LiteyukiBot` - -获取轻雪实例 - - - -Returns: - - LiteyukiBot: 当前的轻雪实例 - -
-源代码 - -```python -def get_bot() -> LiteyukiBot: - """ - 获取轻雪实例 - - Returns: - LiteyukiBot: 当前的轻雪实例 - """ - if IS_MAIN_PROCESS: - if _BOT_INSTANCE is None: - raise RuntimeError('Liteyuki instance not initialized.') - return _BOT_INSTANCE - else: - raise RuntimeError("Can't get bot instance in sub process.") -``` -
- -### ***def*** `get_config(key: str, default: Any) -> Any` - -获取配置 - -Args: - - key: 配置键 - - default: 默认值 - - - -Returns: - - Any: 配置值 - -
-源代码 - -```python -def get_config(key: str, default: Any=None) -> Any: - """ - 获取配置 - Args: - key: 配置键 - default: 默认值 - - Returns: - Any: 配置值 - """ - return get_bot().config.get(key, default) -``` -
- -### ***def*** `get_config_with_compat(key: str, compat_keys: tuple[str], default: Any) -> Any` - -获取配置,兼容旧版本 - -Args: - - key: 配置键 - - compat_keys: 兼容键 - - default: 默认值 - - - -Returns: - - Any: 配置值 - -
-源代码 - -```python -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 get_bot().config: - return get_bot().config[key] - for compat_key in compat_keys: - if compat_key in get_bot().config: - logger.warning(f'Config key "{compat_key}" will be deprecated, use "{key}" instead.') - return get_bot().config[compat_key] - return default -``` -
- -### ***def*** `print_logo() -> None` - - - -
-源代码 - -```python -def print_logo(): - print('\x1b[34m' + '\n __ ______ ________ ________ __ __ __ __ __ __ ______ \n / | / |/ |/ |/ \\ / |/ | / |/ | / |/ |\n $$ | $$$$$$/ $$$$$$$$/ $$$$$$$$/ $$ \\ /$$/ $$ | $$ |$$ | /$$/ $$$$$$/ \n $$ | $$ | $$ | $$ |__ $$ \\/$$/ $$ | $$ |$$ |/$$/ $$ | \n $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |$$ $$< $$ | \n $$ | $$ | $$ | $$$$$/ $$$$/ $$ | $$ |$$$$$ \\ $$ | \n $$ |_____ _$$ |_ $$ | $$ |_____ $$ | $$ \\__$$ |$$ |$$ \\ _$$ |_ \n $$ |/ $$ | $$ | $$ | $$ | $$ $$/ $$ | $$ |/ $$ |\n $$$$$$$$/ $$$$$$/ $$/ $$$$$$$$/ $$/ $$$$$$/ $$/ $$/ $$$$$$/ \n ' + '\x1b[0m') -``` -
- -### ***class*** `LiteyukiBot` - - - -###   ***def*** `__init__(self) -> None` - - 初始化轻雪实例 - -Args: - - *args: - - **kwargs: 配置 - -
-源代码 - -```python -def __init__(self, *args, **kwargs) -> None: - """ - 初始化轻雪实例 - Args: - *args: - **kwargs: 配置 - - """ - '常规操作' - print_logo() - global _BOT_INSTANCE - _BOT_INSTANCE = self - '配置' - self.config: dict[str, Any] = kwargs - '初始化' - self.init(**self.config) - logger.info('Liteyuki is initializing...') - '生命周期管理' - self.lifespan = Lifespan() - self.process_manager: ProcessManager = ProcessManager(lifespan=self.lifespan) - '事件循环' - self.loop = asyncio.new_event_loop() - asyncio.set_event_loop(self.loop) - self.stop_event = threading.Event() - self.call_restart_count = 0 - '加载插件加载器' - load_plugin('liteyuki.plugins.plugin_loader') - '信号处理' - signal.signal(signal.SIGINT, self._handle_exit) - signal.signal(signal.SIGTERM, self._handle_exit) - atexit.register(self.process_manager.terminate_all) -``` -
- -###   ***def*** `run(self) -> None` - - 启动逻辑 - -
-源代码 - -```python -def run(self): - """ - 启动逻辑 - """ - self.lifespan.before_start() - self.process_manager.start_all() - self.lifespan.after_start() - self.keep_alive() -``` -
- -###   ***def*** `keep_alive(self) -> None` - - 保持轻雪运行 - -Returns: - -
-源代码 - -```python -def keep_alive(self): - """ - 保持轻雪运行 - Returns: - - """ - try: - while not self.stop_event.is_set(): - time.sleep(0.5) - except KeyboardInterrupt: - logger.info('Liteyuki is stopping...') - self.stop() -``` -
- -###   ***def*** `restart(self, delay: int) -> None` - - 重启轻雪本体 - -Returns: - -
-源代码 - -```python -def restart(self, delay: int=0): - """ - 重启轻雪本体 - Returns: - - """ - if self.call_restart_count < 1: - executable = sys.executable - args = sys.argv - logger.info('Restarting LiteyukiBot...') - time.sleep(delay) - if platform.system() == 'Windows': - cmd = 'start' - elif platform.system() == 'Linux': - cmd = 'nohup' - elif platform.system() == 'Darwin': - cmd = 'open' - else: - cmd = 'nohup' - self.process_manager.terminate_all() - threading.Thread(target=os.system, args=(f"{cmd} {executable} {' '.join(args)}",)).start() - sys.exit(0) - self.call_restart_count += 1 -``` -
- -###   ***def*** `restart_process(self, name: Optional[str]) -> None` - - 停止轻雪 - -Args: - - name: 进程名称, 默认为None, 所有进程 - -Returns: - -
-源代码 - -```python -def restart_process(self, name: Optional[str]=None): - """ - 停止轻雪 - Args: - name: 进程名称, 默认为None, 所有进程 - Returns: - """ - self.lifespan.before_process_shutdown() - self.lifespan.before_process_shutdown() - if name is not None: - chan_active = get_channel(f'{name}-active') - chan_active.send(1) - else: - for process_name in self.process_manager.processes: - chan_active = get_channel(f'{process_name}-active') - chan_active.send(1) -``` -
- -###   ***def*** `init(self) -> None` - - 初始化轻雪, 自动调用 - -Returns: - -
-源代码 - -```python -def init(self, *args, **kwargs): - """ - 初始化轻雪, 自动调用 - Returns: - - """ - self.init_logger() -``` -
- -###   ***def*** `init_logger(self) -> None` - -  - -
-源代码 - -```python -def init_logger(self): - init_log(config=self.config) -``` -
- -###   ***def*** `stop(self) -> None` - - 停止轻雪 - -Returns: - -
-源代码 - -```python -def stop(self): - """ - 停止轻雪 - Returns: - - """ - self.stop_event.set() - self.loop.stop() -``` -
- -###   ***def*** `on_before_start(self, func: LIFESPAN_FUNC) -> None` - - 注册启动前的函数 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_before_start(self, func: LIFESPAN_FUNC): - """ - 注册启动前的函数 - Args: - func: - - Returns: - - """ - return self.lifespan.on_before_start(func) -``` -
- -###   ***def*** `on_after_start(self, func: LIFESPAN_FUNC) -> None` - - 注册启动后的函数 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_after_start(self, func: LIFESPAN_FUNC): - """ - 注册启动后的函数 - Args: - func: - - Returns: - - """ - return self.lifespan.on_after_start(func) -``` -
- -###   ***def*** `on_after_shutdown(self, func: LIFESPAN_FUNC) -> None` - - 注册停止后的函数:未实现 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_after_shutdown(self, func: LIFESPAN_FUNC): - """ - 注册停止后的函数:未实现 - Args: - func: - - Returns: - - """ - return self.lifespan.on_after_shutdown(func) -``` -
- -###   ***def*** `on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> None` - - 注册进程停止前的函数,为子进程停止时调用 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -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) -> None` - - 注册进程重启前的函数,为子进程重启时调用 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_before_process_restart(self, func: LIFESPAN_FUNC): - """ - 注册进程重启前的函数,为子进程重启时调用 - Args: - func: - - Returns: - - """ - return self.lifespan.on_before_process_restart(func) -``` -
- -###   ***def*** `on_after_restart(self, func: LIFESPAN_FUNC) -> None` - - 注册重启后的函数:未实现 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_after_restart(self, func: LIFESPAN_FUNC): - """ - 注册重启后的函数:未实现 - Args: - func: - - Returns: - - """ - return self.lifespan.on_after_restart(func) -``` -
- -###   ***def*** `on_after_nonebot_init(self, func: LIFESPAN_FUNC) -> None` - - 注册nonebot初始化后的函数 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_after_nonebot_init(self, func: LIFESPAN_FUNC): - """ - 注册nonebot初始化后的函数 - Args: - func: - - Returns: - - """ - return self.lifespan.on_after_nonebot_init(func) -``` -
- -### ***var*** `executable = sys.executable` - - - -### ***var*** `args = sys.argv` - - - -### ***var*** `chan_active = get_channel(f'{name}-active')` - - - -### ***var*** `cmd = 'start'` - - - -### ***var*** `chan_active = get_channel(f'{process_name}-active')` - - - -### ***var*** `cmd = 'nohup'` - - - -### ***var*** `cmd = 'open'` - - - -### ***var*** `cmd = 'nohup'` - - - diff --git a/docs/dev/api/bot/lifespan.md b/docs/dev/api/bot/lifespan.md deleted file mode 100644 index 6466bd52..00000000 --- a/docs/dev/api/bot/lifespan.md +++ /dev/null @@ -1,450 +0,0 @@ ---- -title: liteyuki.bot.lifespan -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC]) -> None` - -运行函数 - -Args: - - funcs: - -Returns: - -
-源代码 - -```python -@staticmethod -def run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC], *args, **kwargs) -> None: - """ - 运行函数 - Args: - funcs: - Returns: - """ - try: - loop = asyncio.get_event_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - tasks = [] - for func in funcs: - if is_coroutine_callable(func): - tasks.append(func(*args, **kwargs)) - else: - tasks.append(async_wrapper(func)(*args, **kwargs)) - loop.run_until_complete(asyncio.gather(*tasks)) -``` -
- -### ***class*** `Lifespan` - - - -###   ***def*** `__init__(self) -> None` - - 轻雪生命周期管理,启动、停止、重启 - -
-源代码 - -```python -def __init__(self) -> None: - """ - 轻雪生命周期管理,启动、停止、重启 - """ - self.life_flag: int = 0 - self._before_start_funcs: list[LIFESPAN_FUNC] = [] - self._after_start_funcs: list[LIFESPAN_FUNC] = [] - self._before_process_shutdown_funcs: list[LIFESPAN_FUNC] = [] - self._after_shutdown_funcs: list[LIFESPAN_FUNC] = [] - self._before_process_restart_funcs: list[LIFESPAN_FUNC] = [] - self._after_restart_funcs: list[LIFESPAN_FUNC] = [] - self._after_nonebot_init_funcs: list[LIFESPAN_FUNC] = [] -``` -
- -###   ***@staticmethod*** -###   ***def*** `run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC]) -> None` - - 运行函数 - -Args: - - funcs: - -Returns: - -
-源代码 - -```python -@staticmethod -def run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC], *args, **kwargs) -> None: - """ - 运行函数 - Args: - funcs: - Returns: - """ - try: - loop = asyncio.get_event_loop() - except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - tasks = [] - for func in funcs: - if is_coroutine_callable(func): - tasks.append(func(*args, **kwargs)) - else: - tasks.append(async_wrapper(func)(*args, **kwargs)) - loop.run_until_complete(asyncio.gather(*tasks)) -``` -
- -###   ***def*** `on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册启动时的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -
-源代码 - -```python -def on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC: - """ - 注册启动时的函数 - Args: - func: - Returns: - LIFESPAN_FUNC: - """ - self._before_start_funcs.append(func) - return func -``` -
- -###   ***def*** `on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册启动时的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -
-源代码 - -```python -def on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC: - """ - 注册启动时的函数 - Args: - func: - Returns: - LIFESPAN_FUNC: - """ - self._after_start_funcs.append(func) - return func -``` -
- -###   ***def*** `on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册停止前的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -
-源代码 - -```python -def on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC: - """ - 注册停止前的函数 - Args: - func: - Returns: - LIFESPAN_FUNC: - """ - self._before_process_shutdown_funcs.append(func) - return func -``` -
- -###   ***def*** `on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册停止后的函数 - -Args: - - func: - - - -Returns: - - LIFESPAN_FUNC: - -
-源代码 - -```python -def on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC: - """ - 注册停止后的函数 - Args: - func: - - Returns: - LIFESPAN_FUNC: - - """ - self._after_shutdown_funcs.append(func) - return func -``` -
- -###   ***def*** `on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册重启时的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -
-源代码 - -```python -def on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC: - """ - 注册重启时的函数 - Args: - func: - Returns: - LIFESPAN_FUNC: - """ - self._before_process_restart_funcs.append(func) - return func -``` -
- -###   ***def*** `on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册重启后的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -
-源代码 - -```python -def on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC: - """ - 注册重启后的函数 - Args: - func: - Returns: - LIFESPAN_FUNC: - """ - self._after_restart_funcs.append(func) - return func -``` -
- -###   ***def*** `on_after_nonebot_init(self, func: Any) -> None` - - 注册 NoneBot 初始化后的函数 - -Args: - - func: - - - -Returns: - -
-源代码 - -```python -def on_after_nonebot_init(self, func): - """ - 注册 NoneBot 初始化后的函数 - Args: - func: - - Returns: - - """ - self._after_nonebot_init_funcs.append(func) - return func -``` -
- -###   ***def*** `before_start(self) -> None` - - 启动前 - -Returns: - -
-源代码 - -```python -def before_start(self) -> None: - """ - 启动前 - Returns: - """ - logger.debug('Running before_start functions') - self.run_funcs(self._before_start_funcs) -``` -
- -###   ***def*** `after_start(self) -> None` - - 启动后 - -Returns: - -
-源代码 - -```python -def after_start(self) -> None: - """ - 启动后 - Returns: - """ - logger.debug('Running after_start functions') - self.run_funcs(self._after_start_funcs) -``` -
- -###   ***def*** `before_process_shutdown(self) -> None` - - 停止前 - -Returns: - -
-源代码 - -```python -def before_process_shutdown(self) -> None: - """ - 停止前 - Returns: - """ - logger.debug('Running before_shutdown functions') - self.run_funcs(self._before_process_shutdown_funcs) -``` -
- -###   ***def*** `after_shutdown(self) -> None` - - 停止后 - -Returns: - -
-源代码 - -```python -def after_shutdown(self) -> None: - """ - 停止后 - Returns: - """ - logger.debug('Running after_shutdown functions') - self.run_funcs(self._after_shutdown_funcs) -``` -
- -###   ***def*** `before_process_restart(self) -> None` - - 重启前 - -Returns: - -
-源代码 - -```python -def before_process_restart(self) -> None: - """ - 重启前 - Returns: - """ - logger.debug('Running before_restart functions') - self.run_funcs(self._before_process_restart_funcs) -``` -
- -###   ***def*** `after_restart(self) -> None` - - 重启后 - -Returns: - -
-源代码 - -```python -def after_restart(self) -> None: - """ - 重启后 - Returns: - - """ - logger.debug('Running after_restart functions') - self.run_funcs(self._after_restart_funcs) -``` -
- -### ***var*** `tasks = []` - - - -### ***var*** `loop = asyncio.get_event_loop()` - - - -### ***var*** `loop = asyncio.new_event_loop()` - - - diff --git a/docs/dev/api/comm/README.md b/docs/dev/api/comm/README.md deleted file mode 100644 index 09bccc34..00000000 --- a/docs/dev/api/comm/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.comm -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/comm/channel.md b/docs/dev/api/comm/channel.md deleted file mode 100644 index 8e5e170f..00000000 --- a/docs/dev/api/comm/channel.md +++ /dev/null @@ -1,427 +0,0 @@ ---- -title: liteyuki.comm.channel -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `set_channel(name: str, channel: Channel) -> None` - -设置通道实例 - -Args: - - name: 通道名称 - - channel: 通道实例 - -
-源代码 - -```python -def set_channel(name: str, channel: Channel): - """ - 设置通道实例 - Args: - name: 通道名称 - channel: 通道实例 - """ - if not isinstance(channel, Channel): - raise TypeError(f'channel_ must be an instance of Channel, {type(channel)} found') - if IS_MAIN_PROCESS: - _channel[name] = channel - else: - channel_deliver_passive_channel.send(('set_channel', {'name': name, 'channel_': channel})) -``` -
- -### ***def*** `set_channels(channels: dict[str, Channel]) -> None` - -设置通道实例 - -Args: - - channels: 通道名称 - -
-源代码 - -```python -def set_channels(channels: dict[str, Channel]): - """ - 设置通道实例 - Args: - channels: 通道名称 - """ - for name, channel in channels.items(): - set_channel(name, channel) -``` -
- -### ***def*** `get_channel(name: str) -> Channel` - -获取通道实例 - -Args: - - name: 通道名称 - -Returns: - -
-源代码 - -```python -def get_channel(name: str) -> Channel: - """ - 获取通道实例 - Args: - name: 通道名称 - Returns: - """ - if IS_MAIN_PROCESS: - return _channel[name] - else: - recv_chan = Channel[Channel[Any]]('recv_chan') - channel_deliver_passive_channel.send(('get_channel', {'name': name, 'recv_chan': recv_chan})) - return recv_chan.receive() -``` -
- -### ***def*** `get_channels() -> dict[str, Channel]` - -获取通道实例 - -Returns: - -
-源代码 - -```python -def get_channels() -> dict[str, Channel]: - """ - 获取通道实例 - Returns: - """ - if IS_MAIN_PROCESS: - return _channel - else: - recv_chan = Channel[dict[str, Channel[Any]]]('recv_chan') - channel_deliver_passive_channel.send(('get_channels', {'recv_chan': recv_chan})) - return recv_chan.receive() -``` -
- -### ***def*** `on_set_channel(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'set_channel') -def on_set_channel(data: tuple[str, dict[str, Any]]): - name, channel = (data[1]['name'], data[1]['channel_']) - set_channel(name, channel) -``` -
- -### ***def*** `on_get_channel(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'get_channel') -def on_get_channel(data: tuple[str, dict[str, Any]]): - name, recv_chan = (data[1]['name'], data[1]['recv_chan']) - recv_chan.send(get_channel(name)) -``` -
- -### ***def*** `on_get_channels(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@channel_deliver_passive_channel.on_receive(filter_func=lambda data: data[0] == 'get_channels') -def on_get_channels(data: tuple[str, dict[str, Any]]): - recv_chan = data[1]['recv_chan'] - recv_chan.send(get_channels()) -``` -
- -### ***def*** `decorator(func: Callable[[T], Any]) -> Callable[[T], Any]` - - - -
-源代码 - -```python -def decorator(func: Callable[[T], Any]) -> Callable[[T], Any]: - global _func_id - - async def wrapper(data: T) -> Any: - if filter_func is not None: - if is_coroutine_callable(filter_func): - if not await filter_func(data): - return - elif not filter_func(data): - return - if is_coroutine_callable(func): - return await func(data) - else: - return func(data) - _callback_funcs[_func_id] = wrapper - if IS_MAIN_PROCESS: - self._on_main_receive_funcs.append(_func_id) - else: - self._on_sub_receive_funcs.append(_func_id) - _func_id += 1 - return func -``` -
- -### ***async def*** `wrapper(data: T) -> Any` - - - -
-源代码 - -```python -async def wrapper(data: T) -> Any: - if filter_func is not None: - if is_coroutine_callable(filter_func): - if not await filter_func(data): - return - elif not filter_func(data): - return - if is_coroutine_callable(func): - return await func(data) - else: - return func(data) -``` -
- -### ***class*** `Channel(Generic[T])` - -通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者 - -有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器 - -###   ***def*** `__init__(self, _id: str, type_check: Optional[bool]) -> None` - - 初始化通道 - -Args: - - _id: 通道ID - - type_check: 是否开启类型检查, 若为空,则传入泛型默认开启,否则默认关闭 - -
-源代码 - -```python -def __init__(self, _id: str, type_check: Optional[bool]=None): - """ - 初始化通道 - Args: - _id: 通道ID - type_check: 是否开启类型检查, 若为空,则传入泛型默认开启,否则默认关闭 - """ - self.conn_send, self.conn_recv = Pipe() - self._closed = False - self._on_main_receive_funcs: list[int] = [] - self._on_sub_receive_funcs: list[int] = [] - self.name: str = _id - self.is_main_receive_loop_running = False - self.is_sub_receive_loop_running = False - if type_check is None: - type_check = self._get_generic_type() is not None - elif type_check: - if self._get_generic_type() is None: - raise TypeError('Type hint is required for enforcing type check.') - self.type_check = type_check -``` -
- -###   ***def*** `send(self, data: T) -> None` - - 发送数据 - -Args: - - data: 数据 - -
-源代码 - -```python -def send(self, data: T): - """ - 发送数据 - Args: - data: 数据 - """ - if self.type_check: - _type = self._get_generic_type() - if _type is not None and (not self._validate_structure(data, _type)): - raise TypeError(f'Data must be an instance of {_type}, {type(data)} found') - if self._closed: - raise RuntimeError('Cannot send to a closed channel_') - self.conn_send.send(data) -``` -
- -###   ***def*** `receive(self) -> T` - - 接收数据 - -Args: - -
-源代码 - -```python -def receive(self) -> T: - """ - 接收数据 - Args: - """ - if self._closed: - raise RuntimeError('Cannot receive from a closed channel_') - while True: - data = self.conn_recv.recv() - return data -``` -
- -###   ***def*** `close(self) -> None` - - 关闭通道 - -
-源代码 - -```python -def close(self): - """ - 关闭通道 - """ - self._closed = True - self.conn_send.close() - self.conn_recv.close() -``` -
- -###   ***def*** `on_receive(self, filter_func: Optional[FILTER_FUNC]) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]` - - 接收数据并执行函数 - -Args: - - filter_func: 过滤函数,为None则不过滤 - -Returns: - - 装饰器,装饰一个函数在接收到数据后执行 - -
-源代码 - -```python -def on_receive(self, filter_func: Optional[FILTER_FUNC]=None) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]: - """ - 接收数据并执行函数 - Args: - filter_func: 过滤函数,为None则不过滤 - Returns: - 装饰器,装饰一个函数在接收到数据后执行 - """ - if not self.is_sub_receive_loop_running and (not IS_MAIN_PROCESS): - threading.Thread(target=self._start_sub_receive_loop, daemon=True).start() - if not self.is_main_receive_loop_running and IS_MAIN_PROCESS: - threading.Thread(target=self._start_main_receive_loop, daemon=True).start() - - def decorator(func: Callable[[T], Any]) -> Callable[[T], Any]: - global _func_id - - async def wrapper(data: T) -> Any: - if filter_func is not None: - if is_coroutine_callable(filter_func): - if not await filter_func(data): - return - elif not filter_func(data): - return - if is_coroutine_callable(func): - return await func(data) - else: - return func(data) - _callback_funcs[_func_id] = wrapper - if IS_MAIN_PROCESS: - self._on_main_receive_funcs.append(_func_id) - else: - self._on_sub_receive_funcs.append(_func_id) - _func_id += 1 - return func - return decorator -``` -
- -### ***var*** `T = TypeVar('T')` - - - -### ***var*** `channel_deliver_active_channel = Channel(_id='channel_deliver_active_channel')` - - - -### ***var*** `channel_deliver_passive_channel = Channel(_id='channel_deliver_passive_channel')` - - - -### ***var*** `recv_chan = data[1]['recv_chan']` - - - -### ***var*** `recv_chan = Channel[Channel[Any]]('recv_chan')` - - - -### ***var*** `recv_chan = Channel[dict[str, Channel[Any]]]('recv_chan')` - - - -### ***var*** `type_check = self._get_generic_type() is not None` - - - -### ***var*** `data = self.conn_recv.recv()` - - - -### ***var*** `func = _callback_funcs[func_id]` - - - -### ***var*** `func = _callback_funcs[func_id]` - - - -### ***var*** `data = self.conn_recv.recv()` - - - -### ***var*** `data = self.conn_recv.recv()` - - - diff --git a/docs/dev/api/comm/event.md b/docs/dev/api/comm/event.md deleted file mode 100644 index a2a15f55..00000000 --- a/docs/dev/api/comm/event.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: liteyuki.comm.event -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `Event` - -事件类 - -###   ***def*** `__init__(self, name: str, data: dict[str, Any]) -> None` - -  - -
-源代码 - -```python -def __init__(self, name: str, data: dict[str, Any]): - self.name = name - self.data = data -``` -
- diff --git a/docs/dev/api/comm/storage.md b/docs/dev/api/comm/storage.md deleted file mode 100644 index cd19dbb7..00000000 --- a/docs/dev/api/comm/storage.md +++ /dev/null @@ -1,563 +0,0 @@ ---- -title: liteyuki.comm.storage -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `run_subscriber_receive_funcs(channel_: str, data: Any) -> None` - -运行订阅者接收函数 - -Args: - - channel_: 频道 - - data: 数据 - -
-源代码 - -```python -@staticmethod -def run_subscriber_receive_funcs(channel_: str, data: Any): - """ - 运行订阅者接收函数 - Args: - channel_: 频道 - data: 数据 - """ - if IS_MAIN_PROCESS: - if channel_ in _on_main_subscriber_receive_funcs and _on_main_subscriber_receive_funcs[channel_]: - run_coroutine(*[func(data) for func in _on_main_subscriber_receive_funcs[channel_]]) - elif channel_ in _on_sub_subscriber_receive_funcs and _on_sub_subscriber_receive_funcs[channel_]: - run_coroutine(*[func(data) for func in _on_sub_subscriber_receive_funcs[channel_]]) -``` -
- -### ***def*** `on_get(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'get') -def on_get(data: tuple[str, dict[str, Any]]): - key = data[1]['key'] - default = data[1]['default'] - recv_chan = data[1]['recv_chan'] - recv_chan.send(shared_memory.get(key, default)) -``` -
- -### ***def*** `on_set(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'set') -def on_set(data: tuple[str, dict[str, Any]]): - key = data[1]['key'] - value = data[1]['value'] - shared_memory.set(key, value) -``` -
- -### ***def*** `on_delete(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'delete') -def on_delete(data: tuple[str, dict[str, Any]]): - key = data[1]['key'] - shared_memory.delete(key) -``` -
- -### ***def*** `on_get_all(data: tuple[str, dict[str, Any]]) -> None` - - - -
-源代码 - -```python -@shared_memory.passive_chan.on_receive(lambda d: d[0] == 'get_all') -def on_get_all(data: tuple[str, dict[str, Any]]): - recv_chan = data[1]['recv_chan'] - recv_chan.send(shared_memory.get_all()) -``` -
- -### ***def*** `on_publish(data: tuple[str, Any]) -> None` - - - -
-源代码 - -```python -@channel.publish_channel.on_receive() -def on_publish(data: tuple[str, Any]): - channel_, data = data - shared_memory.run_subscriber_receive_funcs(channel_, data) -``` -
- -### ***def*** `decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC` - - - -
-源代码 - -```python -def decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC: - - async def wrapper(data: Any): - if is_coroutine_callable(func): - await func(data) - else: - func(data) - if IS_MAIN_PROCESS: - if channel_ not in _on_main_subscriber_receive_funcs: - _on_main_subscriber_receive_funcs[channel_] = [] - _on_main_subscriber_receive_funcs[channel_].append(wrapper) - else: - if channel_ not in _on_sub_subscriber_receive_funcs: - _on_sub_subscriber_receive_funcs[channel_] = [] - _on_sub_subscriber_receive_funcs[channel_].append(wrapper) - return wrapper -``` -
- -### ***async def*** `wrapper(data: Any) -> None` - - - -
-源代码 - -```python -async def wrapper(data: Any): - if is_coroutine_callable(func): - await func(data) - else: - func(data) -``` -
- -### ***class*** `Subscriber` - - - -###   ***def*** `__init__(self) -> None` - -  - -
-源代码 - -```python -def __init__(self): - self._subscribers = {} -``` -
- -###   ***def*** `receive(self) -> Any` - -  - -
-源代码 - -```python -def receive(self) -> Any: - pass -``` -
- -###   ***def*** `unsubscribe(self) -> None` - -  - -
-源代码 - -```python -def unsubscribe(self) -> None: - pass -``` -
- -### ***class*** `KeyValueStore` - - - -###   ***def*** `__init__(self) -> None` - -  - -
-源代码 - -```python -def __init__(self): - self._store = {} - self.active_chan = Channel[tuple[str, Optional[dict[str, Any]]]](_id='shared_memory-active') - self.passive_chan = Channel[tuple[str, Optional[dict[str, Any]]]](_id='shared_memory-passive') - self.publish_channel = Channel[tuple[str, Any]](_id='shared_memory-publish') - self.is_main_receive_loop_running = False - self.is_sub_receive_loop_running = False -``` -
- -###   ***def*** `set(self, key: str, value: Any) -> None` - - 设置键值对 - -Args: - - key: 键 - - value: 值 - -
-源代码 - -```python -def set(self, key: str, value: Any) -> None: - """ - 设置键值对 - Args: - key: 键 - value: 值 - - """ - if IS_MAIN_PROCESS: - lock = _get_lock(key) - with lock: - self._store[key] = value - else: - self.passive_chan.send(('set', {'key': key, 'value': value})) -``` -
- -###   ***def*** `get(self, key: str, default: Optional[Any]) -> Optional[Any]` - - 获取键值对 - -Args: - - key: 键 - - default: 默认值 - - - -Returns: - - Any: 值 - -
-源代码 - -```python -def get(self, key: str, default: Optional[Any]=None) -> Optional[Any]: - """ - 获取键值对 - Args: - key: 键 - default: 默认值 - - Returns: - Any: 值 - """ - if IS_MAIN_PROCESS: - lock = _get_lock(key) - with lock: - return self._store.get(key, default) - else: - recv_chan = Channel[Optional[Any]]('recv_chan') - self.passive_chan.send(('get', {'key': key, 'default': default, 'recv_chan': recv_chan})) - return recv_chan.receive() -``` -
- -###   ***def*** `delete(self, key: str, ignore_key_error: bool) -> None` - - 删除键值对 - -Args: - - key: 键 - - ignore_key_error: 是否忽略键不存在的错误 - - - -Returns: - -
-源代码 - -```python -def delete(self, key: str, ignore_key_error: bool=True) -> None: - """ - 删除键值对 - Args: - key: 键 - ignore_key_error: 是否忽略键不存在的错误 - - Returns: - """ - if IS_MAIN_PROCESS: - lock = _get_lock(key) - with lock: - if key in self._store: - try: - del self._store[key] - del _locks[key] - except KeyError as e: - if not ignore_key_error: - raise e - else: - self.passive_chan.send(('delete', {'key': key})) -``` -
- -###   ***def*** `get_all(self) -> dict[str, Any]` - - 获取所有键值对 - -Returns: - - dict[str, Any]: 键值对 - -
-源代码 - -```python -def get_all(self) -> dict[str, Any]: - """ - 获取所有键值对 - Returns: - dict[str, Any]: 键值对 - """ - if IS_MAIN_PROCESS: - return self._store - else: - recv_chan = Channel[dict[str, Any]]('recv_chan') - self.passive_chan.send(('get_all', {'recv_chan': recv_chan})) - return recv_chan.receive() -``` -
- -###   ***def*** `publish(self, channel_: str, data: Any) -> None` - - 发布消息 - -Args: - - channel_: 频道 - - data: 数据 - - - -Returns: - -
-源代码 - -```python -def publish(self, channel_: str, data: Any) -> None: - """ - 发布消息 - Args: - channel_: 频道 - data: 数据 - - Returns: - """ - self.active_chan.send(('publish', {'channel': channel_, 'data': data})) -``` -
- -###   ***def*** `on_subscriber_receive(self, channel_: str) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]` - - 订阅者接收消息时的回调 - -Args: - - channel_: 频道 - - - -Returns: - - 装饰器 - -
-源代码 - -```python -def on_subscriber_receive(self, channel_: str) -> Callable[[ON_RECEIVE_FUNC], ON_RECEIVE_FUNC]: - """ - 订阅者接收消息时的回调 - Args: - channel_: 频道 - - Returns: - 装饰器 - """ - if IS_MAIN_PROCESS and (not self.is_main_receive_loop_running): - threading.Thread(target=self._start_receive_loop, daemon=True).start() - shared_memory.is_main_receive_loop_running = True - elif not IS_MAIN_PROCESS and (not self.is_sub_receive_loop_running): - threading.Thread(target=self._start_receive_loop, daemon=True).start() - shared_memory.is_sub_receive_loop_running = True - - def decorator(func: ON_RECEIVE_FUNC) -> ON_RECEIVE_FUNC: - - async def wrapper(data: Any): - if is_coroutine_callable(func): - await func(data) - else: - func(data) - if IS_MAIN_PROCESS: - if channel_ not in _on_main_subscriber_receive_funcs: - _on_main_subscriber_receive_funcs[channel_] = [] - _on_main_subscriber_receive_funcs[channel_].append(wrapper) - else: - if channel_ not in _on_sub_subscriber_receive_funcs: - _on_sub_subscriber_receive_funcs[channel_] = [] - _on_sub_subscriber_receive_funcs[channel_].append(wrapper) - return wrapper - return decorator -``` -
- -###   ***@staticmethod*** -###   ***def*** `run_subscriber_receive_funcs(channel_: str, data: Any) -> None` - - 运行订阅者接收函数 - -Args: - - channel_: 频道 - - data: 数据 - -
-源代码 - -```python -@staticmethod -def run_subscriber_receive_funcs(channel_: str, data: Any): - """ - 运行订阅者接收函数 - Args: - channel_: 频道 - data: 数据 - """ - if IS_MAIN_PROCESS: - if channel_ in _on_main_subscriber_receive_funcs and _on_main_subscriber_receive_funcs[channel_]: - run_coroutine(*[func(data) for func in _on_main_subscriber_receive_funcs[channel_]]) - elif channel_ in _on_sub_subscriber_receive_funcs and _on_sub_subscriber_receive_funcs[channel_]: - run_coroutine(*[func(data) for func in _on_sub_subscriber_receive_funcs[channel_]]) -``` -
- -### ***class*** `GlobalKeyValueStore` - - - -###   ***@classmethod*** -###   ***def*** `get_instance(cls: Any) -> None` - -  - -
-源代码 - -```python -@classmethod -def get_instance(cls): - if cls._instance is None: - with cls._lock: - if cls._instance is None: - cls._instance = KeyValueStore() - return cls._instance -``` -
- -###   ***attr*** `_instance: None` - -###   ***attr*** `_lock: threading.Lock()` - -### ***var*** `key = data[1]['key']` - - - -### ***var*** `default = data[1]['default']` - - - -### ***var*** `recv_chan = data[1]['recv_chan']` - - - -### ***var*** `key = data[1]['key']` - - - -### ***var*** `value = data[1]['value']` - - - -### ***var*** `key = data[1]['key']` - - - -### ***var*** `recv_chan = data[1]['recv_chan']` - - - -### ***var*** `lock = _get_lock(key)` - - - -### ***var*** `lock = _get_lock(key)` - - - -### ***var*** `recv_chan = Channel[Optional[Any]]('recv_chan')` - - - -### ***var*** `lock = _get_lock(key)` - - - -### ***var*** `recv_chan = Channel[dict[str, Any]]('recv_chan')` - - - -### ***var*** `data = self.active_chan.receive()` - - - -### ***var*** `data = self.publish_channel.receive()` - - - diff --git a/docs/dev/api/config.md b/docs/dev/api/config.md deleted file mode 100644 index 24afd6c9..00000000 --- a/docs/dev/api/config.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -title: liteyuki.config -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `flat_config(config: dict[str, Any]) -> dict[str, Any]` - -扁平化配置文件 - - - -{a:{b:{c:1}}} -> {"a.b.c": 1} - -Args: - - config: 配置项目 - - - -Returns: - - 扁平化后的配置文件,但也包含原有的键值对 - -
-源代码 - -```python -def flat_config(config: dict[str, Any]) -> dict[str, Any]: - """ - 扁平化配置文件 - - {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 - -
-源代码 - -```python -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 - -
-源代码 - -```python -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 - -
-源代码 - -```python -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() -> dict[str, Any]` - -从指定文件加载配置项,会自动识别文件格式 - -默认执行扁平化选项 - -
-源代码 - -```python -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)) - elif not no_warning: - logger.warning(f'Unsupported config file format: {file}') - elif not no_warning: - logger.warning(f'Config file not found: {file}') - return config -``` -
- -### ***def*** `load_configs_from_dirs() -> dict[str, Any]` - -从目录下加载配置文件,不递归 - -按照读取文件的优先级反向覆盖 - -默认执行扁平化选项 - -
-源代码 - -```python -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) -> dict[str, Any]` - -从一个标准的轻雪项目加载配置文件 - -项目目录下的config.*和config目录下的所有配置文件 - -项目目录下的配置文件优先 - -
-源代码 - -```python -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 -``` -
- -### ***class*** `SatoriNodeConfig(BaseModel)` - - - -### ***class*** `SatoriConfig(BaseModel)` - - - -### ***class*** `BasicConfig(BaseModel)` - - - -### ***var*** `new_config = copy.deepcopy(config)` - - - -### ***var*** `config = yaml.safe_load(open(file, 'r', encoding='utf-8'))` - - - -### ***var*** `config = json.load(open(file, 'r', encoding='utf-8'))` - - - -### ***var*** `config = toml.load(open(file, 'r', encoding='utf-8'))` - - - -### ***var*** `config = {}` - - - -### ***var*** `config = {}` - - - -### ***var*** `config = load_configs_from_dirs('config', no_waring=no_waring)` - - - diff --git a/docs/dev/api/core/README.md b/docs/dev/api/core/README.md deleted file mode 100644 index 26c3500b..00000000 --- a/docs/dev/api/core/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.core -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/core/manager.md b/docs/dev/api/core/manager.md deleted file mode 100644 index 52397375..00000000 --- a/docs/dev/api/core/manager.md +++ /dev/null @@ -1,275 +0,0 @@ ---- -title: liteyuki.core.manager -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `ChannelDeliver` - - - -###   ***def*** `__init__(self, active: Channel[Any], passive: Channel[Any], channel_deliver_active: Channel[Channel[Any]], channel_deliver_passive: Channel[tuple[str, dict]], publish: Channel[tuple[str, Any]]) -> None` - -  - -
-源代码 - -```python -def __init__(self, active: Channel[Any], passive: Channel[Any], channel_deliver_active: Channel[Channel[Any]], channel_deliver_passive: Channel[tuple[str, dict]], publish: Channel[tuple[str, Any]]): - self.active = active - self.passive = passive - self.channel_deliver_active = channel_deliver_active - self.channel_deliver_passive = channel_deliver_passive - self.publish = publish -``` -
- -### ***class*** `ProcessManager` - -进程管理器 - -###   ***def*** `__init__(self, lifespan: 'Lifespan') -> None` - -  - -
-源代码 - -```python -def __init__(self, lifespan: 'Lifespan'): - self.lifespan = lifespan - self.targets: dict[str, tuple[Callable, tuple, dict]] = {} - self.processes: dict[str, Process] = {} -``` -
- -###   ***def*** `start(self, name: str) -> None` - - 开启后自动监控进程,并添加到进程字典中 - -Args: - - name: - -Returns: - -
-源代码 - -```python -def start(self, name: str): - """ - 开启后自动监控进程,并添加到进程字典中 - Args: - name: - Returns: - - """ - if name not in self.targets: - raise KeyError(f'Process {name} not found.') - chan_active = get_channel(f'{name}-active') - - def _start_process(): - process = Process(target=self.targets[name][0], args=self.targets[name][1], kwargs=self.targets[name][2], daemon=True) - self.processes[name] = process - process.start() - _start_process() - while True: - data = chan_active.receive() - if data == 0: - logger.info(f'Stopping process {name}') - self.lifespan.before_process_shutdown() - self.terminate(name) - break - elif data == 1: - logger.info(f'Restarting process {name}') - self.lifespan.before_process_shutdown() - self.lifespan.before_process_restart() - self.terminate(name) - _start_process() - continue - else: - logger.warning('Unknown data received, ignored.') -``` -
- -###   ***def*** `start_all(self) -> None` - - 启动所有进程 - -
-源代码 - -```python -def start_all(self): - """ - 启动所有进程 - """ - for name in self.targets: - threading.Thread(target=self.start, args=(name,), daemon=True).start() -``` -
- -###   ***def*** `add_target(self, name: str, target: TARGET_FUNC, args: tuple, kwargs: Any) -> None` - - 添加进程 - -Args: - - name: 进程名,用于获取和唯一标识 - - target: 进程函数 - - args: 进程函数参数 - - kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive - -
-源代码 - -```python -def add_target(self, name: str, target: TARGET_FUNC, args: tuple=(), kwargs=None): - """ - 添加进程 - Args: - name: 进程名,用于获取和唯一标识 - target: 进程函数 - args: 进程函数参数 - kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive - """ - if kwargs is None: - kwargs = {} - chan_active: Channel = Channel(_id=f'{name}-active') - chan_passive: Channel = Channel(_id=f'{name}-passive') - channel_deliver = ChannelDeliver(active=chan_active, passive=chan_passive, channel_deliver_active=channel_deliver_active_channel, channel_deliver_passive=channel_deliver_passive_channel, publish=publish_channel) - self.targets[name] = (_delivery_channel_wrapper, (target, channel_deliver, shared_memory, *args), kwargs) - set_channels({f'{name}-active': chan_active, f'{name}-passive': chan_passive}) -``` -
- -###   ***def*** `join_all(self) -> None` - -  - -
-源代码 - -```python -def join_all(self): - for name, process in self.targets: - process.join() -``` -
- -###   ***def*** `terminate(self, name: str) -> None` - - 终止进程并从进程字典中删除 - -Args: - - name: - - - -Returns: - -
-源代码 - -```python -def terminate(self, name: str): - """ - 终止进程并从进程字典中删除 - Args: - name: - - Returns: - - """ - if name not in self.processes: - logger.warning(f'Process {name} not found.') - return - process = self.processes[name] - process.terminate() - process.join(TIMEOUT) - if process.is_alive(): - process.kill() - logger.success(f'Process {name} terminated.') -``` -
- -###   ***def*** `terminate_all(self) -> None` - -  - -
-源代码 - -```python -def terminate_all(self): - for name in self.targets: - self.terminate(name) -``` -
- -###   ***def*** `is_process_alive(self, name: str) -> bool` - - 检查进程是否存活 - -Args: - - name: - - - -Returns: - -
-源代码 - -```python -def is_process_alive(self, name: str) -> bool: - """ - 检查进程是否存活 - Args: - name: - - Returns: - - """ - if name not in self.targets: - logger.warning(f'Process {name} not found.') - return self.processes[name].is_alive() -``` -
- -### ***var*** `TIMEOUT = 10` - - - -### ***var*** `chan_active = get_channel(f'{name}-active')` - - - -### ***var*** `channel_deliver = ChannelDeliver(active=chan_active, passive=chan_passive, channel_deliver_active=channel_deliver_active_channel, channel_deliver_passive=channel_deliver_passive_channel, publish=publish_channel)` - - - -### ***var*** `process = self.processes[name]` - - - -### ***var*** `process = Process(target=self.targets[name][0], args=self.targets[name][1], kwargs=self.targets[name][2], daemon=True)` - - - -### ***var*** `data = chan_active.receive()` - - - -### ***var*** `kwargs = {}` - - - diff --git a/docs/dev/api/dev/README.md b/docs/dev/api/dev/README.md deleted file mode 100644 index 6d883442..00000000 --- a/docs/dev/api/dev/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.dev -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/dev/observer.md b/docs/dev/api/dev/observer.md deleted file mode 100644 index 2005bbf2..00000000 --- a/docs/dev/api/dev/observer.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -title: liteyuki.dev.observer -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `debounce(wait: Any) -> None` - -防抖函数 - -
-源代码 - -```python -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 -``` -
- -### ***def*** `on_file_system_event(directories: tuple[str], recursive: bool, event_filter: FILTER_FUNC) -> Callable[[CALLBACK_FUNC], CALLBACK_FUNC]` - -注册文件系统变化监听器 - -Args: - - directories: 监听目录们 - - recursive: 是否递归监听子目录 - - event_filter: 事件过滤器, 返回True则执行回调函数 - -Returns: - - 装饰器,装饰一个函数在接收到数据后执行 - -
-源代码 - -```python -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 -``` -
- -### ***def*** `decorator(func: Any) -> None` - - - -
-源代码 - -```python -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 -``` -
- -### ***def*** `decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC` - - - -
-源代码 - -```python -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 -``` -
- -### ***def*** `wrapper() -> None` - - - -
-源代码 - -```python -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) -``` -
- -### ***def*** `wrapper(event: FileSystemEvent) -> None` - - - -
-源代码 - -```python -def wrapper(event: FileSystemEvent): - if event_filter is not None and (not event_filter(event)): - return - func(event) -``` -
- -### ***class*** `CodeModifiedHandler(FileSystemEventHandler)` - -Handler for code file changes - -###   ***def*** `on_modified(self, event: Any) -> None` - -  - -
-源代码 - -```python -@debounce(1) -def on_modified(self, event): - raise NotImplementedError('on_modified must be implemented') -``` -
- -###   ***def*** `on_created(self, event: Any) -> None` - -  - -
-源代码 - -```python -def on_created(self, event): - self.on_modified(event) -``` -
- -###   ***def*** `on_deleted(self, event: Any) -> None` - -  - -
-源代码 - -```python -def on_deleted(self, event): - self.on_modified(event) -``` -
- -###   ***def*** `on_moved(self, event: Any) -> None` - -  - -
-源代码 - -```python -def on_moved(self, event): - self.on_modified(event) -``` -
- -###   ***def*** `on_any_event(self, event: Any) -> None` - -  - -
-源代码 - -```python -def on_any_event(self, event): - self.on_modified(event) -``` -
- -### ***var*** `liteyuki_bot = get_bot()` - - - -### ***var*** `observer = Observer()` - - - -### ***var*** `last_call_time = None` - - - -### ***var*** `code_modified_handler = CodeModifiedHandler()` - - - -### ***var*** `current_time = time.time()` - - - -### ***var*** `last_call_time = current_time` - - - diff --git a/docs/dev/api/dev/plugin.md b/docs/dev/api/dev/plugin.md deleted file mode 100644 index caafa5ea..00000000 --- a/docs/dev/api/dev/plugin.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: liteyuki.dev.plugin -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `run_plugins() -> None` - -运行插件,无需手动初始化bot - -Args: - - module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名 - -
-源代码 - -```python -def run_plugins(*module_path: str | Path): - """ - 运行插件,无需手动初始化bot - Args: - module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名 - """ - cfg = load_config_in_default() - plugins = cfg.get('liteyuki.plugins', []) - plugins.extend(module_path) - cfg['liteyuki.plugins'] = plugins - bot = LiteyukiBot(**cfg) - bot.run() -``` -
- -### ***var*** `cfg = load_config_in_default()` - - - -### ***var*** `plugins = cfg.get('liteyuki.plugins', [])` - - - -### ***var*** `bot = LiteyukiBot(**cfg)` - - - diff --git a/docs/dev/api/exception.md b/docs/dev/api/exception.md deleted file mode 100644 index 469c00d6..00000000 --- a/docs/dev/api/exception.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: liteyuki.exception -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `LiteyukiException(BaseException)` - -Liteyuki的异常基类。 - diff --git a/docs/dev/api/log.md b/docs/dev/api/log.md deleted file mode 100644 index af382275..00000000 --- a/docs/dev/api/log.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: liteyuki.log -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `get_format(level: str) -> str` - - - -
-源代码 - -```python -def get_format(level: str) -> str: - if level == 'DEBUG': - return debug_format - else: - return default_format -``` -
- -### ***def*** `init_log(config: dict) -> None` - -在语言加载完成后执行 - -Returns: - -
-源代码 - -```python -def init_log(config: dict): - """ - 在语言加载完成后执行 - Returns: - - """ - logger.remove() - logger.add(sys.stdout, level=0, diagnose=False, format=get_format(config.get('log_level', 'INFO'))) - show_icon = config.get('log_icon', True) - logger.level('DEBUG', color='', icon=f"{('🐛' if show_icon else '')}DEBUG") - logger.level('INFO', color='', icon=f"{('ℹ️' if show_icon else '')}INFO") - logger.level('SUCCESS', color='', icon=f"{('✅' if show_icon else '')}SUCCESS") - logger.level('WARNING', color='', icon=f"{('⚠️' if show_icon else '')}WARNING") - logger.level('ERROR', color='', icon=f"{('⭕' if show_icon else '')}ERROR") -``` -
- -### ***var*** `logger = loguru.logger` - - - -### ***var*** `show_icon = config.get('log_icon', True)` - - - diff --git a/docs/dev/api/message/README.md b/docs/dev/api/message/README.md deleted file mode 100644 index 0d664851..00000000 --- a/docs/dev/api/message/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.message -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/message/event.md b/docs/dev/api/message/event.md deleted file mode 100644 index 740ff8bd..00000000 --- a/docs/dev/api/message/event.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: liteyuki.message.event -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `MessageEvent` - - - -###   ***def*** `__init__(self, bot_id: str, message: list[dict[str, Any]] | str, message_type: str, raw_message: str, session_id: str, session_type: str, receive_channel: str, data: Optional[dict[str, Any]]) -> None` - - 轻雪抽象消息事件 - -Args: - - - - bot_id: 机器人ID - - message: 消息,消息段数组[{type: str, data: dict[str, Any]}] - - raw_message: 原始消息(通常为纯文本的格式) - - message_type: 消息类型(private, group, other) - - - - session_id: 会话ID(私聊通常为用户ID,群聊通常为群ID) - - session_type: 会话类型(private, group) - - receive_channel: 接收频道(用于回复消息) - - - - data: 附加数据 - -
-源代码 - -```python -def __init__(self, bot_id: str, message: list[dict[str, Any]] | str, message_type: str, raw_message: str, session_id: str, session_type: str, receive_channel: str, data: Optional[dict[str, Any]]=None): - """ - 轻雪抽象消息事件 - Args: - - bot_id: 机器人ID - message: 消息,消息段数组[{type: str, data: dict[str, Any]}] - raw_message: 原始消息(通常为纯文本的格式) - message_type: 消息类型(private, group, other) - - session_id: 会话ID(私聊通常为用户ID,群聊通常为群ID) - session_type: 会话类型(private, group) - receive_channel: 接收频道(用于回复消息) - - data: 附加数据 - """ - if data is None: - data = {} - self.message_type = message_type - self.data = data - self.bot_id = bot_id - self.message = message - self.raw_message = raw_message - self.session_id = session_id - self.session_type = session_type - self.receive_channel = receive_channel -``` -
- -###   ***def*** `reply(self, message: str | dict[str, Any]) -> None` - - 回复消息 - -Args: - - message: - -Returns: - -
-源代码 - -```python -def reply(self, message: str | dict[str, Any]): - """ - 回复消息 - Args: - message: - Returns: - """ - reply_event = MessageEvent(message_type=self.session_type, message=message, raw_message='', data={'message': message}, bot_id=self.bot_id, session_id=self.session_id, session_type=self.session_type, receive_channel='_') - shared_memory.publish(self.receive_channel, reply_event) -``` -
- -### ***var*** `reply_event = MessageEvent(message_type=self.session_type, message=message, raw_message='', data={'message': message}, bot_id=self.bot_id, session_id=self.session_id, session_type=self.session_type, receive_channel='_')` - - - -### ***var*** `data = {}` - - - diff --git a/docs/dev/api/message/matcher.md b/docs/dev/api/message/matcher.md deleted file mode 100644 index 4a6341b6..00000000 --- a/docs/dev/api/message/matcher.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: liteyuki.message.matcher -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `Matcher` - - - -###   ***def*** `__init__(self, rule: Rule, priority: int, block: bool) -> None` - - 匹配器 - -Args: - - rule: 规则 - - priority: 优先级 >= 0 - - block: 是否阻断后续优先级更低的匹配器 - -
-源代码 - -```python -def __init__(self, rule: Rule, priority: int, block: bool): - """ - 匹配器 - Args: - rule: 规则 - priority: 优先级 >= 0 - block: 是否阻断后续优先级更低的匹配器 - """ - self.rule = rule - self.priority = priority - self.block = block - self.handlers: list[EventHandler] = [] -``` -
- -###   ***def*** `handle(self, handler: EventHandler) -> EventHandler` - - 添加处理函数,装饰器 - -Args: - - handler: - -Returns: - - EventHandler - -
-源代码 - -```python -def handle(self, handler: EventHandler) -> EventHandler: - """ - 添加处理函数,装饰器 - Args: - handler: - Returns: - EventHandler - """ - self.handlers.append(handler) - return handler -``` -
- diff --git a/docs/dev/api/message/on.md b/docs/dev/api/message/on.md deleted file mode 100644 index 4f3c7230..00000000 --- a/docs/dev/api/message/on.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: liteyuki.message.on -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `on_message(rule: Rule, priority: int, block: bool) -> Matcher` - - - -
-源代码 - -```python -def on_message(rule: Rule=Rule(), priority: int=0, block: bool=True) -> Matcher: - matcher = Matcher(rule, priority, block) - for i, m in enumerate(_matcher_list): - if m.priority < matcher.priority: - _matcher_list.insert(i, matcher) - break - else: - _matcher_list.append(matcher) - return matcher -``` -
- -### ***var*** `current_priority = -1` - - - -### ***var*** `matcher = Matcher(rule, priority, block)` - - - -### ***var*** `current_priority = matcher.priority` - - - diff --git a/docs/dev/api/message/rule.md b/docs/dev/api/message/rule.md deleted file mode 100644 index a135f740..00000000 --- a/docs/dev/api/message/rule.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: liteyuki.message.rule -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `Rule` - - - -###   ***def*** `__init__(self, handler: Optional[RuleHandler]) -> None` - -  - -
-源代码 - -```python -def __init__(self, handler: Optional[RuleHandler]=None): - self.handler = handler -``` -
- diff --git a/docs/dev/api/message/session.md b/docs/dev/api/message/session.md deleted file mode 100644 index f8fcad6f..00000000 --- a/docs/dev/api/message/session.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.message.session -order: 1 -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/mkdoc.md b/docs/dev/api/mkdoc.md deleted file mode 100644 index 802aff3c..00000000 --- a/docs/dev/api/mkdoc.md +++ /dev/null @@ -1,473 +0,0 @@ ---- -title: liteyuki.mkdoc -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `get_relative_path(base_path: str, target_path: str) -> str` - -获取相对路径 - -Args: - - base_path: 基础路径 - - target_path: 目标路径 - -
-源代码 - -```python -def get_relative_path(base_path: str, target_path: str) -> str: - """ - 获取相对路径 - Args: - base_path: 基础路径 - target_path: 目标路径 - """ - return os.path.relpath(target_path, base_path) -``` -
- -### ***def*** `write_to_files(file_data: dict[str, str]) -> None` - -输出文件 - -Args: - - file_data: 文件数据 相对路径 - -
-源代码 - -```python -def write_to_files(file_data: dict[str, str]): - """ - 输出文件 - Args: - file_data: 文件数据 相对路径 - """ - for rp, data in file_data.items(): - if not os.path.exists(os.path.dirname(rp)): - os.makedirs(os.path.dirname(rp)) - with open(rp, 'w', encoding='utf-8') as f: - f.write(data) -``` -
- -### ***def*** `get_file_list(module_folder: str) -> None` - - - -
-源代码 - -```python -def get_file_list(module_folder: str): - file_list = [] - for root, dirs, files in os.walk(module_folder): - for file in files: - if file.endswith(('.py', '.pyi')): - file_list.append(os.path.join(root, file)) - return file_list -``` -
- -### ***def*** `get_module_info_normal(file_path: str, ignore_private: bool) -> ModuleInfo` - -获取函数和类 - -Args: - - file_path: Python 文件路径 - - ignore_private: 忽略私有函数和类 - -Returns: - - 模块信息 - -
-源代码 - -```python -def get_module_info_normal(file_path: str, ignore_private: bool=True) -> ModuleInfo: - """ - 获取函数和类 - Args: - file_path: Python 文件路径 - ignore_private: 忽略私有函数和类 - Returns: - 模块信息 - """ - with open(file_path, 'r', encoding='utf-8') as file: - file_content = file.read() - tree = ast.parse(file_content) - dot_sep_module_path = file_path.replace(os.sep, '.').replace('.py', '').replace('.pyi', '') - module_docstring = ast.get_docstring(tree) - module_info = ModuleInfo(module_path=dot_sep_module_path, functions=[], classes=[], attributes=[], docstring=module_docstring if module_docstring else '') - for node in ast.walk(tree): - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): - if not any((isinstance(parent, ast.ClassDef) for parent in ast.iter_child_nodes(node))) and (not ignore_private or not node.name.startswith('_')): - if node.args.args: - first_arg = node.args.args[0] - if first_arg.arg in ('self', 'cls'): - continue - function_docstring = ast.get_docstring(node) - func_info = FunctionInfo(name=node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args], return_type=ast.unparse(node.returns) if node.returns else 'None', docstring=function_docstring if function_docstring else '', type=DefType.FUNCTION, is_async=isinstance(node, ast.AsyncFunctionDef), source_code=ast.unparse(node)) - module_info.functions.append(func_info) - elif isinstance(node, ast.ClassDef): - class_docstring = ast.get_docstring(node) - class_info = ClassInfo(name=node.name, docstring=class_docstring if class_docstring else '', methods=[], attributes=[], inherit=[ast.unparse(base) for base in node.bases]) - for class_node in node.body: - if isinstance(class_node, ast.FunctionDef) and (not ignore_private or not class_node.name.startswith('_') or class_node.name == '__init__'): - method_docstring = ast.get_docstring(class_node) - def_type = DefType.METHOD - if class_node.decorator_list: - if any((isinstance(decorator, ast.Name) and decorator.id == 'staticmethod' for decorator in class_node.decorator_list)): - def_type = DefType.STATIC_METHOD - elif any((isinstance(decorator, ast.Name) and decorator.id == 'classmethod' for decorator in class_node.decorator_list)): - def_type = DefType.CLASS_METHOD - elif any((isinstance(decorator, ast.Name) and decorator.id == 'property' for decorator in class_node.decorator_list)): - def_type = DefType.PROPERTY - class_info.methods.append(FunctionInfo(name=class_node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in class_node.args.args], return_type=ast.unparse(class_node.returns) if class_node.returns else 'None', docstring=method_docstring if method_docstring else '', type=def_type, is_async=isinstance(class_node, ast.AsyncFunctionDef), source_code=ast.unparse(class_node))) - elif isinstance(class_node, ast.Assign): - for target in class_node.targets: - if isinstance(target, ast.Name): - class_info.attributes.append(AttributeInfo(name=target.id, type=ast.unparse(class_node.value))) - module_info.classes.append(class_info) - elif isinstance(node, ast.Assign): - if not any((isinstance(parent, (ast.ClassDef, ast.FunctionDef)) for parent in ast.iter_child_nodes(node))): - for target in node.targets: - if isinstance(target, ast.Name) and (not ignore_private or not target.id.startswith('_')): - attr_type = NO_TYPE_HINT - if isinstance(node.value, ast.AnnAssign) and node.value.annotation: - attr_type = ast.unparse(node.value.annotation) - module_info.attributes.append(AttributeInfo(name=target.id, type=attr_type, value=ast.unparse(node.value) if node.value else None)) - return module_info -``` -
- -### ***def*** `generate_markdown(module_info: ModuleInfo, front_matter: Any) -> str` - -生成模块的Markdown - -你可在此自定义生成的Markdown格式 - -Args: - - module_info: 模块信息 - - front_matter: 自定义选项title, index, icon, category - -Returns: - - Markdown 字符串 - -
-源代码 - -```python -def generate_markdown(module_info: ModuleInfo, front_matter=None) -> str: - """ - 生成模块的Markdown - 你可在此自定义生成的Markdown格式 - Args: - module_info: 模块信息 - front_matter: 自定义选项title, index, icon, category - Returns: - Markdown 字符串 - """ - content = '' - front_matter = '---\n' + '\n'.join([f'{k}: {v}' for k, v in front_matter.items()]) + '\n---\n\n' - content += front_matter - for func in module_info.functions: - args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in func.args] - content += f"### ***{('async ' if func.is_async else '')}def*** `{func.name}({', '.join(args_with_type)}) -> {func.return_type}`\n\n" - func.docstring = func.docstring.replace('\n', '\n\n') - content += f'{func.docstring}\n\n' - content += f'
\n源代码\n\n```python\n{func.source_code}\n```\n
\n\n' - for cls in module_info.classes: - if cls.inherit: - inherit = f"({', '.join(cls.inherit)})" if cls.inherit else '' - content += f'### ***class*** `{cls.name}{inherit}`\n\n' - else: - content += f'### ***class*** `{cls.name}`\n\n' - cls.docstring = cls.docstring.replace('\n', '\n\n') - content += f'{cls.docstring}\n\n' - for method in cls.methods: - if method.type != DefType.METHOD: - args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in method.args] - content += f'###   ***@{method.type.value}***\n' - else: - args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] and arg[0] != 'self' else arg[0] for arg in method.args] - content += f"###   ***{('async ' if method.is_async else '')}def*** `{method.name}({', '.join(args_with_type)}) -> {method.return_type}`\n\n" - method.docstring = method.docstring.replace('\n', '\n\n') - content += f' {method.docstring}\n\n' - content += f'
\n源代码\n\n```python\n{method.source_code}\n```\n
\n\n' - for attr in cls.attributes: - content += f'###   ***attr*** `{attr.name}: {attr.type}`\n\n' - for attr in module_info.attributes: - if attr.type == NO_TYPE_HINT: - content += f'### ***var*** `{attr.name} = {attr.value}`\n\n' - else: - content += f'### ***var*** `{attr.name}: {attr.type} = {attr.value}`\n\n' - attr.docstring = attr.docstring.replace('\n', '\n\n') - content += f'{attr.docstring}\n\n' - return content -``` -
- -### ***def*** `generate_docs(module_folder: str, output_dir: str, with_top: bool, ignored_paths: Any) -> None` - -生成文档 - -Args: - - module_folder: 模块文件夹 - - output_dir: 输出文件夹 - - with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b, True时例如docs/api/module/module_a.md, docs/api/module/module_b.md - - ignored_paths: 忽略的路径 - -
-源代码 - -```python -def generate_docs(module_folder: str, output_dir: str, with_top: bool=False, ignored_paths=None): - """ - 生成文档 - Args: - module_folder: 模块文件夹 - output_dir: 输出文件夹 - with_top: 是否包含顶层文件夹 False时例如docs/api/module_a, docs/api/module_b, True时例如docs/api/module/module_a.md, docs/api/module/module_b.md - ignored_paths: 忽略的路径 - """ - if ignored_paths is None: - ignored_paths = [] - file_data: dict[str, str] = {} - file_list = get_file_list(module_folder) - shutil.rmtree(output_dir, ignore_errors=True) - os.mkdir(output_dir) - replace_data = {'__init__': 'README', '.py': '.md'} - for pyfile_path in file_list: - if any((ignored_path.replace('\\', '/') in pyfile_path.replace('\\', '/') for ignored_path in ignored_paths)): - continue - no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path) - rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path - for rk, rv in replace_data.items(): - rel_md_path = rel_md_path.replace(rk, rv) - abs_md_path = os.path.join(output_dir, rel_md_path) - module_info = get_module_info_normal(pyfile_path) - if 'README' in abs_md_path: - front_matter = {'title': module_info.module_path.replace('.__init__', '').replace('_', '\\n'), 'index': 'true', 'icon': 'laptop-code', 'category': 'API'} - else: - front_matter = {'title': module_info.module_path.replace('_', '\\n'), 'order': '1', 'icon': 'laptop-code', 'category': 'API'} - md_content = generate_markdown(module_info, front_matter) - print(f'Generate {pyfile_path} -> {abs_md_path}') - file_data[abs_md_path] = md_content - write_to_files(file_data) -``` -
- -### ***class*** `DefType(Enum)` - - - -###   ***attr*** `FUNCTION: 'function'` - -###   ***attr*** `METHOD: 'method'` - -###   ***attr*** `STATIC_METHOD: 'staticmethod'` - -###   ***attr*** `CLASS_METHOD: 'classmethod'` - -###   ***attr*** `PROPERTY: 'property'` - -### ***class*** `FunctionInfo(BaseModel)` - - - -### ***class*** `AttributeInfo(BaseModel)` - - - -### ***class*** `ClassInfo(BaseModel)` - - - -### ***class*** `ModuleInfo(BaseModel)` - - - -### ***var*** `NO_TYPE_ANY = 'Any'` - - - -### ***var*** `NO_TYPE_HINT = 'NoTypeHint'` - - - -### ***var*** `FUNCTION = 'function'` - - - -### ***var*** `METHOD = 'method'` - - - -### ***var*** `STATIC_METHOD = 'staticmethod'` - - - -### ***var*** `CLASS_METHOD = 'classmethod'` - - - -### ***var*** `PROPERTY = 'property'` - - - -### ***var*** `file_list = []` - - - -### ***var*** `dot_sep_module_path = file_path.replace(os.sep, '.').replace('.py', '').replace('.pyi', '')` - - - -### ***var*** `module_docstring = ast.get_docstring(tree)` - - - -### ***var*** `module_info = ModuleInfo(module_path=dot_sep_module_path, functions=[], classes=[], attributes=[], docstring=module_docstring if module_docstring else '')` - - - -### ***var*** `content = ''` - - - -### ***var*** `front_matter = '---\n' + '\n'.join([f'{k}: {v}' for k, v in front_matter.items()]) + '\n---\n\n'` - - - -### ***var*** `file_list = get_file_list(module_folder)` - - - -### ***var*** `replace_data = {'__init__': 'README', '.py': '.md'}` - - - -### ***var*** `file_content = file.read()` - - - -### ***var*** `tree = ast.parse(file_content)` - - - -### ***var*** `args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in func.args]` - - - -### ***var*** `ignored_paths = []` - - - -### ***var*** `no_module_name_pyfile_path = get_relative_path(module_folder, pyfile_path)` - - - -### ***var*** `rel_md_path = pyfile_path if with_top else no_module_name_pyfile_path` - - - -### ***var*** `abs_md_path = os.path.join(output_dir, rel_md_path)` - - - -### ***var*** `module_info = get_module_info_normal(pyfile_path)` - - - -### ***var*** `md_content = generate_markdown(module_info, front_matter)` - - - -### ***var*** `inherit = f"({', '.join(cls.inherit)})" if cls.inherit else ''` - - - -### ***var*** `rel_md_path = rel_md_path.replace(rk, rv)` - - - -### ***var*** `front_matter = {'title': module_info.module_path.replace('.__init__', '').replace('_', '\\n'), 'index': 'true', 'icon': 'laptop-code', 'category': 'API'}` - - - -### ***var*** `front_matter = {'title': module_info.module_path.replace('_', '\\n'), 'order': '1', 'icon': 'laptop-code', 'category': 'API'}` - - - -### ***var*** `function_docstring = ast.get_docstring(node)` - - - -### ***var*** `func_info = FunctionInfo(name=node.name, args=[(arg.arg, ast.unparse(arg.annotation) if arg.annotation else NO_TYPE_ANY) for arg in node.args.args], return_type=ast.unparse(node.returns) if node.returns else 'None', docstring=function_docstring if function_docstring else '', type=DefType.FUNCTION, is_async=isinstance(node, ast.AsyncFunctionDef), source_code=ast.unparse(node))` - - - -### ***var*** `class_docstring = ast.get_docstring(node)` - - - -### ***var*** `class_info = ClassInfo(name=node.name, docstring=class_docstring if class_docstring else '', methods=[], attributes=[], inherit=[ast.unparse(base) for base in node.bases])` - - - -### ***var*** `args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] else arg[0] for arg in method.args]` - - - -### ***var*** `args_with_type = [f'{arg[0]}: {arg[1]}' if arg[1] and arg[0] != 'self' else arg[0] for arg in method.args]` - - - -### ***var*** `first_arg = node.args.args[0]` - - - -### ***var*** `method_docstring = ast.get_docstring(class_node)` - - - -### ***var*** `def_type = DefType.METHOD` - - - -### ***var*** `def_type = DefType.STATIC_METHOD` - - - -### ***var*** `attr_type = NO_TYPE_HINT` - - - -### ***var*** `def_type = DefType.CLASS_METHOD` - - - -### ***var*** `attr_type = ast.unparse(node.value.annotation)` - - - -### ***var*** `def_type = DefType.PROPERTY` - - - diff --git a/docs/dev/api/plugin/README.md b/docs/dev/api/plugin/README.md deleted file mode 100644 index 6c67f485..00000000 --- a/docs/dev/api/plugin/README.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: liteyuki.plugin -index: true -icon: laptop-code -category: API ---- - -### ***def*** `get_loaded_plugins() -> dict[str, Plugin]` - -获取已加载的插件 - -Returns: - - dict[str, Plugin]: 插件字典 - -
-源代码 - -```python -def get_loaded_plugins() -> dict[str, Plugin]: - """ - 获取已加载的插件 - Returns: - dict[str, Plugin]: 插件字典 - """ - return _plugins -``` -
- diff --git a/docs/dev/api/plugin/load.md b/docs/dev/api/plugin/load.md deleted file mode 100644 index b999f5e1..00000000 --- a/docs/dev/api/plugin/load.md +++ /dev/null @@ -1,199 +0,0 @@ ---- -title: liteyuki.plugin.load -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `load_plugin(module_path: str | Path) -> Optional[Plugin]` - -加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。 - - - -参数: - - module_path: 插件名称 `path.to.your.plugin` - - 或插件路径 `pathlib.Path(path/to/your/plugin)` - -
-源代码 - -```python -def load_plugin(module_path: str | Path) -> Optional[Plugin]: - """加载单个插件,可以是本地插件或是通过 `pip` 安装的插件。 - - 参数: - module_path: 插件名称 `path.to.your.plugin` - 或插件路径 `pathlib.Path(path/to/your/plugin)` - """ - module_path = path_to_module_name(Path(module_path)) if isinstance(module_path, Path) else module_path - try: - module = import_module(module_path) - _plugins[module.__name__] = Plugin(name=module.__name__, module=module, 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 = format_display_name(f"{metadata.name}({module.__name__.split('.')[-1]})", metadata.type) - logger.opt(colors=True).success(f'Succeeded to load liteyuki plugin "{display_name}"') - return _plugins[module.__name__] - except Exception as e: - logger.opt(colors=True).success(f'Failed to load liteyuki plugin "{module_path}"') - traceback.print_exc() - return None -``` -
- -### ***def*** `load_plugins() -> set[Plugin]` - -导入文件夹下多个插件 - - - -参数: - - plugin_dir: 文件夹路径 - - ignore_warning: 是否忽略警告,通常是目录不存在或目录为空 - -
-源代码 - -```python -def load_plugins(*plugin_dir: str, ignore_warning: bool=True) -> set[Plugin]: - """导入文件夹下多个插件 - - 参数: - plugin_dir: 文件夹路径 - ignore_warning: 是否忽略警告,通常是目录不存在或目录为空 - """ - plugins = set() - for dir_path in plugin_dir: - if not os.path.exists(dir_path): - if not ignore_warning: - logger.warning(f"Plugins dir '{dir_path}' does not exist.") - continue - if not os.listdir(dir_path): - if not ignore_warning: - logger.warning(f"Plugins dir '{dir_path}' is empty.") - continue - if not os.path.isdir(dir_path): - if not ignore_warning: - logger.warning(f"Plugins dir '{dir_path}' is not a directory.") - continue - for f in os.listdir(dir_path): - path = Path(os.path.join(dir_path, f)) - module_name = None - if os.path.isfile(path) and f.endswith('.py') and (f != '__init__.py'): - module_name = f'{path_to_module_name(Path(dir_path))}.{f[:-3]}' - elif os.path.isdir(path) and os.path.exists(os.path.join(path, '__init__.py')): - module_name = path_to_module_name(path) - if module_name: - load_plugin(module_name) - if _plugins.get(module_name): - plugins.add(_plugins[module_name]) - return plugins -``` -
- -### ***def*** `format_display_name(display_name: str, plugin_type: PluginType) -> str` - -设置插件名称颜色,根据不同类型插件设置颜色 - -Args: - - display_name: 插件名称 - - plugin_type: 插件类型 - - - -Returns: - - str: 设置后的插件名称 name - -
-源代码 - -```python -def format_display_name(display_name: str, plugin_type: PluginType) -> str: - """ - 设置插件名称颜色,根据不同类型插件设置颜色 - Args: - display_name: 插件名称 - plugin_type: 插件类型 - - Returns: - str: 设置后的插件名称 name - """ - color = 'y' - match plugin_type: - case PluginType.APPLICATION: - color = 'm' - case PluginType.TEST: - color = 'g' - case PluginType.MODULE: - color = 'e' - case PluginType.SERVICE: - color = 'c' - return f'<{color}>{display_name} [{plugin_type.name}]' -``` -
- -### ***var*** `module_path = path_to_module_name(Path(module_path)) if isinstance(module_path, Path) else module_path` - - - -### ***var*** `plugins = set()` - - - -### ***var*** `color = 'y'` - - - -### ***var*** `module = import_module(module_path)` - - - -### ***var*** `display_name = module.__name__.split('.')[-1]` - - - -### ***var*** `display_name = format_display_name(f"{metadata.name}({module.__name__.split('.')[-1]})", metadata.type)` - - - -### ***var*** `path = Path(os.path.join(dir_path, f))` - - - -### ***var*** `module_name = None` - - - -### ***var*** `color = 'm'` - - - -### ***var*** `color = 'g'` - - - -### ***var*** `color = 'e'` - - - -### ***var*** `color = 'c'` - - - -### ***var*** `module_name = f'{path_to_module_name(Path(dir_path))}.{f[:-3]}'` - - - -### ***var*** `module_name = path_to_module_name(path)` - - - diff --git a/docs/dev/api/plugin/manager.md b/docs/dev/api/plugin/manager.md deleted file mode 100644 index 7d4d951c..00000000 --- a/docs/dev/api/plugin/manager.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.plugin.manager -order: 1 -icon: laptop-code -category: API ---- - diff --git a/docs/dev/api/plugin/model.md b/docs/dev/api/plugin/model.md deleted file mode 100644 index 6f986049..00000000 --- a/docs/dev/api/plugin/model.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: liteyuki.plugin.model -order: 1 -icon: laptop-code -category: API ---- - -### ***class*** `PluginType(Enum)` - -插件类型枚举值 - -###   ***attr*** `APPLICATION: 'application'` - -###   ***attr*** `SERVICE: 'service'` - -###   ***attr*** `MODULE: 'module'` - -###   ***attr*** `UNCLASSIFIED: 'unclassified'` - -###   ***attr*** `TEST: 'test'` - -### ***class*** `PluginMetadata(BaseModel)` - -轻雪插件元数据,由插件编写者提供,name为必填项 - -Attributes: - ----------- - - - -name: str - - 插件名称 - -description: str - - 插件描述 - -usage: str - - 插件使用方法 - -type: str - - 插件类型 - -author: str - - 插件作者 - -homepage: str - - 插件主页 - -extra: dict[str, Any] - - 额外信息 - -### ***class*** `Plugin(BaseModel)` - -存储插件信息 - -###   ***attr*** `model_config: {'arbitrary_types_allowed': True}` - -### ***var*** `APPLICATION = 'application'` - - - -### ***var*** `SERVICE = 'service'` - - - -### ***var*** `MODULE = 'module'` - - - -### ***var*** `UNCLASSIFIED = 'unclassified'` - - - -### ***var*** `TEST = 'test'` - - - -### ***var*** `model_config = {'arbitrary_types_allowed': True}` - - - diff --git a/docs/dev/api/utils.md b/docs/dev/api/utils.md deleted file mode 100644 index 84532f46..00000000 --- a/docs/dev/api/utils.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: liteyuki.utils -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `is_coroutine_callable(call: Callable[..., Any]) -> bool` - -判断是否为协程可调用对象 - -Args: - - call: 可调用对象 - -Returns: - - bool: 是否为协程可调用对象 - -
-源代码 - -```python -def is_coroutine_callable(call: Callable[..., Any]) -> bool: - """ - 判断是否为协程可调用对象 - Args: - call: 可调用对象 - Returns: - bool: 是否为协程可调用对象 - """ - if inspect.isroutine(call): - return inspect.iscoroutinefunction(call) - if inspect.isclass(call): - return False - func_ = getattr(call, '__call__', None) - return inspect.iscoroutinefunction(func_) -``` -
- -### ***def*** `run_coroutine() -> None` - -运行协程 - -Args: - - coro: - - - -Returns: - -
-源代码 - -```python -def run_coroutine(*coro: Coroutine): - """ - 运行协程 - Args: - coro: - - Returns: - - """ - try: - loop = asyncio.get_event_loop() - if loop.is_running(): - for c in coro: - asyncio.ensure_future(c) - else: - for c in coro: - loop.run_until_complete(c) - except RuntimeError: - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - loop.run_until_complete(asyncio.gather(*coro)) - loop.close() - except Exception as e: - logger.error(f'Exception occurred: {e}') -``` -
- -### ***def*** `path_to_module_name(path: Path) -> str` - -转换路径为模块名 - -Args: - - path: 路径a/b/c/d -> a.b.c.d - -Returns: - - str: 模块名 - -
-源代码 - -```python -def path_to_module_name(path: Path) -> str: - """ - 转换路径为模块名 - Args: - path: 路径a/b/c/d -> a.b.c.d - Returns: - str: 模块名 - """ - rel_path = path.resolve().relative_to(Path.cwd().resolve()) - if rel_path.stem == '__init__': - return '.'.join(rel_path.parts[:-1]) - else: - return '.'.join(rel_path.parts[:-1] + (rel_path.stem,)) -``` -
- -### ***def*** `async_wrapper(func: Callable[..., Any]) -> Callable[..., Coroutine]` - -异步包装器 - -Args: - - func: Sync Callable - -Returns: - - Coroutine: Asynchronous Callable - -
-源代码 - -```python -def async_wrapper(func: Callable[..., Any]) -> Callable[..., Coroutine]: - """ - 异步包装器 - Args: - func: Sync Callable - Returns: - Coroutine: Asynchronous Callable - """ - - async def wrapper(*args, **kwargs): - return func(*args, **kwargs) - wrapper.__signature__ = inspect.signature(func) - return wrapper -``` -
- -### ***async def*** `wrapper() -> None` - - - -
-源代码 - -```python -async def wrapper(*args, **kwargs): - return func(*args, **kwargs) -``` -
- -### ***var*** `IS_MAIN_PROCESS = multiprocessing.current_process().name == 'MainProcess'` - - - -### ***var*** `func_ = getattr(call, '__call__', None)` - - - -### ***var*** `rel_path = path.resolve().relative_to(Path.cwd().resolve())` - - - -### ***var*** `loop = asyncio.get_event_loop()` - - - -### ***var*** `loop = asyncio.new_event_loop()` - - - diff --git a/docs/dev/dev_comm.md b/docs/dev/dev_comm.md deleted file mode 100644 index b2011e35..00000000 --- a/docs/dev/dev_comm.md +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: 进程通信 -icon: exchange-alt -order: 4 -category: 开发 ---- - -## **通道通信** - -### 简介 - -轻雪运行在主进程 MainProcess 里,其他插件框架进程是伴随的子进程,因此无法通过内存共享和直接对象传递的方式进行通信,轻雪提供了一个通道`Channel`用于跨进程通信,你可以通过`Channel`发送消息给其他进程,也可以监听其他进程的消息。 - -例如子进程接收到用户信息需要重启机器人,这时可以通过通道对主进程发送消息,主进程接收到消息后重启对应子进程。 - -### 示例 - -通道是全双工的,有两种接收模式,但一个通道只能使用一种,即被动模式和主动模式,被动模式由`chan.on_receive()`装饰回调函数实现,主动模式需调用`chan.receive()`实现 - -- 创建子进程的同时会初始化一个被动通道和一个主动通道,且通道标识为`{process_name}-active`和`{process_name}-passive`, -- 主进程中通过`get_channel`函数获取通道对象 -- 子进程中导入单例`active_channel`及`passive_channel`即可 - -> 在轻雪插件中(主进程中) - -```python -import asyncio - -from liteyuki.comm import get_channel, Channel -from liteyuki import get_bot - -# get_channel函数获取通道对象,参数为调用set_channel时的通道标识 -channel_passive = get_channel("nonebot-passive") # 获取被动通道 -channel_active = get_channel("nonebot-active") # 获取主动通道 -liteyuki_bot = get_bot() - - -# 注册一个函数在轻雪启动后运行 -@liteyuki_bot.on_after_start -async def send_data(): - while True: - channel_passive.send("I am liteyuki main process passive") - channel_active.send("I am liteyuki main process active") - await asyncio.sleep(3) # 每3秒发送一次消息 -``` - -> 在子进程中(例如NoneBot插件中) - -```python -from nonebot import get_driver -from liteyuki.comm import active_channel, passive_channel # 子进程中获取通道直接导入进程全局单例即可 -from liteyuki.log import logger - -driver = get_driver() - - -# 被动模式,通过装饰器注册一个函数在接收到消息时运行,每次接收到字符串数据时都会运行 -@passive_channel.on_receive(filter_func=lambda data: isinstance(data, str)) -async def on_passive_receive(data): - logger.info(f"Passive receive: {data}") - - -# 注册一个函数在NoneBot启动后运行 -@driver.on_startup -def on_startup(): - while True: - data = active_channel.receive() - logger.info(f"Active receive: {data}") -``` - -> 启动后控制台输出 - -```log -0000-00-00 00:00:00 [ℹ️信息] Passive receive: I am liteyuki main process passive -0000-00-00 00:00:00 [ℹ️信息] Active receive: I am liteyuki main process active -0000-00-00 00:00:03 [ℹ️信息] Passive receive: I am liteyuki main process passive -0000-00-00 00:00:03 [ℹ️信息] Active receive: I am liteyuki main process active -... -``` - -## **共享内存通信** - -### 简介 - -- 相比于普通进程通信,内存共享使得代码编写更加简洁,轻雪框架提供了一个内存共享通信的接口,你可以通过`storage`模块实现内存共享通信,该模块封装通道实现 -- 内存共享是线程安全的,你可以在多个线程中读写共享内存,线程锁会自动保护共享内存的读写操作 - -### 示例 - -> 在任意进程中均可使用 - -```python -from liteyuki.comm.storage import shared_memory - -shared_memory.set("key", "value") # 设置共享内存 -value = shared_memory.get("key") # 获取共享内存 -``` - -源代码:[liteyuki/comm/storage.py](https://github.com/LiteyukiStudio/LiteyukiBot/blob/main/liteyuki/comm/storage.py) diff --git a/docs/dev/dev_lyfunc.md b/docs/dev/dev_lyfunc.md deleted file mode 100644 index db33a2db..00000000 --- a/docs/dev/dev_lyfunc.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: 轻雪函数 -icon: code -order: 2 -category: 开发 ---- - -## **轻雪函数** - -轻雪函数 Liteyuki Function 是轻雪的一个功能,它允许你在轻雪中运行一些自定义的由数据驱动的命令,类似于Minecraft的mcfunction,属于资源包的一部分,但需单独起篇幅. - -### **函数文件** - -函数文件放在资源包的`functions`目录下,文件名以`.mcfunction` `.lyfunction` `.lyf`结尾,例如`test.mcfunction`,文件内容为一系列的命令,每行一个命令,支持单行注释`#`(编辑时的语法高亮可采取`shell`格式),例如: - -```shell -# 在发信器输出"hello world" -cmd echo hello world - -# 如果你想同时输出多行内容可以尝试换行符(Python格式) -cmd echo hello world\nLiteyuki bot -``` - -也支持句末注释,例如: -```shell -cmd echo hello world # 输出"hello world" -``` - -### **命令文档** - -```shell -var [var2=value2] ... # 定义变量 -cmd # 在设备上执行命令 -api [var=value...] # 调用Bot API -function # 调用函数,可递归 -sleep