ErisPulse 1.1.15__py3-none-any.whl → 1.2.0__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 +245 -165
- ErisPulse/__main__.py +20 -22
- ErisPulse/adapter.py +0 -125
- ErisPulse/db.py +0 -60
- ErisPulse/logger.py +7 -241
- ErisPulse/mods.py +0 -77
- ErisPulse/raiserr.py +8 -104
- {erispulse-1.1.15.dist-info → erispulse-1.2.0.dist-info}/METADATA +78 -45
- erispulse-1.2.0.dist-info/RECORD +13 -0
- erispulse-1.1.15.dist-info/RECORD +0 -13
- {erispulse-1.1.15.dist-info → erispulse-1.2.0.dist-info}/WHEEL +0 -0
- {erispulse-1.1.15.dist-info → erispulse-1.2.0.dist-info}/entry_points.txt +0 -0
- {erispulse-1.1.15.dist-info → erispulse-1.2.0.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,254 @@ 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():
|
|
71
86
|
try:
|
|
72
|
-
|
|
73
|
-
|
|
87
|
+
raiserr.register(error, doc=doc)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
raise e
|
|
90
|
+
def init_progress():
|
|
91
|
+
from pathlib import Path
|
|
92
|
+
env_file = Path("env.py")
|
|
93
|
+
main_file = Path("main.py")
|
|
94
|
+
|
|
95
|
+
if not env_file.exists():
|
|
96
|
+
content = '''# env.py
|
|
97
|
+
# ErisPulse 环境配置文件
|
|
98
|
+
# 本文件由 SDK 自动创建,请勿随意删除
|
|
99
|
+
# 配置项可通过 sdk.env.get(key, default) 获取,或使用 sdk.env.set(key, value) 设置
|
|
100
|
+
# 你也可以像写普通变量一样直接定义配置项,例如:
|
|
101
|
+
#
|
|
102
|
+
# MY_CONFIG = "value"
|
|
103
|
+
# MY_CONFIG_2 = {"key": "value"}
|
|
104
|
+
# MY_CONFIG_3 = [1, 2, 3]
|
|
105
|
+
#
|
|
106
|
+
# sdk.env.set("MY_CONFIG", "value")
|
|
107
|
+
# sdk.env.set("MY_CONFIG_2", {"key": "value"})
|
|
108
|
+
# sdk.env.set("MY_CONFIG_3", [1, 2, 3])
|
|
109
|
+
#
|
|
110
|
+
# 这些变量会自动被加载到 SDK 的配置系统中,可通过 sdk.env.MY_CONFIG 或 sdk.env.get("MY_CONFIG") 访问。
|
|
111
|
+
|
|
112
|
+
from ErisPulse import sdk
|
|
113
|
+
'''
|
|
114
|
+
if not main_file.exists():
|
|
115
|
+
content += '''# main.py
|
|
116
|
+
# ErisPulse 主程序文件
|
|
117
|
+
# 本文件由 SDK 自动创建,您可随意修改
|
|
118
|
+
|
|
119
|
+
from ErisPulse import sdk
|
|
120
|
+
|
|
121
|
+
async def main():
|
|
122
|
+
try:
|
|
123
|
+
sdk.init()
|
|
124
|
+
await sdk.adapter.startup()
|
|
125
|
+
|
|
126
|
+
except Exception as e:
|
|
127
|
+
sdk.logger.error(e)
|
|
128
|
+
except KeyboardInterrupt:
|
|
129
|
+
sdk.logger.info("正在停止程序")
|
|
130
|
+
finally:
|
|
131
|
+
await sdk.shutdown()
|
|
132
|
+
|
|
133
|
+
if __name__ == "__main__":
|
|
134
|
+
asyncio.run(main())
|
|
135
|
+
'''
|
|
136
|
+
try:
|
|
137
|
+
with open(env_file, "w", encoding="utf-8") as f:
|
|
138
|
+
f.write(content)
|
|
139
|
+
return True
|
|
140
|
+
except Exception as e:
|
|
141
|
+
from . import sdk
|
|
142
|
+
sdk.logger.error(f"无法初始化项目环境: {e}")
|
|
143
|
+
return False
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
def _prepare_environment() -> bool:
|
|
147
|
+
# 检查环境
|
|
148
|
+
logger.info("[Init] 准备初始化环境...")
|
|
149
|
+
try:
|
|
150
|
+
if init_progress():
|
|
74
151
|
logger.info("[Init] 项目首次初始化,建议先配置环境变量")
|
|
75
152
|
if input("是否立即退出?(y/n): ").strip().lower() == "y":
|
|
76
|
-
|
|
153
|
+
return False
|
|
77
154
|
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}")
|
|
155
|
+
return True
|
|
156
|
+
except Exception as e:
|
|
157
|
+
logger.error(f"环境准备失败: {e}")
|
|
158
|
+
return False
|
|
159
|
+
def _scan_modules(module_path: str) -> tuple[dict, list, list]:
|
|
160
|
+
# 扫描并验证模块
|
|
161
|
+
module_objs = {}
|
|
162
|
+
enabled_modules = []
|
|
163
|
+
disabled_modules = []
|
|
164
|
+
|
|
165
|
+
if not os.path.exists(module_path):
|
|
166
|
+
os.makedirs(module_path)
|
|
167
|
+
sys.path.append(module_path)
|
|
168
|
+
|
|
169
|
+
for module_name in os.listdir(module_path):
|
|
170
|
+
if not os.path.isdir(os.path.join(module_path, module_name)):
|
|
171
|
+
continue
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
moduleObj = __import__(module_name)
|
|
175
|
+
if not _validate_module(moduleObj, module_name):
|
|
148
176
|
continue
|
|
149
|
-
|
|
150
|
-
# ==== 构建依赖图并进行拓扑排序 ====
|
|
151
|
-
sdkModuleDependencies = {}
|
|
152
|
-
for module_name in sdkInstalledModuleNames:
|
|
153
|
-
moduleObj = module_objs[module_name]
|
|
177
|
+
|
|
154
178
|
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]
|
|
184
|
-
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, {
|
|
179
|
+
module_info = mods.get_module(meta_name) or {
|
|
203
180
|
"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:
|
|
181
|
+
"info": moduleObj.moduleInfo
|
|
182
|
+
}
|
|
183
|
+
mods.set_module(meta_name, module_info)
|
|
184
|
+
|
|
185
|
+
if not module_info.get('status', True):
|
|
186
|
+
disabled_modules.append(module_name)
|
|
187
|
+
logger.warning(f"模块 {meta_name} 已禁用,跳过加载")
|
|
216
188
|
continue
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
189
|
+
|
|
190
|
+
_check_dependencies(moduleObj, module_name, os.listdir(module_path))
|
|
191
|
+
module_objs[module_name] = moduleObj
|
|
192
|
+
enabled_modules.append(module_name)
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.warning(f"模块 {module_name} 加载失败: {e}")
|
|
196
|
+
|
|
197
|
+
return module_objs, enabled_modules, disabled_modules
|
|
198
|
+
|
|
199
|
+
def _validate_module(moduleObj, module_name: str) -> bool:
|
|
200
|
+
# 验证模块基本结构
|
|
201
|
+
if not hasattr(moduleObj, "moduleInfo") or not isinstance(moduleObj.moduleInfo, dict):
|
|
202
|
+
logger.warning(f"模块 {module_name} 缺少有效的 'moduleInfo' 字典")
|
|
203
|
+
return False
|
|
204
|
+
if "name" not in moduleObj.moduleInfo.get("meta", {}):
|
|
205
|
+
logger.warning(f"模块 {module_name} 缺少必要 'name' 键")
|
|
206
|
+
return False
|
|
207
|
+
if not hasattr(moduleObj, "Main"):
|
|
208
|
+
logger.warning(f"模块 {module_name} 缺少 'Main' 类")
|
|
209
|
+
return False
|
|
210
|
+
return True
|
|
211
|
+
|
|
212
|
+
def _check_dependencies(moduleObj, module_name: str, available_modules: list):
|
|
213
|
+
# 检查模块依赖关系
|
|
214
|
+
required_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
215
|
+
if missing := [dep for dep in required_deps if dep not in available_modules]:
|
|
216
|
+
logger.error(f"模块 {module_name} 缺少必需依赖: {missing}")
|
|
217
|
+
raiserr.MissingDependencyError(f"模块 {module_name} 缺少必需依赖: {missing}")
|
|
218
|
+
|
|
219
|
+
optional_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
|
|
220
|
+
available_optional = [
|
|
221
|
+
dep for dep in optional_deps
|
|
222
|
+
if (isinstance(dep, list) and any(d in available_modules for d in dep))
|
|
223
|
+
or (not isinstance(dep, list) and dep in available_modules)
|
|
224
|
+
]
|
|
225
|
+
if optional_deps and not available_optional:
|
|
226
|
+
logger.warning(f"模块 {module_name} 缺少所有可选依赖: {optional_deps}")
|
|
227
|
+
|
|
228
|
+
def _resolve_dependencies(modules: list, module_objs: dict) -> list:
|
|
229
|
+
# 解析模块依赖关系并进行拓扑排序
|
|
230
|
+
dependencies = {}
|
|
231
|
+
for module_name in modules:
|
|
232
|
+
moduleObj = module_objs[module_name]
|
|
233
|
+
req_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
234
|
+
opt_deps = moduleObj.moduleInfo.get("dependencies", {}).get("optional", [])
|
|
235
|
+
available_opt = [dep for dep in opt_deps if dep in modules]
|
|
236
|
+
dependencies[module_name] = req_deps + available_opt
|
|
237
|
+
|
|
238
|
+
sorted_modules = sdk.util.topological_sort(modules, dependencies, raiserr.CycleDependencyError)
|
|
239
|
+
env.set('module_dependencies', {
|
|
240
|
+
'modules': sorted_modules,
|
|
241
|
+
'dependencies': dependencies
|
|
242
|
+
})
|
|
243
|
+
return sorted_modules
|
|
244
|
+
|
|
245
|
+
def _register_adapters(modules: list, module_objs: dict) -> bool:
|
|
246
|
+
# 注册适配器
|
|
247
|
+
success = True
|
|
248
|
+
logger.debug("[Init] 开始注册适配器...")
|
|
249
|
+
for module_name in modules:
|
|
250
|
+
moduleObj = module_objs[module_name]
|
|
251
|
+
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
252
|
+
|
|
253
|
+
try:
|
|
254
|
+
if hasattr(moduleObj, "adapterInfo") and isinstance(moduleObj.adapterInfo, dict):
|
|
255
|
+
for platform, adapter_class in moduleObj.adapterInfo.items():
|
|
256
|
+
sdk.adapter.register(platform, adapter_class)
|
|
257
|
+
logger.info(f"模块 {meta_name} 注册适配器: {platform}")
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.error(f"模块 {meta_name} 适配器注册失败: {e}")
|
|
260
|
+
success = False
|
|
261
|
+
return success
|
|
262
|
+
|
|
263
|
+
def _initialize_modules(modules: list, module_objs: dict) -> bool:
|
|
264
|
+
# 初始化模块
|
|
265
|
+
success = True
|
|
266
|
+
logger.debug("[Init] 开始实例化模块...")
|
|
267
|
+
for module_name in modules:
|
|
268
|
+
moduleObj = module_objs[module_name]
|
|
269
|
+
meta_name = moduleObj.moduleInfo["meta"]["name"]
|
|
270
|
+
|
|
271
|
+
try:
|
|
272
|
+
if mods.get_module_status(meta_name):
|
|
273
|
+
moduleMain = moduleObj.Main(sdk)
|
|
274
|
+
setattr(moduleMain, "moduleInfo", moduleObj.moduleInfo)
|
|
275
|
+
setattr(sdk, meta_name, moduleMain)
|
|
276
|
+
logger.debug(f"模块 {meta_name} 初始化完成")
|
|
277
|
+
except Exception as e:
|
|
278
|
+
logger.error(f"模块 {meta_name} 初始化失败: {e}")
|
|
279
|
+
success = False
|
|
280
|
+
return success
|
|
281
|
+
|
|
282
|
+
def init() -> bool:
|
|
283
|
+
logger.info("[Init] SDK 正在初始化...")
|
|
284
|
+
try:
|
|
285
|
+
if not _prepare_environment():
|
|
286
|
+
return False
|
|
287
|
+
|
|
288
|
+
module_path = os.path.join(os.path.dirname(__file__), "modules")
|
|
289
|
+
module_objs, enabled_modules, _ = _scan_modules(module_path)
|
|
290
|
+
|
|
291
|
+
if not enabled_modules:
|
|
292
|
+
logger.warning("没有找到可用的模块")
|
|
293
|
+
return True
|
|
294
|
+
|
|
295
|
+
sorted_modules = _resolve_dependencies(enabled_modules, module_objs)
|
|
296
|
+
if not _register_adapters(sorted_modules, module_objs):
|
|
297
|
+
return False
|
|
298
|
+
|
|
299
|
+
return _initialize_modules(sorted_modules, module_objs)
|
|
300
|
+
|
|
222
301
|
except Exception as e:
|
|
302
|
+
logger.critical(f"SDK初始化严重错误: {e}")
|
|
223
303
|
raiserr.InitError(f"sdk初始化失败: {e}", exit=True)
|
|
304
|
+
return False
|
|
224
305
|
|
|
225
|
-
|
|
226
|
-
sdk.init = init
|
|
306
|
+
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
|
|
@@ -550,29 +551,27 @@ def install_pip_dependencies(dependencies):
|
|
|
550
551
|
return False
|
|
551
552
|
|
|
552
553
|
def install_local_module(module_path, force=False):
|
|
553
|
-
"""安装本地目录中的模块"""
|
|
554
|
-
module_path = os.path.abspath(module_path)
|
|
555
|
-
if not os.path.exists(module_path):
|
|
556
|
-
shellprint.panel(f"路径不存在: {module_path}", "错误", "error")
|
|
557
|
-
return False
|
|
558
|
-
|
|
559
|
-
# 尝试从目录名获取模块名
|
|
560
|
-
module_name = os.path.basename(module_path.rstrip('/\\'))
|
|
561
|
-
|
|
562
|
-
# 检查是否是有效的模块目录
|
|
563
|
-
init_py = os.path.join(module_path, '__init__.py')
|
|
564
|
-
if not os.path.exists(init_py):
|
|
565
|
-
shellprint.panel(f"目录 {module_path} 不是一个有效的Python模块", "错误", "error")
|
|
566
|
-
return False
|
|
567
|
-
|
|
568
|
-
# 尝试导入模块获取moduleInfo
|
|
569
554
|
try:
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
if not hasattr(module, 'moduleInfo'):
|
|
574
|
-
shellprint.panel(f"模块 {module_name} 缺少 moduleInfo 定义", "错误", "error")
|
|
555
|
+
module_path = os.path.abspath(module_path)
|
|
556
|
+
if not os.path.exists(module_path):
|
|
557
|
+
shellprint.panel(f"路径不存在: {module_path}", "错误", "error")
|
|
575
558
|
return False
|
|
559
|
+
|
|
560
|
+
module_name = os.path.basename(module_path.rstrip('/\\'))
|
|
561
|
+
init_py = os.path.join(module_path, '__init__.py')
|
|
562
|
+
|
|
563
|
+
if not os.path.exists(init_py):
|
|
564
|
+
shellprint.panel(f"目录 {module_path} 不是一个有效的Python模块", "错误", "error")
|
|
565
|
+
return False
|
|
566
|
+
import sys
|
|
567
|
+
sys.path.insert(0, os.path.dirname(module_path))
|
|
568
|
+
try:
|
|
569
|
+
module = importlib.import_module(module_name)
|
|
570
|
+
if not hasattr(module, 'moduleInfo'):
|
|
571
|
+
shellprint.panel(f"模块 {module_name} 缺少 moduleInfo 定义", "错误", "error")
|
|
572
|
+
return False
|
|
573
|
+
finally:
|
|
574
|
+
sys.path.remove(os.path.dirname(module_path))
|
|
576
575
|
except Exception as e:
|
|
577
576
|
shellprint.panel(f"导入模块 {module_name} 失败: {e}", "错误", "error")
|
|
578
577
|
return False
|
|
@@ -992,7 +991,6 @@ def list_modules(module_name=None):
|
|
|
992
991
|
|
|
993
992
|
def main():
|
|
994
993
|
parser = argparse.ArgumentParser(
|
|
995
|
-
|
|
996
994
|
prog="epsdk",
|
|
997
995
|
formatter_class=argparse.RawTextHelpFormatter
|
|
998
996
|
)
|
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
|