Merge branch 'develop' into asyncio

# Conflicts:
#	pyrogram/client/client.py
#	pyrogram/client/methods/messages/send_media_group.py
#	pyrogram/client/methods/utilities/download_media.py
This commit is contained in:
Dan 2018-12-15 11:12:30 +01:00
commit 976eae63ed
14 changed files with 107 additions and 21 deletions

View File

@ -51,7 +51,7 @@ BOT_INLINE_DISABLED The inline feature of the bot is disabled
INLINE_RESULT_EXPIRED The inline bot query expired INLINE_RESULT_EXPIRED The inline bot query expired
INVITE_HASH_INVALID The invite link hash is invalid INVITE_HASH_INVALID The invite link hash is invalid
USER_ALREADY_PARTICIPANT The user is already a participant of this chat USER_ALREADY_PARTICIPANT The user is already a participant of this chat
TTL_MEDIA_INVALID This kind of media does not support self-destruction TTL_MEDIA_INVALID The media does not support self-destruction
MAX_ID_INVALID The max_id parameter is invalid MAX_ID_INVALID The max_id parameter is invalid
CHANNEL_INVALID The channel parameter is invalid CHANNEL_INVALID The channel parameter is invalid
DC_ID_INVALID The dc_id parameter is invalid DC_ID_INVALID The dc_id parameter is invalid
@ -59,7 +59,7 @@ LIMIT_INVALID The limit parameter is invalid
OFFSET_INVALID The offset parameter is invalid OFFSET_INVALID The offset parameter is invalid
EMAIL_INVALID The email provided is invalid EMAIL_INVALID The email provided is invalid
USER_IS_BOT A bot cannot send messages to other bots or to itself USER_IS_BOT A bot cannot send messages to other bots or to itself
WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL WEBPAGE_CURL_FAILED Telegram server could not fetch the provided URL
STICKERSET_INVALID The requested sticker set is invalid STICKERSET_INVALID The requested sticker set is invalid
PEER_FLOOD The method can't be used because your account is limited PEER_FLOOD The method can't be used because your account is limited
MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
@ -68,4 +68,12 @@ USER_CHANNELS_TOO_MUCH The user is already in too many channels or supergroups
API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
USER_NOT_PARTICIPANT The user is not a member of this chat USER_NOT_PARTICIPANT The user is not a member of this chat
CHANNEL_PRIVATE The channel/supergroup is not accessible CHANNEL_PRIVATE The channel/supergroup is not accessible
MESSAGE_IDS_EMPTY The requested message doesn't exist MESSAGE_IDS_EMPTY The requested message doesn't exist
WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media
QUERY_ID_INVALID The callback query id is invalid
MEDIA_EMPTY The media is invalid
USER_IS_BLOCKED The user blocked you
YOU_BLOCKED_USER You blocked this user
ADMINS_TOO_MUCH The chat has too many administrators
BOTS_TOO_MUCH The chat has too many bots
USER_ADMIN_INVALID The action requires admin privileges
1 id message
51 INLINE_RESULT_EXPIRED The inline bot query expired
52 INVITE_HASH_INVALID The invite link hash is invalid
53 USER_ALREADY_PARTICIPANT The user is already a participant of this chat
54 TTL_MEDIA_INVALID This kind of media does not support self-destruction The media does not support self-destruction
55 MAX_ID_INVALID The max_id parameter is invalid
56 CHANNEL_INVALID The channel parameter is invalid
57 DC_ID_INVALID The dc_id parameter is invalid
59 OFFSET_INVALID The offset parameter is invalid
60 EMAIL_INVALID The email provided is invalid
61 USER_IS_BOT A bot cannot send messages to other bots or to itself
62 WEBPAGE_CURL_FAILED Telegram could not fetch the provided URL Telegram server could not fetch the provided URL
63 STICKERSET_INVALID The requested sticker set is invalid
64 PEER_FLOOD The method can't be used because your account is limited
65 MEDIA_CAPTION_TOO_LONG The media caption is longer than 200 characters
68 API_ID_PUBLISHED_FLOOD You are using an API key that is limited on the server side
69 USER_NOT_PARTICIPANT The user is not a member of this chat
70 CHANNEL_PRIVATE The channel/supergroup is not accessible
71 MESSAGE_IDS_EMPTY The requested message doesn't exist
72 WEBPAGE_MEDIA_EMPTY The URL doesn't contain any valid media
73 QUERY_ID_INVALID The callback query id is invalid
74 MEDIA_EMPTY The media is invalid
75 USER_IS_BLOCKED The user blocked you
76 YOU_BLOCKED_USER You blocked this user
77 ADMINS_TOO_MUCH The chat has too many administrators
78 BOTS_TOO_MUCH The chat has too many bots
79 USER_ADMIN_INVALID The action requires admin privileges

View File

@ -4,4 +4,5 @@ RPC_CALL_FAIL Telegram is having internal problems. Please try again later
RPC_MCGET_FAIL Telegram is having internal problems. Please try again later RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later
HISTORY_GET_FAILED Telegram is having internal problems. Please try again later HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later
RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later
1 id message
4 RPC_MCGET_FAIL Telegram is having internal problems. Please try again later
5 PERSISTENT_TIMESTAMP_OUTDATED Telegram is having internal problems. Please try again later
6 HISTORY_GET_FAILED Telegram is having internal problems. Please try again later
7 REG_ID_GENERATE_FAILED Telegram is having internal problems. Please try again later
8 RANDOM_ID_DUPLICATE Telegram is having internal problems. Please try again later

View File

@ -6,22 +6,40 @@ to make it only work for specific messages in a specific chat.
from pyrogram import Client, Emoji, Filters from pyrogram import Client, Emoji, Filters
MENTION = "[{}](tg://user?id={})" USER = "**{}**"
MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {}!" MESSAGE = "{} Welcome to [Pyrogram](https://docs.pyrogram.ml/)'s group chat {{}}!".format(Emoji.SPARKLES)
enabled_groups = Filters.chat("PyrogramChat")
last_welcomes = {}
app = Client("my_account") app = Client("my_account")
@app.on_message(Filters.chat("PyrogramChat") & Filters.new_chat_members) @app.on_message(enabled_groups & Filters.new_chat_members)
def welcome(client, message): def welcome(client, message):
# Build the new members list (with mentions) by using their first_name chat_id = message.chat.id
new_members = [MENTION.format(i.first_name, i.id) for i in message.new_chat_members]
# Build the welcome message by using an emoji and the list we built above # Get the previous welcome message and members, if any
text = MESSAGE.format(Emoji.SPARKLES, ", ".join(new_members)) previous_welcome, previous_members = last_welcomes.pop(chat_id, (None, []))
# Send the welcome message # Delete the previous message, if exists
message.reply(text, disable_web_page_preview=True) if previous_welcome:
previous_welcome.delete()
# Build the new members list by using their first_name. Also append the previous members, if any
new_members = [USER.format(i.first_name) for i in message.new_chat_members] + previous_members
# Build the welcome message by using an emoji and the list we created above
text = MESSAGE.format(", ".join(new_members))
# Actually send the welcome and save the new message and the new members list
last_welcomes[message.chat.id] = message.reply(text, disable_web_page_preview=True), new_members
@app.on_message(enabled_groups)
def reset(client, message):
# Don't make the bot delete the previous welcome in case someone talks in the middle
last_welcomes.pop(message.chat.id, None)
app.run() # Automatically start() and idle() app.run() # Automatically start() and idle()

View File

@ -199,10 +199,9 @@ class Client(Methods, BaseClient):
self.dispatcher = Dispatcher(self, workers) self.dispatcher = Dispatcher(self, workers)
def __enter__(self): def __enter__(self):
self.start() return self.start()
return self
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, *args):
self.stop() self.stop()
@property @property
@ -284,6 +283,8 @@ class Client(Methods, BaseClient):
mimetypes.init() mimetypes.init()
return self
async def stop(self): async def stop(self):
"""Use this method to manually stop the Client. """Use this method to manually stop the Client.
Requires no parameters. Requires no parameters.
@ -318,6 +319,8 @@ class Client(Methods, BaseClient):
self.is_started = False self.is_started = False
await self.session.stop() await self.session.stop()
return self
async def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)): async def idle(self, stop_signals: tuple = (SIGINT, SIGTERM, SIGABRT)):
"""Blocks the program execution until one of the signals are received, """Blocks the program execution until one of the signals are received,
then gently stop the Client by closing the underlying connection. then gently stop the Client by closing the underlying connection.
@ -439,6 +442,8 @@ class Client(Methods, BaseClient):
else: else:
self.user_id = r.user.id self.user_id = r.user.id
print("Logged in successfully as @{}".format(r.user.username))
async def authorize_user(self): async def authorize_user(self):
phone_number_invalid_raises = self.phone_number is not None phone_number_invalid_raises = self.phone_number is not None
phone_code_invalid_raises = self.phone_code is not None phone_code_invalid_raises = self.phone_code is not None
@ -623,7 +628,7 @@ class Client(Methods, BaseClient):
self.password = None self.password = None
self.user_id = r.user.id self.user_id = r.user.id
print("Login successful") print("Logged in successfully as {}".format(r.user.first_name))
def fetch_peers(self, entities: list): def fetch_peers(self, entities: list):
for entity in entities: for entity in entities:
@ -1234,7 +1239,7 @@ class Client(Methods, BaseClient):
version: int = 0, version: int = 0,
size: int = None, size: int = None,
progress: callable = None, progress: callable = None,
progress_args: tuple = None) -> str: progress_args: tuple = ()) -> str:
with await self.media_sessions_lock: with await self.media_sessions_lock:
session = self.media_sessions.get(dc_id, None) session = self.media_sessions.get(dc_id, None)
@ -1316,7 +1321,7 @@ class Client(Methods, BaseClient):
offset += limit offset += limit
if progress: if progress:
progress(self, min(offset, size), size, *progress_args) progress(self, min(offset, size) if size != 0 else offset, size, *progress_args)
r = await session.send( r = await session.send(
functions.upload.GetFile( functions.upload.GetFile(
@ -1398,7 +1403,7 @@ class Client(Methods, BaseClient):
offset += limit offset += limit
if progress: if progress:
progress(self, min(offset, size), size, *progress_args) progress(self, min(offset, size) if size != 0 else offset, size, *progress_args)
if len(chunk) < limit: if len(chunk) < limit:
break break

View File

@ -338,6 +338,7 @@ async def parse_messages(
video_note = None video_note = None
sticker = None sticker = None
document = None document = None
web_page = None
media = message.media media = message.media
@ -585,6 +586,8 @@ async def parse_messages(
file_size=doc.size, file_size=doc.size,
date=doc.date date=doc.date
) )
elif isinstance(media, types.MessageMediaWebPage):
web_page = True
else: else:
media = None media = None
@ -632,6 +635,7 @@ async def parse_messages(
video_note=video_note, video_note=video_note,
sticker=sticker, sticker=sticker,
document=document, document=document,
web_page=web_page,
views=message.views, views=message.views,
via_bot=parse_user(users.get(message.via_bot_id, None)), via_bot=parse_user(users.get(message.via_bot_id, None)),
outgoing=message.out, outgoing=message.out,

View File

@ -118,6 +118,9 @@ class Filters:
venue = create("Venue", lambda _, m: bool(m.venue)) venue = create("Venue", lambda _, m: bool(m.venue))
"""Filter messages that contain :obj:`Venue <pyrogram.api.types.pyrogram.Venue>` objects.""" """Filter messages that contain :obj:`Venue <pyrogram.api.types.pyrogram.Venue>` objects."""
web_page = create("WebPage", lambda _, m: m.web_page)
"""Filter messages sent with a webpage preview."""
private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private")) private = create("Private", lambda _, m: bool(m.chat and m.chat.type == "private"))
"""Filter messages sent in private chats.""" """Filter messages sent in private chats."""
@ -169,6 +172,9 @@ class Filters:
mentioned = create("Mentioned", lambda _, m: bool(m.mentioned)) mentioned = create("Mentioned", lambda _, m: bool(m.mentioned))
"""Filter messages containing mentions""" """Filter messages containing mentions"""
via_bot = create("ViaBot", lambda _, m: bool(m.via_bot))
"""Filter messages sent via inline bots"""
service = create("Service", lambda _, m: bool(m.service)) service = create("Service", lambda _, m: bool(m.service))
"""Filter service messages. A service message contains any of the following fields set """Filter service messages. A service message contains any of the following fields set

View File

@ -27,6 +27,13 @@ class OnCallbackQuery(BaseClient):
callback queries. This does the same thing as :meth:`add_handler` using the callback queries. This does the same thing as :meth:`add_handler` using the
:class:`CallbackQueryHandler`. :class:`CallbackQueryHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of callback queries to be passed Pass one or more filters to allow only a subset of callback queries to be passed

View File

@ -27,6 +27,13 @@ class OnDeletedMessages(BaseClient):
deleted messages. This does the same thing as :meth:`add_handler` using the deleted messages. This does the same thing as :meth:`add_handler` using the
:class:`DeletedMessagesHandler`. :class:`DeletedMessagesHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of messages to be passed Pass one or more filters to allow only a subset of messages to be passed

View File

@ -27,6 +27,13 @@ class OnMessage(BaseClient):
messages. This does the same thing as :meth:`add_handler` using the messages. This does the same thing as :meth:`add_handler` using the
:class:`MessageHandler`. :class:`MessageHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of messages to be passed Pass one or more filters to allow only a subset of messages to be passed

View File

@ -26,6 +26,13 @@ class OnRawUpdate(BaseClient):
raw updates. This does the same thing as :meth:`add_handler` using the raw updates. This does the same thing as :meth:`add_handler` using the
:class:`RawUpdateHandler`. :class:`RawUpdateHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
group (``int``, *optional*): group (``int``, *optional*):
The group identifier, defaults to 0. The group identifier, defaults to 0.

View File

@ -27,6 +27,13 @@ class OnUserStatus(BaseClient):
user status updates. This does the same thing as :meth:`add_handler` using the user status updates. This does the same thing as :meth:`add_handler` using the
:class:`UserStatusHandler`. :class:`UserStatusHandler`.
.. note::
This decorator will wrap your defined function in a tuple consisting of *(Handler, group)*.
To reference your own function after it has been decorated, you need to access
*my_function[0].callback*, that is, the *callback* field of Handler object which is the the
first element in the tuple.
Args: Args:
filters (:obj:`Filters <pyrogram.Filters>`): filters (:obj:`Filters <pyrogram.Filters>`):
Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function. Pass one or more filters to allow only a subset of UserStatus updated to be passed in your function.

View File

@ -108,6 +108,7 @@ class SendMediaGroup(BaseClient):
peer=await self.resolve_peer(chat_id), peer=await self.resolve_peer(chat_id),
media=types.InputMediaUploadedDocument( media=types.InputMediaUploadedDocument(
file=await self.save_file(i.media), file=await self.save_file(i.media),
thumb=None if i.thumb is None else self.save_file(i.thumb),
mime_type=mimetypes.types_map[".mp4"], mime_type=mimetypes.types_map[".mp4"],
attributes=[ attributes=[
types.DocumentAttributeVideo( types.DocumentAttributeVideo(

View File

@ -28,7 +28,7 @@ class DownloadMedia(BaseClient):
file_name: str = "", file_name: str = "",
block: bool = True, block: bool = True,
progress: callable = None, progress: callable = None,
progress_args: tuple = None): progress_args: tuple = ()):
"""Use this method to download the media from a Message. """Use this method to download the media from a Message.
Args: Args:

View File

@ -134,6 +134,12 @@ class Message(Object):
venue (:obj:`Venue <pyrogram.Venue>`, *optional*): venue (:obj:`Venue <pyrogram.Venue>`, *optional*):
Message is a venue, information about the venue. Message is a venue, information about the venue.
web_page (``bool``, *optional*):
Message was sent with a webpage preview.
**Note:** Support for web pages is still basic; a simple boolean is set in case the message contains a
web page preview. In future versions this property could turn into a full web page object that contains
more details.
new_chat_members (List of :obj:`User <pyrogram.User>`, *optional*): new_chat_members (List of :obj:`User <pyrogram.User>`, *optional*):
New members that were added to the group or supergroup and information about them New members that were added to the group or supergroup and information about them
(the bot itself may be one of these members). (the bot itself may be one of these members).
@ -246,6 +252,7 @@ class Message(Object):
contact=None, contact=None,
location=None, location=None,
venue=None, venue=None,
web_page=None,
new_chat_members: list = None, new_chat_members: list = None,
left_chat_member=None, left_chat_member=None,
new_chat_title: str = None, new_chat_title: str = None,
@ -297,6 +304,7 @@ class Message(Object):
self.contact = contact # flags.22?Contact self.contact = contact # flags.22?Contact
self.location = location # flags.23?Location self.location = location # flags.23?Location
self.venue = venue # flags.24?Venue self.venue = venue # flags.24?Venue
self.web_page = web_page
self.new_chat_members = new_chat_members # flags.25?Vector<User> self.new_chat_members = new_chat_members # flags.25?Vector<User>
self.left_chat_member = left_chat_member # flags.26?User self.left_chat_member = left_chat_member # flags.26?User
self.new_chat_title = new_chat_title # flags.27?string self.new_chat_title = new_chat_title # flags.27?string