pyrogram-client 0.0.2.dev1__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.
- pyro_client/client/base.py +42 -0
- pyro_client/client/bot.py +9 -0
- pyro_client/client/file.py +58 -0
- pyro_client/client/user.py +106 -0
- pyro_client/loader.py +11 -0
- pyro_client/storage.py +144 -0
- pyrogram_client-0.0.2.dev1.dist-info/METADATA +16 -0
- pyrogram_client-0.0.2.dev1.dist-info/RECORD +10 -0
- pyrogram_client-0.0.2.dev1.dist-info/WHEEL +5 -0
- pyrogram_client-0.0.2.dev1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,42 @@
|
|
|
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
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from pyro_client.client.base import BaseClient, AuthTopic
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
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
|
+
|
|
8
|
+
async def wait_auth_from(self, uid: int, topic: AuthTopic, past: int = 0, timeout: int = 60) -> str:
|
|
9
|
+
return await super().wait_from(uid, topic, past, timeout)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
from io import BytesIO
|
|
2
|
+
|
|
3
|
+
from pyrogram.raw.functions.messages import UploadMedia
|
|
4
|
+
from pyrogram.raw.functions.upload import GetFile
|
|
5
|
+
from pyrogram.raw.types import (
|
|
6
|
+
MessageMediaDocument,
|
|
7
|
+
InputMediaUploadedDocument,
|
|
8
|
+
InputPeerSelf,
|
|
9
|
+
MessageMediaPhoto,
|
|
10
|
+
InputMediaUploadedPhoto,
|
|
11
|
+
InputDocumentFileLocation,
|
|
12
|
+
InputPhotoFileLocation,
|
|
13
|
+
)
|
|
14
|
+
from pyrogram.raw.types.upload import File
|
|
15
|
+
from pyrogram.types import Message
|
|
16
|
+
|
|
17
|
+
from pyro_client.client.bot import BotClient
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FileClient(BotClient):
|
|
21
|
+
@staticmethod
|
|
22
|
+
def ref_enc(ph_id: int, access_hash: int, ref: bytes) -> bytes:
|
|
23
|
+
return ph_id.to_bytes(8, "big") + access_hash.to_bytes(8, "big", signed=True) + ref
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def ref_dec(full_ref: bytes) -> tuple[int, int, bytes]:
|
|
27
|
+
pid, ah = int.from_bytes(full_ref[:8], "big"), int.from_bytes(full_ref[8:16], "big", signed=True)
|
|
28
|
+
return pid, ah, full_ref[16:]
|
|
29
|
+
|
|
30
|
+
async def save_doc(self, byts: bytes, ctype: str) -> tuple[MessageMediaDocument, bytes]:
|
|
31
|
+
in_file = await self.save_file(BytesIO(byts))
|
|
32
|
+
imud = InputMediaUploadedDocument(file=in_file, mime_type=ctype, attributes=[])
|
|
33
|
+
upf: MessageMediaDocument = await self.invoke(UploadMedia(peer=InputPeerSelf(), media=imud))
|
|
34
|
+
return upf, (
|
|
35
|
+
upf.document.id.to_bytes(8, "big")
|
|
36
|
+
+ upf.document.access_hash.to_bytes(8, "big", signed=True)
|
|
37
|
+
+ upf.document.file_reference
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
async def save_photo(self, file: bytes) -> tuple[MessageMediaPhoto, bytes]:
|
|
41
|
+
in_file = await self.save_file(BytesIO(file))
|
|
42
|
+
upm = UploadMedia(peer=InputPeerSelf(), media=InputMediaUploadedPhoto(file=in_file))
|
|
43
|
+
upp: MessageMediaPhoto = await self.invoke(upm)
|
|
44
|
+
return upp, self.ref_enc(upp.photo.id, upp.photo.access_hash, upp.photo.file_reference)
|
|
45
|
+
|
|
46
|
+
async def get_doc(self, fid: bytes) -> File:
|
|
47
|
+
pid, ah, ref = self.ref_dec(fid)
|
|
48
|
+
loc = InputDocumentFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size="x")
|
|
49
|
+
return await self.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
|
|
50
|
+
|
|
51
|
+
async def get_photo(self, fid: bytes, st: str) -> File:
|
|
52
|
+
pid, ah, ref = self.ref_dec(fid)
|
|
53
|
+
loc = InputPhotoFileLocation(id=pid, access_hash=ah, file_reference=ref, thumb_size=st)
|
|
54
|
+
return await self.invoke(GetFile(location=loc, offset=0, limit=512 * 1024))
|
|
55
|
+
|
|
56
|
+
async def bot_got_msg(self, _, msg: Message):
|
|
57
|
+
if state := self.storage.session.state.pop("bot", None):
|
|
58
|
+
self.storage.session.state[state] = msg.text
|
|
@@ -0,0 +1,106 @@
|
|
|
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())
|
pyro_client/loader.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from dotenv import load_dotenv
|
|
2
|
+
from os import getenv as env
|
|
3
|
+
|
|
4
|
+
load_dotenv()
|
|
5
|
+
|
|
6
|
+
API_ID = env("API_ID")
|
|
7
|
+
API_HASH = env("API_HASH")
|
|
8
|
+
PG_DSN = f"postgres://{env('POSTGRES_USER')}:{env('POSTGRES_PASSWORD')}@{env('POSTGRES_HOST', 'xyncdbs')}:" \
|
|
9
|
+
f"{env('POSTGRES_PORT', 5432)}/{env('POSTGRES_DB', env('POSTGRES_USER'))}"
|
|
10
|
+
TOKEN = env("TOKEN")
|
|
11
|
+
WSToken = env("WST")
|
pyro_client/storage.py
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from pyrogram import raw, utils
|
|
5
|
+
from pyrogram.storage import Storage
|
|
6
|
+
from tortoise.functions import Count
|
|
7
|
+
from x_auth.models import Proxy, Username, Version, Session, Peer, UpdateState
|
|
8
|
+
|
|
9
|
+
from pyro_client.loader import WSToken
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
|
|
13
|
+
if peer_type in ["user", "bot"]:
|
|
14
|
+
return raw.types.InputPeerUser(user_id=peer_id, access_hash=access_hash)
|
|
15
|
+
|
|
16
|
+
if peer_type == "group":
|
|
17
|
+
return raw.types.InputPeerChat(chat_id=-peer_id)
|
|
18
|
+
|
|
19
|
+
if peer_type in ["channel", "supergroup"]:
|
|
20
|
+
return raw.types.InputPeerChannel(channel_id=utils.get_channel_id(peer_id), access_hash=access_hash)
|
|
21
|
+
|
|
22
|
+
raise ValueError(f"Invalid peer type: {peer_type}")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class PgStorage(Storage):
|
|
26
|
+
VERSION = 1
|
|
27
|
+
USERNAME_TTL = 8 * 60 * 60
|
|
28
|
+
session: Session
|
|
29
|
+
me_id: int
|
|
30
|
+
|
|
31
|
+
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
|
|
51
|
+
|
|
52
|
+
async def save(self):
|
|
53
|
+
await self.date(int(time.time()))
|
|
54
|
+
|
|
55
|
+
async def close(self): ...
|
|
56
|
+
|
|
57
|
+
async def delete(self):
|
|
58
|
+
await Session.filter(id=self.name).delete()
|
|
59
|
+
|
|
60
|
+
async def update_peers(self, peers: list[tuple[int, int, str, str]]):
|
|
61
|
+
for peer in peers:
|
|
62
|
+
uid, ac_hsh, typ, phn = peer
|
|
63
|
+
un, _ = await Username.get_or_create(id=uid)
|
|
64
|
+
await Peer.update_or_create(
|
|
65
|
+
{"username": un, "type": typ, "phone_number": phn}, session_id=self.name, id=ac_hsh
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
async def update_usernames(self, usernames: list[tuple[int, list[str]]]):
|
|
69
|
+
for telegram_id, user_list in usernames:
|
|
70
|
+
for username in user_list:
|
|
71
|
+
await Username.update_or_create({"username": username}, id=telegram_id)
|
|
72
|
+
|
|
73
|
+
async def get_peer_by_id(self, peer_id_or_username: int | str):
|
|
74
|
+
attr = "id" if isinstance(peer_id_or_username, int) else "username"
|
|
75
|
+
if not (peer := await Peer.get_or_none(session_id=self.name, **{"username__" + attr: peer_id_or_username})):
|
|
76
|
+
raise KeyError(f"User not found: {peer_id_or_username}")
|
|
77
|
+
if peer.last_update_on:
|
|
78
|
+
if abs(time.time() - peer.last_update_on.timestamp()) > self.USERNAME_TTL:
|
|
79
|
+
raise KeyError(f"Username expired: {peer_id_or_username}")
|
|
80
|
+
return get_input_peer(peer.username_id, peer.id, peer.type)
|
|
81
|
+
|
|
82
|
+
async def get_peer_by_username(self, username: str):
|
|
83
|
+
return await self.get_peer_by_id(username)
|
|
84
|
+
|
|
85
|
+
async def update_state(self, value: tuple[int, int, int, int, int] = object):
|
|
86
|
+
if value is None:
|
|
87
|
+
return await UpdateState.filter(session_id=self.name)
|
|
88
|
+
elif isinstance(value, int):
|
|
89
|
+
await UpdateState.filter(session_id=self.name, id=value).delete()
|
|
90
|
+
else:
|
|
91
|
+
sid, pts, qts, date, seq = value
|
|
92
|
+
await UpdateState.get_or_create(
|
|
93
|
+
{"pts": pts, "qts": qts, "date": date, "seq": seq}, session_id=self.name, id=sid
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
async def get_peer_by_phone_number(self, phone_number: str):
|
|
97
|
+
if not (
|
|
98
|
+
peer := await Peer.filter(session_id=self.name, phone_number=phone_number).values_list(
|
|
99
|
+
"id", "access_hash", "type"
|
|
100
|
+
)
|
|
101
|
+
):
|
|
102
|
+
raise KeyError(f"Phone number not found: {phone_number}")
|
|
103
|
+
return get_input_peer(*peer)
|
|
104
|
+
|
|
105
|
+
async def _get(self, attr: str):
|
|
106
|
+
return await Session.get(id=self.name).values_list(attr, flat=True)
|
|
107
|
+
|
|
108
|
+
async def _set(self, attr: str, value):
|
|
109
|
+
await Session.update_or_create({attr: value}, id=self.name)
|
|
110
|
+
|
|
111
|
+
async def _accessor(self, attr: str, value: Any = object):
|
|
112
|
+
if value == object:
|
|
113
|
+
return await self._get(attr)
|
|
114
|
+
else:
|
|
115
|
+
await self._set(attr, value)
|
|
116
|
+
|
|
117
|
+
async def dc_id(self, value: int = object):
|
|
118
|
+
return await self._accessor("dc_id", value)
|
|
119
|
+
|
|
120
|
+
async def api_id(self, value: int = object):
|
|
121
|
+
return await self._accessor("api_id", value)
|
|
122
|
+
|
|
123
|
+
async def test_mode(self, value: bool = object):
|
|
124
|
+
return await self._accessor("test_mode", value)
|
|
125
|
+
|
|
126
|
+
async def auth_key(self, value: bytes = object):
|
|
127
|
+
return await self._accessor("auth_key", value)
|
|
128
|
+
|
|
129
|
+
async def date(self, value: int = object):
|
|
130
|
+
return await self._accessor("date", value)
|
|
131
|
+
|
|
132
|
+
async def user_id(self, value: int = object):
|
|
133
|
+
return await self._accessor("user_id", value)
|
|
134
|
+
|
|
135
|
+
async def is_bot(self, value: bool = object):
|
|
136
|
+
return await self._accessor("is_bot", value)
|
|
137
|
+
|
|
138
|
+
@staticmethod
|
|
139
|
+
async def version(value: int = object):
|
|
140
|
+
if value == object:
|
|
141
|
+
ver = await Version.first()
|
|
142
|
+
return ver.number
|
|
143
|
+
else:
|
|
144
|
+
await Version.update_or_create(id=value)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyrogram-client
|
|
3
|
+
Version: 0.0.2.dev1
|
|
4
|
+
Author-email: Mike Artemiev <mixartemev@gmail.com>
|
|
5
|
+
Project-URL: Homepage, https://gitlab.com/XyncNet/pyro-client
|
|
6
|
+
Project-URL: Repository, https://gitlab.com/XyncNet/pyro-client
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: kurigram
|
|
9
|
+
Requires-Dist: tgcrypto
|
|
10
|
+
Requires-Dist: xn-auth
|
|
11
|
+
Provides-Extra: dev
|
|
12
|
+
Requires-Dist: build; extra == "dev"
|
|
13
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
14
|
+
Requires-Dist: python-dotenv; extra == "dev"
|
|
15
|
+
Requires-Dist: setuptools-scm; extra == "dev"
|
|
16
|
+
Requires-Dist: twine; extra == "dev"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pyro_client/loader.py,sha256=CsBBsb8PfEtU4z7eDMlkc0V3x4PfwTvc2R1HbdVhAMg,362
|
|
2
|
+
pyro_client/storage.py,sha256=c538LC1uof0glq2LIWlyd5yqJxqWz1FUgqF9l6znBCQ,5419
|
|
3
|
+
pyro_client/client/base.py,sha256=m3NiiqnWTMvYvZO9p2HXFboBIUDN_NcERrek0aw-QiM,1550
|
|
4
|
+
pyro_client/client/bot.py,sha256=GECmzAQh0MU3REJB8dsq5A0sIeDzzmnRYYfftRG5xF8,406
|
|
5
|
+
pyro_client/client/file.py,sha256=JzP-BqqOdfP8LfVXI-qnO4hIS68mZaMnk8SVqrSngA0,2548
|
|
6
|
+
pyro_client/client/user.py,sha256=2GndFXUCESC6H2H8M3JgZZK3x2GkZx1I2UXHjjAiTwY,4289
|
|
7
|
+
pyrogram_client-0.0.2.dev1.dist-info/METADATA,sha256=m46X7Bm523iBohF3ogVn7JoqjfUAAybkdPC3gx65zSA,563
|
|
8
|
+
pyrogram_client-0.0.2.dev1.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
|
9
|
+
pyrogram_client-0.0.2.dev1.dist-info/top_level.txt,sha256=pR3onX0Aots7ODOSrd8dssJg3J2kz36Crt_48QSwyi0,12
|
|
10
|
+
pyrogram_client-0.0.2.dev1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pyro_client
|