ErisPulse 1.1.14.dev1__py3-none-any.whl → 1.1.15__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/__main__.py +73 -1
- ErisPulse/adapter.py +335 -31
- ErisPulse/db.py +436 -36
- ErisPulse/logger.py +303 -25
- ErisPulse/mods.py +312 -19
- ErisPulse/raiserr.py +141 -18
- {erispulse-1.1.14.dev1.dist-info → erispulse-1.1.15.dist-info}/METADATA +1 -1
- erispulse-1.1.15.dist-info/RECORD +13 -0
- {erispulse-1.1.14.dev1.dist-info → erispulse-1.1.15.dist-info}/entry_points.txt +2 -2
- erispulse-1.1.14.dev1.dist-info/RECORD +0 -13
- {erispulse-1.1.14.dev1.dist-info → erispulse-1.1.15.dist-info}/WHEEL +0 -0
- {erispulse-1.1.14.dev1.dist-info → erispulse-1.1.15.dist-info}/licenses/LICENSE +0 -0
ErisPulse/__main__.py
CHANGED
|
@@ -549,7 +549,74 @@ def install_pip_dependencies(dependencies):
|
|
|
549
549
|
shellprint.panel(f"安装pip依赖失败: {e.stderr}", "错误", "error")
|
|
550
550
|
return False
|
|
551
551
|
|
|
552
|
+
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
|
+
try:
|
|
570
|
+
spec = importlib.util.spec_from_file_location(module_name, init_py)
|
|
571
|
+
module = importlib.util.module_from_spec(spec)
|
|
572
|
+
spec.loader.exec_module(module)
|
|
573
|
+
if not hasattr(module, 'moduleInfo'):
|
|
574
|
+
shellprint.panel(f"模块 {module_name} 缺少 moduleInfo 定义", "错误", "error")
|
|
575
|
+
return False
|
|
576
|
+
except Exception as e:
|
|
577
|
+
shellprint.panel(f"导入模块 {module_name} 失败: {e}", "错误", "error")
|
|
578
|
+
return False
|
|
579
|
+
|
|
580
|
+
module_info = mods.get_module(module_name)
|
|
581
|
+
if module_info and not force:
|
|
582
|
+
meta = module_info.get('info', {}).get('meta', {})
|
|
583
|
+
shellprint.panel(
|
|
584
|
+
f"{Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}\n版本: {meta.get('version', '未知')}\n描述: {meta.get('description', '无描述')}",
|
|
585
|
+
"模块已存在",
|
|
586
|
+
"info"
|
|
587
|
+
)
|
|
588
|
+
if not shellprint.confirm("是否要强制重新安装?", default=False):
|
|
589
|
+
return False
|
|
590
|
+
|
|
591
|
+
# 复制模块到modules目录
|
|
592
|
+
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
593
|
+
target_dir = os.path.join(script_dir, 'modules', module_name)
|
|
594
|
+
|
|
595
|
+
try:
|
|
596
|
+
if os.path.exists(target_dir):
|
|
597
|
+
shutil.rmtree(target_dir)
|
|
598
|
+
shutil.copytree(module_path, target_dir)
|
|
599
|
+
except Exception as e:
|
|
600
|
+
shellprint.panel(f"复制模块文件失败: {e}", "错误", "error")
|
|
601
|
+
return False
|
|
602
|
+
|
|
603
|
+
# 注册模块信息
|
|
604
|
+
mods.set_module(module_name, {
|
|
605
|
+
'status': True,
|
|
606
|
+
'info': {
|
|
607
|
+
'meta': module.moduleInfo.get('meta', {}),
|
|
608
|
+
'dependencies': module.moduleInfo.get('dependencies', {})
|
|
609
|
+
}
|
|
610
|
+
})
|
|
611
|
+
|
|
612
|
+
shellprint.panel(f"本地模块 {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET} 安装成功", "成功", "success")
|
|
613
|
+
return True
|
|
614
|
+
|
|
552
615
|
def install_module(module_name, force=False):
|
|
616
|
+
# 检查是否是本地路径
|
|
617
|
+
if module_name.startswith('.') or os.path.isabs(module_name):
|
|
618
|
+
return install_local_module(module_name, force)
|
|
619
|
+
|
|
553
620
|
shellprint.panel(f"准备安装模块: {Shell_Printer.BOLD}{module_name}{Shell_Printer.RESET}", "安装摘要", "info")
|
|
554
621
|
last_update_time = env.get('last_origin_update_time', None)
|
|
555
622
|
if last_update_time:
|
|
@@ -1147,7 +1214,12 @@ def main():
|
|
|
1147
1214
|
else:
|
|
1148
1215
|
shellprint.panel(f"运行脚本: {Shell_Printer.BOLD}{script_path}{Shell_Printer.RESET}", "执行", "info")
|
|
1149
1216
|
import runpy
|
|
1150
|
-
|
|
1217
|
+
|
|
1218
|
+
# 添加KeyboardInterrupt异常捕捉
|
|
1219
|
+
try:
|
|
1220
|
+
runpy.run_path(script_path, run_name="__main__")
|
|
1221
|
+
except KeyboardInterrupt:
|
|
1222
|
+
shellprint.panel("脚本执行已中断", "中断", "info")
|
|
1151
1223
|
|
|
1152
1224
|
elif args.command == 'origin':
|
|
1153
1225
|
if args.origin_command == 'add':
|
ErisPulse/adapter.py
CHANGED
|
@@ -1,48 +1,352 @@
|
|
|
1
1
|
"""
|
|
2
2
|
# 适配器系统
|
|
3
3
|
|
|
4
|
-
提供平台适配器基类、消息发送DSL
|
|
4
|
+
提供平台适配器基类、消息发送DSL和适配器管理功能。支持多平台消息处理、事件驱动和生命周期管理。
|
|
5
5
|
|
|
6
|
-
##
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
## 核心功能
|
|
7
|
+
1. 适配器基类定义
|
|
8
|
+
2. 链式消息发送DSL
|
|
9
|
+
3. 适配器注册和管理
|
|
10
|
+
4. 事件处理系统
|
|
11
|
+
5. 中间件支持
|
|
11
12
|
|
|
12
13
|
## API 文档
|
|
13
|
-
### 适配器基类 (BaseAdapter):
|
|
14
|
-
- call_api(): 必须实现的API调用方法
|
|
15
|
-
- start(): 启动适配器
|
|
16
|
-
- shutdown(): 关闭适配器
|
|
17
|
-
- on(): 事件监听装饰器
|
|
18
|
-
- emit(): 触发事件
|
|
19
|
-
|
|
20
|
-
### 消息发送DSL (SendDSL):
|
|
21
|
-
- To(): 设置消息目标
|
|
22
|
-
- Text(): 发送文本消息
|
|
23
|
-
- 可扩展其他消息类型
|
|
24
|
-
|
|
25
|
-
### 适配器管理 (AdapterManager):
|
|
26
|
-
- register(): 注册适配器
|
|
27
|
-
- startup(): 启动适配器
|
|
28
|
-
- shutdown(): 关闭所有适配器
|
|
29
|
-
- get(): 获取适配器实例
|
|
30
|
-
|
|
31
|
-
### 示例用法:
|
|
32
14
|
|
|
15
|
+
### 适配器基类 (BaseAdapter)
|
|
16
|
+
适配器基类提供了与外部平台交互的标准接口。
|
|
17
|
+
|
|
18
|
+
#### call_api(endpoint: str, **params) -> Any
|
|
19
|
+
调用平台API的抽象方法。
|
|
20
|
+
- 参数:
|
|
21
|
+
- endpoint: API端点
|
|
22
|
+
- **params: API参数
|
|
23
|
+
- 返回:
|
|
24
|
+
- Any: API调用结果
|
|
25
|
+
- 说明:
|
|
26
|
+
- 必须由子类实现
|
|
27
|
+
- 处理与平台的实际通信
|
|
28
|
+
- 示例:
|
|
29
|
+
```python
|
|
30
|
+
class MyPlatformAdapter(BaseAdapter):
|
|
31
|
+
async def call_api(self, endpoint: str, **params):
|
|
32
|
+
if endpoint == "/send":
|
|
33
|
+
return await self._send_message(params)
|
|
34
|
+
elif endpoint == "/upload":
|
|
35
|
+
return await self._upload_file(params)
|
|
36
|
+
raise NotImplementedError(f"未实现的端点: {endpoint}")
|
|
33
37
|
```
|
|
34
|
-
from ErisPulse import sdk
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
#### start() -> None
|
|
40
|
+
启动适配器的抽象方法。
|
|
41
|
+
- 参数: 无
|
|
42
|
+
- 返回:
|
|
43
|
+
- None
|
|
44
|
+
- 说明:
|
|
45
|
+
- 必须由子类实现
|
|
46
|
+
- 处理适配器的初始化和启动逻辑
|
|
47
|
+
- 示例:
|
|
48
|
+
```python
|
|
49
|
+
class MyPlatformAdapter(BaseAdapter):
|
|
50
|
+
async def start(self):
|
|
51
|
+
self.client = await self._create_client()
|
|
52
|
+
self.ws = await self.client.create_websocket()
|
|
53
|
+
self._start_heartbeat()
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### shutdown() -> None
|
|
57
|
+
关闭适配器的抽象方法。
|
|
58
|
+
- 参数: 无
|
|
59
|
+
- 返回:
|
|
60
|
+
- None
|
|
61
|
+
- 说明:
|
|
62
|
+
- 必须由子类实现
|
|
63
|
+
- 处理资源清理和关闭逻辑
|
|
64
|
+
- 示例:
|
|
65
|
+
```python
|
|
66
|
+
class MyPlatformAdapter(BaseAdapter):
|
|
67
|
+
async def shutdown(self):
|
|
68
|
+
if self.ws:
|
|
69
|
+
await self.ws.close()
|
|
70
|
+
if self.client:
|
|
71
|
+
await self.client.close()
|
|
72
|
+
```
|
|
38
73
|
|
|
39
|
-
|
|
40
|
-
|
|
74
|
+
#### on(event_type: str = "*") -> Callable
|
|
75
|
+
事件监听装饰器。
|
|
76
|
+
- 参数:
|
|
77
|
+
- event_type: 事件类型,默认"*"表示所有事件
|
|
78
|
+
- 返回:
|
|
79
|
+
- Callable: 装饰器函数
|
|
80
|
+
- 示例:
|
|
81
|
+
```python
|
|
82
|
+
adapter = MyPlatformAdapter()
|
|
83
|
+
|
|
84
|
+
@adapter.on("message")
|
|
85
|
+
async def handle_message(data):
|
|
86
|
+
print(f"收到消息: {data}")
|
|
87
|
+
|
|
88
|
+
@adapter.on("error")
|
|
89
|
+
async def handle_error(error):
|
|
90
|
+
print(f"发生错误: {error}")
|
|
91
|
+
|
|
92
|
+
# 处理所有事件
|
|
93
|
+
@adapter.on()
|
|
94
|
+
async def handle_all(event):
|
|
95
|
+
print(f"事件: {event}")
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### emit(event_type: str, data: Any) -> None
|
|
99
|
+
触发事件。
|
|
100
|
+
- 参数:
|
|
101
|
+
- event_type: 事件类型
|
|
102
|
+
- data: 事件数据
|
|
103
|
+
- 返回:
|
|
104
|
+
- None
|
|
105
|
+
- 示例:
|
|
106
|
+
```python
|
|
107
|
+
class MyPlatformAdapter(BaseAdapter):
|
|
108
|
+
async def _handle_websocket_message(self, message):
|
|
109
|
+
# 处理消息并触发相应事件
|
|
110
|
+
if message.type == "chat":
|
|
111
|
+
await self.emit("message", {
|
|
112
|
+
"type": "chat",
|
|
113
|
+
"content": message.content,
|
|
114
|
+
"sender": message.sender
|
|
115
|
+
})
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### middleware(func: Callable) -> Callable
|
|
119
|
+
添加中间件处理器。
|
|
120
|
+
- 参数:
|
|
121
|
+
- func: 中间件函数
|
|
122
|
+
- 返回:
|
|
123
|
+
- Callable: 中间件函数
|
|
124
|
+
- 示例:
|
|
125
|
+
```python
|
|
126
|
+
adapter = MyPlatformAdapter()
|
|
127
|
+
|
|
128
|
+
@adapter.middleware
|
|
129
|
+
async def log_middleware(data):
|
|
130
|
+
print(f"处理数据: {data}")
|
|
131
|
+
return data
|
|
132
|
+
|
|
133
|
+
@adapter.middleware
|
|
134
|
+
async def filter_middleware(data):
|
|
135
|
+
if "spam" in data.get("content", ""):
|
|
136
|
+
return None
|
|
137
|
+
return data
|
|
138
|
+
```
|
|
41
139
|
|
|
42
|
-
|
|
140
|
+
### 消息发送DSL (SendDSL)
|
|
141
|
+
提供链式调用风格的消息发送接口。
|
|
142
|
+
|
|
143
|
+
#### To(target_type: str = None, target_id: str = None) -> 'SendDSL'
|
|
144
|
+
设置消息目标。
|
|
145
|
+
- 参数:
|
|
146
|
+
- target_type: 目标类型(可选)
|
|
147
|
+
- target_id: 目标ID
|
|
148
|
+
- 返回:
|
|
149
|
+
- SendDSL: 发送器实例
|
|
150
|
+
- 示例:
|
|
151
|
+
```python
|
|
152
|
+
# 发送到用户
|
|
153
|
+
sdk.adapter.Platform.Send.To("user", "123").Text("Hello")
|
|
154
|
+
|
|
155
|
+
# 发送到群组
|
|
156
|
+
sdk.adapter.Platform.Send.To("group", "456").Text("Hello Group")
|
|
157
|
+
|
|
158
|
+
# 简化形式(只有ID)
|
|
159
|
+
sdk.adapter.Platform.Send.To("123").Text("Hello")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
#### Text(text: str) -> Task
|
|
163
|
+
发送文本消息。
|
|
164
|
+
- 参数:
|
|
165
|
+
- text: 文本内容
|
|
166
|
+
- 返回:
|
|
167
|
+
- Task: 异步任务
|
|
168
|
+
- 示例:
|
|
169
|
+
```python
|
|
170
|
+
# 发送简单文本
|
|
171
|
+
await sdk.adapter.Platform.Send.To("user", "123").Text("Hello")
|
|
172
|
+
|
|
173
|
+
# 发送格式化文本
|
|
174
|
+
name = "Alice"
|
|
175
|
+
await sdk.adapter.Platform.Send.To("123").Text(f"Hello {name}")
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 适配器管理 (AdapterManager)
|
|
179
|
+
管理多个平台适配器的注册、启动和关闭。
|
|
180
|
+
|
|
181
|
+
#### register(platform: str, adapter_class: Type[BaseAdapter]) -> bool
|
|
182
|
+
注册新的适配器类。
|
|
183
|
+
- 参数:
|
|
184
|
+
- platform: 平台名称
|
|
185
|
+
- adapter_class: 适配器类
|
|
186
|
+
- 返回:
|
|
187
|
+
- bool: 注册是否成功
|
|
188
|
+
- 示例:
|
|
189
|
+
```python
|
|
190
|
+
# 注册适配器
|
|
191
|
+
sdk.adapter.register("MyPlatform", MyPlatformAdapter)
|
|
192
|
+
|
|
193
|
+
# 注册多个适配器
|
|
194
|
+
adapters = {
|
|
195
|
+
"Platform1": Platform1Adapter,
|
|
196
|
+
"Platform2": Platform2Adapter
|
|
197
|
+
}
|
|
198
|
+
for name, adapter in adapters.items():
|
|
199
|
+
sdk.adapter.register(name, adapter)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
#### startup(platforms: List[str] = None) -> None
|
|
203
|
+
启动指定的适配器。
|
|
204
|
+
- 参数:
|
|
205
|
+
- platforms: 要启动的平台列表,None表示所有平台
|
|
206
|
+
- 返回:
|
|
207
|
+
- None
|
|
208
|
+
- 示例:
|
|
209
|
+
```python
|
|
210
|
+
# 启动所有适配器
|
|
43
211
|
await sdk.adapter.startup()
|
|
212
|
+
|
|
213
|
+
# 启动指定适配器
|
|
214
|
+
await sdk.adapter.startup(["Platform1", "Platform2"])
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
#### shutdown() -> None
|
|
218
|
+
关闭所有适配器。
|
|
219
|
+
- 参数: 无
|
|
220
|
+
- 返回:
|
|
221
|
+
- None
|
|
222
|
+
- 示例:
|
|
223
|
+
```python
|
|
224
|
+
# 关闭所有适配器
|
|
225
|
+
await sdk.adapter.shutdown()
|
|
226
|
+
|
|
227
|
+
# 在程序退出时关闭
|
|
228
|
+
import atexit
|
|
229
|
+
atexit.register(lambda: asyncio.run(sdk.adapter.shutdown()))
|
|
230
|
+
```
|
|
231
|
+
|
|
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)
|
|
44
323
|
```
|
|
45
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
|
+
- 注意资源泄漏问题
|
|
46
350
|
"""
|
|
47
351
|
|
|
48
352
|
import functools
|