satori-python 1.3.5__py3-none-any.whl → 1.3.7__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 CHANGED
@@ -45,7 +45,7 @@ from .model import Role as Role
45
45
  from .model import Upload as Upload
46
46
  from .model import User as User
47
47
 
48
- __version__ = "1.3.5"
48
+ __version__ = "1.3.7"
49
49
 
50
50
 
51
51
  MessageReceipt = MessageObject
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
@@ -129,7 +129,7 @@ class Element:
129
129
  args = {**self._attrs}
130
130
  elem = f"{self.__class__.__name__}(" + ", ".join(f"{k}={v!r}" for k, v in args.items())
131
131
  if self._children:
132
- elem += ", { " + ", ".join(repr(i) for i in self._children) + " }"
132
+ elem += ", [" + ", ".join(repr(i) for i in self._children) + "]"
133
133
  return elem + ")"
134
134
 
135
135
  def __call__(self, *content: "str | Element"):
@@ -257,7 +257,6 @@ class Link(Element):
257
257
  class Resource(Element):
258
258
  src: str
259
259
  title: str | None = None
260
- extra: InitVar[dict[str, Any] | None] = None
261
260
  cache: bool | None = None
262
261
  timeout: int | None = None
263
262
 
@@ -271,14 +270,15 @@ class Resource(Element):
271
270
  raw: bytes | BytesIO | None = None,
272
271
  mime: str | None = None,
273
272
  name: str | None = None,
273
+ width: int | None = None,
274
+ height: int | None = None,
274
275
  duration: float | None = None,
275
276
  poster: str | None = None,
276
- extra: dict[str, Any] | None = None,
277
277
  cache: bool | None = None,
278
278
  timeout: int | None = None,
279
279
  **kwargs,
280
280
  ) -> Self:
281
- data: dict[str, Any] = {"extra": extra, **kwargs}
281
+ data: dict[str, Any] = {**kwargs}
282
282
  if url is not None:
283
283
  data |= {"src": url}
284
284
  elif path:
@@ -296,6 +296,10 @@ class Resource(Element):
296
296
  raise ValueError(f"{cls} need at least one of url, path and raw")
297
297
  if name is not None:
298
298
  data["title"] = name
299
+ if width is not None and cls in (Image, Video):
300
+ data["width"] = width
301
+ if height is not None and cls in (Image, Video):
302
+ data["height"] = height
299
303
  if duration is not None and cls is Audio:
300
304
  data["duration"] = duration
301
305
  if poster is not None and cls in (Video, Audio, File):
@@ -304,11 +308,7 @@ class Resource(Element):
304
308
  data["cache"] = cache
305
309
  if timeout is not None:
306
310
  data["timeout"] = timeout
307
- return cls(**data)
308
-
309
- def __post_init__(self, extra: dict[str, Any] | None = None):
310
- if extra:
311
- self._attrs.update(extra)
311
+ return cls.unpack(data)
312
312
 
313
313
 
314
314
  @dataclass(repr=False)
@@ -582,7 +582,7 @@ class Custom(Element):
582
582
  attrs: dict[str, Any] | None = None,
583
583
  children: Sequence[str | Element] | None = None,
584
584
  ):
585
- self.type = type
585
+ self._type = type
586
586
  if not hasattr(self, "_attrs"):
587
587
  self._attrs = attrs or {}
588
588
  else:
@@ -595,7 +595,21 @@ class Custom(Element):
595
595
  @property
596
596
  @override
597
597
  def tag(self) -> str:
598
- return self.type
598
+ return self._type
599
+
600
+ def __repr__(self) -> str:
601
+ if not self._attrs:
602
+ self._attrs = {k: v for k, v in self.__dict__.items() if not k.startswith("_")}
603
+ args = {**self._attrs}
604
+ elem = (
605
+ f"{self.__class__.__name__}({self._type!r}"
606
+ + ", {"
607
+ + ", ".join(f"{k!r}: {v!r}" for k, v in args.items())
608
+ + "}"
609
+ )
610
+ if self._children:
611
+ elem += ", [" + ", ".join(repr(i) for i in self._children) + "]"
612
+ return elem + ")"
599
613
 
600
614
 
601
615
  @dataclass(repr=False)
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=str(ae))
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=str(e))
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 connection in self.connections:
243
- if not connection.alive:
244
- continue
245
- try:
246
- await connection.send({"op": Opcode.EVENT, "body": event.dump()})
247
- except (WebSocketDisconnect, RuntimeError):
248
- break
249
- except Exception as e:
250
- print_exc()
251
- logger.error(e)
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
- print_exc()
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python
3
- Version: 1.3.5
3
+ Version: 1.3.7
4
4
  Summary: Satori Protocol SDK for python
5
5
  Home-page: https://github.com/RF-Tar-Railt/satori-python
6
6
  Author-Email: RF-Tar-Railt <rf_tar_railt@qq.com>
@@ -1,9 +1,9 @@
1
- satori/__init__.py,sha256=A3joKPbJO_1wuVS1FhBVUG6NqypAwgpxRWhAUV56L9w,1888
1
+ satori/__init__.py,sha256=9-XGaPuZ5S-JE-Ew5MKGzQLQ2TzrPSLog__4k7td8CY,1888
2
2
  satori/_vendor/fleep.py,sha256=_zKP7iY3mMQr0rC5KbgkbkMorT3KVLeIsWPLIUa0Y34,16347
3
- satori/client/__init__.py,sha256=1HLiYmYZoScymOzTuOVZht99eREXMUT09F8PZzvAMPA,14484
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=iHBazxFDDE_-Qeyra2DVxZvQRCYqEoDrrzEVJjig93Q,1858
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=Yo92PcQUk2vUHomk0zDVXB5wZavnKQCNcgHlPtMWAno,20831
14
+ satori/element.py,sha256=7a1azqihsYBNe7BHGvN-MQH4jOsTj47Ei9h5qwDVu9M,21355
15
15
  satori/event.py,sha256=yC6rKaa7JbeOZ4Un1rX-Isvm3EcR7e782nxYf1j6JdM,1060
16
16
  satori/exception.py,sha256=edKeuLRZAQxyCdw1_XCgOtv5tIHkfHw0Die9B818_HM,545
17
17
  satori/model.py,sha256=L8Wlk4zGlnR5kM4rAsaXvPufxdwmUsshkFY9PLGj8tY,17614
18
18
  satori/parser.py,sha256=EyKitj39OewGhZuNIvrd8nWJiL5EJAVy7mtcv8Zk6v0,14658
19
19
  satori/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- satori/server/__init__.py,sha256=qvbPHSbIiOQGJV_D3yfEKupqOqMKwzrLixkjy3AMgqs,23820
21
- satori/server/adapter.py,sha256=-eIkj1TvZ5LJ6jrRoobvea_MlrqksY3HiZ8sDYY3wP8,1600
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.5.dist-info/METADATA,sha256=Qfsx-7KwVQ6mFAgJkwUklMRVmaoqo8WGCIlJCILLX4w,5511
29
- satori_python-1.3.5.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
30
- satori_python-1.3.5.dist-info/licenses/LICENSE,sha256=2EDswKd1M1648judap8BEEwp3Jz6IpfFP2wYVdU0Y2o,1069
31
- satori_python-1.3.5.dist-info/RECORD,,
28
+ satori_python-1.3.7.dist-info/METADATA,sha256=DraUqSrYvoBkX-l8wAmLmr04Nh1Oz6CSVcK-ngCnzE4,5511
29
+ satori_python-1.3.7.dist-info/WHEEL,sha256=rSwsxJWe3vzyR5HCwjWXQruDgschpei4h_giTm0dJVE,90
30
+ satori_python-1.3.7.dist-info/licenses/LICENSE,sha256=2EDswKd1M1648judap8BEEwp3Jz6IpfFP2wYVdU0Y2o,1069
31
+ satori_python-1.3.7.dist-info/RECORD,,