mirror of
https://github.com/TeamPGM/pyrogram.git
synced 2024-11-28 00:56:19 +00:00
Merge branch 'master' into docs
This commit is contained in:
commit
5af4a0a49a
@ -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;
|
||||||
|
|
||||||
|
@ -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
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"
|
"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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user