ErisPulse 1.1.8__py3-none-any.whl → 1.1.11__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 +9 -3
- ErisPulse/__main__.py +600 -198
- ErisPulse/adapter.py +60 -13
- ErisPulse/db.py +5 -2
- ErisPulse/mods.py +5 -0
- ErisPulse/util.py +28 -2
- {erispulse-1.1.8.dist-info → erispulse-1.1.11.dist-info}/METADATA +15 -3
- erispulse-1.1.11.dist-info/RECORD +14 -0
- erispulse-1.1.11.dist-info/licenses/LICENSE +21 -0
- erispulse-1.1.8.dist-info/RECORD +0 -13
- {erispulse-1.1.8.dist-info → erispulse-1.1.11.dist-info}/WHEEL +0 -0
- {erispulse-1.1.8.dist-info → erispulse-1.1.11.dist-info}/entry_points.txt +0 -0
- {erispulse-1.1.8.dist-info → erispulse-1.1.11.dist-info}/top_level.txt +0 -0
ErisPulse/adapter.py
CHANGED
|
@@ -47,7 +47,7 @@ class BaseAdapter:
|
|
|
47
47
|
# 绑定当前适配器的 Send 实例
|
|
48
48
|
self.Send = self.__class__.Send(self)
|
|
49
49
|
|
|
50
|
-
def on(self, event_type: str):
|
|
50
|
+
def on(self, event_type: str = "*"):
|
|
51
51
|
def decorator(func: Callable):
|
|
52
52
|
@functools.wraps(func)
|
|
53
53
|
async def wrapper(*args, **kwargs):
|
|
@@ -69,11 +69,32 @@ class BaseAdapter:
|
|
|
69
69
|
async def shutdown(self):
|
|
70
70
|
raise NotImplementedError
|
|
71
71
|
|
|
72
|
+
def add_handler(self, *args):
|
|
73
|
+
if len(args) == 1:
|
|
74
|
+
event_type = "*"
|
|
75
|
+
handler = args[0]
|
|
76
|
+
elif len(args) == 2:
|
|
77
|
+
event_type, handler = args
|
|
78
|
+
else:
|
|
79
|
+
raise TypeError("add_handler() 接受 1 个(监听所有事件)或 2 个参数(指定事件类型)")
|
|
80
|
+
|
|
81
|
+
@functools.wraps(handler)
|
|
82
|
+
async def wrapper(*handler_args, **handler_kwargs):
|
|
83
|
+
return await handler(*handler_args, **handler_kwargs)
|
|
84
|
+
|
|
85
|
+
self._handlers[event_type].append(wrapper)
|
|
72
86
|
async def emit(self, event_type: str, data: Any):
|
|
87
|
+
# 先执行中间件
|
|
73
88
|
for middleware in self._middlewares:
|
|
74
89
|
data = await middleware(data)
|
|
75
90
|
|
|
76
|
-
|
|
91
|
+
# 触发具体事件类型的处理器
|
|
92
|
+
if event_type in self._handlers:
|
|
93
|
+
for handler in self._handlers[event_type]:
|
|
94
|
+
await handler(data)
|
|
95
|
+
|
|
96
|
+
# 触发通配符 "*" 的处理器
|
|
97
|
+
for handler in self._handlers.get("*", []):
|
|
77
98
|
await handler(data)
|
|
78
99
|
|
|
79
100
|
async def send(self, target_type: str, target_id: str, message: Any, **kwargs):
|
|
@@ -107,6 +128,17 @@ class AdapterManager:
|
|
|
107
128
|
self._adapters[platform] = instance
|
|
108
129
|
self._platform_to_instance[platform] = instance
|
|
109
130
|
|
|
131
|
+
if len(platform) <= 10:
|
|
132
|
+
from itertools import product
|
|
133
|
+
combinations = [''.join(c) for c in product(*[(ch.lower(), ch.upper()) for ch in platform])]
|
|
134
|
+
for name in set(combinations):
|
|
135
|
+
setattr(self, name, instance)
|
|
136
|
+
else:
|
|
137
|
+
self.logger.warning(f"平台名 {platform} 过长,如果您是开发者,请考虑使用更短的名称")
|
|
138
|
+
setattr(self, platform.lower(), instance)
|
|
139
|
+
setattr(self, platform.upper(), instance)
|
|
140
|
+
setattr(self, platform.capitalize(), instance)
|
|
141
|
+
|
|
110
142
|
return True
|
|
111
143
|
|
|
112
144
|
async def startup(self, platforms: List[str] = None):
|
|
@@ -131,8 +163,6 @@ class AdapterManager:
|
|
|
131
163
|
|
|
132
164
|
async def _run_adapter(self, adapter: BaseAdapter, platform: str):
|
|
133
165
|
from . import sdk
|
|
134
|
-
retry_count = 0
|
|
135
|
-
max_retry = 3
|
|
136
166
|
|
|
137
167
|
# 加锁防止并发启动
|
|
138
168
|
if not getattr(adapter, "_starting_lock", None):
|
|
@@ -144,34 +174,51 @@ class AdapterManager:
|
|
|
144
174
|
sdk.logger.info(f"适配器 {platform}(实例ID: {id(adapter)})已被其他协程启动,跳过")
|
|
145
175
|
return
|
|
146
176
|
|
|
147
|
-
|
|
177
|
+
retry_count = 0
|
|
178
|
+
fixed_delay = 3 * 60 * 60
|
|
179
|
+
backoff_intervals = [60, 10 * 60, 30 * 60, 60 * 60]
|
|
180
|
+
|
|
181
|
+
while True:
|
|
148
182
|
try:
|
|
149
183
|
await adapter.start()
|
|
150
184
|
self._started_instances.add(adapter)
|
|
151
185
|
sdk.logger.info(f"适配器 {platform}(实例ID: {id(adapter)})已启动")
|
|
152
|
-
|
|
186
|
+
return
|
|
153
187
|
except Exception as e:
|
|
154
188
|
retry_count += 1
|
|
155
189
|
sdk.logger.error(f"平台 {platform} 启动失败(第{retry_count}次重试): {e}")
|
|
190
|
+
|
|
156
191
|
try:
|
|
157
192
|
await adapter.shutdown()
|
|
158
193
|
except Exception as stop_err:
|
|
159
194
|
sdk.logger.warning(f"停止适配器失败: {stop_err}")
|
|
160
195
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
196
|
+
# 计算等待时间
|
|
197
|
+
if retry_count <= len(backoff_intervals):
|
|
198
|
+
wait_time = backoff_intervals[retry_count - 1]
|
|
199
|
+
else:
|
|
200
|
+
wait_time = fixed_delay
|
|
201
|
+
|
|
202
|
+
sdk.logger.info(f"将在 {wait_time // 60} 分钟后再次尝试重启 {platform}")
|
|
203
|
+
await asyncio.sleep(wait_time)
|
|
204
|
+
|
|
164
205
|
async def shutdown(self):
|
|
165
206
|
for adapter in self._adapters.values():
|
|
166
207
|
await adapter.shutdown()
|
|
167
208
|
|
|
168
209
|
def get(self, platform: str) -> BaseAdapter:
|
|
169
|
-
|
|
210
|
+
platform_lower = platform.lower()
|
|
211
|
+
for registered, instance in self._adapters.items():
|
|
212
|
+
if registered.lower() == platform_lower:
|
|
213
|
+
return instance
|
|
214
|
+
return None
|
|
170
215
|
|
|
171
216
|
def __getattr__(self, platform: str) -> BaseAdapter:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
217
|
+
platform_lower = platform.lower()
|
|
218
|
+
for registered, instance in self._adapters.items():
|
|
219
|
+
if registered.lower() == platform_lower:
|
|
220
|
+
return instance
|
|
221
|
+
raise AttributeError(f"平台 {platform} 的适配器未注册")
|
|
175
222
|
|
|
176
223
|
@property
|
|
177
224
|
def platforms(self) -> list:
|
ErisPulse/db.py
CHANGED
|
@@ -113,9 +113,12 @@ from ErisPulse import sdk
|
|
|
113
113
|
try:
|
|
114
114
|
with open(env_file, "w", encoding="utf-8") as f:
|
|
115
115
|
f.write(content)
|
|
116
|
-
|
|
116
|
+
return True
|
|
117
117
|
except Exception as e:
|
|
118
|
-
|
|
118
|
+
from . import sdk
|
|
119
|
+
sdk.logger.error(f"无法创建 env.py 文件: {e}")
|
|
120
|
+
return False
|
|
121
|
+
return False
|
|
119
122
|
|
|
120
123
|
def __getattr__(self, key):
|
|
121
124
|
try:
|
ErisPulse/mods.py
CHANGED
|
@@ -27,6 +27,11 @@ class ModuleManager:
|
|
|
27
27
|
def set_module_status(self, module_name: str, status: bool) -> None:
|
|
28
28
|
self.env.set(f"{self.status_prefix}{module_name}", bool(status))
|
|
29
29
|
|
|
30
|
+
module_info = self.get_module(module_name)
|
|
31
|
+
if module_info:
|
|
32
|
+
module_info["status"] = bool(status)
|
|
33
|
+
self.env.set(f"{self.module_prefix}{module_name}", module_info)
|
|
34
|
+
|
|
30
35
|
def get_module_status(self, module_name: str) -> bool:
|
|
31
36
|
status = self.env.get(f"{self.status_prefix}{module_name}", True)
|
|
32
37
|
if isinstance(status, str):
|
ErisPulse/util.py
CHANGED
|
@@ -25,9 +25,35 @@ def topological_sort(elements, dependencies, error):
|
|
|
25
25
|
queue.append(neighbor)
|
|
26
26
|
if len(sorted_list) != len(elements):
|
|
27
27
|
from . import sdk
|
|
28
|
-
sdk.logger.error(f"
|
|
28
|
+
sdk.logger.error(f"依赖导入错误: {elements} vs {sorted_list} | 发生了循环依赖")
|
|
29
29
|
return sorted_list
|
|
30
30
|
|
|
31
|
+
def show_topology():
|
|
32
|
+
from . import sdk
|
|
33
|
+
dep_data = sdk.env.get('module_dependencies')
|
|
34
|
+
if not dep_data:
|
|
35
|
+
return "未找到模块依赖关系数据,请先运行sdk.init()"
|
|
36
|
+
|
|
37
|
+
sorted_modules = topological_sort(
|
|
38
|
+
dep_data['modules'],
|
|
39
|
+
dep_data['dependencies'],
|
|
40
|
+
sdk.raiserr.CycleDependencyError
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
tree = {}
|
|
44
|
+
for module in sorted_modules:
|
|
45
|
+
tree[module] = dep_data['dependencies'].get(module, [])
|
|
46
|
+
|
|
47
|
+
result = ["模块拓扑关系表:"]
|
|
48
|
+
for i, module in enumerate(sorted_modules, 1):
|
|
49
|
+
deps = dep_data['dependencies'].get(module, [])
|
|
50
|
+
indent = " " * (len(deps) if deps else 0)
|
|
51
|
+
result.append(f"{i}. {indent}{module}")
|
|
52
|
+
if deps:
|
|
53
|
+
result.append(f" {indent}└─ 依赖: {', '.join(deps)}")
|
|
54
|
+
|
|
55
|
+
return "\n".join(result)
|
|
56
|
+
|
|
31
57
|
def ExecAsync(async_func, *args, **kwargs):
|
|
32
58
|
loop = asyncio.get_event_loop()
|
|
33
59
|
return loop.run_in_executor(executor, lambda: asyncio.run(async_func(*args, **kwargs)))
|
|
@@ -70,4 +96,4 @@ def retry(max_attempts=3, delay=1):
|
|
|
70
96
|
raise
|
|
71
97
|
time.sleep(delay)
|
|
72
98
|
return wrapper
|
|
73
|
-
return decorator
|
|
99
|
+
return decorator
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.11
|
|
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
|
|
@@ -18,11 +18,23 @@ Classifier: Operating System :: OS Independent
|
|
|
18
18
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
19
|
Requires-Python: >=3.7
|
|
20
20
|
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
21
22
|
Requires-Dist: aiohttp
|
|
23
|
+
Requires-Dist: watchdog
|
|
24
|
+
Dynamic: license-file
|
|
22
25
|
|
|
23
26
|

|
|
27
|
+
**ErisPulse** 是基于 [Framer](https://github.com/FramerOrg/Framer) 构建的异步机器人开发框架。
|
|
28
|
+
|
|
29
|
+
## 合作伙伴
|
|
30
|
+
[](https://github.com/FramerOrg)
|
|
31
|
+
|
|
32
|
+
### 框架选型指南
|
|
33
|
+
| 需求 | 推荐框架 | 理由 |
|
|
34
|
+
|-------------------|----------------|-----------------------------|
|
|
35
|
+
| 轻量化/底层模块化 | [Framer](https://github.com/FramerOrg/Framer) | 高度解耦的模块化设计 |
|
|
36
|
+
| 全功能机器人开发 | ErisPulse | 开箱即用的完整解决方案 |
|
|
24
37
|
|
|
25
|
-
基于 [RyhBotPythonSDK V2](https://github.com/runoneall/RyhBotPythonSDK2) 构建,由 [sdkFrame](https://github.com/runoneall/sdkFrame) 提供支持的异步机器人开发框架。
|
|
26
38
|
|
|
27
39
|
## ✨ 核心特性
|
|
28
40
|
- ⚡ 完全异步架构设计(async/await)
|
|
@@ -48,7 +60,7 @@ from ErisPulse import sdk, logger
|
|
|
48
60
|
async def main():
|
|
49
61
|
sdk.init()
|
|
50
62
|
logger.info("ErisPulse 已启动")
|
|
51
|
-
# 这里可以添加自定义逻辑
|
|
63
|
+
# 这里可以添加自定义逻辑
|
|
52
64
|
|
|
53
65
|
if __name__ == "__main__":
|
|
54
66
|
asyncio.run(main())
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
ErisPulse/__init__.py,sha256=tCgjl-0rUeiHV_2rdFlQ-CVTFzbKdjdqXvUApRc6Avk,8095
|
|
2
|
+
ErisPulse/__main__.py,sha256=ZdrOqsZU1C5wJ4CycTge9l7IUldVUDtak17yWRxnxhs,47728
|
|
3
|
+
ErisPulse/adapter.py,sha256=slwStdR8fNPp72o5miut9ffLjwekfljzq0c1qkA1tmg,8625
|
|
4
|
+
ErisPulse/db.py,sha256=DuIc19GbCQMi17BWfLjaDHnthIbdysTSRxaYVFur07w,4424
|
|
5
|
+
ErisPulse/logger.py,sha256=sMA1mUZvwJ8wHwdyCrgIf_VRICv_uBCkx3tmd1stF3E,6094
|
|
6
|
+
ErisPulse/mods.py,sha256=lNiZP2EcfUhYRnOQwROyVnmhsfmk8JAZhmbhxfC2-VQ,3513
|
|
7
|
+
ErisPulse/raiserr.py,sha256=z8BigWkVrBE9dD_dJa5np2YYREwdugyWXKE4_-LEO_Q,2616
|
|
8
|
+
ErisPulse/util.py,sha256=ux3-QRT0_JjabL6S9KChhyR1E_CSRiVYEFYV5txML1M,3406
|
|
9
|
+
erispulse-1.1.11.dist-info/licenses/LICENSE,sha256=plj4EYVfKAzc0ZWoC5T2vsQ86u0yLpu17NdAPeIcgVo,1066
|
|
10
|
+
erispulse-1.1.11.dist-info/METADATA,sha256=6_XH6PEXuF2kNGuorHF1msyRdF6R1iyybLRx0OegrXk,2751
|
|
11
|
+
erispulse-1.1.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
erispulse-1.1.11.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
13
|
+
erispulse-1.1.11.dist-info/top_level.txt,sha256=Lm_qtkVvNJR8_dXh_qEDdl_12cZGpic-i4HUlVVUMZc,10
|
|
14
|
+
erispulse-1.1.11.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ErisPulse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
erispulse-1.1.8.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ErisPulse/__init__.py,sha256=wNfAnZu0-jFzCLyQhfiXvj7lb0DxSLmRCBUowxUYJW0,7783
|
|
2
|
-
ErisPulse/__main__.py,sha256=-UdhsYP_X7EagomCjo73Y_qQdXwtMbDS5KoOSrI-OcU,32181
|
|
3
|
-
ErisPulse/adapter.py,sha256=u7XdJxIEHxGRh4RXwTX-vv1R1_oP0M6ZJBehe3rKi0I,6807
|
|
4
|
-
ErisPulse/db.py,sha256=OIndsysBOUfSyGMhKs-YVYj9dv5D92YIAltM0ZmwrkA,4379
|
|
5
|
-
ErisPulse/logger.py,sha256=sMA1mUZvwJ8wHwdyCrgIf_VRICv_uBCkx3tmd1stF3E,6094
|
|
6
|
-
ErisPulse/mods.py,sha256=M9XQWUQYNZ11m845hxbewBAauWXnysy-aOdLwb5xy_M,3312
|
|
7
|
-
ErisPulse/raiserr.py,sha256=z8BigWkVrBE9dD_dJa5np2YYREwdugyWXKE4_-LEO_Q,2616
|
|
8
|
-
ErisPulse/util.py,sha256=b9TqyRZKkpclN2fkHmWqBl3lnBMnUbucMvKvbqD5Ws8,2541
|
|
9
|
-
erispulse-1.1.8.dist-info/METADATA,sha256=UnKdj5QcnPTnmW0zRHWo2mxUME2CjJSM9ffcB1UUQNM,2282
|
|
10
|
-
erispulse-1.1.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
-
erispulse-1.1.8.dist-info/entry_points.txt,sha256=AjKvOdYR7QGXVpEJhjUYUwV2JluE4lm9vNbknC3hjOM,155
|
|
12
|
-
erispulse-1.1.8.dist-info/top_level.txt,sha256=Lm_qtkVvNJR8_dXh_qEDdl_12cZGpic-i4HUlVVUMZc,10
|
|
13
|
-
erispulse-1.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|