ErisPulse 1.1.15__py3-none-any.whl → 1.1.16__py3-none-any.whl
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/__init__.py +189 -164
- ErisPulse/__main__.py +1 -1
- ErisPulse/adapter.py +0 -125
- ErisPulse/db.py +0 -28
- ErisPulse/logger.py +7 -241
- ErisPulse/mods.py +0 -77
- ErisPulse/raiserr.py +8 -104
- {erispulse-1.1.15.dist-info → erispulse-1.1.16.dist-info}/METADATA +78 -45
- erispulse-1.1.16.dist-info/RECORD +13 -0
- erispulse-1.1.15.dist-info/RECORD +0 -13
- {erispulse-1.1.15.dist-info → erispulse-1.1.16.dist-info}/WHEEL +0 -0
- {erispulse-1.1.15.dist-info → erispulse-1.1.16.dist-info}/entry_points.txt +0 -0
- {erispulse-1.1.15.dist-info → erispulse-1.1.16.dist-info}/licenses/LICENSE +0 -0
ErisPulse/__init__.py
CHANGED
|
@@ -36,10 +36,13 @@ sdk.logger.info("SDK已初始化")
|
|
|
36
36
|
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
import types
|
|
40
|
-
sdk = types.SimpleNamespace()
|
|
41
39
|
import os
|
|
42
40
|
import sys
|
|
41
|
+
|
|
42
|
+
# 依赖类型
|
|
43
|
+
import types
|
|
44
|
+
|
|
45
|
+
# BaseModules: SDK核心模块
|
|
43
46
|
from . import util
|
|
44
47
|
from .raiserr import raiserr
|
|
45
48
|
from .logger import logger
|
|
@@ -50,177 +53,199 @@ from .adapter import adapter, BaseAdapter, SendDSL
|
|
|
50
53
|
# 这里不能删,确保windows下的shell能正确显示颜色
|
|
51
54
|
os.system('')
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
56
|
+
sdk = types.SimpleNamespace()
|
|
57
|
+
|
|
58
|
+
BaseModules = {
|
|
59
|
+
"util" : util,
|
|
60
|
+
"logger" : logger,
|
|
61
|
+
"env" : env,
|
|
62
|
+
"mods" : mods,
|
|
63
|
+
"adapter" : adapter,
|
|
64
|
+
"SendDSL" : SendDSL, # 链式发送基类 - 兼容原 sdk.SendDSL | 待弃用, 需要在 Adapter继承类中手动创建嵌套类并集成 super().SendDSL()
|
|
65
|
+
"AdapterFather" : BaseAdapter,
|
|
66
|
+
"BaseAdapter" : BaseAdapter
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
BaseErrors = {
|
|
70
|
+
"ExternalError" : "外部捕获异常",
|
|
71
|
+
"CaughtExternalError" : "捕获的非SDK抛出的异常",
|
|
72
|
+
"InitError" : "SDK初始化错误",
|
|
73
|
+
"MissingDependencyError" : "缺少依赖错误",
|
|
74
|
+
"InvalidDependencyError" : "依赖无效错误",
|
|
75
|
+
"CycleDependencyError" : "依赖循环错误",
|
|
76
|
+
"ModuleLoadError" : "模块加载错误"
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
for module, moduleObj in BaseModules.items():
|
|
80
|
+
try:
|
|
81
|
+
setattr(sdk, module, moduleObj)
|
|
82
|
+
except Exception as e:
|
|
83
|
+
raise e
|
|
84
|
+
|
|
85
|
+
for error, doc in BaseErrors.items():
|
|
86
|
+
try:
|
|
87
|
+
raiserr.register(error, doc=doc)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
raise e
|
|
90
|
+
|
|
91
|
+
def _prepare_environment() -> bool:
|
|
92
|
+
# 检查环境
|
|
93
|
+
logger.info("[Init] 准备初始化环境...")
|
|
71
94
|
try:
|
|
72
|
-
logger.info("[Init] SDK 正在初始化...")
|
|
73
95
|
if env.create_env_file_if_not_exists():
|
|
74
96
|
logger.info("[Init] 项目首次初始化,建议先配置环境变量")
|
|
75
97
|
if input("是否立即退出?(y/n): ").strip().lower() == "y":
|
|
76
|
-
|
|
98
|
+
return False
|
|
77
99
|
env.load_env_file()
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
if not hasattr(moduleObj, "moduleInfo") or not isinstance(moduleObj.moduleInfo, dict):
|
|
100
|
-
logger.warning(f"模块 {module_name} 缺少有效的 'moduleInfo' 字典.")
|
|
101
|
-
continue
|
|
102
|
-
if "name" not in moduleObj.moduleInfo.get("meta", {}):
|
|
103
|
-
logger.warning(f"模块 {module_name} 的 'moduleInfo' 字典 缺少必要 'name' 键.")
|
|
104
|
-
continue
|
|
105
|
-
if not hasattr(moduleObj, "Main"):
|
|
106
|
-
logger.warning(f"模块 {module_name} 缺少 'Main' 类.")
|
|
107
|
-
continue
|
|
108
|
-
|
|
109
|
-
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
110
|
-
module_info = mods.get_module(meta_name)
|
|
111
|
-
if module_info is None:
|
|
112
|
-
module_info = {
|
|
113
|
-
"status": True,
|
|
114
|
-
"info": moduleObj.moduleInfo
|
|
115
|
-
}
|
|
116
|
-
mods.set_module(meta_name, module_info)
|
|
117
|
-
logger.info(f"模块 {meta_name} 信息已初始化并存储到数据库")
|
|
118
|
-
|
|
119
|
-
if not module_info.get('status', True):
|
|
120
|
-
disabledModules.append(module_name)
|
|
121
|
-
logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
|
|
122
|
-
continue
|
|
123
|
-
|
|
124
|
-
required_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
125
|
-
missing_required_deps = [dep for dep in required_deps if dep not in TempModules]
|
|
126
|
-
if missing_required_deps:
|
|
127
|
-
logger.error(f"模块 {module_name} 缺少必需依赖: {missing_required_deps}")
|
|
128
|
-
raiserr.MissingDependencyError(f"模块 {module_name} 缺少必需依赖: {missing_required_deps}")
|
|
129
|
-
|
|
130
|
-
optional_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
|
|
131
|
-
available_optional_deps = []
|
|
132
|
-
for dep in optional_deps:
|
|
133
|
-
if isinstance(dep, list):
|
|
134
|
-
available_deps = [d for d in dep if d in TempModules]
|
|
135
|
-
if available_deps:
|
|
136
|
-
available_optional_deps.extend(available_deps)
|
|
137
|
-
elif dep in TempModules:
|
|
138
|
-
available_optional_deps.append(dep)
|
|
139
|
-
|
|
140
|
-
if optional_deps and not available_optional_deps:
|
|
141
|
-
logger.warning(f"模块 {module_name} 缺少所有可选依赖: {optional_deps}")
|
|
142
|
-
|
|
143
|
-
module_objs[module_name] = moduleObj
|
|
144
|
-
sdkInstalledModuleNames.append(module_name)
|
|
145
|
-
|
|
146
|
-
except Exception as e:
|
|
147
|
-
logger.warning(f"模块 {module_name} 加载失败: {e}")
|
|
100
|
+
return True
|
|
101
|
+
except Exception as e:
|
|
102
|
+
logger.error(f"环境准备失败: {e}")
|
|
103
|
+
return False
|
|
104
|
+
def _scan_modules(module_path: str) -> tuple[dict, list, list]:
|
|
105
|
+
# 扫描并验证模块
|
|
106
|
+
module_objs = {}
|
|
107
|
+
enabled_modules = []
|
|
108
|
+
disabled_modules = []
|
|
109
|
+
|
|
110
|
+
if not os.path.exists(module_path):
|
|
111
|
+
os.makedirs(module_path)
|
|
112
|
+
sys.path.append(module_path)
|
|
113
|
+
|
|
114
|
+
for module_name in os.listdir(module_path):
|
|
115
|
+
if not os.path.isdir(os.path.join(module_path, module_name)):
|
|
116
|
+
continue
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
moduleObj = __import__(module_name)
|
|
120
|
+
if not _validate_module(moduleObj, module_name):
|
|
148
121
|
continue
|
|
149
|
-
|
|
150
|
-
# ==== 构建依赖图并进行拓扑排序 ====
|
|
151
|
-
sdkModuleDependencies = {}
|
|
152
|
-
for module_name in sdkInstalledModuleNames:
|
|
153
|
-
moduleObj = module_objs[module_name]
|
|
154
|
-
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
155
|
-
|
|
156
|
-
req_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
157
|
-
opt_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
|
|
158
|
-
|
|
159
|
-
available_optional_deps = [dep for dep in opt_deps if dep in sdkInstalledModuleNames]
|
|
160
|
-
deps = req_deps + available_optional_deps
|
|
161
|
-
|
|
162
|
-
for dep in deps:
|
|
163
|
-
if dep in disabledModules:
|
|
164
|
-
logger.warning(f"模块 {meta_name} 的依赖模块 {dep} 已禁用,跳过加载")
|
|
165
|
-
continue
|
|
166
|
-
|
|
167
|
-
if not all(dep in sdkInstalledModuleNames for dep in deps):
|
|
168
|
-
raiserr.InvalidDependencyError(f"模块 {meta_name} 的依赖无效: {deps}")
|
|
169
|
-
sdkModuleDependencies[module_name] = deps
|
|
170
|
-
|
|
171
|
-
sdkInstalledModuleNames: list[str] = sdk.util.topological_sort(
|
|
172
|
-
sdkInstalledModuleNames, sdkModuleDependencies, raiserr.CycleDependencyError
|
|
173
|
-
)
|
|
174
|
-
# 存储模块依赖关系到env
|
|
175
|
-
env.set('module_dependencies', {
|
|
176
|
-
'modules': sdkInstalledModuleNames,
|
|
177
|
-
'dependencies': sdkModuleDependencies
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
# ==== 注册适配器 ====
|
|
181
|
-
logger.debug("[Init] 开始注册适配器...")
|
|
182
|
-
for module_name in sdkInstalledModuleNames:
|
|
183
|
-
moduleObj = module_objs[module_name]
|
|
122
|
+
|
|
184
123
|
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
185
|
-
|
|
186
|
-
try:
|
|
187
|
-
if hasattr(moduleObj, "adapterInfo") and isinstance(moduleObj.adapterInfo, dict):
|
|
188
|
-
for platform_name, adapter_class in moduleObj.adapterInfo.items():
|
|
189
|
-
sdk.adapter.register(platform_name, adapter_class)
|
|
190
|
-
logger.info(f"模块 {meta_name} 注册了适配器: {platform_name}")
|
|
191
|
-
except Exception as e:
|
|
192
|
-
logger.error(f"模块 {meta_name} 注册适配器失败: {e}")
|
|
193
|
-
|
|
194
|
-
# ==== 存储模块信息到数据库 ====
|
|
195
|
-
all_modules_info = {}
|
|
196
|
-
for module_name in sdkInstalledModuleNames:
|
|
197
|
-
moduleObj = module_objs[module_name]
|
|
198
|
-
moduleInfo: dict = moduleObj.moduleInfo
|
|
199
|
-
|
|
200
|
-
meta_name = moduleInfo.get("meta", {}).get("name", None)
|
|
201
|
-
module_info = mods.get_module(meta_name)
|
|
202
|
-
mods.set_module(meta_name, {
|
|
124
|
+
module_info = mods.get_module(meta_name) or {
|
|
203
125
|
"status": True,
|
|
204
|
-
"info": moduleInfo
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
moduleObj = module_objs[module_name]
|
|
212
|
-
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
213
|
-
|
|
214
|
-
module_status = mods.get_module_status(meta_name)
|
|
215
|
-
if not module_status:
|
|
126
|
+
"info": moduleObj.moduleInfo
|
|
127
|
+
}
|
|
128
|
+
mods.set_module(meta_name, module_info)
|
|
129
|
+
|
|
130
|
+
if not module_info.get('status', True):
|
|
131
|
+
disabled_modules.append(module_name)
|
|
132
|
+
logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
|
|
216
133
|
continue
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
134
|
+
|
|
135
|
+
_check_dependencies(moduleObj, module_name, os.listdir(module_path))
|
|
136
|
+
module_objs[module_name] = moduleObj
|
|
137
|
+
enabled_modules.append(module_name)
|
|
138
|
+
|
|
139
|
+
except Exception as e:
|
|
140
|
+
logger.warning(f"模块 {module_name} 加载失败: {e}")
|
|
141
|
+
|
|
142
|
+
return module_objs, enabled_modules, disabled_modules
|
|
143
|
+
|
|
144
|
+
def _validate_module(moduleObj, module_name: str) -> bool:
|
|
145
|
+
# 验证模块基本结构
|
|
146
|
+
if not hasattr(moduleObj, "moduleInfo") or not isinstance(moduleObj.moduleInfo, dict):
|
|
147
|
+
logger.warning(f"模块 {module_name} 缺少有效的 'moduleInfo' 字典")
|
|
148
|
+
return False
|
|
149
|
+
if "name" not in moduleObj.moduleInfo.get("meta", {}):
|
|
150
|
+
logger.warning(f"模块 {module_name} 缺少必要 'name' 键")
|
|
151
|
+
return False
|
|
152
|
+
if not hasattr(moduleObj, "Main"):
|
|
153
|
+
logger.warning(f"模块 {module_name} 缺少 'Main' 类")
|
|
154
|
+
return False
|
|
155
|
+
return True
|
|
156
|
+
|
|
157
|
+
def _check_dependencies(moduleObj, module_name: str, available_modules: list):
|
|
158
|
+
# 检查模块依赖关系
|
|
159
|
+
required_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
160
|
+
if missing := [dep for dep in required_deps if dep not in available_modules]:
|
|
161
|
+
logger.error(f"模块 {module_name} 缺少必需依赖: {missing}")
|
|
162
|
+
raiserr.MissingDependencyError(f"模块 {module_name} 缺少必需依赖: {missing}")
|
|
163
|
+
|
|
164
|
+
optional_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
|
|
165
|
+
available_optional = [
|
|
166
|
+
dep for dep in optional_deps
|
|
167
|
+
if (isinstance(dep, list) and any(d in available_modules for d in dep))
|
|
168
|
+
or (not isinstance(dep, list) and dep in available_modules)
|
|
169
|
+
]
|
|
170
|
+
if optional_deps and not available_optional:
|
|
171
|
+
logger.warning(f"模块 {module_name} 缺少所有可选依赖: {optional_deps}")
|
|
172
|
+
|
|
173
|
+
def _resolve_dependencies(modules: list, module_objs: dict) -> list:
|
|
174
|
+
# 解析模块依赖关系并进行拓扑排序
|
|
175
|
+
dependencies = {}
|
|
176
|
+
for module_name in modules:
|
|
177
|
+
moduleObj = module_objs[module_name]
|
|
178
|
+
req_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
179
|
+
opt_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
|
|
180
|
+
available_opt = [dep for dep in opt_deps if dep in modules]
|
|
181
|
+
dependencies[module_name] = req_deps + available_opt
|
|
182
|
+
|
|
183
|
+
sorted_modules = sdk.util.topological_sort(modules, dependencies, raiserr.CycleDependencyError)
|
|
184
|
+
env.set('module_dependencies', {
|
|
185
|
+
'modules': sorted_modules,
|
|
186
|
+
'dependencies': dependencies
|
|
187
|
+
})
|
|
188
|
+
return sorted_modules
|
|
189
|
+
|
|
190
|
+
def _register_adapters(modules: list, module_objs: dict) -> bool:
|
|
191
|
+
# 注册适配器
|
|
192
|
+
success = True
|
|
193
|
+
logger.debug("[Init] 开始注册适配器...")
|
|
194
|
+
for module_name in modules:
|
|
195
|
+
moduleObj = module_objs[module_name]
|
|
196
|
+
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
197
|
+
|
|
198
|
+
try:
|
|
199
|
+
if hasattr(moduleObj, "adapterInfo") and isinstance(moduleObj.adapterInfo, dict):
|
|
200
|
+
for platform, adapter_class in moduleObj.adapterInfo.items():
|
|
201
|
+
sdk.adapter.register(platform, adapter_class)
|
|
202
|
+
logger.info(f"模块 {meta_name} 注册适配器: {platform}")
|
|
203
|
+
except Exception as e:
|
|
204
|
+
logger.error(f"模块 {meta_name} 适配器注册失败: {e}")
|
|
205
|
+
success = False
|
|
206
|
+
return success
|
|
207
|
+
|
|
208
|
+
def _initialize_modules(modules: list, module_objs: dict) -> bool:
|
|
209
|
+
# 初始化模块
|
|
210
|
+
success = True
|
|
211
|
+
logger.debug("[Init] 开始实例化模块...")
|
|
212
|
+
for module_name in modules:
|
|
213
|
+
moduleObj = module_objs[module_name]
|
|
214
|
+
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
if mods.get_module_status(meta_name):
|
|
218
|
+
moduleMain = moduleObj.Main(sdk)
|
|
219
|
+
setattr(moduleMain, "moduleInfo", moduleObj.moduleInfo)
|
|
220
|
+
setattr(sdk, meta_name, moduleMain)
|
|
221
|
+
logger.debug(f"模块 {meta_name} 初始化完成")
|
|
222
|
+
except Exception as e:
|
|
223
|
+
logger.error(f"模块 {meta_name} 初始化失败: {e}")
|
|
224
|
+
success = False
|
|
225
|
+
return success
|
|
226
|
+
|
|
227
|
+
def init() -> bool:
|
|
228
|
+
logger.info("[Init] SDK 正在初始化...")
|
|
229
|
+
try:
|
|
230
|
+
if not _prepare_environment():
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
module_path = os.path.join(os.path.dirname(__file__), "modules")
|
|
234
|
+
module_objs, enabled_modules, _ = _scan_modules(module_path)
|
|
235
|
+
|
|
236
|
+
if not enabled_modules:
|
|
237
|
+
logger.warning("没有找到可用的模块")
|
|
238
|
+
return True
|
|
239
|
+
|
|
240
|
+
sorted_modules = _resolve_dependencies(enabled_modules, module_objs)
|
|
241
|
+
if not _register_adapters(sorted_modules, module_objs):
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
return _initialize_modules(sorted_modules, module_objs)
|
|
245
|
+
|
|
222
246
|
except Exception as e:
|
|
247
|
+
logger.critical(f"SDK初始化严重错误: {e}")
|
|
223
248
|
raiserr.InitError(f"sdk初始化失败: {e}", exit=True)
|
|
249
|
+
return False
|
|
224
250
|
|
|
225
|
-
|
|
226
|
-
sdk.init = init
|
|
251
|
+
sdk.init = init
|
ErisPulse/__main__.py
CHANGED
|
@@ -44,6 +44,7 @@ epsdk origin add https://example.com/map.json
|
|
|
44
44
|
"""
|
|
45
45
|
|
|
46
46
|
import argparse
|
|
47
|
+
import importlib
|
|
47
48
|
import os
|
|
48
49
|
import sys
|
|
49
50
|
import time
|
|
@@ -992,7 +993,6 @@ def list_modules(module_name=None):
|
|
|
992
993
|
|
|
993
994
|
def main():
|
|
994
995
|
parser = argparse.ArgumentParser(
|
|
995
|
-
|
|
996
996
|
prog="epsdk",
|
|
997
997
|
formatter_class=argparse.RawTextHelpFormatter
|
|
998
998
|
)
|
ErisPulse/adapter.py
CHANGED
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
提供平台适配器基类、消息发送DSL和适配器管理功能。支持多平台消息处理、事件驱动和生命周期管理。
|
|
5
5
|
|
|
6
|
-
## 核心功能
|
|
7
|
-
1. 适配器基类定义
|
|
8
|
-
2. 链式消息发送DSL
|
|
9
|
-
3. 适配器注册和管理
|
|
10
|
-
4. 事件处理系统
|
|
11
|
-
5. 中间件支持
|
|
12
|
-
|
|
13
6
|
## API 文档
|
|
14
7
|
|
|
15
8
|
### 适配器基类 (BaseAdapter)
|
|
@@ -229,124 +222,6 @@ import atexit
|
|
|
229
222
|
atexit.register(lambda: asyncio.run(sdk.adapter.shutdown()))
|
|
230
223
|
```
|
|
231
224
|
|
|
232
|
-
## 最佳实践
|
|
233
|
-
|
|
234
|
-
1. 适配器实现
|
|
235
|
-
```python
|
|
236
|
-
class MyPlatformAdapter(sdk.BaseAdapter):
|
|
237
|
-
class Send(sdk.BaseAdapter.Send):
|
|
238
|
-
# 实现基本消息类型
|
|
239
|
-
def Text(self, text: str):
|
|
240
|
-
return asyncio.create_task(
|
|
241
|
-
self._adapter.call_api(
|
|
242
|
-
endpoint="/send",
|
|
243
|
-
content=text,
|
|
244
|
-
recvId=self._target_id,
|
|
245
|
-
recvType=self._target_type
|
|
246
|
-
)
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
# 添加自定义消息类型
|
|
250
|
-
def Image(self, file: bytes):
|
|
251
|
-
return asyncio.create_task(
|
|
252
|
-
self._adapter.call_api(
|
|
253
|
-
endpoint="/send_image",
|
|
254
|
-
file=file,
|
|
255
|
-
recvId=self._target_id,
|
|
256
|
-
recvType=self._target_type
|
|
257
|
-
)
|
|
258
|
-
)
|
|
259
|
-
|
|
260
|
-
async def call_api(self, endpoint: str, **params):
|
|
261
|
-
# 实现API调用逻辑
|
|
262
|
-
async with aiohttp.ClientSession() as session:
|
|
263
|
-
async with session.post(
|
|
264
|
-
f"{self.api_base}{endpoint}",
|
|
265
|
-
json=params
|
|
266
|
-
) as response:
|
|
267
|
-
return await response.json()
|
|
268
|
-
|
|
269
|
-
async def start(self):
|
|
270
|
-
# 初始化连接
|
|
271
|
-
self.client = await self._create_client()
|
|
272
|
-
# 启动事件监听
|
|
273
|
-
asyncio.create_task(self._listen_events())
|
|
274
|
-
|
|
275
|
-
async def shutdown(self):
|
|
276
|
-
# 清理资源
|
|
277
|
-
if self.client:
|
|
278
|
-
await self.client.close()
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
2. 事件处理
|
|
282
|
-
```python
|
|
283
|
-
# 注册事件处理器
|
|
284
|
-
adapter = MyPlatformAdapter()
|
|
285
|
-
|
|
286
|
-
@adapter.on("message")
|
|
287
|
-
async def handle_message(data):
|
|
288
|
-
# 消息处理逻辑
|
|
289
|
-
if data["type"] == "text":
|
|
290
|
-
await process_text_message(data)
|
|
291
|
-
elif data["type"] == "image":
|
|
292
|
-
await process_image_message(data)
|
|
293
|
-
|
|
294
|
-
# 使用中间件
|
|
295
|
-
@adapter.middleware
|
|
296
|
-
async def auth_middleware(data):
|
|
297
|
-
if not verify_token(data.get("token")):
|
|
298
|
-
return None
|
|
299
|
-
return data
|
|
300
|
-
|
|
301
|
-
@adapter.middleware
|
|
302
|
-
async def log_middleware(data):
|
|
303
|
-
sdk.logger.info(f"处理事件: {data}")
|
|
304
|
-
return data
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
3. 消息发送
|
|
308
|
-
```python
|
|
309
|
-
# 基本消息发送
|
|
310
|
-
async def send_welcome(user_id: str):
|
|
311
|
-
await sdk.adapter.Platform.Send.To("user", user_id).Text("欢迎!")
|
|
312
|
-
|
|
313
|
-
# 复杂消息处理
|
|
314
|
-
async def process_group_notification(group_id: str, event: dict):
|
|
315
|
-
# 发送格式化消息
|
|
316
|
-
message = format_notification(event)
|
|
317
|
-
await sdk.adapter.Platform.Send.To("group", group_id).Text(message)
|
|
318
|
-
|
|
319
|
-
# 发送附加文件
|
|
320
|
-
if event.get("has_attachment"):
|
|
321
|
-
file_data = await get_attachment(event["attachment_id"])
|
|
322
|
-
await sdk.adapter.Platform.Send.To("group", group_id).File(file_data)
|
|
323
|
-
```
|
|
324
|
-
|
|
325
|
-
## 注意事项
|
|
326
|
-
|
|
327
|
-
1. 适配器实现
|
|
328
|
-
- 确保正确实现所有抽象方法
|
|
329
|
-
- 处理所有可能的异常情况
|
|
330
|
-
- 实现适当的重试机制
|
|
331
|
-
- 注意资源的正确释放
|
|
332
|
-
|
|
333
|
-
2. 事件处理
|
|
334
|
-
- 避免在事件处理器中执行长时间操作
|
|
335
|
-
- 使用适当的错误处理
|
|
336
|
-
- 考虑事件处理的顺序性
|
|
337
|
-
- 合理使用中间件过滤机制
|
|
338
|
-
|
|
339
|
-
3. 消息发送
|
|
340
|
-
- 实现消息发送的限流机制
|
|
341
|
-
- 处理发送失败的情况
|
|
342
|
-
- 注意消息格式的平台兼容性
|
|
343
|
-
- 大文件传输时考虑分片
|
|
344
|
-
|
|
345
|
-
4. 生命周期管理
|
|
346
|
-
- 确保适配器正确启动和关闭
|
|
347
|
-
- 处理意外断开的情况
|
|
348
|
-
- 实现自动重连机制
|
|
349
|
-
- 注意资源泄漏问题
|
|
350
225
|
"""
|
|
351
226
|
|
|
352
227
|
import functools
|
ErisPulse/db.py
CHANGED
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
提供键值存储、事务支持、快照和恢复功能,用于管理框架配置数据。基于SQLite实现持久化存储,支持复杂数据类型和原子操作。
|
|
5
5
|
|
|
6
|
-
## 核心功能
|
|
7
|
-
1. 键值存储
|
|
8
|
-
2. 事务支持
|
|
9
|
-
3. 数据库快照
|
|
10
|
-
4. 自动备份
|
|
11
|
-
5. 配置文件集成
|
|
12
|
-
|
|
13
6
|
## API 文档
|
|
14
7
|
|
|
15
8
|
### 基本操作
|
|
@@ -431,27 +424,6 @@ def safe_bulk_update(updates):
|
|
|
431
424
|
raise
|
|
432
425
|
```
|
|
433
426
|
|
|
434
|
-
## 注意事项
|
|
435
|
-
|
|
436
|
-
1. 性能优化
|
|
437
|
-
- 使用批量操作代替多次单独操作
|
|
438
|
-
- 合理使用事务减少数据库操作次数
|
|
439
|
-
- 避免存储过大的值,考虑分片存储
|
|
440
|
-
|
|
441
|
-
2. 数据安全
|
|
442
|
-
- 定期创建快照备份重要数据
|
|
443
|
-
- 使用事务确保数据一致性
|
|
444
|
-
- 不要存储敏感信息(如密码)的明文
|
|
445
|
-
|
|
446
|
-
3. 配置管理
|
|
447
|
-
- 使用有意义的键名和层次结构
|
|
448
|
-
- 记录配置的更新历史
|
|
449
|
-
- 定期清理不再使用的配置
|
|
450
|
-
|
|
451
|
-
4. 错误处理
|
|
452
|
-
- 所有数据库操作都应该有错误处理
|
|
453
|
-
- 重要操作前创建快照以便回滚
|
|
454
|
-
- 记录所有关键操作的日志
|
|
455
427
|
"""
|
|
456
428
|
|
|
457
429
|
import os
|
ErisPulse/logger.py
CHANGED
|
@@ -3,62 +3,16 @@
|
|
|
3
3
|
|
|
4
4
|
提供模块化、多级别的日志记录功能,支持内存存储和文件输出。实现了模块级别的日志控制、彩色输出和灵活的存储选项。
|
|
5
5
|
|
|
6
|
-
## 核心功能
|
|
7
|
-
1. 多级别日志记录
|
|
8
|
-
2. 模块级别日志控制
|
|
9
|
-
3. 内存日志存储
|
|
10
|
-
4. 文件输出支持
|
|
11
|
-
5. 自动调用者识别
|
|
12
|
-
6. 异常捕获装饰器
|
|
13
|
-
|
|
14
6
|
## API 文档
|
|
15
7
|
|
|
16
8
|
### 基本日志操作
|
|
17
|
-
#### debug(msg: str, *args, **kwargs) -> None
|
|
18
|
-
记录调试级别的日志信息。
|
|
19
|
-
- 参数:
|
|
20
|
-
- msg: 日志消息
|
|
21
|
-
- *args: 传递给底层logger的位置参数
|
|
22
|
-
- **kwargs: 传递给底层logger的关键字参数
|
|
23
|
-
- 返回:
|
|
24
|
-
- None
|
|
25
|
-
- 示例:
|
|
26
|
-
```python
|
|
27
|
-
# 基本调试信息
|
|
28
|
-
sdk.logger.debug("初始化配置")
|
|
29
9
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
sdk.logger.debug(f"读取配置: timeout = {config_value}")
|
|
10
|
+
以debug为例:
|
|
11
|
+
> 此外,还有其他级别的日志记录函数,如info, warning, error, critical等,用法相同。
|
|
33
12
|
|
|
34
|
-
|
|
35
|
-
if is_development_mode():
|
|
36
|
-
sdk.logger.debug("开发模式下的详细信息: %s", detailed_info)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
#### info(msg: str, *args, **kwargs) -> None
|
|
40
|
-
记录信息级别的日志信息。
|
|
41
|
-
- 参数:
|
|
42
|
-
- msg: 日志消息
|
|
43
|
-
- *args: 传递给底层logger的位置参数
|
|
44
|
-
- **kwargs: 传递给底层logger的关键字参数
|
|
45
|
-
- 返回:
|
|
46
|
-
- None
|
|
47
|
-
- 示例:
|
|
48
|
-
```python
|
|
49
|
-
# 基本信息记录
|
|
50
|
-
sdk.logger.info("应用已启动")
|
|
13
|
+
debug(msg: str, *args, **kwargs) -> None
|
|
51
14
|
|
|
52
|
-
|
|
53
|
-
user_count = get_active_users()
|
|
54
|
-
sdk.logger.info(f"当前活跃用户: {user_count}")
|
|
55
|
-
|
|
56
|
-
# 记录操作结果
|
|
57
|
-
sdk.logger.info("数据导入完成,共处理 %d 条记录", record_count)
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
#### warning(msg: str, *args, **kwargs) -> None
|
|
61
|
-
记录警告级别的日志信息。
|
|
15
|
+
记录调试级别的日志信息。
|
|
62
16
|
- 参数:
|
|
63
17
|
- msg: 日志消息
|
|
64
18
|
- *args: 传递给底层logger的位置参数
|
|
@@ -66,64 +20,9 @@ sdk.logger.info("数据导入完成,共处理 %d 条记录", record_count)
|
|
|
66
20
|
- 返回:
|
|
67
21
|
- None
|
|
68
22
|
- 示例:
|
|
69
|
-
```python
|
|
70
|
-
# 基本警告信息
|
|
71
|
-
sdk.logger.warning("配置文件未找到,使用默认配置")
|
|
72
23
|
|
|
73
|
-
# 性能警告
|
|
74
|
-
if response_time > threshold:
|
|
75
|
-
sdk.logger.warning(f"响应时间过长: {response_time}ms > {threshold}ms")
|
|
76
|
-
|
|
77
|
-
# 资源使用警告
|
|
78
|
-
memory_usage = get_memory_usage()
|
|
79
|
-
if memory_usage > 80:
|
|
80
|
-
sdk.logger.warning("内存使用率高: %d%%", memory_usage)
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
#### error(msg: str, *args, **kwargs) -> None
|
|
84
|
-
记录错误级别的日志信息。
|
|
85
|
-
- 参数:
|
|
86
|
-
- msg: 日志消息
|
|
87
|
-
- *args: 传递给底层logger的位置参数
|
|
88
|
-
- **kwargs: 传递给底层logger的关键字参数
|
|
89
|
-
- 返回:
|
|
90
|
-
- None
|
|
91
|
-
- 示例:
|
|
92
24
|
```python
|
|
93
|
-
|
|
94
|
-
sdk.logger.error("数据库连接失败")
|
|
95
|
-
|
|
96
|
-
# 带有异常信息的错误
|
|
97
|
-
try:
|
|
98
|
-
process_data()
|
|
99
|
-
except Exception as e:
|
|
100
|
-
sdk.logger.error(f"数据处理错误: {str(e)}")
|
|
101
|
-
|
|
102
|
-
# 带有错误代码的错误
|
|
103
|
-
sdk.logger.error("API请求失败,状态码: %d, 错误: %s", status_code, error_message)
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
#### critical(msg: str, *args, **kwargs) -> None
|
|
107
|
-
记录致命错误级别的日志信息,并终止程序。
|
|
108
|
-
- 参数:
|
|
109
|
-
- msg: 日志消息
|
|
110
|
-
- *args: 传递给底层logger的位置参数
|
|
111
|
-
- **kwargs: 传递给底层logger的关键字参数
|
|
112
|
-
- 返回:
|
|
113
|
-
- None (程序会终止)
|
|
114
|
-
- 示例:
|
|
115
|
-
```python
|
|
116
|
-
# 致命错误记录
|
|
117
|
-
if not database_connection:
|
|
118
|
-
sdk.logger.critical("无法连接到主数据库,应用无法继续运行")
|
|
119
|
-
|
|
120
|
-
# 安全相关的致命错误
|
|
121
|
-
if security_breach_detected():
|
|
122
|
-
sdk.logger.critical("检测到安全漏洞,强制关闭系统")
|
|
123
|
-
|
|
124
|
-
# 资源耗尽的致命错误
|
|
125
|
-
if disk_space < min_required:
|
|
126
|
-
sdk.logger.critical("磁盘空间不足 (%dMB),无法继续运行", disk_space)
|
|
25
|
+
sdk.logger.debug("这是一条日志")
|
|
127
26
|
```
|
|
128
27
|
|
|
129
28
|
### 日志级别控制
|
|
@@ -214,118 +113,11 @@ import atexit
|
|
|
214
113
|
atexit.register(lambda: sdk.logger.save_logs("final_logs.txt"))
|
|
215
114
|
```
|
|
216
115
|
|
|
217
|
-
### 异常捕获 (准备弃用)
|
|
218
|
-
#### catch(func_or_level=None, level="error")
|
|
219
|
-
异常捕获装饰器。
|
|
220
|
-
- 参数:
|
|
221
|
-
- func_or_level: 要装饰的函数或日志级别
|
|
222
|
-
- level: 捕获异常时使用的日志级别
|
|
223
|
-
- 返回:
|
|
224
|
-
- function: 装饰后的函数
|
|
225
|
-
- 注意:
|
|
226
|
-
- 此功能已集成到 raiserr 模块中,建议使用 raiserr 进行异常处理
|
|
227
|
-
- 示例:
|
|
228
|
-
```python
|
|
229
|
-
# 基本用法 (不推荐,请使用raiserr)
|
|
230
|
-
@sdk.logger.catch
|
|
231
|
-
def risky_function():
|
|
232
|
-
# 可能抛出异常的代码
|
|
233
|
-
process_data()
|
|
234
|
-
|
|
235
|
-
# 指定日志级别 (不推荐,请使用raiserr)
|
|
236
|
-
@sdk.logger.catch(level="critical")
|
|
237
|
-
def very_important_function():
|
|
238
|
-
# 关键操作
|
|
239
|
-
update_database()
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## 最佳实践
|
|
243
|
-
1. 日志级别使用
|
|
244
|
-
```python
|
|
245
|
-
# 开发环境使用详细日志
|
|
246
|
-
if is_development():
|
|
247
|
-
sdk.logger.set_level("DEBUG")
|
|
248
|
-
sdk.logger.debug("详细的调试信息")
|
|
249
|
-
else:
|
|
250
|
-
sdk.logger.set_level("INFO")
|
|
251
|
-
|
|
252
|
-
# 性能敏感模块使用更高级别
|
|
253
|
-
sdk.logger.set_module_level("PerformanceModule", "WARNING")
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
2. 结构化日志信息
|
|
257
|
-
```python
|
|
258
|
-
# 使用一致的格式
|
|
259
|
-
def log_api_request(endpoint, method, status, duration):
|
|
260
|
-
sdk.logger.info(
|
|
261
|
-
f"API请求: {method} {endpoint} - 状态: {status}, 耗时: {duration}ms"
|
|
262
|
-
)
|
|
263
|
-
|
|
264
|
-
# 包含关键上下文
|
|
265
|
-
def log_user_action(user_id, action, result):
|
|
266
|
-
sdk.logger.info(
|
|
267
|
-
f"用户操作: [用户:{user_id}] {action} - 结果: {result}"
|
|
268
|
-
)
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
3. 日志文件管理
|
|
272
|
-
```python
|
|
273
|
-
# 按日期分割日志文件
|
|
274
|
-
from datetime import datetime
|
|
275
|
-
import os
|
|
276
|
-
|
|
277
|
-
def setup_logging():
|
|
278
|
-
log_dir = "logs"
|
|
279
|
-
os.makedirs(log_dir, exist_ok=True)
|
|
280
|
-
|
|
281
|
-
today = datetime.now().strftime("%Y-%m-%d")
|
|
282
|
-
log_file = os.path.join(log_dir, f"app_{today}.log")
|
|
283
|
-
|
|
284
|
-
sdk.logger.set_output_file(log_file)
|
|
285
|
-
sdk.logger.info(f"日志文件已设置: {log_file}")
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
4. 异常处理与日志
|
|
289
|
-
```python
|
|
290
|
-
# 推荐方式:使用raiserr结合logger
|
|
291
|
-
def process_with_logging():
|
|
292
|
-
try:
|
|
293
|
-
result = perform_operation()
|
|
294
|
-
sdk.logger.info(f"操作成功: {result}")
|
|
295
|
-
return result
|
|
296
|
-
except Exception as e:
|
|
297
|
-
sdk.logger.error(f"操作失败: {str(e)}")
|
|
298
|
-
sdk.raiserr.OperationError(f"处理失败: {str(e)}")
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## 注意事项
|
|
302
|
-
1. 日志级别选择
|
|
303
|
-
- DEBUG: 详细的调试信息,仅在开发环境使用
|
|
304
|
-
- INFO: 常规操作信息,适用于生产环境
|
|
305
|
-
- WARNING: 潜在问题或异常情况
|
|
306
|
-
- ERROR: 错误但不影响整体功能
|
|
307
|
-
- CRITICAL: 致命错误,导致程序终止
|
|
308
|
-
|
|
309
|
-
2. 性能考虑
|
|
310
|
-
- 避免在高频循环中记录过多日志
|
|
311
|
-
- 使用适当的日志级别减少不必要的输出
|
|
312
|
-
- 考虑日志文件大小和轮转策略
|
|
313
|
-
|
|
314
|
-
3. 敏感信息保护
|
|
315
|
-
- 不要记录密码、令牌等敏感信息
|
|
316
|
-
- 在记录用户数据前进行脱敏处理
|
|
317
|
-
- 遵循数据保护法规要求
|
|
318
|
-
|
|
319
|
-
4. 迁移建议
|
|
320
|
-
- 从catch装饰器迁移到raiserr模块
|
|
321
|
-
- 使用结构化的错误处理方式
|
|
322
|
-
- 结合日志和错误管理实现完整的异常处理流程
|
|
323
116
|
"""
|
|
324
117
|
|
|
325
118
|
import logging
|
|
326
119
|
import inspect
|
|
327
120
|
import datetime
|
|
328
|
-
import functools
|
|
329
121
|
|
|
330
122
|
class Logger:
|
|
331
123
|
def __init__(self):
|
|
@@ -395,34 +187,6 @@ class Logger:
|
|
|
395
187
|
self._logger.error(f"无法保存日志到 {p}: {e}。")
|
|
396
188
|
raise e
|
|
397
189
|
|
|
398
|
-
def catch(self, func_or_level=None, level="error"):
|
|
399
|
-
if isinstance(func_or_level, str):
|
|
400
|
-
return lambda func: self.catch(func, level=func_or_level)
|
|
401
|
-
if func_or_level is None:
|
|
402
|
-
return lambda func: self.catch(func, level=level)
|
|
403
|
-
func = func_or_level
|
|
404
|
-
|
|
405
|
-
@functools.wraps(func)
|
|
406
|
-
def wrapper(*args, **kwargs):
|
|
407
|
-
try:
|
|
408
|
-
return func(*args, **kwargs)
|
|
409
|
-
except Exception as e:
|
|
410
|
-
import traceback
|
|
411
|
-
error_info = traceback.format_exc()
|
|
412
|
-
|
|
413
|
-
module_name = func.__module__
|
|
414
|
-
if module_name == "__main__":
|
|
415
|
-
module_name = "Main"
|
|
416
|
-
func_name = func.__name__
|
|
417
|
-
|
|
418
|
-
error_msg = f"Exception in {func_name}: {str(e)}\n{error_info}"
|
|
419
|
-
|
|
420
|
-
log_method = getattr(self, level, self.error)
|
|
421
|
-
log_method(error_msg)
|
|
422
|
-
|
|
423
|
-
return None
|
|
424
|
-
return wrapper
|
|
425
|
-
|
|
426
190
|
def _save_in_memory(self, ModuleName, msg):
|
|
427
191
|
if ModuleName not in self._logs:
|
|
428
192
|
self._logs[ModuleName] = []
|
|
@@ -441,6 +205,8 @@ class Logger:
|
|
|
441
205
|
module_name = "Main"
|
|
442
206
|
if module_name.endswith(".Core"):
|
|
443
207
|
module_name = module_name[:-5]
|
|
208
|
+
if module_name.startswith("ErisPulse"):
|
|
209
|
+
module_name = "ErisPulse"
|
|
444
210
|
return module_name
|
|
445
211
|
|
|
446
212
|
def debug(self, msg, *args, **kwargs):
|
ErisPulse/mods.py
CHANGED
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
提供模块的注册、状态管理和依赖解析功能。支持模块信息存储、状态切换和批量操作。
|
|
5
5
|
|
|
6
|
-
## 核心功能
|
|
7
|
-
1. 模块信息管理
|
|
8
|
-
2. 模块状态控制
|
|
9
|
-
3. 批量模块操作
|
|
10
|
-
4. 存储前缀自定义
|
|
11
|
-
5. 模块依赖管理
|
|
12
|
-
|
|
13
6
|
## API 文档
|
|
14
7
|
|
|
15
8
|
### 模块状态管理
|
|
@@ -257,76 +250,6 @@ custom_status_key = f"{sdk.mods.status_prefix}custom.{module_name}"
|
|
|
257
250
|
sdk.env.set(custom_status_key, is_active)
|
|
258
251
|
```
|
|
259
252
|
|
|
260
|
-
## 最佳实践
|
|
261
|
-
1. 模块信息结构
|
|
262
|
-
```python
|
|
263
|
-
# 推荐的模块信息结构
|
|
264
|
-
module_info = {
|
|
265
|
-
"status": True, # 模块启用状态
|
|
266
|
-
"info": {
|
|
267
|
-
"meta": {
|
|
268
|
-
"name": "ModuleName", # 模块名称
|
|
269
|
-
"version": "1.0.0", # 模块版本
|
|
270
|
-
"description": "模块描述",
|
|
271
|
-
"author": "作者",
|
|
272
|
-
"license": "MIT",
|
|
273
|
-
"tags": ["tag1", "tag2"] # 分类标签
|
|
274
|
-
},
|
|
275
|
-
"dependencies": {
|
|
276
|
-
"requires": ["RequiredModule1"], # 必需依赖
|
|
277
|
-
"optional": ["OptionalModule1"], # 可选依赖
|
|
278
|
-
"pip": ["package1", "package2"] # pip包依赖
|
|
279
|
-
}
|
|
280
|
-
},
|
|
281
|
-
"config": { # 模块配置(可选)
|
|
282
|
-
"setting1": "value1",
|
|
283
|
-
"setting2": "value2"
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
2. 模块状态管理
|
|
289
|
-
```python
|
|
290
|
-
# 根据条件启用/禁用模块
|
|
291
|
-
def toggle_modules_by_environment():
|
|
292
|
-
env_type = get_environment_type()
|
|
293
|
-
|
|
294
|
-
# 开发环境启用调试模块
|
|
295
|
-
if env_type == "development":
|
|
296
|
-
sdk.mods.set_module_status("DebugModule", True)
|
|
297
|
-
sdk.mods.set_module_status("PerformanceModule", False)
|
|
298
|
-
|
|
299
|
-
# 生产环境禁用调试模块,启用性能模块
|
|
300
|
-
elif env_type == "production":
|
|
301
|
-
sdk.mods.set_module_status("DebugModule", False)
|
|
302
|
-
sdk.mods.set_module_status("PerformanceModule", True)
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
3. 模块依赖检查
|
|
306
|
-
```python
|
|
307
|
-
# 检查模块依赖
|
|
308
|
-
def check_module_dependencies(module_name):
|
|
309
|
-
module_info = sdk.mods.get_module(module_name)
|
|
310
|
-
if not module_info:
|
|
311
|
-
return False
|
|
312
|
-
|
|
313
|
-
dependencies = module_info.get("info", {}).get("dependencies", {}).get("requires", [])
|
|
314
|
-
|
|
315
|
-
for dep in dependencies:
|
|
316
|
-
dep_info = sdk.mods.get_module(dep)
|
|
317
|
-
if not dep_info or not dep_info.get("status", False):
|
|
318
|
-
sdk.logger.warning(f"模块 {module_name} 的依赖 {dep} 未启用或不存在")
|
|
319
|
-
return False
|
|
320
|
-
|
|
321
|
-
return True
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
## 注意事项
|
|
325
|
-
1. 模块名称应唯一,避免冲突
|
|
326
|
-
2. 模块信息结构应保持一致,便于管理
|
|
327
|
-
3. 更新模块信息前应先获取现有信息,避免覆盖
|
|
328
|
-
4. 批量操作时注意性能影响
|
|
329
|
-
5. 自定义前缀时确保不与系统其他键冲突
|
|
330
253
|
"""
|
|
331
254
|
|
|
332
255
|
import json
|
ErisPulse/raiserr.py
CHANGED
|
@@ -3,13 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
提供错误类型注册、抛出和管理功能,集成全局异常处理。支持自定义错误类型、错误链追踪和全局异常捕获。
|
|
5
5
|
|
|
6
|
-
## 核心功能
|
|
7
|
-
1. 错误类型注册和管理
|
|
8
|
-
2. 动态错误抛出
|
|
9
|
-
3. 全局异常处理
|
|
10
|
-
4. 错误信息追踪
|
|
11
|
-
5. 异步错误处理
|
|
12
|
-
|
|
13
6
|
## API 文档
|
|
14
7
|
|
|
15
8
|
### 错误注册
|
|
@@ -73,87 +66,6 @@ except Exception as e:
|
|
|
73
66
|
print(f"捕获到错误: {e}")
|
|
74
67
|
```
|
|
75
68
|
|
|
76
|
-
### 全局异常处理
|
|
77
|
-
#### global_exception_handler(exc_type: type, exc_value: Exception, exc_traceback: traceback)
|
|
78
|
-
全局同步异常处理器。
|
|
79
|
-
- 参数:
|
|
80
|
-
- exc_type: 异常类型
|
|
81
|
-
- exc_value: 异常值
|
|
82
|
-
- exc_traceback: 异常追踪信息
|
|
83
|
-
- 示例:
|
|
84
|
-
```python
|
|
85
|
-
# 系统会自动捕获未处理的异常
|
|
86
|
-
def risky_operation():
|
|
87
|
-
raise Exception("未处理的异常")
|
|
88
|
-
|
|
89
|
-
# 异常会被global_exception_handler捕获并处理
|
|
90
|
-
risky_operation()
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
#### async_exception_handler(loop: asyncio.AbstractEventLoop, context: dict)
|
|
94
|
-
全局异步异常处理器。
|
|
95
|
-
- 参数:
|
|
96
|
-
- loop: 事件循环实例
|
|
97
|
-
- context: 异常上下文信息
|
|
98
|
-
- 示例:
|
|
99
|
-
```python
|
|
100
|
-
async def async_operation():
|
|
101
|
-
raise Exception("异步操作错误")
|
|
102
|
-
|
|
103
|
-
# 异常会被async_exception_handler捕获并处理
|
|
104
|
-
asyncio.create_task(async_operation())
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
## 最佳实践
|
|
108
|
-
1. 错误类型注册
|
|
109
|
-
```python
|
|
110
|
-
# 为特定功能模块注册错误类型
|
|
111
|
-
sdk.raiserr.register("DatabaseError", "数据库操作错误")
|
|
112
|
-
sdk.raiserr.register("NetworkError", "网络连接错误")
|
|
113
|
-
sdk.raiserr.register("ValidationError", "数据验证错误")
|
|
114
|
-
|
|
115
|
-
# 使用继承关系组织错误类型
|
|
116
|
-
class ModuleError(Exception):
|
|
117
|
-
pass
|
|
118
|
-
sdk.raiserr.register("ConfigError", "配置错误", ModuleError)
|
|
119
|
-
sdk.raiserr.register("PluginError", "插件错误", ModuleError)
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
2. 错误处理流程
|
|
123
|
-
```python
|
|
124
|
-
def process_data(data):
|
|
125
|
-
try:
|
|
126
|
-
if not data:
|
|
127
|
-
sdk.raiserr.ValidationError("数据不能为空")
|
|
128
|
-
if not isinstance(data, dict):
|
|
129
|
-
sdk.raiserr.ValidationError("数据必须是字典类型")
|
|
130
|
-
|
|
131
|
-
# 处理数据...
|
|
132
|
-
|
|
133
|
-
except Exception as e:
|
|
134
|
-
# 错误会被自动记录并处理
|
|
135
|
-
sdk.raiserr.ProcessingError(f"数据处理失败: {str(e)}")
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
3. 异步环境使用
|
|
139
|
-
```python
|
|
140
|
-
async def async_task():
|
|
141
|
-
try:
|
|
142
|
-
result = await some_async_operation()
|
|
143
|
-
if not result.success:
|
|
144
|
-
sdk.raiserr.AsyncOperationError("异步操作失败")
|
|
145
|
-
except Exception as e:
|
|
146
|
-
# 异步错误会被async_exception_handler捕获
|
|
147
|
-
raise
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
## 注意事项
|
|
151
|
-
1. 错误类型命名应具有描述性,便于理解错误来源
|
|
152
|
-
2. 错误消息应包含足够的上下文信息,便于调试
|
|
153
|
-
3. 适当使用exit参数,只在致命错误时设置为True
|
|
154
|
-
4. 避免在全局异常处理器中执行耗时操作
|
|
155
|
-
5. 确保异步代码中的错误能够被正确捕获和处理
|
|
156
|
-
|
|
157
69
|
"""
|
|
158
70
|
|
|
159
71
|
import sys
|
|
@@ -209,24 +121,16 @@ raiserr = Error()
|
|
|
209
121
|
|
|
210
122
|
# 全局异常处理器
|
|
211
123
|
def global_exception_handler(exc_type, exc_value, exc_traceback):
|
|
212
|
-
from .logger import logger
|
|
213
124
|
error_message = ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback))
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
f"检测到外部异常,请优先使用 sdk.raiserr 抛出错误。\n原始异常: {exc_type.__name__}: {exc_value}\nTraceback:\n{error_message}"
|
|
125
|
+
raiserr.ExternalError(
|
|
126
|
+
f"{exc_type.__name__}: {exc_value}\nTraceback:\n{error_message}"
|
|
217
127
|
)
|
|
218
|
-
sys.excepthook = global_exception_handler
|
|
219
|
-
|
|
220
128
|
def async_exception_handler(loop, context):
|
|
221
|
-
from .logger import logger
|
|
222
129
|
exception = context.get('exception')
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
)
|
|
230
|
-
else:
|
|
231
|
-
logger.warning(f"异步任务警告: {message}")
|
|
130
|
+
tb = ''.join(traceback.format_exception(type(exception), exception, exception.__traceback__))
|
|
131
|
+
raiserr.ExternalError(
|
|
132
|
+
f"{type(exception).__name__}: {exception}\nTraceback:\n{tb}"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
sys.excepthook = global_exception_handler
|
|
232
136
|
asyncio.get_event_loop().set_exception_handler(async_exception_handler)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.16
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -31,61 +31,68 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
31
31
|
Classifier: Operating System :: OS Independent
|
|
32
32
|
Classifier: Programming Language :: Python :: 3
|
|
33
33
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
34
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
35
34
|
Classifier: Programming Language :: Python :: 3.10
|
|
36
35
|
Classifier: Programming Language :: Python :: 3.11
|
|
37
36
|
Classifier: Programming Language :: Python :: 3.12
|
|
38
37
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
39
|
-
Requires-Python: >=3.
|
|
38
|
+
Requires-Python: >=3.10
|
|
40
39
|
Requires-Dist: aiohttp
|
|
40
|
+
Requires-Dist: importlib>=1.0.4
|
|
41
41
|
Requires-Dist: watchdog
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
# ErisPulse - 异步机器人开发框架
|
|
45
|
+
|
|
46
|
+

|
|
46
47
|
|
|
47
48
|
[](https://github.com/FramerOrg)
|
|
48
49
|
[](https://github.com/ErisPulse/ErisPulse/blob/main/LICENSE)
|
|
49
|
-
|
|
50
50
|
[](https://pypi.org/project/ErisPulse/)
|
|
51
51
|
|
|
52
|
-
> 文档站:
|
|
53
|
-
|
|
52
|
+
> 文档站:
|
|
54
53
|
[](https://www.erisdev.com/docs.html)
|
|
55
54
|
[](https://erispulse.pages.dev/docs.html)
|
|
56
55
|
[](https://erispulse.github.io/docs.html)
|
|
57
56
|
[](https://erispulse.netlify.app/docs.htm)
|
|
58
57
|
|
|
59
|
-
- [GitHub 社区讨论](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
60
58
|
|
|
61
|
-
|
|
62
|
-
| 需求 | 推荐框架 | 理由 |
|
|
63
|
-
|-------------------|----------------|-----------------------------|
|
|
64
|
-
| 轻量化/底层模块化 | [Framer](https://github.com/FramerOrg/Framer) | 高度解耦的模块化设计 |
|
|
65
|
-
| 全功能机器人开发 | ErisPulse | 开箱即用的完整解决方案 |
|
|
59
|
+
## 核心特性
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
| 特性 | 描述 |
|
|
62
|
+
|------|------|
|
|
63
|
+
| **异步架构** | 完全基于 async/await 的异步设计 |
|
|
64
|
+
| **模块化系统** | 灵活的插件和模块管理 |
|
|
65
|
+
| **热重载** | 开发时自动重载,无需重启 |
|
|
66
|
+
| **错误管理** | 统一的错误处理和报告系统 |
|
|
67
|
+
| **配置管理** | 灵活的配置存储和访问 |
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
---
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
## 快速开始
|
|
72
|
+
|
|
73
|
+
### 框架选型指南
|
|
74
|
+
|
|
75
|
+
| 需求 | 推荐框架 | 理由 |
|
|
76
|
+
|------|---------|------|
|
|
77
|
+
| 轻量化/底层模块化 | [Framer](https://github.com/FramerOrg/Framer) | 高度解耦的模块化设计 |
|
|
78
|
+
| 全功能机器人开发 | ErisPulse | 开箱即用的完整解决方案 |
|
|
79
79
|
|
|
80
80
|
---
|
|
81
81
|
|
|
82
|
-
##
|
|
82
|
+
## 安装指南
|
|
83
|
+
|
|
84
|
+
我们全面采用 [`uv`](https://github.com/astral-sh/uv) 作为 Python 工具链,提供更快速可靠的安装体验。
|
|
85
|
+
|
|
86
|
+
> ℹ️ **uv** 是由 Astral 开发的新一代 Python 包管理工具,比传统 pip 快 10-100 倍,并具有更好的依赖解析能力。
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
### 1. 安装 uv
|
|
85
89
|
|
|
86
|
-
|
|
90
|
+
#### 通用方法 (pip):
|
|
91
|
+
```bash
|
|
92
|
+
pip install uv
|
|
93
|
+
```
|
|
87
94
|
|
|
88
|
-
#### macOS
|
|
95
|
+
#### macOS/Linux:
|
|
89
96
|
```bash
|
|
90
97
|
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
91
98
|
```
|
|
@@ -95,11 +102,24 @@ curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
|
95
102
|
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
|
96
103
|
```
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
验证安装:
|
|
99
106
|
```bash
|
|
100
107
|
uv --version
|
|
101
108
|
```
|
|
102
109
|
|
|
110
|
+
### 2. 安装 ErisPulse
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
uv python install 3.12 # 安装 Python 3.12
|
|
114
|
+
uv venv # 创建虚拟环境
|
|
115
|
+
source .venv/bin/activate # 激活环境 (Windows: .venv\Scripts\activate)
|
|
116
|
+
uv pip install ErisPulse --upgrade # 安装框架
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 测试与开发
|
|
122
|
+
|
|
103
123
|
### 克隆项目并进入目录
|
|
104
124
|
|
|
105
125
|
```bash
|
|
@@ -107,18 +127,19 @@ git clone https://github.com/ErisPulse/ErisPulse.git
|
|
|
107
127
|
cd ErisPulse
|
|
108
128
|
```
|
|
109
129
|
|
|
110
|
-
### 使用 `uv`
|
|
130
|
+
### 使用 `uv` 同步项目环境
|
|
111
131
|
|
|
112
132
|
```bash
|
|
113
|
-
uv
|
|
114
|
-
|
|
115
|
-
|
|
133
|
+
uv sync
|
|
134
|
+
|
|
135
|
+
# 启动虚拟环境
|
|
136
|
+
source .venv/bin/activate
|
|
116
137
|
# Windows: .venv\Scripts\activate
|
|
117
138
|
```
|
|
118
139
|
|
|
119
|
-
>
|
|
140
|
+
> `ErisPulse` 目前正在使用 `python3.13` 进行开发(所以您同步环境时会自动安装 `3.13`),但也可以使用其他版本(版本不应低于 `3.10`)。
|
|
120
141
|
|
|
121
|
-
###
|
|
142
|
+
### 安装依赖并开始
|
|
122
143
|
|
|
123
144
|
```bash
|
|
124
145
|
uv pip install -e .
|
|
@@ -142,24 +163,36 @@ python -c "from ErisPulse import sdk; sdk.init()"
|
|
|
142
163
|
uv run devs/test.py
|
|
143
164
|
```
|
|
144
165
|
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
- 环境配置测试
|
|
166
|
+
测试功能包括:
|
|
167
|
+
- 日志系统测试
|
|
168
|
+
- 环境配置测试
|
|
148
169
|
- 错误管理测试
|
|
149
170
|
- 工具函数测试
|
|
150
171
|
- 适配器功能测试
|
|
151
|
-
- 版本信息查看
|
|
152
|
-
|
|
153
|
-
### 开始开发
|
|
154
|
-
|
|
155
|
-
你可以通过 CLI 工具进行模块调试、热重载开发等操作:
|
|
156
172
|
|
|
173
|
+
### 开发模式 (热重载)
|
|
157
174
|
```bash
|
|
158
175
|
epsdk run your_script.py --reload
|
|
159
176
|
```
|
|
160
177
|
|
|
161
178
|
---
|
|
162
179
|
|
|
163
|
-
## 🤝
|
|
180
|
+
## 🤝 贡献指南
|
|
181
|
+
|
|
182
|
+
我们欢迎各种形式的贡献,包括但不限于:
|
|
183
|
+
|
|
184
|
+
1. **报告问题**
|
|
185
|
+
在 [GitHub Issues](https://github.com/ErisPulse/ErisPulse/issues) 提交bug报告
|
|
186
|
+
|
|
187
|
+
2. **功能请求**
|
|
188
|
+
通过 [社区讨论](https://github.com/ErisPulse/ErisPulse/discussions) 提出新想法
|
|
189
|
+
|
|
190
|
+
3. **代码贡献**
|
|
191
|
+
提交 Pull Request 前请阅读我们的 [开发指南](docs/DEVELOPMENT.md)
|
|
192
|
+
|
|
193
|
+
4. **文档改进**
|
|
194
|
+
帮助完善文档和示例代码
|
|
195
|
+
|
|
196
|
+
---
|
|
164
197
|
|
|
165
|
-
|
|
198
|
+
[加入社区讨论 →](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=FZUyXv8ZCO-xKzemSgp0Rw_snH_6BwI5i0F4sJu-5fs,8841
|
|
2
|
+
ErisPulse/__main__.py,sha256=D718GwmJh9u1ce7u3f8wCxjB-VznwaWL_awSeHufq9M,52870
|
|
3
|
+
ErisPulse/adapter.py,sha256=rrBdPSHvOzzN0VJCD8lThygM9I-_iQIeZ5iMgqCJ34w,13977
|
|
4
|
+
ErisPulse/db.py,sha256=4qYFyOmgp_04xnrlO2Htyr5z6cjh6LtFyJa3zz0oTLw,24173
|
|
5
|
+
ErisPulse/logger.py,sha256=UfQBF9fLx4WKe_se_lB5jNvaeTDtpv5PKT9x49QMmyw,8152
|
|
6
|
+
ErisPulse/mods.py,sha256=1vEFiuL61Ouu7cocyysev5k2lGqcJ_hJGZqXNNbhJqg,9936
|
|
7
|
+
ErisPulse/raiserr.py,sha256=QeKffL8Gm4Vl1DDMFQiKluiKCx0mxgN2Jj98WP4tUHo,3958
|
|
8
|
+
ErisPulse/util.py,sha256=tmmZA4Ef5ElB01KZg2fTZLrkZOJbORjnkAd8sXj7xqw,4335
|
|
9
|
+
erispulse-1.1.16.dist-info/METADATA,sha256=pqBRSDlkJUlODhMbKZEqEGs5Uvn_vvsIMwglS5l90_s,6371
|
|
10
|
+
erispulse-1.1.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
+
erispulse-1.1.16.dist-info/entry_points.txt,sha256=y8ppdsQhk-azC7_ikQgRr4rITSZ4VC4crVKtzHh4TBY,146
|
|
12
|
+
erispulse-1.1.16.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
|
|
13
|
+
erispulse-1.1.16.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=k-X8WV9sIrYeYDNb8qZyBZ8s8yNL2bKWhZdkPbzRWII,8936
|
|
2
|
-
ErisPulse/__main__.py,sha256=KHBHedKC2bYWeqOi2rENgv_l5Pz-d2y7Bf4ST3R0TY4,52862
|
|
3
|
-
ErisPulse/adapter.py,sha256=3DayPbvWit2D8DTLIvhbP3862arv2UZuAkFnE9QWGro,17479
|
|
4
|
-
ErisPulse/db.py,sha256=a_5uTQk8w2TDp6J6AIQ4rF8Z2IvswuSg8q-Tx0SHfGA,24900
|
|
5
|
-
ErisPulse/logger.py,sha256=np6H4-x3xNpmoOGou5znsfv-iVlX56cVrsX8446cY-M,14764
|
|
6
|
-
ErisPulse/mods.py,sha256=oDVgEh56IUTLZDIxxWTQe5QvQSyWVAb8vhgd1fzFlYo,12253
|
|
7
|
-
ErisPulse/raiserr.py,sha256=Un8VGcVYDOSi7ptRVmyBzUTW7mQzDatqIza6919dQ1w,7034
|
|
8
|
-
ErisPulse/util.py,sha256=tmmZA4Ef5ElB01KZg2fTZLrkZOJbORjnkAd8sXj7xqw,4335
|
|
9
|
-
erispulse-1.1.15.dist-info/METADATA,sha256=4gCjt0C4F_qGRW3rKd0Rn9n2Tr9liDB0eCslFwex7zY,5877
|
|
10
|
-
erispulse-1.1.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
-
erispulse-1.1.15.dist-info/entry_points.txt,sha256=y8ppdsQhk-azC7_ikQgRr4rITSZ4VC4crVKtzHh4TBY,146
|
|
12
|
-
erispulse-1.1.15.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
|
|
13
|
-
erispulse-1.1.15.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|