satori-python 0.10.0__tar.gz → 0.11.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-0.10.0 → satori_python-0.11.0}/PKG-INFO +1 -1
- {satori_python-0.10.0 → satori_python-0.11.0}/pyproject.toml +9 -4
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/__init__.py +1 -1
- satori_python-0.11.0/src/satori/client/__init__.py +218 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/account.pyi +3 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/network/base.py +2 -4
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/network/websocket.py +5 -3
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/const.py +2 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/element.py +8 -2
- satori_python-0.11.0/src/satori/event.py +63 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/model.py +63 -15
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/server/__init__.py +175 -5
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/server/adapter.py +7 -16
- satori_python-0.11.0/src/satori/server/model.py +36 -0
- satori_python-0.11.0/src/satori/server/route.py +240 -0
- satori_python-0.10.0/src/satori/client/__init__.py +0 -108
- satori_python-0.10.0/src/satori/server/model.py +0 -40
- {satori_python-0.10.0 → satori_python-0.11.0}/LICENSE +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/README.md +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/account.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/network/__init__.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/network/webhook.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/client/session.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/config.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/exception.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/parser.py +0 -0
- {satori_python-0.10.0 → satori_python-0.11.0}/src/satori/server/conection.py +0 -0
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
"Programming Language :: Python :: 3.12",
|
|
29
29
|
"Operating System :: OS Independent",
|
|
30
30
|
]
|
|
31
|
-
version = "0.
|
|
31
|
+
version = "0.11.0"
|
|
32
32
|
|
|
33
33
|
[project.license]
|
|
34
34
|
text = "MIT"
|
|
@@ -63,7 +63,7 @@ includes = [
|
|
|
63
63
|
composite = [
|
|
64
64
|
"isort ./src/ ./example/",
|
|
65
65
|
"black ./src/ ./example/",
|
|
66
|
-
"ruff ./src/ ./example/",
|
|
66
|
+
"ruff check ./src/ ./example/",
|
|
67
67
|
]
|
|
68
68
|
|
|
69
69
|
[tool.pdm.version]
|
|
@@ -84,6 +84,13 @@ extra_standard_library = [
|
|
|
84
84
|
]
|
|
85
85
|
|
|
86
86
|
[tool.ruff]
|
|
87
|
+
line-length = 110
|
|
88
|
+
target-version = "py38"
|
|
89
|
+
exclude = [
|
|
90
|
+
"exam.py",
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
87
94
|
select = [
|
|
88
95
|
"E",
|
|
89
96
|
"W",
|
|
@@ -100,8 +107,6 @@ ignore = [
|
|
|
100
107
|
"C901",
|
|
101
108
|
"UP037",
|
|
102
109
|
]
|
|
103
|
-
line-length = 110
|
|
104
|
-
target-version = "py38"
|
|
105
110
|
|
|
106
111
|
[tool.pyright]
|
|
107
112
|
pythonPlatform = "All"
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import signal
|
|
5
|
+
from functools import wraps
|
|
6
|
+
from typing import Any, Awaitable, Callable, Iterable, Literal, TypeVar, overload
|
|
7
|
+
|
|
8
|
+
from creart import it
|
|
9
|
+
from launart import Launart, Service, any_completed
|
|
10
|
+
from loguru import logger
|
|
11
|
+
|
|
12
|
+
from satori import event
|
|
13
|
+
from satori.config import Config, WebhookInfo, WebsocketsInfo
|
|
14
|
+
from satori.const import EventType
|
|
15
|
+
from satori.model import Event, LoginStatus
|
|
16
|
+
|
|
17
|
+
from .account import Account as Account
|
|
18
|
+
from .account import ApiInfo as ApiInfo
|
|
19
|
+
from .network.base import BaseNetwork as BaseNetwork
|
|
20
|
+
from .network.webhook import WebhookNetwork
|
|
21
|
+
from .network.websocket import WsNetwork
|
|
22
|
+
|
|
23
|
+
TConfig = TypeVar("TConfig", bound=Config)
|
|
24
|
+
TE = TypeVar("TE", bound=Event, contravariant=True)
|
|
25
|
+
|
|
26
|
+
MAPPING: dict[type[Config], type[BaseNetwork]] = {
|
|
27
|
+
WebhookInfo: WebhookNetwork,
|
|
28
|
+
WebsocketsInfo: WsNetwork,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class App(Service):
|
|
33
|
+
id = "satori-python.client"
|
|
34
|
+
required: set[str] = set()
|
|
35
|
+
stages: set[str] = {"preparing", "blocking", "cleanup"}
|
|
36
|
+
|
|
37
|
+
accounts: dict[str, Account]
|
|
38
|
+
connections: list[BaseNetwork]
|
|
39
|
+
event_callbacks: list[Callable[[Account, Event], Awaitable[Any]]]
|
|
40
|
+
lifecycle_callbacks: list[Callable[[Account, LoginStatus], Awaitable[Any]]]
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def register_config(cls, tc: type[TConfig], tn: type[BaseNetwork[TConfig]]):
|
|
44
|
+
MAPPING[tc] = tn
|
|
45
|
+
|
|
46
|
+
def __init__(self, *configs: Config):
|
|
47
|
+
self.accounts = {}
|
|
48
|
+
self.connections = []
|
|
49
|
+
self.event_callbacks = []
|
|
50
|
+
self.lifecycle_callbacks = []
|
|
51
|
+
super().__init__()
|
|
52
|
+
for config in configs:
|
|
53
|
+
self.apply(config)
|
|
54
|
+
|
|
55
|
+
def apply(self, config: Config):
|
|
56
|
+
try:
|
|
57
|
+
connection = MAPPING[config.__class__](self, config)
|
|
58
|
+
except KeyError:
|
|
59
|
+
raise TypeError(f"Unknown config type: {config}")
|
|
60
|
+
self.connections.append(connection)
|
|
61
|
+
|
|
62
|
+
def get_account(self, self_id: str) -> Account:
|
|
63
|
+
return self.accounts[self_id]
|
|
64
|
+
|
|
65
|
+
def register(self, callback: Callable[[Account, Event], Awaitable[Any]]):
|
|
66
|
+
self.event_callbacks.append(callback)
|
|
67
|
+
|
|
68
|
+
@overload
|
|
69
|
+
def register_on(self, event_type: Literal[EventType.FRIEND_REQUEST]) -> Callable[
|
|
70
|
+
[Callable[[Account, event.UserEvent], Awaitable[Any]]],
|
|
71
|
+
Callable[[Account, event.UserEvent], Awaitable[Any]],
|
|
72
|
+
]: ...
|
|
73
|
+
|
|
74
|
+
@overload
|
|
75
|
+
def register_on(
|
|
76
|
+
self,
|
|
77
|
+
event_type: Literal[
|
|
78
|
+
EventType.GUILD_ADDED, EventType.GUILD_REMOVED, EventType.GUILD_REQUEST, EventType.GUILD_UPDATED
|
|
79
|
+
],
|
|
80
|
+
) -> Callable[
|
|
81
|
+
[Callable[[Account, event.GuildEvent], Awaitable[Any]]],
|
|
82
|
+
Callable[[Account, event.GuildEvent], Awaitable[Any]],
|
|
83
|
+
]: ...
|
|
84
|
+
|
|
85
|
+
@overload
|
|
86
|
+
def register_on(
|
|
87
|
+
self,
|
|
88
|
+
event_type: Literal[
|
|
89
|
+
EventType.GUILD_MEMBER_ADDED,
|
|
90
|
+
EventType.GUILD_MEMBER_REMOVED,
|
|
91
|
+
EventType.GUILD_MEMBER_UPDATED,
|
|
92
|
+
EventType.GUILD_MEMBER_REQUEST,
|
|
93
|
+
],
|
|
94
|
+
) -> Callable[
|
|
95
|
+
[Callable[[Account, event.GuildMemberEvent], Awaitable[Any]]],
|
|
96
|
+
Callable[[Account, event.GuildMemberEvent], Awaitable[Any]],
|
|
97
|
+
]: ...
|
|
98
|
+
|
|
99
|
+
@overload
|
|
100
|
+
def register_on(
|
|
101
|
+
self,
|
|
102
|
+
event_type: Literal[
|
|
103
|
+
EventType.GUILD_ROLE_CREATED, EventType.GUILD_ROLE_DELETED, EventType.GUILD_ROLE_UPDATED
|
|
104
|
+
],
|
|
105
|
+
) -> Callable[
|
|
106
|
+
[Callable[[Account, event.GuildRoleEvent], Awaitable[Any]]],
|
|
107
|
+
Callable[[Account, event.GuildRoleEvent], Awaitable[Any]],
|
|
108
|
+
]: ...
|
|
109
|
+
|
|
110
|
+
@overload
|
|
111
|
+
def register_on(
|
|
112
|
+
self, event_type: Literal[EventType.LOGIN_ADDED, EventType.LOGIN_REMOVED, EventType.LOGIN_UPDATED]
|
|
113
|
+
) -> Callable[
|
|
114
|
+
[Callable[[Account, event.LoginEvent], Awaitable[Any]]],
|
|
115
|
+
Callable[[Account, event.LoginEvent], Awaitable[Any]],
|
|
116
|
+
]: ...
|
|
117
|
+
|
|
118
|
+
@overload
|
|
119
|
+
def register_on(
|
|
120
|
+
self,
|
|
121
|
+
event_type: Literal[EventType.MESSAGE_CREATED, EventType.MESSAGE_DELETED, EventType.MESSAGE_UPDATED],
|
|
122
|
+
) -> Callable[
|
|
123
|
+
[Callable[[Account, event.MessageEvent], Awaitable[Any]]],
|
|
124
|
+
Callable[[Account, event.MessageEvent], Awaitable[Any]],
|
|
125
|
+
]: ...
|
|
126
|
+
|
|
127
|
+
@overload
|
|
128
|
+
def register_on(
|
|
129
|
+
self, event_type: Literal[EventType.REACTION_ADDED, EventType.REACTION_REMOVED]
|
|
130
|
+
) -> Callable[
|
|
131
|
+
[Callable[[Account, event.ReactionEvent], Awaitable[Any]]],
|
|
132
|
+
Callable[[Account, event.ReactionEvent], Awaitable[Any]],
|
|
133
|
+
]: ...
|
|
134
|
+
|
|
135
|
+
@overload
|
|
136
|
+
def register_on(self, event_type: Literal[EventType.INTERACTION_BUTTON]) -> Callable[
|
|
137
|
+
[Callable[[Account, event.ButtonInteractionEvent], Awaitable[Any]]],
|
|
138
|
+
Callable[[Account, event.ButtonInteractionEvent], Awaitable[Any]],
|
|
139
|
+
]: ...
|
|
140
|
+
|
|
141
|
+
@overload
|
|
142
|
+
def register_on(self, event_type: Literal[EventType.INTERACTION_COMMAND]) -> Callable[
|
|
143
|
+
[Callable[[Account, event.ArgvInteractionEvent | event.MessageEvent], Awaitable[Any]]],
|
|
144
|
+
Callable[[Account, event.ArgvInteractionEvent | event.MessageEvent], Awaitable[Any]],
|
|
145
|
+
]: ...
|
|
146
|
+
|
|
147
|
+
@overload
|
|
148
|
+
def register_on(self, event_type: Literal[EventType.INTERNAL]) -> Callable[
|
|
149
|
+
[Callable[[Account, event.InternalEvent], Awaitable[Any]]],
|
|
150
|
+
Callable[[Account, event.InternalEvent], Awaitable[Any]],
|
|
151
|
+
]: ...
|
|
152
|
+
|
|
153
|
+
def register_on(self, event_type: EventType):
|
|
154
|
+
def decorator(
|
|
155
|
+
func: Callable[[Account, Any], Awaitable[Any]], /
|
|
156
|
+
) -> Callable[[Account, Any], Awaitable[Any]]:
|
|
157
|
+
@wraps(func)
|
|
158
|
+
async def wrapper(account: Account, event: Event) -> Any:
|
|
159
|
+
if event.type == event_type.value:
|
|
160
|
+
return await func(account, event)
|
|
161
|
+
|
|
162
|
+
self.register(wrapper)
|
|
163
|
+
return wrapper
|
|
164
|
+
|
|
165
|
+
return decorator
|
|
166
|
+
|
|
167
|
+
def lifecycle(self, callback: Callable[[Account, LoginStatus], Awaitable[Any]]):
|
|
168
|
+
self.lifecycle_callbacks.append(callback)
|
|
169
|
+
|
|
170
|
+
async def account_update(self, account: Account, state: LoginStatus):
|
|
171
|
+
if self.lifecycle_callbacks:
|
|
172
|
+
await asyncio.gather(*(callback(account, state) for callback in self.lifecycle_callbacks))
|
|
173
|
+
|
|
174
|
+
async def post(self, event: Event):
|
|
175
|
+
if not self.event_callbacks:
|
|
176
|
+
return
|
|
177
|
+
identity = f"{event.platform}/{event.self_id}"
|
|
178
|
+
if identity not in self.accounts:
|
|
179
|
+
logger.warning(f"Received event for unknown account: {event}")
|
|
180
|
+
return
|
|
181
|
+
account = self.accounts[identity]
|
|
182
|
+
await asyncio.gather(*(callback(account, event) for callback in self.event_callbacks))
|
|
183
|
+
|
|
184
|
+
async def launch(self, manager: Launart):
|
|
185
|
+
for conn in self.connections:
|
|
186
|
+
manager.add_component(conn)
|
|
187
|
+
|
|
188
|
+
async with self.stage("preparing"):
|
|
189
|
+
...
|
|
190
|
+
|
|
191
|
+
async with self.stage("blocking"):
|
|
192
|
+
await any_completed(
|
|
193
|
+
manager.status.wait_for_sigexit(),
|
|
194
|
+
*(conn.status.wait_for("blocking-completed") for conn in self.connections),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
async with self.stage("cleanup"):
|
|
198
|
+
for account in self.accounts.values():
|
|
199
|
+
await self.account_update(account, LoginStatus.OFFLINE)
|
|
200
|
+
self.accounts.clear()
|
|
201
|
+
|
|
202
|
+
def run(
|
|
203
|
+
self,
|
|
204
|
+
manager: Launart | None = None,
|
|
205
|
+
*,
|
|
206
|
+
loop: asyncio.AbstractEventLoop | None = None,
|
|
207
|
+
stop_signal: Iterable[signal.Signals] = (signal.SIGINT,),
|
|
208
|
+
):
|
|
209
|
+
if manager is None:
|
|
210
|
+
manager = it(Launart)
|
|
211
|
+
manager.add_component(self)
|
|
212
|
+
manager.launch_blocking(loop=loop, stop_signal=stop_signal)
|
|
213
|
+
|
|
214
|
+
async def run_async(self, manager: Launart | None = None):
|
|
215
|
+
if manager is None:
|
|
216
|
+
manager = it(Launart)
|
|
217
|
+
manager.add_component(self)
|
|
218
|
+
await manager.launch()
|
|
@@ -55,6 +55,7 @@ class Account:
|
|
|
55
55
|
message: 要发送的消息
|
|
56
56
|
"""
|
|
57
57
|
...
|
|
58
|
+
|
|
58
59
|
async def send_private_message(
|
|
59
60
|
self,
|
|
60
61
|
user_id: str,
|
|
@@ -67,6 +68,7 @@ class Account:
|
|
|
67
68
|
message: 要发送的消息
|
|
68
69
|
"""
|
|
69
70
|
...
|
|
71
|
+
|
|
70
72
|
async def update_message(
|
|
71
73
|
self,
|
|
72
74
|
channel_id: str,
|
|
@@ -81,6 +83,7 @@ class Account:
|
|
|
81
83
|
message: 要更新的消息
|
|
82
84
|
"""
|
|
83
85
|
...
|
|
86
|
+
|
|
84
87
|
async def message_create(
|
|
85
88
|
self,
|
|
86
89
|
*,
|
|
@@ -27,12 +27,10 @@ class BaseNetwork(Generic[TConfig], Service):
|
|
|
27
27
|
self.close_signal = asyncio.Event()
|
|
28
28
|
self.sequence = -1
|
|
29
29
|
|
|
30
|
-
async def wait_for_available(self):
|
|
31
|
-
...
|
|
30
|
+
async def wait_for_available(self): ...
|
|
32
31
|
|
|
33
32
|
@property
|
|
34
|
-
def alive(self) -> bool:
|
|
35
|
-
...
|
|
33
|
+
def alive(self) -> bool: ...
|
|
36
34
|
|
|
37
35
|
async def connection_closed(self):
|
|
38
36
|
self.close_signal.set()
|
|
@@ -101,9 +101,11 @@ class WsNetwork(BaseNetwork[WebsocketsInfo]):
|
|
|
101
101
|
else:
|
|
102
102
|
account = Account(platform, self_id, self.config)
|
|
103
103
|
logger.info(f"account registered: {account}")
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
(
|
|
105
|
+
account.connected.set()
|
|
106
|
+
if login["status"] == LoginStatus.ONLINE
|
|
107
|
+
else account.connected.clear()
|
|
108
|
+
)
|
|
107
109
|
self.app.accounts[identity] = account
|
|
108
110
|
self.accounts[identity] = account
|
|
109
111
|
await self.app.account_update(account, LoginStatus.ONLINE)
|
|
@@ -13,6 +13,7 @@ class Api(str, Enum):
|
|
|
13
13
|
CHANNEL_CREATE = "channel.create"
|
|
14
14
|
CHANNEL_UPDATE = "channel.update"
|
|
15
15
|
CHANNEL_DELETE = "channel.delete"
|
|
16
|
+
CHANNEL_MUTE = "channel.mute"
|
|
16
17
|
USER_CHANNEL_CREATE = "user.channel.create"
|
|
17
18
|
|
|
18
19
|
GUILD_GET = "guild.get"
|
|
@@ -22,6 +23,7 @@ class Api(str, Enum):
|
|
|
22
23
|
GUILD_MEMBER_LIST = "guild.member.list"
|
|
23
24
|
GUILD_MEMBER_GET = "guild.member.get"
|
|
24
25
|
GUILD_MEMBER_KICK = "guild.member.kick"
|
|
26
|
+
GUILD_MEMBER_MUTE = "guild.member.mute"
|
|
25
27
|
GUILD_MEMBER_APPROVE = "guild.member.approve"
|
|
26
28
|
GUILD_MEMBER_ROLE_SET = "guild.member.role.set"
|
|
27
29
|
GUILD_MEMBER_ROLE_UNSET = "guild.member.role.unset"
|
|
@@ -24,6 +24,13 @@ class Element:
|
|
|
24
24
|
for f in fields(self):
|
|
25
25
|
if f.name in ("_attrs", "_children"):
|
|
26
26
|
continue
|
|
27
|
+
if f.type is not str and isinstance(attr := getattr(self, f.name), str):
|
|
28
|
+
if f.type is bool:
|
|
29
|
+
if attr.lower() not in ("true", "false"):
|
|
30
|
+
raise TypeError(f.name, attr)
|
|
31
|
+
setattr(self, f.name, attr.lower() == "true")
|
|
32
|
+
else:
|
|
33
|
+
setattr(self, f.name, f.type(attr))
|
|
27
34
|
self._attrs[f.name] = getattr(self, f.name)
|
|
28
35
|
self._attrs = {k: v for k, v in self._attrs.items() if v is not None}
|
|
29
36
|
|
|
@@ -59,8 +66,7 @@ class Element:
|
|
|
59
66
|
self.__post_call__()
|
|
60
67
|
return self
|
|
61
68
|
|
|
62
|
-
def __post_call__(self):
|
|
63
|
-
...
|
|
69
|
+
def __post_call__(self): ...
|
|
64
70
|
|
|
65
71
|
|
|
66
72
|
@dataclass
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from satori.model import (
|
|
2
|
+
ArgvInteraction,
|
|
3
|
+
ButtonInteraction,
|
|
4
|
+
Channel,
|
|
5
|
+
Event,
|
|
6
|
+
Guild,
|
|
7
|
+
Login,
|
|
8
|
+
Member,
|
|
9
|
+
MessageObject,
|
|
10
|
+
Role,
|
|
11
|
+
User,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageEvent(Event):
|
|
16
|
+
channel: Channel
|
|
17
|
+
member: Member
|
|
18
|
+
message: MessageObject
|
|
19
|
+
user: User
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class UserEvent(Event):
|
|
23
|
+
user: User
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class GuildEvent(Event):
|
|
27
|
+
guild: Guild
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class GuildMemberEvent(GuildEvent):
|
|
31
|
+
user: User
|
|
32
|
+
member: Member
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class GuildRoleEvent(GuildEvent):
|
|
36
|
+
role: Role
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class LoginEvent(Event):
|
|
40
|
+
login: Login
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ReactionEvent(Event):
|
|
44
|
+
channel: Channel
|
|
45
|
+
user: User
|
|
46
|
+
message: MessageObject
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ButtonInteractionEvent(Event):
|
|
50
|
+
button: ButtonInteraction
|
|
51
|
+
user: User
|
|
52
|
+
channel: Channel
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class ArgvInteractionEvent(Event):
|
|
56
|
+
argv: ArgvInteraction
|
|
57
|
+
user: User
|
|
58
|
+
channel: Channel
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class InternalEvent(Event):
|
|
62
|
+
_type: str
|
|
63
|
+
_data: dict
|
|
@@ -7,6 +7,15 @@ from .element import Element, transform
|
|
|
7
7
|
from .parser import parse
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
class ModelBase:
|
|
11
|
+
@classmethod
|
|
12
|
+
def parse(cls, raw: dict):
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
|
|
15
|
+
def dump(self) -> dict:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
|
|
10
19
|
class ChannelType(IntEnum):
|
|
11
20
|
TEXT = 0
|
|
12
21
|
DIRECT = 1
|
|
@@ -15,7 +24,7 @@ class ChannelType(IntEnum):
|
|
|
15
24
|
|
|
16
25
|
|
|
17
26
|
@dataclass
|
|
18
|
-
class Channel:
|
|
27
|
+
class Channel(ModelBase):
|
|
19
28
|
id: str
|
|
20
29
|
type: ChannelType
|
|
21
30
|
name: Optional[str] = None
|
|
@@ -37,7 +46,7 @@ class Channel:
|
|
|
37
46
|
|
|
38
47
|
|
|
39
48
|
@dataclass
|
|
40
|
-
class Guild:
|
|
49
|
+
class Guild(ModelBase):
|
|
41
50
|
id: str
|
|
42
51
|
name: Optional[str] = None
|
|
43
52
|
avatar: Optional[str] = None
|
|
@@ -56,7 +65,7 @@ class Guild:
|
|
|
56
65
|
|
|
57
66
|
|
|
58
67
|
@dataclass
|
|
59
|
-
class User:
|
|
68
|
+
class User(ModelBase):
|
|
60
69
|
id: str
|
|
61
70
|
name: Optional[str] = None
|
|
62
71
|
nick: Optional[str] = None
|
|
@@ -81,7 +90,7 @@ class User:
|
|
|
81
90
|
|
|
82
91
|
|
|
83
92
|
@dataclass
|
|
84
|
-
class Member:
|
|
93
|
+
class Member(ModelBase):
|
|
85
94
|
user: Optional[User] = None
|
|
86
95
|
nick: Optional[str] = None
|
|
87
96
|
name: Optional[str] = None
|
|
@@ -111,7 +120,7 @@ class Member:
|
|
|
111
120
|
|
|
112
121
|
|
|
113
122
|
@dataclass
|
|
114
|
-
class Role:
|
|
123
|
+
class Role(ModelBase):
|
|
115
124
|
id: str
|
|
116
125
|
name: Optional[str] = None
|
|
117
126
|
|
|
@@ -135,7 +144,7 @@ class LoginStatus(IntEnum):
|
|
|
135
144
|
|
|
136
145
|
|
|
137
146
|
@dataclass
|
|
138
|
-
class Login:
|
|
147
|
+
class Login(ModelBase):
|
|
139
148
|
status: LoginStatus
|
|
140
149
|
user: Optional[User] = None
|
|
141
150
|
self_id: Optional[str] = None
|
|
@@ -161,19 +170,27 @@ class Login:
|
|
|
161
170
|
|
|
162
171
|
|
|
163
172
|
@dataclass
|
|
164
|
-
class ArgvInteraction:
|
|
173
|
+
class ArgvInteraction(ModelBase):
|
|
165
174
|
name: str
|
|
166
175
|
arguments: list
|
|
167
176
|
options: Any
|
|
168
177
|
|
|
178
|
+
@classmethod
|
|
179
|
+
def parse(cls, raw: dict):
|
|
180
|
+
return cls(**raw)
|
|
181
|
+
|
|
169
182
|
def dump(self):
|
|
170
183
|
return asdict(self)
|
|
171
184
|
|
|
172
185
|
|
|
173
186
|
@dataclass
|
|
174
|
-
class ButtonInteraction:
|
|
187
|
+
class ButtonInteraction(ModelBase):
|
|
175
188
|
id: str
|
|
176
189
|
|
|
190
|
+
@classmethod
|
|
191
|
+
def parse(cls, raw: dict):
|
|
192
|
+
return cls(**raw)
|
|
193
|
+
|
|
177
194
|
def dump(self):
|
|
178
195
|
return asdict(self)
|
|
179
196
|
|
|
@@ -187,18 +204,18 @@ class Opcode(IntEnum):
|
|
|
187
204
|
|
|
188
205
|
|
|
189
206
|
@dataclass
|
|
190
|
-
class Identify:
|
|
207
|
+
class Identify(ModelBase):
|
|
191
208
|
token: Optional[str] = None
|
|
192
209
|
sequence: Optional[int] = None
|
|
193
210
|
|
|
194
211
|
|
|
195
212
|
@dataclass
|
|
196
|
-
class Ready:
|
|
213
|
+
class Ready(ModelBase):
|
|
197
214
|
logins: List[Login]
|
|
198
215
|
|
|
199
216
|
|
|
200
217
|
@dataclass
|
|
201
|
-
class MessageObject:
|
|
218
|
+
class MessageObject(ModelBase):
|
|
202
219
|
id: str
|
|
203
220
|
content: str
|
|
204
221
|
channel: Optional[Channel] = None
|
|
@@ -208,6 +225,20 @@ class MessageObject:
|
|
|
208
225
|
created_at: Optional[datetime] = None
|
|
209
226
|
updated_at: Optional[datetime] = None
|
|
210
227
|
|
|
228
|
+
@classmethod
|
|
229
|
+
def from_elements(
|
|
230
|
+
cls,
|
|
231
|
+
id: str,
|
|
232
|
+
content: List[Element],
|
|
233
|
+
channel: Optional[Channel] = None,
|
|
234
|
+
guild: Optional[Guild] = None,
|
|
235
|
+
member: Optional[Member] = None,
|
|
236
|
+
user: Optional[User] = None,
|
|
237
|
+
created_at: Optional[datetime] = None,
|
|
238
|
+
updated_at: Optional[datetime] = None,
|
|
239
|
+
):
|
|
240
|
+
return cls(id, "".join(str(i) for i in content), channel, guild, member, user, created_at, updated_at)
|
|
241
|
+
|
|
211
242
|
@property
|
|
212
243
|
def message(self) -> List[Element]:
|
|
213
244
|
return transform(parse(self.content))
|
|
@@ -267,6 +298,9 @@ class Event:
|
|
|
267
298
|
role: Optional[Role] = None
|
|
268
299
|
user: Optional[User] = None
|
|
269
300
|
|
|
301
|
+
_type: Optional[str] = None
|
|
302
|
+
_data: Optional[dict] = None
|
|
303
|
+
|
|
270
304
|
@classmethod
|
|
271
305
|
def parse(cls, raw: dict):
|
|
272
306
|
data = {
|
|
@@ -277,9 +311,9 @@ class Event:
|
|
|
277
311
|
"timestamp": datetime.fromtimestamp(int(raw["timestamp"]) / 1000),
|
|
278
312
|
}
|
|
279
313
|
if "argv" in raw:
|
|
280
|
-
data["argv"] = ArgvInteraction(
|
|
314
|
+
data["argv"] = ArgvInteraction.parse(raw["argv"])
|
|
281
315
|
if "button" in raw:
|
|
282
|
-
data["button"] = ButtonInteraction(
|
|
316
|
+
data["button"] = ButtonInteraction.parse(raw["button"])
|
|
283
317
|
if "channel" in raw:
|
|
284
318
|
data["channel"] = Channel.parse(raw["channel"])
|
|
285
319
|
if "guild" in raw:
|
|
@@ -296,6 +330,10 @@ class Event:
|
|
|
296
330
|
data["role"] = Role.parse(raw["role"])
|
|
297
331
|
if "user" in raw:
|
|
298
332
|
data["user"] = User.parse(raw["user"])
|
|
333
|
+
if "_type" in raw:
|
|
334
|
+
data["_type"] = raw["_type"]
|
|
335
|
+
if "_data" in raw:
|
|
336
|
+
data["_data"] = raw["_data"]
|
|
299
337
|
return cls(**data)
|
|
300
338
|
|
|
301
339
|
def dump(self):
|
|
@@ -326,14 +364,18 @@ class Event:
|
|
|
326
364
|
res["role"] = self.role.dump()
|
|
327
365
|
if self.user:
|
|
328
366
|
res["user"] = self.user.dump()
|
|
367
|
+
if self._type:
|
|
368
|
+
res["_type"] = self._type
|
|
369
|
+
if self._data:
|
|
370
|
+
res["_data"] = self._data
|
|
329
371
|
return res
|
|
330
372
|
|
|
331
373
|
|
|
332
|
-
T = TypeVar("T")
|
|
374
|
+
T = TypeVar("T", bound=ModelBase)
|
|
333
375
|
|
|
334
376
|
|
|
335
377
|
@dataclass
|
|
336
|
-
class PageResult(Generic[T]):
|
|
378
|
+
class PageResult(ModelBase, Generic[T]):
|
|
337
379
|
data: List[T]
|
|
338
380
|
next: Optional[str] = None
|
|
339
381
|
|
|
@@ -341,3 +383,9 @@ class PageResult(Generic[T]):
|
|
|
341
383
|
def parse(cls, raw: dict, parser: Callable[[dict], T]) -> "PageResult[T]":
|
|
342
384
|
data = [parser(item) for item in raw["data"]]
|
|
343
385
|
return cls(data, raw.get("next"))
|
|
386
|
+
|
|
387
|
+
def dump(self):
|
|
388
|
+
res: dict = {"data": [item.dump() for item in self.data]}
|
|
389
|
+
if self.next:
|
|
390
|
+
res["next"] = self.next
|
|
391
|
+
return res
|