mirror of
https://github.com/pyrogram/pyrogram
synced 2025-08-29 21:38:04 +00:00
Merge branch 'master' into new-api
# Conflicts: # pyrogram/crypto/aes.py
This commit is contained in:
commit
38e895ed82
@ -1,5 +1,5 @@
|
|||||||
## Include
|
## Include
|
||||||
include COPYING COPYING.lesser NOTICE requirements.txt
|
include COPYING COPYING.lesser NOTICE requirements.txt requirements_extras.txt
|
||||||
recursive-include compiler *.py *.tl *.tsv *.txt
|
recursive-include compiler *.py *.tl *.tsv *.txt
|
||||||
|
|
||||||
## Exclude
|
## Exclude
|
||||||
|
@ -56,12 +56,6 @@ from .style import Markdown, HTML
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class APIKey:
|
|
||||||
def __init__(self, api_id: int, api_hash: str):
|
|
||||||
self.api_id = api_id
|
|
||||||
self.api_hash = api_hash
|
|
||||||
|
|
||||||
|
|
||||||
class Proxy:
|
class Proxy:
|
||||||
def __init__(self, enabled: bool, hostname: str, port: int, username: str, password: str):
|
def __init__(self, enabled: bool, hostname: str, port: int, username: str, password: str):
|
||||||
self.enabled = enabled
|
self.enabled = enabled
|
||||||
@ -83,10 +77,13 @@ class Client:
|
|||||||
For Bots: pass your Bot API token, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
|
For Bots: pass your Bot API token, e.g.: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
|
||||||
Note: as long as a valid User session file exists, Pyrogram won't ask you again to input your phone number.
|
Note: as long as a valid User session file exists, Pyrogram won't ask you again to input your phone number.
|
||||||
|
|
||||||
api_key (``tuple``, optional):
|
api_id (``int``, optional):
|
||||||
Your Telegram API Key as tuple: *(api_id, api_hash)*.
|
The *api_id* part of your Telegram API Key, as integer. E.g.: 12345
|
||||||
E.g.: *(12345, "0123456789abcdef0123456789abcdef")*. This is an alternative way to pass it if you
|
This is an alternative way to pass it if you don't want to use the *config.ini* file.
|
||||||
don't want to use the *config.ini* file.
|
|
||||||
|
api_hash (``str``, optional):
|
||||||
|
The *api_hash* part of your Telegram API Key, as string. E.g.: "0123456789abcdef0123456789abcdef"
|
||||||
|
This is an alternative way to pass it if you don't want to use the *config.ini* file.
|
||||||
|
|
||||||
proxy (``dict``, optional):
|
proxy (``dict``, optional):
|
||||||
Your SOCKS5 Proxy settings as dict,
|
Your SOCKS5 Proxy settings as dict,
|
||||||
@ -112,13 +109,18 @@ class Client:
|
|||||||
Pass your Two-Step Verification password (if you have one) to avoid entering it
|
Pass your Two-Step Verification password (if you have one) to avoid entering it
|
||||||
manually. Only applicable for new sessions.
|
manually. Only applicable for new sessions.
|
||||||
|
|
||||||
|
force_sms (``str``, optional):
|
||||||
|
Pass True to force Telegram sending the authorization code via SMS.
|
||||||
|
Only applicable for new sessions.
|
||||||
|
|
||||||
first_name (``str``, optional):
|
first_name (``str``, optional):
|
||||||
Pass a First Name to avoid entering it manually. It will be used to automatically
|
Pass a First Name to avoid entering it manually. It will be used to automatically
|
||||||
create a new Telegram account in case the phone number you passed is not registered yet.
|
create a new Telegram account in case the phone number you passed is not registered yet.
|
||||||
|
Only applicable for new sessions.
|
||||||
|
|
||||||
last_name (``str``, optional):
|
last_name (``str``, optional):
|
||||||
Same purpose as *first_name*; pass a Last Name to avoid entering it manually. It can
|
Same purpose as *first_name*; pass a Last Name to avoid entering it manually. It can
|
||||||
be an empty string: ""
|
be an empty string: "". Only applicable for new sessions.
|
||||||
|
|
||||||
workers (``int``, optional):
|
workers (``int``, optional):
|
||||||
Thread pool size for handling incoming updates. Defaults to 4.
|
Thread pool size for handling incoming updates. Defaults to 4.
|
||||||
@ -132,17 +134,20 @@ class Client:
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
session_name: str,
|
session_name: str,
|
||||||
api_key: tuple or APIKey = None,
|
api_id: int = None,
|
||||||
|
api_hash: str = None,
|
||||||
proxy: dict or Proxy = None,
|
proxy: dict or Proxy = None,
|
||||||
test_mode: bool = False,
|
test_mode: bool = False,
|
||||||
phone_number: str = None,
|
phone_number: str = None,
|
||||||
phone_code: str or callable = None,
|
phone_code: str or callable = None,
|
||||||
password: str = None,
|
password: str = None,
|
||||||
|
force_sms: bool = False,
|
||||||
first_name: str = None,
|
first_name: str = None,
|
||||||
last_name: str = None,
|
last_name: str = None,
|
||||||
workers: int = 4):
|
workers: int = 4):
|
||||||
self.session_name = session_name
|
self.session_name = session_name
|
||||||
self.api_key = api_key
|
self.api_id = api_id
|
||||||
|
self.api_hash = api_hash
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
self.test_mode = test_mode
|
self.test_mode = test_mode
|
||||||
|
|
||||||
@ -151,6 +156,7 @@ class Client:
|
|||||||
self.phone_code = phone_code
|
self.phone_code = phone_code
|
||||||
self.first_name = first_name
|
self.first_name = first_name
|
||||||
self.last_name = last_name
|
self.last_name = last_name
|
||||||
|
self.force_sms = force_sms
|
||||||
|
|
||||||
self.workers = workers
|
self.workers = workers
|
||||||
|
|
||||||
@ -189,6 +195,9 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
:class:`Error <pyrogram.Error>`
|
:class:`Error <pyrogram.Error>`
|
||||||
"""
|
"""
|
||||||
|
if self.is_started:
|
||||||
|
raise ConnectionError("Client has already been started")
|
||||||
|
|
||||||
if self.BOT_TOKEN_RE.match(self.session_name):
|
if self.BOT_TOKEN_RE.match(self.session_name):
|
||||||
self.token = self.session_name
|
self.token = self.session_name
|
||||||
self.session_name = self.session_name.split(":")[0]
|
self.session_name = self.session_name.split(":")[0]
|
||||||
@ -201,7 +210,7 @@ class Client:
|
|||||||
self.test_mode,
|
self.test_mode,
|
||||||
self.proxy,
|
self.proxy,
|
||||||
self.auth_key,
|
self.auth_key,
|
||||||
self.api_key.api_id,
|
self.api_id,
|
||||||
client=self
|
client=self
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -237,6 +246,9 @@ class Client:
|
|||||||
"""Use this method to manually stop the Client.
|
"""Use this method to manually stop the Client.
|
||||||
Requires no parameters.
|
Requires no parameters.
|
||||||
"""
|
"""
|
||||||
|
if not self.is_started:
|
||||||
|
raise ConnectionError("Client is already stopped")
|
||||||
|
|
||||||
self.is_started = False
|
self.is_started = False
|
||||||
self.session.stop()
|
self.session.stop()
|
||||||
|
|
||||||
@ -254,8 +266,8 @@ class Client:
|
|||||||
r = self.send(
|
r = self.send(
|
||||||
functions.auth.ImportBotAuthorization(
|
functions.auth.ImportBotAuthorization(
|
||||||
flags=0,
|
flags=0,
|
||||||
api_id=self.api_key.api_id,
|
api_id=self.api_id,
|
||||||
api_hash=self.api_key.api_hash,
|
api_hash=self.api_hash,
|
||||||
bot_auth_token=self.token
|
bot_auth_token=self.token
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -270,7 +282,7 @@ class Client:
|
|||||||
self.test_mode,
|
self.test_mode,
|
||||||
self.proxy,
|
self.proxy,
|
||||||
self.auth_key,
|
self.auth_key,
|
||||||
self.api_key.api_id,
|
self.api_id,
|
||||||
client=self
|
client=self
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -303,8 +315,8 @@ class Client:
|
|||||||
r = self.send(
|
r = self.send(
|
||||||
functions.auth.SendCode(
|
functions.auth.SendCode(
|
||||||
self.phone_number,
|
self.phone_number,
|
||||||
self.api_key.api_id,
|
self.api_id,
|
||||||
self.api_key.api_hash
|
self.api_hash
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
except (PhoneMigrate, NetworkMigrate) as e:
|
except (PhoneMigrate, NetworkMigrate) as e:
|
||||||
@ -318,7 +330,7 @@ class Client:
|
|||||||
self.test_mode,
|
self.test_mode,
|
||||||
self.proxy,
|
self.proxy,
|
||||||
self.auth_key,
|
self.auth_key,
|
||||||
self.api_key.api_id,
|
self.api_id,
|
||||||
client=self
|
client=self
|
||||||
)
|
)
|
||||||
self.session.start()
|
self.session.start()
|
||||||
@ -326,8 +338,8 @@ class Client:
|
|||||||
r = self.send(
|
r = self.send(
|
||||||
functions.auth.SendCode(
|
functions.auth.SendCode(
|
||||||
self.phone_number,
|
self.phone_number,
|
||||||
self.api_key.api_id,
|
self.api_id,
|
||||||
self.api_key.api_hash
|
self.api_hash
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
@ -348,6 +360,14 @@ class Client:
|
|||||||
phone_registered = r.phone_registered
|
phone_registered = r.phone_registered
|
||||||
phone_code_hash = r.phone_code_hash
|
phone_code_hash = r.phone_code_hash
|
||||||
|
|
||||||
|
if self.force_sms:
|
||||||
|
self.send(
|
||||||
|
functions.auth.ResendCode(
|
||||||
|
phone_number=self.phone_number,
|
||||||
|
phone_code_hash=phone_code_hash
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
self.phone_code = (
|
self.phone_code = (
|
||||||
input("Enter phone code: ") if self.phone_code is None
|
input("Enter phone code: ") if self.phone_code is None
|
||||||
@ -636,7 +656,7 @@ class Client:
|
|||||||
if not isinstance(message, types.MessageEmpty):
|
if not isinstance(message, types.MessageEmpty):
|
||||||
diff = self.send(
|
diff = self.send(
|
||||||
functions.updates.GetChannelDifference(
|
functions.updates.GetChannelDifference(
|
||||||
channel=self.resolve_peer(update.message.to_id.channel_id),
|
channel=self.resolve_peer(int("-100" + str(update.message.to_id.channel_id))),
|
||||||
filter=types.ChannelMessagesFilter(
|
filter=types.ChannelMessagesFilter(
|
||||||
ranges=[types.MessageRange(
|
ranges=[types.MessageRange(
|
||||||
min_id=update.message.id,
|
min_id=update.message.id,
|
||||||
@ -813,32 +833,28 @@ class Client:
|
|||||||
Raises:
|
Raises:
|
||||||
:class:`Error <pyrogram.Error>`
|
:class:`Error <pyrogram.Error>`
|
||||||
"""
|
"""
|
||||||
if self.is_started:
|
if not self.is_started:
|
||||||
r = self.session.send(data)
|
raise ConnectionError("Client has not been started")
|
||||||
|
|
||||||
self.fetch_peers(getattr(r, "users", []))
|
r = self.session.send(data)
|
||||||
self.fetch_peers(getattr(r, "chats", []))
|
|
||||||
|
|
||||||
return r
|
self.fetch_peers(getattr(r, "users", []))
|
||||||
else:
|
self.fetch_peers(getattr(r, "chats", []))
|
||||||
raise ConnectionError("client '{}' is not started".format(self.session_name))
|
|
||||||
|
return r
|
||||||
|
|
||||||
def load_config(self):
|
def load_config(self):
|
||||||
parser = ConfigParser()
|
parser = ConfigParser()
|
||||||
parser.read("config.ini")
|
parser.read("config.ini")
|
||||||
|
|
||||||
if self.api_key is not None:
|
if self.api_id and self.api_hash:
|
||||||
self.api_key = APIKey(
|
pass
|
||||||
api_id=int(self.api_key[0]),
|
|
||||||
api_hash=self.api_key[1]
|
|
||||||
)
|
|
||||||
elif parser.has_section("pyrogram"):
|
|
||||||
self.api_key = APIKey(
|
|
||||||
api_id=parser.getint("pyrogram", "api_id"),
|
|
||||||
api_hash=parser.get("pyrogram", "api_hash")
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise AttributeError("No API Key found")
|
if parser.has_section("pyrogram"):
|
||||||
|
self.api_id = parser.getint("pyrogram", "api_id")
|
||||||
|
self.api_hash = parser.get("pyrogram", "api_hash")
|
||||||
|
else:
|
||||||
|
raise AttributeError("No API Key found")
|
||||||
|
|
||||||
if self.proxy is not None:
|
if self.proxy is not None:
|
||||||
self.proxy = Proxy(
|
self.proxy = Proxy(
|
||||||
@ -925,6 +941,15 @@ class Client:
|
|||||||
offset_date = parse_dialogs(dialogs)
|
offset_date = parse_dialogs(dialogs)
|
||||||
log.info("Entities count: {}".format(len(self.peers_by_id)))
|
log.info("Entities count: {}".format(len(self.peers_by_id)))
|
||||||
|
|
||||||
|
self.send(
|
||||||
|
functions.messages.GetDialogs(
|
||||||
|
0, 0, types.InputPeerEmpty(),
|
||||||
|
self.DIALOGS_AT_ONCE, True
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
log.info("Entities count: {}".format(len(self.peers_by_id)))
|
||||||
|
|
||||||
def resolve_peer(self, peer_id: int or str):
|
def resolve_peer(self, peer_id: int or str):
|
||||||
"""Use this method to get the *InputPeer* of a known *peer_id*.
|
"""Use this method to get the *InputPeer* of a known *peer_id*.
|
||||||
|
|
||||||
@ -957,7 +982,7 @@ class Client:
|
|||||||
except (AttributeError, binascii.Error, struct.error):
|
except (AttributeError, binascii.Error, struct.error):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
peer_id = peer_id.lower().strip("@+")
|
peer_id = re.sub(r"[@+\s]", "", peer_id.lower())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
int(peer_id)
|
int(peer_id)
|
||||||
@ -2187,7 +2212,7 @@ class Client:
|
|||||||
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
|
||||||
|
|
||||||
session = Session(self.dc_id, self.test_mode, self.proxy, self.auth_key, self.api_key.api_id)
|
session = Session(self.dc_id, self.test_mode, self.proxy, self.auth_key, self.api_id)
|
||||||
session.start()
|
session.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -2270,7 +2295,7 @@ class Client:
|
|||||||
self.test_mode,
|
self.test_mode,
|
||||||
self.proxy,
|
self.proxy,
|
||||||
Auth(dc_id, self.test_mode, self.proxy).create(),
|
Auth(dc_id, self.test_mode, self.proxy).create(),
|
||||||
self.api_key.api_id
|
self.api_id
|
||||||
)
|
)
|
||||||
|
|
||||||
session.start()
|
session.start()
|
||||||
@ -2287,7 +2312,7 @@ class Client:
|
|||||||
self.test_mode,
|
self.test_mode,
|
||||||
self.proxy,
|
self.proxy,
|
||||||
self.auth_key,
|
self.auth_key,
|
||||||
self.api_key.api_id
|
self.api_id
|
||||||
)
|
)
|
||||||
|
|
||||||
session.start()
|
session.start()
|
||||||
@ -2319,7 +2344,7 @@ class Client:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(r, types.upload.File):
|
if isinstance(r, types.upload.File):
|
||||||
with tempfile.NamedTemporaryFile('wb', delete=False) as f:
|
with tempfile.NamedTemporaryFile("wb", delete=False) as f:
|
||||||
file_name = f.name
|
file_name = f.name
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
@ -2351,14 +2376,14 @@ class Client:
|
|||||||
self.test_mode,
|
self.test_mode,
|
||||||
self.proxy,
|
self.proxy,
|
||||||
Auth(r.dc_id, self.test_mode, self.proxy).create(),
|
Auth(r.dc_id, self.test_mode, self.proxy).create(),
|
||||||
self.api_key.api_id,
|
self.api_id,
|
||||||
is_cdn=True
|
is_cdn=True
|
||||||
)
|
)
|
||||||
|
|
||||||
cdn_session.start()
|
cdn_session.start()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with tempfile.NamedTemporaryFile('wb', delete=False) as f:
|
with tempfile.NamedTemporaryFile("wb", delete=False) as f:
|
||||||
file_name = f.name
|
file_name = f.name
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
@ -1 +1,92 @@
|
|||||||
# Pyrogram - Telegram MTProto API Client Library for Python
# Copyright (C) 2017-2018 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/>.
try:
import tgcrypto
except ImportError as e:
e.msg = (
"TgCrypto is missing and Pyrogram can't run without. "
"Please install it using \"pip3 install tgcrypto\". "
"More info: https://docs.pyrogram.ml/resources/TgCrypto"
)
raise e
class AES:
@classmethod
def ige_encrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes:
return tgcrypto.ige_encrypt(data, key, iv)
@classmethod
def ige_decrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes:
return tgcrypto.ige_decrypt(data, key, iv)
@staticmethod
def ctr_decrypt(data: bytes, key: bytes, iv: bytes, offset: int) -> bytes:
replace = int.to_bytes(offset // 16, 4, "big")
iv = iv[:-4] + replace
return tgcrypto.ctr_decrypt(data, key, iv)
@staticmethod
def xor(a: bytes, b: bytes) -> bytes:
return int.to_bytes(
int.from_bytes(a, "big") ^ int.from_bytes(b, "big"),
len(a),
"big",
)
|
# Pyrogram - Telegram MTProto API Client Library for Python
|
||||||
|
# Copyright (C) 2017-2018 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 logging
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
try:
|
||||||
|
import tgcrypto
|
||||||
|
except ImportError:
|
||||||
|
log.warning(
|
||||||
|
"TgCrypto is missing! "
|
||||||
|
"Pyrogram will work the same, but at a much slower speed. "
|
||||||
|
"More info: https://docs.pyrogram.ml/resources/TgCrypto"
|
||||||
|
)
|
||||||
|
is_fast = False
|
||||||
|
import pyaes
|
||||||
|
else:
|
||||||
|
log.info("Using TgCrypto")
|
||||||
|
is_fast = True
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Ugly IFs
|
||||||
|
class AES:
|
||||||
|
@classmethod
|
||||||
|
def ige_encrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes:
|
||||||
|
if is_fast:
|
||||||
|
return tgcrypto.ige_encrypt(data, key, iv)
|
||||||
|
else:
|
||||||
|
return cls.ige(data, key, iv, True)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ige_decrypt(cls, data: bytes, key: bytes, iv: bytes) -> bytes:
|
||||||
|
if is_fast:
|
||||||
|
return tgcrypto.ige_decrypt(data, key, iv)
|
||||||
|
else:
|
||||||
|
return cls.ige(data, key, iv, False)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ctr_decrypt(data: bytes, key: bytes, iv: bytes, offset: int) -> bytes:
|
||||||
|
replace = int.to_bytes(offset // 16, 4, "big")
|
||||||
|
iv = iv[:-4] + replace
|
||||||
|
|
||||||
|
if is_fast:
|
||||||
|
return tgcrypto.ctr_decrypt(data, key, iv)
|
||||||
|
else:
|
||||||
|
ctr = pyaes.AESModeOfOperationCTR(key)
|
||||||
|
ctr._counter._counter = list(iv)
|
||||||
|
return ctr.decrypt(data)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def xor(a: bytes, b: bytes) -> bytes:
|
||||||
|
return int.to_bytes(
|
||||||
|
int.from_bytes(a, "big") ^ int.from_bytes(b, "big"),
|
||||||
|
len(a),
|
||||||
|
"big",
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ige(cls, data: bytes, key: bytes, iv: bytes, encrypt: bool) -> bytes:
|
||||||
|
cipher = pyaes.AES(key)
|
||||||
|
|
||||||
|
iv_1 = iv[:16]
|
||||||
|
iv_2 = iv[16:]
|
||||||
|
|
||||||
|
data = [data[i: i + 16] for i in range(0, len(data), 16)]
|
||||||
|
|
||||||
|
if encrypt:
|
||||||
|
for i, chunk in enumerate(data):
|
||||||
|
iv_1 = data[i] = cls.xor(cipher.encrypt(cls.xor(chunk, iv_1)), iv_2)
|
||||||
|
iv_2 = chunk
|
||||||
|
else:
|
||||||
|
for i, chunk in enumerate(data):
|
||||||
|
iv_2 = data[i] = cls.xor(cipher.decrypt(cls.xor(chunk, iv_2)), iv_1)
|
||||||
|
iv_1 = chunk
|
||||||
|
|
||||||
|
return b"".join(data)
|
||||||
|
@ -87,7 +87,7 @@ class Session:
|
|||||||
test_mode: bool,
|
test_mode: bool,
|
||||||
proxy: type,
|
proxy: type,
|
||||||
auth_key: bytes,
|
auth_key: bytes,
|
||||||
api_id: str,
|
api_id: int,
|
||||||
is_cdn: bool = False,
|
is_cdn: bool = False,
|
||||||
client: pyrogram = None):
|
client: pyrogram = None):
|
||||||
if not Session.notice_displayed:
|
if not Session.notice_displayed:
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
pysocks
|
pyaes>=1.6.1
|
||||||
tgcrypto
|
pysocks>=1.6.8
|
1
requirements_extras.txt
Normal file
1
requirements_extras.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
tgcrypto>=1.0.4
|
7
setup.py
7
setup.py
@ -25,8 +25,8 @@ from compiler.api import compiler as api_compiler
|
|||||||
from compiler.error import compiler as error_compiler
|
from compiler.error import compiler as error_compiler
|
||||||
|
|
||||||
|
|
||||||
def requirements():
|
def read(file: str) -> list:
|
||||||
with open("requirements.txt", encoding="utf-8") as r:
|
with open(file, encoding="utf-8") as r:
|
||||||
return [i.strip() for i in r]
|
return [i.strip() for i in r]
|
||||||
|
|
||||||
|
|
||||||
@ -82,5 +82,6 @@ setup(
|
|||||||
python_requires="~=3.4",
|
python_requires="~=3.4",
|
||||||
packages=find_packages(exclude=["compiler*"]),
|
packages=find_packages(exclude=["compiler*"]),
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
install_requires=requirements()
|
install_requires=read("requirements.txt"),
|
||||||
|
extras_require={"tgcrypto": read("requirements_extras.txt")}
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user