pyrogram-client 0.0.2.dev1__tar.gz → 0.0.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.
Files changed (22) hide show
  1. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/.pre-commit-config.yaml +1 -1
  2. {pyrogram_client-0.0.2.dev1/pyrogram_client.egg-info → pyrogram_client-0.0.3}/PKG-INFO +1 -1
  3. pyrogram_client-0.0.3/pyro_client/client/base.py +82 -0
  4. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyro_client/client/bot.py +4 -2
  5. pyrogram_client-0.0.3/pyro_client/client/dc.json +180 -0
  6. pyrogram_client-0.0.3/pyro_client/client/single.py +40 -0
  7. pyrogram_client-0.0.3/pyro_client/client/user.py +199 -0
  8. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyro_client/storage.py +15 -29
  9. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3/pyrogram_client.egg-info}/PKG-INFO +1 -1
  10. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyrogram_client.egg-info/SOURCES.txt +2 -0
  11. pyrogram_client-0.0.2.dev1/pyro_client/client/base.py +0 -42
  12. pyrogram_client-0.0.2.dev1/pyro_client/client/user.py +0 -106
  13. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/.env.sample +0 -0
  14. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/.gitignore +0 -0
  15. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/makefile +0 -0
  16. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyproject.toml +0 -0
  17. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyro_client/client/file.py +0 -0
  18. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyro_client/loader.py +0 -0
  19. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyrogram_client.egg-info/dependency_links.txt +0 -0
  20. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyrogram_client.egg-info/requires.txt +0 -0
  21. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/pyrogram_client.egg-info/top_level.txt +0 -0
  22. {pyrogram_client-0.0.2.dev1 → pyrogram_client-0.0.3}/setup.cfg +0 -0
@@ -14,7 +14,7 @@ repos:
14
14
  - id: build
15
15
  name: build
16
16
  ### build & upload package only for "main" branch push
17
- entry: bash -c 'echo $PRE_COMMIT_LOCAL_BRANCH | grep /master && make twine || echo 0'
17
+ entry: bash -c 'echo $PRE_COMMIT_LOCAL_BRANCH | grep /main && make twine || echo 0'
18
18
  language: system
19
19
  pass_filenames: false
20
20
  verbose: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrogram-client
3
- Version: 0.0.2.dev1
3
+ Version: 0.0.3
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
@@ -0,0 +1,82 @@
1
+ import asyncio
2
+ from collections import OrderedDict
3
+ from io import BytesIO
4
+ from typing import Literal
5
+
6
+ from pyrogram import Client
7
+ from pyrogram.filters import chat, contact, AndFilter
8
+ from pyrogram.handlers import MessageHandler
9
+ from pyrogram.types import Message, InlineKeyboardMarkup, InlineKeyboardButton, ReplyKeyboardMarkup, KeyboardButton
10
+
11
+ from pyro_client.client.single import SingleMeta
12
+ from pyro_client.storage import PgStorage
13
+
14
+ AuthTopic = Literal["phone", "code", "pass"]
15
+
16
+
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) # Блокируемся, пока корутина не выполнится
22
+
23
+
24
+ class BaseClient(Client, metaclass=SingleMeta):
25
+ storage: PgStorage
26
+
27
+ def __init__(self, name: str, api_id, api_hash, *args, **kwargs):
28
+ super().__init__(name, api_id=api_id, api_hash=api_hash, *args, storage_engine=PgStorage(name), **kwargs)
29
+
30
+ async def send(
31
+ self,
32
+ txt: str,
33
+ uid: int | str = "me",
34
+ btns: list[InlineKeyboardButton | KeyboardButton] = None,
35
+ photo: bytes = None,
36
+ video: bytes = None,
37
+ ) -> Message:
38
+ ikm = (
39
+ (
40
+ InlineKeyboardMarkup([btns])
41
+ if isinstance(btns[0], InlineKeyboardButton)
42
+ else ReplyKeyboardMarkup([btns], one_time_keyboard=True)
43
+ )
44
+ if btns
45
+ else None
46
+ )
47
+ if photo:
48
+ return await self.send_photo(uid, BytesIO(photo), txt, reply_markup=ikm)
49
+ elif video:
50
+ return await self.send_video(uid, BytesIO(video), txt, reply_markup=ikm)
51
+ else:
52
+ return await self.send_message(uid, txt, reply_markup=ikm)
53
+
54
+ async def wait_from(self, uid: int, topic: str, past: int = 0, timeout: int = 10) -> str | None:
55
+ fltr = chat(uid)
56
+ if topic == "phone":
57
+ fltr &= contact
58
+ handler = MessageHandler(self.got_msg, fltr)
59
+ # handler, g = self.add_handler(handler, 1)
60
+ g = 0
61
+ if g not in self.dispatcher.groups:
62
+ self.dispatcher.groups[g] = []
63
+ self.dispatcher.groups = OrderedDict(sorted(self.dispatcher.groups.items()))
64
+ self.dispatcher.groups[g].append(handler)
65
+ #
66
+ while past < timeout:
67
+ if txt := self.storage.session.state.get(uid, {}).pop(topic, None):
68
+ # self.remove_handler(handler)
69
+ self.dispatcher.groups[g].remove(handler)
70
+ #
71
+ return txt
72
+ await asyncio.sleep(1)
73
+ past += 1
74
+ return self.remove_handler(handler, g)
75
+
76
+ async def got_msg(self, _, msg: Message):
77
+ if tpc := self.storage.session.state.get(msg.from_user.id, {}).pop("waiting_for", None):
78
+ self.storage.session.state[msg.from_user.id][tpc] = msg.contact.phone_number if tpc == "phone" else msg.text
79
+
80
+ def rm_handler(self, uid: int):
81
+ for gi, grp in self.dispatcher.groups.items():
82
+ [self.remove_handler(h, gi) for h in grp if isinstance(h.filters, AndFilter) and uid in h.filters.base]
@@ -1,9 +1,11 @@
1
+ from x_auth.models import Session
2
+
1
3
  from pyro_client.client.base import BaseClient, AuthTopic
2
4
 
3
5
 
4
6
  class BotClient(BaseClient):
5
- def __init__(self, api_id: str, api_hash: str, token: str):
6
- super().__init__(token.split(":")[0], api_id, api_hash, bot_token=token)
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}")
7
9
 
8
10
  async def wait_auth_from(self, uid: int, topic: AuthTopic, past: int = 0, timeout: int = 60) -> str:
9
11
  return await super().wait_from(uid, topic, past, timeout)
@@ -0,0 +1,180 @@
1
+ {
2
+ "7": 2,
3
+ "1": 1,
4
+ "20": 4,
5
+ "211": 2,
6
+ "212": 4,
7
+ "213": 4,
8
+ "216": 4,
9
+ "218": 4,
10
+ "220": 4,
11
+ "221": 4,
12
+ "222": 4,
13
+ "223": 4,
14
+ "224": 4,
15
+ "225": 4,
16
+ "226": 4,
17
+ "227": 4,
18
+ "228": 4,
19
+ "229": 4,
20
+ "230": 4,
21
+ "231": 4,
22
+ "232": 4,
23
+ "233": 4,
24
+ "234": 4,
25
+ "235": 4,
26
+ "236": 4,
27
+ "237": 4,
28
+ "238": 4,
29
+ "239": 4,
30
+ "240": 4,
31
+ "241": 4,
32
+ "242": 4,
33
+ "243": 4,
34
+ "244": 4,
35
+ "245": 4,
36
+ "246": 2,
37
+ "247": 4,
38
+ "248": 4,
39
+ "249": 4,
40
+ "250": 4,
41
+ "251": 4,
42
+ "252": 4,
43
+ "253": 4,
44
+ "254": 4,
45
+ "255": 4,
46
+ "256": 4,
47
+ "257": 4,
48
+ "258": 4,
49
+ "260": 4,
50
+ "261": 4,
51
+ "262": 4,
52
+ "263": 4,
53
+ "264": 4,
54
+ "265": 4,
55
+ "266": 4,
56
+ "267": 4,
57
+ "268": 4,
58
+ "269": 4,
59
+ "27": 4,
60
+ "290": 4,
61
+ "291": 4,
62
+ "297": 1,
63
+ "298": 4,
64
+ "299": 1,
65
+ "30": 4,
66
+ "31": 4,
67
+ "32": 4,
68
+ "33": 4,
69
+ "34": 4,
70
+ "36": 4,
71
+ "373": 2,
72
+ "374": 2,
73
+ "375": 2,
74
+ "380": 2,
75
+ "39": 4,
76
+ "40": 4,
77
+ "41": 4,
78
+ "43": 4,
79
+ "44": 4,
80
+ "45": 4,
81
+ "46": 4,
82
+ "47": 4,
83
+ "48": 4,
84
+ "49": 2,
85
+ "500": 1,
86
+ "501": 1,
87
+ "502": 1,
88
+ "503": 1,
89
+ "504": 1,
90
+ "505": 1,
91
+ "506": 1,
92
+ "507": 1,
93
+ "508": 1,
94
+ "509": 1,
95
+ "51": 1,
96
+ "52": 1,
97
+ "53": 1,
98
+ "54": 1,
99
+ "55": 1,
100
+ "56": 1,
101
+ "57": 1,
102
+ "58": 1,
103
+ "590": 1,
104
+ "591": 1,
105
+ "592": 1,
106
+ "593": 1,
107
+ "594": 1,
108
+ "595": 1,
109
+ "596": 1,
110
+ "597": 1,
111
+ "598": 1,
112
+ "599": 2,
113
+ "60": 5,
114
+ "61": 5,
115
+ "62": 5,
116
+ "63": 5,
117
+ "64": 5,
118
+ "65": 5,
119
+ "66": 5,
120
+ "672": 1,
121
+ "673": 5,
122
+ "674": 1,
123
+ "675": 5,
124
+ "676": 5,
125
+ "677": 5,
126
+ "678": 1,
127
+ "679": 1,
128
+ "680": 1,
129
+ "681": 1,
130
+ "682": 1,
131
+ "683": 1,
132
+ "684": 1,
133
+ "685": 1,
134
+ "686": 1,
135
+ "687": 1,
136
+ "688": 1,
137
+ "689": 1,
138
+ "690": 1,
139
+ "691": 1,
140
+ "692": 1,
141
+ "81": 5,
142
+ "82": 5,
143
+ "84": 5,
144
+ "86": 5,
145
+ "852": 5,
146
+ "853": 5,
147
+ "855": 5,
148
+ "856": 5,
149
+ "880": 5,
150
+ "886": 5,
151
+ "90": 4,
152
+ "91": 4,
153
+ "92": 4,
154
+ "93": 5,
155
+ "94": 5,
156
+ "95": 5,
157
+ "960": 5,
158
+ "961": 4,
159
+ "962": 4,
160
+ "963": 4,
161
+ "964": 2,
162
+ "965": 4,
163
+ "966": 4,
164
+ "967": 4,
165
+ "968": 4,
166
+ "970": 2,
167
+ "971": 4,
168
+ "972": 4,
169
+ "973": 4,
170
+ "974": 4,
171
+ "975": 5,
172
+ "976": 5,
173
+ "977": 5,
174
+ "992": 2,
175
+ "993": 2,
176
+ "995": 2,
177
+ "994": 2,
178
+ "996": 2,
179
+ "998": 2
180
+ }
@@ -0,0 +1,40 @@
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
@@ -0,0 +1,199 @@
1
+ import logging
2
+ from json import load
3
+ from os.path import dirname
4
+
5
+ from pydantic import BaseModel, Field
6
+ from pyrogram import enums
7
+ from pyrogram.enums import ClientPlatform
8
+ from pyrogram.errors import BadRequest, SessionPasswordNeeded, AuthKeyUnregistered, Unauthorized
9
+ from pyrogram.session import Auth, Session as PyroSession
10
+ from pyrogram.types import Message, User, SentCode, InlineKeyboardButton, KeyboardButton
11
+ from x_auth.models import Session, Username, Proxy
12
+
13
+ from pyro_client.client.base import BaseClient, AuthTopic
14
+ from pyro_client.client.bot import BotClient
15
+ from pyro_client.loader import WSToken
16
+
17
+ vers: dict[ClientPlatform, str] = {
18
+ ClientPlatform.IOS: "18.5",
19
+ ClientPlatform.ANDROID: "16",
20
+ }
21
+
22
+
23
+ class Prx(BaseModel):
24
+ scheme: str = "socks5"
25
+ hostname: str = Field(validation_alias="host")
26
+ port: int
27
+ username: str
28
+ password: str
29
+
30
+
31
+ class UserClient(BaseClient):
32
+ bot: BotClient
33
+
34
+ def __init__(self, sess: Session, bot):
35
+ 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
+ )
46
+
47
+ async def start(self, use_qr: bool = False, except_ids: list[int] = None):
48
+ if not self.bot.is_connected:
49
+ 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
59
+
60
+ async def ask_for(
61
+ self, topic: AuthTopic, question: str, btns: list[InlineKeyboardButton, KeyboardButton] = None
62
+ ) -> str:
63
+ if topic == "phone":
64
+ btns = btns or [] + [KeyboardButton("Phone", True)]
65
+ await self.receive(question, btns)
66
+ uid = int(self.storage.name)
67
+ self.bot.storage.session.state[uid] = {"waiting_for": topic}
68
+ return await self.bot.wait_auth_from(uid, topic)
69
+
70
+ async def receive(
71
+ self,
72
+ txt: str,
73
+ btns: list[InlineKeyboardButton | KeyboardButton] = None,
74
+ photo: bytes = None,
75
+ video: bytes = None,
76
+ ) -> Message:
77
+ return await self.bot.send(txt, int(self.storage.name), btns, photo, video)
78
+
79
+ def get_dc(self):
80
+ if not self.phone_number:
81
+ return 2
82
+ with open(f"{dirname(__file__)}/dc.json", "r") as file:
83
+ jsn = load(file)
84
+ for k, v in jsn.items():
85
+ if self.phone_number.startswith(k):
86
+ return v
87
+ return 2
88
+
89
+ async def authorize(self, sent_code: SentCode = None) -> User:
90
+ sent_code_desc = {
91
+ enums.SentCodeType.APP: "Telegram app",
92
+ enums.SentCodeType.SMS: "SMS",
93
+ enums.SentCodeType.CALL: "phone call",
94
+ enums.SentCodeType.FLASH_CALL: "phone flash call",
95
+ enums.SentCodeType.FRAGMENT_SMS: "Fragment SMS",
96
+ enums.SentCodeType.EMAIL_CODE: "email code",
97
+ }
98
+ # Step 1: Phone
99
+ if not self.phone_number:
100
+ user = await Username[self.storage.session.id]
101
+ if not (user.phone and (phone := str(user.phone))):
102
+ phone = await self.ask_for("phone", "Phone plz")
103
+ user.phone = phone
104
+ await user.save()
105
+ self.phone_number = phone
106
+ if (dc := self.get_dc()) != 2:
107
+ await self.session_update(dc)
108
+ if not self.phone_number:
109
+ await self.authorize()
110
+ try:
111
+ # user.phone = int(self.phone_number.strip("+ ").replace(" ", ""))
112
+ sent_code = await self.send_code(self.phone_number, True, False, True, False, True)
113
+ except BadRequest as e:
114
+ await self.send(e.MESSAGE)
115
+ self.phone_number = None
116
+ return await self.authorize(sent_code)
117
+ # Step 2: Code
118
+ if not self.phone_code:
119
+ if code := await self.ask_for("code", f"Code from {sent_code_desc[sent_code.type]}"):
120
+ self.phone_code = code.replace("_", "")
121
+ try:
122
+ signed_in = await self.sign_in(self.phone_number, sent_code.phone_code_hash, self.phone_code)
123
+ except BadRequest as e:
124
+ await self.receive(e.MESSAGE)
125
+ self.phone_code = None
126
+ return await self.authorize(sent_code)
127
+ except SessionPasswordNeeded:
128
+ # Step 2.1?: Cloud password
129
+ while True:
130
+ self.password = await self.ask_for(
131
+ "pass", f"Enter pass: (hint: {await self.get_password_hint()})"
132
+ )
133
+ try:
134
+ signed_in = await self.check_password(self.password)
135
+ break
136
+ except BadRequest as e:
137
+ await self.send(e.MESSAGE)
138
+ self.password = None
139
+ else:
140
+ raise Exception("User does not sent code")
141
+ if isinstance(signed_in, User):
142
+ await self.send("✅", self.bot.me.id)
143
+ await self.storage.save()
144
+ return signed_in
145
+
146
+ if not signed_in:
147
+ await self.receive("No registered such phone number")
148
+
149
+ async def stop(self, block: bool = True):
150
+ await super().stop(block)
151
+ await self.bot.stop(block)
152
+
153
+ async def session_update(self, dc: int):
154
+ await self.session.stop()
155
+
156
+ await self.storage.dc_id(dc)
157
+ await self.storage.auth_key(
158
+ await Auth(self, await self.storage.dc_id(), await self.storage.test_mode()).create()
159
+ )
160
+ self.session = PyroSession(
161
+ self, await self.storage.dc_id(), await self.storage.auth_key(), await self.storage.test_mode()
162
+ )
163
+ if not self.proxy and not self.storage.session.proxy:
164
+ await Proxy.load_list(WSToken)
165
+ prx: Proxy = await Proxy.filter(valid=True).order_by("-updated_at").first()
166
+ self.storage.session.proxy = prx
167
+ await self.storage.session.save()
168
+ self.proxy = prx.dict()
169
+ await self.session.start()
170
+
171
+
172
+ async def main():
173
+ from x_auth import models
174
+ from x_model import init_db
175
+
176
+ from pyro_client.loader import WSToken, PG_DSN
177
+
178
+ _ = await init_db(PG_DSN, models, True)
179
+
180
+ logging.basicConfig(level=logging.INFO)
181
+
182
+ await models.Proxy.load_list(WSToken)
183
+ # session = await models.Session.filter(is_bot__isnull=True).order_by("-date").prefetch_related("proxy").first()
184
+ bc: BotClient = await BotClient("xyncnetbot")
185
+ uc: UserClient = await UserClient(5547330178, bc)
186
+ # try:
187
+ await uc.start()
188
+ # except Exception as e:
189
+ # print(e.MESSAGE)
190
+ # await uc.send(e.MESSAGE)
191
+ # await uc.storage.session.delete()
192
+ # finally:
193
+ await uc.stop()
194
+
195
+
196
+ if __name__ == "__main__":
197
+ from asyncio import run
198
+
199
+ run(main())
@@ -1,15 +1,12 @@
1
1
  import time
2
- from typing import Any
2
+ from typing import Any, Literal
3
3
 
4
4
  from pyrogram import raw, utils
5
5
  from pyrogram.storage import Storage
6
- from tortoise.functions import Count
7
- from x_auth.models import Proxy, Username, Version, Session, Peer, UpdateState
6
+ from x_auth.models import Username, Version, Session, Peer, UpdateState
8
7
 
9
- from pyro_client.loader import WSToken
10
8
 
11
-
12
- def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
9
+ def get_input_peer(peer_id: int, access_hash: int, peer_type: Literal["user", "bot", "group", "channel", "supergroup"]):
13
10
  if peer_type in ["user", "bot"]:
14
11
  return raw.types.InputPeerUser(user_id=peer_id, access_hash=access_hash)
15
12
 
@@ -26,28 +23,11 @@ class PgStorage(Storage):
26
23
  VERSION = 1
27
24
  USERNAME_TTL = 8 * 60 * 60
28
25
  session: Session
29
- me_id: int
26
+ # me_id: int
30
27
 
31
28
  async def open(self):
32
- self.me_id = int((uid_dc := self.name.split("_")).pop(0))
33
- if not (session := await Session.get_or_none(id=self.name)):
34
- username, _ = await Username.get_or_create(id=self.me_id)
35
- await Proxy.load_list(WSToken)
36
- # await Proxy.get_replaced(WSToken)
37
- proxy = (
38
- await Proxy.annotate(sessions_count=Count("sessions"))
39
- .filter(valid=True)
40
- .order_by("sessions_count", "-updated_at")
41
- .first()
42
- )
43
- session = await Session.create(
44
- id=self.name,
45
- user=username,
46
- dc_id=int(uid_dc[0]) if uid_dc else None,
47
- date=int(time.time()),
48
- proxy=proxy,
49
- )
50
- self.session = session
29
+ # self.me_id = int((uid_dc := self.name.split("_")).pop(0))
30
+ self.session = await Session[self.name]
51
31
 
52
32
  async def save(self):
53
33
  await self.date(int(time.time()))
@@ -60,7 +40,7 @@ class PgStorage(Storage):
60
40
  async def update_peers(self, peers: list[tuple[int, int, str, str]]):
61
41
  for peer in peers:
62
42
  uid, ac_hsh, typ, phn = peer
63
- un, _ = await Username.get_or_create(id=uid)
43
+ un, _ = await Username.get_or_create(phn and {"phone": phn}, id=uid)
64
44
  await Peer.update_or_create(
65
45
  {"username": un, "type": typ, "phone_number": phn}, session_id=self.name, id=ac_hsh
66
46
  )
@@ -106,10 +86,16 @@ class PgStorage(Storage):
106
86
  return await Session.get(id=self.name).values_list(attr, flat=True)
107
87
 
108
88
  async def _set(self, attr: str, value):
89
+ # if "__" in attr:
90
+ # table, attr = attr.split("__")
91
+ # rel = await self.session.__getattribute__(table)
92
+ # rel.__setattr__(attr, value)
93
+ # await rel.save()
94
+ # else:
109
95
  await Session.update_or_create({attr: value}, id=self.name)
110
96
 
111
97
  async def _accessor(self, attr: str, value: Any = object):
112
- if value == object:
98
+ if value is object:
113
99
  return await self._get(attr)
114
100
  else:
115
101
  await self._set(attr, value)
@@ -137,7 +123,7 @@ class PgStorage(Storage):
137
123
 
138
124
  @staticmethod
139
125
  async def version(value: int = object):
140
- if value == object:
126
+ if value is object:
141
127
  ver = await Version.first()
142
128
  return ver.number
143
129
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyrogram-client
3
- Version: 0.0.2.dev1
3
+ Version: 0.0.3
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
@@ -7,7 +7,9 @@ pyro_client/loader.py
7
7
  pyro_client/storage.py
8
8
  pyro_client/client/base.py
9
9
  pyro_client/client/bot.py
10
+ pyro_client/client/dc.json
10
11
  pyro_client/client/file.py
12
+ pyro_client/client/single.py
11
13
  pyro_client/client/user.py
12
14
  pyrogram_client.egg-info/PKG-INFO
13
15
  pyrogram_client.egg-info/SOURCES.txt
@@ -1,42 +0,0 @@
1
- from asyncio import sleep
2
- from io import BytesIO
3
- from typing import Literal
4
-
5
- from pyrogram import Client
6
- from pyrogram.filters import chat
7
- from pyrogram.handlers import MessageHandler
8
- from pyrogram.types import Message
9
-
10
- from pyro_client.storage import PgStorage
11
-
12
- AuthTopic = Literal["phone", "code", "pass"]
13
-
14
-
15
- class BaseClient(Client):
16
- storage: PgStorage
17
-
18
- def __init__(self, name: str, *args, **kwargs):
19
- super().__init__(name, *args, storage_engine=PgStorage(name), **kwargs)
20
-
21
- async def send(self, txt: str, uid: int | str = "me", photo: bytes = None, video: bytes = None) -> Message:
22
- if photo:
23
- return await self.send_photo(uid, BytesIO(photo), txt)
24
- elif video:
25
- return await self.send_video(uid, BytesIO(video), txt)
26
- else:
27
- return await self.send_message(uid, txt)
28
-
29
- async def wait_from(self, uid: int, topic: str, past: int = 0, timeout: int = 10) -> str:
30
- handler = MessageHandler(self.got_msg, chat(uid))
31
- self.add_handler(handler)
32
- while past < timeout:
33
- if txt := self.storage.session.state.get(uid, {}).pop(topic, None):
34
- self.remove_handler(handler)
35
- return txt
36
- await sleep(1)
37
- past += 1
38
- return await self.wait_from(uid, topic, past, timeout)
39
-
40
- async def got_msg(self, _, msg: Message):
41
- if topic := self.storage.session.state.get(msg.from_user.id, {}).pop("waiting_for", None):
42
- self.storage.session.state[msg.from_user.id][topic] = msg.text
@@ -1,106 +0,0 @@
1
- from pyrogram import enums
2
- from pyrogram.errors import BadRequest, SessionPasswordNeeded, AuthKeyUnregistered
3
- from pyrogram.types import Message, User, SentCode
4
-
5
- from pyro_client.client.base import BaseClient, AuthTopic
6
- from pyro_client.client.bot import BotClient
7
-
8
-
9
- class UserClient(BaseClient):
10
- bot: BotClient
11
-
12
- def __init__(self, name: str, api_id: str, api_hash: str, bot_token: str = None, proxy: dict = None,
13
- device: str = "iPhone 17 Air", app: str = "XyncNet 1.0", ver: str = "iOS 19.0.1"):
14
- super().__init__(name, api_id, api_hash, device_model=device, app_version=app, system_version=ver)
15
- self.bot = bot_token and BotClient(api_id, api_hash, bot_token)
16
-
17
- async def start(self, use_qr: bool = False, except_ids: list[int] = None):
18
- await self.bot.start()
19
- await super().start(use_qr=use_qr, except_ids=except_ids or [])
20
-
21
- async def ask_for(self, topic: AuthTopic, question: str) -> str:
22
- await self.bot.send_message(self.storage.me_id, question)
23
- self.bot.storage.session.state[self.storage.me_id] = {"waiting_for": topic}
24
- return await self.bot.wait_auth_from(self.storage.me_id, topic)
25
-
26
- async def receive(self, txt: str, photo: bytes = None, video: bytes = None) -> Message:
27
- return await self.bot.send(txt, self.me.id, photo, video)
28
-
29
- async def authorize(self, sent_code: SentCode = None) -> User:
30
- sent_code_desc = {
31
- enums.SentCodeType.APP: "Telegram app",
32
- enums.SentCodeType.SMS: "SMS",
33
- enums.SentCodeType.CALL: "phone call",
34
- enums.SentCodeType.FLASH_CALL: "phone flash call",
35
- enums.SentCodeType.FRAGMENT_SMS: "Fragment SMS",
36
- enums.SentCodeType.EMAIL_CODE: "email code",
37
- }
38
- # Step 1: Phone
39
- if not self.phone_number:
40
- try:
41
- self.phone_number = await self.ask_for("phone", "Your phone:")
42
- if not self.phone_number:
43
- await self.authorize()
44
- sent_code = await self.send_code(self.phone_number)
45
- except BadRequest as e:
46
- await self.send(e.MESSAGE)
47
- self.phone_number = None
48
- return await self.authorize(sent_code)
49
- # Step 2: Code
50
- if not self.phone_code:
51
- _ = await self.ask_for("code", f"The confirm code sent via {sent_code_desc[sent_code.type]}")
52
- self.phone_code = _.replace("_", "")
53
- try:
54
- signed_in = await self.sign_in(self.phone_number, sent_code.phone_code_hash, self.phone_code)
55
- except BadRequest as e:
56
- await self.send(e.MESSAGE)
57
- self.phone_code = None
58
- return await self.authorize(sent_code)
59
- except SessionPasswordNeeded as e:
60
- # Step 2.1?: Cloud password
61
- await self.send(e.MESSAGE)
62
- while True:
63
- self.password = await self.ask_for("pass", f"Enter pass: (hint: {await self.get_password_hint()})")
64
- try:
65
- return await self.check_password(self.password)
66
- except BadRequest as e:
67
- await self.send(e.MESSAGE)
68
- self.password = None
69
-
70
- if isinstance(signed_in, User):
71
- await self.send("✅")
72
- return signed_in
73
-
74
- if not signed_in:
75
- await self.send("No registered such phone number")
76
-
77
- async def stop(self, block: bool = True):
78
- await super().stop(block)
79
- await self.bot.stop(block)
80
-
81
-
82
- async def main():
83
- from x_auth import models
84
- from x_model import init_db
85
-
86
- from pyro_client.loader import WSToken, PG_DSN, TOKEN, API_ID, API_HASH
87
-
88
- _ = await init_db(PG_DSN, models, True)
89
- await models.Proxy.load_list(WSToken)
90
- session = await models.Session.filter(is_bot=False).order_by("-date").first()
91
- uc = UserClient("", API_ID, API_HASH, TOKEN)
92
- # try:
93
- await uc.start()
94
- await uc.receive("hi")
95
- # except Exception as e:
96
- # print(e.MESSAGE)
97
- # await uc.send(e.MESSAGE)
98
- # await uc.storage.session.delete()
99
- # finally:
100
- await uc.stop()
101
-
102
-
103
- if __name__ == "__main__":
104
- from asyncio import run
105
-
106
- run(main())