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.
Files changed (123) hide show
  1. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/PKG-INFO +1 -1
  2. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/pyproject.toml +1 -1
  3. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/init.py +118 -12
  4. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/wrapper.py +212 -3
  5. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/wrapper.pyi +113 -2
  6. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/__init__.py +12 -3
  7. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/__init__.pyi +4 -3
  8. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/adapter.py +5 -1
  9. erispulse-2.4.5.dev3/src/ErisPulse/Core/config.py +630 -0
  10. erispulse-2.4.5.dev3/src/ErisPulse/Core/config.pyi +175 -0
  11. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/logger.py +7 -10
  12. erispulse-2.4.5.dev3/src/ErisPulse/Core/metrics.py +489 -0
  13. erispulse-2.4.5.dev3/src/ErisPulse/Core/metrics.pyi +302 -0
  14. erispulse-2.4.5.dev3/src/ErisPulse/Core/router.py +1234 -0
  15. erispulse-2.4.5.dev3/src/ErisPulse/Core/router.pyi +451 -0
  16. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__init__.py +1 -0
  17. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__init__.pyi +1 -1
  18. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/module.py +109 -8
  19. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/module.pyi +1 -3
  20. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/frame_config.py +5 -3
  21. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/sdk.py +6 -4
  22. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/sdk.pyi +3 -0
  23. erispulse-2.4.5.dev2/src/ErisPulse/Core/config.py +0 -333
  24. erispulse-2.4.5.dev2/src/ErisPulse/Core/config.pyi +0 -80
  25. erispulse-2.4.5.dev2/src/ErisPulse/Core/router.py +0 -571
  26. erispulse-2.4.5.dev2/src/ErisPulse/Core/router.pyi +0 -174
  27. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/.gitignore +0 -0
  28. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/LICENSE +0 -0
  29. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/README.md +0 -0
  30. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/__init__.py +0 -0
  31. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/__init__.pyi +0 -0
  32. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/base.py +0 -0
  33. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/base.pyi +0 -0
  34. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/cli.py +0 -0
  35. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/cli.pyi +0 -0
  36. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/__init__.py +0 -0
  37. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/__init__.pyi +0 -0
  38. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/init.pyi +0 -0
  39. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/install.py +0 -0
  40. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/install.pyi +0 -0
  41. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list.py +0 -0
  42. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list.pyi +0 -0
  43. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
  44. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/list_remote.pyi +0 -0
  45. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/run.py +0 -0
  46. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/run.pyi +0 -0
  47. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/self_update.py +0 -0
  48. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/self_update.pyi +0 -0
  49. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
  50. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/uninstall.pyi +0 -0
  51. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
  52. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/commands/upgrade.pyi +0 -0
  53. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/console.py +0 -0
  54. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/console.pyi +0 -0
  55. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/registry.py +0 -0
  56. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/registry.pyi +0 -0
  57. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/__init__.py +0 -0
  58. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/__init__.pyi +0 -0
  59. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
  60. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/CLI/utils/package_manager.pyi +0 -0
  61. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/__init__.py +0 -0
  62. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/__init__.pyi +0 -0
  63. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/adapter.py +0 -0
  64. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/adapter.pyi +0 -0
  65. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/manager.py +0 -0
  66. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/manager.pyi +0 -0
  67. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/module.py +0 -0
  68. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/module.pyi +0 -0
  69. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/storage.py +0 -0
  70. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Bases/storage.pyi +0 -0
  71. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/__init__.py +0 -0
  72. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/__init__.pyi +0 -0
  73. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/base.py +0 -0
  74. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/base.pyi +0 -0
  75. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/command.py +0 -0
  76. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/command.pyi +0 -0
  77. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message.py +0 -0
  78. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message.pyi +0 -0
  79. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message_builder.py +0 -0
  80. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/message_builder.pyi +0 -0
  81. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/meta.py +0 -0
  82. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/meta.pyi +0 -0
  83. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/notice.py +0 -0
  84. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/notice.pyi +0 -0
  85. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/request.py +0 -0
  86. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/request.pyi +0 -0
  87. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/session_type.py +0 -0
  88. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/Event/session_type.pyi +0 -0
  89. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/adapter.pyi +0 -0
  90. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/lifecycle.py +0 -0
  91. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/lifecycle.pyi +0 -0
  92. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/logger.pyi +0 -0
  93. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/module.py +0 -0
  94. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/module.pyi +0 -0
  95. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/storage.py +0 -0
  96. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/Core/storage.pyi +0 -0
  97. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__main__.py +0 -0
  98. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/__main__.pyi +0 -0
  99. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/__init__.py +0 -0
  100. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/__init__.pyi +0 -0
  101. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/adapter.py +0 -0
  102. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/adapter.pyi +0 -0
  103. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/__init__.py +0 -0
  104. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/__init__.pyi +0 -0
  105. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/finder.py +0 -0
  106. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/bases/finder.pyi +0 -0
  107. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/module.py +0 -0
  108. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/finders/module.pyi +0 -0
  109. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/__init__.py +0 -0
  110. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/__init__.pyi +0 -0
  111. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/adapter.py +0 -0
  112. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/adapter.pyi +0 -0
  113. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/__init__.py +0 -0
  114. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/__init__.pyi +0 -0
  115. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/loader.py +0 -0
  116. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/bases/loader.pyi +0 -0
  117. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/strategy.py +0 -0
  118. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/loaders/strategy.pyi +0 -0
  119. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/__init__.py +0 -0
  120. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/__init__.pyi +0 -0
  121. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/exceptions.py +0 -0
  122. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/exceptions.pyi +0 -0
  123. {erispulse-2.4.5.dev2 → erispulse-2.4.5.dev3}/src/ErisPulse/runtime/frame_config.pyi +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse
3
- Version: 2.4.5.dev2
3
+ Version: 2.4.5.dev3
4
4
  Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
5
5
  Author-email: ErisDev <erisdev@88.com>
6
6
  Maintainer-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ErisPulse"
7
- version = "2.4.5-dev.2"
7
+ version = "2.4.5-dev.3"
8
8
  description = "ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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\n")
92
- f.write("[ErisPulse]\n")
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 # 默认禁用,需要时启用\n')
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
- fields,
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
- processed_data = await middleware(processed_data)
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 = []