ErisPulse 1.2.9__py3-none-any.whl → 2.1.0__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.
@@ -1,14 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ErisPulse
3
- Version: 1.2.9
3
+ Version: 2.1.0
4
4
  Summary: ErisPulse 是一个模块化、可扩展的异步 Python SDK 框架,主要用于构建高效、可维护的机器人应用程序。
5
5
  Author-email: "艾莉丝·格雷拉特(WSu2059)" <wsu2059@qq.com>, runoneall <runoobsteve@gmail.com>
6
6
  License: MIT License
7
7
 
8
8
  Copyright (c) 2025 ErisPulse
9
9
 
10
- Portions of this software are based on https://github.com/FramerOrg/Framer
11
- (Copyright (c) 2025 runoneall).
10
+ Portions of this software are based on https://github.com/runoneall/sdkFrame.
12
11
 
13
12
  Permission is hereby granted, free of charge, to any person obtaining a copy
14
13
  of this software and associated documentation files (the "Software"), to deal
@@ -31,6 +30,8 @@ License: MIT License
31
30
  The documentation portion of this project references content from https://codeberg.org/ybr/yhwiki,
32
31
  licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
33
32
  International License (CC BY-NC-ND 4.0).
33
+
34
+ Note: This license file has been updated to correct the upstream project reference.
34
35
  License-File: LICENSE
35
36
  Classifier: Development Status :: 5 - Production/Stable
36
37
  Classifier: Intended Audience :: Developers
@@ -46,7 +47,12 @@ Classifier: Programming Language :: Python :: 3.12
46
47
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
47
48
  Requires-Python: >=3.8
48
49
  Requires-Dist: aiohttp
50
+ Requires-Dist: fastapi>=0.116.1
51
+ Requires-Dist: hypercorn>=0.14.0
49
52
  Requires-Dist: pip
53
+ Requires-Dist: pydantic>=2.10.6
54
+ Requires-Dist: python-multipart>=0.0.20
55
+ Requires-Dist: toml
50
56
  Requires-Dist: watchdog
51
57
  Description-Content-Type: text/markdown
52
58
 
@@ -78,50 +84,19 @@ Description-Content-Type: text/markdown
78
84
 
79
85
  ## 快速开始
80
86
 
81
- ### 框架选型指南
82
-
83
- | 需求 | 推荐框架 | 理由 |
84
- |------|---------|------|
85
- | 轻量化/底层模块化 | [Framer](https://github.com/FramerOrg/Framer) | 高度解耦的模块化设计 |
86
- | 全功能机器人开发 | ErisPulse | 开箱即用的完整解决方案 |
87
-
88
- ---
89
-
90
- ## 安装指南
91
-
92
- 我们全面采用 [`uv`](https://github.com/astral-sh/uv) 作为 Python 工具链,提供更快速可靠的安装体验。
93
-
94
- > ℹ️ **uv** 是由 Astral 开发的新一代 Python 包管理工具,比传统 pip 快 10-100 倍,并具有更好的依赖解析能力。
95
-
96
- ### 1. 安装 uv
97
-
98
- #### 通用方法 (pip):
99
- ```bash
100
- pip install uv
101
- ```
87
+ ### 一键安装脚本
102
88
 
103
- #### macOS/Linux:
104
- ```bash
105
- curl -LsSf https://astral.sh/uv/install.sh | sh
106
- ```
89
+ 我们提供了一键安装脚本,支持所有主流平台:
107
90
 
108
91
  #### Windows (PowerShell):
109
- ```powershell
110
- powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
111
- ```
112
92
 
113
- 验证安装:
114
- ```bash
115
- uv --version
93
+ ```powershell
94
+ irm https://get.erisdev.com/install.ps1 -OutFile install.ps1; powershell -ExecutionPolicy Bypass -File install.ps1
116
95
  ```
117
96
 
118
- ### 2. 安装 ErisPulse
119
-
97
+ #### macOS/Linux:
120
98
  ```bash
121
- uv python install 3.12 # 安装 Python 3.12
122
- uv venv # 创建虚拟环境
123
- source .venv/bin/activate # 激活环境 (Windows: .venv\Scripts\activate)
124
- uv pip install ErisPulse --upgrade # 安装框架
99
+ curl -sSL https://get.erisdev.com/install.sh | tee install.sh >/dev/null && chmod +x install.sh && ./install.sh
125
100
  ```
126
101
 
127
102
  ---
@@ -131,7 +106,7 @@ uv pip install ErisPulse --upgrade # 安装框架
131
106
  ### 克隆项目并进入目录
132
107
 
133
108
  ```bash
134
- git clone https://github.com/ErisPulse/ErisPulse.git
109
+ git clone -b Develop/v2 https://github.com/ErisPulse/ErisPulse.git
135
110
  cd ErisPulse
136
111
  ```
137
112
 
@@ -0,0 +1,16 @@
1
+ ErisPulse/__init__.py,sha256=4jcAocMDQt4xdE9B3di8lGCtrU47Y4DY2ipW_9diYbU,26210
2
+ ErisPulse/__main__.py,sha256=DeMBevz0KyLywnd5HccgZDwVfv-EKQOpo1fsVypKwiE,18525
3
+ ErisPulse/Core/__init__.py,sha256=CIxWFdB6-0D8YJz7IdW052A4YllDsklEONCRj7mOpkQ,362
4
+ ErisPulse/Core/adapter.py,sha256=gzryZjrOy0uowE5oU4MaVTtGPW7HP4StfvP5ECC4rUk,20510
5
+ ErisPulse/Core/env.py,sha256=9WYNadD9h2jP_2wxOVBJEhH1uDzbctW7eB4Ba9RSjA4,20409
6
+ ErisPulse/Core/logger.py,sha256=035II2YvRmcAvbbCEsmMxjvAJhdLvQlUqaDXAufWk_E,5782
7
+ ErisPulse/Core/mods.py,sha256=5SPutuzbMrA-VZwiXeNxYWfrdbpLRdYfQ0RvEkFuQgg,7308
8
+ ErisPulse/Core/raiserr.py,sha256=QLQ3r7p4iFP86XBLq9mtf1wv1xSlgny35i8t5-l4DXo,4620
9
+ ErisPulse/Core/server.py,sha256=H8dUUj8mxBLEd3ick7btbWEE_m58Y6277Zo5FY7Ild4,9217
10
+ ErisPulse/Core/shellprint.py,sha256=-BFoyFho_D3XEhxIoKt6x5gO4C62LKwmJWKDUGiPjNY,5908
11
+ ErisPulse/Core/util.py,sha256=kyydBAJHHG9I7rMRzKWtLAQMZoJyBqHiBAweqcraFkU,4001
12
+ erispulse-2.1.0.dist-info/METADATA,sha256=Ip9gteaaYMG0Qkzxq_DbFnmvCYNC8ULMPxCZ6jYimeA,6161
13
+ erispulse-2.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
+ erispulse-2.1.0.dist-info/entry_points.txt,sha256=Jss71M6nEha0TA-DyVZugPYdcL14s9QpiOeIlgWxzOc,182
15
+ erispulse-2.1.0.dist-info/licenses/LICENSE,sha256=lBYj7Nk4urLvByj4HvQFxu8j9hThhFF6OGfyxAZBP9Q,1451
16
+ erispulse-2.1.0.dist-info/RECORD,,
@@ -3,3 +3,4 @@ ep = ErisPulse.__main__:main
3
3
  ep-cli = ErisPulse.__main__:main
4
4
  ep-init = ErisPulse.__init__:init
5
5
  epsdk = ErisPulse.__main__:main
6
+ epsdk-cli = ErisPulse.__main__:main
@@ -2,8 +2,7 @@ MIT License
2
2
 
3
3
  Copyright (c) 2025 ErisPulse
4
4
 
5
- Portions of this software are based on https://github.com/FramerOrg/Framer
6
- (Copyright (c) 2025 runoneall).
5
+ Portions of this software are based on https://github.com/runoneall/sdkFrame.
7
6
 
8
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
9
8
  of this software and associated documentation files (the "Software"), to deal
@@ -25,4 +24,6 @@ SOFTWARE.
25
24
 
26
25
  The documentation portion of this project references content from https://codeberg.org/ybr/yhwiki,
27
26
  licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0
28
- International License (CC BY-NC-ND 4.0).
27
+ International License (CC BY-NC-ND 4.0).
28
+
29
+ Note: This license file has been updated to correct the upstream project reference.
ErisPulse/adapter.py DELETED
@@ -1,465 +0,0 @@
1
- """
2
- # 适配器系统
3
-
4
- 提供平台适配器基类、消息发送DSL和适配器管理功能。支持多平台消息处理、事件驱动和生命周期管理。
5
-
6
- ## API 文档
7
-
8
- ### 适配器基类 (BaseAdapter)
9
- 适配器基类提供了与外部平台交互的标准接口。
10
-
11
- #### call_api(endpoint: str, **params: Any) -> Any
12
- 调用平台API的抽象方法。
13
- - 参数:
14
- - endpoint: API端点
15
- - **params: API参数
16
- - 返回:
17
- - Any: API调用结果
18
- - 说明:
19
- - 必须由子类实现
20
- - 处理与平台的实际通信
21
- - 示例:
22
- ```python
23
- class MyPlatformAdapter(BaseAdapter):
24
- async def call_api(self, endpoint: str, **params: Any) -> Any:
25
- if endpoint == "/send":
26
- return await self._send_message(params)
27
- elif endpoint == "/upload":
28
- return await self._upload_file(params)
29
- raise NotImplementedError(f"未实现的端点: {endpoint}")
30
- ```
31
-
32
- #### start() -> None
33
- 启动适配器的抽象方法。
34
- - 参数: 无
35
- - 返回:
36
- - None
37
- - 说明:
38
- - 必须由子类实现
39
- - 处理适配器的初始化和启动逻辑
40
- - 示例:
41
- ```python
42
- class MyPlatformAdapter(BaseAdapter):
43
- async def start(self) -> None:
44
- self.client = await self._create_client()
45
- self.ws = await self.client.create_websocket()
46
- self._start_heartbeat()
47
- ```
48
-
49
- #### shutdown() -> None
50
- 关闭适配器的抽象方法。
51
- - 参数: 无
52
- - 返回:
53
- - None
54
- - 说明:
55
- - 必须由子类实现
56
- - 处理资源清理和关闭逻辑
57
- - 示例:
58
- ```python
59
- class MyPlatformAdapter(BaseAdapter):
60
- async def shutdown(self) -> None:
61
- if self.ws:
62
- await self.ws.close()
63
- if self.client:
64
- await self.client.close()
65
- ```
66
-
67
- #### on(event_type: str = "*") -> Callable[[Callable[..., Any]], Callable[..., Any]]
68
- 事件监听装饰器。
69
- - 参数:
70
- - event_type: 事件类型,默认"*"表示所有事件
71
- - 返回:
72
- - Callable[[Callable[..., Any]], Callable[..., Any]]: 装饰器函数
73
- - 示例:
74
- ```python
75
- adapter = MyPlatformAdapter()
76
-
77
- @adapter.on("message")
78
- async def handle_message(data: Any) -> None:
79
- print(f"收到消息: {data}")
80
-
81
- @adapter.on("error")
82
- async def handle_error(error: Exception) -> None:
83
- print(f"发生错误: {error}")
84
-
85
- # 处理所有事件
86
- @adapter.on()
87
- async def handle_all(event: Any) -> None:
88
- print(f"事件: {event}")
89
- ```
90
-
91
- #### emit(event_type: str, data: Any) -> None
92
- 触发事件。
93
- - 参数:
94
- - event_type: 事件类型
95
- - data: 事件数据
96
- - 返回:
97
- - None
98
- - 示例:
99
- ```python
100
- class MyPlatformAdapter(BaseAdapter):
101
- async def _handle_websocket_message(self, message: Any) -> None:
102
- # 处理消息并触发相应事件
103
- if message.type == "chat":
104
- await self.emit("message", {
105
- "type": "chat",
106
- "content": message.content,
107
- "sender": message.sender
108
- })
109
- ```
110
-
111
- #### middleware(func: Callable[..., Any]) -> Callable[..., Any]
112
- 添加中间件处理器。
113
- - 参数:
114
- - func: 中间件函数
115
- - 返回:
116
- - Callable[..., Any]: 中间件函数
117
- - 示例:
118
- ```python
119
- adapter = MyPlatformAdapter()
120
-
121
- @adapter.middleware
122
- async def log_middleware(data: Any) -> Any:
123
- print(f"处理数据: {data}")
124
- return data
125
-
126
- @adapter.middleware
127
- async def filter_middleware(data: Any) -> Optional[Any]:
128
- if "spam" in data.get("content", ""):
129
- return None
130
- return data
131
- ```
132
-
133
- ### 消息发送DSL (SendDSL)
134
- 提供链式调用风格的消息发送接口。
135
-
136
- #### To(target_type: Optional[str] = None, target_id: Optional[str] = None) -> 'SendDSL'
137
- 设置消息目标。
138
- - 参数:
139
- - target_type: 目标类型(可选)
140
- - target_id: 目标ID
141
- - 返回:
142
- - SendDSL: 发送器实例
143
- - 示例:
144
- ```python
145
- # 发送到用户
146
- sdk.adapter.Platform.Send.To("user", "123").Text("Hello")
147
-
148
- # 发送到群组
149
- sdk.adapter.Platform.Send.To("group", "456").Text("Hello Group")
150
-
151
- # 简化形式(只有ID)
152
- sdk.adapter.Platform.Send.To("123").Text("Hello")
153
- ```
154
-
155
- #### Text(text: str) -> asyncio.Task
156
- 发送文本消息。
157
- - 参数:
158
- - text: 文本内容
159
- - 返回:
160
- - asyncio.Task: 异步任务
161
- - 示例:
162
- ```python
163
- # 发送简单文本
164
- await sdk.adapter.Platform.Send.To("user", "123").Text("Hello")
165
-
166
- # 发送格式化文本
167
- name = "Alice"
168
- await sdk.adapter.Platform.Send.To("123").Text(f"Hello {name}")
169
- ```
170
-
171
- ### 适配器管理 (AdapterManager)
172
- 管理多个平台适配器的注册、启动和关闭。
173
-
174
- #### register(platform: str, adapter_class: Type[BaseAdapter]) -> bool
175
- 注册新的适配器类。
176
- - 参数:
177
- - platform: 平台名称
178
- - adapter_class: 适配器类
179
- - 返回:
180
- - bool: 注册是否成功
181
- - 示例:
182
- ```python
183
- # 注册适配器
184
- sdk.adapter.register("MyPlatform", MyPlatformAdapter)
185
-
186
- # 注册多个适配器
187
- adapters = {
188
- "Platform1": Platform1Adapter,
189
- "Platform2": Platform2Adapter
190
- }
191
- for name, adapter in adapters.items():
192
- sdk.adapter.register(name, adapter)
193
- ```
194
-
195
- #### startup(platforms: Optional[List[str]] = None) -> None
196
- 启动指定的适配器。
197
- - 参数:
198
- - platforms: 要启动的平台列表,None表示所有平台
199
- - 返回:
200
- - None
201
- - 示例:
202
- ```python
203
- # 启动所有适配器
204
- await sdk.adapter.startup()
205
-
206
- # 启动指定适配器
207
- await sdk.adapter.startup(["Platform1", "Platform2"])
208
- ```
209
-
210
- #### shutdown() -> None
211
- 关闭所有适配器。
212
- - 参数: 无
213
- - 返回:
214
- - None
215
- - 示例:
216
- ```python
217
- # 关闭所有适配器
218
- await sdk.adapter.shutdown()
219
-
220
- # 在程序退出时关闭
221
- import atexit
222
- atexit.register(lambda: asyncio.run(sdk.adapter.shutdown()))
223
- ```
224
-
225
- """
226
-
227
- import functools
228
- import asyncio
229
- from typing import (
230
- Callable, Any, Dict, List, Type, Optional, Set,
231
- Union, Awaitable, TypeVar, Generic, Tuple, Coroutine, FrozenSet
232
- )
233
- from collections import defaultdict
234
-
235
-
236
- # DSL 基类,用于实现 Send.To(...).Func(...) 风格
237
- class SendDSLBase:
238
- def __init__(self, adapter: 'BaseAdapter', target_type: Optional[str] = None, target_id: Optional[str] = None):
239
- self._adapter = adapter
240
- self._target_type = target_type
241
- self._target_id = target_id
242
- self._target_to = target_id
243
-
244
- def To(self, target_type: str = None, target_id: str = None) -> 'SendDSL':
245
- if target_id is None and target_type is not None:
246
- target_id = target_type
247
- target_type = None
248
-
249
- return self.__class__(self._adapter, target_type, target_id)
250
-
251
- def __getattr__(self, name: str):
252
- def wrapper(*args, **kwargs):
253
- return asyncio.create_task(
254
- self._adapter._real_send(
255
- target_type=self._target_type,
256
- target_id=self._target_id,
257
- action=name,
258
- data={
259
- "args": args,
260
- "kwargs": kwargs
261
- }
262
- )
263
- )
264
- return wrapper
265
-
266
-
267
- class BaseAdapter:
268
- class Send(SendDSLBase):
269
- def Text(self, text: str):
270
- """基础文本消息发送方法,子类应该重写此方法"""
271
- return asyncio.create_task(
272
- self._adapter.call_api(
273
- endpoint="/send",
274
- content=text,
275
- recvId=self._target_id,
276
- recvType=self._target_type
277
- )
278
- )
279
-
280
- def __init__(self):
281
- self._handlers = defaultdict(list)
282
- self._middlewares = []
283
- # 绑定当前适配器的 Send 实例
284
- self.Send = self.__class__.Send(self)
285
-
286
- def on(self, event_type: str = "*"):
287
- def decorator(func: Callable):
288
- @functools.wraps(func)
289
- async def wrapper(*args, **kwargs):
290
- return await func(*args, **kwargs)
291
- self._handlers[event_type].append(wrapper)
292
- return wrapper
293
- return decorator
294
-
295
- def middleware(self, func: Callable):
296
- self._middlewares.append(func)
297
- return func
298
-
299
- async def call_api(self, endpoint: str, **params):
300
- raise NotImplementedError
301
-
302
- async def start(self):
303
- raise NotImplementedError
304
-
305
- async def shutdown(self):
306
- raise NotImplementedError
307
-
308
- def add_handler(self, *args):
309
- if len(args) == 1:
310
- event_type = "*"
311
- handler = args[0]
312
- elif len(args) == 2:
313
- event_type, handler = args
314
- else:
315
- raise TypeError("add_handler() 接受 1 个(监听所有事件)或 2 个参数(指定事件类型)")
316
-
317
- @functools.wraps(handler)
318
- async def wrapper(*handler_args, **handler_kwargs):
319
- return await handler(*handler_args, **handler_kwargs)
320
-
321
- self._handlers[event_type].append(wrapper)
322
- async def emit(self, event_type: str, data: Any):
323
- # 先执行中间件
324
- for middleware in self._middlewares:
325
- data = await middleware(data)
326
-
327
- # 触发具体事件类型的处理器
328
- if event_type in self._handlers:
329
- for handler in self._handlers[event_type]:
330
- await handler(data)
331
-
332
- # 触发通配符 "*" 的处理器
333
- for handler in self._handlers.get("*", []):
334
- await handler(data)
335
-
336
- async def send(self, target_type: str, target_id: str, message: Any, **kwargs):
337
- method_name = kwargs.pop("method", "Text")
338
- method = getattr(self.Send.To(target_type, target_id), method_name, None)
339
- if not method:
340
- raise AttributeError(f"未找到 {method_name} 方法,请确保已在 Send 类中定义")
341
- return await method(text=message, **kwargs)
342
-
343
-
344
- class AdapterManager:
345
- def __init__(self):
346
- self._adapters: Dict[str, BaseAdapter] = {}
347
- self._adapter_instances: Dict[Type[BaseAdapter], BaseAdapter] = {}
348
- self._platform_to_instance: Dict[str, BaseAdapter] = {}
349
- self._started_instances: Set[BaseAdapter] = set()
350
-
351
- def register(self, platform: str, adapter_class: Type[BaseAdapter]) -> bool:
352
- if not issubclass(adapter_class, BaseAdapter):
353
- raise TypeError("适配器必须继承自BaseAdapter")
354
- from . import sdk
355
-
356
- # 如果该类已经创建过实例,复用
357
- if adapter_class in self._adapter_instances:
358
- instance = self._adapter_instances[adapter_class]
359
- else:
360
- instance = adapter_class(sdk)
361
- self._adapter_instances[adapter_class] = instance
362
-
363
- # 注册平台名,并统一映射到该实例
364
- self._adapters[platform] = instance
365
- self._platform_to_instance[platform] = instance
366
-
367
- if len(platform) <= 10:
368
- from itertools import product
369
- combinations = [''.join(c) for c in product(*[(ch.lower(), ch.upper()) for ch in platform])]
370
- for name in set(combinations):
371
- setattr(self, name, instance)
372
- else:
373
- self.logger.warning(f"平台名 {platform} 过长,如果您是开发者,请考虑使用更短的名称")
374
- setattr(self, platform.lower(), instance)
375
- setattr(self, platform.upper(), instance)
376
- setattr(self, platform.capitalize(), instance)
377
-
378
- return True
379
-
380
- async def startup(self, platforms: List[str] = None):
381
- if platforms is None:
382
- platforms = list(self._adapters.keys())
383
-
384
- # 已经被调度过的 adapter 实例集合(防止重复调度)
385
- scheduled_adapters = set()
386
-
387
- for platform in platforms:
388
- if platform not in self._adapters:
389
- raise ValueError(f"平台 {platform} 未注册")
390
- adapter = self._adapters[platform]
391
-
392
- # 如果该实例已经被启动或已调度,跳过
393
- if adapter in self._started_instances or adapter in scheduled_adapters:
394
- continue
395
-
396
- # 加入调度队列
397
- scheduled_adapters.add(adapter)
398
- asyncio.create_task(self._run_adapter(adapter, platform))
399
-
400
- async def _run_adapter(self, adapter: BaseAdapter, platform: str):
401
- from . import sdk
402
-
403
- # 加锁防止并发启动
404
- if not getattr(adapter, "_starting_lock", None):
405
- adapter._starting_lock = asyncio.Lock()
406
-
407
- async with adapter._starting_lock:
408
- # 再次确认是否已经被启动
409
- if adapter in self._started_instances:
410
- sdk.logger.info(f"适配器 {platform}(实例ID: {id(adapter)})已被其他协程启动,跳过")
411
- return
412
-
413
- retry_count = 0
414
- fixed_delay = 3 * 60 * 60
415
- backoff_intervals = [60, 10 * 60, 30 * 60, 60 * 60]
416
-
417
- while True:
418
- try:
419
- await adapter.start()
420
- self._started_instances.add(adapter)
421
- sdk.logger.info(f"适配器 {platform}(实例ID: {id(adapter)})已启动")
422
- return
423
- except Exception as e:
424
- retry_count += 1
425
- sdk.logger.error(f"平台 {platform} 启动失败(第{retry_count}次重试): {e}")
426
-
427
- try:
428
- await adapter.shutdown()
429
- except Exception as stop_err:
430
- sdk.logger.warning(f"停止适配器失败: {stop_err}")
431
-
432
- # 计算等待时间
433
- if retry_count <= len(backoff_intervals):
434
- wait_time = backoff_intervals[retry_count - 1]
435
- else:
436
- wait_time = fixed_delay
437
-
438
- sdk.logger.info(f"将在 {wait_time // 60} 分钟后再次尝试重启 {platform}")
439
- await asyncio.sleep(wait_time)
440
-
441
- async def shutdown(self):
442
- for adapter in self._adapters.values():
443
- await adapter.shutdown()
444
-
445
- def get(self, platform: str) -> BaseAdapter:
446
- platform_lower = platform.lower()
447
- for registered, instance in self._adapters.items():
448
- if registered.lower() == platform_lower:
449
- return instance
450
- return None
451
-
452
- def __getattr__(self, platform: str) -> BaseAdapter:
453
- platform_lower = platform.lower()
454
- for registered, instance in self._adapters.items():
455
- if registered.lower() == platform_lower:
456
- return instance
457
- raise AttributeError(f"平台 {platform} 的适配器未注册")
458
-
459
- @property
460
- def platforms(self) -> list:
461
- return list(self._adapters.keys())
462
-
463
- AdapterFather = BaseAdapter
464
- adapter = AdapterManager()
465
- SendDSL = SendDSLBase