Merge branch 'master' into docs

This commit is contained in:
Dan 2018-03-11 17:19:45 +01:00
commit 5af4a0a49a
8 changed files with 350 additions and 31 deletions

View File

@ -29,6 +29,7 @@ destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
---functions--- ---functions---
req_pq#60469778 nonce:int128 = ResPQ; req_pq#60469778 nonce:int128 = ResPQ;
req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params; req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:bytes q:bytes public_key_fingerprint:long encrypted_data:bytes = Server_DH_Params;

View File

@ -8,7 +8,9 @@ you have to change are the target chats (username, id) and file paths for sendin
- [**hello_world.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py) - [**hello_world.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/hello_world.py)
- [**get_history.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_history.py) - [**get_history.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_history.py)
- [**get_participants.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_participants.py) - [**get_participants.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/get_participants.py)
- [**inline_bots.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/inline_bots.py)
- [**updates.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/updates.py) - [**updates.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/updates.py)
- [**simple_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/simple_echo.py) - [**simple_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/simple_echo.py)
- [**advanced_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo.py) - [**advanced_echo.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo.py)
- [**advanced_echo2.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo2.py) - [**advanced_echo2.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/advanced_echo2.py)
- [**welcome_bot.py**](https://github.com/pyrogram/pyrogram/blob/master/examples/welcome_bot.py)

52
examples/welcome_bot.py Normal file
View File

@ -0,0 +1,52 @@
from pyrogram import Client, Emoji
from pyrogram.api import types
"""
This is the Welcome Bot in @PyrogramChat
The code is commented to help you understand each part
It also uses the Emoji module to easily add emojis in your text messages
"""
# Your Supergroup ID
SUPERGROUP_ID = 1387666944
def update_handler(client, update, users, chats):
# Supergroup messages are contained in the "UpdateNewChannelMessage" update type
if isinstance(update, types.UpdateNewChannelMessage):
message = update.message
# When a user joins, a "MessageService" is received
if isinstance(message, types.MessageService):
# Check if the message is sent to your SUPERGROUP_ID
if message.to_id.channel_id == SUPERGROUP_ID:
# A "MessageService" contains the "action" field.
# The action for user joins is "MessageActionChatAddUser" if the user
# joined using the username, otherwise is "MessageActionChatJoinedByLink" if
# the user joined a private group by link
if isinstance(message.action, (types.MessageActionChatAddUser, types.MessageActionChatJoinedByLink)):
# Now send the welcome message. Extra info about a user (such as the first_name, username, ...)
# are contained in the users dictionary and can be accessed by the user ID
client.send_message(
SUPERGROUP_ID,
"{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s "
"group chat, [{}](tg://user?id={})!".format(
Emoji.SPARKLES, # Add an emoji
users[message.from_id].first_name,
users[message.from_id].id
),
reply_to_message_id=message.id,
disable_web_page_preview=True
)
def main():
client = Client("example")
client.set_update_handler(update_handler)
client.start()
client.idle()
if __name__ == "__main__":
main()

View File

@ -23,7 +23,7 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
"e" if sys.getfilesystemencoding() == "ascii" else "\xe8" "e" if sys.getfilesystemencoding() == "ascii" else "\xe8"
) )
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)" __license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
__version__ = "0.6.1" __version__ = "0.6.2"
from .api.errors import Error from .api.errors import Error
from .client import ChatAction from .client import ChatAction

View File

@ -256,15 +256,16 @@ class Client:
if isinstance(entity, Chat): if isinstance(entity, Chat):
chat_id = entity.id chat_id = entity.id
peer_id = -chat_id
if chat_id in self.peers_by_id: if peer_id in self.peers_by_id:
continue continue
input_peer = InputPeerChat( input_peer = InputPeerChat(
chat_id=chat_id chat_id=chat_id
) )
self.peers_by_id[chat_id] = input_peer self.peers_by_id[peer_id] = input_peer
if isinstance(entity, Channel): if isinstance(entity, Channel):
channel_id = entity.id channel_id = entity.id
@ -571,6 +572,8 @@ class Client:
elif confirm in ("n", "2"): elif confirm in ("n", "2"):
self.phone_number = input("Enter phone number: ") self.phone_number = input("Enter phone number: ")
self.phone_number = self.phone_number.strip("+")
try: try:
r = self.send( r = self.send(
functions.auth.SendCode( functions.auth.SendCode(
@ -886,16 +889,19 @@ class Client:
if isinstance(peer_id, types.PeerUser): if isinstance(peer_id, types.PeerUser):
peer_id = peer_id.user_id peer_id = peer_id.user_id
elif isinstance(peer_id, types.PeerChat): elif isinstance(peer_id, types.PeerChat):
peer_id = peer_id.chat_id peer_id = -peer_id.chat_id
elif isinstance(peer_id, types.PeerChannel): elif isinstance(peer_id, types.PeerChannel):
peer_id = int("-100" + str(peer_id.channel_id)) peer_id = int("-100" + str(peer_id.channel_id))
try: try: # User
return self.peers_by_id[peer_id] return self.peers_by_id[peer_id]
except KeyError: except KeyError:
try: try: # Chat
return self.peers_by_id[int("-100" + str(peer_id))] return self.peers_by_id[-peer_id]
except KeyError: except KeyError:
try: # Channel
return self.peers_by_id[int("-100" + str(peer_id))]
except (KeyError, ValueError):
raise PeerIdInvalid raise PeerIdInvalid
def get_me(self): def get_me(self):
@ -1014,7 +1020,8 @@ class Client:
parse_mode: str = "", parse_mode: str = "",
ttl_seconds: int = None, ttl_seconds: int = None,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send photos. """Use this method to send photos.
Args: Args:
@ -1047,6 +1054,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
@ -1054,7 +1072,7 @@ class Client:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
style = self.html if parse_mode.lower() == "html" else self.markdown style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(photo) file = self.save_file(photo, progress=progress)
while True: while True:
try: try:
@ -1085,7 +1103,8 @@ class Client:
performer: str = None, performer: str = None,
title: str = None, title: str = None,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send audio files. """Use this method to send audio files.
For sending voice messages, use the :obj:`send_voice` method instead. For sending voice messages, use the :obj:`send_voice` method instead.
@ -1124,6 +1143,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
@ -1131,7 +1161,7 @@ class Client:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
style = self.html if parse_mode.lower() == "html" else self.markdown style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(audio) file = self.save_file(audio, progress=progress)
while True: while True:
try: try:
@ -1167,7 +1197,8 @@ class Client:
caption: str = "", caption: str = "",
parse_mode: str = "", parse_mode: str = "",
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send general files. """Use this method to send general files.
Args: Args:
@ -1195,6 +1226,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
@ -1202,7 +1244,7 @@ class Client:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
style = self.html if parse_mode.lower() == "html" else self.markdown style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(document) file = self.save_file(document, progress=progress)
while True: while True:
try: try:
@ -1231,7 +1273,8 @@ class Client:
chat_id: int or str, chat_id: int or str,
sticker: str, sticker: str,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send .webp stickers. """Use this method to send .webp stickers.
Args: Args:
@ -1251,13 +1294,24 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
Raises: Raises:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
file = self.save_file(sticker) file = self.save_file(sticker, progress=progress)
while True: while True:
try: try:
@ -1290,9 +1344,11 @@ class Client:
duration: int = 0, duration: int = 0,
width: int = 0, width: int = 0,
height: int = 0, height: int = 0,
thumb: str = None,
supports_streaming: bool = None, supports_streaming: bool = None,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send video files. """Use this method to send video files.
Args: Args:
@ -1322,6 +1378,11 @@ class Client:
height (:obj:`int`, optional): height (:obj:`int`, optional):
Video height. Video height.
thumb (:obj:`str`, optional):
Video thumbnail.
Pass a file path as string to send an image that exists on your local machine.
Thumbnail should have 90 or less pixels of width and 90 or less pixels of height.
supports_streaming (:obj:`bool`, optional): supports_streaming (:obj:`bool`, optional):
Pass True, if the uploaded video is suitable for streaming. Pass True, if the uploaded video is suitable for streaming.
@ -1332,6 +1393,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message. If the message is a reply, ID of the original message.
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
@ -1339,7 +1411,8 @@ class Client:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
style = self.html if parse_mode.lower() == "html" else self.markdown style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(video) file = self.save_file(video, progress=progress)
file_thumb = None if thumb is None else self.save_file(thumb)
while True: while True:
try: try:
@ -1349,6 +1422,7 @@ class Client:
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
mime_type=mimetypes.types_map[".mp4"], mime_type=mimetypes.types_map[".mp4"],
file=file, file=file,
thumb=file_thumb,
attributes=[ attributes=[
types.DocumentAttributeVideo( types.DocumentAttributeVideo(
supports_streaming=supports_streaming, supports_streaming=supports_streaming,
@ -1377,7 +1451,8 @@ class Client:
parse_mode: str = "", parse_mode: str = "",
duration: int = 0, duration: int = 0,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send audio files. """Use this method to send audio files.
Args: Args:
@ -1408,6 +1483,17 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message If the message is a reply, ID of the original message
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
@ -1415,7 +1501,7 @@ class Client:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
style = self.html if parse_mode.lower() == "html" else self.markdown style = self.html if parse_mode.lower() == "html" else self.markdown
file = self.save_file(voice) file = self.save_file(voice, progress=progress)
while True: while True:
try: try:
@ -1449,7 +1535,8 @@ class Client:
duration: int = 0, duration: int = 0,
length: int = 1, length: int = 1,
disable_notification: bool = None, disable_notification: bool = None,
reply_to_message_id: int = None): reply_to_message_id: int = None,
progress: callable = None):
"""Use this method to send video messages. """Use this method to send video messages.
Args: Args:
@ -1475,13 +1562,24 @@ class Client:
reply_to_message_id (:obj:`int`, optional): reply_to_message_id (:obj:`int`, optional):
If the message is a reply, ID of the original message If the message is a reply, ID of the original message
progress (:obj:`callable`):
Pass a callback function to view the upload progress.
The function must accept two arguments (current, total).
Other Parameters:
current (:obj:`int`):
The amount of bytes uploaded so far.
total (:obj:`int`):
The size of the file.
Returns: Returns:
On success, the sent Message is returned. On success, the sent Message is returned.
Raises: Raises:
:class:`pyrogram.Error` :class:`pyrogram.Error`
""" """
file = self.save_file(video_note) file = self.save_file(video_note, progress=progress)
while True: while True:
try: try:
@ -1511,6 +1609,7 @@ class Client:
else: else:
return r return r
# TODO: Add progress parameter
def send_media_group(self, def send_media_group(self,
chat_id: int or str, chat_id: int or str,
media: list, media: list,
@ -1965,7 +2064,11 @@ class Client:
) )
# TODO: Remove redundant code # TODO: Remove redundant code
def save_file(self, path: str, file_id: int = None, file_part: int = 0): def save_file(self,
path: str,
file_id: int = None,
file_part: int = 0,
progress: callable = None):
part_size = 512 * 1024 part_size = 512 * 1024
file_size = os.path.getsize(path) file_size = os.path.getsize(path)
file_total_parts = math.ceil(file_size / part_size) file_total_parts = math.ceil(file_size / part_size)
@ -2005,6 +2108,9 @@ class Client:
md5_sum.update(chunk) md5_sum.update(chunk)
file_part += 1 file_part += 1
if progress:
progress(min(file_part * part_size, file_size), file_size)
except Exception as e: except Exception as e:
log.error(e) log.error(e)
else: else:
@ -2102,7 +2208,7 @@ class Client:
offset += limit offset += limit
if progress: if progress:
progress(offset, size) progress(min(offset, size), size)
r = session.send( r = session.send(
functions.upload.GetFile( functions.upload.GetFile(
@ -2466,10 +2572,10 @@ class Client:
progress (:obj:`callable`): progress (:obj:`callable`):
Pass a callback function to view the download progress. Pass a callback function to view the download progress.
The function must accept two arguments (progress, total). The function must accept two arguments (current, total).
Other Parameters: Other Parameters:
progress (:obj:`int`): current (:obj:`int`):
The amount of bytes downloaded so far. The amount of bytes downloaded so far.
total (:obj:`int`): total (:obj:`int`):
@ -2648,3 +2754,39 @@ class Client:
reply_to_msg_id=reply_to_message_id reply_to_msg_id=reply_to_message_id
) )
) )
def get_messages(self,
chat_id: int or str,
message_ids: list):
"""Use this method to get messages that belong to a specific chat.
You can retrieve up to 200 messages at once.
Args:
chat_id (:obj:`int` | :obj:`str`):
Unique identifier for the target chat or username of the target channel/supergroup
(in the format @username). For your personal cloud storage (Saved Messages) you can
simply use "me" or "self". Phone numbers that exist in your Telegram address book are also supported.
message_ids (:obj:`list`):
A list of Message identifiers in the chat specified in *chat_id*.
Returns:
List of the requested messages
Raises:
:class:`pyrogram.Error`
"""
peer = self.resolve_peer(chat_id)
message_ids = [types.InputMessageID(i) for i in message_ids]
if isinstance(peer, types.InputPeerChannel):
rpc = functions.channels.GetMessages(
channel=peer,
id=message_ids
)
else:
rpc = functions.messages.GetMessages(
id=message_ids
)
return self.send(rpc)

View File

@ -17,6 +17,7 @@
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>. # along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
from pyrogram.api.types import InputPhoneContact as RawInputPhoneContact from pyrogram.api.types import InputPhoneContact as RawInputPhoneContact
from pyrogram.session.internals import MsgId
class InputPhoneContact: class InputPhoneContact:
@ -36,7 +37,7 @@ class InputPhoneContact:
def __new__(cls, phone: str, first_name: str, last_name: str = ""): def __new__(cls, phone: str, first_name: str, last_name: str = ""):
return RawInputPhoneContact( return RawInputPhoneContact(
client_id=0, client_id=MsgId(),
phone="+" + phone.strip("+"), phone="+" + phone.strip("+"),
first_name=first_name, first_name=first_name,
last_name=last_name last_name=last_name

View File

@ -23,10 +23,16 @@ PublicKey = namedtuple("PublicKey", ["m", "e"])
class RSA: class RSA:
# To get modulus and exponent: # To get modulus and exponent:
#
# [RSA PUBLIC KEY]:
# grep -v -- - public.key | tr -d \\n | base64 -d | openssl asn1parse -inform DER -i # grep -v -- - public.key | tr -d \\n | base64 -d | openssl asn1parse -inform DER -i
#
# [PUBLIC KEY]:
# openssl rsa -pubin -in key -text -noout
server_public_keys = { server_public_keys = {
0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers # -4344800451088585951
0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers #1
# -----BEGIN RSA PUBLIC KEY----- # -----BEGIN RSA PUBLIC KEY-----
# MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6 # MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
# lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS # lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
@ -48,6 +54,108 @@ class RSA:
), # Modulus ), # Modulus
int("010001", 16) # Exponent int("010001", 16) # Exponent
), ),
# 847625836280919973
0x10bc35f3509f7b7a5 - (1 << 64): PublicKey( # Telegram servers #2
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAruw2yP/BCcsJliRoW5eB
# VBVle9dtjJw+OYED160Wybum9SXtBBLXriwt4rROd9csv0t0OHCaTmRqBcQ0J8fx
# hN6/cpR1GWgOZRUAiQxoMnlt0R93LCX/j1dnVa/gVbCjdSxpbrfY2g2L4frzjJvd
# l84Kd9ORYjDEAyFnEA7dD556OptgLQQ2e2iVNq8NZLYTzLp5YpOdO1doK+ttrltg
# gTCy5SrKeLoCPPbOgGsdxJxyz5KKcZnSLj16yE5HvJQn0CNpRdENvRUXe6tBP78O
# 39oJ8BTHp9oIjd6XWXAsp2CvK45Ol8wFXGF710w9lwCGNbmNxNYhtIkdqfsEcwR5
# JwIDAQAB
# -----END PUBLIC KEY-----
int(
"AEEC36C8FFC109CB099624685B97815415657BD76D8C9C3E398103D7AD16C9BB"
"A6F525ED0412D7AE2C2DE2B44E77D72CBF4B7438709A4E646A05C43427C7F184"
"DEBF72947519680E651500890C6832796DD11F772C25FF8F576755AFE055B0A3"
"752C696EB7D8DA0D8BE1FAF38C9BDD97CE0A77D3916230C4032167100EDD0F9E"
"7A3A9B602D04367B689536AF0D64B613CCBA7962939D3B57682BEB6DAE5B6081"
"30B2E52ACA78BA023CF6CE806B1DC49C72CF928A7199D22E3D7AC84E47BC9427"
"D0236945D10DBD15177BAB413FBF0EDFDA09F014C7A7DA088DDE9759702CA760"
"AF2B8E4E97CC055C617BD74C3D97008635B98DC4D621B4891DA9FB0473047927",
16
), # Modulus
int("010001", 16) # Exponent
),
# 1562291298945373506
0x115ae5fa8b5529542 - (1 << 64): PublicKey( # Telegram servers #3
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvfLHfYH2r9R70w8prHbl
# Wt/nDkh+XkgpflqQVcnAfSuTtO05lNPspQmL8Y2XjVT4t8cT6xAkdgfmmvnvRPOO
# KPi0OfJXoRVylFzAQG/j83u5K3kRLbae7fLccVhKZhY46lvsueI1hQdLgNV9n1cQ
# 3TDS2pQOCtovG4eDl9wacrXOJTG2990VjgnIKNA0UMoP+KF03qzryqIt3oTvZq03
# DyWdGK+AZjgBLaDKSnC6qD2cFY81UryRWOab8zKkWAnhw2kFpcqhI0jdV5QaSCEx
# vnsjVaX0Y1N0870931/5Jb9ICe4nweZ9kSDF/gip3kWLG0o8XQpChDfyvsqB9OLV
# /wIDAQAB
# -----END PUBLIC KEY-----
int(
"BDF2C77D81F6AFD47BD30F29AC76E55ADFE70E487E5E48297E5A9055C9C07D2B"
"93B4ED3994D3ECA5098BF18D978D54F8B7C713EB10247607E69AF9EF44F38E28"
"F8B439F257A11572945CC0406FE3F37BB92B79112DB69EEDF2DC71584A661638"
"EA5BECB9E23585074B80D57D9F5710DD30D2DA940E0ADA2F1B878397DC1A72B5"
"CE2531B6F7DD158E09C828D03450CA0FF8A174DEACEBCAA22DDE84EF66AD370F"
"259D18AF806638012DA0CA4A70BAA83D9C158F3552BC9158E69BF332A45809E1"
"C36905A5CAA12348DD57941A482131BE7B2355A5F4635374F3BD3DDF5FF925BF"
"4809EE27C1E67D9120C5FE08A9DE458B1B4A3C5D0A428437F2BECA81F4E2D5FF",
16
), # Modulus
int("010001", 16) # Exponent
),
# -5859577972006586033
0xaeae98e13cd7f94f - (1 << 64): PublicKey( # Telegram servers #4
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ditzm+mPND6xkhzwFI
# z6J/968CtkcSE/7Z2qAJiXbmZ3UDJPGrzqTDHkO30R8VeRM/Kz2f4nR05GIFiITl
# 4bEjvpy7xqRDspJcCFIOcyXm8abVDhF+th6knSU0yLtNKuQVP6voMrnt9MV1X92L
# GZQLgdHZbPQz0Z5qIpaKhdyA8DEvWWvSUwwc+yi1/gGaybwlzZwqXYoPOhwMebzK
# Uk0xW14htcJrRrq+PXXQbRzTMynseCoPIoke0dtCodbA3qQxQovE16q9zz4Otv2k
# 4j63cz53J+mhkVWAeWxVGI0lltJmWtEYK6er8VqqWot3nqmWMXogrgRLggv/Nbbo
# oQIDAQAB
# -----END PUBLIC KEY-----
int(
"B3F762B739BE98F343EB1921CF0148CFA27FF7AF02B6471213FED9DAA0098976"
"E667750324F1ABCEA4C31E43B7D11F1579133F2B3D9FE27474E462058884E5E1"
"B123BE9CBBC6A443B2925C08520E7325E6F1A6D50E117EB61EA49D2534C8BB4D"
"2AE4153FABE832B9EDF4C5755FDD8B19940B81D1D96CF433D19E6A22968A85DC"
"80F0312F596BD2530C1CFB28B5FE019AC9BC25CD9C2A5D8A0F3A1C0C79BCCA52"
"4D315B5E21B5C26B46BABE3D75D06D1CD33329EC782A0F22891ED1DB42A1D6C0"
"DEA431428BC4D7AABDCF3E0EB6FDA4E23EB7733E7727E9A1915580796C55188D"
"2596D2665AD1182BA7ABF15AAA5A8B779EA996317A20AE044B820BFF35B6E8A1",
16
), # Modulus
int("010001", 16) # Exponent
),
# 6491968696586960280
0x15a181b2235057d98 - (1 << 64): PublicKey( # Telegram servers #5
# -----BEGIN PUBLIC KEY-----
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvmpxVY7ld/8DAjz6F6q0
# 5shjg8/4p6047bn6/m8yPy1RBsvIyvuDuGnP/RzPEhzXQ9UJ5Ynmh2XJZgHoE9xb
# nfxL5BXHplJhMtADXKM9bWB11PU1Eioc3+AXBB8QiNFBn2XI5UkO5hPhbb9mJpjA
# 9Uhw8EdfqJP8QetVsI/xrCEbwEXe0xvifRLJbY08/Gp66KpQvy7g8w7VB8wlgePe
# xW3pT13Ap6vuC+mQuJPyiHvSxjEKHgqePji9NP3tJUFQjcECqcm0yV7/2d0t/pbC
# m+ZH1sadZspQCEPPrtbkQBlvHb4OLiIWPGHKSMeRFvp3IWcmdJqXahxLCUS1Eh6M
# AQIDAQAB
# -----END PUBLIC KEY-----
int(
"BE6A71558EE577FF03023CFA17AAB4E6C86383CFF8A7AD38EDB9FAFE6F323F2D"
"5106CBC8CAFB83B869CFFD1CCF121CD743D509E589E68765C96601E813DC5B9D"
"FC4BE415C7A6526132D0035CA33D6D6075D4F535122A1CDFE017041F1088D141"
"9F65C8E5490EE613E16DBF662698C0F54870F0475FA893FC41EB55B08FF1AC21"
"1BC045DED31BE27D12C96D8D3CFC6A7AE8AA50BF2EE0F30ED507CC2581E3DEC5"
"6DE94F5DC0A7ABEE0BE990B893F2887BD2C6310A1E0A9E3E38BD34FDED254150"
"8DC102A9C9B4C95EFFD9DD2DFE96C29BE647D6C69D66CA500843CFAED6E44019"
"6F1DBE0E2E22163C61CA48C79116FA77216726749A976A1C4B0944B5121E8C01",
16
), # Modulus
int("010001", 16) # Exponent
),
# 6427105915145367799
0x15931aac70e0d30f7 - (1 << 64): PublicKey( # CDN DC-121 0x15931aac70e0d30f7 - (1 << 64): PublicKey( # CDN DC-121
# -----BEGIN RSA PUBLIC KEY----- # -----BEGIN RSA PUBLIC KEY-----
# MIIBCgKCAQEA+Lf3PvgE1yxbJUCMaEAkV0QySTVpnaDjiednB5RbtNWjCeqSVakY # MIIBCgKCAQEA+Lf3PvgE1yxbJUCMaEAkV0QySTVpnaDjiednB5RbtNWjCeqSVakY
@ -70,6 +178,8 @@ class RSA:
), # Modulus ), # Modulus
int("010001", 16) # Exponent int("010001", 16) # Exponent
), ),
# 2685959930972952888
0x1254672538e935938 - (1 << 64): PublicKey( # CDN DC-140 0x1254672538e935938 - (1 << 64): PublicKey( # CDN DC-140
# -----BEGIN RSA PUBLIC KEY----- # -----BEGIN RSA PUBLIC KEY-----
# MIIBCgKCAQEAzuHVC7sE50Kho/yDVZtWnlmA5Bf/aM8KZY3WzS16w6w1sBqipj8o # MIIBCgKCAQEAzuHVC7sE50Kho/yDVZtWnlmA5Bf/aM8KZY3WzS16w6w1sBqipj8o

View File

@ -91,8 +91,19 @@ class Auth:
# Step 1; Step 2 # Step 1; Step 2
nonce = int.from_bytes(urandom(16), "little", signed=True) nonce = int.from_bytes(urandom(16), "little", signed=True)
log.debug("Send req_pq: {}".format(nonce)) log.debug("Send req_pq: {}".format(nonce))
res_pq = self.send(functions.ReqPq(nonce)) res_pq = self.send(functions.ReqPqMulti(nonce))
log.debug("Got ResPq: {}".format(res_pq.server_nonce)) log.debug("Got ResPq: {}".format(res_pq.server_nonce))
log.debug("Server public key fingerprints: {}".format(res_pq.server_public_key_fingerprints))
for i in res_pq.server_public_key_fingerprints:
if i in RSA.server_public_keys:
log.debug("Using fingerprint: {}".format(i))
public_key_fingerprint = i
break
else:
log.debug("Fingerprint unknown: {}".format(i))
else:
raise Exception("Public key not found")
# Step 3 # Step 3
pq = int.from_bytes(res_pq.pq, "big") pq = int.from_bytes(res_pq.pq, "big")
@ -118,7 +129,7 @@ class Auth:
sha = sha1(data).digest() sha = sha1(data).digest()
padding = urandom(- (len(data) + len(sha)) % 255) padding = urandom(- (len(data) + len(sha)) % 255)
data_with_hash = sha + data + padding data_with_hash = sha + data + padding
encrypted_data = RSA.encrypt(data_with_hash, res_pq.server_public_key_fingerprints[0]) encrypted_data = RSA.encrypt(data_with_hash, public_key_fingerprint)
log.debug("Done encrypt data with RSA") log.debug("Done encrypt data with RSA")
@ -130,7 +141,7 @@ class Auth:
server_nonce, server_nonce,
int.to_bytes(p, 4, "big"), int.to_bytes(p, 4, "big"),
int.to_bytes(q, 4, "big"), int.to_bytes(q, 4, "big"),
res_pq.server_public_key_fingerprints[0], public_key_fingerprint,
encrypted_data encrypted_data
) )
) )