satori-python-server 0.14.1__tar.gz → 0.14.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: satori-python-server
3
- Version: 0.14.1
3
+ Version: 0.14.3
4
4
  Summary: Satori Protocol SDK for python, specify server 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>
@@ -27,7 +27,7 @@ classifiers = [
27
27
  "Programming Language :: Python :: 3.12",
28
28
  "Operating System :: OS Independent",
29
29
  ]
30
- version = "0.14.1"
30
+ version = "0.14.3"
31
31
 
32
32
  [project.license]
33
33
  text = "MIT"
@@ -20,7 +20,7 @@ from graia.amnesia.builtins.asgi import UvicornASGIService
20
20
  from launart import Launart, Service, any_completed
21
21
  from loguru import logger
22
22
  from starlette.applications import Starlette
23
- from starlette.datastructures import FormData
23
+ from starlette.datastructures import FormData as FormData
24
24
  from starlette.requests import Request as StarletteRequest
25
25
  from starlette.responses import JSONResponse, Response
26
26
  from starlette.routing import Route, WebSocketRoute
@@ -34,7 +34,7 @@ from satori.model import Event, ModelBase, Opcode
34
34
  from .adapter import Adapter as Adapter
35
35
  from .conection import WebsocketConnection
36
36
  from .deque import Deque
37
- from .formdata import parse_content_disposition
37
+ from .formdata import parse_content_disposition as parse_content_disposition
38
38
  from .model import Provider as Provider
39
39
  from .model import Request as Request
40
40
  from .model import Router as Router
@@ -94,6 +94,7 @@ class Server(Service, RouterMixin):
94
94
  self.path = path
95
95
  if self.path and not self.path.startswith("/"):
96
96
  self.path = f"/{self.path}"
97
+ self.url_base = f"http://{host}:{port}{self.path}/{version}"
97
98
  self._adapters = []
98
99
  self.providers = []
99
100
  self.routers = []
@@ -107,14 +108,13 @@ class Server(Service, RouterMixin):
107
108
 
108
109
  def apply(self, item: Provider | Router | Adapter):
109
110
  if isinstance(item, Adapter):
111
+ item.ensure_server(self)
110
112
  self._adapters.append(item)
111
113
  self.providers.append(item)
112
- for proxy_url_pf in item.proxy_urls():
113
- self.proxy_url_mapping[proxy_url_pf] = item
114
+ self.proxy_url_mapping[item.id] = item.proxy_urls()
114
115
  elif isinstance(item, Provider):
115
116
  self.providers.append(item)
116
- for proxy_url_pf in item.proxy_urls():
117
- self.proxy_url_mapping[proxy_url_pf] = item
117
+ self.proxy_url_mapping[item.id] = item.proxy_urls()
118
118
  elif isinstance(item, Router):
119
119
  self.routers.append(item)
120
120
  else:
@@ -159,20 +159,29 @@ class Server(Service, RouterMixin):
159
159
  for provider in self.providers:
160
160
  if not provider.authenticate(token):
161
161
  return await ws.close(code=3000, reason="Unauthorized")
162
- logins.extend(await provider.get_logins())
162
+ _logins = await provider.get_logins()
163
+ for _login in _logins:
164
+ _login.proxy_urls.extend(provider.proxy_urls())
165
+ logins.extend(_logins)
163
166
  sequence = body.get("sequence")
164
167
  if sequence is None:
165
168
  sequence = -1
166
169
  await connection.send({"op": Opcode.READY, "body": {"logins": [lo.dump() for lo in logins]}})
167
170
  self.connections.append(connection)
168
171
  logger.debug(f"New connection: {id(connection)}")
172
+ heartbeat_task = asyncio.create_task(connection.heartbeat())
173
+ close_task = asyncio.create_task(connection.close_signal.wait())
169
174
  try:
170
175
  if sequence > -1:
171
176
  for event in self._event_cache.after(sequence):
172
177
  await connection.send({"op": Opcode.EVENT, "body": event.dump()})
173
178
  await asyncio.sleep(0.1)
174
- await any_completed(connection.heartbeat(), connection.close_signal.wait())
179
+ await any_completed(heartbeat_task, close_task)
175
180
  finally:
181
+ await connection.connection_closed()
182
+ logger.debug(f"Connection closed: {id(connection)}")
183
+ heartbeat_task.cancel()
184
+ close_task.cancel()
176
185
  self.connections.remove(connection)
177
186
 
178
187
  async def admin_login_list_handler(self, request: StarletteRequest):
@@ -232,10 +241,11 @@ class Server(Service, RouterMixin):
232
241
  for provider in self.providers:
233
242
  if provider.ensure(platform, self_id):
234
243
  return await provider.download_uploaded(platform, self_id, path)
235
- for proxy_url_pf, provider in self.proxy_url_mapping.items():
236
- if url.startswith(proxy_url_pf):
237
- async with self.session.get(url) as resp:
238
- return await resp.read()
244
+ for provider in self.providers:
245
+ for proxy_url_pf in self.proxy_url_mapping[provider.id]:
246
+ if url.startswith(proxy_url_pf):
247
+ async with self.session.get(url) as resp:
248
+ return await resp.read()
239
249
  raise ValueError(f"Unknown proxy url: {url}")
240
250
 
241
251
  def get_local_file(self, url: str):
@@ -1,14 +1,19 @@
1
1
  from abc import abstractmethod
2
2
  from collections.abc import AsyncIterator
3
- from typing import Optional
3
+ from typing import TYPE_CHECKING, Optional
4
4
 
5
5
  from launart import Service
6
6
 
7
7
  from ..model import Event, Login
8
8
  from .route import RouterMixin
9
9
 
10
+ if TYPE_CHECKING:
11
+ from . import Server
12
+
10
13
 
11
14
  class Adapter(Service, RouterMixin):
15
+ server: "Server"
16
+
12
17
  @abstractmethod
13
18
  def get_platform(self) -> str: ...
14
19
 
@@ -38,3 +43,6 @@ class Adapter(Service, RouterMixin):
38
43
  @property
39
44
  def id(self):
40
45
  return f"satori-python.adapter.{self.get_platform()}#{id(self)}"
46
+
47
+ def ensure_server(self, server: "Server"):
48
+ self.server = server
@@ -22,7 +22,7 @@ class WebsocketConnection:
22
22
  async def heartbeat(self):
23
23
  while True:
24
24
  try:
25
- msg = await asyncio.wait_for(self.connection.receive_json(), timeout=10)
25
+ msg = await asyncio.wait_for(self.connection.receive_json(), timeout=12)
26
26
  if not isinstance(msg, dict) or msg.get("op") != Opcode.PING:
27
27
  continue
28
28
  await self.connection.send_json({"op": Opcode.PONG})
@@ -22,6 +22,9 @@ class Request(Generic[TP]):
22
22
 
23
23
  @runtime_checkable
24
24
  class Provider(Protocol):
25
+ @property
26
+ def id(self) -> str: ...
27
+
25
28
  def publisher(self) -> AsyncIterator[Event]: ...
26
29
 
27
30
  def authenticate(self, token: Optional[str]) -> bool: ...