2
0
mirror of https://github.com/pyrogram/pyrogram synced 2025-08-29 13:27:47 +00:00

Merge develop -> asyncio

This commit is contained in:
Dan 2019-09-14 20:42:06 +02:00
commit 826885a821
14 changed files with 323 additions and 449 deletions

View File

@ -40,7 +40,6 @@ Welcome to Pyrogram
topics/more-on-updates topics/more-on-updates
topics/config-file topics/config-file
topics/smart-plugins topics/smart-plugins
topics/auto-auth
topics/session-settings topics/session-settings
topics/tgcrypto topics/tgcrypto
topics/storage-engines topics/storage-engines

View File

@ -1,68 +0,0 @@
Auto Authorization
==================
Manually writing phone number, phone code and password on the terminal every time you want to login can be tedious.
Pyrogram is able to automate both **Log In** and **Sign Up** processes, all you need to do is pass the relevant
parameters when creating a new :class:`~pyrogram.Client`.
.. note:: If you omit any of the optional parameter required for the authorization, Pyrogram will ask you to
manually write it. For instance, if you don't want to set a ``last_name`` when creating a new account you
have to explicitly pass an empty string ""; the default value (None) will trigger the input() call.
Log In
-------
To automate the **Log In** process, pass your ``phone_number`` and ``password`` (if you have one) in the Client parameters.
If you want to retrieve the phone code programmatically, pass a callback function in the ``phone_code`` field — this
function accepts a single positional argument (phone_number) and must return the correct phone code (e.g., "12345")
— otherwise, ignore this parameter, Pyrogram will ask you to input the phone code manually.
Example:
.. code-block:: python
from pyrogram import Client
def phone_code_callback(phone_number):
code = ... # Get your code programmatically
return code # e.g., "12345"
app = Client(
session_name="example",
phone_number="39**********",
phone_code=phone_code_callback, # Note the missing parentheses
password="password" # (if you have one)
)
with app:
print(app.get_me())
Sign Up
-------
To automate the **Sign Up** process (i.e., automatically create a new Telegram account), simply fill **both**
``first_name`` and ``last_name`` fields alongside the other parameters; they will be used to automatically create a new
Telegram account in case the phone number you passed is not registered yet.
Example:
.. code-block:: python
from pyrogram import Client
def phone_code_callback(phone_number):
code = ... # Get your code programmatically
return code # e.g., "12345"
app = Client(
session_name="example",
phone_number="39**********",
phone_code=phone_code_callback, # Note the missing parentheses
first_name="Pyrogram",
last_name="" # Can be an empty string
)
with app:
print(app.get_me())

View File

@ -266,13 +266,13 @@ class Client(Methods, BaseClient):
self.load_config() self.load_config()
await self.load_session() await self.load_session()
self.session = Session(self, self.storage.dc_id, self.storage.auth_key) self.session = Session(self, self.storage.dc_id(), self.storage.auth_key())
await self.session.start() await self.session.start()
self.is_connected = True self.is_connected = True
return bool(self.storage.user_id) return bool(self.storage.user_id())
async def disconnect(self): async def disconnect(self):
"""Disconnect the client from Telegram servers. """Disconnect the client from Telegram servers.
@ -402,9 +402,9 @@ class Client(Methods, BaseClient):
except (PhoneMigrate, NetworkMigrate) as e: except (PhoneMigrate, NetworkMigrate) as e:
await self.session.stop() await self.session.stop()
self.storage.dc_id = e.x self.storage.dc_id(e.x)
self.storage.auth_key = await Auth(self, self.storage.dc_id).create() self.storage.auth_key(await Auth(self, self.storage.dc_id()).create())
self.session = Session(self, self.storage.dc_id, self.storage.auth_key) self.session = Session(self, self.storage.dc_id(), self.storage.auth_key())
await self.session.start() await self.session.start()
else: else:
@ -480,8 +480,8 @@ class Client(Methods, BaseClient):
return False return False
else: else:
self.storage.user_id = r.user.id self.storage.user_id(r.user.id)
self.storage.is_bot = False self.storage.is_bot(False)
return User._parse(self, r.user) return User._parse(self, r.user)
@ -518,8 +518,8 @@ class Client(Methods, BaseClient):
) )
) )
self.storage.user_id = r.user.id self.storage.user_id(r.user.id)
self.storage.is_bot = False self.storage.is_bot(False)
return User._parse(self, r.user) return User._parse(self, r.user)
@ -549,14 +549,14 @@ class Client(Methods, BaseClient):
except UserMigrate as e: except UserMigrate as e:
await self.session.stop() await self.session.stop()
self.storage.dc_id = e.x self.storage.dc_id(e.x)
self.storage.auth_key = await Auth(self, self.storage.dc_id).create() self.storage.auth_key(await Auth(self, self.storage.dc_id()).create())
self.session = Session(self, self.storage.dc_id, self.storage.auth_key) self.session = Session(self, self.storage.dc_id(), self.storage.auth_key())
await self.session.start() await self.session.start()
else: else:
self.storage.user_id = r.user.id self.storage.user_id(r.user.id)
self.storage.is_bot = True self.storage.is_bot(True)
return User._parse(self, r.user) return User._parse(self, r.user)
@ -590,8 +590,8 @@ class Client(Methods, BaseClient):
) )
) )
self.storage.user_id = r.user.id self.storage.user_id(r.user.id)
self.storage.is_bot = False self.storage.is_bot(False)
return User._parse(self, r.user) return User._parse(self, r.user)
@ -627,8 +627,8 @@ class Client(Methods, BaseClient):
) )
) )
self.storage.user_id = r.user.id self.storage.user_id(r.user.id)
self.storage.is_bot = False self.storage.is_bot(False)
return User._parse(self, r.user) return User._parse(self, r.user)
@ -784,7 +784,7 @@ class Client(Methods, BaseClient):
async def log_out(self): async def log_out(self):
"""Log out from Telegram and delete the *\\*.session* file. """Log out from Telegram and delete the *\\*.session* file.
When you log out, the current client is stopped and the storage session destroyed. When you log out, the current client is stopped and the storage session deleted.
No more API calls can be made until you start the client and re-authorize again. No more API calls can be made until you start the client and re-authorize again.
Returns: Returns:
@ -798,7 +798,7 @@ class Client(Methods, BaseClient):
""" """
await self.send(functions.auth.LogOut()) await self.send(functions.auth.LogOut())
await self.stop() await self.stop()
self.storage.destroy() self.storage.delete()
return True return True
@ -833,7 +833,7 @@ class Client(Methods, BaseClient):
if not is_authorized: if not is_authorized:
await self.authorize() await self.authorize()
if not self.storage.is_bot and self.takeout: if not self.storage.is_bot() and self.takeout:
self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id self.takeout_id = (await self.send(functions.account.InitTakeoutSession())).id
log.warning("Takeout session {} initiated".format(self.takeout_id)) log.warning("Takeout session {} initiated".format(self.takeout_id))
@ -1176,41 +1176,24 @@ class Client(Methods, BaseClient):
self.parse_mode = parse_mode self.parse_mode = parse_mode
def fetch_peers( def fetch_peers(self, peers: List[Union[types.User, types.Chat, types.Channel]]) -> bool:
self,
peers: List[
Union[
types.User,
types.Chat, types.ChatForbidden,
types.Channel, types.ChannelForbidden
]
]
) -> bool:
is_min = False is_min = False
parsed_peers = [] parsed_peers = []
for peer in peers: for peer in peers:
if getattr(peer, "min", False):
is_min = True
continue
username = None username = None
phone_number = None phone_number = None
if isinstance(peer, types.User): if isinstance(peer, types.User):
peer_id = peer.id peer_id = peer.id
access_hash = peer.access_hash access_hash = peer.access_hash
username = (peer.username or "").lower() or None
username = peer.username
phone_number = peer.phone phone_number = peer.phone
peer_type = "bot" if peer.bot else "user"
if peer.bot:
peer_type = "bot"
else:
peer_type = "user"
if access_hash is None:
is_min = True
continue
if username is not None:
username = username.lower()
elif isinstance(peer, (types.Chat, types.ChatForbidden)): elif isinstance(peer, (types.Chat, types.ChatForbidden)):
peer_id = -peer.id peer_id = -peer.id
access_hash = 0 access_hash = 0
@ -1218,20 +1201,8 @@ class Client(Methods, BaseClient):
elif isinstance(peer, (types.Channel, types.ChannelForbidden)): elif isinstance(peer, (types.Channel, types.ChannelForbidden)):
peer_id = utils.get_channel_id(peer.id) peer_id = utils.get_channel_id(peer.id)
access_hash = peer.access_hash access_hash = peer.access_hash
username = (getattr(peer, "username", None) or "").lower() or None
username = getattr(peer, "username", None) peer_type = "channel" if peer.broadcast else "supergroup"
if peer.broadcast:
peer_type = "channel"
else:
peer_type = "supergroup"
if access_hash is None:
is_min = True
continue
if username is not None:
username = username.lower()
else: else:
continue continue
@ -1494,20 +1465,20 @@ class Client(Methods, BaseClient):
self.storage.open() self.storage.open()
session_empty = any([ session_empty = any([
self.storage.test_mode is None, self.storage.test_mode() is None,
self.storage.auth_key is None, self.storage.auth_key() is None,
self.storage.user_id is None, self.storage.user_id() is None,
self.storage.is_bot is None self.storage.is_bot() is None
]) ])
if session_empty: if session_empty:
self.storage.dc_id = 2 self.storage.dc_id(2)
self.storage.date = 0 self.storage.date(0)
self.storage.test_mode = self.test_mode self.storage.test_mode(self.test_mode)
self.storage.auth_key = await Auth(self, self.storage.dc_id).create() self.storage.auth_key(await Auth(self, self.storage.dc_id()).create())
self.storage.user_id = None self.storage.user_id(None)
self.storage.is_bot = None self.storage.is_bot(None)
def load_plugins(self): def load_plugins(self):
if self.plugins: if self.plugins:
@ -1715,7 +1686,7 @@ class Client(Methods, BaseClient):
except KeyError: except KeyError:
raise PeerIdInvalid raise PeerIdInvalid
peer_type = utils.get_type(peer_id) peer_type = utils.get_peer_type(peer_id)
if peer_type == "user": if peer_type == "user":
self.fetch_peers( self.fetch_peers(
@ -1836,7 +1807,7 @@ class Client(Methods, BaseClient):
is_missing_part = file_id is not None is_missing_part = file_id is not None
file_id = file_id or self.rnd_id() file_id = file_id or self.rnd_id()
md5_sum = md5() if not is_big and not is_missing_part else None md5_sum = md5() if not is_big and not is_missing_part else None
pool = [Session(self, self.storage.dc_id, self.storage.auth_key, is_media=True) for _ in range(pool_size)] pool = [Session(self, self.storage.dc_id(), self.storage.auth_key(), is_media=True) for _ in range(pool_size)]
workers = [asyncio.ensure_future(worker(session)) for session in pool for _ in range(workers_count)] workers = [asyncio.ensure_future(worker(session)) for session in pool for _ in range(workers_count)]
queue = asyncio.Queue(16) queue = asyncio.Queue(16)
@ -1926,7 +1897,7 @@ class Client(Methods, BaseClient):
session = self.media_sessions.get(dc_id, None) session = self.media_sessions.get(dc_id, None)
if session is None: if session is None:
if dc_id != self.storage.dc_id: if dc_id != self.storage.dc_id():
session = Session(self, dc_id, await Auth(self, dc_id).create(), is_media=True) session = Session(self, dc_id, await Auth(self, dc_id).create(), is_media=True)
await session.start() await session.start()
@ -1952,7 +1923,7 @@ class Client(Methods, BaseClient):
await session.stop() await session.stop()
raise AuthBytesInvalid raise AuthBytesInvalid
else: else:
session = Session(self, dc_id, self.storage.auth_key, is_media=True) session = Session(self, dc_id, self.storage.auth_key(), is_media=True)
await session.start() await session.start()
self.media_sessions[dc_id] = session self.media_sessions[dc_id] = session

View File

@ -227,7 +227,7 @@ def get_peer_id(peer: Union[PeerUser, PeerChat, PeerChannel]) -> int:
raise ValueError("Peer type invalid: {}".format(peer)) raise ValueError("Peer type invalid: {}".format(peer))
def get_type(peer_id: int) -> str: def get_peer_type(peer_id: int) -> str:
if peer_id < 0: if peer_id < 0:
if MIN_CHAT_ID <= peer_id: if MIN_CHAT_ID <= peer_id:
return "chat" return "chat"

View File

@ -22,34 +22,29 @@ import logging
import os import os
import sqlite3 import sqlite3
from pathlib import Path from pathlib import Path
from threading import Lock
from .memory_storage import MemoryStorage from .sqlite_storage import SQLiteStorage
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class FileStorage(MemoryStorage): class FileStorage(SQLiteStorage):
FILE_EXTENSION = ".session" FILE_EXTENSION = ".session"
def __init__(self, name: str, workdir: Path): def __init__(self, name: str, workdir: Path):
super().__init__(name) super().__init__(name)
self.workdir = workdir
self.database = workdir / (self.name + self.FILE_EXTENSION) self.database = workdir / (self.name + self.FILE_EXTENSION)
self.conn = None # type: sqlite3.Connection
self.lock = Lock()
# noinspection PyAttributeOutsideInit
def migrate_from_json(self, session_json: dict): def migrate_from_json(self, session_json: dict):
self.open() self.open()
self.dc_id = session_json["dc_id"] self.dc_id(session_json["dc_id"])
self.test_mode = session_json["test_mode"] self.test_mode(session_json["test_mode"])
self.auth_key = base64.b64decode("".join(session_json["auth_key"])) self.auth_key(base64.b64decode("".join(session_json["auth_key"])))
self.user_id = session_json["user_id"] self.user_id(session_json["user_id"])
self.date = session_json.get("date", 0) self.date(session_json.get("date", 0))
self.is_bot = session_json.get("is_bot", False) self.is_bot(session_json.get("is_bot", False))
peers_by_id = session_json.get("peers_by_id", {}) peers_by_id = session_json.get("peers_by_id", {})
peers_by_phone = session_json.get("peers_by_phone", {}) peers_by_phone = session_json.get("peers_by_phone", {})
@ -72,6 +67,17 @@ class FileStorage(MemoryStorage):
# noinspection PyTypeChecker # noinspection PyTypeChecker
self.update_peers(peers.values()) self.update_peers(peers.values())
def update(self):
version = self.version()
if version == 1:
with self.lock, self.conn:
self.conn.execute("DELETE FROM peers")
version += 1
self.version(version)
def open(self): def open(self):
path = self.database path = self.database
file_exists = path.is_file() file_exists = path.is_file()
@ -98,14 +104,12 @@ class FileStorage(MemoryStorage):
if Path(path.name + ".OLD").is_file(): if Path(path.name + ".OLD").is_file():
log.warning('Old session file detected: "{}.OLD". You can remove this file now'.format(path.name)) log.warning('Old session file detected: "{}.OLD". You can remove this file now'.format(path.name))
self.conn = sqlite3.connect( self.conn = sqlite3.connect(str(path), timeout=1, check_same_thread=False)
str(path),
timeout=1,
check_same_thread=False
)
if not file_exists: if not file_exists:
self.create() self.create()
else:
self.update()
with self.conn: with self.conn:
try: # Python 3.6.0 (exactly this version) is bugged and won't successfully execute the vacuum try: # Python 3.6.0 (exactly this version) is bugged and won't successfully execute the vacuum
@ -113,5 +117,5 @@ class FileStorage(MemoryStorage):
except sqlite3.OperationalError: except sqlite3.OperationalError:
pass pass
def destroy(self): def delete(self):
os.remove(self.database) os.remove(self.database)

View File

@ -17,226 +17,37 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import base64 import base64
import inspect
import logging import logging
import sqlite3 import sqlite3
import struct import struct
import time
from pathlib import Path
from threading import Lock
from typing import List, Tuple
from pyrogram.api import types from .sqlite_storage import SQLiteStorage
from pyrogram.client.storage.storage import Storage
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class MemoryStorage(Storage): class MemoryStorage(SQLiteStorage):
SCHEMA_VERSION = 1
USERNAME_TTL = 8 * 60 * 60
SESSION_STRING_FMT = ">B?256sI?"
SESSION_STRING_SIZE = 351
def __init__(self, name: str): def __init__(self, name: str):
super().__init__(name) super().__init__(name)
self.conn = None # type: sqlite3.Connection
self.lock = Lock()
def create(self):
with self.lock, self.conn:
with open(str(Path(__file__).parent / "schema.sql"), "r") as schema:
self.conn.executescript(schema.read())
self.conn.execute(
"INSERT INTO version VALUES (?)",
(self.SCHEMA_VERSION,)
)
self.conn.execute(
"INSERT INTO sessions VALUES (?, ?, ?, ?, ?, ?)",
(1, None, None, 0, None, None)
)
def _import_session_string(self, session_string: str):
decoded = base64.urlsafe_b64decode(session_string + "=" * (-len(session_string) % 4))
return struct.unpack(self.SESSION_STRING_FMT, decoded)
def export_session_string(self):
packed = struct.pack(
self.SESSION_STRING_FMT,
self.dc_id,
self.test_mode,
self.auth_key,
self.user_id,
self.is_bot
)
return base64.urlsafe_b64encode(packed).decode().rstrip("=")
# noinspection PyAttributeOutsideInit
def open(self): def open(self):
self.conn = sqlite3.connect(":memory:", check_same_thread=False) self.conn = sqlite3.connect(":memory:", check_same_thread=False)
self.create() self.create()
if self.name != ":memory:": if self.name != ":memory:":
imported_session_string = self._import_session_string(self.name) dc_id, test_mode, auth_key, user_id, is_bot = struct.unpack(
self.SESSION_STRING_FORMAT,
base64.urlsafe_b64decode(
self.name + "=" * (-len(self.name) % 4)
)
)
self.dc_id, self.test_mode, self.auth_key, self.user_id, self.is_bot = imported_session_string self.dc_id(dc_id)
self.date = 0 self.test_mode(test_mode)
self.auth_key(auth_key)
self.user_id(user_id)
self.is_bot(is_bot)
self.date(0)
# noinspection PyAttributeOutsideInit def delete(self):
def save(self):
self.date = int(time.time())
with self.lock:
self.conn.commit()
def close(self):
with self.lock:
self.conn.close()
def destroy(self):
pass pass
def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
with self.lock:
self.conn.executemany(
"REPLACE INTO peers (id, access_hash, type, username, phone_number)"
"VALUES (?, ?, ?, ?, ?)",
peers
)
def clear_peers(self):
with self.lock, self.conn:
self.conn.execute(
"DELETE FROM peers"
)
@staticmethod
def _get_input_peer(peer_id: int, access_hash: int, peer_type: str):
if peer_type in ["user", "bot"]:
return types.InputPeerUser(
user_id=peer_id,
access_hash=access_hash
)
if peer_type == "group":
return types.InputPeerChat(
chat_id=-peer_id
)
if peer_type in ["channel", "supergroup"]:
return types.InputPeerChannel(
channel_id=int(str(peer_id)[4:]),
access_hash=access_hash
)
raise ValueError("Invalid peer type: {}".format(peer_type))
def get_peer_by_id(self, peer_id: int):
r = self.conn.execute(
"SELECT id, access_hash, type FROM peers WHERE id = ?",
(peer_id,)
).fetchone()
if r is None:
raise KeyError("ID not found: {}".format(peer_id))
return self._get_input_peer(*r)
def get_peer_by_username(self, username: str):
r = self.conn.execute(
"SELECT id, access_hash, type, last_update_on FROM peers WHERE username = ?",
(username,)
).fetchone()
if r is None:
raise KeyError("Username not found: {}".format(username))
if abs(time.time() - r[3]) > self.USERNAME_TTL:
raise KeyError("Username expired: {}".format(username))
return self._get_input_peer(*r[:3])
def get_peer_by_phone_number(self, phone_number: str):
r = self.conn.execute(
"SELECT id, access_hash, type FROM peers WHERE phone_number = ?",
(phone_number,)
).fetchone()
if r is None:
raise KeyError("Phone number not found: {}".format(phone_number))
return self._get_input_peer(*r)
@property
def peers_count(self):
return self.conn.execute(
"SELECT COUNT(*) FROM peers"
).fetchone()[0]
def _get(self):
attr = inspect.stack()[1].function
return self.conn.execute(
"SELECT {} FROM sessions".format(attr)
).fetchone()[0]
def _set(self, value):
attr = inspect.stack()[1].function
with self.lock, self.conn:
self.conn.execute(
"UPDATE sessions SET {} = ?".format(attr),
(value,)
)
@property
def dc_id(self):
return self._get()
@dc_id.setter
def dc_id(self, value):
self._set(value)
@property
def test_mode(self):
return self._get()
@test_mode.setter
def test_mode(self, value):
self._set(value)
@property
def auth_key(self):
return self._get()
@auth_key.setter
def auth_key(self, value):
self._set(value)
@property
def date(self):
return self._get()
@date.setter
def date(self, value):
self._set(value)
@property
def user_id(self):
return self._get()
@user_id.setter
def user_id(self, value):
self._set(value)
@property
def is_bot(self):
return self._get()
@is_bot.setter
def is_bot(self, value):
self._set(value)

View File

@ -0,0 +1,184 @@
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2019 Dan Tès <https://github.com/delivrance>
#
# This file is part of Pyrogram.
#
# Pyrogram is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Pyrogram is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import inspect
import sqlite3
import time
from pathlib import Path
from threading import Lock
from typing import List, Tuple, Any
from pyrogram.api import types
from pyrogram.client.ext import utils
from .storage import Storage
def get_input_peer(peer_id: int, access_hash: int, peer_type: str):
if peer_type in ["user", "bot"]:
return types.InputPeerUser(
user_id=peer_id,
access_hash=access_hash
)
if peer_type == "group":
return types.InputPeerChat(
chat_id=-peer_id
)
if peer_type in ["channel", "supergroup"]:
return types.InputPeerChannel(
channel_id=utils.get_channel_id(peer_id),
access_hash=access_hash
)
raise ValueError("Invalid peer type: {}".format(peer_type))
class SQLiteStorage(Storage):
VERSION = 2
USERNAME_TTL = 8 * 60 * 60
def __init__(self, name: str):
super().__init__(name)
self.conn = None # type: sqlite3.Connection
self.lock = Lock()
def create(self):
with self.lock, self.conn:
with open(str(Path(__file__).parent / "schema.sql"), "r") as schema:
self.conn.executescript(schema.read())
self.conn.execute(
"INSERT INTO version VALUES (?)",
(self.VERSION,)
)
self.conn.execute(
"INSERT INTO sessions VALUES (?, ?, ?, ?, ?, ?)",
(2, None, None, 0, None, None)
)
def open(self):
raise NotImplementedError
def save(self):
self.date(int(time.time()))
with self.lock:
self.conn.commit()
def close(self):
with self.lock:
self.conn.close()
def delete(self):
raise NotImplementedError
def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
with self.lock:
self.conn.executemany(
"REPLACE INTO peers (id, access_hash, type, username, phone_number)"
"VALUES (?, ?, ?, ?, ?)",
peers
)
def get_peer_by_id(self, peer_id: int):
r = self.conn.execute(
"SELECT id, access_hash, type FROM peers WHERE id = ?",
(peer_id,)
).fetchone()
if r is None:
raise KeyError("ID not found: {}".format(peer_id))
return get_input_peer(*r)
def get_peer_by_username(self, username: str):
r = self.conn.execute(
"SELECT id, access_hash, type, last_update_on FROM peers WHERE username = ?",
(username,)
).fetchone()
if r is None:
raise KeyError("Username not found: {}".format(username))
if abs(time.time() - r[3]) > self.USERNAME_TTL:
raise KeyError("Username expired: {}".format(username))
return get_input_peer(*r[:3])
def get_peer_by_phone_number(self, phone_number: str):
r = self.conn.execute(
"SELECT id, access_hash, type FROM peers WHERE phone_number = ?",
(phone_number,)
).fetchone()
if r is None:
raise KeyError("Phone number not found: {}".format(phone_number))
return get_input_peer(*r)
def _get(self):
attr = inspect.stack()[2].function
return self.conn.execute(
"SELECT {} FROM sessions".format(attr)
).fetchone()[0]
def _set(self, value: Any):
attr = inspect.stack()[2].function
with self.lock, self.conn:
self.conn.execute(
"UPDATE sessions SET {} = ?".format(attr),
(value,)
)
def _accessor(self, value: Any = object):
return self._get() if value == object else self._set(value)
def dc_id(self, value: int = object):
return self._accessor(value)
def test_mode(self, value: bool = object):
return self._accessor(value)
def auth_key(self, value: bytes = object):
return self._accessor(value)
def date(self, value: int = object):
return self._accessor(value)
def user_id(self, value: int = object):
return self._accessor(value)
def is_bot(self, value: bool = object):
return self._accessor(value)
def version(self, value: int = object):
if value == object:
return self.conn.execute(
"SELECT number FROM version"
).fetchone()[0]
else:
with self.lock, self.conn:
self.conn.execute(
"UPDATE version SET number = ?",
(value,)
)

View File

@ -16,8 +16,15 @@
# You should have received a copy of the GNU Lesser General Public License # You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
import base64
import struct
from typing import List, Tuple
class Storage: class Storage:
SESSION_STRING_FORMAT = ">B?256sI?"
SESSION_STRING_SIZE = 351
def __init__(self, name: str): def __init__(self, name: str):
self.name = name self.name = name
@ -30,72 +37,47 @@ class Storage:
def close(self): def close(self):
raise NotImplementedError raise NotImplementedError
def destroy(self): def delete(self):
raise NotImplementedError raise NotImplementedError
def update_peers(self, peers): def update_peers(self, peers: List[Tuple[int, int, str, str, str]]):
raise NotImplementedError raise NotImplementedError
def get_peer_by_id(self, peer_id): def get_peer_by_id(self, peer_id: int):
raise NotImplementedError raise NotImplementedError
def get_peer_by_username(self, username): def get_peer_by_username(self, username: str):
raise NotImplementedError raise NotImplementedError
def get_peer_by_phone_number(self, phone_number): def get_peer_by_phone_number(self, phone_number: str):
raise NotImplementedError
def dc_id(self, value: int = object):
raise NotImplementedError
def test_mode(self, value: bool = object):
raise NotImplementedError
def auth_key(self, value: bytes = object):
raise NotImplementedError
def date(self, value: int = object):
raise NotImplementedError
def user_id(self, value: int = object):
raise NotImplementedError
def is_bot(self, value: bool = object):
raise NotImplementedError raise NotImplementedError
def export_session_string(self): def export_session_string(self):
raise NotImplementedError return base64.urlsafe_b64encode(
struct.pack(
@property self.SESSION_STRING_FORMAT,
def peers_count(self): self.dc_id(),
raise NotImplementedError self.test_mode(),
self.auth_key(),
@property self.user_id(),
def dc_id(self): self.is_bot()
raise NotImplementedError )
).decode().rstrip("=")
@dc_id.setter
def dc_id(self, value):
raise NotImplementedError
@property
def test_mode(self):
raise NotImplementedError
@test_mode.setter
def test_mode(self, value):
raise NotImplementedError
@property
def auth_key(self):
raise NotImplementedError
@auth_key.setter
def auth_key(self, value):
raise NotImplementedError
@property
def date(self):
raise NotImplementedError
@date.setter
def date(self, value):
raise NotImplementedError
@property
def user_id(self):
raise NotImplementedError
@user_id.setter
def user_id(self, value):
raise NotImplementedError
@property
def is_bot(self):
raise NotImplementedError
@is_bot.setter
def is_bot(self, value):
raise NotImplementedError

View File

@ -162,7 +162,7 @@ class Chat(Object):
username=user.username, username=user.username,
first_name=user.first_name, first_name=user.first_name,
last_name=user.last_name, last_name=user.last_name,
photo=ChatPhoto._parse(client, user.photo, peer_id), photo=ChatPhoto._parse(client, user.photo, peer_id, user.access_hash),
restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None, restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None,
client=client client=client
) )
@ -175,7 +175,7 @@ class Chat(Object):
id=peer_id, id=peer_id,
type="group", type="group",
title=chat.title, title=chat.title,
photo=ChatPhoto._parse(client, getattr(chat, "photo", None), peer_id), photo=ChatPhoto._parse(client, getattr(chat, "photo", None), peer_id, 0),
permissions=ChatPermissions._parse(getattr(chat, "default_banned_rights", None)), permissions=ChatPermissions._parse(getattr(chat, "default_banned_rights", None)),
members_count=getattr(chat, "participants_count", None), members_count=getattr(chat, "participants_count", None),
client=client client=client
@ -194,7 +194,7 @@ class Chat(Object):
is_scam=getattr(channel, "scam", None), is_scam=getattr(channel, "scam", None),
title=channel.title, title=channel.title,
username=getattr(channel, "username", None), username=getattr(channel, "username", None),
photo=ChatPhoto._parse(client, getattr(channel, "photo", None), peer_id), photo=ChatPhoto._parse(client, getattr(channel, "photo", None), peer_id, channel.access_hash),
restrictions=pyrogram.List([Restriction._parse(r) for r in restriction_reason]) or None, restrictions=pyrogram.List([Restriction._parse(r) for r in restriction_reason]) or None,
permissions=ChatPermissions._parse(getattr(channel, "default_banned_rights", None)), permissions=ChatPermissions._parse(getattr(channel, "default_banned_rights", None)),
members_count=getattr(channel, "participants_count", None), members_count=getattr(channel, "participants_count", None),

View File

@ -20,6 +20,7 @@ from struct import pack
import pyrogram import pyrogram
from pyrogram.api import types from pyrogram.api import types
from pyrogram.client.ext import utils
from ..object import Object from ..object import Object
from ...ext.utils import encode from ...ext.utils import encode
@ -50,7 +51,7 @@ class ChatPhoto(Object):
self.big_file_id = big_file_id self.big_file_id = big_file_id
@staticmethod @staticmethod
def _parse(client, chat_photo: types.UserProfilePhoto or types.ChatPhoto, peer_id: int): def _parse(client, chat_photo: types.UserProfilePhoto or types.ChatPhoto, peer_id: int, peer_access_hash: int):
if not isinstance(chat_photo, (types.UserProfilePhoto, types.ChatPhoto)): if not isinstance(chat_photo, (types.UserProfilePhoto, types.ChatPhoto)):
return None return None
@ -58,24 +59,14 @@ class ChatPhoto(Object):
loc_small = chat_photo.photo_small loc_small = chat_photo.photo_small
loc_big = chat_photo.photo_big loc_big = chat_photo.photo_big
try: peer_type = utils.get_peer_type(peer_id)
# We just want a local storage lookup by id, whose method is not async.
# Otherwise we have to turn this _parse method async and also all the other methods that use this one.
peer = client.storage.get_peer_by_id(peer_id)
except KeyError:
return None
if isinstance(peer, types.InputPeerUser): if peer_type == "user":
peer_id = peer.user_id
peer_access_hash = peer.access_hash
x = 0 x = 0
elif isinstance(peer, types.InputPeerChat): elif peer_type == "chat":
peer_id = -peer.chat_id
peer_access_hash = 0
x = -1 x = -1
else: else:
peer_id += 1000727379968 peer_id += 1000727379968
peer_access_hash = peer.access_hash
x = -234 x = -234
return ChatPhoto( return ChatPhoto(

View File

@ -187,7 +187,7 @@ class User(Object, Update):
language_code=user.lang_code, language_code=user.lang_code,
dc_id=getattr(user.photo, "dc_id", None), dc_id=getattr(user.photo, "dc_id", None),
phone_number=user.phone, phone_number=user.phone,
photo=ChatPhoto._parse(client, user.photo, user.id), photo=ChatPhoto._parse(client, user.photo, user.id, user.access_hash),
restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None, restrictions=pyrogram.List([Restriction._parse(r) for r in user.restriction_reason]) or None,
client=client client=client
) )

View File

@ -38,7 +38,7 @@ class Auth:
def __init__(self, client: "pyrogram.Client", dc_id: int): def __init__(self, client: "pyrogram.Client", dc_id: int):
self.dc_id = dc_id self.dc_id = dc_id
self.test_mode = client.storage.test_mode self.test_mode = client.storage.test_mode()
self.ipv6 = client.ipv6 self.ipv6 = client.ipv6
self.proxy = client.proxy self.proxy = client.proxy

View File

@ -114,7 +114,7 @@ class Session:
while True: while True:
self.connection = Connection( self.connection = Connection(
self.dc_id, self.dc_id,
self.client.storage.test_mode, self.client.storage.test_mode(),
self.client.ipv6, self.client.ipv6,
self.client.proxy self.client.proxy
) )