pyrogram-client 0.0.5__tar.gz → 0.0.6__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.
Files changed (21) hide show
  1. {pyrogram_client-0.0.5/pyrogram_client.egg-info → pyrogram_client-0.0.6}/PKG-INFO +1 -1
  2. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyro_client/client/base.py +37 -12
  3. pyrogram_client-0.0.6/pyro_client/client/bot.py +41 -0
  4. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyro_client/client/user.py +28 -32
  5. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyro_client/storage.py +50 -42
  6. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6/pyrogram_client.egg-info}/PKG-INFO +1 -1
  7. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyrogram_client.egg-info/SOURCES.txt +0 -1
  8. pyrogram_client-0.0.5/pyro_client/client/bot.py +0 -11
  9. pyrogram_client-0.0.5/pyro_client/client/single.py +0 -40
  10. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/.env.sample +0 -0
  11. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/.gitignore +0 -0
  12. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/.pre-commit-config.yaml +0 -0
  13. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/makefile +0 -0
  14. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyproject.toml +0 -0
  15. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyro_client/client/dc.json +0 -0
  16. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyro_client/client/file.py +0 -0
  17. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyro_client/loader.py +0 -0
  18. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyrogram_client.egg-info/dependency_links.txt +0 -0
  19. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyrogram_client.egg-info/requires.txt +0 -0
  20. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/pyrogram_client.egg-info/top_level.txt +0 -0
  21. {pyrogram_client-0.0.5 → pyrogram_client-0.0.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrogram-client
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Author-email: Mike Artemiev <mixartemev@gmail.com>
5
5
  Project-URL: Homepage, https://gitlab.com/XyncNet/pyro-client
6
6
  Project-URL: Repository, https://gitlab.com/XyncNet/pyro-client
@@ -4,30 +4,54 @@ from io import BytesIO
4
4
  from typing import Literal
5
5
 
6
6
  from pyrogram import Client
7
+ from pyrogram.errors import AuthKeyUnregistered, Unauthorized
7
8
  from pyrogram.filters import chat, contact, AndFilter
8
9
  from pyrogram.handlers import MessageHandler
9
10
  from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
11
+ from x_auth.models import Session, App, Username
10
12
 
11
- from pyro_client.client.single import SingleMeta
12
13
  from pyro_client.storage import PgStorage
13
14
 
14
15
  AuthTopic = Literal["phone", "code", "pass"]
15
16
 
16
17
 
17
- def sync_call_async(async_func, *args):
18
- loop = asyncio.get_event_loop() # Получаем текущий запущенный цикл
19
- if loop.is_running():
20
- future = asyncio.run_coroutine_threadsafe(async_func(*args), loop)
21
- return future.result(5) # Блокируемся, пока корутина не выполнится
18
+ class BaseClient(Client):
19
+ storage: PgStorage
20
+ uid: int = None
22
21
 
22
+ _insts: dict[int, "BaseClient"] = {}
23
23
 
24
- class BaseClient(Client, metaclass=SingleMeta):
25
- storage: PgStorage
24
+ def __new__(cls, uid: int, *args, **kwargs):
25
+ """Single client for each uid"""
26
+ cls.uid = uid
27
+ if uid not in cls._insts:
28
+ cls._insts[uid] = Client.__new__(cls)
29
+ return cls._insts[uid]
26
30
 
27
- def __init__(self, name: str, api_id, api_hash, *args, **kwargs):
28
- super().__init__(
29
- name, api_id=api_id, api_hash=api_hash, *args, storage_engine=PgStorage(name), workers=1, **kwargs
30
- )
31
+ def __init__(self, *args, **kwargs):
32
+ if not hasattr(self, "name"):
33
+ name = str(self.uid)
34
+ super().__init__(name, storage_engine=PgStorage(name), **kwargs) # , workers=2
35
+
36
+ async def preload(self):
37
+ if not (session := await Session.get_or_none(id=self.uid).prefetch_related("api")):
38
+ app = await App[20373304]
39
+ username, _ = await Username.get_or_create(id=self.uid)
40
+ bt = self.bot_token and self.bot_token.split(":")[1]
41
+ session = await Session.create(id=self.uid, api=app, user=username, is_bot=bt)
42
+ self.api_id = app.id
43
+ self.api_hash = app.hsh
44
+ await session.fetch_related("api")
45
+ self.storage.session = session
46
+
47
+ async def start(self, use_qr: bool = False, except_ids: list[int] = None):
48
+ if not self.is_connected:
49
+ await self.preload()
50
+ try:
51
+ await super().start(use_qr=use_qr, except_ids=except_ids or [])
52
+ except (AuthKeyUnregistered, Unauthorized) as e:
53
+ await self.storage.session.delete()
54
+ raise e
31
55
 
32
56
  async def send(
33
57
  self,
@@ -64,6 +88,7 @@ class BaseClient(Client, metaclass=SingleMeta):
64
88
  self.dispatcher.groups[g] = []
65
89
  self.dispatcher.groups = OrderedDict(sorted(self.dispatcher.groups.items()))
66
90
  self.dispatcher.groups[g].append(handler)
91
+ self.storage.session.state |= {uid: {"waiting_for": topic}}
67
92
  #
68
93
  while past < timeout:
69
94
  if txt := self.storage.session.state.get(uid, {}).pop(topic, None):
@@ -0,0 +1,41 @@
1
+ from pyro_client.client.base import BaseClient, AuthTopic
2
+ from pyro_client.loader import TOKEN
3
+
4
+
5
+ class BotClient(BaseClient):
6
+ def __new__(cls, bid: int | str) -> "BotClient":
7
+ """
8
+ :param bid: int | str - Если для такого bot_id в бд есть Session с его токеном в is_bot - можно просто id: int
9
+ если нет - нужно передавать весь токен, что б Сессия в бд создалась.
10
+ """
11
+ if isinstance(bid, str):
12
+ bid = int(bid.split(":")[0])
13
+ return super().__new__(cls, bid)
14
+
15
+ def __init__(self, bid: int | str):
16
+ bt = isinstance(bid, str) and ":" in bid and bid
17
+ super().__init__(bid, bot_token=bt or None)
18
+
19
+ async def wait_auth_from(self, uid: int, topic: AuthTopic, past: int = 0, timeout: int = 60) -> str:
20
+ return await super().wait_from(uid, topic, past, timeout)
21
+
22
+
23
+ async def main():
24
+ from x_auth import models
25
+ from x_model import init_db
26
+
27
+ from pyro_client.loader import PG_DSN
28
+
29
+ _ = await init_db(PG_DSN, models, True)
30
+
31
+ bc: BotClient = BotClient(6806432376)
32
+ bc1: BotClient = BotClient(TOKEN)
33
+ await bc.start()
34
+ await bc1.start()
35
+ await bc.stop()
36
+
37
+
38
+ if __name__ == "__main__":
39
+ from asyncio import run
40
+
41
+ run(main())
@@ -5,20 +5,25 @@ from os.path import dirname
5
5
  from pydantic import BaseModel, Field
6
6
  from pyrogram import enums
7
7
  from pyrogram.enums import ClientPlatform
8
- from pyrogram.errors import BadRequest, SessionPasswordNeeded, AuthKeyUnregistered, Unauthorized
8
+ from pyrogram.errors import BadRequest, SessionPasswordNeeded
9
9
  from pyrogram.session import Auth, Session as PyroSession
10
10
  from pyrogram.types import Message, User, SentCode, InlineKeyboardButton, KeyboardButton
11
- from x_auth.models import Session, Username, Proxy
11
+ from x_auth.models import Username, Proxy
12
12
 
13
13
  from pyro_client.client.base import BaseClient, AuthTopic
14
14
  from pyro_client.client.bot import BotClient
15
- from pyro_client.loader import WSToken, TOKEN
15
+ from pyro_client.loader import WSToken
16
16
 
17
17
  vers: dict[ClientPlatform, str] = {
18
18
  ClientPlatform.IOS: "18.5",
19
19
  ClientPlatform.ANDROID: "16",
20
20
  }
21
21
 
22
+ devs: dict[ClientPlatform, str] = {
23
+ ClientPlatform.IOS: "iPhone 17 Air",
24
+ ClientPlatform.ANDROID: "Tesla Phone",
25
+ }
26
+
22
27
 
23
28
  class Prx(BaseModel):
24
29
  scheme: str = "socks5"
@@ -31,31 +36,24 @@ class Prx(BaseModel):
31
36
  class UserClient(BaseClient):
32
37
  bot: BotClient
33
38
 
34
- def __init__(self, sess: Session, bot):
39
+ def __init__(self, uid: int, bot: BotClient = None):
35
40
  self.bot = bot
36
- super().__init__(
37
- sess.id,
38
- sess.api.id,
39
- sess.api.hsh,
40
- device_model="iPhone 16e",
41
- app_version=sess.api.ver,
42
- system_version=vers.get(sess.api.platform),
43
- client_platform=sess.api.platform,
44
- proxy=sess.proxy and sess.proxy.dict(),
45
- )
41
+ super().__init__(uid)
42
+
43
+ async def preload(self):
44
+ await super().preload()
45
+ self.device_model = devs[self.storage.session.api.platform]
46
+ self.app_version = self.storage.session.api.ver
47
+ self.system_version = vers[self.storage.session.api.platform]
48
+ self.client_platform = self.storage.session.api.platform
49
+ # await self.storage.session.fetch_related("proxy")
50
+ # self.proxy: Proxy = self.storage.session.proxy and self.storage.session.proxy.dict()
46
51
 
47
52
  async def start(self, use_qr: bool = False, except_ids: list[int] = None):
48
53
  if not self.bot.is_connected:
49
54
  await self.bot.start()
50
- # dcs = await self.bot.invoke(GetConfig())
51
- try:
52
- await super().start(use_qr=use_qr, except_ids=except_ids or [])
53
- await self.send("im ok")
54
- except AuthKeyUnregistered as e:
55
- await self.storage.session.delete()
56
- raise e
57
- except (AuthKeyUnregistered, Unauthorized) as e:
58
- raise e
55
+ await super().start(use_qr, except_ids)
56
+ # await self.send("/start", self.bot.uid)
59
57
 
60
58
  async def ask_for(
61
59
  self, topic: AuthTopic, question: str, btns: list[InlineKeyboardButton, KeyboardButton] = None
@@ -139,17 +137,13 @@ class UserClient(BaseClient):
139
137
  else:
140
138
  raise Exception("User does not sent code")
141
139
  if isinstance(signed_in, User):
142
- await self.send("✅", self.bot.me.id)
140
+ await self.send("✅")
143
141
  await self.storage.save()
144
142
  return signed_in
145
143
 
146
144
  if not signed_in:
147
145
  await self.receive("No registered such phone number")
148
146
 
149
- async def stop(self, block: bool = True):
150
- await super().stop(block)
151
- await self.bot.stop(block)
152
-
153
147
  async def session_update(self, dc: int):
154
148
  await self.session.stop()
155
149
 
@@ -173,18 +167,20 @@ async def main():
173
167
  from x_auth import models
174
168
  from x_model import init_db
175
169
 
176
- from pyro_client.loader import WSToken, PG_DSN
170
+ from pyro_client.loader import PG_DSN
177
171
 
178
172
  _ = await init_db(PG_DSN, models, True)
179
173
 
180
174
  logging.basicConfig(level=logging.INFO)
181
175
 
182
- await models.Proxy.load_list(WSToken)
176
+ # await models.Proxy.load_list(WSToken)
183
177
  # session = await models.Session.filter(is_bot__isnull=True).order_by("-date").prefetch_related("proxy").first()
184
- bc: BotClient = await BotClient(TOKEN)
185
- uc: UserClient = await UserClient(5547330178, bc)
178
+ bc: BotClient = BotClient(6806432376)
179
+ uc: UserClient = UserClient(7314099964, bc)
186
180
  # try:
187
181
  await uc.start()
182
+ # b = await uc.resolve_peer('xyncnetbot')
183
+ await uc.send("/start", bc.me.username)
188
184
  # except Exception as e:
189
185
  # print(e.MESSAGE)
190
186
  # await uc.send(e.MESSAGE)
@@ -1,34 +1,32 @@
1
1
  import time
2
- from typing import Any, Literal
2
+ from typing import Any
3
3
 
4
4
  from pyrogram import raw, utils
5
5
  from pyrogram.storage import Storage
6
+ from x_auth.enums import PeerType
6
7
  from x_auth.models import Username, Version, Session, Peer, UpdateState
7
8
 
8
9
 
9
- def get_input_peer(peer_id: int, access_hash: int, peer_type: Literal["user", "bot", "group", "channel", "supergroup"]):
10
- if peer_type in ["user", "bot"]:
10
+ def get_input_peer(peer_id: int, access_hash: int, peer_type: PeerType):
11
+ if peer_type in [PeerType.user, PeerType.bot]:
11
12
  return raw.types.InputPeerUser(user_id=peer_id, access_hash=access_hash)
12
-
13
- if peer_type == "group":
13
+ if peer_type == PeerType.group:
14
14
  return raw.types.InputPeerChat(chat_id=-peer_id)
15
-
16
- if peer_type in ["channel", "supergroup"]:
15
+ if peer_type in [PeerType.channel, PeerType.supergroup]:
17
16
  return raw.types.InputPeerChannel(channel_id=utils.get_channel_id(peer_id), access_hash=access_hash)
18
-
19
- raise ValueError(f"Invalid peer type: {peer_type}")
17
+ raise ValueError(f"Invalid peer type: {peer_type.name}")
20
18
 
21
19
 
22
20
  class PgStorage(Storage):
23
21
  VERSION = 1
24
- USERNAME_TTL = 8 * 60 * 60
22
+ USERNAME_TTL = 30 * 24 * 3600
25
23
  session: Session
26
-
27
- # me_id: int
24
+ sid: int
28
25
 
29
26
  async def open(self):
30
- # self.me_id = int((uid_dc := self.name.split("_")).pop(0))
31
- self.session = await Session[self.name]
27
+ self.sid = int(self.name)
28
+ if not self.session:
29
+ self.session = await Session[self.sid]
32
30
 
33
31
  async def save(self):
34
32
  await self.date(int(time.time()))
@@ -36,14 +34,14 @@ class PgStorage(Storage):
36
34
  async def close(self): ...
37
35
 
38
36
  async def delete(self):
39
- await Session.filter(id=self.name).delete()
37
+ await Session.filter(id=self.sid).delete()
40
38
 
41
39
  async def update_peers(self, peers: list[tuple[int, int, str, str]]):
42
40
  for peer in peers:
43
41
  uid, ac_hsh, typ, phone = peer
44
42
  un, _ = await Username.update_or_create(phone and {"phone": phone}, id=uid)
45
43
  await Peer.update_or_create(
46
- {"username": un, "type": typ, "phone_number": phone}, session_id=self.name, id=ac_hsh
44
+ {"username": un, "type": PeerType[typ], "phone_number": phone}, session_id=self.sid, id=ac_hsh
47
45
  )
48
46
 
49
47
  async def update_usernames(self, usernames: list[tuple[int, list[str]]]):
@@ -51,37 +49,45 @@ class PgStorage(Storage):
51
49
  for username in user_list:
52
50
  await Username.update_or_create({"username": username}, id=telegram_id)
53
51
 
54
- async def get_peer_by_id(self, uin: int | str):
55
- peer = await (
56
- Peer.get(session_id=self.name, username_id=uin) if isinstance(uin, int) else self.get_peer_by_username(uin)
57
- )
52
+ async def get_peer_by_id(self, uid: int | str):
53
+ if isinstance(uid, str):
54
+ if uid.isnumeric():
55
+ uid = int(uid)
56
+ else:
57
+ return await self.get_peer_by_username(uid)
58
+ if not (peer := await Peer.get_or_none(session_id=self.sid, username_id=uid)):
59
+ raise KeyError(f"Peer#{uid} not found")
58
60
  if peer.last_update_on:
59
61
  if abs(time.time() - peer.last_update_on.timestamp()) > self.USERNAME_TTL:
60
- raise KeyError(f"Username expired: {uin}")
62
+ raise KeyError(f"Username expired: {uid}")
61
63
  return get_input_peer(peer.username_id, peer.id, peer.type)
62
64
 
63
65
  async def get_peer_by_username(self, username: str):
64
- return await Peer.get(session_id=self.name, username__username=username)
66
+ if not (peer := await Peer.get_or_none(session_id=self.sid, username__username=username)):
67
+ if not (user := await Username.get_or_none(username=username)):
68
+ raise KeyError(f"Username: {username} not found")
69
+ return await self.get_peer_by_id(user.id)
70
+ return get_input_peer(peer.username_id, peer.id, peer.type)
65
71
 
66
- async def update_state(self, value: tuple[int, int, int, int, int] = object):
72
+ async def update_state(self, value: tuple[int, int, int, int, int] = ...):
67
73
  if value is None:
68
- return await UpdateState.filter(session_id=self.name)
74
+ return await UpdateState.filter(session_id=self.sid)
69
75
  elif isinstance(value, int):
70
- await UpdateState.filter(session_id=self.name, id=value).delete()
76
+ await UpdateState.filter(session_id=self.sid, id=value).delete()
71
77
  else:
72
78
  sid, pts, qts, date, seq = value
73
79
  await UpdateState.get_or_create(
74
- {"pts": pts, "qts": qts, "date": date, "seq": seq}, session_id=self.name, id=sid
80
+ {"pts": pts, "qts": qts, "date": date, "seq": seq}, session_id=self.sid, id=sid
75
81
  )
76
82
 
77
83
  async def get_peer_by_phone_number(self, phone_number: str):
78
84
  attrs = "id", "access_hash", "type"
79
- if not (peer := await Peer.get_or_none(session_id=self.name, phone_number=phone_number).values_list(*attrs)):
80
- peer = await Peer.get(session_id=self.name, username__phone=phone_number).values_list(*attrs)
85
+ if not (peer := await Peer.get_or_none(session_id=self.sid, phone_number=phone_number).values_list(*attrs)):
86
+ peer = await Peer.get(session_id=self.sid, username__phone=phone_number).values_list(*attrs)
81
87
  return get_input_peer(*peer)
82
88
 
83
89
  async def _get(self, attr: str):
84
- return await Session.get(id=self.name).values_list(attr, flat=True)
90
+ return await Session.get(id=self.sid).values_list(attr, flat=True)
85
91
 
86
92
  async def _set(self, attr: str, value):
87
93
  # if "__" in attr:
@@ -90,40 +96,42 @@ class PgStorage(Storage):
90
96
  # rel.__setattr__(attr, value)
91
97
  # await rel.save()
92
98
  # else:
93
- await Session.update_or_create({attr: value}, id=self.name)
99
+ await Session.update_or_create({attr: value}, id=self.sid)
94
100
 
95
- async def _accessor(self, attr: str, value: Any = object):
96
- if value is object:
101
+ async def _accessor(self, attr: str, value: Any = ...):
102
+ if value is ...:
97
103
  return await self._get(attr)
104
+ # elif attr == ...:
105
+ # return await self._set(attr, ...)
98
106
  else:
99
107
  await self._set(attr, value)
100
108
 
101
- async def dc_id(self, value: int = object):
109
+ async def dc_id(self, value: int = ...):
102
110
  return await self._accessor("dc_id", value)
103
111
 
104
- async def api_id(self, value: int = object):
112
+ async def api_id(self, value: int = ...):
105
113
  return await self._accessor("api_id", value)
106
114
 
107
- async def test_mode(self, value: bool = object):
115
+ async def test_mode(self, value: bool = ...):
108
116
  return await self._accessor("test_mode", value)
109
117
 
110
- async def auth_key(self, value: bytes = object):
118
+ async def auth_key(self, value: bytes = ...):
111
119
  return await self._accessor("auth_key", value)
112
120
 
113
- async def date(self, value: int = object):
121
+ async def date(self, value: int = ...):
114
122
  return await self._accessor("date", value)
115
123
 
116
- async def user_id(self, value: int = object):
124
+ async def user_id(self, value: int = ...):
117
125
  return await self._accessor("user_id", value)
118
126
 
119
- async def is_bot(self, value: bool = object):
120
- if value is not object:
127
+ async def is_bot(self, value: bool = ...):
128
+ if value is not ...:
121
129
  value = self.session.is_bot if value else None # dirty
122
130
  return bool(await self._accessor("is_bot", value))
123
131
 
124
132
  @staticmethod
125
- async def version(value: int = object):
126
- if value is object:
133
+ async def version(value: int = ...):
134
+ if value is ...:
127
135
  ver = await Version.first()
128
136
  return ver.number
129
137
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrogram-client
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Author-email: Mike Artemiev <mixartemev@gmail.com>
5
5
  Project-URL: Homepage, https://gitlab.com/XyncNet/pyro-client
6
6
  Project-URL: Repository, https://gitlab.com/XyncNet/pyro-client
@@ -9,7 +9,6 @@ pyro_client/client/base.py
9
9
  pyro_client/client/bot.py
10
10
  pyro_client/client/dc.json
11
11
  pyro_client/client/file.py
12
- pyro_client/client/single.py
13
12
  pyro_client/client/user.py
14
13
  pyrogram_client.egg-info/PKG-INFO
15
14
  pyrogram_client.egg-info/SOURCES.txt
@@ -1,11 +0,0 @@
1
- from x_auth.models import Session
2
-
3
- from pyro_client.client.base import BaseClient, AuthTopic
4
-
5
-
6
- class BotClient(BaseClient):
7
- def __init__(self, sess: Session, _=None):
8
- super().__init__(sess.id, sess.api.id, sess.api.hsh, bot_token=f"{sess.id}:{sess.is_bot}")
9
-
10
- async def wait_auth_from(self, uid: int, topic: AuthTopic, past: int = 0, timeout: int = 60) -> str:
11
- return await super().wait_from(uid, topic, past, timeout)
@@ -1,40 +0,0 @@
1
- from tortoise.functions import Count
2
- from x_auth.models import Proxy, Session, Username, App
3
-
4
- from pyro_client.loader import WSToken
5
-
6
-
7
- class SingleMeta(type):
8
- _instances = {}
9
-
10
- async def __call__(cls, uid: int | str, bot=None):
11
- if cls not in cls._instances:
12
- prx = ...
13
- bt = None
14
- if isinstance(uid, str):
15
- if len(ub := uid.split(":")) == 2:
16
- uid, bt = ub
17
- prx = None
18
- if uid.isnumeric():
19
- uid = int(uid)
20
- sess = await cls._sess(uid, bt, prx)
21
- cls._instances[cls] = super().__call__(sess, bot)
22
- return cls._instances[cls]
23
-
24
- async def _sess(self, uid: int | str, bt: str = None, px: Proxy | None = ..., dc: int = 2) -> Session:
25
- username, _ = await Username.get_or_create(**{"id" if isinstance(uid, int) else "username": uid})
26
- if not (
27
- session := await Session.get_or_none(user=username, api__dc=dc) or await Session.get_or_none(id=username.id)
28
- ):
29
- if px is Ellipsis:
30
- await Proxy.load_list(WSToken)
31
- # await Proxy.get_replaced(WSToken)
32
- px = await Proxy.annotate(sc=Count("sessions")).filter(valid=True).order_by("sc", "-updated_at").first()
33
- if username.phone:
34
- # noinspection PyUnresolvedReferences
35
- dc = dc or self.get_dc()
36
- session = await Session.create(
37
- id=username.id, api=await App[20373304], user=username, dc_id=dc, proxy=px, is_bot=bt
38
- )
39
- await session.fetch_related("proxy", "api")
40
- return session