ErisPulse 2.4.2.dev1__py3-none-any.whl → 2.4.3.dev0__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/CLI/commands/run.py +90 -190
- ErisPulse/CLI/commands/run.pyi +14 -67
- ErisPulse/Core/Bases/adapter.py +4 -5
- ErisPulse/Core/Bases/adapter.pyi +1 -1
- ErisPulse/Core/__init__.py +22 -33
- ErisPulse/Core/adapter.py +22 -14
- ErisPulse/Core/adapter.pyi +3 -7
- ErisPulse/Core/config.py +3 -2
- ErisPulse/Core/lifecycle.py +3 -1
- ErisPulse/Core/lifecycle.pyi +0 -9
- ErisPulse/Core/module.py +11 -7
- ErisPulse/Core/module.pyi +3 -7
- ErisPulse/Core/router.py +20 -15
- ErisPulse/Core/router.pyi +1 -2
- ErisPulse/__init__.py +0 -12
- ErisPulse/__init__.pyi +1 -2
- ErisPulse/sdk.py +9 -67
- ErisPulse/sdk.pyi +2 -6
- {erispulse-2.4.2.dev1.dist-info → erispulse-2.4.3.dev0.dist-info}/METADATA +21 -19
- {erispulse-2.4.2.dev1.dist-info → erispulse-2.4.3.dev0.dist-info}/RECORD +23 -25
- ErisPulse/runtime/cleanup.py +0 -202
- ErisPulse/runtime/cleanup.pyi +0 -76
- {erispulse-2.4.2.dev1.dist-info → erispulse-2.4.3.dev0.dist-info}/WHEEL +0 -0
- {erispulse-2.4.2.dev1.dist-info → erispulse-2.4.3.dev0.dist-info}/entry_points.txt +0 -0
- {erispulse-2.4.2.dev1.dist-info → erispulse-2.4.3.dev0.dist-info}/licenses/LICENSE +0 -0
ErisPulse/CLI/commands/run.py
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Run 命令实现
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
直接运行主程序,支持热重载模式
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
8
|
import time
|
|
9
|
-
import
|
|
10
|
-
import subprocess
|
|
11
|
-
import tempfile
|
|
9
|
+
import asyncio
|
|
12
10
|
from argparse import ArgumentParser
|
|
13
11
|
from watchdog.observers import Observer
|
|
14
12
|
from rich.panel import Panel
|
|
@@ -22,127 +20,38 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
22
20
|
"""
|
|
23
21
|
文件系统事件处理器
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
监控 .py 文件变更并触发 sdk.restart() 热重载
|
|
24
|
+
|
|
27
25
|
{!--< tips >!--}
|
|
28
|
-
1.
|
|
29
|
-
2.
|
|
30
|
-
3.
|
|
31
|
-
4. 进程终止时使用清理信号文件确保资源正确释放
|
|
26
|
+
1. 文件监控运行在独立线程
|
|
27
|
+
2. 通过 run_coroutine_threadsafe 安全调度到事件循环
|
|
28
|
+
3. 内置 1 秒防抖,避免短时间内多次重载
|
|
32
29
|
{!--< /tips >!--}
|
|
33
30
|
"""
|
|
34
31
|
|
|
35
|
-
def __init__(self,
|
|
36
|
-
"""
|
|
37
|
-
初始化处理器
|
|
38
|
-
|
|
39
|
-
:param script_path: [str] 要监控的脚本路径
|
|
40
|
-
:param reload_mode: [bool] 是否启用重载模式 (默认: False)
|
|
41
|
-
"""
|
|
32
|
+
def __init__(self, loop: asyncio.AbstractEventLoop):
|
|
42
33
|
super().__init__()
|
|
43
|
-
self.
|
|
44
|
-
self.
|
|
45
|
-
self.last_reload = time.time()
|
|
46
|
-
self.reload_mode = reload_mode
|
|
47
|
-
# 清理信号文件
|
|
48
|
-
self.cleanup_file = os.path.join(
|
|
49
|
-
tempfile.gettempdir(),
|
|
50
|
-
f"erispulse_cleanup_{os.getpid()}.signal"
|
|
51
|
-
)
|
|
52
|
-
self.start_process()
|
|
53
|
-
|
|
54
|
-
def start_process(self):
|
|
55
|
-
"""
|
|
56
|
-
启动监控进程
|
|
57
|
-
|
|
58
|
-
注入环境变量,让子进程知道自己是由 CLI 启动的
|
|
59
|
-
"""
|
|
60
|
-
if self.process:
|
|
61
|
-
self._terminate_process()
|
|
62
|
-
|
|
63
|
-
console.print(f"[bold]启动进程: [path]{self.script_path}[/][/]")
|
|
64
|
-
env = os.environ.copy()
|
|
65
|
-
env['ERISPULSE_SUBPROCESS'] = 'true'
|
|
66
|
-
env['ERISPULSE_CLEANUP_FILE'] = self.cleanup_file
|
|
67
|
-
|
|
68
|
-
self.process = subprocess.Popen(
|
|
69
|
-
[sys.executable, self.script_path],
|
|
70
|
-
stdin=sys.stdin,
|
|
71
|
-
stdout=sys.stdout,
|
|
72
|
-
stderr=sys.stderr,
|
|
73
|
-
env=env
|
|
74
|
-
)
|
|
75
|
-
self.last_reload = time.time()
|
|
76
|
-
|
|
77
|
-
def _terminate_process(self):
|
|
78
|
-
"""
|
|
79
|
-
终止当前进程
|
|
80
|
-
|
|
81
|
-
创建清理信号文件,通知子进程执行清理操作
|
|
82
|
-
等待最多 10 秒让进程正常退出
|
|
83
|
-
"""
|
|
84
|
-
try:
|
|
85
|
-
# console.print(f"[dim]正在终止进程 (PID: {self.process.pid})[/]")
|
|
86
|
-
console.print(f"[dim]正在终止进程[/]")
|
|
87
|
-
|
|
88
|
-
# 创建清理信号文件
|
|
89
|
-
try:
|
|
90
|
-
with open(self.cleanup_file, 'w') as f:
|
|
91
|
-
f.write('cleanup')
|
|
92
|
-
# console.print(f"[dim]已创建清理信号文件: {self.cleanup_file}[/]")
|
|
93
|
-
except Exception as e:
|
|
94
|
-
console.print(f"[warning]创建清理信号文件失败: {e}[/]")
|
|
95
|
-
|
|
96
|
-
# 等待子进程开始清理
|
|
97
|
-
time.sleep(0.5)
|
|
98
|
-
|
|
99
|
-
# 终止进程
|
|
100
|
-
self.process.terminate()
|
|
101
|
-
|
|
102
|
-
# 等待进程退出
|
|
103
|
-
returncode = self.process.wait(timeout=10)
|
|
104
|
-
console.print(f"[dim]进程已退出[/]")
|
|
105
|
-
|
|
106
|
-
# 清理信号文件
|
|
107
|
-
try:
|
|
108
|
-
if os.path.exists(self.cleanup_file):
|
|
109
|
-
os.remove(self.cleanup_file)
|
|
110
|
-
except Exception:
|
|
111
|
-
pass
|
|
112
|
-
except subprocess.TimeoutExpired:
|
|
113
|
-
console.print("[warning]进程未在10秒内正常退出,强制终止[/]")
|
|
114
|
-
self.process.kill()
|
|
115
|
-
self.process.wait()
|
|
116
|
-
except Exception as e:
|
|
117
|
-
console.print(f"[error]终止进程时出错: {e}[/]")
|
|
34
|
+
self._loop = loop
|
|
35
|
+
self._last_reload = 0.0
|
|
118
36
|
|
|
119
37
|
def on_modified(self, event):
|
|
120
|
-
"""
|
|
121
|
-
文件修改事件处理
|
|
122
|
-
|
|
123
|
-
仅在 --reload 模式下监控 .py 文件变更
|
|
124
|
-
|
|
125
|
-
:param event: [FileSystemEvent] 文件系统事件
|
|
126
|
-
"""
|
|
127
38
|
now = time.time()
|
|
128
|
-
if now - self.
|
|
39
|
+
if now - self._last_reload < 1.0:
|
|
129
40
|
return
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
self.
|
|
41
|
+
if event.src_path.endswith(".py"):
|
|
42
|
+
self._last_reload = now
|
|
43
|
+
self._schedule_reload(event)
|
|
133
44
|
|
|
134
|
-
def
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
""
|
|
143
|
-
|
|
144
|
-
self._terminate_process()
|
|
145
|
-
self.start_process()
|
|
45
|
+
def _schedule_reload(self, event):
|
|
46
|
+
async def _do_reload():
|
|
47
|
+
try:
|
|
48
|
+
from ... import sdk
|
|
49
|
+
await sdk.restart()
|
|
50
|
+
except Exception as e:
|
|
51
|
+
console.print(f"[error]热重载失败: {e}[/]")
|
|
52
|
+
|
|
53
|
+
console.print(f"检测到文件变更 ({os.path.basename(event.src_path)}),正在热重载...")
|
|
54
|
+
asyncio.run_coroutine_threadsafe(_do_reload(), self._loop)
|
|
146
55
|
|
|
147
56
|
|
|
148
57
|
class RunCommand(Command):
|
|
@@ -156,16 +65,11 @@ class RunCommand(Command):
|
|
|
156
65
|
description = "运行主程序"
|
|
157
66
|
|
|
158
67
|
def add_arguments(self, parser: ArgumentParser):
|
|
159
|
-
"""
|
|
160
|
-
添加命令行参数
|
|
161
|
-
|
|
162
|
-
:param parser: [ArgumentParser] 参数解析器
|
|
163
|
-
"""
|
|
164
68
|
parser.add_argument(
|
|
165
69
|
'script',
|
|
166
70
|
nargs='?',
|
|
167
|
-
default=
|
|
168
|
-
help='要运行的主程序路径 (
|
|
71
|
+
default=None,
|
|
72
|
+
help='要运行的主程序路径 (不指定则直接运行 SDK)'
|
|
169
73
|
)
|
|
170
74
|
parser.add_argument(
|
|
171
75
|
'--reload',
|
|
@@ -175,78 +79,74 @@ class RunCommand(Command):
|
|
|
175
79
|
)
|
|
176
80
|
|
|
177
81
|
def execute(self, args):
|
|
178
|
-
"""
|
|
179
|
-
执行命令
|
|
180
|
-
|
|
181
|
-
:param args: [Namespace] 命令行参数
|
|
182
|
-
"""
|
|
183
82
|
script = args.script
|
|
184
83
|
reload_mode = args.reload
|
|
185
84
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
self._setup_watchdog(script, reload_mode)
|
|
197
|
-
|
|
198
|
-
try:
|
|
199
|
-
while True:
|
|
200
|
-
time.sleep(0.5)
|
|
201
|
-
except KeyboardInterrupt:
|
|
202
|
-
self._cleanup()
|
|
203
|
-
|
|
204
|
-
def _setup_watchdog(self, script_path: str, reload_mode: bool) -> list:
|
|
85
|
+
if script:
|
|
86
|
+
if not os.path.exists(script):
|
|
87
|
+
console.print(f"[error]脚本 [path]{script}[/] 不存在[/]")
|
|
88
|
+
console.print("[info]使用 [cyan]epsdk init[/cyan] 创建新项目[/]")
|
|
89
|
+
return
|
|
90
|
+
self._run_script(script, reload_mode)
|
|
91
|
+
else:
|
|
92
|
+
self._run_internal(reload_mode)
|
|
93
|
+
|
|
94
|
+
def _run_internal(self, reload_mode: bool):
|
|
205
95
|
"""
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
:param script_path: [str] 要监控的脚本路径
|
|
209
|
-
:param reload_mode: [bool] 是否启用重载模式
|
|
210
|
-
:return: [list] 监控的目录列表
|
|
96
|
+
直接运行 SDK(不指定脚本时)
|
|
211
97
|
"""
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
else:
|
|
232
|
-
console.print(Panel(
|
|
233
|
-
f"主程序: [path]{script_path}[/]",
|
|
234
|
-
title="程序已启动",
|
|
235
|
-
border_style="info"
|
|
236
|
-
))
|
|
237
|
-
|
|
238
|
-
# 启动 observer
|
|
239
|
-
self.observer.start()
|
|
240
|
-
return watch_dirs
|
|
241
|
-
|
|
242
|
-
def _cleanup(self):
|
|
98
|
+
async def _run():
|
|
99
|
+
from ... import sdk
|
|
100
|
+
|
|
101
|
+
if reload_mode:
|
|
102
|
+
loop = asyncio.get_running_loop()
|
|
103
|
+
self._setup_watchdog(".", loop)
|
|
104
|
+
|
|
105
|
+
await sdk.run(keep_running=True)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
asyncio.run(_run())
|
|
109
|
+
except KeyboardInterrupt:
|
|
110
|
+
pass
|
|
111
|
+
finally:
|
|
112
|
+
if reload_mode and hasattr(self, '_observer'):
|
|
113
|
+
self._observer.stop()
|
|
114
|
+
self._observer.join()
|
|
115
|
+
|
|
116
|
+
def _run_script(self, script_path: str, reload_mode: bool):
|
|
243
117
|
"""
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
停止文件监控,终止子进程
|
|
118
|
+
运行指定脚本文件
|
|
247
119
|
"""
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
if
|
|
252
|
-
|
|
120
|
+
async def _run():
|
|
121
|
+
from ... import sdk
|
|
122
|
+
|
|
123
|
+
if reload_mode:
|
|
124
|
+
loop = asyncio.get_running_loop()
|
|
125
|
+
watch_dir = os.path.dirname(os.path.abspath(script_path))
|
|
126
|
+
self._setup_watchdog(watch_dir, loop)
|
|
127
|
+
|
|
128
|
+
await sdk.run(keep_running=True)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
asyncio.run(_run())
|
|
132
|
+
except KeyboardInterrupt:
|
|
133
|
+
pass
|
|
134
|
+
finally:
|
|
135
|
+
if reload_mode and hasattr(self, '_observer'):
|
|
136
|
+
self._observer.stop()
|
|
137
|
+
self._observer.join()
|
|
138
|
+
|
|
139
|
+
def _setup_watchdog(self, watch_dir: str, loop: asyncio.AbstractEventLoop):
|
|
140
|
+
if not os.path.exists(watch_dir):
|
|
141
|
+
return
|
|
142
|
+
|
|
143
|
+
self._observer = Observer()
|
|
144
|
+
self._handler = ReloadHandler(loop=loop)
|
|
145
|
+
self._observer.schedule(self._handler, watch_dir, recursive=True)
|
|
146
|
+
self._observer.start()
|
|
147
|
+
|
|
148
|
+
console.print(Panel(
|
|
149
|
+
f"[bold]开发重载模式[/]\n监控目录: [path]{watch_dir}[/]",
|
|
150
|
+
title="热重载已启动",
|
|
151
|
+
border_style="info"
|
|
152
|
+
))
|
ErisPulse/CLI/commands/run.pyi
CHANGED
|
@@ -7,14 +7,12 @@
|
|
|
7
7
|
"""
|
|
8
8
|
Run 命令实现
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
直接运行主程序,支持热重载模式
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
import os
|
|
14
14
|
import time
|
|
15
|
-
import
|
|
16
|
-
import subprocess
|
|
17
|
-
import tempfile
|
|
15
|
+
import asyncio
|
|
18
16
|
from argparse import ArgumentParser
|
|
19
17
|
from watchdog.observers import Observer
|
|
20
18
|
from rich.panel import Panel
|
|
@@ -26,56 +24,19 @@ class ReloadHandler(FileSystemEventHandler):
|
|
|
26
24
|
"""
|
|
27
25
|
文件系统事件处理器
|
|
28
26
|
|
|
29
|
-
|
|
27
|
+
监控 .py 文件变更并触发 sdk.restart() 热重载
|
|
30
28
|
|
|
31
29
|
{!--< tips >!--}
|
|
32
|
-
1.
|
|
33
|
-
2.
|
|
34
|
-
3.
|
|
35
|
-
4. 进程终止时使用清理信号文件确保资源正确释放
|
|
30
|
+
1. 文件监控运行在独立线程
|
|
31
|
+
2. 通过 run_coroutine_threadsafe 安全调度到事件循环
|
|
32
|
+
3. 内置 1 秒防抖,避免短时间内多次重载
|
|
36
33
|
{!--< /tips >!--}
|
|
37
34
|
"""
|
|
38
|
-
def __init__(self: None,
|
|
39
|
-
"""
|
|
40
|
-
初始化处理器
|
|
41
|
-
|
|
42
|
-
:param script_path: [str] 要监控的脚本路径
|
|
43
|
-
:param reload_mode: [bool] 是否启用重载模式 (默认: False)
|
|
44
|
-
"""
|
|
45
|
-
...
|
|
46
|
-
def start_process(self: object) -> ...:
|
|
47
|
-
"""
|
|
48
|
-
启动监控进程
|
|
49
|
-
|
|
50
|
-
注入环境变量,让子进程知道自己是由 CLI 启动的
|
|
51
|
-
"""
|
|
52
|
-
...
|
|
53
|
-
def _terminate_process(self: object) -> ...:
|
|
54
|
-
"""
|
|
55
|
-
终止当前进程
|
|
56
|
-
|
|
57
|
-
创建清理信号文件,通知子进程执行清理操作
|
|
58
|
-
等待最多 10 秒让进程正常退出
|
|
59
|
-
"""
|
|
35
|
+
def __init__(self: None, loop: asyncio.AbstractEventLoop) -> ...:
|
|
60
36
|
...
|
|
61
37
|
def on_modified(self: object, event: ...) -> ...:
|
|
62
|
-
"""
|
|
63
|
-
文件修改事件处理
|
|
64
|
-
|
|
65
|
-
仅在 --reload 模式下监控 .py 文件变更
|
|
66
|
-
|
|
67
|
-
:param event: [FileSystemEvent] 文件系统事件
|
|
68
|
-
"""
|
|
69
38
|
...
|
|
70
|
-
def
|
|
71
|
-
"""
|
|
72
|
-
处理热重载逻辑
|
|
73
|
-
|
|
74
|
-
创建清理信号文件,通知子进程执行清理,然后重启进程
|
|
75
|
-
|
|
76
|
-
:param event: [FileSystemEvent] 文件系统事件
|
|
77
|
-
:param reason: [str] 重载原因
|
|
78
|
-
"""
|
|
39
|
+
def _schedule_reload(self: object, event: ...) -> ...:
|
|
79
40
|
...
|
|
80
41
|
|
|
81
42
|
class RunCommand(Command):
|
|
@@ -85,32 +46,18 @@ class RunCommand(Command):
|
|
|
85
46
|
运行主程序,支持热重载模式
|
|
86
47
|
"""
|
|
87
48
|
def add_arguments(self: object, parser: ArgumentParser) -> ...:
|
|
88
|
-
"""
|
|
89
|
-
添加命令行参数
|
|
90
|
-
|
|
91
|
-
:param parser: [ArgumentParser] 参数解析器
|
|
92
|
-
"""
|
|
93
49
|
...
|
|
94
50
|
def execute(self: object, args: ...) -> ...:
|
|
95
|
-
"""
|
|
96
|
-
执行命令
|
|
97
|
-
|
|
98
|
-
:param args: [Namespace] 命令行参数
|
|
99
|
-
"""
|
|
100
51
|
...
|
|
101
|
-
def
|
|
52
|
+
def _run_internal(self: object, reload_mode: bool) -> ...:
|
|
102
53
|
"""
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
:param script_path: [str] 要监控的脚本路径
|
|
106
|
-
:param reload_mode: [bool] 是否启用重载模式
|
|
107
|
-
:return: [list] 监控的目录列表
|
|
54
|
+
直接运行 SDK(不指定脚本时)
|
|
108
55
|
"""
|
|
109
56
|
...
|
|
110
|
-
def
|
|
57
|
+
def _run_script(self: object, script_path: str, reload_mode: bool) -> ...:
|
|
111
58
|
"""
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
停止文件监控,终止子进程
|
|
59
|
+
运行指定脚本文件
|
|
115
60
|
"""
|
|
116
61
|
...
|
|
62
|
+
def _setup_watchdog(self: object, watch_dir: str, loop: asyncio.AbstractEventLoop) -> ...:
|
|
63
|
+
...
|
ErisPulse/Core/Bases/adapter.py
CHANGED
|
@@ -341,11 +341,10 @@ class BaseAdapter(ABC):
|
|
|
341
341
|
"""
|
|
342
342
|
raise NotImplementedError("适配器必须实现shutdown方法")
|
|
343
343
|
|
|
344
|
-
async def emit(self
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
"适配器调用了一个被弃用的原生方法emit,请检查适配器的实现,如果你是开发者请查看ErisPulse的文档进行更新。如果你是普通用户请查看本适配器是否有更新"
|
|
344
|
+
async def emit(self, *args, **kwargs):
|
|
345
|
+
raise NotImplementedError(
|
|
346
|
+
"适配器的 emit 方法已被弃用。请使用 adapter.emit() 通过 AdapterManager 提交事件。"
|
|
347
|
+
"如果你是适配器开发者,请查看 ErisPulse 文档进行更新。"
|
|
349
348
|
)
|
|
350
349
|
|
|
351
350
|
def send(
|
ErisPulse/Core/Bases/adapter.pyi
CHANGED
|
@@ -158,7 +158,7 @@ class BaseAdapter(ABC):
|
|
|
158
158
|
:raises NotImplementedError: 必须由子类实现
|
|
159
159
|
"""
|
|
160
160
|
...
|
|
161
|
-
async def emit(self: object) ->
|
|
161
|
+
async def emit(self: object, *args: ..., **kwargs: ...) -> ...:
|
|
162
162
|
...
|
|
163
163
|
def send(self: object, target_type: str, target_id: str, message: Any, **kwargs: Any) -> asyncio.Task:
|
|
164
164
|
"""
|
ErisPulse/Core/__init__.py
CHANGED
|
@@ -15,44 +15,33 @@ from .config import config, ConfigManager
|
|
|
15
15
|
from . import Event
|
|
16
16
|
from .Event.message_builder import MessageBuilder
|
|
17
17
|
|
|
18
|
-
# 兼容性别名定义
|
|
19
|
-
adapter_server = router # 路由管理器别名
|
|
20
18
|
env = storage # 存储管理器别名
|
|
21
|
-
AdapterFather = BaseAdapter # 适配器基类别名
|
|
22
19
|
|
|
23
20
|
__all__ = [
|
|
24
|
-
# 事件模块
|
|
25
21
|
'Event',
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
|
|
33
|
-
'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
|
|
40
|
-
# 存储和配置相关
|
|
41
|
-
'storage', # 存储管理器
|
|
42
|
-
'StorageManager', # 存储管理器类
|
|
43
|
-
'config', # 配置管理器
|
|
44
|
-
'ConfigManager', # 配置管理器类
|
|
23
|
+
'adapter',
|
|
24
|
+
'AdapterManager',
|
|
25
|
+
'BaseAdapter',
|
|
26
|
+
'SendDSL',
|
|
27
|
+
'MessageBuilder',
|
|
28
|
+
|
|
29
|
+
'module',
|
|
30
|
+
'ModuleManager',
|
|
31
|
+
'BaseModule',
|
|
32
|
+
|
|
33
|
+
'storage',
|
|
34
|
+
'StorageManager',
|
|
35
|
+
'config',
|
|
45
36
|
'env', # 配置管理器别名
|
|
37
|
+
'ConfigManager',
|
|
38
|
+
|
|
39
|
+
'router',
|
|
40
|
+
'RouterManager',
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
|
|
52
|
-
# 基础设施
|
|
53
|
-
'logger', # 日志管理器
|
|
54
|
-
'Logger', # 日志管理器类
|
|
55
|
-
'LoggerChild', # 日志子管理器类
|
|
56
|
-
'lifecycle', # 生命周期管理器
|
|
57
|
-
'LifecycleManager', # 生命周期管理器类
|
|
42
|
+
'logger',
|
|
43
|
+
'Logger',
|
|
44
|
+
'LoggerChild',
|
|
45
|
+
'lifecycle',
|
|
46
|
+
'LifecycleManager',
|
|
58
47
|
]
|
ErisPulse/Core/adapter.py
CHANGED
|
@@ -8,6 +8,7 @@ import functools
|
|
|
8
8
|
import asyncio
|
|
9
9
|
import inspect
|
|
10
10
|
import time
|
|
11
|
+
import warnings
|
|
11
12
|
from typing import Any
|
|
12
13
|
from collections.abc import Callable
|
|
13
14
|
from collections import defaultdict
|
|
@@ -586,13 +587,11 @@ class AdapterManager(ManagerBase):
|
|
|
586
587
|
|
|
587
588
|
# 兼容性方法 - 保持向后兼容
|
|
588
589
|
def list_adapters(self) -> dict[str, bool]:
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
:return: [dict[str, bool]] 平台适配器状态字典
|
|
595
|
-
"""
|
|
590
|
+
warnings.warn(
|
|
591
|
+
"list_adapters() 已弃用,请使用 list_items() 代替",
|
|
592
|
+
DeprecationWarning,
|
|
593
|
+
stacklevel=2,
|
|
594
|
+
)
|
|
596
595
|
return self.list_items()
|
|
597
596
|
|
|
598
597
|
# ==================== 事件处理与消息发送 ====================
|
|
@@ -851,15 +850,19 @@ class AdapterManager(ManagerBase):
|
|
|
851
850
|
)
|
|
852
851
|
|
|
853
852
|
if status == "offline":
|
|
854
|
-
# 只有在非主动关闭的情况下才提交事件(避免与 shutdown() 重复)
|
|
855
853
|
if not self._is_being_shutdown:
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
854
|
+
try:
|
|
855
|
+
loop = asyncio.get_running_loop()
|
|
856
|
+
task = loop.create_task(
|
|
857
|
+
lifecycle.submit_event(
|
|
858
|
+
"adapter.bot.offline",
|
|
859
|
+
msg=f"Bot {platform}/{bot_id} 离线",
|
|
860
|
+
data={"platform": platform, "bot_id": bot_id, "status": "offline"},
|
|
861
|
+
)
|
|
861
862
|
)
|
|
862
|
-
|
|
863
|
+
self._adapter_tasks.setdefault(f"_bot_offline_{platform}_{bot_id}", task)
|
|
864
|
+
except RuntimeError:
|
|
865
|
+
pass
|
|
863
866
|
|
|
864
867
|
def _update_bot_heartbeat(self, platform: str, self_info: dict) -> None:
|
|
865
868
|
"""
|
|
@@ -1171,6 +1174,11 @@ class AdapterManager(ManagerBase):
|
|
|
1171
1174
|
"""
|
|
1172
1175
|
return self.exists(platform) and self.is_enabled(platform)
|
|
1173
1176
|
|
|
1177
|
+
def __repr__(self) -> str:
|
|
1178
|
+
registered = list(self._adapters.keys())
|
|
1179
|
+
running = [p for p, a in self._adapters.items() if a in self._started_instances]
|
|
1180
|
+
return f"<AdapterManager registered={registered} running={running}>"
|
|
1181
|
+
|
|
1174
1182
|
|
|
1175
1183
|
adapter: AdapterManager = AdapterManager()
|
|
1176
1184
|
|
ErisPulse/Core/adapter.pyi
CHANGED
|
@@ -14,6 +14,7 @@ import functools
|
|
|
14
14
|
import asyncio
|
|
15
15
|
import inspect
|
|
16
16
|
import time
|
|
17
|
+
import warnings
|
|
17
18
|
from typing import Any
|
|
18
19
|
from collections.abc import Callable
|
|
19
20
|
from collections import defaultdict
|
|
@@ -157,13 +158,6 @@ class AdapterManager(ManagerBase):
|
|
|
157
158
|
"""
|
|
158
159
|
...
|
|
159
160
|
def list_adapters(self: object) -> dict[(str, bool)]:
|
|
160
|
-
"""
|
|
161
|
-
列出所有平台适配器状态
|
|
162
|
-
|
|
163
|
-
{!--< deprecated >!--} 请使用 list_items() 代替
|
|
164
|
-
|
|
165
|
-
:return: [dict[str, bool]] 平台适配器状态字典
|
|
166
|
-
"""
|
|
167
161
|
...
|
|
168
162
|
def on(self: object, event_type: str = ...) -> Callable[([Callable], Callable)]:
|
|
169
163
|
"""
|
|
@@ -391,5 +385,7 @@ class AdapterManager(ManagerBase):
|
|
|
391
385
|
:return: [bool] 平台是否存在且启用
|
|
392
386
|
"""
|
|
393
387
|
...
|
|
388
|
+
def __repr__(self: object) -> str:
|
|
389
|
+
...
|
|
394
390
|
|
|
395
391
|
adapter: AdapterManager
|
ErisPulse/Core/config.py
CHANGED
ErisPulse/Core/lifecycle.py
CHANGED
ErisPulse/Core/lifecycle.pyi
CHANGED
|
@@ -77,15 +77,6 @@ class LifecycleManager:
|
|
|
77
77
|
"""
|
|
78
78
|
...
|
|
79
79
|
async def submit_event(self: object, event_type: str) -> None:
|
|
80
|
-
"""
|
|
81
|
-
提交生命周期事件
|
|
82
|
-
|
|
83
|
-
:param event_type: 事件名称
|
|
84
|
-
:param source: 事件来源(默认"ErisPulse")
|
|
85
|
-
:param msg: 事件描述
|
|
86
|
-
:param data: 事件相关数据
|
|
87
|
-
:param timestamp: 时间戳(默认当前时间)
|
|
88
|
-
"""
|
|
89
80
|
...
|
|
90
81
|
async def _execute_handlers(self: object, event: str, event_data: dict[(str, Any)]) -> None:
|
|
91
82
|
"""
|