ErisPulse 1.2.3__py3-none-any.whl → 1.2.6__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 +11 -9
- ErisPulse/__main__.py +52 -2
- ErisPulse/adapter.py +23 -20
- ErisPulse/db.py +85 -67
- ErisPulse/logger.py +27 -14
- ErisPulse/mods.py +2 -2
- ErisPulse/raiserr.py +5 -4
- ErisPulse/util.py +7 -6
- {erispulse-1.2.3.dist-info → erispulse-1.2.6.dist-info}/METADATA +5 -4
- erispulse-1.2.6.dist-info/RECORD +13 -0
- erispulse-1.2.3.dist-info/RECORD +0 -13
- {erispulse-1.2.3.dist-info → erispulse-1.2.6.dist-info}/WHEEL +0 -0
- {erispulse-1.2.3.dist-info → erispulse-1.2.6.dist-info}/entry_points.txt +0 -0
- {erispulse-1.2.3.dist-info → erispulse-1.2.6.dist-info}/licenses/LICENSE +0 -0
ErisPulse/__init__.py
CHANGED
|
@@ -38,6 +38,7 @@ sdk.logger.info("SDK已初始化")
|
|
|
38
38
|
|
|
39
39
|
import os
|
|
40
40
|
import sys
|
|
41
|
+
from typing import Tuple, Dict, List, Any, Optional, Set, Union, Type, FrozenSet
|
|
41
42
|
|
|
42
43
|
# 依赖类型
|
|
43
44
|
import types
|
|
@@ -48,7 +49,7 @@ from .raiserr import raiserr
|
|
|
48
49
|
from .logger import logger
|
|
49
50
|
from .db import env
|
|
50
51
|
from .mods import mods
|
|
51
|
-
from .adapter import adapter,
|
|
52
|
+
from .adapter import adapter, AdapterFather, SendDSL
|
|
52
53
|
|
|
53
54
|
# 这里不能删,确保windows下的shell能正确显示颜色
|
|
54
55
|
os.system('')
|
|
@@ -58,12 +59,13 @@ sdk = types.SimpleNamespace()
|
|
|
58
59
|
BaseModules = {
|
|
59
60
|
"util" : util,
|
|
60
61
|
"logger" : logger,
|
|
62
|
+
"raiserr" : raiserr,
|
|
61
63
|
"env" : env,
|
|
62
64
|
"mods" : mods,
|
|
63
65
|
"adapter" : adapter,
|
|
64
|
-
"SendDSL" : SendDSL,
|
|
65
|
-
"AdapterFather" :
|
|
66
|
-
"BaseAdapter" :
|
|
66
|
+
"SendDSL" : SendDSL, # 链式发送基类 - 兼容原 sdk.SendDSL | 待弃用, 需要在 Adapter继承类中手动创建嵌套类并集成 super().SendDSL()
|
|
67
|
+
"AdapterFather" : AdapterFather,
|
|
68
|
+
"BaseAdapter" : AdapterFather
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
BaseErrors = {
|
|
@@ -87,7 +89,7 @@ for error, doc in BaseErrors.items():
|
|
|
87
89
|
raiserr.register(error, doc=doc)
|
|
88
90
|
except Exception as e:
|
|
89
91
|
raise e
|
|
90
|
-
def init_progress():
|
|
92
|
+
def init_progress() -> Tuple[bool, bool]:
|
|
91
93
|
from pathlib import Path
|
|
92
94
|
env_file = Path("env.py")
|
|
93
95
|
main_file = Path("main.py")
|
|
@@ -149,7 +151,6 @@ if __name__ == "__main__":
|
|
|
149
151
|
return False
|
|
150
152
|
|
|
151
153
|
def _prepare_environment() -> bool:
|
|
152
|
-
# 检查环境
|
|
153
154
|
logger.info("[Init] 准备初始化环境...")
|
|
154
155
|
try:
|
|
155
156
|
env_init, main_init = init_progress()
|
|
@@ -166,7 +167,8 @@ def _prepare_environment() -> bool:
|
|
|
166
167
|
except Exception as e:
|
|
167
168
|
logger.error(f"环境准备失败: {e}")
|
|
168
169
|
return False
|
|
169
|
-
def _scan_modules(module_path: str) ->
|
|
170
|
+
def _scan_modules(module_path: str) -> Tuple[Dict, List, List]:
|
|
171
|
+
|
|
170
172
|
# 扫描并验证模块
|
|
171
173
|
module_objs = {}
|
|
172
174
|
enabled_modules = []
|
|
@@ -219,7 +221,7 @@ def _validate_module(moduleObj, module_name: str) -> bool:
|
|
|
219
221
|
return False
|
|
220
222
|
return True
|
|
221
223
|
|
|
222
|
-
def _check_dependencies(moduleObj, module_name: str, available_modules: list):
|
|
224
|
+
def _check_dependencies(moduleObj, module_name: str, available_modules: list) -> None:
|
|
223
225
|
# 检查模块依赖关系
|
|
224
226
|
required_deps = moduleObj.moduleInfo.get("dependencies", {}).get("requires", [])
|
|
225
227
|
if missing := [dep for dep in required_deps if dep not in available_modules]:
|
|
@@ -313,4 +315,4 @@ def init() -> bool:
|
|
|
313
315
|
raiserr.InitError(f"sdk初始化失败: {e}", exit=True)
|
|
314
316
|
return False
|
|
315
317
|
|
|
316
|
-
sdk.init = init
|
|
318
|
+
sdk.init = init
|
ErisPulse/__main__.py
CHANGED
|
@@ -203,7 +203,7 @@ class Shell_Printer:
|
|
|
203
203
|
print(f"{cls.YELLOW}请输入 'y' 或 'n'{cls.RESET}")
|
|
204
204
|
|
|
205
205
|
@classmethod
|
|
206
|
-
def ask(cls, msg, choices=None, default=
|
|
206
|
+
def ask(cls, msg, choices=None, default="") -> str:
|
|
207
207
|
prompt = f"{cls.BOLD}{msg}{cls.RESET}"
|
|
208
208
|
if choices:
|
|
209
209
|
prompt += f" ({cls.CYAN}{'/'.join(choices)}{cls.RESET})"
|
|
@@ -909,11 +909,56 @@ def upgrade_all_modules(force=False):
|
|
|
909
909
|
|
|
910
910
|
for i, update in enumerate(updates_available, 1):
|
|
911
911
|
print(f"\n{Shell_Printer.BOLD}[{i}/{len(updates_available)}]{Shell_Printer.RESET} 更新模块 {Shell_Printer.BOLD}{update['name']}{Shell_Printer.RESET}")
|
|
912
|
+
|
|
913
|
+
# 检查新版本的依赖
|
|
914
|
+
module_key = f"{update['name']}@{update['provider']}"
|
|
915
|
+
new_module_info = modules_data[module_key]
|
|
916
|
+
new_dependencies = new_module_info.get('dependencies', {}).get('requires', [])
|
|
917
|
+
|
|
918
|
+
# 检查缺失的依赖
|
|
919
|
+
missing_deps = []
|
|
920
|
+
for dep in new_dependencies:
|
|
921
|
+
if dep not in all_modules or not all_modules[dep].get('status', True):
|
|
922
|
+
missing_deps.append(dep)
|
|
923
|
+
|
|
924
|
+
if missing_deps:
|
|
925
|
+
shellprint.panel(
|
|
926
|
+
f"模块 {update['name']} 需要以下依赖:\n{Shell_Printer.YELLOW}{', '.join(missing_deps)}{Shell_Printer.RESET}",
|
|
927
|
+
"缺失依赖",
|
|
928
|
+
"warning"
|
|
929
|
+
)
|
|
930
|
+
if not shellprint.confirm("是否安装这些依赖?", default=True):
|
|
931
|
+
print(f"{Shell_Printer.BLUE}跳过模块 {update['name']} 的更新{Shell_Printer.RESET}")
|
|
932
|
+
continue
|
|
933
|
+
|
|
934
|
+
for dep in missing_deps:
|
|
935
|
+
print(f"\n{Shell_Printer.BOLD}安装依赖: {dep}{Shell_Printer.RESET}")
|
|
936
|
+
install_module(dep)
|
|
937
|
+
|
|
912
938
|
module_url = update['url'] + update['path']
|
|
913
939
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
914
940
|
module_dir = os.path.join(script_dir, 'modules', update['name'])
|
|
915
941
|
zip_path = os.path.join(script_dir, f"{update['name']}.zip")
|
|
916
942
|
|
|
943
|
+
# 检查新版本的pip依赖
|
|
944
|
+
new_pip_deps = new_module_info.get('dependencies', {}).get('pip', [])
|
|
945
|
+
current_pip_deps = all_modules[update['name']].get('info', {}).get('dependencies', {}).get('pip', [])
|
|
946
|
+
added_pip_deps = [dep for dep in new_pip_deps if dep not in current_pip_deps]
|
|
947
|
+
|
|
948
|
+
if added_pip_deps:
|
|
949
|
+
shellprint.panel(
|
|
950
|
+
f"模块 {update['name']} 需要以下新的pip依赖:\n{Shell_Printer.YELLOW}{', '.join(added_pip_deps)}{Shell_Printer.RESET}",
|
|
951
|
+
"新增pip依赖",
|
|
952
|
+
"warning"
|
|
953
|
+
)
|
|
954
|
+
if not shellprint.confirm("是否安装这些pip依赖?", default=True):
|
|
955
|
+
print(f"{Shell_Printer.BLUE}跳过模块 {update['name']} 的更新{Shell_Printer.RESET}")
|
|
956
|
+
continue
|
|
957
|
+
|
|
958
|
+
if not install_pip_dependencies(added_pip_deps):
|
|
959
|
+
print(f"{Shell_Printer.RED}无法安装模块 {update['name']} 的pip依赖,更新终止{Shell_Printer.RESET}")
|
|
960
|
+
continue
|
|
961
|
+
|
|
917
962
|
if not extract_and_setup_module(
|
|
918
963
|
module_name=update['name'],
|
|
919
964
|
module_url=module_url,
|
|
@@ -922,7 +967,12 @@ def upgrade_all_modules(force=False):
|
|
|
922
967
|
):
|
|
923
968
|
continue
|
|
924
969
|
|
|
970
|
+
# 更新模块信息,包括新的依赖
|
|
925
971
|
all_modules[update['name']]['info']['version'] = update['remote_version']
|
|
972
|
+
all_modules[update['name']]['info']['dependencies'] = {
|
|
973
|
+
'requires': new_dependencies,
|
|
974
|
+
'pip': new_pip_deps
|
|
975
|
+
}
|
|
926
976
|
mods.set_all_modules(all_modules)
|
|
927
977
|
print(f"{Shell_Printer.GREEN}模块 {update['name']} 已更新至版本 {update['remote_version']}{Shell_Printer.RESET}")
|
|
928
978
|
|
|
@@ -1264,4 +1314,4 @@ def main():
|
|
|
1264
1314
|
parser.print_help()
|
|
1265
1315
|
|
|
1266
1316
|
if __name__ == "__main__":
|
|
1267
|
-
main()
|
|
1317
|
+
main()
|
ErisPulse/adapter.py
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
### 适配器基类 (BaseAdapter)
|
|
9
9
|
适配器基类提供了与外部平台交互的标准接口。
|
|
10
10
|
|
|
11
|
-
#### call_api(endpoint: str, **params) -> Any
|
|
11
|
+
#### call_api(endpoint: str, **params: Any) -> Any
|
|
12
12
|
调用平台API的抽象方法。
|
|
13
13
|
- 参数:
|
|
14
14
|
- endpoint: API端点
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
- 示例:
|
|
22
22
|
```python
|
|
23
23
|
class MyPlatformAdapter(BaseAdapter):
|
|
24
|
-
async def call_api(self, endpoint: str, **params):
|
|
24
|
+
async def call_api(self, endpoint: str, **params: Any) -> Any:
|
|
25
25
|
if endpoint == "/send":
|
|
26
26
|
return await self._send_message(params)
|
|
27
27
|
elif endpoint == "/upload":
|
|
@@ -40,7 +40,7 @@ class MyPlatformAdapter(BaseAdapter):
|
|
|
40
40
|
- 示例:
|
|
41
41
|
```python
|
|
42
42
|
class MyPlatformAdapter(BaseAdapter):
|
|
43
|
-
async def start(self):
|
|
43
|
+
async def start(self) -> None:
|
|
44
44
|
self.client = await self._create_client()
|
|
45
45
|
self.ws = await self.client.create_websocket()
|
|
46
46
|
self._start_heartbeat()
|
|
@@ -57,34 +57,34 @@ class MyPlatformAdapter(BaseAdapter):
|
|
|
57
57
|
- 示例:
|
|
58
58
|
```python
|
|
59
59
|
class MyPlatformAdapter(BaseAdapter):
|
|
60
|
-
async def shutdown(self):
|
|
60
|
+
async def shutdown(self) -> None:
|
|
61
61
|
if self.ws:
|
|
62
62
|
await self.ws.close()
|
|
63
63
|
if self.client:
|
|
64
64
|
await self.client.close()
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
#### on(event_type: str = "*") -> Callable
|
|
67
|
+
#### on(event_type: str = "*") -> Callable[[Callable[..., Any]], Callable[..., Any]]
|
|
68
68
|
事件监听装饰器。
|
|
69
69
|
- 参数:
|
|
70
70
|
- event_type: 事件类型,默认"*"表示所有事件
|
|
71
71
|
- 返回:
|
|
72
|
-
- Callable: 装饰器函数
|
|
72
|
+
- Callable[[Callable[..., Any]], Callable[..., Any]]: 装饰器函数
|
|
73
73
|
- 示例:
|
|
74
74
|
```python
|
|
75
75
|
adapter = MyPlatformAdapter()
|
|
76
76
|
|
|
77
77
|
@adapter.on("message")
|
|
78
|
-
async def handle_message(data):
|
|
78
|
+
async def handle_message(data: Any) -> None:
|
|
79
79
|
print(f"收到消息: {data}")
|
|
80
80
|
|
|
81
81
|
@adapter.on("error")
|
|
82
|
-
async def handle_error(error):
|
|
82
|
+
async def handle_error(error: Exception) -> None:
|
|
83
83
|
print(f"发生错误: {error}")
|
|
84
84
|
|
|
85
85
|
# 处理所有事件
|
|
86
86
|
@adapter.on()
|
|
87
|
-
async def handle_all(event):
|
|
87
|
+
async def handle_all(event: Any) -> None:
|
|
88
88
|
print(f"事件: {event}")
|
|
89
89
|
```
|
|
90
90
|
|
|
@@ -98,7 +98,7 @@ async def handle_all(event):
|
|
|
98
98
|
- 示例:
|
|
99
99
|
```python
|
|
100
100
|
class MyPlatformAdapter(BaseAdapter):
|
|
101
|
-
async def _handle_websocket_message(self, message):
|
|
101
|
+
async def _handle_websocket_message(self, message: Any) -> None:
|
|
102
102
|
# 处理消息并触发相应事件
|
|
103
103
|
if message.type == "chat":
|
|
104
104
|
await self.emit("message", {
|
|
@@ -108,23 +108,23 @@ class MyPlatformAdapter(BaseAdapter):
|
|
|
108
108
|
})
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
#### middleware(func: Callable) -> Callable
|
|
111
|
+
#### middleware(func: Callable[..., Any]) -> Callable[..., Any]
|
|
112
112
|
添加中间件处理器。
|
|
113
113
|
- 参数:
|
|
114
114
|
- func: 中间件函数
|
|
115
115
|
- 返回:
|
|
116
|
-
- Callable: 中间件函数
|
|
116
|
+
- Callable[..., Any]: 中间件函数
|
|
117
117
|
- 示例:
|
|
118
118
|
```python
|
|
119
119
|
adapter = MyPlatformAdapter()
|
|
120
120
|
|
|
121
121
|
@adapter.middleware
|
|
122
|
-
async def log_middleware(data):
|
|
122
|
+
async def log_middleware(data: Any) -> Any:
|
|
123
123
|
print(f"处理数据: {data}")
|
|
124
124
|
return data
|
|
125
125
|
|
|
126
126
|
@adapter.middleware
|
|
127
|
-
async def filter_middleware(data):
|
|
127
|
+
async def filter_middleware(data: Any) -> Optional[Any]:
|
|
128
128
|
if "spam" in data.get("content", ""):
|
|
129
129
|
return None
|
|
130
130
|
return data
|
|
@@ -133,7 +133,7 @@ async def filter_middleware(data):
|
|
|
133
133
|
### 消息发送DSL (SendDSL)
|
|
134
134
|
提供链式调用风格的消息发送接口。
|
|
135
135
|
|
|
136
|
-
#### To(target_type: str = None, target_id: str = None) -> 'SendDSL'
|
|
136
|
+
#### To(target_type: Optional[str] = None, target_id: Optional[str] = None) -> 'SendDSL'
|
|
137
137
|
设置消息目标。
|
|
138
138
|
- 参数:
|
|
139
139
|
- target_type: 目标类型(可选)
|
|
@@ -152,12 +152,12 @@ sdk.adapter.Platform.Send.To("group", "456").Text("Hello Group")
|
|
|
152
152
|
sdk.adapter.Platform.Send.To("123").Text("Hello")
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
#### Text(text: str) -> Task
|
|
155
|
+
#### Text(text: str) -> asyncio.Task
|
|
156
156
|
发送文本消息。
|
|
157
157
|
- 参数:
|
|
158
158
|
- text: 文本内容
|
|
159
159
|
- 返回:
|
|
160
|
-
- Task: 异步任务
|
|
160
|
+
- asyncio.Task: 异步任务
|
|
161
161
|
- 示例:
|
|
162
162
|
```python
|
|
163
163
|
# 发送简单文本
|
|
@@ -192,7 +192,7 @@ for name, adapter in adapters.items():
|
|
|
192
192
|
sdk.adapter.register(name, adapter)
|
|
193
193
|
```
|
|
194
194
|
|
|
195
|
-
#### startup(platforms: List[str] = None) -> None
|
|
195
|
+
#### startup(platforms: Optional[List[str]] = None) -> None
|
|
196
196
|
启动指定的适配器。
|
|
197
197
|
- 参数:
|
|
198
198
|
- platforms: 要启动的平台列表,None表示所有平台
|
|
@@ -226,7 +226,10 @@ atexit.register(lambda: asyncio.run(sdk.adapter.shutdown()))
|
|
|
226
226
|
|
|
227
227
|
import functools
|
|
228
228
|
import asyncio
|
|
229
|
-
from typing import
|
|
229
|
+
from typing import (
|
|
230
|
+
Callable, Any, Dict, List, Type, Optional, Set,
|
|
231
|
+
Union, Awaitable, TypeVar, Generic, Tuple, Coroutine, FrozenSet
|
|
232
|
+
)
|
|
230
233
|
from collections import defaultdict
|
|
231
234
|
|
|
232
235
|
|
|
@@ -457,6 +460,6 @@ class AdapterManager:
|
|
|
457
460
|
def platforms(self) -> list:
|
|
458
461
|
return list(self._adapters.keys())
|
|
459
462
|
|
|
460
|
-
|
|
463
|
+
AdapterFather = BaseAdapter
|
|
461
464
|
adapter = AdapterManager()
|
|
462
465
|
SendDSL = SendDSLBase
|
ErisPulse/db.py
CHANGED
|
@@ -436,6 +436,7 @@ import threading
|
|
|
436
436
|
from pathlib import Path
|
|
437
437
|
from datetime import datetime
|
|
438
438
|
from functools import lru_cache
|
|
439
|
+
from typing import List, Dict, Optional, Any, Set, Tuple, Union, Type, FrozenSet
|
|
439
440
|
from .raiserr import raiserr
|
|
440
441
|
|
|
441
442
|
class EnvManager:
|
|
@@ -479,7 +480,7 @@ class EnvManager:
|
|
|
479
480
|
self._last_snapshot_time = time.time() # 初始化为当前时间
|
|
480
481
|
self._snapshot_interval = 3600 # 默认每小时自动快照
|
|
481
482
|
|
|
482
|
-
def get(self, key, default=None):
|
|
483
|
+
def get(self, key, default=None) -> Any:
|
|
483
484
|
try:
|
|
484
485
|
with sqlite3.connect(self.db_path) as conn:
|
|
485
486
|
cursor = conn.cursor()
|
|
@@ -505,55 +506,65 @@ class EnvManager:
|
|
|
505
506
|
cursor.execute("SELECT key FROM config")
|
|
506
507
|
return [row[0] for row in cursor.fetchall()]
|
|
507
508
|
|
|
508
|
-
def set(self, key, value):
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
509
|
+
def set(self, key, value) -> bool:
|
|
510
|
+
try:
|
|
511
|
+
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
512
|
+
with self.transaction():
|
|
513
|
+
conn = sqlite3.connect(self.db_path)
|
|
514
|
+
cursor = conn.cursor()
|
|
515
|
+
cursor.execute("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)", (key, serialized_value))
|
|
516
|
+
conn.commit()
|
|
517
|
+
conn.close()
|
|
518
|
+
|
|
519
|
+
self._check_auto_snapshot()
|
|
520
|
+
return True
|
|
521
|
+
except Exception as e:
|
|
522
|
+
return False
|
|
519
523
|
|
|
520
524
|
def set_multi(self, items):
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
(key,
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
conn = sqlite3.connect(self.db_path)
|
|
537
|
-
cursor = conn.cursor()
|
|
538
|
-
cursor.execute("DELETE FROM config WHERE key = ?", (key,))
|
|
539
|
-
conn.commit()
|
|
540
|
-
conn.close()
|
|
541
|
-
|
|
542
|
-
self._check_auto_snapshot()
|
|
543
|
-
return True
|
|
544
|
-
|
|
545
|
-
def delete_multi(self, keys):
|
|
546
|
-
with self.transaction():
|
|
547
|
-
conn = sqlite3.connect(self.db_path)
|
|
548
|
-
cursor = conn.cursor()
|
|
549
|
-
cursor.executemany("DELETE FROM config WHERE key = ?", [(k,) for k in keys])
|
|
550
|
-
conn.commit()
|
|
551
|
-
conn.close()
|
|
552
|
-
|
|
553
|
-
self._check_auto_snapshot()
|
|
554
|
-
return True
|
|
525
|
+
try:
|
|
526
|
+
with self.transaction():
|
|
527
|
+
conn = sqlite3.connect(self.db_path)
|
|
528
|
+
cursor = conn.cursor()
|
|
529
|
+
for key, value in items.items():
|
|
530
|
+
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
531
|
+
cursor.execute("INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)",
|
|
532
|
+
(key, serialized_value))
|
|
533
|
+
conn.commit()
|
|
534
|
+
conn.close()
|
|
535
|
+
|
|
536
|
+
self._check_auto_snapshot()
|
|
537
|
+
return True
|
|
538
|
+
except Exception as e:
|
|
539
|
+
return False
|
|
555
540
|
|
|
556
|
-
def
|
|
541
|
+
def delete(self, key) -> bool:
|
|
542
|
+
try:
|
|
543
|
+
with self.transaction():
|
|
544
|
+
conn = sqlite3.connect(self.db_path)
|
|
545
|
+
cursor = conn.cursor()
|
|
546
|
+
cursor.execute("DELETE FROM config WHERE key = ?", (key,))
|
|
547
|
+
conn.commit()
|
|
548
|
+
conn.close()
|
|
549
|
+
|
|
550
|
+
self._check_auto_snapshot()
|
|
551
|
+
return True
|
|
552
|
+
except Exception as e:
|
|
553
|
+
return False
|
|
554
|
+
def delete_multi(self, keys) -> bool:
|
|
555
|
+
try:
|
|
556
|
+
with self.transaction():
|
|
557
|
+
conn = sqlite3.connect(self.db_path)
|
|
558
|
+
cursor = conn.cursor()
|
|
559
|
+
cursor.executemany("DELETE FROM config WHERE key = ?", [(k,) for k in keys])
|
|
560
|
+
conn.commit()
|
|
561
|
+
conn.close()
|
|
562
|
+
|
|
563
|
+
self._check_auto_snapshot()
|
|
564
|
+
return True
|
|
565
|
+
except Exception as e:
|
|
566
|
+
return False
|
|
567
|
+
def get_multi(self, keys) -> dict:
|
|
557
568
|
conn = sqlite3.connect(self.db_path)
|
|
558
569
|
cursor = conn.cursor()
|
|
559
570
|
placeholders = ','.join(['?'] * len(keys))
|
|
@@ -630,22 +641,29 @@ class EnvManager:
|
|
|
630
641
|
def set_snapshot_interval(self, seconds):
|
|
631
642
|
self._snapshot_interval = seconds
|
|
632
643
|
|
|
633
|
-
def clear(self):
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
644
|
+
def clear(self) -> bool:
|
|
645
|
+
try:
|
|
646
|
+
conn = sqlite3.connect(self.db_path)
|
|
647
|
+
cursor = conn.cursor()
|
|
648
|
+
cursor.execute("DELETE FROM config")
|
|
649
|
+
conn.commit()
|
|
650
|
+
conn.close()
|
|
651
|
+
return True
|
|
652
|
+
except Exception as e:
|
|
653
|
+
return False
|
|
654
|
+
def load_env_file(self) -> bool:
|
|
655
|
+
try:
|
|
656
|
+
env_file = Path("env.py")
|
|
657
|
+
if env_file.exists():
|
|
658
|
+
spec = importlib.util.spec_from_file_location("env_module", env_file)
|
|
659
|
+
env_module = importlib.util.module_from_spec(spec)
|
|
660
|
+
spec.loader.exec_module(env_module)
|
|
661
|
+
for key, value in vars(env_module).items():
|
|
662
|
+
if not key.startswith("__") and isinstance(value, (dict, list, str, int, float, bool)):
|
|
663
|
+
self.set(key, value)
|
|
664
|
+
return True
|
|
665
|
+
except Exception as e:
|
|
666
|
+
return False
|
|
649
667
|
def __getattr__(self, key):
|
|
650
668
|
try:
|
|
651
669
|
return self.get(key)
|
|
@@ -660,7 +678,7 @@ class EnvManager:
|
|
|
660
678
|
from .logger import logger
|
|
661
679
|
logger.error(f"设置配置项 {key} 失败: {e}")
|
|
662
680
|
|
|
663
|
-
def snapshot(self, name=None):
|
|
681
|
+
def snapshot(self, name=None) -> str:
|
|
664
682
|
if not name:
|
|
665
683
|
name = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
666
684
|
snapshot_path = os.path.join(self.SNAPSHOT_DIR, f"{name}.db")
|
|
@@ -687,7 +705,7 @@ class EnvManager:
|
|
|
687
705
|
logger.error(f"创建快照失败: {e}")
|
|
688
706
|
raise
|
|
689
707
|
|
|
690
|
-
def restore(self, snapshot_name):
|
|
708
|
+
def restore(self, snapshot_name) -> bool:
|
|
691
709
|
snapshot_path = os.path.join(self.SNAPSHOT_DIR, f"{snapshot_name}.db") \
|
|
692
710
|
if not snapshot_name.endswith('.db') else snapshot_name
|
|
693
711
|
|
|
@@ -716,7 +734,7 @@ class EnvManager:
|
|
|
716
734
|
logger.error(f"恢复快照失败: {e}")
|
|
717
735
|
return False
|
|
718
736
|
|
|
719
|
-
def list_snapshots(self):
|
|
737
|
+
def list_snapshots(self) -> list:
|
|
720
738
|
snapshots = []
|
|
721
739
|
for f in os.listdir(self.SNAPSHOT_DIR):
|
|
722
740
|
if f.endswith('.db'):
|
|
@@ -729,7 +747,7 @@ class EnvManager:
|
|
|
729
747
|
))
|
|
730
748
|
return sorted(snapshots, key=lambda x: x[1], reverse=True)
|
|
731
749
|
|
|
732
|
-
def delete_snapshot(self, snapshot_name):
|
|
750
|
+
def delete_snapshot(self, snapshot_name) -> bool:
|
|
733
751
|
snapshot_path = os.path.join(self.SNAPSHOT_DIR, f"{snapshot_name}.db") \
|
|
734
752
|
if not snapshot_name.endswith('.db') else snapshot_name
|
|
735
753
|
|
ErisPulse/logger.py
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
以debug为例:
|
|
11
11
|
> 此外,还有其他级别的日志记录函数,如info, warning, error, critical等,用法相同。
|
|
12
12
|
|
|
13
|
-
debug(msg: str, *args, **kwargs) -> None
|
|
13
|
+
debug(msg: str, *args: Any, **kwargs: Any) -> None
|
|
14
14
|
|
|
15
15
|
记录调试级别的日志信息。
|
|
16
16
|
- 参数:
|
|
@@ -70,7 +70,7 @@ for module, level in config.get("logging", {}).items():
|
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
### 日志存储和输出
|
|
73
|
-
#### set_output_file(path: str
|
|
73
|
+
#### set_output_file(path: Union[str, List[str]]) -> None
|
|
74
74
|
设置日志输出文件。
|
|
75
75
|
- 参数:
|
|
76
76
|
- path: 日志文件路径,可以是单个字符串或路径列表
|
|
@@ -92,7 +92,7 @@ log_file = f"logs/app_{datetime.now().strftime('%Y%m%d')}.log"
|
|
|
92
92
|
sdk.logger.set_output_file(log_file)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
#### save_logs(path: str
|
|
95
|
+
#### save_logs(path: Union[str, List[str]]) -> None
|
|
96
96
|
保存内存中的日志到文件。
|
|
97
97
|
- 参数:
|
|
98
98
|
- path: 保存路径,可以是单个字符串或路径列表
|
|
@@ -118,6 +118,7 @@ atexit.register(lambda: sdk.logger.save_logs("final_logs.txt"))
|
|
|
118
118
|
import logging
|
|
119
119
|
import inspect
|
|
120
120
|
import datetime
|
|
121
|
+
from typing import List, Dict, Any, Optional, Union, Type, Set, Tuple, FrozenSet
|
|
121
122
|
|
|
122
123
|
class Logger:
|
|
123
124
|
def __init__(self):
|
|
@@ -131,10 +132,16 @@ class Logger:
|
|
|
131
132
|
console_handler.setFormatter(logging.Formatter("%(message)s"))
|
|
132
133
|
self._logger.addHandler(console_handler)
|
|
133
134
|
|
|
134
|
-
def set_level(self, level: str):
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
def set_level(self, level: str) -> bool:
|
|
136
|
+
try:
|
|
137
|
+
level = level.upper()
|
|
138
|
+
if hasattr(logging, level):
|
|
139
|
+
self._logger.setLevel(getattr(logging, level))
|
|
140
|
+
return True
|
|
141
|
+
return False
|
|
142
|
+
except Exception as e:
|
|
143
|
+
self._logger.error(f"无效的日志等级: {level}")
|
|
144
|
+
return False
|
|
138
145
|
|
|
139
146
|
def set_module_level(self, module_name: str, level: str) -> bool:
|
|
140
147
|
from .db import env
|
|
@@ -150,7 +157,7 @@ class Logger:
|
|
|
150
157
|
self._logger.error(f"无效的日志等级: {level}")
|
|
151
158
|
return False
|
|
152
159
|
|
|
153
|
-
def set_output_file(self, path
|
|
160
|
+
def set_output_file(self, path) -> bool:
|
|
154
161
|
if self._file_handler:
|
|
155
162
|
self._logger.removeHandler(self._file_handler)
|
|
156
163
|
self._file_handler.close()
|
|
@@ -164,17 +171,17 @@ class Logger:
|
|
|
164
171
|
file_handler.setFormatter(logging.Formatter("%(message)s"))
|
|
165
172
|
self._logger.addHandler(file_handler)
|
|
166
173
|
self._logger.info(f"日志输出已设置到文件: {p}")
|
|
174
|
+
return True
|
|
167
175
|
except Exception as e:
|
|
168
176
|
self._logger.error(f"无法设置日志文件 {p}: {e}")
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
def save_logs(self, path: str | list):
|
|
177
|
+
return False
|
|
178
|
+
def save_logs(self, path) -> bool:
|
|
172
179
|
if self._logs == None:
|
|
173
180
|
self._logger.warning("没有log记录可供保存。")
|
|
174
|
-
return
|
|
181
|
+
return False
|
|
175
182
|
if isinstance(path, str):
|
|
176
183
|
path = [path]
|
|
177
|
-
|
|
184
|
+
|
|
178
185
|
for p in path:
|
|
179
186
|
try:
|
|
180
187
|
with open(p, "w", encoding="utf-8") as file:
|
|
@@ -183,10 +190,16 @@ class Logger:
|
|
|
183
190
|
for log in logs:
|
|
184
191
|
file.write(f" {log}\n")
|
|
185
192
|
self._logger.info(f"日志已被保存到:{p}。")
|
|
193
|
+
return True
|
|
186
194
|
except Exception as e:
|
|
187
195
|
self._logger.error(f"无法保存日志到 {p}: {e}。")
|
|
188
|
-
|
|
196
|
+
return False
|
|
189
197
|
|
|
198
|
+
def get_logs(self, module_name: str = None) -> dict:
|
|
199
|
+
if module_name:
|
|
200
|
+
return {module_name: self._logs.get(module_name, [])}
|
|
201
|
+
return {k: v.copy() for k, v in self._logs.items()}
|
|
202
|
+
|
|
190
203
|
def _save_in_memory(self, ModuleName, msg):
|
|
191
204
|
if ModuleName not in self._logs:
|
|
192
205
|
self._logs[ModuleName] = []
|
ErisPulse/mods.py
CHANGED
|
@@ -81,7 +81,7 @@ module_info["info"]["meta"]["version"] = "1.1.0"
|
|
|
81
81
|
sdk.mods.set_module("MyModule", module_info)
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
#### get_module(module_name: str) -> dict
|
|
84
|
+
#### get_module(module_name: str) -> dict
|
|
85
85
|
获取模块信息。
|
|
86
86
|
- 参数:
|
|
87
87
|
- module_name: 模块名称
|
|
@@ -253,7 +253,7 @@ sdk.env.set(custom_status_key, is_active)
|
|
|
253
253
|
"""
|
|
254
254
|
|
|
255
255
|
import json
|
|
256
|
-
from typing import Dict, Optional
|
|
256
|
+
from typing import Dict, Optional, Any, List, Set, Tuple, Union, Type, FrozenSet
|
|
257
257
|
|
|
258
258
|
class ModuleManager:
|
|
259
259
|
DEFAULT_MODULE_PREFIX = "erispulse.module.data:"
|
ErisPulse/raiserr.py
CHANGED
|
@@ -25,12 +25,12 @@ class CustomBase(Exception):
|
|
|
25
25
|
sdk.raiserr.register("AdvancedError", "高级错误", CustomBase)
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
#### info(name: str = None) ->
|
|
28
|
+
#### info(name: str = None) -> Dict[str, Any] | None
|
|
29
29
|
获取错误类型信息。
|
|
30
30
|
- 参数:
|
|
31
31
|
- name: 错误类型名称,如果为None则返回所有错误类型信息
|
|
32
32
|
- 返回:
|
|
33
|
-
-
|
|
33
|
+
- Dict[str, Any]: 包含错误类型信息的字典,包括类型名、文档和类引用
|
|
34
34
|
- None: 如果指定的错误类型不存在
|
|
35
35
|
- 示例:
|
|
36
36
|
```python
|
|
@@ -71,12 +71,13 @@ except Exception as e:
|
|
|
71
71
|
import sys
|
|
72
72
|
import traceback
|
|
73
73
|
import asyncio
|
|
74
|
+
from typing import Dict, Any, Optional, Type, List, Set, Tuple, Union
|
|
74
75
|
|
|
75
76
|
class Error:
|
|
76
77
|
def __init__(self):
|
|
77
78
|
self._types = {}
|
|
78
79
|
|
|
79
|
-
def register(self, name, doc="", base=Exception):
|
|
80
|
+
def register(self, name, doc="", base=Exception) -> Type:
|
|
80
81
|
if name not in self._types:
|
|
81
82
|
err_cls = type(name, (base,), {"__doc__": doc})
|
|
82
83
|
self._types[name] = err_cls
|
|
@@ -98,7 +99,7 @@ class Error:
|
|
|
98
99
|
raise exc
|
|
99
100
|
return raiser
|
|
100
101
|
|
|
101
|
-
def info(self, name: str = None):
|
|
102
|
+
def info(self, name: str = None) -> Dict[str, Any] | None:
|
|
102
103
|
result = {}
|
|
103
104
|
for err_name, err_cls in self._types.items():
|
|
104
105
|
result[err_name] = {
|
ErisPulse/util.py
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
## API 文档
|
|
7
7
|
### 拓扑排序:
|
|
8
|
-
- topological_sort(elements, dependencies, error): 拓扑排序依赖关系
|
|
9
|
-
- show_topology(): 可视化模块依赖关系
|
|
8
|
+
- topological_sort(elements: List[str], dependencies: Dict[str, List[str]], error: Type[Exception]) -> List[str]: 拓扑排序依赖关系
|
|
9
|
+
- show_topology() -> str: 可视化模块依赖关系
|
|
10
10
|
|
|
11
11
|
### 装饰器:
|
|
12
12
|
- @cache: 缓存函数结果
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
- @retry(max_attempts=3, delay=1): 失败自动重试
|
|
15
15
|
|
|
16
16
|
### 异步执行:
|
|
17
|
-
- ExecAsync(async_func, *args, **kwargs): 异步执行函数
|
|
17
|
+
- ExecAsync(async_func: Callable, *args: Any, **kwargs: Any) -> Any: 异步执行函数
|
|
18
18
|
|
|
19
19
|
### 示例用法:
|
|
20
20
|
|
|
@@ -48,10 +48,11 @@ import functools
|
|
|
48
48
|
import traceback
|
|
49
49
|
from concurrent.futures import ThreadPoolExecutor
|
|
50
50
|
from collections import defaultdict, deque
|
|
51
|
+
from typing import List, Dict, Type, Callable, Any, Optional, Set
|
|
51
52
|
|
|
52
53
|
executor = ThreadPoolExecutor()
|
|
53
54
|
|
|
54
|
-
def topological_sort(elements, dependencies, error):
|
|
55
|
+
def topological_sort(elements, dependencies, error) -> List[str]:
|
|
55
56
|
graph = defaultdict(list)
|
|
56
57
|
in_degree = {element: 0 for element in elements}
|
|
57
58
|
for element, deps in dependencies.items():
|
|
@@ -72,7 +73,7 @@ def topological_sort(elements, dependencies, error):
|
|
|
72
73
|
sdk.logger.error(f"依赖导入错误: {elements} vs {sorted_list} | 发生了循环依赖")
|
|
73
74
|
return sorted_list
|
|
74
75
|
|
|
75
|
-
def show_topology():
|
|
76
|
+
def show_topology() -> str:
|
|
76
77
|
from . import sdk
|
|
77
78
|
dep_data = sdk.env.get('module_dependencies')
|
|
78
79
|
if not dep_data:
|
|
@@ -98,7 +99,7 @@ def show_topology():
|
|
|
98
99
|
|
|
99
100
|
return "\n".join(result)
|
|
100
101
|
|
|
101
|
-
def ExecAsync(async_func, *args, **kwargs):
|
|
102
|
+
def ExecAsync(async_func, *args, **kwargs) -> Any:
|
|
102
103
|
loop = asyncio.get_event_loop()
|
|
103
104
|
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
104
105
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.6
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -38,11 +38,13 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
38
38
|
Classifier: Operating System :: OS Independent
|
|
39
39
|
Classifier: Programming Language :: Python :: 3
|
|
40
40
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
41
43
|
Classifier: Programming Language :: Python :: 3.10
|
|
42
44
|
Classifier: Programming Language :: Python :: 3.11
|
|
43
45
|
Classifier: Programming Language :: Python :: 3.12
|
|
44
46
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
45
|
-
Requires-Python: >=3.
|
|
47
|
+
Requires-Python: >=3.8
|
|
46
48
|
Requires-Dist: aiohttp
|
|
47
49
|
Requires-Dist: importlib>=1.0.4
|
|
48
50
|
Requires-Dist: pip
|
|
@@ -54,7 +56,6 @@ Description-Content-Type: text/markdown
|
|
|
54
56
|

|
|
55
57
|
|
|
56
58
|
[](https://github.com/FramerOrg)
|
|
57
|
-
[](https://github.com/ErisPulse/ErisPulse/blob/main/LICENSE)
|
|
58
59
|
[](https://pypi.org/project/ErisPulse/)
|
|
59
60
|
|
|
60
61
|
> 文档站:
|
|
@@ -203,4 +204,4 @@ epsdk run your_script.py --reload
|
|
|
203
204
|
|
|
204
205
|
---
|
|
205
206
|
|
|
206
|
-
[加入社区讨论 →](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
207
|
+
[加入社区讨论 →](https://github.com/ErisPulse/ErisPulse/discussions)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=2hEVWxKaDfFGAe3ekpUFx5tIub2riwDtSscsfpA0T2w,11108
|
|
2
|
+
ErisPulse/__main__.py,sha256=MQ_npNFErw5m8My092apQ4gyZAL-7VPLL9Wc7HTq4f4,56451
|
|
3
|
+
ErisPulse/adapter.py,sha256=sho8HMYVSa3syovU2mDxV2Wsw99yD5_sFz6UER89Tes,14367
|
|
4
|
+
ErisPulse/db.py,sha256=mSLLxjqOxSwvMbBSYCpH9TvCvdfccWrKJx6GtkhAl8U,23881
|
|
5
|
+
ErisPulse/logger.py,sha256=ftXp4bfk0S5cnkK4wmF5WqhvuzpUL4MXsuJa-ZTBoMs,8760
|
|
6
|
+
ErisPulse/mods.py,sha256=2OgueJPetQ5N1uDr5DiFvDZ4zF1HSuZ4OsL8IFDqro8,9976
|
|
7
|
+
ErisPulse/raiserr.py,sha256=6txXLfHmC9hb-HI1mbSMxaz1H0T4dfXGSrENRZ06AVg,4081
|
|
8
|
+
ErisPulse/util.py,sha256=eXtmW39bCjbV8lxOmOro4b4gyBhKo-db_EeffJ9BDf0,4525
|
|
9
|
+
erispulse-1.2.6.dist-info/METADATA,sha256=mc8HWywQBC_Safrt-F97Ggboxv_3Nd2hKM7vbe_R_0g,6728
|
|
10
|
+
erispulse-1.2.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
+
erispulse-1.2.6.dist-info/entry_points.txt,sha256=y8ppdsQhk-azC7_ikQgRr4rITSZ4VC4crVKtzHh4TBY,146
|
|
12
|
+
erispulse-1.2.6.dist-info/licenses/LICENSE,sha256=qAe8UGD2uYa8mUPqffBkdIS_4Q5d66FlhYuo3eC_Dq8,1396
|
|
13
|
+
erispulse-1.2.6.dist-info/RECORD,,
|
erispulse-1.2.3.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=fFHqjboYUJGRiqjQlowqaBMj5E6qdfpbVWUzA06IMCY,10976
|
|
2
|
-
ErisPulse/__main__.py,sha256=lFhd9Dn93G6z4ZjZJMEZDIBndy9J3msjdbzijDFrQfY,54056
|
|
3
|
-
ErisPulse/adapter.py,sha256=rrBdPSHvOzzN0VJCD8lThygM9I-_iQIeZ5iMgqCJ34w,13977
|
|
4
|
-
ErisPulse/db.py,sha256=LSIw6MpNtZ-P3deARLj8Umo06EBrwyuzhF4Nj1Lkbyc,23043
|
|
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.2.3.dist-info/METADATA,sha256=0aAnbImoFDRDumggAbPyHfoY4uPPNOl4fG2W1BBZ6vQ,6776
|
|
10
|
-
erispulse-1.2.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
11
|
-
erispulse-1.2.3.dist-info/entry_points.txt,sha256=y8ppdsQhk-azC7_ikQgRr4rITSZ4VC4crVKtzHh4TBY,146
|
|
12
|
-
erispulse-1.2.3.dist-info/licenses/LICENSE,sha256=qAe8UGD2uYa8mUPqffBkdIS_4Q5d66FlhYuo3eC_Dq8,1396
|
|
13
|
-
erispulse-1.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|