ErisPulse 2.4.5.dev2__tar.gz → 2.4.5.dev3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/PKG-INFO +1 -1
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/pyproject.toml +1 -1
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/init.py +118 -12
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/wrapper.py +212 -3
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/wrapper.pyi +113 -2
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/__init__.py +12 -3
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/__init__.pyi +4 -3
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/adapter.py +5 -1
- erispulse-2.4.5.dev3/src/ErisPulse/Core/config.py +630 -0
- erispulse-2.4.5.dev3/src/ErisPulse/Core/config.pyi +175 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/logger.py +7 -10
- erispulse-2.4.5.dev3/src/ErisPulse/Core/metrics.py +489 -0
- erispulse-2.4.5.dev3/src/ErisPulse/Core/metrics.pyi +302 -0
- erispulse-2.4.5.dev3/src/ErisPulse/Core/router.py +1234 -0
- erispulse-2.4.5.dev3/src/ErisPulse/Core/router.pyi +451 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__init__.py +1 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__init__.pyi +1 -1
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/module.py +109 -8
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/module.pyi +1 -3
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/frame_config.py +5 -3
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/sdk.py +6 -4
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/sdk.pyi +3 -0
- erispulse-2.4.5.dev2/src/ErisPulse/Core/config.py +0 -333
- erispulse-2.4.5.dev2/src/ErisPulse/Core/config.pyi +0 -80
- erispulse-2.4.5.dev2/src/ErisPulse/Core/router.py +0 -571
- erispulse-2.4.5.dev2/src/ErisPulse/Core/router.pyi +0 -174
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/.gitignore +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/LICENSE +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/README.md +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/base.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/base.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/cli.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/cli.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/init.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/install.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/install.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list_remote.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/run.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/run.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/self_update.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/self_update.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/uninstall.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/upgrade.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/console.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/console.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/registry.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/package_manager.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/adapter.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/adapter.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/manager.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/module.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/module.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/storage.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/base.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/base.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/command.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/command.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message_builder.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message_builder.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/meta.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/notice.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/request.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/session_type.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/session_type.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/adapter.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/lifecycle.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/lifecycle.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/logger.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/module.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/module.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/storage.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/storage.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__main__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/adapter.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/adapter.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/finder.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/module.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/module.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/adapter.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/adapter.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/loader.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/strategy.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/__init__.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/__init__.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/exceptions.pyi +0 -0
- {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/frame_config.pyi +0 -0
|
@@ -84,32 +84,32 @@ class InitCommand(Command):
|
|
|
84
84
|
dir_path.mkdir(exist_ok=True)
|
|
85
85
|
console.print(f"[green]创建目录: {dir_name}[/green]")
|
|
86
86
|
|
|
87
|
-
# 创建配置文件
|
|
87
|
+
# 创建配置文件 (最小必须配置)
|
|
88
88
|
config_file = project_path / "config" / "config.toml"
|
|
89
89
|
if not config_file.exists():
|
|
90
90
|
with open(config_file, "w", encoding="utf-8") as f:
|
|
91
|
-
f.write("# ErisPulse 配置文件\n
|
|
92
|
-
f.write("
|
|
93
|
-
f.write("# 全局配置\n\n")
|
|
91
|
+
f.write("# ErisPulse 配置文件\n")
|
|
92
|
+
f.write("# 完整配置示例请参考 config.full.example\n\n")
|
|
94
93
|
f.write("[ErisPulse.server]\n")
|
|
95
94
|
f.write('host = "0.0.0.0"\n')
|
|
96
95
|
f.write("port = 8000\n\n")
|
|
97
96
|
f.write("[ErisPulse.logger]\n")
|
|
98
97
|
f.write('level = "INFO"\n')
|
|
99
|
-
f.write("log_files = [\"logs/app.log\"]\n")
|
|
100
|
-
f.write("memory_limit = 1000\n\n")
|
|
101
98
|
|
|
102
|
-
# 添加适配器配置
|
|
103
99
|
if adapter_list:
|
|
104
|
-
f.write("[ErisPulse.adapters]\n")
|
|
105
|
-
f.write("# 适配器配置\n\n")
|
|
106
|
-
f.write("[ErisPulse.adapters.status]\n")
|
|
100
|
+
f.write("\n[ErisPulse.adapters.status]\n")
|
|
107
101
|
for adapter in adapter_list:
|
|
108
|
-
f.write(f'{adapter} = false
|
|
109
|
-
f.write("\n")
|
|
102
|
+
f.write(f'{adapter} = false\n')
|
|
110
103
|
|
|
111
104
|
console.print("[green]创建配置文件: config/config.toml[/green]")
|
|
112
105
|
|
|
106
|
+
# 创建完整配置示例文件
|
|
107
|
+
example_file = project_path / "config" / "config.full.example"
|
|
108
|
+
if not example_file.exists():
|
|
109
|
+
with open(example_file, "w", encoding="utf-8") as f:
|
|
110
|
+
f.write(self._get_full_example_config(adapter_list))
|
|
111
|
+
console.print("[green]创建配置示例: config/config.full.example[/green]")
|
|
112
|
+
|
|
113
113
|
# 创建主程序文件
|
|
114
114
|
main_file = project_path / "main.py"
|
|
115
115
|
if not main_file.exists():
|
|
@@ -139,6 +139,112 @@ class InitCommand(Command):
|
|
|
139
139
|
console.print(f"[red]初始化项目失败: {e}[/]")
|
|
140
140
|
return False
|
|
141
141
|
|
|
142
|
+
@staticmethod
|
|
143
|
+
def _get_full_example_config(adapter_list=None):
|
|
144
|
+
"""
|
|
145
|
+
生成完整配置示例文件内容
|
|
146
|
+
|
|
147
|
+
:param adapter_list: list 可用适配器列表
|
|
148
|
+
:return: str 完整配置示例内容
|
|
149
|
+
|
|
150
|
+
{!--< internal-use >!--}
|
|
151
|
+
{!--< /internal-use >!--}
|
|
152
|
+
"""
|
|
153
|
+
lines = [
|
|
154
|
+
"# ErisPulse 完整配置示例",
|
|
155
|
+
"# 此文件展示所有可用配置项及其默认值",
|
|
156
|
+
"# 如需使用,将所需配置复制到 config.toml 并按需修改",
|
|
157
|
+
"",
|
|
158
|
+
"# ==================== 服务器 ====================",
|
|
159
|
+
"",
|
|
160
|
+
"[ErisPulse.server]",
|
|
161
|
+
'host = "0.0.0.0" # 监听地址',
|
|
162
|
+
"port = 8000 # 监听端口",
|
|
163
|
+
"ssl_certfile = null # SSL 证书路径",
|
|
164
|
+
"ssl_keyfile = null # SSL 密钥路径",
|
|
165
|
+
"",
|
|
166
|
+
"# ==================== 日志 ====================",
|
|
167
|
+
"",
|
|
168
|
+
"[ErisPulse.logger]",
|
|
169
|
+
'level = "INFO" # 日志级别: DEBUG/INFO/WARNING/ERROR',
|
|
170
|
+
"log_files = [] # 日志文件列表, 如 [\"logs/app.log\"]",
|
|
171
|
+
"memory_limit = 1000 # 内存日志条数上限",
|
|
172
|
+
"",
|
|
173
|
+
"# ==================== 存储 ====================",
|
|
174
|
+
"",
|
|
175
|
+
"[ErisPulse.storage]",
|
|
176
|
+
"use_global_db = false # 是否使用全局数据库",
|
|
177
|
+
"",
|
|
178
|
+
"# ==================== 事件系统 ====================",
|
|
179
|
+
"",
|
|
180
|
+
"[ErisPulse.event.message]",
|
|
181
|
+
"ignore_self = true # 忽略自身消息",
|
|
182
|
+
"",
|
|
183
|
+
"[ErisPulse.event.command]",
|
|
184
|
+
'prefix = "/" # 命令前缀',
|
|
185
|
+
"case_sensitive = true # 区分大小写",
|
|
186
|
+
"allow_space_prefix = false # 允许前缀前有空格",
|
|
187
|
+
"must_at_bot = false # 必须艾特Bot才触发",
|
|
188
|
+
"",
|
|
189
|
+
"# ==================== 框架 ====================",
|
|
190
|
+
"",
|
|
191
|
+
"[ErisPulse.framework]",
|
|
192
|
+
"enable_lazy_loading = true # 启用模块懒加载",
|
|
193
|
+
"",
|
|
194
|
+
"# ==================== 配置审计 ====================",
|
|
195
|
+
"",
|
|
196
|
+
"[ErisPulse.config.audit]",
|
|
197
|
+
"enabled = false # 是否启用配置审计",
|
|
198
|
+
"max_entries = 1000 # 审计日志最大条数",
|
|
199
|
+
"",
|
|
200
|
+
"# ==================== 指标监控 ====================",
|
|
201
|
+
"",
|
|
202
|
+
"[ErisPulse.metrics]",
|
|
203
|
+
"enabled = false # 是否启用指标采集",
|
|
204
|
+
"",
|
|
205
|
+
"# ==================== 路由增强 ====================",
|
|
206
|
+
"",
|
|
207
|
+
"[ErisPulse.router.cors]",
|
|
208
|
+
"enabled = false",
|
|
209
|
+
'allow_origins = ["*"]',
|
|
210
|
+
'allow_methods = ["*"]',
|
|
211
|
+
'allow_headers = ["*"]',
|
|
212
|
+
"allow_credentials = false",
|
|
213
|
+
"max_age = 600",
|
|
214
|
+
"",
|
|
215
|
+
"[ErisPulse.router.security]",
|
|
216
|
+
"enabled = false",
|
|
217
|
+
"",
|
|
218
|
+
"[ErisPulse.router.security.headers]",
|
|
219
|
+
'X-Content-Type-Options = "nosniff"',
|
|
220
|
+
'X-Frame-Options = "DENY"',
|
|
221
|
+
"",
|
|
222
|
+
"# ==================== 适配器状态 ====================",
|
|
223
|
+
"",
|
|
224
|
+
"[ErisPulse.adapters.status]",
|
|
225
|
+
]
|
|
226
|
+
|
|
227
|
+
if adapter_list:
|
|
228
|
+
for adapter in adapter_list:
|
|
229
|
+
lines.append(f"# {adapter} = false")
|
|
230
|
+
else:
|
|
231
|
+
lines.extend([
|
|
232
|
+
"# yunhu = false",
|
|
233
|
+
"# telegram = false",
|
|
234
|
+
"# onebot11 = false",
|
|
235
|
+
])
|
|
236
|
+
|
|
237
|
+
lines.extend([
|
|
238
|
+
"",
|
|
239
|
+
"# ==================== 模块状态 ====================",
|
|
240
|
+
"",
|
|
241
|
+
"[ErisPulse.modules.status]",
|
|
242
|
+
"# MyModule = true",
|
|
243
|
+
"",
|
|
244
|
+
])
|
|
245
|
+
|
|
246
|
+
return "\n".join(lines)
|
|
247
|
+
|
|
142
248
|
async def _fetch_available_adapters(self):
|
|
143
249
|
"""
|
|
144
250
|
从云端获取可用适配器列表
|
|
@@ -1164,12 +1164,15 @@ class Conversation:
|
|
|
1164
1164
|
"""
|
|
1165
1165
|
多轮对话上下文
|
|
1166
1166
|
|
|
1167
|
-
|
|
1167
|
+
提供在同一会话中进行多轮交互的便捷方法,支持分支跳转、上下文持久化
|
|
1168
1168
|
|
|
1169
1169
|
{!--< tips >!--}
|
|
1170
1170
|
1. 通过 event.conversation() 方法创建
|
|
1171
1171
|
2. 超时后自动标记为非活跃状态
|
|
1172
1172
|
3. 支持链式调用 say() 方法
|
|
1173
|
+
4. 支持 branch() 定义分支和 goto() 跳转
|
|
1174
|
+
5. 支持 context 字典存储对话状态
|
|
1175
|
+
6. 支持 save()/resume() 持久化到 storage
|
|
1173
1176
|
{!--< /tips >!--}
|
|
1174
1177
|
"""
|
|
1175
1178
|
|
|
@@ -1183,6 +1186,10 @@ class Conversation:
|
|
|
1183
1186
|
self._event = event
|
|
1184
1187
|
self._timeout = timeout
|
|
1185
1188
|
self._alive = True
|
|
1189
|
+
self._branches: dict[str, Callable] = {}
|
|
1190
|
+
self._current_branch: str | None = None
|
|
1191
|
+
self._branch_task: asyncio.Task | None = None
|
|
1192
|
+
self.context: dict[str, Any] = {}
|
|
1186
1193
|
|
|
1187
1194
|
@property
|
|
1188
1195
|
def is_active(self) -> bool:
|
|
@@ -1259,18 +1266,33 @@ class Conversation:
|
|
|
1259
1266
|
"""
|
|
1260
1267
|
多步骤收集信息
|
|
1261
1268
|
|
|
1262
|
-
:param fields: list[dict] -
|
|
1269
|
+
:param fields: list[dict] - 字段列表,支持 condition 字段:
|
|
1270
|
+
- condition: callable - 接收已收集数据 dict, 返回 bool 决定是否收集此字段
|
|
1263
1271
|
:return: dict|None - 收集到的数据字典或 None
|
|
1264
1272
|
"""
|
|
1265
1273
|
if not self._alive:
|
|
1266
1274
|
return None
|
|
1275
|
+
|
|
1276
|
+
filtered_fields = []
|
|
1277
|
+
for f in fields:
|
|
1278
|
+
cond = f.get("condition")
|
|
1279
|
+
if cond is not None:
|
|
1280
|
+
try:
|
|
1281
|
+
if not cond(self.context):
|
|
1282
|
+
continue
|
|
1283
|
+
except Exception:
|
|
1284
|
+
continue
|
|
1285
|
+
filtered_fields.append(f)
|
|
1286
|
+
|
|
1267
1287
|
result = await self._event.collect(
|
|
1268
|
-
|
|
1288
|
+
filtered_fields,
|
|
1269
1289
|
timeout_per_field=kwargs.pop("timeout_per_field", self._timeout),
|
|
1270
1290
|
**kwargs,
|
|
1271
1291
|
)
|
|
1272
1292
|
if result is None:
|
|
1273
1293
|
self._alive = False
|
|
1294
|
+
else:
|
|
1295
|
+
self.context.update(result)
|
|
1274
1296
|
return result
|
|
1275
1297
|
|
|
1276
1298
|
def stop(self):
|
|
@@ -1278,6 +1300,193 @@ class Conversation:
|
|
|
1278
1300
|
结束对话
|
|
1279
1301
|
"""
|
|
1280
1302
|
self._alive = False
|
|
1303
|
+
if self._branch_task and not self._branch_task.done():
|
|
1304
|
+
self._branch_task.cancel()
|
|
1305
|
+
|
|
1306
|
+
# 分支系统
|
|
1307
|
+
|
|
1308
|
+
def branch(self, name: str):
|
|
1309
|
+
"""
|
|
1310
|
+
注册分支处理器
|
|
1311
|
+
|
|
1312
|
+
:param name: str 分支名称
|
|
1313
|
+
:return: Callable 装饰器
|
|
1314
|
+
|
|
1315
|
+
:example:
|
|
1316
|
+
>>> conv = event.conversation()
|
|
1317
|
+
>>>
|
|
1318
|
+
>>> @conv.branch("menu")
|
|
1319
|
+
... async def menu_branch(conv, event):
|
|
1320
|
+
... await conv.say("1.饮品 2.主食")
|
|
1321
|
+
... resp = await conv.wait()
|
|
1322
|
+
... if resp and resp.get_text() == "1":
|
|
1323
|
+
... conv.goto("drink")
|
|
1324
|
+
...
|
|
1325
|
+
>>> @conv.branch("drink")
|
|
1326
|
+
... async def drink_branch(conv, event):
|
|
1327
|
+
... await conv.say("请选择饮品")
|
|
1328
|
+
... resp = await conv.wait()
|
|
1329
|
+
... conv.context["drink"] = resp.get_text()
|
|
1330
|
+
... conv.goto("confirm")
|
|
1331
|
+
...
|
|
1332
|
+
>>> conv.start("menu")
|
|
1333
|
+
"""
|
|
1334
|
+
def decorator(func: Callable):
|
|
1335
|
+
self._branches[name] = func
|
|
1336
|
+
return func
|
|
1337
|
+
return decorator
|
|
1338
|
+
|
|
1339
|
+
def goto(self, branch_name: str, event: "Event" = None):
|
|
1340
|
+
"""
|
|
1341
|
+
跳转到指定分支
|
|
1342
|
+
|
|
1343
|
+
:param branch_name: str 目标分支名称
|
|
1344
|
+
:param event: Event 传递给分支的事件对象 (可选)
|
|
1345
|
+
|
|
1346
|
+
:raises ValueError: 当目标分支不存在时
|
|
1347
|
+
|
|
1348
|
+
:example:
|
|
1349
|
+
>>> conv.goto("drink")
|
|
1350
|
+
"""
|
|
1351
|
+
if branch_name not in self._branches:
|
|
1352
|
+
raise ValueError(f"分支 '{branch_name}' 未定义")
|
|
1353
|
+
self._current_branch = branch_name
|
|
1354
|
+
|
|
1355
|
+
evt = event or self._event
|
|
1356
|
+
|
|
1357
|
+
if self._branch_task and not self._branch_task.done():
|
|
1358
|
+
self._branch_task.cancel()
|
|
1359
|
+
|
|
1360
|
+
async def _run_branch():
|
|
1361
|
+
handler = self._branches[branch_name]
|
|
1362
|
+
try:
|
|
1363
|
+
if asyncio.iscoroutinefunction(handler):
|
|
1364
|
+
await handler(self, evt)
|
|
1365
|
+
else:
|
|
1366
|
+
handler(self, evt)
|
|
1367
|
+
except asyncio.CancelledError:
|
|
1368
|
+
pass
|
|
1369
|
+
except Exception as e:
|
|
1370
|
+
from ..logger import logger as _logger
|
|
1371
|
+
_logger.warning(f"分支 '{branch_name}' 执行异常: {e}")
|
|
1372
|
+
self._alive = False
|
|
1373
|
+
|
|
1374
|
+
try:
|
|
1375
|
+
loop = asyncio.get_running_loop()
|
|
1376
|
+
self._branch_task = loop.create_task(_run_branch())
|
|
1377
|
+
except RuntimeError:
|
|
1378
|
+
pass
|
|
1379
|
+
|
|
1380
|
+
def start(self, branch_name: str, event: "Event" = None):
|
|
1381
|
+
"""
|
|
1382
|
+
启动对话,从指定分支开始
|
|
1383
|
+
|
|
1384
|
+
:param branch_name: str 起始分支名称
|
|
1385
|
+
:param event: Event 初始事件对象 (可选)
|
|
1386
|
+
|
|
1387
|
+
:raises ValueError: 当起始分支不存在时
|
|
1388
|
+
|
|
1389
|
+
:example:
|
|
1390
|
+
>>> conv.start("menu")
|
|
1391
|
+
"""
|
|
1392
|
+
self._alive = True
|
|
1393
|
+
self.goto(branch_name, event)
|
|
1394
|
+
|
|
1395
|
+
def get_current_branch(self) -> str | None:
|
|
1396
|
+
"""
|
|
1397
|
+
获取当前分支名称
|
|
1398
|
+
|
|
1399
|
+
:return: str|None 当前分支名, 未在分支中时返回 None
|
|
1400
|
+
"""
|
|
1401
|
+
return self._current_branch
|
|
1402
|
+
|
|
1403
|
+
def has_branch(self, name: str) -> bool:
|
|
1404
|
+
"""
|
|
1405
|
+
检查分支是否存在
|
|
1406
|
+
|
|
1407
|
+
:param name: str 分支名称
|
|
1408
|
+
:return: bool 是否存在
|
|
1409
|
+
"""
|
|
1410
|
+
return name in self._branches
|
|
1411
|
+
|
|
1412
|
+
# ==================== 持久化 ====================
|
|
1413
|
+
|
|
1414
|
+
async def save(self):
|
|
1415
|
+
"""
|
|
1416
|
+
保存对话状态到 storage
|
|
1417
|
+
|
|
1418
|
+
:example:
|
|
1419
|
+
>>> await conv.save()
|
|
1420
|
+
|
|
1421
|
+
{!--< tips >!--}
|
|
1422
|
+
保存内容包括: 当前分支、上下文数据、活跃状态
|
|
1423
|
+
可用于重启后恢复对话
|
|
1424
|
+
{!--< /tips >!--}
|
|
1425
|
+
"""
|
|
1426
|
+
try:
|
|
1427
|
+
from ..storage import storage
|
|
1428
|
+
user_id = self._event.get_user_id()
|
|
1429
|
+
platform = self._event.get_platform()
|
|
1430
|
+
key = f"conversation:{platform}:{user_id}"
|
|
1431
|
+
storage.set(key, {
|
|
1432
|
+
"branch": self._current_branch,
|
|
1433
|
+
"context": self.context,
|
|
1434
|
+
"alive": self._alive,
|
|
1435
|
+
"timeout": self._timeout,
|
|
1436
|
+
})
|
|
1437
|
+
except Exception:
|
|
1438
|
+
pass
|
|
1439
|
+
|
|
1440
|
+
async def resume(self, event: "Event" = None) -> bool:
|
|
1441
|
+
"""
|
|
1442
|
+
从 storage 恢复对话状态
|
|
1443
|
+
|
|
1444
|
+
:param event: Event 新的事件对象 (可选, 不传则使用原事件)
|
|
1445
|
+
:return: bool 是否恢复成功
|
|
1446
|
+
|
|
1447
|
+
:example:
|
|
1448
|
+
>>> conv = event.conversation()
|
|
1449
|
+
>>> # ... 注册分支 ...
|
|
1450
|
+
>>> if await conv.resume():
|
|
1451
|
+
... conv.goto(conv.get_current_branch())
|
|
1452
|
+
|
|
1453
|
+
{!--< tips >!--}
|
|
1454
|
+
需要在 resume() 之前先注册好所有分支
|
|
1455
|
+
{!--< /tips >!--}
|
|
1456
|
+
"""
|
|
1457
|
+
try:
|
|
1458
|
+
from ..storage import storage
|
|
1459
|
+
evt = event or self._event
|
|
1460
|
+
user_id = evt.get_user_id()
|
|
1461
|
+
platform = evt.get_platform()
|
|
1462
|
+
key = f"conversation:{platform}:{user_id}"
|
|
1463
|
+
data = storage.get(key)
|
|
1464
|
+
if data and isinstance(data, dict):
|
|
1465
|
+
self.context = data.get("context", {})
|
|
1466
|
+
self._current_branch = data.get("branch")
|
|
1467
|
+
self._alive = data.get("alive", False)
|
|
1468
|
+
if event:
|
|
1469
|
+
self._event = event
|
|
1470
|
+
return True
|
|
1471
|
+
except Exception:
|
|
1472
|
+
pass
|
|
1473
|
+
return False
|
|
1474
|
+
|
|
1475
|
+
async def clear_saved(self):
|
|
1476
|
+
"""
|
|
1477
|
+
清除保存的对话状态
|
|
1478
|
+
|
|
1479
|
+
:example:
|
|
1480
|
+
>>> await conv.clear_saved()
|
|
1481
|
+
"""
|
|
1482
|
+
try:
|
|
1483
|
+
from ..storage import storage
|
|
1484
|
+
user_id = self._event.get_user_id()
|
|
1485
|
+
platform = self._event.get_platform()
|
|
1486
|
+
key = f"conversation:{platform}:{user_id}"
|
|
1487
|
+
storage.delete(key)
|
|
1488
|
+
except Exception:
|
|
1489
|
+
pass
|
|
1281
1490
|
|
|
1282
1491
|
|
|
1283
1492
|
__all__ = [
|
|
@@ -678,12 +678,15 @@ class Conversation:
|
|
|
678
678
|
"""
|
|
679
679
|
多轮对话上下文
|
|
680
680
|
|
|
681
|
-
|
|
681
|
+
提供在同一会话中进行多轮交互的便捷方法,支持分支跳转、上下文持久化
|
|
682
682
|
|
|
683
683
|
{!--< tips >!--}
|
|
684
684
|
1. 通过 event.conversation() 方法创建
|
|
685
685
|
2. 超时后自动标记为非活跃状态
|
|
686
686
|
3. 支持链式调用 say() 方法
|
|
687
|
+
4. 支持 branch() 定义分支和 goto() 跳转
|
|
688
|
+
5. 支持 context 字典存储对话状态
|
|
689
|
+
6. 支持 save()/resume() 持久化到 storage
|
|
687
690
|
{!--< /tips >!--}
|
|
688
691
|
"""
|
|
689
692
|
def __init__(self: None, event: Event, timeout: float = ...) -> ...:
|
|
@@ -739,7 +742,8 @@ class Conversation:
|
|
|
739
742
|
"""
|
|
740
743
|
多步骤收集信息
|
|
741
744
|
|
|
742
|
-
:param fields: list[dict] -
|
|
745
|
+
:param fields: list[dict] - 字段列表,支持 condition 字段:
|
|
746
|
+
- condition: callable - 接收已收集数据 dict, 返回 bool 决定是否收集此字段
|
|
743
747
|
:return: dict|None - 收集到的数据字典或 None
|
|
744
748
|
"""
|
|
745
749
|
...
|
|
@@ -748,3 +752,110 @@ class Conversation:
|
|
|
748
752
|
结束对话
|
|
749
753
|
"""
|
|
750
754
|
...
|
|
755
|
+
def branch(self: object, name: str) -> ...:
|
|
756
|
+
"""
|
|
757
|
+
注册分支处理器
|
|
758
|
+
|
|
759
|
+
:param name: str 分支名称
|
|
760
|
+
:return: Callable 装饰器
|
|
761
|
+
|
|
762
|
+
:example:
|
|
763
|
+
>>> conv = event.conversation()
|
|
764
|
+
>>>
|
|
765
|
+
>>> @conv.branch("menu")
|
|
766
|
+
... async def menu_branch(conv, event):
|
|
767
|
+
... await conv.say("1.饮品 2.主食")
|
|
768
|
+
... resp = await conv.wait()
|
|
769
|
+
... if resp and resp.get_text() == "1":
|
|
770
|
+
... conv.goto("drink")
|
|
771
|
+
...
|
|
772
|
+
>>> @conv.branch("drink")
|
|
773
|
+
... async def drink_branch(conv, event):
|
|
774
|
+
... await conv.say("请选择饮品")
|
|
775
|
+
... resp = await conv.wait()
|
|
776
|
+
... conv.context["drink"] = resp.get_text()
|
|
777
|
+
... conv.goto("confirm")
|
|
778
|
+
...
|
|
779
|
+
>>> conv.start("menu")
|
|
780
|
+
"""
|
|
781
|
+
...
|
|
782
|
+
def goto(self: object, branch_name: str, event: Event = ...) -> ...:
|
|
783
|
+
"""
|
|
784
|
+
跳转到指定分支
|
|
785
|
+
|
|
786
|
+
:param branch_name: str 目标分支名称
|
|
787
|
+
:param event: Event 传递给分支的事件对象 (可选)
|
|
788
|
+
|
|
789
|
+
:raises ValueError: 当目标分支不存在时
|
|
790
|
+
|
|
791
|
+
:example:
|
|
792
|
+
>>> conv.goto("drink")
|
|
793
|
+
"""
|
|
794
|
+
...
|
|
795
|
+
def start(self: object, branch_name: str, event: Event = ...) -> ...:
|
|
796
|
+
"""
|
|
797
|
+
启动对话,从指定分支开始
|
|
798
|
+
|
|
799
|
+
:param branch_name: str 起始分支名称
|
|
800
|
+
:param event: Event 初始事件对象 (可选)
|
|
801
|
+
|
|
802
|
+
:raises ValueError: 当起始分支不存在时
|
|
803
|
+
|
|
804
|
+
:example:
|
|
805
|
+
>>> conv.start("menu")
|
|
806
|
+
"""
|
|
807
|
+
...
|
|
808
|
+
def get_current_branch(self: object) -> str | None:
|
|
809
|
+
"""
|
|
810
|
+
获取当前分支名称
|
|
811
|
+
|
|
812
|
+
:return: str|None 当前分支名, 未在分支中时返回 None
|
|
813
|
+
"""
|
|
814
|
+
...
|
|
815
|
+
def has_branch(self: object, name: str) -> bool:
|
|
816
|
+
"""
|
|
817
|
+
检查分支是否存在
|
|
818
|
+
|
|
819
|
+
:param name: str 分支名称
|
|
820
|
+
:return: bool 是否存在
|
|
821
|
+
"""
|
|
822
|
+
...
|
|
823
|
+
async def save(self: object) -> ...:
|
|
824
|
+
"""
|
|
825
|
+
保存对话状态到 storage
|
|
826
|
+
|
|
827
|
+
:example:
|
|
828
|
+
>>> await conv.save()
|
|
829
|
+
|
|
830
|
+
{!--< tips >!--}
|
|
831
|
+
保存内容包括: 当前分支、上下文数据、活跃状态
|
|
832
|
+
可用于重启后恢复对话
|
|
833
|
+
{!--< /tips >!--}
|
|
834
|
+
"""
|
|
835
|
+
...
|
|
836
|
+
async def resume(self: object, event: Event = ...) -> bool:
|
|
837
|
+
"""
|
|
838
|
+
从 storage 恢复对话状态
|
|
839
|
+
|
|
840
|
+
:param event: Event 新的事件对象 (可选, 不传则使用原事件)
|
|
841
|
+
:return: bool 是否恢复成功
|
|
842
|
+
|
|
843
|
+
:example:
|
|
844
|
+
>>> conv = event.conversation()
|
|
845
|
+
>>> # ... 注册分支 ...
|
|
846
|
+
>>> if await conv.resume():
|
|
847
|
+
... conv.goto(conv.get_current_branch())
|
|
848
|
+
|
|
849
|
+
{!--< tips >!--}
|
|
850
|
+
需要在 resume() 之前先注册好所有分支
|
|
851
|
+
{!--< /tips >!--}
|
|
852
|
+
"""
|
|
853
|
+
...
|
|
854
|
+
async def clear_saved(self: object) -> ...:
|
|
855
|
+
"""
|
|
856
|
+
清除保存的对话状态
|
|
857
|
+
|
|
858
|
+
:example:
|
|
859
|
+
>>> await conv.clear_saved()
|
|
860
|
+
"""
|
|
861
|
+
...
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
ErisPulse 核心模块
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
提供了一系列用于构建和管理应用的核心组件,包括适配器、模块、存储、配置、路由、指标监控和生命周期管理等。
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from .lifecycle import lifecycle, LifecycleManager
|
|
@@ -10,8 +10,9 @@ from .Bases import BaseAdapter, BaseModule, SendDSL, BaseStorage, BaseQueryBuild
|
|
|
10
10
|
from .storage import storage, StorageManager
|
|
11
11
|
from .logger import logger, Logger, LoggerChild
|
|
12
12
|
from .module import module, ModuleManager
|
|
13
|
-
from .router import router, RouterManager
|
|
14
|
-
from .config import config, ConfigManager
|
|
13
|
+
from .router import router, RouterManager, RouteGroup
|
|
14
|
+
from .config import config, ConfigManager, AuditEntry
|
|
15
|
+
from .metrics import metrics, MetricsManager, Counter, Gauge, Histogram
|
|
15
16
|
from . import Event
|
|
16
17
|
from .Event.message_builder import MessageBuilder
|
|
17
18
|
|
|
@@ -37,13 +38,21 @@ __all__ = [
|
|
|
37
38
|
'config', # 配置模块单例
|
|
38
39
|
'env', # 配置管理器别名
|
|
39
40
|
'ConfigManager', # 配置管理器类
|
|
41
|
+
'AuditEntry', # 审计条目类
|
|
40
42
|
|
|
41
43
|
'router', # 路由模块单例
|
|
42
44
|
'RouterManager', # 路由管理器类
|
|
45
|
+
'RouteGroup', # 路由组类
|
|
43
46
|
|
|
44
47
|
'logger', # 日志模块单例
|
|
45
48
|
'Logger', # 日志类
|
|
46
49
|
'LoggerChild', # 日志子类
|
|
47
50
|
'lifecycle', # 生命周期模块单例
|
|
48
51
|
'LifecycleManager', # 生命周期管理器类
|
|
52
|
+
|
|
53
|
+
'metrics', # 指标模块单例
|
|
54
|
+
'MetricsManager', # 指标管理器类
|
|
55
|
+
'Counter', # 计数器类
|
|
56
|
+
'Gauge', # 仪表类
|
|
57
|
+
'Histogram', # 直方图类
|
|
49
58
|
]
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"""
|
|
8
8
|
ErisPulse 核心模块
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
提供了一系列用于构建和管理应用的核心组件,包括适配器、模块、存储、配置、路由、指标监控和生命周期管理等。
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
from .lifecycle import lifecycle, LifecycleManager
|
|
@@ -16,7 +16,8 @@ from .Bases import BaseAdapter, BaseModule, SendDSL, BaseStorage, BaseQueryBuild
|
|
|
16
16
|
from .storage import storage, StorageManager
|
|
17
17
|
from .logger import logger, Logger, LoggerChild
|
|
18
18
|
from .module import module, ModuleManager
|
|
19
|
-
from .router import router, RouterManager
|
|
20
|
-
from .config import config, ConfigManager
|
|
19
|
+
from .router import router, RouterManager, RouteGroup
|
|
20
|
+
from .config import config, ConfigManager, AuditEntry
|
|
21
|
+
from .metrics import metrics, MetricsManager, Counter, Gauge, Histogram
|
|
21
22
|
from . import Event
|
|
22
23
|
from .Event.message_builder import MessageBuilder
|
|
@@ -721,7 +721,11 @@ class AdapterManager(ManagerBase):
|
|
|
721
721
|
# 先执行OneBot12中间件
|
|
722
722
|
processed_data = data
|
|
723
723
|
for middleware in self._onebot_middlewares:
|
|
724
|
-
|
|
724
|
+
result = await middleware(processed_data)
|
|
725
|
+
if result is not None:
|
|
726
|
+
processed_data = result
|
|
727
|
+
else:
|
|
728
|
+
logger.warning(f"中间件 {middleware.__qualname__} 返回 None,已忽略并保留原数据")
|
|
725
729
|
|
|
726
730
|
# 分发到OneBot12事件处理器
|
|
727
731
|
handlers_to_call = []
|