satori-python 1.3.6__py3-none-any.whl → 1.3.8__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.
- satori/__init__.py +1 -1
- satori/client/__init__.py +1 -4
- satori/client/config.py +3 -3
- satori/element.py +9 -0
- satori/model.py +3 -3
- satori/server/__init__.py +20 -14
- satori/server/adapter.py +4 -2
- {satori_python-1.3.6.dist-info → satori_python-1.3.8.dist-info}/METADATA +1 -1
- {satori_python-1.3.6.dist-info → satori_python-1.3.8.dist-info}/RECORD +11 -11
- {satori_python-1.3.6.dist-info → satori_python-1.3.8.dist-info}/WHEEL +0 -0
- {satori_python-1.3.6.dist-info → satori_python-1.3.8.dist-info}/licenses/LICENSE +0 -0
satori/__init__.py
CHANGED
satori/client/__init__.py
CHANGED
|
@@ -31,10 +31,7 @@ from .protocol import ApiProtocol as ApiProtocol
|
|
|
31
31
|
TConfig = TypeVar("TConfig", bound=Config)
|
|
32
32
|
TE = TypeVar("TE", bound=Event, contravariant=True)
|
|
33
33
|
|
|
34
|
-
MAPPING: dict[type[Config], type[BaseNetwork]] = {
|
|
35
|
-
WebhookInfo: WebhookNetwork,
|
|
36
|
-
WebsocketsInfo: WsNetwork,
|
|
37
|
-
}
|
|
34
|
+
MAPPING: dict[type[Config], type[BaseNetwork]] = {WebhookInfo: WebhookNetwork, WebsocketsInfo: WsNetwork} # noqa
|
|
38
35
|
|
|
39
36
|
_app: App | None = None
|
|
40
37
|
|
satori/client/config.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
|
|
3
3
|
from yarl import URL
|
|
4
4
|
|
|
@@ -25,8 +25,8 @@ class WebsocketsInfo(Config):
|
|
|
25
25
|
token: str | None = None
|
|
26
26
|
secure: bool = False
|
|
27
27
|
timeout: float | None = None
|
|
28
|
-
identity: str = None # type: ignore
|
|
29
|
-
api_base: URL = None # type: ignore
|
|
28
|
+
identity: str = field(init=False, default=None) # type: ignore
|
|
29
|
+
api_base: URL = field(init=False, default=None) # type: ignore
|
|
30
30
|
|
|
31
31
|
def __post_init__(self):
|
|
32
32
|
if self.path and not self.path.startswith("/"):
|
satori/element.py
CHANGED
|
@@ -469,6 +469,15 @@ class Paragraph(Style):
|
|
|
469
469
|
return "p"
|
|
470
470
|
|
|
471
471
|
|
|
472
|
+
class Blockquote(Style):
|
|
473
|
+
"""<blockquote> 元素表示块引用"""
|
|
474
|
+
|
|
475
|
+
@property
|
|
476
|
+
@override
|
|
477
|
+
def tag(self) -> str:
|
|
478
|
+
return "blockquote"
|
|
479
|
+
|
|
480
|
+
|
|
472
481
|
@dataclass(init=False, repr=False)
|
|
473
482
|
class Message(Element):
|
|
474
483
|
"""<message> 元素的基本用法是表示一条消息。
|
satori/model.py
CHANGED
|
@@ -34,7 +34,7 @@ class ModelBase:
|
|
|
34
34
|
data = {}
|
|
35
35
|
cls.before_parse(data)
|
|
36
36
|
for name in cls.__dataclass_fields__:
|
|
37
|
-
if name in raw:
|
|
37
|
+
if name in raw and raw[name] is not None:
|
|
38
38
|
if name in cls.__converter__:
|
|
39
39
|
data[name] = cls.__converter__[name](raw[name])
|
|
40
40
|
else:
|
|
@@ -55,7 +55,7 @@ class ModelBase:
|
|
|
55
55
|
keys = frozenset(k for k in keys if not k.startswith("_"))
|
|
56
56
|
|
|
57
57
|
def parse1(cls_: type[Self], raw: dict, _keys=keys) -> Self:
|
|
58
|
-
data = {k: v for k, v in raw.items() if k in _keys}
|
|
58
|
+
data = {k: v for k, v in raw.items() if k in _keys and v is not None}
|
|
59
59
|
obj = cls_(**data) # type: ignore
|
|
60
60
|
obj._raw_data = raw
|
|
61
61
|
return obj
|
|
@@ -64,7 +64,7 @@ class ModelBase:
|
|
|
64
64
|
data = {}
|
|
65
65
|
cls_.before_parse(data)
|
|
66
66
|
for name in _keys:
|
|
67
|
-
if name in raw:
|
|
67
|
+
if name in raw and raw[name] is not None:
|
|
68
68
|
if name in cls_.__converter__:
|
|
69
69
|
data[name] = cls_.__converter__[name](raw[name])
|
|
70
70
|
else:
|
satori/server/__init__.py
CHANGED
|
@@ -7,13 +7,13 @@ import re
|
|
|
7
7
|
import secrets
|
|
8
8
|
import signal
|
|
9
9
|
import threading
|
|
10
|
+
import traceback
|
|
10
11
|
import urllib.parse
|
|
11
12
|
from collections.abc import Awaitable, Callable, Iterable
|
|
12
13
|
from contextlib import suppress
|
|
13
14
|
from itertools import chain
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from tempfile import TemporaryDirectory
|
|
16
|
-
from traceback import print_exc
|
|
17
17
|
from typing import Any, TypeVar
|
|
18
18
|
|
|
19
19
|
import aiohttp
|
|
@@ -103,10 +103,10 @@ async def _request_handler(action: str, request: StarletteRequest, func: RouteCa
|
|
|
103
103
|
return Response(status_code=504, content="Request timeout")
|
|
104
104
|
except ActionFailed as ae:
|
|
105
105
|
logger.warning(ae)
|
|
106
|
-
return Response(status_code=ae.CODE, content=
|
|
106
|
+
return Response(status_code=ae.CODE, content=repr(ae))
|
|
107
107
|
except Exception as e:
|
|
108
108
|
logger.error(e)
|
|
109
|
-
return Response(status_code=500, content=
|
|
109
|
+
return Response(status_code=500, content=repr(e))
|
|
110
110
|
if isinstance(res, ModelBase):
|
|
111
111
|
return JSONResponse(content=res.dump())
|
|
112
112
|
if res and isinstance(res, list) and isinstance(res[0], ModelBase):
|
|
@@ -239,16 +239,22 @@ class Server(Service, RouterMixin):
|
|
|
239
239
|
event.sn = self._sequence
|
|
240
240
|
self._event_cache.append(event)
|
|
241
241
|
self._sequence += 1
|
|
242
|
-
for
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
242
|
+
connections = [conn for conn in self.connections if conn.alive]
|
|
243
|
+
payload = {"op": Opcode.EVENT, "body": event.dump()}
|
|
244
|
+
ws_tasks = [conn.send(payload) for conn in connections]
|
|
245
|
+
if ws_tasks:
|
|
246
|
+
results = await asyncio.gather(*ws_tasks, return_exceptions=True)
|
|
247
|
+
for conn, result in zip(connections, results):
|
|
248
|
+
if not result:
|
|
249
|
+
continue
|
|
250
|
+
if isinstance(result, (WebSocketDisconnect, RuntimeError)):
|
|
251
|
+
logger.warning(f"Connection {id(conn):x} closed: {result}")
|
|
252
|
+
else:
|
|
253
|
+
traceback.print_exception(type(result), result, result.__traceback__)
|
|
254
|
+
logger.error(result)
|
|
255
|
+
await conn.connection_closed()
|
|
256
|
+
if conn in self.connections:
|
|
257
|
+
self.connections.remove(conn)
|
|
252
258
|
for hook in self.webhooks:
|
|
253
259
|
try:
|
|
254
260
|
async with self.session.post(
|
|
@@ -263,7 +269,7 @@ class Server(Service, RouterMixin):
|
|
|
263
269
|
) as resp:
|
|
264
270
|
resp.raise_for_status()
|
|
265
271
|
except Exception as e:
|
|
266
|
-
|
|
272
|
+
traceback.print_exception(type(e), e, e.__traceback__)
|
|
267
273
|
logger.error(e)
|
|
268
274
|
|
|
269
275
|
async def websocket_server_handler(self, ws: WebSocket):
|
satori/server/adapter.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
|
-
from typing import TYPE_CHECKING, Union
|
|
2
|
+
from typing import TYPE_CHECKING, Generic, TypeVar, Union
|
|
3
3
|
|
|
4
4
|
from launart import Service
|
|
5
5
|
from starlette.responses import Response
|
|
@@ -16,10 +16,12 @@ if TYPE_CHECKING:
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
LoginType = Union[Login, LoginPartial] # noqa: UP007
|
|
19
|
+
T = TypeVar("T")
|
|
19
20
|
|
|
20
21
|
|
|
21
|
-
class Adapter(Service, RouterMixin):
|
|
22
|
+
class Adapter(Service, RouterMixin, Generic[T]):
|
|
22
23
|
server: "Server"
|
|
24
|
+
config: T
|
|
23
25
|
|
|
24
26
|
@abstractmethod
|
|
25
27
|
def get_platform(self) -> str:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
satori/__init__.py,sha256=
|
|
1
|
+
satori/__init__.py,sha256=gzKo9sSVvK-ievs6_yV1j96p_m3UTojchviX4kdw1Z8,1888
|
|
2
2
|
satori/_vendor/fleep.py,sha256=_zKP7iY3mMQr0rC5KbgkbkMorT3KVLeIsWPLIUa0Y34,16347
|
|
3
|
-
satori/client/__init__.py,sha256=
|
|
3
|
+
satori/client/__init__.py,sha256=KZB5Ta5KzxdfCO8Ey5AuG7l4dzI0KnkqlaBVuyrfEMA,14481
|
|
4
4
|
satori/client/account.py,sha256=HP7BJWkN69SB9ldNIjHxWlXcrmB-IPnTnYqm3lBMvl8,2742
|
|
5
5
|
satori/client/account.pyi,sha256=ha632NYjlQDU65cjmYiP3eoPJnb82tMXuApLlUBwUKE,18744
|
|
6
|
-
satori/client/config.py,sha256=
|
|
6
|
+
satori/client/config.py,sha256=50y9RXHHUJ2noNEiREsri_MOM_6Waozvx6x2JOcOFd8,1919
|
|
7
7
|
satori/client/network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
satori/client/network/base.py,sha256=0ajEhclVpVch-HMWLknoicexNe_keWW0dbLtboAtdtE,817
|
|
9
9
|
satori/client/network/util.py,sha256=WfVTjmYxj4EjrbR8r3zm7RJZjxmGQwC7yjZtXQT6SWE,1246
|
|
@@ -11,21 +11,21 @@ satori/client/network/webhook.py,sha256=N1zcJGx-Ijwtc0dIsPWzdnJSLi0ydQTengHBxtbb
|
|
|
11
11
|
satori/client/network/websocket.py,sha256=oZtiFLywP_Rw-_I-hl32nShCTTQ1XKlv0BhWJj53x9Y,8476
|
|
12
12
|
satori/client/protocol.py,sha256=hXrtgh-ypiF1W-dTl5I4LBWg8P2VTNKYQWFq8aRCeeo,28577
|
|
13
13
|
satori/const.py,sha256=6TQAIlgNq7fHLiadxGUU-mzkffOcpRrEvq5yrWYMuGc,2738
|
|
14
|
-
satori/element.py,sha256=
|
|
14
|
+
satori/element.py,sha256=JjF_flhH1GpJlSLEA1mHUvxLugn47L5KlHK8kt5Sp4k,21510
|
|
15
15
|
satori/event.py,sha256=yC6rKaa7JbeOZ4Un1rX-Isvm3EcR7e782nxYf1j6JdM,1060
|
|
16
16
|
satori/exception.py,sha256=edKeuLRZAQxyCdw1_XCgOtv5tIHkfHw0Die9B818_HM,545
|
|
17
|
-
satori/model.py,sha256=
|
|
17
|
+
satori/model.py,sha256=tY47aOsb6HrpPAvGA6IHLNthVCwoUB8c2w1XeDFFmvQ,17684
|
|
18
18
|
satori/parser.py,sha256=EyKitj39OewGhZuNIvrd8nWJiL5EJAVy7mtcv8Zk6v0,14658
|
|
19
19
|
satori/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
|
-
satori/server/__init__.py,sha256=
|
|
21
|
-
satori/server/adapter.py,sha256
|
|
20
|
+
satori/server/__init__.py,sha256=SumWaENrpRiZL7hLZpk8zcOJ-wvphHPMJjKwyhVzhDQ,24338
|
|
21
|
+
satori/server/adapter.py,sha256=sxP6XPcRmRSxxkXhnczXghLASYpMKgDYAebsGft_ud4,1661
|
|
22
22
|
satori/server/connection.py,sha256=28sDNT9QmTJknCRQAMB1jzxcRiG3Tw_TO0AFlCMNr5g,1439
|
|
23
23
|
satori/server/formdata.py,sha256=lHLTatf6117eC4U7FX77PjGvchEPiZGmVWnpe9tceh4,437
|
|
24
24
|
satori/server/model.py,sha256=zzjNa3iBFkd8crZIVTrEisPaB0g8JsxV4fA2rRU0maw,1105
|
|
25
25
|
satori/server/route.py,sha256=kpelAX0kYQbJncp5biWi8pWRcEIez9m9sJN8NF3sjn4,10654
|
|
26
26
|
satori/server/utils.py,sha256=hMNqLzbzcwyLJO-sGncO5kuviiW5Y6k34fj4AS2iVWQ,910
|
|
27
27
|
satori/utils.py,sha256=N8HEw-DOXxOBwFHmmXgqPdMh6AXjiGR0ZGa1zBPH6EI,822
|
|
28
|
-
satori_python-1.3.
|
|
29
|
-
satori_python-1.3.
|
|
30
|
-
satori_python-1.3.
|
|
31
|
-
satori_python-1.3.
|
|
28
|
+
satori_python-1.3.8.dist-info/METADATA,sha256=GaRExW9GyUj81eVGnfFxhaugCbH_LB-U4TsuByfUc-M,5511
|
|
29
|
+
satori_python-1.3.8.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
|
|
30
|
+
satori_python-1.3.8.dist-info/licenses/LICENSE,sha256=2EDswKd1M1648judap8BEEwp3Jz6IpfFP2wYVdU0Y2o,1069
|
|
31
|
+
satori_python-1.3.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|