satori-python-client 0.11.4__tar.gz → 0.13.0__tar.gz
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.
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/.mina/client.toml +1 -0
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/PKG-INFO +11 -5
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/README.md +9 -4
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/pyproject.toml +5 -4
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/src/satori/client/__init__.py +70 -27
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/src/satori/client/account.py +23 -7
- satori_python_client-0.13.0/src/satori/client/account.pyi +553 -0
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/src/satori/client/network/base.py +1 -1
- satori_python_client-0.13.0/src/satori/client/network/util.py +43 -0
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/src/satori/client/network/webhook.py +13 -2
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/src/satori/client/network/websocket.py +2 -2
- satori_python_client-0.13.0/src/satori/client/protocol.py +767 -0
- satori_python_client-0.11.4/src/satori/client/account.pyi +0 -181
- satori_python_client-0.11.4/src/satori/client/session.py +0 -384
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/LICENSE +0 -0
- {satori_python_client-0.11.4 → satori_python_client-0.13.0}/src/satori/client/network/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: satori-python-client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0
|
|
4
4
|
Summary: Satori Protocol SDK for python, specify client part
|
|
5
5
|
Home-page: https://github.com/RF-Tar-Railt/satori-python
|
|
6
6
|
Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
|
|
@@ -19,6 +19,7 @@ Project-URL: Repository, https://github.com/RF-Tar-Railt/satori-python
|
|
|
19
19
|
Requires-Python: >=3.8
|
|
20
20
|
Requires-Dist: aiohttp>=3.9.3
|
|
21
21
|
Requires-Dist: launart>=0.8.2
|
|
22
|
+
Requires-Dist: graia-amnesia>=0.9.0
|
|
22
23
|
Requires-Dist: satori-python-core>=0.11.4
|
|
23
24
|
Description-Content-Type: text/markdown
|
|
24
25
|
|
|
@@ -42,6 +43,10 @@ Description-Content-Type: text/markdown
|
|
|
42
43
|
- [Chronocat](https://chronocat.vercel.app)
|
|
43
44
|
- Koishi (搭配 `@koishijs/plugin-server`)
|
|
44
45
|
|
|
46
|
+
### 使用该 SDK 的框架
|
|
47
|
+
|
|
48
|
+
- [`Entari`](https://github.com/ArcletProject/Entari)
|
|
49
|
+
|
|
45
50
|
## 安装
|
|
46
51
|
|
|
47
52
|
安装完整体:
|
|
@@ -69,14 +74,15 @@ pip install satori-python-server
|
|
|
69
74
|
客户端:
|
|
70
75
|
|
|
71
76
|
```python
|
|
72
|
-
from satori import
|
|
77
|
+
from satori import EventType, WebsocketsInfo
|
|
78
|
+
from satori.event import MessageEvent
|
|
73
79
|
from satori.client import Account, App
|
|
74
80
|
|
|
75
81
|
app = App(WebsocketsInfo(port=5140))
|
|
76
82
|
|
|
77
|
-
@app.
|
|
78
|
-
async def on_message(account: Account, event:
|
|
79
|
-
if event.user
|
|
83
|
+
@app.register_on(EventType.MESSAGE_CREATED)
|
|
84
|
+
async def on_message(account: Account, event: MessageEvent):
|
|
85
|
+
if event.user.id == "xxxxxxxxxxx":
|
|
80
86
|
await account.send(event, "Hello, World!")
|
|
81
87
|
|
|
82
88
|
app.run()
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
- [Chronocat](https://chronocat.vercel.app)
|
|
19
19
|
- Koishi (搭配 `@koishijs/plugin-server`)
|
|
20
20
|
|
|
21
|
+
### 使用该 SDK 的框架
|
|
22
|
+
|
|
23
|
+
- [`Entari`](https://github.com/ArcletProject/Entari)
|
|
24
|
+
|
|
21
25
|
## 安装
|
|
22
26
|
|
|
23
27
|
安装完整体:
|
|
@@ -45,14 +49,15 @@ pip install satori-python-server
|
|
|
45
49
|
客户端:
|
|
46
50
|
|
|
47
51
|
```python
|
|
48
|
-
from satori import
|
|
52
|
+
from satori import EventType, WebsocketsInfo
|
|
53
|
+
from satori.event import MessageEvent
|
|
49
54
|
from satori.client import Account, App
|
|
50
55
|
|
|
51
56
|
app = App(WebsocketsInfo(port=5140))
|
|
52
57
|
|
|
53
|
-
@app.
|
|
54
|
-
async def on_message(account: Account, event:
|
|
55
|
-
if event.user
|
|
58
|
+
@app.register_on(EventType.MESSAGE_CREATED)
|
|
59
|
+
async def on_message(account: Account, event: MessageEvent):
|
|
60
|
+
if event.user.id == "xxxxxxxxxxx":
|
|
56
61
|
await account.send(event, "Hello, World!")
|
|
57
62
|
|
|
58
63
|
app.run()
|
|
@@ -7,6 +7,7 @@ authors = [
|
|
|
7
7
|
dependencies = [
|
|
8
8
|
"aiohttp>=3.9.3",
|
|
9
9
|
"launart>=0.8.2",
|
|
10
|
+
"graia-amnesia>=0.9.0",
|
|
10
11
|
"satori-python-core >= 0.11.4",
|
|
11
12
|
]
|
|
12
13
|
description = "Satori Protocol SDK for python, specify client part"
|
|
@@ -23,7 +24,7 @@ classifiers = [
|
|
|
23
24
|
"Programming Language :: Python :: 3.12",
|
|
24
25
|
"Operating System :: OS Independent",
|
|
25
26
|
]
|
|
26
|
-
version = "0.
|
|
27
|
+
version = "0.13.0"
|
|
27
28
|
|
|
28
29
|
[project.license]
|
|
29
30
|
text = "MIT"
|
|
@@ -41,9 +42,9 @@ build-backend = "mina.backend"
|
|
|
41
42
|
[tool.pdm.dev-dependencies]
|
|
42
43
|
dev = [
|
|
43
44
|
"isort>=5.13.2",
|
|
44
|
-
"black>=24.
|
|
45
|
-
"ruff>=0.
|
|
46
|
-
"pre-commit>=3.
|
|
45
|
+
"black>=24.4.0",
|
|
46
|
+
"ruff>=0.4.1",
|
|
47
|
+
"pre-commit>=3.7.0",
|
|
47
48
|
"fix-future-annotations>=0.5.0",
|
|
48
49
|
"mina-build<0.6,>=0.5.1",
|
|
49
50
|
"pdm-mina>=0.3.2",
|
|
@@ -5,13 +5,14 @@ import functools
|
|
|
5
5
|
import signal
|
|
6
6
|
import threading
|
|
7
7
|
from functools import wraps
|
|
8
|
-
from typing import Any, Awaitable, Callable, Iterable, Literal, TypeVar, overload
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Awaitable, Callable, Iterable, Literal, TypeVar, overload
|
|
9
9
|
|
|
10
10
|
from creart import it
|
|
11
|
+
from graia.amnesia.builtins.aiohttp import AiohttpClientService
|
|
11
12
|
from launart import Launart, Service, any_completed
|
|
12
13
|
from loguru import logger
|
|
13
14
|
|
|
14
|
-
from satori import event
|
|
15
|
+
from satori import event as events
|
|
15
16
|
from satori.config import Config, WebhookInfo, WebsocketsInfo
|
|
16
17
|
from satori.const import EventType
|
|
17
18
|
from satori.model import Event, LoginStatus
|
|
@@ -33,7 +34,7 @@ MAPPING: dict[type[Config], type[BaseNetwork]] = {
|
|
|
33
34
|
|
|
34
35
|
class App(Service):
|
|
35
36
|
id = "satori-python.client"
|
|
36
|
-
required: set[str] =
|
|
37
|
+
required: set[str] = {"http.client/aiohttp"}
|
|
37
38
|
stages: set[str] = {"preparing", "blocking", "cleanup"}
|
|
38
39
|
|
|
39
40
|
accounts: dict[str, Account]
|
|
@@ -69,8 +70,8 @@ class App(Service):
|
|
|
69
70
|
|
|
70
71
|
@overload
|
|
71
72
|
def register_on(self, event_type: Literal[EventType.FRIEND_REQUEST]) -> Callable[
|
|
72
|
-
[Callable[[Account,
|
|
73
|
-
Callable[[Account,
|
|
73
|
+
[Callable[[Account, events.UserEvent], Awaitable[Any]]],
|
|
74
|
+
Callable[[Account, events.UserEvent], Awaitable[Any]],
|
|
74
75
|
]: ...
|
|
75
76
|
|
|
76
77
|
@overload
|
|
@@ -80,8 +81,8 @@ class App(Service):
|
|
|
80
81
|
EventType.GUILD_ADDED, EventType.GUILD_REMOVED, EventType.GUILD_REQUEST, EventType.GUILD_UPDATED
|
|
81
82
|
],
|
|
82
83
|
) -> Callable[
|
|
83
|
-
[Callable[[Account,
|
|
84
|
-
Callable[[Account,
|
|
84
|
+
[Callable[[Account, events.GuildEvent], Awaitable[Any]]],
|
|
85
|
+
Callable[[Account, events.GuildEvent], Awaitable[Any]],
|
|
85
86
|
]: ...
|
|
86
87
|
|
|
87
88
|
@overload
|
|
@@ -94,8 +95,8 @@ class App(Service):
|
|
|
94
95
|
EventType.GUILD_MEMBER_REQUEST,
|
|
95
96
|
],
|
|
96
97
|
) -> Callable[
|
|
97
|
-
[Callable[[Account,
|
|
98
|
-
Callable[[Account,
|
|
98
|
+
[Callable[[Account, events.GuildMemberEvent], Awaitable[Any]]],
|
|
99
|
+
Callable[[Account, events.GuildMemberEvent], Awaitable[Any]],
|
|
99
100
|
]: ...
|
|
100
101
|
|
|
101
102
|
@overload
|
|
@@ -105,16 +106,16 @@ class App(Service):
|
|
|
105
106
|
EventType.GUILD_ROLE_CREATED, EventType.GUILD_ROLE_DELETED, EventType.GUILD_ROLE_UPDATED
|
|
106
107
|
],
|
|
107
108
|
) -> Callable[
|
|
108
|
-
[Callable[[Account,
|
|
109
|
-
Callable[[Account,
|
|
109
|
+
[Callable[[Account, events.GuildRoleEvent], Awaitable[Any]]],
|
|
110
|
+
Callable[[Account, events.GuildRoleEvent], Awaitable[Any]],
|
|
110
111
|
]: ...
|
|
111
112
|
|
|
112
113
|
@overload
|
|
113
114
|
def register_on(
|
|
114
115
|
self, event_type: Literal[EventType.LOGIN_ADDED, EventType.LOGIN_REMOVED, EventType.LOGIN_UPDATED]
|
|
115
116
|
) -> Callable[
|
|
116
|
-
[Callable[[Account,
|
|
117
|
-
Callable[[Account,
|
|
117
|
+
[Callable[[Account, events.LoginEvent], Awaitable[Any]]],
|
|
118
|
+
Callable[[Account, events.LoginEvent], Awaitable[Any]],
|
|
118
119
|
]: ...
|
|
119
120
|
|
|
120
121
|
@overload
|
|
@@ -122,34 +123,34 @@ class App(Service):
|
|
|
122
123
|
self,
|
|
123
124
|
event_type: Literal[EventType.MESSAGE_CREATED, EventType.MESSAGE_DELETED, EventType.MESSAGE_UPDATED],
|
|
124
125
|
) -> Callable[
|
|
125
|
-
[Callable[[Account,
|
|
126
|
-
Callable[[Account,
|
|
126
|
+
[Callable[[Account, events.MessageEvent], Awaitable[Any]]],
|
|
127
|
+
Callable[[Account, events.MessageEvent], Awaitable[Any]],
|
|
127
128
|
]: ...
|
|
128
129
|
|
|
129
130
|
@overload
|
|
130
131
|
def register_on(
|
|
131
132
|
self, event_type: Literal[EventType.REACTION_ADDED, EventType.REACTION_REMOVED]
|
|
132
133
|
) -> Callable[
|
|
133
|
-
[Callable[[Account,
|
|
134
|
-
Callable[[Account,
|
|
134
|
+
[Callable[[Account, events.ReactionEvent], Awaitable[Any]]],
|
|
135
|
+
Callable[[Account, events.ReactionEvent], Awaitable[Any]],
|
|
135
136
|
]: ...
|
|
136
137
|
|
|
137
138
|
@overload
|
|
138
139
|
def register_on(self, event_type: Literal[EventType.INTERACTION_BUTTON]) -> Callable[
|
|
139
|
-
[Callable[[Account,
|
|
140
|
-
Callable[[Account,
|
|
140
|
+
[Callable[[Account, events.ButtonInteractionEvent], Awaitable[Any]]],
|
|
141
|
+
Callable[[Account, events.ButtonInteractionEvent], Awaitable[Any]],
|
|
141
142
|
]: ...
|
|
142
143
|
|
|
143
144
|
@overload
|
|
144
145
|
def register_on(self, event_type: Literal[EventType.INTERACTION_COMMAND]) -> Callable[
|
|
145
|
-
[Callable[[Account,
|
|
146
|
-
Callable[[Account,
|
|
146
|
+
[Callable[[Account, events.ArgvInteractionEvent | events.MessageEvent], Awaitable[Any]]],
|
|
147
|
+
Callable[[Account, events.ArgvInteractionEvent | events.MessageEvent], Awaitable[Any]],
|
|
147
148
|
]: ...
|
|
148
149
|
|
|
149
150
|
@overload
|
|
150
151
|
def register_on(self, event_type: Literal[EventType.INTERNAL]) -> Callable[
|
|
151
|
-
[Callable[[Account,
|
|
152
|
-
Callable[[Account,
|
|
152
|
+
[Callable[[Account, events.InternalEvent], Awaitable[Any]]],
|
|
153
|
+
Callable[[Account, events.InternalEvent], Awaitable[Any]],
|
|
153
154
|
]: ...
|
|
154
155
|
|
|
155
156
|
@overload
|
|
@@ -180,16 +181,56 @@ class App(Service):
|
|
|
180
181
|
if self.lifecycle_callbacks:
|
|
181
182
|
await asyncio.gather(*(callback(account, state) for callback in self.lifecycle_callbacks))
|
|
182
183
|
|
|
183
|
-
async def post(self, event: Event):
|
|
184
|
+
async def post(self, event: Event, conn: BaseNetwork):
|
|
184
185
|
if not self.event_callbacks:
|
|
185
186
|
return
|
|
186
187
|
identity = f"{event.platform}/{event.self_id}"
|
|
187
188
|
if identity not in self.accounts:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
189
|
+
if event.type == EventType.LOGIN_ADDED:
|
|
190
|
+
if TYPE_CHECKING:
|
|
191
|
+
assert isinstance(event, events.LoginEvent)
|
|
192
|
+
account = Account(
|
|
193
|
+
event.platform,
|
|
194
|
+
event.self_id,
|
|
195
|
+
event.login,
|
|
196
|
+
conn.config,
|
|
197
|
+
)
|
|
198
|
+
logger.info(f"account added: {account}")
|
|
199
|
+
(
|
|
200
|
+
account.connected.set()
|
|
201
|
+
if event.login.status == LoginStatus.ONLINE
|
|
202
|
+
else account.connected.clear()
|
|
203
|
+
)
|
|
204
|
+
self.accounts[identity] = account
|
|
205
|
+
conn.accounts[identity] = account
|
|
206
|
+
await self.account_update(account, LoginStatus.ONLINE)
|
|
207
|
+
await self.account_update(account, LoginStatus.CONNECT)
|
|
208
|
+
else:
|
|
209
|
+
logger.warning(f"Received event for unknown account: {event}")
|
|
210
|
+
return
|
|
211
|
+
else:
|
|
212
|
+
account = self.accounts[identity]
|
|
213
|
+
if event.type == EventType.LOGIN_UPDATED:
|
|
214
|
+
if TYPE_CHECKING:
|
|
215
|
+
assert isinstance(event, events.LoginEvent)
|
|
216
|
+
logger.info(f"account updated: {account}")
|
|
217
|
+
(
|
|
218
|
+
account.connected.set()
|
|
219
|
+
if event.login.status in (LoginStatus.ONLINE, LoginStatus.CONNECT)
|
|
220
|
+
else account.connected.clear()
|
|
221
|
+
)
|
|
222
|
+
|
|
191
223
|
await asyncio.gather(*(callback(account, event) for callback in self.event_callbacks))
|
|
192
224
|
|
|
225
|
+
if event.type == EventType.LOGIN_REMOVED:
|
|
226
|
+
if TYPE_CHECKING:
|
|
227
|
+
assert isinstance(event, events.LoginEvent)
|
|
228
|
+
logger.info(f"account removed: {account}")
|
|
229
|
+
account.connected.clear()
|
|
230
|
+
await self.account_update(account, LoginStatus.DISCONNECT)
|
|
231
|
+
del self.accounts[identity]
|
|
232
|
+
del conn.accounts[identity]
|
|
233
|
+
|
|
193
234
|
async def launch(self, manager: Launart):
|
|
194
235
|
for conn in self.connections:
|
|
195
236
|
manager.add_component(conn)
|
|
@@ -217,6 +258,7 @@ class App(Service):
|
|
|
217
258
|
):
|
|
218
259
|
if manager is None:
|
|
219
260
|
manager = it(Launart)
|
|
261
|
+
manager.add_component(AiohttpClientService())
|
|
220
262
|
manager.add_component(self)
|
|
221
263
|
manager.launch_blocking(loop=loop, stop_signal=stop_signal)
|
|
222
264
|
|
|
@@ -227,6 +269,7 @@ class App(Service):
|
|
|
227
269
|
):
|
|
228
270
|
if manager is None:
|
|
229
271
|
manager = it(Launart)
|
|
272
|
+
manager.add_component(AiohttpClientService())
|
|
230
273
|
manager.add_component(self)
|
|
231
274
|
handled_signals: dict[signal.Signals, Any] = {}
|
|
232
275
|
launch_task = asyncio.create_task(manager.launch(), name="amnesia-launch")
|
|
@@ -6,9 +6,11 @@ from typing import TypeVar
|
|
|
6
6
|
|
|
7
7
|
from yarl import URL
|
|
8
8
|
|
|
9
|
-
from .
|
|
9
|
+
from satori.model import Login
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
from .protocol import ApiProtocol
|
|
12
|
+
|
|
13
|
+
TP = TypeVar("TP", bound="ApiProtocol")
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
@dataclass
|
|
@@ -28,15 +30,29 @@ class ApiInfo:
|
|
|
28
30
|
|
|
29
31
|
|
|
30
32
|
class Account:
|
|
31
|
-
def __init__(
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
platform: str,
|
|
36
|
+
self_id: str,
|
|
37
|
+
self_info: Login,
|
|
38
|
+
config: ApiInfo,
|
|
39
|
+
protocol_cls: type[ApiProtocol] = ApiProtocol,
|
|
40
|
+
):
|
|
32
41
|
self.platform = platform
|
|
33
42
|
self.self_id = self_id
|
|
43
|
+
self.self_info = self_info
|
|
34
44
|
self.config = config
|
|
35
|
-
self.
|
|
45
|
+
self.protocol = protocol_cls(self) # type: ignore
|
|
36
46
|
self.connected = asyncio.Event()
|
|
37
47
|
|
|
38
|
-
def custom(self, config: ApiInfo | None = None,
|
|
39
|
-
return Account(
|
|
48
|
+
def custom(self, config: ApiInfo | None = None, protocol_cls: type[TP] = ApiProtocol, **kwargs) -> TP:
|
|
49
|
+
return Account(
|
|
50
|
+
self.platform,
|
|
51
|
+
self.self_id,
|
|
52
|
+
self.self_info,
|
|
53
|
+
config or ApiInfo(**kwargs),
|
|
54
|
+
protocol_cls, # type: ignore
|
|
55
|
+
).protocol
|
|
40
56
|
|
|
41
57
|
@property
|
|
42
58
|
def identity(self):
|
|
@@ -46,4 +62,4 @@ class Account:
|
|
|
46
62
|
return f"<Account {self.self_id} ({self.platform})>"
|
|
47
63
|
|
|
48
64
|
def __getattr__(self, item):
|
|
49
|
-
return getattr(self.
|
|
65
|
+
return getattr(self.protocol, item)
|