Merge branch 'master' into docs
This commit is contained in:
commit
5af4a0a49a
@ -29,6 +29,7 @@ destroy_auth_key_fail#ea109b13 = DestroyAuthKeyRes;
|
||||
---functions---
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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)
|
||||
- [**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)
|
||||
- [**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)
|
||||
- [**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_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
52
examples/welcome_bot.py
Normal 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()
|
@ -23,7 +23,7 @@ __copyright__ = "Copyright (C) 2017-2018 Dan Tès <https://github.com/delivrance
|
||||
"e" if sys.getfilesystemencoding() == "ascii" else "\xe8"
|
||||
)
|
||||
__license__ = "GNU Lesser General Public License v3 or later (LGPLv3+)"
|
||||
__version__ = "0.6.1"
|
||||
__version__ = "0.6.2"
|
||||
|
||||
from .api.errors import Error
|
||||
from .client import ChatAction
|
||||
|
@ -256,15 +256,16 @@ class Client:
|
||||
|
||||
if isinstance(entity, Chat):
|
||||
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
|
||||
|
||||
input_peer = InputPeerChat(
|
||||
chat_id=chat_id
|
||||
)
|
||||
|
||||
self.peers_by_id[chat_id] = input_peer
|
||||
self.peers_by_id[peer_id] = input_peer
|
||||
|
||||
if isinstance(entity, Channel):
|
||||
channel_id = entity.id
|
||||
@ -571,6 +572,8 @@ class Client:
|
||||
elif confirm in ("n", "2"):
|
||||
self.phone_number = input("Enter phone number: ")
|
||||
|
||||
self.phone_number = self.phone_number.strip("+")
|
||||
|
||||
try:
|
||||
r = self.send(
|
||||
functions.auth.SendCode(
|
||||
@ -886,17 +889,20 @@ class Client:
|
||||
if isinstance(peer_id, types.PeerUser):
|
||||
peer_id = peer_id.user_id
|
||||
elif isinstance(peer_id, types.PeerChat):
|
||||
peer_id = peer_id.chat_id
|
||||
peer_id = -peer_id.chat_id
|
||||
elif isinstance(peer_id, types.PeerChannel):
|
||||
peer_id = int("-100" + str(peer_id.channel_id))
|
||||
|
||||
try:
|
||||
try: # User
|
||||
return self.peers_by_id[peer_id]
|
||||
except KeyError:
|
||||
try:
|
||||
return self.peers_by_id[int("-100" + str(peer_id))]
|
||||
try: # Chat
|
||||
return self.peers_by_id[-peer_id]
|
||||
except KeyError:
|
||||
raise PeerIdInvalid
|
||||
try: # Channel
|
||||
return self.peers_by_id[int("-100" + str(peer_id))]
|
||||
except (KeyError, ValueError):
|
||||
raise PeerIdInvalid
|
||||
|
||||
def get_me(self):
|
||||
"""A simple method for testing the user authorization. Requires no parameters.
|
||||
@ -1014,7 +1020,8 @@ class Client:
|
||||
parse_mode: str = "",
|
||||
ttl_seconds: int = 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.
|
||||
|
||||
Args:
|
||||
@ -1047,6 +1054,17 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
@ -1054,7 +1072,7 @@ class Client:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
@ -1085,7 +1103,8 @@ class Client:
|
||||
performer: str = None,
|
||||
title: str = 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.
|
||||
|
||||
For sending voice messages, use the :obj:`send_voice` method instead.
|
||||
@ -1124,6 +1143,17 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
@ -1131,7 +1161,7 @@ class Client:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
@ -1167,7 +1197,8 @@ class Client:
|
||||
caption: str = "",
|
||||
parse_mode: str = "",
|
||||
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.
|
||||
|
||||
Args:
|
||||
@ -1195,6 +1226,17 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
@ -1202,7 +1244,7 @@ class Client:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
@ -1231,7 +1273,8 @@ class Client:
|
||||
chat_id: int or str,
|
||||
sticker: str,
|
||||
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.
|
||||
|
||||
Args:
|
||||
@ -1251,13 +1294,24 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
Raises:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
file = self.save_file(sticker)
|
||||
file = self.save_file(sticker, progress=progress)
|
||||
|
||||
while True:
|
||||
try:
|
||||
@ -1290,9 +1344,11 @@ class Client:
|
||||
duration: int = 0,
|
||||
width: int = 0,
|
||||
height: int = 0,
|
||||
thumb: str = None,
|
||||
supports_streaming: 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.
|
||||
|
||||
Args:
|
||||
@ -1322,6 +1378,11 @@ class Client:
|
||||
height (:obj:`int`, optional):
|
||||
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):
|
||||
Pass True, if the uploaded video is suitable for streaming.
|
||||
|
||||
@ -1332,6 +1393,17 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
@ -1339,7 +1411,8 @@ class Client:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
@ -1349,6 +1422,7 @@ class Client:
|
||||
media=types.InputMediaUploadedDocument(
|
||||
mime_type=mimetypes.types_map[".mp4"],
|
||||
file=file,
|
||||
thumb=file_thumb,
|
||||
attributes=[
|
||||
types.DocumentAttributeVideo(
|
||||
supports_streaming=supports_streaming,
|
||||
@ -1377,7 +1451,8 @@ class Client:
|
||||
parse_mode: str = "",
|
||||
duration: int = 0,
|
||||
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.
|
||||
|
||||
Args:
|
||||
@ -1408,6 +1483,17 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
@ -1415,7 +1501,7 @@ class Client:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
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:
|
||||
try:
|
||||
@ -1449,7 +1535,8 @@ class Client:
|
||||
duration: int = 0,
|
||||
length: int = 1,
|
||||
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.
|
||||
|
||||
Args:
|
||||
@ -1475,13 +1562,24 @@ class Client:
|
||||
reply_to_message_id (:obj:`int`, optional):
|
||||
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:
|
||||
On success, the sent Message is returned.
|
||||
|
||||
Raises:
|
||||
:class:`pyrogram.Error`
|
||||
"""
|
||||
file = self.save_file(video_note)
|
||||
file = self.save_file(video_note, progress=progress)
|
||||
|
||||
while True:
|
||||
try:
|
||||
@ -1511,6 +1609,7 @@ class Client:
|
||||
else:
|
||||
return r
|
||||
|
||||
# TODO: Add progress parameter
|
||||
def send_media_group(self,
|
||||
chat_id: int or str,
|
||||
media: list,
|
||||
@ -1965,7 +2064,11 @@ class Client:
|
||||
)
|
||||
|
||||
# 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
|
||||
file_size = os.path.getsize(path)
|
||||
file_total_parts = math.ceil(file_size / part_size)
|
||||
@ -2005,6 +2108,9 @@ class Client:
|
||||
md5_sum.update(chunk)
|
||||
|
||||
file_part += 1
|
||||
|
||||
if progress:
|
||||
progress(min(file_part * part_size, file_size), file_size)
|
||||
except Exception as e:
|
||||
log.error(e)
|
||||
else:
|
||||
@ -2102,7 +2208,7 @@ class Client:
|
||||
offset += limit
|
||||
|
||||
if progress:
|
||||
progress(offset, size)
|
||||
progress(min(offset, size), size)
|
||||
|
||||
r = session.send(
|
||||
functions.upload.GetFile(
|
||||
@ -2466,10 +2572,10 @@ class Client:
|
||||
|
||||
progress (:obj:`callable`):
|
||||
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:
|
||||
progress (:obj:`int`):
|
||||
current (:obj:`int`):
|
||||
The amount of bytes downloaded so far.
|
||||
|
||||
total (:obj:`int`):
|
||||
@ -2648,3 +2754,39 @@ class Client:
|
||||
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)
|
||||
|
@ -17,6 +17,7 @@
|
||||
# along with Pyrogram. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from pyrogram.api.types import InputPhoneContact as RawInputPhoneContact
|
||||
from pyrogram.session.internals import MsgId
|
||||
|
||||
|
||||
class InputPhoneContact:
|
||||
@ -36,7 +37,7 @@ class InputPhoneContact:
|
||||
|
||||
def __new__(cls, phone: str, first_name: str, last_name: str = ""):
|
||||
return RawInputPhoneContact(
|
||||
client_id=0,
|
||||
client_id=MsgId(),
|
||||
phone="+" + phone.strip("+"),
|
||||
first_name=first_name,
|
||||
last_name=last_name
|
||||
|
@ -23,10 +23,16 @@ PublicKey = namedtuple("PublicKey", ["m", "e"])
|
||||
|
||||
class RSA:
|
||||
# To get modulus and exponent:
|
||||
#
|
||||
# [RSA PUBLIC KEY]:
|
||||
# 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 = {
|
||||
0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers
|
||||
# -4344800451088585951
|
||||
0xc3b42b026ce86b21 - (1 << 64): PublicKey( # Telegram servers #1
|
||||
# -----BEGIN RSA PUBLIC KEY-----
|
||||
# MIIBCgKCAQEAwVACPi9w23mF3tBkdZz+zwrzKOaaQdr01vAbU4E1pvkfj4sqDsm6
|
||||
# lyDONS789sVoD/xCS9Y0hkkC3gtL1tSfTlgCMOOul9lcixlEKzwKENj1Yz/s7daS
|
||||
@ -48,6 +54,108 @@ class RSA:
|
||||
), # Modulus
|
||||
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
|
||||
# -----BEGIN RSA PUBLIC KEY-----
|
||||
# MIIBCgKCAQEA+Lf3PvgE1yxbJUCMaEAkV0QySTVpnaDjiednB5RbtNWjCeqSVakY
|
||||
@ -70,6 +178,8 @@ class RSA:
|
||||
), # Modulus
|
||||
int("010001", 16) # Exponent
|
||||
),
|
||||
|
||||
# 2685959930972952888
|
||||
0x1254672538e935938 - (1 << 64): PublicKey( # CDN DC-140
|
||||
# -----BEGIN RSA PUBLIC KEY-----
|
||||
# MIIBCgKCAQEAzuHVC7sE50Kho/yDVZtWnlmA5Bf/aM8KZY3WzS16w6w1sBqipj8o
|
||||
|
@ -91,8 +91,19 @@ class Auth:
|
||||
# Step 1; Step 2
|
||||
nonce = int.from_bytes(urandom(16), "little", signed=True)
|
||||
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("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
|
||||
pq = int.from_bytes(res_pq.pq, "big")
|
||||
@ -118,7 +129,7 @@ class Auth:
|
||||
sha = sha1(data).digest()
|
||||
padding = urandom(- (len(data) + len(sha)) % 255)
|
||||
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")
|
||||
|
||||
@ -130,7 +141,7 @@ class Auth:
|
||||
server_nonce,
|
||||
int.to_bytes(p, 4, "big"),
|
||||
int.to_bytes(q, 4, "big"),
|
||||
res_pq.server_public_key_fingerprints[0],
|
||||
public_key_fingerprint,
|
||||
encrypted_data
|
||||
)
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user