ErisPulse 1.0.14.dev2__py3-none-any.whl → 1.0.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/__init__.py +28 -12
- ErisPulse/__main__.py +18 -17
- ErisPulse/adapter.py +81 -0
- ErisPulse/db.py +10 -140
- ErisPulse/logger.py +14 -14
- ErisPulse/mods.py +86 -0
- ErisPulse/raiserr.py +1 -1
- ErisPulse/util.py +36 -1
- {erispulse-1.0.14.dev2.dist-info → erispulse-1.0.15.dist-info}/METADATA +1 -3
- erispulse-1.0.15.dist-info/RECORD +13 -0
- erispulse-1.0.14.dev2.dist-info/RECORD +0 -11
- {erispulse-1.0.14.dev2.dist-info → erispulse-1.0.15.dist-info}/WHEEL +0 -0
- {erispulse-1.0.14.dev2.dist-info → erispulse-1.0.15.dist-info}/entry_points.txt +0 -0
- {erispulse-1.0.14.dev2.dist-info → erispulse-1.0.15.dist-info}/top_level.txt +0 -0
ErisPulse/__init__.py
CHANGED
|
@@ -5,6 +5,8 @@ from . import util
|
|
|
5
5
|
from .raiserr import raiserr
|
|
6
6
|
from .logger import logger
|
|
7
7
|
from .db import env
|
|
8
|
+
from .mods import mods
|
|
9
|
+
from .adapter import adapter, adapterbase
|
|
8
10
|
|
|
9
11
|
# 注册 ErrorHook 并预注册常用错误类型
|
|
10
12
|
raiserr.register("MissingDependencyError", doc="缺少依赖错误")
|
|
@@ -14,16 +16,19 @@ raiserr.register("ModuleLoadError" , doc="模块加载错误")
|
|
|
14
16
|
|
|
15
17
|
sdk = types.SimpleNamespace()
|
|
16
18
|
setattr(sdk, "env", env)
|
|
19
|
+
setattr(sdk, "mods", mods)
|
|
20
|
+
setattr(sdk, "util", util)
|
|
17
21
|
setattr(sdk, "raiserr", raiserr)
|
|
18
22
|
setattr(sdk, "logger", logger)
|
|
19
|
-
setattr(sdk, "
|
|
23
|
+
setattr(sdk, "adapter", adapter)
|
|
24
|
+
setattr(sdk, "BaseAdapter", adapterbase)
|
|
20
25
|
|
|
21
26
|
env.load_env_file()
|
|
22
27
|
|
|
23
28
|
def init():
|
|
24
29
|
try:
|
|
25
30
|
sdkModulePath = os.path.join(os.path.dirname(__file__), "modules")
|
|
26
|
-
|
|
31
|
+
|
|
27
32
|
if not os.path.exists(sdkModulePath):
|
|
28
33
|
os.makedirs(sdkModulePath)
|
|
29
34
|
|
|
@@ -49,21 +54,21 @@ def init():
|
|
|
49
54
|
if not hasattr(moduleObj, "Main"):
|
|
50
55
|
logger.warning(f"模块 {module_name} 缺少 'Main' 类.")
|
|
51
56
|
continue
|
|
52
|
-
|
|
53
|
-
module_info =
|
|
57
|
+
|
|
58
|
+
module_info = mods.get_module(moduleObj.moduleInfo.get("meta", {}).get("name", None))
|
|
54
59
|
if module_info is None:
|
|
55
60
|
module_info = {
|
|
56
61
|
"status": True,
|
|
57
62
|
"info": moduleObj.moduleInfo
|
|
58
63
|
}
|
|
59
|
-
|
|
64
|
+
mods.set_module(moduleObj.moduleInfo.get("meta", {}).get("name", None), module_info)
|
|
60
65
|
logger.info(f"模块 {moduleObj.moduleInfo.get('meta', {}).get('name', None)} 信息已初始化并存储到数据库")
|
|
61
|
-
|
|
66
|
+
|
|
62
67
|
if not module_info.get('status', True):
|
|
63
68
|
disabledModules.append(module_name)
|
|
64
69
|
logger.warning(f"模块 {moduleObj.moduleInfo.get('meta', {}).get('name', None)} 已禁用,跳过加载")
|
|
65
70
|
continue
|
|
66
|
-
|
|
71
|
+
|
|
67
72
|
required_deps = moduleObj.moduleInfo.get("dependencies", []).get("requires", [])
|
|
68
73
|
missing_required_deps = [dep for dep in required_deps if dep not in TempModules]
|
|
69
74
|
if missing_required_deps:
|
|
@@ -105,7 +110,7 @@ def init():
|
|
|
105
110
|
if dep in disabledModules:
|
|
106
111
|
logger.warning(f"模块 {module_name} 的依赖模块 {dep} 已禁用,跳过加载")
|
|
107
112
|
continue
|
|
108
|
-
|
|
113
|
+
|
|
109
114
|
if not all(dep in sdkInstalledModuleNames for dep in moduleDependecies):
|
|
110
115
|
raiserr.InvalidDependencyError(
|
|
111
116
|
f"模块 {module_name} 的依赖无效: {moduleDependecies}"
|
|
@@ -121,8 +126,8 @@ def init():
|
|
|
121
126
|
moduleObj = __import__(module_name)
|
|
122
127
|
moduleInfo: dict = moduleObj.moduleInfo
|
|
123
128
|
|
|
124
|
-
module_info =
|
|
125
|
-
|
|
129
|
+
module_info = mods.get_module(moduleInfo.get("meta", {}).get("name", None))
|
|
130
|
+
mods.set_module(moduleInfo.get("meta", {}).get("name", None), {
|
|
126
131
|
"status": True,
|
|
127
132
|
"info": moduleInfo
|
|
128
133
|
})
|
|
@@ -131,14 +136,25 @@ def init():
|
|
|
131
136
|
for module_name in sdkInstalledModuleNames:
|
|
132
137
|
moduleObj = __import__(module_name)
|
|
133
138
|
moduleInfo = moduleObj.moduleInfo
|
|
134
|
-
module_status =
|
|
139
|
+
module_status = mods.get_module_status(moduleInfo.get("meta", {}).get("name", None))
|
|
135
140
|
if not module_status:
|
|
136
141
|
continue
|
|
137
|
-
|
|
142
|
+
|
|
138
143
|
moduleMain = moduleObj.Main(sdk)
|
|
139
144
|
setattr(moduleMain, "moduleInfo", moduleInfo)
|
|
140
145
|
setattr(sdk, moduleInfo.get("meta", {}).get("name", None), moduleMain)
|
|
141
146
|
logger.debug(f"模块 {moduleInfo.get('meta', {}).get('name', None)} 正在初始化")
|
|
147
|
+
|
|
148
|
+
if hasattr(moduleMain, "register_adapters"):
|
|
149
|
+
try:
|
|
150
|
+
adapters = moduleMain.register_adapters()
|
|
151
|
+
if isinstance(adapters, dict):
|
|
152
|
+
for platform_name, adapter_class in adapters.items():
|
|
153
|
+
sdk.adapter.register(platform_name, adapter_class)
|
|
154
|
+
logger.info(f"模块 {moduleInfo['meta']['name']} 注册了适配器: {platform_name}")
|
|
155
|
+
except Exception as e:
|
|
156
|
+
logger.error(f"注册适配器失败: {e}")
|
|
157
|
+
|
|
142
158
|
except Exception as e:
|
|
143
159
|
logger.error(f"初始化失败: {e}")
|
|
144
160
|
raise e
|
ErisPulse/__main__.py
CHANGED
|
@@ -9,6 +9,7 @@ import asyncio
|
|
|
9
9
|
import subprocess
|
|
10
10
|
import json
|
|
11
11
|
from .db import env
|
|
12
|
+
from .mods import mods
|
|
12
13
|
|
|
13
14
|
def print_panel(msg, title=None, border_style=None):
|
|
14
15
|
print("=" * 60)
|
|
@@ -157,17 +158,17 @@ class SourceManager:
|
|
|
157
158
|
print_panel(f"源 {value} 不存在", "错误")
|
|
158
159
|
|
|
159
160
|
def enable_module(module_name):
|
|
160
|
-
module_info =
|
|
161
|
+
module_info = mods.get_module(module_name)
|
|
161
162
|
if module_info:
|
|
162
|
-
|
|
163
|
+
mods.set_module_status(module_name, True)
|
|
163
164
|
print_panel(f"✓ 模块 {module_name} 已成功启用", "成功")
|
|
164
165
|
else:
|
|
165
166
|
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
166
167
|
|
|
167
168
|
def disable_module(module_name):
|
|
168
|
-
module_info =
|
|
169
|
+
module_info = mods.get_module(module_name)
|
|
169
170
|
if module_info:
|
|
170
|
-
|
|
171
|
+
mods.set_module_status(module_name, False)
|
|
171
172
|
print_panel(f"✓ 模块 {module_name} 已成功禁用", "成功")
|
|
172
173
|
else:
|
|
173
174
|
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
@@ -255,7 +256,7 @@ def install_module(module_name, force=False):
|
|
|
255
256
|
SourceManager().update_sources()
|
|
256
257
|
env.set('last_origin_update_time', datetime.now().isoformat())
|
|
257
258
|
print("✓ 源更新完成")
|
|
258
|
-
module_info =
|
|
259
|
+
module_info = mods.get_module(module_name)
|
|
259
260
|
if module_info and not force:
|
|
260
261
|
meta = module_info.get('info', {}).get('meta', {})
|
|
261
262
|
print_panel(
|
|
@@ -332,7 +333,7 @@ def install_module(module_name, force=False):
|
|
|
332
333
|
module_dir=module_dir
|
|
333
334
|
):
|
|
334
335
|
return
|
|
335
|
-
|
|
336
|
+
mods.set_module(module_name, {
|
|
336
337
|
'status': True,
|
|
337
338
|
'info': {
|
|
338
339
|
'meta': {
|
|
@@ -352,7 +353,7 @@ def install_module(module_name, force=False):
|
|
|
352
353
|
|
|
353
354
|
def uninstall_module(module_name):
|
|
354
355
|
print_panel(f"准备卸载模块: {module_name}", "卸载摘要")
|
|
355
|
-
module_info =
|
|
356
|
+
module_info = mods.get_module(module_name)
|
|
356
357
|
if not module_info:
|
|
357
358
|
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
358
359
|
return
|
|
@@ -383,7 +384,7 @@ def uninstall_module(module_name):
|
|
|
383
384
|
return
|
|
384
385
|
pip_dependencies = depsinfo.get('pip', [])
|
|
385
386
|
if pip_dependencies:
|
|
386
|
-
all_modules =
|
|
387
|
+
all_modules = mods.get_all_modules()
|
|
387
388
|
unused_pip_dependencies = []
|
|
388
389
|
essential_packages = {'aiohttp'}
|
|
389
390
|
for dep in pip_dependencies:
|
|
@@ -419,13 +420,13 @@ def uninstall_module(module_name):
|
|
|
419
420
|
f"卸载 pip 依赖失败: {e.stderr.decode()}",
|
|
420
421
|
"错误"
|
|
421
422
|
)
|
|
422
|
-
if
|
|
423
|
+
if mods.remove_module(module_name):
|
|
423
424
|
print_panel(f"✓ 模块 {module_name} 已成功卸载", "成功")
|
|
424
425
|
else:
|
|
425
426
|
print_panel(f"模块 {module_name} 不存在", "错误")
|
|
426
427
|
|
|
427
428
|
def upgrade_all_modules(force=False):
|
|
428
|
-
all_modules =
|
|
429
|
+
all_modules = mods.get_all_modules()
|
|
429
430
|
if not all_modules:
|
|
430
431
|
print("未找到任何模块,无法更新")
|
|
431
432
|
return
|
|
@@ -478,16 +479,16 @@ def upgrade_all_modules(force=False):
|
|
|
478
479
|
):
|
|
479
480
|
continue
|
|
480
481
|
all_modules[update['name']]['info']['version'] = update['remote_version']
|
|
481
|
-
|
|
482
|
+
mods.set_all_modules(all_modules)
|
|
482
483
|
print(f"模块 {update['name']} 已更新至版本 {update['remote_version']}")
|
|
483
484
|
|
|
484
485
|
def list_modules(module_name=None):
|
|
485
|
-
all_modules =
|
|
486
|
+
all_modules = mods.get_all_modules()
|
|
486
487
|
if not all_modules:
|
|
487
488
|
print_panel("未在数据库中发现注册模块,正在初始化模块列表...", "提示")
|
|
488
489
|
from . import init as init_module
|
|
489
490
|
init_module()
|
|
490
|
-
all_modules =
|
|
491
|
+
all_modules = mods.get_all_modules()
|
|
491
492
|
if not all_modules:
|
|
492
493
|
print_panel("未找到任何模块", "错误")
|
|
493
494
|
return
|
|
@@ -576,7 +577,7 @@ def main():
|
|
|
576
577
|
continue
|
|
577
578
|
if '*' in module_name or '?' in module_name:
|
|
578
579
|
print(f"正在匹配模块模式: {module_name}...")
|
|
579
|
-
all_modules =
|
|
580
|
+
all_modules = mods.get_all_modules()
|
|
580
581
|
if not all_modules:
|
|
581
582
|
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
582
583
|
continue
|
|
@@ -601,7 +602,7 @@ def main():
|
|
|
601
602
|
continue
|
|
602
603
|
if '*' in module_name or '?' in module_name:
|
|
603
604
|
print(f"正在匹配模块模式: {module_name}...")
|
|
604
|
-
all_modules =
|
|
605
|
+
all_modules = mods.get_all_modules()
|
|
605
606
|
if not all_modules:
|
|
606
607
|
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
607
608
|
continue
|
|
@@ -628,7 +629,7 @@ def main():
|
|
|
628
629
|
continue
|
|
629
630
|
if '*' in module_name or '?' in module_name:
|
|
630
631
|
print(f"正在匹配模块模式: {module_name}...")
|
|
631
|
-
all_modules =
|
|
632
|
+
all_modules = mods.get_all_modules()
|
|
632
633
|
if not all_modules:
|
|
633
634
|
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
634
635
|
continue
|
|
@@ -653,7 +654,7 @@ def main():
|
|
|
653
654
|
continue
|
|
654
655
|
if '*' in module_name or '?' in module_name:
|
|
655
656
|
print(f"正在匹配模块模式: {module_name}...")
|
|
656
|
-
all_modules =
|
|
657
|
+
all_modules = mods.get_all_modules()
|
|
657
658
|
if not all_modules:
|
|
658
659
|
print_panel("未找到任何模块,请先更新源或检查配置", "错误")
|
|
659
660
|
continue
|
ErisPulse/adapter.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import asyncio
|
|
3
|
+
from typing import Callable, Any, Dict, List, Type
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
|
|
6
|
+
class BaseAdapter:
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self._handlers = defaultdict(list)
|
|
9
|
+
self._middlewares = []
|
|
10
|
+
|
|
11
|
+
def on(self, event_type: str):
|
|
12
|
+
def decorator(func: Callable):
|
|
13
|
+
@functools.wraps(func)
|
|
14
|
+
async def wrapper(*args, **kwargs):
|
|
15
|
+
return await func(*args, **kwargs)
|
|
16
|
+
self._handlers[event_type].append(wrapper)
|
|
17
|
+
return wrapper
|
|
18
|
+
return decorator
|
|
19
|
+
|
|
20
|
+
def middleware(self, func: Callable):
|
|
21
|
+
self._middlewares.append(func)
|
|
22
|
+
return func
|
|
23
|
+
|
|
24
|
+
async def send(self, target: Any, message: Any, **kwargs):
|
|
25
|
+
raise NotImplementedError
|
|
26
|
+
|
|
27
|
+
async def call_api(self, endpoint: str, **params):
|
|
28
|
+
raise NotImplementedError
|
|
29
|
+
|
|
30
|
+
async def emit(self, event_type: str, data: Any):
|
|
31
|
+
for middleware in self._middlewares:
|
|
32
|
+
data = await middleware(data)
|
|
33
|
+
|
|
34
|
+
for handler in self._handlers.get(event_type, []):
|
|
35
|
+
await handler(data)
|
|
36
|
+
|
|
37
|
+
class AdapterManager:
|
|
38
|
+
def __init__(self):
|
|
39
|
+
self._adapters: Dict[str, BaseAdapter] = {}
|
|
40
|
+
|
|
41
|
+
def register(self, platform: str, adapter_class: Type[BaseAdapter]) -> bool:
|
|
42
|
+
if not issubclass(adapter_class, BaseAdapter):
|
|
43
|
+
raise TypeError("适配器必须继承自BaseAdapter")
|
|
44
|
+
from . import sdk
|
|
45
|
+
self._adapters[platform] = adapter_class(sdk)
|
|
46
|
+
return True
|
|
47
|
+
|
|
48
|
+
async def startup(self, platforms: List[str] = None):
|
|
49
|
+
if platforms is None:
|
|
50
|
+
platforms = self._adapters.keys()
|
|
51
|
+
|
|
52
|
+
for platform in platforms:
|
|
53
|
+
if platform not in self._adapters:
|
|
54
|
+
raise ValueError(f"平台 {platform} 未注册")
|
|
55
|
+
adapter = self._adapters[platform]
|
|
56
|
+
asyncio.create_task(self._run_adapter(adapter, platform))
|
|
57
|
+
|
|
58
|
+
async def _run_adapter(self, adapter: BaseAdapter, platform: str):
|
|
59
|
+
try:
|
|
60
|
+
await adapter.start()
|
|
61
|
+
except Exception as e:
|
|
62
|
+
self.logger.error(f"平台 {platform} 停止时遇到了错误: {e}")
|
|
63
|
+
|
|
64
|
+
async def shutdown(self):
|
|
65
|
+
for adapter in self._adapters.values():
|
|
66
|
+
await adapter.shutdown()
|
|
67
|
+
|
|
68
|
+
def get(self, platform: str) -> BaseAdapter:
|
|
69
|
+
return self._adapters.get(platform)
|
|
70
|
+
|
|
71
|
+
def __getattr__(self, platform: str) -> BaseAdapter:
|
|
72
|
+
if platform not in self._adapters:
|
|
73
|
+
raise AttributeError(f"平台 {platform} 的适配器未注册")
|
|
74
|
+
return self._adapters[platform]
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def platforms(self) -> list:
|
|
78
|
+
return list(self._adapters.keys())
|
|
79
|
+
|
|
80
|
+
adapter = AdapterManager()
|
|
81
|
+
adapterbase = BaseAdapter
|
ErisPulse/db.py
CHANGED
|
@@ -11,17 +11,15 @@ class EnvManager:
|
|
|
11
11
|
def __new__(cls, *args, **kwargs):
|
|
12
12
|
if not cls._instance:
|
|
13
13
|
cls._instance = super().__new__(cls)
|
|
14
|
-
cls._instance.dev_mode = kwargs.get('dev_mode', False)
|
|
15
14
|
return cls._instance
|
|
16
15
|
|
|
17
|
-
def __init__(self
|
|
16
|
+
def __init__(self):
|
|
18
17
|
if not hasattr(self, "_initialized"):
|
|
19
|
-
self.dev_mode = dev_mode
|
|
20
18
|
self._init_db()
|
|
21
|
-
|
|
19
|
+
self._initialized = True
|
|
20
|
+
|
|
22
21
|
def _init_db(self):
|
|
23
22
|
os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
|
|
24
|
-
|
|
25
23
|
conn = sqlite3.connect(self.db_path)
|
|
26
24
|
cursor = conn.cursor()
|
|
27
25
|
cursor.execute("""
|
|
@@ -30,18 +28,6 @@ class EnvManager:
|
|
|
30
28
|
value TEXT NOT NULL
|
|
31
29
|
)
|
|
32
30
|
""")
|
|
33
|
-
cursor.execute("""
|
|
34
|
-
CREATE TABLE IF NOT EXISTS modules (
|
|
35
|
-
module_name TEXT PRIMARY KEY,
|
|
36
|
-
status INTEGER NOT NULL,
|
|
37
|
-
version TEXT,
|
|
38
|
-
description TEXT,
|
|
39
|
-
author TEXT,
|
|
40
|
-
dependencies TEXT,
|
|
41
|
-
optional_dependencies TEXT,
|
|
42
|
-
pip_dependencies TEXT
|
|
43
|
-
)
|
|
44
|
-
""")
|
|
45
31
|
conn.commit()
|
|
46
32
|
conn.close()
|
|
47
33
|
|
|
@@ -64,6 +50,12 @@ class EnvManager:
|
|
|
64
50
|
else:
|
|
65
51
|
raise
|
|
66
52
|
|
|
53
|
+
def get_all_keys(self) -> list:
|
|
54
|
+
with sqlite3.connect(self.db_path) as conn:
|
|
55
|
+
cursor = conn.cursor()
|
|
56
|
+
cursor.execute("SELECT key FROM config")
|
|
57
|
+
return [row[0] for row in cursor.fetchall()]
|
|
58
|
+
|
|
67
59
|
def set(self, key, value):
|
|
68
60
|
serialized_value = json.dumps(value) if isinstance(value, (dict, list)) else str(value)
|
|
69
61
|
conn = sqlite3.connect(self.db_path)
|
|
@@ -78,7 +70,7 @@ class EnvManager:
|
|
|
78
70
|
cursor.execute("DELETE FROM config WHERE key = ?", (key,))
|
|
79
71
|
conn.commit()
|
|
80
72
|
conn.close()
|
|
81
|
-
|
|
73
|
+
|
|
82
74
|
def clear(self):
|
|
83
75
|
conn = sqlite3.connect(self.db_path)
|
|
84
76
|
cursor = conn.cursor()
|
|
@@ -95,129 +87,7 @@ class EnvManager:
|
|
|
95
87
|
for key, value in vars(env_module).items():
|
|
96
88
|
if not key.startswith("__") and isinstance(value, (dict, list, str, int, float, bool)):
|
|
97
89
|
self.set(key, value)
|
|
98
|
-
def set_module_status(self, module_name, status):
|
|
99
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
100
|
-
cursor = conn.cursor()
|
|
101
|
-
cursor.execute("""
|
|
102
|
-
UPDATE modules SET status = ? WHERE module_name = ?
|
|
103
|
-
""", (int(status), module_name))
|
|
104
|
-
conn.commit()
|
|
105
|
-
|
|
106
|
-
def get_module_status(self, module_name):
|
|
107
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
108
|
-
cursor = conn.cursor()
|
|
109
|
-
cursor.execute("""
|
|
110
|
-
SELECT status FROM modules WHERE module_name = ?
|
|
111
|
-
""", (module_name,))
|
|
112
|
-
result = cursor.fetchone()
|
|
113
|
-
return bool(result[0]) if result else True
|
|
114
90
|
|
|
115
|
-
def set_all_modules(self, modules_info):
|
|
116
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
117
|
-
cursor = conn.cursor()
|
|
118
|
-
for module_name, module_info in modules_info.items():
|
|
119
|
-
meta = module_info.get('info', {}).get('meta', {})
|
|
120
|
-
dependencies = module_info.get('info', {}).get('dependencies', {})
|
|
121
|
-
cursor.execute("""
|
|
122
|
-
INSERT OR REPLACE INTO modules (
|
|
123
|
-
module_name, status, version, description, author,
|
|
124
|
-
dependencies, optional_dependencies, pip_dependencies
|
|
125
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
126
|
-
""", (
|
|
127
|
-
module_name,
|
|
128
|
-
int(module_info.get('status', True)),
|
|
129
|
-
meta.get('version', ''),
|
|
130
|
-
meta.get('description', ''),
|
|
131
|
-
meta.get('author', ''),
|
|
132
|
-
json.dumps(dependencies.get('requires', [])),
|
|
133
|
-
json.dumps(dependencies.get('optional', [])),
|
|
134
|
-
json.dumps(dependencies.get('pip', []))
|
|
135
|
-
))
|
|
136
|
-
conn.commit()
|
|
137
|
-
|
|
138
|
-
def get_all_modules(self):
|
|
139
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
140
|
-
cursor = conn.cursor()
|
|
141
|
-
cursor.execute("SELECT * FROM modules")
|
|
142
|
-
rows = cursor.fetchall()
|
|
143
|
-
modules_info = {}
|
|
144
|
-
for row in rows:
|
|
145
|
-
module_name, status, version, description, author, dependencies, optional_dependencies, pip_dependencies = row
|
|
146
|
-
modules_info[module_name] = {
|
|
147
|
-
'status': bool(status),
|
|
148
|
-
'info': {
|
|
149
|
-
'meta': {
|
|
150
|
-
'version': version,
|
|
151
|
-
'description': description,
|
|
152
|
-
'author': author,
|
|
153
|
-
'pip_dependencies': json.loads(pip_dependencies) if pip_dependencies else []
|
|
154
|
-
},
|
|
155
|
-
'dependencies': {
|
|
156
|
-
'requires': json.loads(dependencies) if dependencies else [],
|
|
157
|
-
'optional': json.loads(optional_dependencies) if optional_dependencies else [],
|
|
158
|
-
'pip': json.loads(pip_dependencies) if pip_dependencies else []
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return modules_info
|
|
163
|
-
|
|
164
|
-
def set_module(self, module_name, module_info):
|
|
165
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
166
|
-
cursor = conn.cursor()
|
|
167
|
-
meta = module_info.get('info', {}).get('meta', {})
|
|
168
|
-
dependencies = module_info.get('info', {}).get('dependencies', {})
|
|
169
|
-
cursor.execute("""
|
|
170
|
-
INSERT OR REPLACE INTO modules (
|
|
171
|
-
module_name, status, version, description, author,
|
|
172
|
-
dependencies, optional_dependencies, pip_dependencies
|
|
173
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
174
|
-
""", (
|
|
175
|
-
module_name,
|
|
176
|
-
int(module_info.get('status', True)),
|
|
177
|
-
meta.get('version', ''),
|
|
178
|
-
meta.get('description', ''),
|
|
179
|
-
meta.get('author', ''),
|
|
180
|
-
json.dumps(dependencies.get('requires', [])),
|
|
181
|
-
json.dumps(dependencies.get('optional', [])),
|
|
182
|
-
json.dumps(dependencies.get('pip', []))
|
|
183
|
-
))
|
|
184
|
-
conn.commit()
|
|
185
|
-
|
|
186
|
-
def get_module(self, module_name):
|
|
187
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
188
|
-
cursor = conn.cursor()
|
|
189
|
-
cursor.execute("SELECT * FROM modules WHERE module_name = ?", (module_name,))
|
|
190
|
-
row = cursor.fetchone()
|
|
191
|
-
if row:
|
|
192
|
-
module_name, status, version, description, author, dependencies, optional_dependencies, pip_dependencies = row
|
|
193
|
-
return {
|
|
194
|
-
'status': bool(status),
|
|
195
|
-
'info': {
|
|
196
|
-
'meta': {
|
|
197
|
-
'version': version,
|
|
198
|
-
'description': description,
|
|
199
|
-
'author': author,
|
|
200
|
-
'pip_dependencies': json.loads(pip_dependencies) if pip_dependencies else []
|
|
201
|
-
},
|
|
202
|
-
'dependencies': {
|
|
203
|
-
'requires': json.loads(dependencies) if dependencies else [],
|
|
204
|
-
'optional': json.loads(optional_dependencies) if optional_dependencies else [],
|
|
205
|
-
'pip': json.loads(pip_dependencies) if pip_dependencies else []
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return None
|
|
210
|
-
|
|
211
|
-
def update_module(self, module_name, module_info):
|
|
212
|
-
self.set_module(module_name, module_info)
|
|
213
|
-
|
|
214
|
-
def remove_module(self, module_name):
|
|
215
|
-
with sqlite3.connect(self.db_path) as conn:
|
|
216
|
-
cursor = conn.cursor()
|
|
217
|
-
cursor.execute("DELETE FROM modules WHERE module_name = ?", (module_name,))
|
|
218
|
-
conn.commit()
|
|
219
|
-
return cursor.rowcount > 0
|
|
220
|
-
|
|
221
91
|
def __getattr__(self, key):
|
|
222
92
|
try:
|
|
223
93
|
return self.get(key)
|
ErisPulse/logger.py
CHANGED
|
@@ -19,7 +19,7 @@ class Logger:
|
|
|
19
19
|
level = level.upper()
|
|
20
20
|
if hasattr(logging, level):
|
|
21
21
|
self._logger.setLevel(getattr(logging, level))
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
def set_module_level(self, module_name: str, level: str) -> bool:
|
|
24
24
|
from .db import env
|
|
25
25
|
if not env.get_module_status(module_name):
|
|
@@ -33,15 +33,15 @@ class Logger:
|
|
|
33
33
|
else:
|
|
34
34
|
self._logger.error(f"无效的日志等级: {level}")
|
|
35
35
|
return False
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
def set_output_file(self, path: str | list):
|
|
38
38
|
if self._file_handler:
|
|
39
39
|
self._logger.removeHandler(self._file_handler)
|
|
40
40
|
self._file_handler.close()
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
if isinstance(path, str):
|
|
43
43
|
path = [path]
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
for p in path:
|
|
46
46
|
try:
|
|
47
47
|
file_handler = logging.FileHandler(p, encoding='utf-8')
|
|
@@ -51,14 +51,14 @@ class Logger:
|
|
|
51
51
|
except Exception as e:
|
|
52
52
|
self._logger.error(f"无法设置日志文件 {p}: {e}")
|
|
53
53
|
raise e
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
def save_logs(self, path: str | list):
|
|
56
56
|
if self._logs == None:
|
|
57
57
|
self._logger.warning("没有log记录可供保存。")
|
|
58
58
|
return
|
|
59
59
|
if isinstance(path, str):
|
|
60
60
|
path = [path]
|
|
61
|
-
|
|
61
|
+
|
|
62
62
|
for p in path:
|
|
63
63
|
try:
|
|
64
64
|
with open(p, "w", encoding="utf-8") as file:
|
|
@@ -70,14 +70,14 @@ class Logger:
|
|
|
70
70
|
except Exception as e:
|
|
71
71
|
self._logger.error(f"无法保存日志到 {p}: {e}。")
|
|
72
72
|
raise e
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
def catch(self, func_or_level=None, level="error"):
|
|
75
75
|
if isinstance(func_or_level, str):
|
|
76
76
|
return lambda func: self.catch(func, level=func_or_level)
|
|
77
77
|
if func_or_level is None:
|
|
78
78
|
return lambda func: self.catch(func, level=level)
|
|
79
79
|
func = func_or_level
|
|
80
|
-
|
|
80
|
+
|
|
81
81
|
@functools.wraps(func)
|
|
82
82
|
def wrapper(*args, **kwargs):
|
|
83
83
|
try:
|
|
@@ -85,17 +85,17 @@ class Logger:
|
|
|
85
85
|
except Exception as e:
|
|
86
86
|
import traceback
|
|
87
87
|
error_info = traceback.format_exc()
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
module_name = func.__module__
|
|
90
90
|
if module_name == "__main__":
|
|
91
91
|
module_name = "Main"
|
|
92
92
|
func_name = func.__name__
|
|
93
|
-
|
|
93
|
+
|
|
94
94
|
error_msg = f"Exception in {func_name}: {str(e)}\n{error_info}"
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
log_method = getattr(self, level, self.error)
|
|
97
97
|
log_method(error_msg)
|
|
98
|
-
|
|
98
|
+
|
|
99
99
|
return None
|
|
100
100
|
return wrapper
|
|
101
101
|
|
|
@@ -105,10 +105,10 @@ class Logger:
|
|
|
105
105
|
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
106
106
|
msg = f"{timestamp} - {msg}"
|
|
107
107
|
self._logs[ModuleName].append(msg)
|
|
108
|
-
|
|
108
|
+
|
|
109
109
|
def _get_effective_level(self, module_name):
|
|
110
110
|
return self._module_levels.get(module_name, self._logger.level)
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
def _get_caller(self):
|
|
113
113
|
frame = inspect.currentframe().f_back.f_back
|
|
114
114
|
module = inspect.getmodule(frame)
|
ErisPulse/mods.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Dict, Optional
|
|
3
|
+
|
|
4
|
+
class ModuleManager:
|
|
5
|
+
DEFAULT_MODULE_PREFIX = "erispulse.module.data:"
|
|
6
|
+
DEFAULT_STATUS_PREFIX = "erispulse.module.status:"
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
from .db import env
|
|
10
|
+
self.env = env
|
|
11
|
+
self._ensure_prefixes()
|
|
12
|
+
|
|
13
|
+
def _ensure_prefixes(self):
|
|
14
|
+
if not self.env.get("erispulse.system.module_prefix"):
|
|
15
|
+
self.env.set("erispulse.system.module_prefix", self.DEFAULT_MODULE_PREFIX)
|
|
16
|
+
if not self.env.get("erispulse.system.status_prefix"):
|
|
17
|
+
self.env.set("erispulse.system.status_prefix", self.DEFAULT_STATUS_PREFIX)
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def module_prefix(self) -> str:
|
|
21
|
+
return self.env.get("erispulse.system.module_prefix")
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def status_prefix(self) -> str:
|
|
25
|
+
return self.env.get("erispulse.system.status_prefix")
|
|
26
|
+
|
|
27
|
+
def set_module_status(self, module_name: str, status: bool) -> None:
|
|
28
|
+
self.env.set(f"{self.status_prefix}{module_name}", bool(status))
|
|
29
|
+
|
|
30
|
+
def get_module_status(self, module_name: str) -> bool:
|
|
31
|
+
status = self.env.get(f"{self.status_prefix}{module_name}", True)
|
|
32
|
+
if isinstance(status, str):
|
|
33
|
+
return status.lower() == 'true'
|
|
34
|
+
return bool(status)
|
|
35
|
+
|
|
36
|
+
def set_module(self, module_name: str, module_info: dict) -> None:
|
|
37
|
+
self.env.set(f"{self.module_prefix}{module_name}", module_info)
|
|
38
|
+
self.set_module_status(module_name, module_info.get('status', True))
|
|
39
|
+
|
|
40
|
+
def get_module(self, module_name: str) -> Optional[dict]:
|
|
41
|
+
return self.env.get(f"{self.module_prefix}{module_name}")
|
|
42
|
+
|
|
43
|
+
def set_all_modules(self, modules_info: Dict[str, dict]) -> None:
|
|
44
|
+
for module_name, module_info in modules_info.items():
|
|
45
|
+
self.set_module(module_name, module_info)
|
|
46
|
+
|
|
47
|
+
def get_all_modules(self) -> dict:
|
|
48
|
+
modules_info = {}
|
|
49
|
+
all_keys = self.env.get_all_keys()
|
|
50
|
+
prefix_len = len(self.module_prefix)
|
|
51
|
+
|
|
52
|
+
for key in all_keys:
|
|
53
|
+
if key.startswith(self.module_prefix):
|
|
54
|
+
module_name = key[prefix_len:]
|
|
55
|
+
module_info = self.get_module(module_name)
|
|
56
|
+
if module_info:
|
|
57
|
+
status = self.get_module_status(module_name)
|
|
58
|
+
module_info['status'] = bool(status)
|
|
59
|
+
modules_info[module_name] = module_info
|
|
60
|
+
return modules_info
|
|
61
|
+
|
|
62
|
+
def update_module(self, module_name: str, module_info: dict) -> None:
|
|
63
|
+
self.set_module(module_name, module_info)
|
|
64
|
+
|
|
65
|
+
def remove_module(self, module_name: str) -> bool:
|
|
66
|
+
module_key = f"{self.module_prefix}{module_name}"
|
|
67
|
+
status_key = f"{self.status_prefix}{module_name}"
|
|
68
|
+
|
|
69
|
+
if self.env.get(module_key) is not None:
|
|
70
|
+
self.env.delete(module_key)
|
|
71
|
+
self.env.delete(status_key)
|
|
72
|
+
return True
|
|
73
|
+
return False
|
|
74
|
+
|
|
75
|
+
def update_prefixes(self, module_prefix: str = None, status_prefix: str = None) -> None:
|
|
76
|
+
if module_prefix:
|
|
77
|
+
if not module_prefix.endswith(':'):
|
|
78
|
+
module_prefix += ':'
|
|
79
|
+
self.env.set("erispulse.system.module_prefix", module_prefix)
|
|
80
|
+
|
|
81
|
+
if status_prefix:
|
|
82
|
+
if not status_prefix.endswith(':'):
|
|
83
|
+
status_prefix += ':'
|
|
84
|
+
self.env.set("erispulse.system.status_prefix", status_prefix)
|
|
85
|
+
|
|
86
|
+
mods = ModuleManager()
|
ErisPulse/raiserr.py
CHANGED
ErisPulse/util.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import time
|
|
1
2
|
import asyncio
|
|
3
|
+
import functools
|
|
2
4
|
from concurrent.futures import ThreadPoolExecutor
|
|
3
5
|
from collections import defaultdict, deque
|
|
4
6
|
|
|
@@ -26,4 +28,37 @@ def topological_sort(elements, dependencies, error):
|
|
|
26
28
|
|
|
27
29
|
def ExecAsync(async_func, *args, **kwargs):
|
|
28
30
|
loop = asyncio.get_event_loop()
|
|
29
|
-
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
31
|
+
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
32
|
+
|
|
33
|
+
def cache(func):
|
|
34
|
+
cache_dict = {}
|
|
35
|
+
@functools.wraps(func)
|
|
36
|
+
def wrapper(*args, **kwargs):
|
|
37
|
+
key = (args, tuple(sorted(kwargs.items())))
|
|
38
|
+
if key not in cache_dict:
|
|
39
|
+
cache_dict[key] = func(*args, **kwargs)
|
|
40
|
+
return cache_dict[key]
|
|
41
|
+
return wrapper
|
|
42
|
+
|
|
43
|
+
def run_in_executor(func):
|
|
44
|
+
@functools.wraps(func)
|
|
45
|
+
async def wrapper(*args, **kwargs):
|
|
46
|
+
loop = asyncio.get_event_loop()
|
|
47
|
+
return await loop.run_in_executor(None, lambda: func(*args, **kwargs))
|
|
48
|
+
return wrapper
|
|
49
|
+
|
|
50
|
+
def retry(max_attempts=3, delay=1):
|
|
51
|
+
def decorator(func):
|
|
52
|
+
@functools.wraps(func)
|
|
53
|
+
def wrapper(*args, **kwargs):
|
|
54
|
+
attempts = 0
|
|
55
|
+
while attempts < max_attempts:
|
|
56
|
+
try:
|
|
57
|
+
return func(*args, **kwargs)
|
|
58
|
+
except Exception as e:
|
|
59
|
+
attempts += 1
|
|
60
|
+
if attempts == max_attempts:
|
|
61
|
+
raise
|
|
62
|
+
time.sleep(delay)
|
|
63
|
+
return wrapper
|
|
64
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.15
|
|
4
4
|
Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
|
|
5
5
|
Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
|
|
6
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -20,8 +20,6 @@ Requires-Python: >=3.7
|
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
21
|
Requires-Dist: aiohttp
|
|
22
22
|
|
|
23
|
-
⚠ 当前版本处于开发中,请谨慎使用。
|
|
24
|
-
|
|
25
23
|

|
|
26
24
|
|
|
27
25
|
基于 [RyhBotPythonSDK V2](https://github.com/runoneall/RyhBotPythonSDK2) 构建,由 [sdkFrame](https://github.com/runoneall/sdkFrame) 提供支持的异步机器人开发框架。
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=di4Bii26xJYMk1xqoPNGM0lk2CABlxa3MC-9iOvKPYs,7318
|
|
2
|
+
ErisPulse/__main__.py,sha256=-UdhsYP_X7EagomCjo73Y_qQdXwtMbDS5KoOSrI-OcU,32181
|
|
3
|
+
ErisPulse/adapter.py,sha256=9BxzBrr5LkQcKY8ONuv7MYxlUrvkINuG9TtdzKgwUWY,2687
|
|
4
|
+
ErisPulse/db.py,sha256=oLdOe8LgklBSOWHXu8QDEnaVkjf0bvJLsbJVoT_uw2U,3188
|
|
5
|
+
ErisPulse/logger.py,sha256=HIQMYD75K-PL4IETMlm7V6Eyg0ussZ11_riEw5E5a08,5899
|
|
6
|
+
ErisPulse/mods.py,sha256=M9XQWUQYNZ11m845hxbewBAauWXnysy-aOdLwb5xy_M,3312
|
|
7
|
+
ErisPulse/raiserr.py,sha256=MPA3ZcPEzuhJldmFG19oSvjxzVojcQ9jZYfmcEQr_SU,1292
|
|
8
|
+
ErisPulse/util.py,sha256=01kUQwVFeE7gyD1skCwjpANYzVUHjLC6J1UUzfQXPco,2139
|
|
9
|
+
erispulse-1.0.15.dist-info/METADATA,sha256=VmyDSAX2FLp-kHcsMeby2KImmwi_g_xwD8LJEVOsivI,2443
|
|
10
|
+
erispulse-1.0.15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
erispulse-1.0.15.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
12
|
+
erispulse-1.0.15.dist-info/top_level.txt,sha256=Lm_qtkVvNJR8_dXh_qEDdl_12cZGpic-i4HUlVVUMZc,10
|
|
13
|
+
erispulse-1.0.15.dist-info/RECORD,,
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=75SfkJEYHy73K3UGaEuMYlHtJadjpoNWd1yF-f5-aGc,6662
|
|
2
|
-
ErisPulse/__main__.py,sha256=1-mWPmZArG-o_eYxZiabmMs4MokQn_-TBa43R4_WYlc,32141
|
|
3
|
-
ErisPulse/db.py,sha256=2vBVFFEU-AcR-GkzWTqpn8fFV9EvuQee2mi1XzfDXVI,9077
|
|
4
|
-
ErisPulse/logger.py,sha256=HwceD81nLiLCh_WGcCVsEG6OqL-NL5HmdONFTGH-8Og,6023
|
|
5
|
-
ErisPulse/raiserr.py,sha256=WTMQEUbUSx4PS9_l9cEnuNuvm6zulgwQFipqVZZg3zc,1296
|
|
6
|
-
ErisPulse/util.py,sha256=90j71c32yP-s2zy1SM-1FExJRu7pWpkuPVQSXCaVTrg,1087
|
|
7
|
-
erispulse-1.0.14.dev2.dist-info/METADATA,sha256=THVd9A1Nl5F0ij89R0mOuwvZxgO2ufyJlk72GZeBjAQ,2502
|
|
8
|
-
erispulse-1.0.14.dev2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
9
|
-
erispulse-1.0.14.dev2.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
10
|
-
erispulse-1.0.14.dev2.dist-info/top_level.txt,sha256=Lm_qtkVvNJR8_dXh_qEDdl_12cZGpic-i4HUlVVUMZc,10
|
|
11
|
-
erispulse-1.0.14.dev2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|