diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 54c9858f..54afb942 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -33,6 +33,13 @@ jobs: cd docs pnpm install + - name: Python生成API文档 + uses: actions/setup-python@v2 + run: |- + python -m pip install pydantic~=2.8.3 + python liteyuki/mkdoc.py + + - name: 构建文档 env: NODE_OPTIONS: --max_old_space_size=8192 diff --git a/docs/.vuepress/theme.ts b/docs/.vuepress/theme.ts index 249938d9..f33f7bf9 100644 --- a/docs/.vuepress/theme.ts +++ b/docs/.vuepress/theme.ts @@ -163,23 +163,23 @@ export default hopeTheme({ // src: "/assets/icon/chrome-mask-512.png", // sizes: "512x512", // purpose: "maskable", - // type: "image/png", + // type_: "image/png", // }, // { // src: "/assets/icon/chrome-mask-192.png", // sizes: "192x192", // purpose: "maskable", - // type: "image/png", + // type_: "image/png", // }, // { // src: "/assets/icon/chrome-512.png", // sizes: "512x512", - // type: "image/png", + // type_: "image/png", // }, // { // src: "/assets/icon/chrome-192.png", // sizes: "192x192", - // type: "image/png", + // type_: "image/png", // }, // ], // shortcuts: [ @@ -192,7 +192,7 @@ export default hopeTheme({ // src: "/assets/icon/guide-maskable.png", // sizes: "192x192", // purpose: "maskable", - // type: "image/png", + // type_: "image/png", // }, // ], // }, diff --git a/docs/dev/api/comm/channel.md b/docs/dev/api/comm/channel.md index c05d1cd8..f34d7054 100644 --- a/docs/dev/api/comm/channel.md +++ b/docs/dev/api/comm/channel.md @@ -1,5 +1,5 @@ --- -title: liteyuki.comm.channel +title: liteyuki.comm.channel_ order: 1 icon: laptop-code category: API diff --git a/docs/en/dev/api/liteyuki/README.md b/docs/en/dev/api/liteyuki/README.md deleted file mode 100644 index 181ec73d..00000000 --- a/docs/en/dev/api/liteyuki/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/en/dev/api/liteyuki/bot/README.md b/docs/en/dev/api/liteyuki/bot/README.md deleted file mode 100644 index 8f027bf5..00000000 --- a/docs/en/dev/api/liteyuki/bot/README.md +++ /dev/null @@ -1,227 +0,0 @@ ---- -title: liteyuki.bot -index: true -icon: laptop-code -category: API ---- - -### ***def*** `get_bot() -> LiteyukiBot` - -获取轻雪实例 - - - -Returns: - - LiteyukiBot: 当前的轻雪实例 - -### ***def*** `get_config(key: str, default: Any) -> Any` - -获取配置 - -Args: - - key: 配置键 - - default: 默认值 - - - -Returns: - - Any: 配置值 - -### ***def*** `get_config_with_compat(key: str, compat_keys: tuple[str], default: Any) -> Any` - -获取配置,兼容旧版本 - -Args: - - key: 配置键 - - compat_keys: 兼容键 - - default: 默认值 - - - -Returns: - - Any: 配置值 - -### ***def*** `print_logo() -> None` - - - -### ***class*** `LiteyukiBot` - - - -###   ***def*** `__init__(self) -> None` - - 初始化轻雪实例 - -Args: - - *args: - - **kwargs: 配置 - -###   ***def*** `run(self) -> None` - - 启动逻辑 - -###   ***def*** `keep_alive(self) -> None` - - 保持轻雪运行 - -Returns: - -###   ***def*** `restart(self, delay: int) -> None` - - 重启轻雪本体 - -Returns: - -###   ***def*** `restart_process(self, name: Optional[str]) -> None` - - 停止轻雪 - -Args: - - name: 进程名称, 默认为None, 所有进程 - -Returns: - -###   ***def*** `init(self) -> None` - - 初始化轻雪, 自动调用 - -Returns: - -###   ***def*** `init_logger(self) -> None` - -  - -###   ***def*** `stop(self) -> None` - - 停止轻雪 - -Returns: - -###   ***def*** `on_before_start(self, func: LIFESPAN_FUNC) -> None` - - 注册启动前的函数 - -Args: - - func: - - - -Returns: - -###   ***def*** `on_after_start(self, func: LIFESPAN_FUNC) -> None` - - 注册启动后的函数 - -Args: - - func: - - - -Returns: - -###   ***def*** `on_after_shutdown(self, func: LIFESPAN_FUNC) -> None` - - 注册停止后的函数:未实现 - -Args: - - func: - - - -Returns: - -###   ***def*** `on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> None` - - 注册进程停止前的函数,为子进程停止时调用 - -Args: - - func: - - - -Returns: - -###   ***def*** `on_before_process_restart(self, func: LIFESPAN_FUNC) -> None` - - 注册进程重启前的函数,为子进程重启时调用 - -Args: - - func: - - - -Returns: - -###   ***def*** `on_after_restart(self, func: LIFESPAN_FUNC) -> None` - - 注册重启后的函数:未实现 - -Args: - - func: - - - -Returns: - -###   ***def*** `on_after_nonebot_init(self, func: LIFESPAN_FUNC) -> None` - - 注册nonebot初始化后的函数 - -Args: - - func: - - - -Returns: - -### ***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/en/dev/api/liteyuki/bot/lifespan.md b/docs/en/dev/api/liteyuki/bot/lifespan.md deleted file mode 100644 index 396403b4..00000000 --- a/docs/en/dev/api/liteyuki/bot/lifespan.md +++ /dev/null @@ -1,170 +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: - -### ***class*** `Lifespan` - - - -###   ***def*** `__init__(self) -> None` - - 轻雪生命周期管理,启动、停止、重启 - -###   ***@staticmethod*** -###   ***def*** `run_funcs(funcs: list[LIFESPAN_FUNC | PROCESS_LIFESPAN_FUNC]) -> None` - - 运行函数 - -Args: - - funcs: - -Returns: - -###   ***def*** `on_before_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册启动时的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -###   ***def*** `on_after_start(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册启动时的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -###   ***def*** `on_before_process_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册停止前的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -###   ***def*** `on_after_shutdown(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册停止后的函数 - -Args: - - func: - - - -Returns: - - LIFESPAN_FUNC: - -###   ***def*** `on_before_process_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册重启时的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -###   ***def*** `on_after_restart(self, func: LIFESPAN_FUNC) -> LIFESPAN_FUNC` - - 注册重启后的函数 - -Args: - - func: - -Returns: - - LIFESPAN_FUNC: - -###   ***def*** `on_after_nonebot_init(self, func: Any) -> None` - - 注册 NoneBot 初始化后的函数 - -Args: - - func: - - - -Returns: - -###   ***def*** `before_start(self) -> None` - - 启动前 - -Returns: - -###   ***def*** `after_start(self) -> None` - - 启动后 - -Returns: - -###   ***def*** `before_process_shutdown(self) -> None` - - 停止前 - -Returns: - -###   ***def*** `after_shutdown(self) -> None` - - 停止后 - -Returns: - -###   ***def*** `before_process_restart(self) -> None` - - 重启前 - -Returns: - -###   ***def*** `after_restart(self) -> None` - - 重启后 - -Returns: - -### ***var*** `tasks = []` - - - -### ***var*** `loop = asyncio.get_event_loop()` - - - -### ***var*** `loop = asyncio.new_event_loop()` - - - diff --git a/docs/en/dev/api/liteyuki/comm/README.md b/docs/en/dev/api/liteyuki/comm/README.md deleted file mode 100644 index 09bccc34..00000000 --- a/docs/en/dev/api/liteyuki/comm/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.comm -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/en/dev/api/liteyuki/comm/channel.md b/docs/en/dev/api/liteyuki/comm/channel.md deleted file mode 100644 index c05d1cd8..00000000 --- a/docs/en/dev/api/liteyuki/comm/channel.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: liteyuki.comm.channel -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `set_channel(name: str, channel: Channel) -> None` - -设置通道实例 - -Args: - - name: 通道名称 - - channel: 通道实例 - -### ***def*** `set_channels(channels: dict[str, Channel]) -> None` - -设置通道实例 - -Args: - - channels: 通道名称 - -### ***def*** `get_channel(name: str) -> Channel` - -获取通道实例 - -Args: - - name: 通道名称 - -Returns: - -### ***def*** `get_channels() -> dict[str, Channel]` - -获取通道实例 - -Returns: - -### ***def*** `on_set_channel(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***def*** `on_get_channel(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***def*** `on_get_channels(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***def*** `decorator(func: Callable[[T], Any]) -> Callable[[T], Any]` - - - -### ***async def*** `wrapper(data: T) -> Any` - - - -### ***class*** `Channel(Generic[T])` - -通道类,可以在进程间和进程内通信,双向但同时只能有一个发送者和一个接收者 - -有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器 - -###   ***def*** `__init__(self, _id: str, type_check: bool) -> None` - - 初始化通道 - -Args: - - _id: 通道ID - -###   ***def*** `send(self, data: T) -> None` - - 发送数据 - -Args: - - data: 数据 - -###   ***def*** `receive(self) -> T` - - 接收数据 - -Args: - -###   ***def*** `close(self) -> None` - - 关闭通道 - -###   ***def*** `on_receive(self, filter_func: Optional[FILTER_FUNC]) -> Callable[[Callable[[T], Any]], Callable[[T], Any]]` - - 接收数据并执行函数 - -Args: - - filter_func: 过滤函数,为None则不过滤 - -Returns: - - 装饰器,装饰一个函数在接收到数据后执行 - -### ***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*** `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/en/dev/api/liteyuki/comm/event.md b/docs/en/dev/api/liteyuki/comm/event.md deleted file mode 100644 index a5dbf619..00000000 --- a/docs/en/dev/api/liteyuki/comm/event.md +++ /dev/null @@ -1,15 +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` - -  - diff --git a/docs/en/dev/api/liteyuki/comm/storage.md b/docs/en/dev/api/liteyuki/comm/storage.md deleted file mode 100644 index 9042d3a1..00000000 --- a/docs/en/dev/api/liteyuki/comm/storage.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: liteyuki.comm.storage -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `on_get(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***def*** `on_set(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***def*** `on_delete(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***def*** `on_get_all(data: tuple[str, dict[str, Any]]) -> None` - - - -### ***class*** `KeyValueStore` - - - -###   ***def*** `__init__(self) -> None` - -  - -###   ***def*** `set(self, key: str, value: Any) -> None` - - 设置键值对 - -Args: - - key: 键 - - value: 值 - -###   ***def*** `get(self, key: str, default: Optional[Any]) -> Optional[Any]` - - 获取键值对 - -Args: - - key: 键 - - default: 默认值 - - - -Returns: - - Any: 值 - -###   ***def*** `delete(self, key: str, ignore_key_error: bool) -> None` - - 删除键值对 - -Args: - - key: 键 - - ignore_key_error: 是否忽略键不存在的错误 - - - -Returns: - -###   ***def*** `get_all(self) -> dict[str, Any]` - - 获取所有键值对 - -Returns: - - dict[str, Any]: 键值对 - -### ***class*** `GlobalKeyValueStore` - - - -###   ***@classmethod*** -###   ***def*** `get_instance(cls: Any) -> None` - -  - -###   ***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')` - - - diff --git a/docs/en/dev/api/liteyuki/config.md b/docs/en/dev/api/liteyuki/config.md deleted file mode 100644 index 30066528..00000000 --- a/docs/en/dev/api/liteyuki/config.md +++ /dev/null @@ -1,99 +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: - - 扁平化后的配置文件,但也包含原有的键值对 - -### ***def*** `load_from_yaml(file: str) -> dict[str, Any]` - -Load config from yaml file - -### ***def*** `load_from_json(file: str) -> dict[str, Any]` - -Load config from json file - -### ***def*** `load_from_toml(file: str) -> dict[str, Any]` - -Load config from toml file - -### ***def*** `load_from_files() -> dict[str, Any]` - -从指定文件加载配置项,会自动识别文件格式 - -默认执行扁平化选项 - -### ***def*** `load_configs_from_dirs() -> dict[str, Any]` - -从目录下加载配置文件,不递归 - -按照读取文件的优先级反向覆盖 - -默认执行扁平化选项 - -### ***def*** `load_config_in_default(no_waring: bool) -> dict[str, Any]` - -从一个标准的轻雪项目加载配置文件 - -项目目录下的config.*和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/en/dev/api/liteyuki/core/README.md b/docs/en/dev/api/liteyuki/core/README.md deleted file mode 100644 index 26c3500b..00000000 --- a/docs/en/dev/api/liteyuki/core/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.core -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/en/dev/api/liteyuki/core/manager.md b/docs/en/dev/api/liteyuki/core/manager.md deleted file mode 100644 index ed8f2f18..00000000 --- a/docs/en/dev/api/liteyuki/core/manager.md +++ /dev/null @@ -1,111 +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]]) -> None` - -  - -### ***class*** `ProcessManager` - -进程管理器 - -###   ***def*** `__init__(self, lifespan: 'Lifespan') -> None` - -  - -###   ***def*** `start(self, name: str) -> None` - - 开启后自动监控进程,并添加到进程字典中 - -Args: - - name: - -Returns: - -###   ***def*** `start_all(self) -> None` - - 启动所有进程 - -###   ***def*** `add_target(self, name: str, target: TARGET_FUNC, args: tuple, kwargs: Any) -> None` - - 添加进程 - -Args: - - name: 进程名,用于获取和唯一标识 - - target: 进程函数 - - args: 进程函数参数 - - kwargs: 进程函数关键字参数,通常会默认传入chan_active和chan_passive - -###   ***def*** `join_all(self) -> None` - -  - -###   ***def*** `terminate(self, name: str) -> None` - - 终止进程并从进程字典中删除 - -Args: - - name: - - - -Returns: - -###   ***def*** `terminate_all(self) -> None` - -  - -###   ***def*** `is_process_alive(self, name: str) -> bool` - - 检查进程是否存活 - -Args: - - name: - - - -Returns: - -### ***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)` - - - -### ***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/en/dev/api/liteyuki/dev/README.md b/docs/en/dev/api/liteyuki/dev/README.md deleted file mode 100644 index 6d883442..00000000 --- a/docs/en/dev/api/liteyuki/dev/README.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.dev -index: true -icon: laptop-code -category: API ---- - diff --git a/docs/en/dev/api/liteyuki/dev/observer.md b/docs/en/dev/api/liteyuki/dev/observer.md deleted file mode 100644 index f0f5643b..00000000 --- a/docs/en/dev/api/liteyuki/dev/observer.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: liteyuki.dev.observer -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `debounce(wait: Any) -> None` - -防抖函数 - -### ***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: - - 装饰器,装饰一个函数在接收到数据后执行 - -### ***def*** `decorator(func: Any) -> None` - - - -### ***def*** `decorator(func: CALLBACK_FUNC) -> CALLBACK_FUNC` - - - -### ***def*** `wrapper() -> None` - - - -### ***def*** `wrapper(event: FileSystemEvent) -> None` - - - -### ***class*** `CodeModifiedHandler(FileSystemEventHandler)` - -Handler for code file changes - -###   ***def*** `on_modified(self, event: Any) -> None` - -  - -###   ***def*** `on_created(self, event: Any) -> None` - -  - -###   ***def*** `on_deleted(self, event: Any) -> None` - -  - -###   ***def*** `on_moved(self, event: Any) -> None` - -  - -###   ***def*** `on_any_event(self, event: Any) -> None` - -  - -### ***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/en/dev/api/liteyuki/dev/plugin.md b/docs/en/dev/api/liteyuki/dev/plugin.md deleted file mode 100644 index a93c922b..00000000 --- a/docs/en/dev/api/liteyuki/dev/plugin.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: liteyuki.dev.plugin -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `run_plugins() -> None` - -运行插件,无需手动初始化bot - -Args: - - module_path: 插件路径,参考`liteyuki.load_plugin`的函数签名 - -### ***var*** `cfg = load_config_in_default()` - - - -### ***var*** `plugins = cfg.get('liteyuki.plugins', [])` - - - -### ***var*** `bot = LiteyukiBot(**cfg)` - - - diff --git a/docs/en/dev/api/liteyuki/exception.md b/docs/en/dev/api/liteyuki/exception.md deleted file mode 100644 index 469c00d6..00000000 --- a/docs/en/dev/api/liteyuki/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/en/dev/api/liteyuki/log.md b/docs/en/dev/api/liteyuki/log.md deleted file mode 100644 index c698f3ba..00000000 --- a/docs/en/dev/api/liteyuki/log.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: liteyuki.log -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `get_format(level: str) -> str` - - - -### ***def*** `init_log(config: dict) -> None` - -在语言加载完成后执行 - -Returns: - -### ***var*** `show_icon = config.get('log_icon', True)` - - - diff --git a/docs/en/dev/api/liteyuki/mkdoc.md b/docs/en/dev/api/liteyuki/mkdoc.md deleted file mode 100644 index 765d9a81..00000000 --- a/docs/en/dev/api/liteyuki/mkdoc.md +++ /dev/null @@ -1,271 +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: 目标路径 - -### ***def*** `write_to_files(file_data: dict[str, str]) -> None` - -输出文件 - -Args: - - file_data: 文件数据 相对路径 - -### ***def*** `get_file_list(module_folder: str) -> None` - - - -### ***def*** `get_module_info_normal(file_path: str, ignore_private: bool) -> ModuleInfo` - -获取函数和类 - -Args: - - file_path: Python 文件路径 - - ignore_private: 忽略私有函数和类 - -Returns: - - 模块信息 - -### ***def*** `generate_markdown(module_info: ModuleInfo, front_matter: Any) -> str` - -生成模块的Markdown - -你可在此自定义生成的Markdown格式 - -Args: - - module_info: 模块信息 - - front_matter: 自定义选项title, index, icon, category - -Returns: - - Markdown 字符串 - -### ***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: 忽略的路径 - -### ***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))` - - - -### ***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/en/dev/api/liteyuki/plugin/README.md b/docs/en/dev/api/liteyuki/plugin/README.md deleted file mode 100644 index 2e7020f9..00000000 --- a/docs/en/dev/api/liteyuki/plugin/README.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: liteyuki.plugin -index: true -icon: laptop-code -category: API ---- - -### ***def*** `get_loaded_plugins() -> dict[str, Plugin]` - -获取已加载的插件 - -Returns: - - dict[str, Plugin]: 插件字典 - diff --git a/docs/en/dev/api/liteyuki/plugin/load.md b/docs/en/dev/api/liteyuki/plugin/load.md deleted file mode 100644 index 9669642e..00000000 --- a/docs/en/dev/api/liteyuki/plugin/load.md +++ /dev/null @@ -1,103 +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)` - -### ***def*** `load_plugins() -> set[Plugin]` - -导入文件夹下多个插件 - - - -参数: - - plugin_dir: 文件夹路径 - - ignore_warning: 是否忽略警告,通常是目录不存在或目录为空 - -### ***def*** `format_display_name(display_name: str, plugin_type: PluginType) -> str` - -设置插件名称颜色,根据不同类型插件设置颜色 - -Args: - - display_name: 插件名称 - - plugin_type: 插件类型 - - - -Returns: - - str: 设置后的插件名称 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/en/dev/api/liteyuki/plugin/manager.md b/docs/en/dev/api/liteyuki/plugin/manager.md deleted file mode 100644 index 7d4d951c..00000000 --- a/docs/en/dev/api/liteyuki/plugin/manager.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: liteyuki.plugin.manager -order: 1 -icon: laptop-code -category: API ---- - diff --git a/docs/en/dev/api/liteyuki/plugin/model.md b/docs/en/dev/api/liteyuki/plugin/model.md deleted file mode 100644 index fd8fec0c..00000000 --- a/docs/en/dev/api/liteyuki/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*** `IMPLEMENTATION: 'implementation'` - -###   ***attr*** `MODULE: 'module'` - -###   ***attr*** `UNCLASSIFIED: 'unclassified'` - -### ***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*** `IMPLEMENTATION = 'implementation'` - - - -### ***var*** `MODULE = 'module'` - - - -### ***var*** `UNCLASSIFIED = 'unclassified'` - - - -### ***var*** `model_config = {'arbitrary_types_allowed': True}` - - - diff --git a/docs/en/dev/api/liteyuki/utils.md b/docs/en/dev/api/liteyuki/utils.md deleted file mode 100644 index b215a53d..00000000 --- a/docs/en/dev/api/liteyuki/utils.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: liteyuki.utils -order: 1 -icon: laptop-code -category: API ---- - -### ***def*** `is_coroutine_callable(call: Callable[..., Any]) -> bool` - -判断是否为协程可调用对象 - -Args: - - call: 可调用对象 - -Returns: - - bool: 是否为协程可调用对象 - -### ***def*** `run_coroutine() -> None` - -运行协程 - -Args: - - coro: - - - -Returns: - -### ***def*** `path_to_module_name(path: Path) -> str` - -转换路径为模块名 - -Args: - - path: 路径a/b/c/d -> a.b.c.d - -Returns: - - str: 模块名 - -### ***def*** `async_wrapper(func: Callable[..., Any]) -> Callable[..., Coroutine]` - -异步包装器 - -Args: - - func: Sync Callable - -Returns: - - Coroutine: Asynchronous Callable - -### ***async def*** `wrapper() -> None` - - - -### ***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/liteyuki/comm/__init__.py b/liteyuki/comm/__init__.py index 6dac6959..35826ef4 100644 --- a/liteyuki/comm/__init__.py +++ b/liteyuki/comm/__init__.py @@ -3,8 +3,8 @@ 该模块用于轻雪主进程和Nonebot子进程之间的通信 依赖关系 event -> _ -storage -> channel -rpc -> channel, storage +storage -> channel_ +rpc -> channel_, storage """ from liteyuki.comm.channel import ( Channel, diff --git a/liteyuki/comm/channel.py b/liteyuki/comm/channel.py index b4d35862..17bec928 100644 --- a/liteyuki/comm/channel.py +++ b/liteyuki/comm/channel.py @@ -5,7 +5,7 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved @Time : 2024/7/26 下午11:21 @Author : snowykami @Email : snowykami@outlook.com -@File : channel.py +@File : channel_.py @Software: PyCharm 本模块定义了一个通用的通道类,用于进程间通信 @@ -38,11 +38,12 @@ class Channel(Generic[T]): 有两种接收工作方式,但是只能选择一种,主动接收和被动接收,主动接收使用 `receive` 方法,被动接收使用 `on_receive` 装饰器 """ - def __init__(self, _id: str, type_check: bool = False): + 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 @@ -53,9 +54,13 @@ class Channel(Generic[T]): self.is_main_receive_loop_running = False self.is_sub_receive_loop_running = False - if type_check: + 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.") + raise TypeError("Type hint is required for enforcing type_ check.") self.type_check = type_check def _get_generic_type(self) -> Optional[type]: @@ -110,7 +115,7 @@ class Channel(Generic[T]): raise TypeError(f"Data must be an instance of {_type}, {type(data)} found") if self._closed: - raise RuntimeError("Cannot send to a closed channel") + raise RuntimeError("Cannot send to a closed channel_") self.conn_send.send(data) def receive(self) -> T: @@ -119,7 +124,7 @@ class Channel(Generic[T]): Args: """ if self._closed: - raise RuntimeError("Cannot receive from a closed channel") + raise RuntimeError("Cannot receive from a closed channel_") while True: data = self.conn_recv.recv() @@ -153,7 +158,7 @@ class Channel(Generic[T]): async def wrapper(data: T) -> Any: if filter_func is not None: if is_coroutine_callable(filter_func): - if not (await filter_func(data)): # type: ignore + if not (await filter_func(data)): # type_: ignore return else: if not filter_func(data): @@ -226,10 +231,12 @@ class Channel(Generic[T]): """子进程可用的主动和被动通道""" active_channel: Optional["Channel"] = None passive_channel: Optional["Channel"] = None +publish_channel: Channel[tuple[str, dict[str, Any]]] = Channel(_id="publish_channel") """通道传递通道,主进程创建单例,子进程初始化时实例化""" channel_deliver_active_channel: Channel[Channel[Any]] channel_deliver_passive_channel: Channel[tuple[str, dict[str, Any]]] + if IS_MAIN_PROCESS: channel_deliver_active_channel = Channel(_id="channel_deliver_active_channel") channel_deliver_passive_channel = Channel(_id="channel_deliver_passive_channel") @@ -237,7 +244,7 @@ if IS_MAIN_PROCESS: @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"] + name, channel = data[1]["name"], data[1]["channel_"] set_channel(name, channel) @@ -261,7 +268,7 @@ def set_channel(name: str, channel: Channel): channel: 通道实例 """ if not isinstance(channel, Channel): - raise TypeError(f"channel must be an instance of Channel, {type(channel)} found") + raise TypeError(f"channel_ must be an instance of Channel, {type(channel)} found") if IS_MAIN_PROCESS: _channel[name] = channel @@ -271,7 +278,7 @@ def set_channel(name: str, channel: Channel): ( "set_channel", { "name" : name, - "channel": channel, + "channel_": channel, } ) ) diff --git a/liteyuki/comm/storage.py b/liteyuki/comm/storage.py index b94ec9d4..7fb4f81f 100644 --- a/liteyuki/comm/storage.py +++ b/liteyuki/comm/storage.py @@ -4,14 +4,20 @@ """ import threading -from typing import Any, Optional +from typing import Any, Coroutine, Optional, TypeAlias, Callable -from liteyuki.comm.channel import Channel -from liteyuki.utils import IS_MAIN_PROCESS +from liteyuki.comm import channel +from liteyuki.comm.channel import Channel, ON_RECEIVE_FUNC, ASYNC_ON_RECEIVE_FUNC +from liteyuki.utils import IS_MAIN_PROCESS, is_coroutine_callable, run_coroutine if IS_MAIN_PROCESS: _locks = {} +_on_main_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type_: ignore +"""主进程订阅者接收函数""" +_on_sub_subscriber_receive_funcs: dict[str, list[ASYNC_ON_RECEIVE_FUNC]] = {} # type_: ignore +"""子进程订阅者接收函数""" + def _get_lock(key) -> threading.Lock: """ @@ -25,12 +31,28 @@ def _get_lock(key) -> threading.Lock: raise RuntimeError("Cannot get lock in sub process.") +class Subscriber: + def __init__(self): + self._subscribers = {} + + def receive(self) -> Any: + pass + + def unsubscribe(self) -> None: + pass + + class KeyValueStore: 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: """ 设置键值对 @@ -134,6 +156,94 @@ class KeyValueStore: ) return recv_chan.receive() + 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: + 装饰器 + """ + 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): + """ + 运行订阅者接收函数 + 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_]]) + else: + if 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 _start_receive_loop(self): + """ + 启动发布订阅接收器循环,在主进程中运行,若有子进程订阅则推送给子进程 + """ + if IS_MAIN_PROCESS: + while True: + data = self.active_chan.receive() + if data[0] == "publish": + # 运行主进程订阅函数 + self.run_subscriber_receive_funcs(data[1]["channel_"], data[1]["data"]) + # 推送给子进程 + self.publish_channel.send(data) + else: + while True: + data = self.publish_channel.receive() + if data[0] == "publish": + # 运行子进程订阅函数 + self.run_subscriber_receive_funcs(data[1]["channel_"], data[1]["data"]) + class GlobalKeyValueStore: _instance = None @@ -141,20 +251,17 @@ class GlobalKeyValueStore: @classmethod def get_instance(cls): - if IS_MAIN_PROCESS: - if cls._instance is None: - with cls._lock: - if cls._instance is None: - cls._instance = KeyValueStore() - return cls._instance - else: - raise RuntimeError("Cannot get instance in sub process.") + if cls._instance is None: + with cls._lock: + if cls._instance is None: + cls._instance = KeyValueStore() + return cls._instance +shared_memory: KeyValueStore = GlobalKeyValueStore.get_instance() + # 全局单例访问点 if IS_MAIN_PROCESS: - shared_memory: KeyValueStore = GlobalKeyValueStore.get_instance() - @shared_memory.passive_chan.on_receive(lambda d: d[0] == "get") def on_get(data: tuple[str, dict[str, Any]]): @@ -182,9 +289,13 @@ if IS_MAIN_PROCESS: recv_chan = data[1]["recv_chan"] recv_chan.send(shared_memory.get_all()) + else: # 子进程在入口函数中对shared_memory进行初始化 - shared_memory: Optional[KeyValueStore] = None # type: ignore + @channel.publish_channel.on_receive() + def on_publish(data: tuple[str, Any]): + channel_, data = data + shared_memory.run_subscriber_receive_funcs(channel_, data) _ref_count = 0 # import 引用计数, 防止获取空指针 if not IS_MAIN_PROCESS: diff --git a/liteyuki/core/manager.py b/liteyuki/core/manager.py index d5b6f8f3..8c390582 100644 --- a/liteyuki/core/manager.py +++ b/liteyuki/core/manager.py @@ -13,7 +13,7 @@ import threading from multiprocessing import Process from typing import Any, Callable, TYPE_CHECKING, TypeAlias -from liteyuki.comm.channel import Channel, get_channel, set_channels +from liteyuki.comm.channel import Channel, get_channel, set_channels, publish_channel from liteyuki.comm.storage import shared_memory from liteyuki.log import logger from liteyuki.utils import IS_MAIN_PROCESS @@ -42,12 +42,14 @@ class ChannelDeliver: active: Channel[Any], passive: Channel[Any], channel_deliver_active: Channel[Channel[Any]], - channel_deliver_passive: Channel[tuple[str, dict]] + 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 # 函数处理一些跨进程通道的 @@ -64,6 +66,7 @@ def _delivery_channel_wrapper(func: TARGET_FUNC, cd: ChannelDeliver, sm: "KeyVal channel.passive_channel = cd.passive # 子进程被动通道 channel.channel_deliver_active_channel = cd.channel_deliver_active # 子进程通道传递主动通道 channel.channel_deliver_passive_channel = cd.channel_deliver_passive # 子进程通道传递被动通道 + channel.publish_channel = cd.publish # 子进程发布通道 # 给子进程创建共享内存实例 from liteyuki.comm import storage @@ -148,7 +151,8 @@ class ProcessManager: active=chan_active, passive=chan_passive, channel_deliver_active=channel_deliver_active_channel, - channel_deliver_passive=channel_deliver_passive_channel + channel_deliver_passive=channel_deliver_passive_channel, + publish=publish_channel ) self.targets[name] = (_delivery_channel_wrapper, (target, channel_deliver, shared_memory, *args), kwargs) diff --git a/liteyuki/log.py b/liteyuki/log.py index 2ccd7d0f..e6ca5230 100644 --- a/liteyuki/log.py +++ b/liteyuki/log.py @@ -10,7 +10,9 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved """ import sys -from loguru import logger +import loguru + +logger = loguru.logger # DEBUG日志格式 debug_format: str = ( diff --git a/liteyuki/message/__init__.py b/liteyuki/message/__init__.py new file mode 100644 index 00000000..8c566fd8 --- /dev/null +++ b/liteyuki/message/__init__.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:44 +@Author : snowykami +@Email : snowykami@outlook.com +@File : __init__.py.py +@Software: PyCharm +""" diff --git a/liteyuki/message/event.py b/liteyuki/message/event.py new file mode 100644 index 00000000..ae248f6c --- /dev/null +++ b/liteyuki/message/event.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:47 +@Author : snowykami +@Email : snowykami@outlook.com +@File : event.py +@Software: PyCharm +""" + + +class Event: + def __init__(self, type_: str, data: dict): + self.type = type_ + self.data = data diff --git a/liteyuki/message/matcher.py b/liteyuki/message/matcher.py new file mode 100644 index 00000000..59fa9ad9 --- /dev/null +++ b/liteyuki/message/matcher.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:51 +@Author : snowykami +@Email : snowykami@outlook.com +@File : matcher.py +@Software: PyCharm +""" + + +class Matcher: + pass diff --git a/liteyuki/message/on.py b/liteyuki/message/on.py new file mode 100644 index 00000000..a5bba8c5 --- /dev/null +++ b/liteyuki/message/on.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:52 +@Author : snowykami +@Email : snowykami@outlook.com +@File : on.py +@Software: PyCharm +""" + +from liteyuki.message.matcher import Matcher + +_matcher_list: list[Matcher] = [] + + +def on_message(permission) -> Matcher: + pass diff --git a/liteyuki/message/permission.py b/liteyuki/message/permission.py new file mode 100644 index 00000000..85c92455 --- /dev/null +++ b/liteyuki/message/permission.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:55 +@Author : snowykami +@Email : snowykami@outlook.com +@File : permission.py +@Software: PyCharm +""" + +from typing import Callable, Coroutine, TypeAlias + +PERMISSION_HANDLER: TypeAlias = Callable[[str], bool | Coroutine[None, None, bool]] + + +class Permission: + def __init__(self, handler: PERMISSION_HANDLER): + self.handler = handler + diff --git a/liteyuki/message/rule.py b/liteyuki/message/rule.py new file mode 100644 index 00000000..09eab1ec --- /dev/null +++ b/liteyuki/message/rule.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:55 +@Author : snowykami +@Email : snowykami@outlook.com +@File : rule.py +@Software: PyCharm +""" \ No newline at end of file diff --git a/liteyuki/message/session.py b/liteyuki/message/session.py new file mode 100644 index 00000000..e125511d --- /dev/null +++ b/liteyuki/message/session.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:47 +@Author : snowykami +@Email : snowykami@outlook.com +@File : session.py +@Software: PyCharm +""" \ No newline at end of file diff --git a/liteyuki/mkdoc.py b/liteyuki/mkdoc.py index 49060c56..b62ba5b6 100644 --- a/liteyuki/mkdoc.py +++ b/liteyuki/mkdoc.py @@ -340,5 +340,4 @@ def generate_docs(module_folder: str, output_dir: str, with_top: bool = False, i if __name__ == '__main__': # 这里填入你的模块路径 generate_docs('liteyuki', 'docs/dev/api', with_top=False, ignored_paths=["liteyuki/plugins"]) - generate_docs('liteyuki', 'docs/en/dev/api', with_top=True, ignored_paths=["liteyuki/plugins"]) - # generate_docs('melobot', 'melodoc', with_top=False) + generate_docs('liteyuki', 'docs/en/dev/api', with_top=False, ignored_paths=["liteyuki/plugins"]) diff --git a/liteyuki/plugin/model.py b/liteyuki/plugin/model.py index 4409ba2c..644a3d1f 100644 --- a/liteyuki/plugin/model.py +++ b/liteyuki/plugin/model.py @@ -47,7 +47,7 @@ class PluginMetadata(BaseModel): 插件描述 usage: str 插件使用方法 - type: str + type_: str 插件类型 author: str 插件作者 diff --git a/src/liteyuki_main/core.py b/src/liteyuki_main/core.py index d34cb9dd..78515fbc 100644 --- a/src/liteyuki_main/core.py +++ b/src/liteyuki_main/core.py @@ -299,7 +299,7 @@ async def test_for_md_image(bot: T_Bot, api: str, data: dict): session_id = data.get("group_id") else: return - if len(data.get("message", [])) == 1 and data["message"][0].get("type") == "image": + if len(data.get("message", [])) == 1 and data["message"][0].get("type_") == "image": file: str = data["message"][0].data.get("file") # file:// http:// base64:// if file.startswith("http"): diff --git a/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py b/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py index 2aae4137..58e1d11e 100644 --- a/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py +++ b/src/liteyuki_plugins/liteyukibot_plugin_nonebot/__init__.py @@ -12,7 +12,7 @@ Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved import nonebot from liteyuki.utils import IS_MAIN_PROCESS from liteyuki.plugin import PluginMetadata, PluginType -from .nb_utils import adapter_manager, driver_manager # type: ignore +from .nb_utils import adapter_manager, driver_manager # type_: ignore __plugin_meta__ = PluginMetadata( name="NoneBot2启动器", diff --git a/src/liteyuki_plugins/ts_sm_ly.py b/src/liteyuki_plugins/ts_sm_ly.py new file mode 100644 index 00000000..5e68b499 --- /dev/null +++ b/src/liteyuki_plugins/ts_sm_ly.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午8:57 +@Author : snowykami +@Email : snowykami@outlook.com +@File : ts_sm.py +@Software: PyCharm +""" +import asyncio + +from liteyuki.comm.storage import shared_memory +from liteyuki import get_bot + + +@shared_memory.on_subscriber_receive("to_liteyuki") +async def _(data): + print("主进程接收数据async:", data) diff --git a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py index c2437128..cb2365d4 100644 --- a/src/nonebot_plugins/liteyuki_crt_utils/__init__.py +++ b/src/nonebot_plugins/liteyuki_crt_utils/__init__.py @@ -1,8 +1,8 @@ from nonebot.plugin import PluginMetadata from liteyuki import get_bot -from .crt_matchers import * # type: ignore -from .rt_guide import * # type: ignore +from .crt_matchers import * # type_: ignore +from .rt_guide import * # type_: ignore __plugin_meta__ = PluginMetadata( diff --git a/src/nonebot_plugins/liteyuki_pacman/npm.py b/src/nonebot_plugins/liteyuki_pacman/npm.py index 24e0dfad..0803a292 100644 --- a/src/nonebot_plugins/liteyuki_pacman/npm.py +++ b/src/nonebot_plugins/liteyuki_pacman/npm.py @@ -339,7 +339,7 @@ async def _(result: Arparma, event: T_MessageEvent, bot: T_Bot, npm: Matcher): await md.send_md(f"{info}\n\n" f"```\n{log}\n```", bot, event=event) elif sc.get("uninstall") and perm_s: - plugin_name: str = result.subcommands["uninstall"].args.get("plugin_name") # type: ignore + plugin_name: str = result.subcommands["uninstall"].args.get("plugin_name") # type_: ignore found_installed_plugin: InstalledPlugin = plugin_db.where_one( InstalledPlugin(), "module_name = ?", plugin_name ) diff --git a/src/nonebot_plugins/nonebot-plugin-toliteyuki/__init__.py b/src/nonebot_plugins/nonebot-plugin-toliteyuki/__init__.py new file mode 100644 index 00000000..314b13ed --- /dev/null +++ b/src/nonebot_plugins/nonebot-plugin-toliteyuki/__init__.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +""" +Copyright (C) 2020-2024 LiteyukiStudio. All Rights Reserved + +@Time : 2024/8/19 下午10:30 +@Author : snowykami +@Email : snowykami@outlook.com +@File : __init__.py.py +@Software: PyCharm +""" +from nonebot import require + +from liteyuki.comm.storage import shared_memory + +require("nonebot_plugin_alconna") + +from nonebot_plugin_alconna import UniMessage, Command, on_alconna + diff --git a/src/resources/liteyuki_crt/templates/js/crt_route.js b/src/resources/liteyuki_crt/templates/js/crt_route.js index aafb060a..392ff689 100644 --- a/src/resources/liteyuki_crt/templates/js/crt_route.js +++ b/src/resources/liteyuki_crt/templates/js/crt_route.js @@ -2,7 +2,7 @@ /** - * @type {{ + * @type_ {{ * results: Array<{ * abstracts: string, * createdDt: string, diff --git a/src/resources/liteyuki_weather/templates/js/weather_now.js b/src/resources/liteyuki_weather/templates/js/weather_now.js index c2cb7973..71129324 100644 --- a/src/resources/liteyuki_weather/templates/js/weather_now.js +++ b/src/resources/liteyuki_weather/templates/js/weather_now.js @@ -12,7 +12,7 @@ * @property {Weather} weather - The weather data. */ -/** @type {Data} */ +/** @type_ {Data} */ let data = JSON.parse(document.getElementById("data").innerText) diff --git a/src/utils/base/data.py b/src/utils/base/data.py index 07ad7bbb..7c632b7c 100644 --- a/src/utils/base/data.py +++ b/src/utils/base/data.py @@ -110,7 +110,7 @@ class Database: *args: Returns: """ - table_list = [item[0] for item in self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()] + table_list = [item[0] for item in self.cursor.execute("SELECT name FROM sqlite_master WHERE type_='table'").fetchall()] for model in args: logger.debug(f"Upserting {model}") if not model.TABLE_NAME: diff --git a/src/utils/message/message.py b/src/utils/message/message.py index 6fbefa66..270f8531 100644 --- a/src/utils/message/message.py +++ b/src/utils/message/message.py @@ -83,14 +83,14 @@ class MarkdownMessage: "send_private_forward_msg", messages=[ { - "type": "node", + "type_": "node", "data": { "content": [ { "data": { "content": "{\"content\":\"%s\"}" % formatted_md, }, - "type": "markdown" + "type_": "markdown" } ], "name": "[]", @@ -107,7 +107,7 @@ class MarkdownMessage: message_type=message_type, message=[ { - "type": "longmsg", + "type_": "longmsg", "data": { "id": forward_id } @@ -156,7 +156,7 @@ class MarkdownMessage: Args: image: 图片字节流或图片本地路径,链接请使用Markdown.image_async方法获取后通过send_md发送 bot: bot instance - message_type: message type + message_type: message type_ session_id: session id event: event kwargs: other arguments