From 9d701bc226d67b7eba64cf681e7636aa8c616cc4 Mon Sep 17 00:00:00 2001
From: Dan <14043624+delivrance@users.noreply.github.com>
Date: Sat, 16 Mar 2019 20:15:25 +0100
Subject: [PATCH 1/4] Fix import order causing errors
---
pyrogram/api/errors/__init__.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pyrogram/api/errors/__init__.py b/pyrogram/api/errors/__init__.py
index 8a1dc699..ca65619c 100644
--- a/pyrogram/api/errors/__init__.py
+++ b/pyrogram/api/errors/__init__.py
@@ -16,5 +16,5 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from .error import UnknownError
from .exceptions import *
+from .error import UnknownError
From 5aa93b8287ca4fbe1de306296d47dd43f95abf5f Mon Sep 17 00:00:00 2001
From: bakatrouble
Date: Wed, 20 Mar 2019 16:20:38 +0300
Subject: [PATCH 2/4] Show TOS on signup just once while program is running
(#231)
---
pyrogram/client/client.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/pyrogram/client/client.py b/pyrogram/client/client.py
index 253cc754..737139ca 100644
--- a/pyrogram/client/client.py
+++ b/pyrogram/client/client.py
@@ -181,6 +181,8 @@ class Client(Methods, BaseClient):
Defaults to False (normal session).
"""
+ terms_of_service_displayed = False
+
def __init__(self,
session_name: str,
api_id: Union[int, str] = None,
@@ -591,8 +593,9 @@ class Client(Methods, BaseClient):
phone_code_hash = r.phone_code_hash
terms_of_service = r.terms_of_service
- if terms_of_service:
+ if terms_of_service and not Client.terms_of_service_displayed:
print("\n" + terms_of_service.text + "\n")
+ Client.terms_of_service_displayed = True
if self.force_sms:
self.send(
From ac591cf3c7e6f7da20d957d6f3f911dad6282cab Mon Sep 17 00:00:00 2001
From: Eric Solinas
Date: Wed, 20 Mar 2019 15:44:20 +0100
Subject: [PATCH 3/4] Add more Message convenience methods (#233)
* Added convenience methods
message.reply_animation
message.reply_audio
message.reply_cached_media
message.reply_chat_action
message.reply_contact
message.reply_document
message.reply_game
message.reply_inline_bot_result
message.reply_location
message.reply_media_group
message.reply_photo
message.reply_poll
message.reply_sticker
message.reply_venue
message.reply_video
message.reply_video_note
message.reply_voice
message.edit_caption
message.edit_media
message.edit_reply_markup
message.pin
fixed send_document docstrings while doing so
uniformed function declaration of send_poll
* Update style and small fixes
---
.../client/methods/messages/send_document.py | 2 +-
pyrogram/client/methods/messages/send_poll.py | 28 +-
.../types/messages_and_media/message.py | 1855 ++++++++++++++++-
3 files changed, 1854 insertions(+), 31 deletions(-)
diff --git a/pyrogram/client/methods/messages/send_document.py b/pyrogram/client/methods/messages/send_document.py
index 2693697a..343a63a3 100644
--- a/pyrogram/client/methods/messages/send_document.py
+++ b/pyrogram/client/methods/messages/send_document.py
@@ -60,7 +60,7 @@ class SendDocument(BaseClient):
pass an HTTP URL as a string for Telegram to get a file from the Internet, or
pass a file path as string to upload a new file that exists on your local machine.
- thumb (``str``):
+ thumb (``str``, *optional*):
Thumbnail of the file sent.
The thumbnail should be in JPEG format and less than 200 KB in size.
A thumbnail's width and height should not exceed 90 pixels.
diff --git a/pyrogram/client/methods/messages/send_poll.py b/pyrogram/client/methods/messages/send_poll.py
index 423f962d..c9525c83 100644
--- a/pyrogram/client/methods/messages/send_poll.py
+++ b/pyrogram/client/methods/messages/send_poll.py
@@ -24,20 +24,20 @@ from pyrogram.client.ext import BaseClient
class SendPoll(BaseClient):
- def send_poll \
- (self,
- chat_id: Union[int, str],
- question: str,
- options: List[str],
- disable_notification: bool = None,
- reply_to_message_id: int = None,
- reply_markup: Union[
- "pyrogram.InlineKeyboardMarkup",
- "pyrogram.ReplyKeyboardMarkup",
- "pyrogram.ReplyKeyboardRemove",
- "pyrogram.ForceReply"
- ] = None
- ) -> "pyrogram.Message":
+ def send_poll(
+ self,
+ chat_id: Union[int, str],
+ question: str,
+ options: List[str],
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "pyrogram.Message":
"""Use this method to send a new poll.
Args:
diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py
index 9a4e4314..558efd38 100644
--- a/pyrogram/client/types/messages_and_media/message.py
+++ b/pyrogram/client/types/messages_and_media/message.py
@@ -21,6 +21,8 @@ from typing import List, Match, Union
import pyrogram
from pyrogram.api import types
from pyrogram.api.errors import MessageIdsEmpty
+from pyrogram.client.ext import ChatAction
+from pyrogram.client.types.input_media import InputMedia
from .contact import Contact
from .location import Location
from .message_entity import MessageEntity
@@ -611,14 +613,16 @@ class Message(PyrogramType, Update):
return parsed_message
- def reply(self,
- text: str,
- quote: bool = None,
- parse_mode: str = "",
- disable_web_page_preview: bool = None,
- disable_notification: bool = None,
- reply_to_message_id: int = None,
- reply_markup=None):
+ def reply(
+ self,
+ text: str,
+ quote: bool = None,
+ parse_mode: str = "",
+ disable_web_page_preview: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup=None
+ ) -> "Message":
"""Bound method *reply* of :obj:`Message `.
Use as a shortcut for:
@@ -686,7 +690,1661 @@ class Message(PyrogramType, Update):
reply_markup=reply_markup
)
- def edit(self, text: str, parse_mode: str = "", disable_web_page_preview: bool = None, reply_markup=None):
+ def reply_animation(
+ self,
+ animation: str,
+ quote: bool = None,
+ caption: str = "",
+ parse_mode: str = "",
+ duration: int = 0,
+ width: int = 0,
+ height: int = 0,
+ thumb: str = None,
+ disable_notification: bool = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ reply_to_message_id: int = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_animation* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_animation(
+ chat_id=message.chat.id,
+ animation=animation
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_animation(animation)
+
+ Args:
+ animation (``str``):
+ Animation to send.
+ Pass a file_id as string to send an animation that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get an animation from the Internet, or
+ pass a file path as string to upload a new animation that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ caption (``str``, *optional*):
+ Animation caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ duration (``int``, *optional*):
+ Duration of sent animation in seconds.
+
+ width (``int``, *optional*):
+ Animation width.
+
+ height (``int``, *optional*):
+ Animation height.
+
+ thumb (``str``, *optional*):
+ Thumbnail of the animation file sent.
+ The thumbnail should be in JPEG format and less than 200 KB in size.
+ A thumbnail's width and height should not exceed 90 pixels.
+ Thumbnails can't be reused and can be only uploaded as a new file.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error `
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_animation(
+ chat_id=self.chat.id,
+ animation=animation,
+ caption=caption,
+ parse_mode=parse_mode,
+ duration=duration,
+ width=width,
+ height=height,
+ thumb=thumb,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_audio(
+ self,
+ audio: str,
+ quote: bool = None,
+ caption: str = "",
+ parse_mode: str = "",
+ duration: int = 0,
+ performer: str = None,
+ title: str = None,
+ thumb: str = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_audio* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_audio(
+ chat_id=message.chat.id,
+ audio=audio
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_audio(audio)
+
+ Args:
+ audio (``str``):
+ Audio file to send.
+ Pass a file_id as string to send an audio file that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get an audio file from the Internet, or
+ pass a file path as string to upload a new audio file that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ caption (``str``, *optional*):
+ Audio caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ duration (``int``, *optional*):
+ Duration of the audio in seconds.
+
+ performer (``str``, *optional*):
+ Performer.
+
+ title (``str``, *optional*):
+ Track name.
+
+ thumb (``str``, *optional*):
+ Thumbnail of the music file album cover.
+ The thumbnail should be in JPEG format and less than 200 KB in size.
+ A thumbnail's width and height should not exceed 90 pixels.
+ Thumbnails can't be reused and can be only uploaded as a new file.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error `
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_audio(
+ chat_id=self.chat.id,
+ audio=audio,
+ caption=caption,
+ parse_mode=parse_mode,
+ duration=duration,
+ performer=performer,
+ title=title,
+ thumb=thumb,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_cached_media(
+ self,
+ file_id: str,
+ quote: bool = None,
+ caption: str = "",
+ parse_mode: str = "",
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *reply_cached_media* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_cached_media(
+ chat_id=message.chat.id,
+ file_id=file_id
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_cached_media(file_id)
+
+ Args:
+ file_id (``str``):
+ Media to send.
+ Pass a file_id as string to send a media that exists on the Telegram servers.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ caption (``bool``, *optional*):
+ Media caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error `
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_cached_media(
+ chat_id=self.chat.id,
+ file_id=file_id,
+ caption=caption,
+ parse_mode=parse_mode,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup
+ )
+
+ def reply_chat_action(
+ self,
+ action: Union[ChatAction, str],
+ progress: int = 0
+ ) -> "Message":
+ """Bound method *reply_chat_action* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_chat_action(
+ chat_id=message.chat.id,
+ action="typing"
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_chat_action("typing")
+
+ Args:
+ action (:obj:`ChatAction ` | ``str``):
+ Type of action to broadcast.
+ Choose one from the :class:`ChatAction ` enumeration,
+ depending on what the user is about to receive.
+ You can also provide a string (e.g. "typing", "upload_photo", "record_audio", ...).
+
+ progress (``int``, *optional*):
+ Progress of the upload process.
+ Currently useless because official clients don't seem to be handling this.
+
+ Returns:
+ On success, True is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ ``ValueError`` if the provided string is not a valid ChatAction.
+ """
+ return self._client.send_chat_action(
+ chat_id=self.chat.id,
+ action=action,
+ progress=progress
+ )
+
+ def reply_contact(
+ self,
+ phone_number: str,
+ first_name: str,
+ quote: bool = None,
+ last_name: str = "",
+ vcard: str = "",
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *reply_contact* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_contact(
+ chat_id=message.chat.id,
+ phone_number=phone_number,
+ first_name=first_name
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_contact(phone_number, "Dan")
+
+ Args:
+ phone_number (``str``):
+ Contact's phone number.
+
+ first_name (``str``):
+ Contact's first name.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ last_name (``str``, *optional*):
+ Contact's last name.
+
+ vcard (``str``, *optional*):
+ Additional data about the contact in the form of a vCard, 0-2048 bytes
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_contact(
+ chat_id=self.chat.id,
+ phone_number=phone_number,
+ first_name=first_name,
+ last_name=last_name,
+ vcard=vcard,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup
+ )
+
+ def reply_document(
+ self,
+ document: str,
+ quote: bool = None,
+ thumb: str = None,
+ caption: str = "",
+ parse_mode: str = "",
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_document* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_document(
+ chat_id=message.chat.id,
+ document=document
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_document(document)
+
+ Args:
+ document (``str``):
+ File to send.
+ Pass a file_id as string to send a file that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get a file from the Internet, or
+ pass a file path as string to upload a new file that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ thumb (``str``, *optional*):
+ Thumbnail of the file sent.
+ The thumbnail should be in JPEG format and less than 200 KB in size.
+ A thumbnail's width and height should not exceed 90 pixels.
+ Thumbnails can't be reused and can be only uploaded as a new file.
+
+ caption (``str``, *optional*):
+ Document caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_document(
+ chat_id=self.chat.id,
+ document=document,
+ thumb=thumb,
+ caption=caption,
+ parse_mode=parse_mode,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_game(
+ self,
+ game_short_name: str,
+ quote: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *reply_game* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_game(
+ chat_id=message.chat.id,
+ game_short_name="lumberjack"
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_game("lumberjack")
+
+ Args:
+ game_short_name (``str``):
+ Short name of the game, serves as the unique identifier for the game. Set up your games via Botfather.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
+ An object for an inline keyboard. If empty, one ‘Play game_title’ button will be shown automatically.
+ If not empty, the first button must launch the game.
+
+ Returns:
+ On success, the sent :obj:`Message` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_game(
+ chat_id=self.chat.id,
+ game_short_name=game_short_name,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup
+ )
+
+ def reply_inline_bot_result(
+ self,
+ query_id: int,
+ result_id: str,
+ quote: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ hide_via: bool = None
+ ) -> "Message":
+ """Bound method *reply_inline_bot_result* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_inline_bot_result(
+ chat_id=message.chat.id,
+ query_id=query_id,
+ result_id=result_id
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_inline_bot_result(query_id, result_id)
+
+ Args:
+ query_id (``int``):
+ Unique identifier for the answered query.
+
+ result_id (``str``):
+ Unique identifier for the result that was chosen.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``bool``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ hide_via (``bool``):
+ Sends the message with *via @bot* hidden.
+
+ Returns:
+ On success, the sent Message is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_inline_bot_result(
+ chat_id=self.chat.id,
+ query_id=query_id,
+ result_id=result_id,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ hide_via=hide_via
+ )
+
+ def reply_location(
+ self,
+ latitude: float,
+ longitude: float,
+ quote: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *reply_location* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_location(
+ chat_id=message.chat.id,
+ latitude=41.890251,
+ longitude=12.492373
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_location(41.890251, 12.492373)
+
+ Args:
+ latitude (``float``):
+ Latitude of the location.
+
+ longitude (``float``):
+ Longitude of the location.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_location(
+ chat_id=self.chat.id,
+ latitude=latitude,
+ longitude=longitude,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup
+ )
+
+ def reply_media_group(
+ self,
+ media: List[Union["pyrogram.InputMediaPhoto", "pyrogram.InputMediaVideo"]],
+ quote: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None
+ ) -> "Message":
+ """Bound method *reply_media_group* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_media_group(
+ chat_id=message.chat.id,
+ media=list_of_media
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_media_group(list_of_media)
+
+ Args:
+ media (``list``):
+ A list containing either :obj:`InputMediaPhoto ` or
+ :obj:`InputMediaVideo ` objects
+ describing photos and videos to be sent, must include 2–10 items.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ Returns:
+ On success, a :obj:`Messages ` object is returned containing all the
+ single messages sent.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_media_group(
+ chat_id=self.chat.id,
+ media=media,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id
+ )
+
+ def reply_photo(
+ self,
+ photo: str,
+ quote: bool = None,
+ caption: str = "",
+ parse_mode: str = "",
+ ttl_seconds: int = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_photo* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_photo(
+ chat_id=message.chat.id,
+ photo=photo
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_photo(photo)
+
+ Args:
+ photo (``str``):
+ Photo to send.
+ Pass a file_id as string to send a photo that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get a photo from the Internet, or
+ pass a file path as string to upload a new photo that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ caption (``bool``, *optional*):
+ Photo caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ ttl_seconds (``int``, *optional*):
+ Self-Destruct Timer.
+ If you set a timer, the photo will self-destruct in *ttl_seconds*
+ seconds after it was viewed.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_photo(
+ chat_id=self.chat.id,
+ photo=photo,
+ caption=caption,
+ parse_mode=parse_mode,
+ ttl_seconds=ttl_seconds,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_poll(
+ self,
+ question: str,
+ options: List[str],
+ quote: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *reply_poll* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_poll(
+ chat_id=message.chat.id,
+ question="Is Pyrogram the best?",
+ options=["Yes", "Yes"]
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_poll("Is Pyrogram the best?", ["Yes", "Yes"])
+
+ Args:
+ question (``str``):
+ The poll question, as string.
+
+ options (List of ``str``):
+ The poll options, as list of strings (2 to 10 options are allowed).
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_poll(
+ chat_id=self.chat.id,
+ question=question,
+ options=options,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup
+ )
+
+ def reply_sticker(
+ self,
+ sticker: str,
+ quote: bool = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_sticker* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_sticker(
+ chat_id=message.chat.id,
+ sticker=sticker
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_sticker(sticker)
+
+ Args:
+ sticker (``str``):
+ Sticker to send.
+ Pass a file_id as string to send a sticker that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get a .webp sticker file from the Internet, or
+ pass a file path as string to upload a new sticker that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_sticker(
+ chat_id=self.chat.id,
+ sticker=sticker,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_venue(
+ self,
+ latitude: float,
+ longitude: float,
+ title: str,
+ address: str,
+ quote: bool = None,
+ foursquare_id: str = "",
+ foursquare_type: str = "",
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *reply_venue* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_venue(
+ chat_id=message.chat.id,
+ latitude=41.890251,
+ longitude=12.492373,
+ title="Coliseum",
+ address="Piazza del Colosseo, 1, 00184 Roma RM"
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_venue(41.890251, 12.492373, "Coliseum", "Piazza del Colosseo, 1, 00184 Roma RM")
+
+ Args:
+ latitude (``float``):
+ Latitude of the venue.
+
+ longitude (``float``):
+ Longitude of the venue.
+
+ title (``str``):
+ Name of the venue.
+
+ address (``str``):
+ Address of the venue.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ foursquare_id (``str``, *optional*):
+ Foursquare identifier of the venue.
+
+ foursquare_type (``str``, *optional*):
+ Foursquare type of the venue, if known.
+ (For example, "arts_entertainment/default", "arts_entertainment/aquarium" or "food/icecream".)
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_venue(
+ chat_id=self.chat.id,
+ latitude=latitude,
+ longitude=longitude,
+ title=title,
+ address=address,
+ foursquare_id=foursquare_id,
+ foursquare_type=foursquare_type,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup
+ )
+
+ def reply_video(
+ self,
+ video: str,
+ quote: bool = None,
+ caption: str = "",
+ parse_mode: str = "",
+ duration: int = 0,
+ width: int = 0,
+ height: int = 0,
+ thumb: str = None,
+ supports_streaming: bool = True,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_video* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_video(
+ chat_id=message.chat.id,
+ video=video
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_video(video)
+
+ Args:
+ video (``str``):
+ Video to send.
+ Pass a file_id as string to send a video that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get a video from the Internet, or
+ pass a file path as string to upload a new video that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ caption (``str``, *optional*):
+ Video caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ duration (``int``, *optional*):
+ Duration of sent video in seconds.
+
+ width (``int``, *optional*):
+ Video width.
+
+ height (``int``, *optional*):
+ Video height.
+
+ thumb (``str``, *optional*):
+ Thumbnail of the video sent.
+ The thumbnail should be in JPEG format and less than 200 KB in size.
+ A thumbnail's width and height should not exceed 90 pixels.
+ Thumbnails can't be reused and can be only uploaded as a new file.
+
+ supports_streaming (``bool``, *optional*):
+ Pass True, if the uploaded video is suitable for streaming.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message.
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_video(
+ chat_id=self.chat.id,
+ video=video,
+ caption=caption,
+ parse_mode=parse_mode,
+ duration=duration,
+ width=width,
+ height=height,
+ thumb=thumb,
+ supports_streaming=supports_streaming,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_video_note(
+ self,
+ video_note: str,
+ quote: bool = None,
+ duration: int = 0,
+ length: int = 1,
+ thumb: str = None,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_video_note* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_video_note(
+ chat_id=message.chat.id,
+ video_note=video_note
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_video_note(video_note)
+
+ Args:
+ video_note (``str``):
+ Video note to send.
+ Pass a file_id as string to send a video note that exists on the Telegram servers, or
+ pass a file path as string to upload a new video note that exists on your local machine.
+ Sending video notes by a URL is currently unsupported.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ duration (``int``, *optional*):
+ Duration of sent video in seconds.
+
+ length (``int``, *optional*):
+ Video width and height.
+
+ thumb (``str``, *optional*):
+ Thumbnail of the video sent.
+ The thumbnail should be in JPEG format and less than 200 KB in size.
+ A thumbnail's width and height should not exceed 90 pixels.
+ Thumbnails can't be reused and can be only uploaded as a new file.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_video_note(
+ chat_id=self.chat.id,
+ video_note=video_note,
+ duration=duration,
+ length=length,
+ thumb=thumb,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def reply_voice(
+ self,
+ voice: str,
+ quote: bool = None,
+ caption: str = "",
+ parse_mode: str = "",
+ duration: int = 0,
+ disable_notification: bool = None,
+ reply_to_message_id: int = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
+ """Bound method *reply_voice* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.send_voice(
+ chat_id=message.chat.id,
+ voice=voice
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.reply_voice(voice)
+
+ Args:
+ voice (``str``):
+ Audio file to send.
+ Pass a file_id as string to send an audio that exists on the Telegram servers,
+ pass an HTTP URL as a string for Telegram to get an audio from the Internet, or
+ pass a file path as string to upload a new audio that exists on your local machine.
+
+ quote (``bool``, *optional*):
+ If ``True``, the message will be sent as a reply to this message.
+ If *reply_to_message_id* is passed, this parameter will be ignored.
+ Defaults to ``True`` in group chats and ``False`` in private chats.
+
+ caption (``str``, *optional*):
+ Voice message caption, 0-1024 characters.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your caption.
+ Defaults to Markdown.
+
+ duration (``int``, *optional*):
+ Duration of the voice message in seconds.
+
+ disable_notification (``bool``, *optional*):
+ Sends the message silently.
+ Users will receive a notification with no sound.
+
+ reply_to_message_id (``int``, *optional*):
+ If the message is a reply, ID of the original message
+
+ reply_markup (:obj:`InlineKeyboardMarkup` | :obj:`ReplyKeyboardMarkup` | :obj:`ReplyKeyboardRemove` | :obj:`ForceReply`, *optional*):
+ Additional interface options. An object for an inline keyboard, custom reply keyboard,
+ instructions to remove reply keyboard or to force a reply from the user.
+
+ progress (``callable``, *optional*):
+ Pass a callback function to view the upload progress.
+ The function must take *(client, current, total, \*args)* as positional arguments (look at the section
+ below for a detailed description).
+
+ progress_args (``tuple``, *optional*):
+ Extra custom arguments for the progress callback function. Useful, for example, if you want to pass
+ a chat_id and a message_id in order to edit a message with the updated progress.
+
+ Other Parameters:
+ client (:obj:`Client `):
+ The Client itself, useful when you want to call other API methods inside the callback function.
+
+ current (``int``):
+ The amount of bytes uploaded so far.
+
+ total (``int``):
+ The size of the file.
+
+ *args (``tuple``, *optional*):
+ Extra custom arguments as defined in the *progress_args* parameter.
+ You can either keep *\*args* or add every single extra argument in your function signature.
+
+ Returns:
+ On success, the sent :obj:`Message ` is returned.
+ In case the upload is deliberately stopped with :meth:`stop_transmission`, None is returned instead.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ if quote is None:
+ quote = self.chat.type != "private"
+
+ if reply_to_message_id is None and quote:
+ reply_to_message_id = self.message_id
+
+ return self._client.send_voice(
+ chat_id=self.chat.id,
+ voice=voice,
+ caption=caption,
+ parse_mode=parse_mode,
+ duration=duration,
+ disable_notification=disable_notification,
+ reply_to_message_id=reply_to_message_id,
+ reply_markup=reply_markup,
+ progress=progress,
+ progress_args=progress_args
+ )
+
+ def edit(
+ self,
+ text: str,
+ parse_mode: str = "",
+ disable_web_page_preview: bool = None,
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
"""Bound method *edit* of :obj:`Message `
Use as a shortcut for:
@@ -696,7 +2354,7 @@ class Message(PyrogramType, Update):
client.edit_message_text(
chat_id=message.chat.id,
message_id=message.message_id,
- text="hello",
+ text="hello"
)
Example:
@@ -734,9 +2392,134 @@ class Message(PyrogramType, Update):
reply_markup=reply_markup
)
- def forward(self,
- chat_id: int or str,
- disable_notification: bool = None):
+ def edit_caption(
+ self,
+ caption: str,
+ parse_mode: str = "",
+ reply_markup: Union[
+ "pyrogram.InlineKeyboardMarkup",
+ "pyrogram.ReplyKeyboardMarkup",
+ "pyrogram.ReplyKeyboardRemove",
+ "pyrogram.ForceReply"
+ ] = None
+ ) -> "Message":
+ """Bound method *edit_caption* of :obj:`Message `
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.edit_message_caption(
+ chat_id=message.chat.id,
+ message_id=message.message_id,
+ caption="hello"
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.edit_caption("hello")
+
+ Args:
+ caption (``str``):
+ New caption of the message.
+
+ parse_mode (``str``, *optional*):
+ Use :obj:`MARKDOWN ` or :obj:`HTML `
+ if you want Telegram apps to show bold, italic, fixed-width text or inline URLs in your message.
+ Defaults to Markdown.
+
+ reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
+ An InlineKeyboardMarkup object.
+
+ Returns:
+ On success, the edited :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ return self._client.edit_message_caption(
+ chat_id=self.chat.id,
+ message_id=self.message_id,
+ caption=caption,
+ parse_mode=parse_mode,
+ reply_markup=reply_markup
+ )
+
+ def edit_media(self, media: InputMedia, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message":
+ """Bound method *edit_media* of :obj:`Message `
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.edit_message_media(
+ chat_id=message.chat.id,
+ message_id=message.message_id,
+ media=media
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.edit_media(media)
+
+ Args:
+ media (:obj:`InputMediaAnimation` | :obj:`InputMediaAudio` | :obj:`InputMediaDocument` | :obj:`InputMediaPhoto` | :obj:`InputMediaVideo`)
+ One of the InputMedia objects describing an animation, audio, document, photo or video.
+
+ reply_markup (:obj:`InlineKeyboardMarkup`, *optional*):
+ An InlineKeyboardMarkup object.
+
+ Returns:
+ On success, the edited :obj:`Message ` is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ return self._client.edit_message_media(
+ chat_id=self.chat.id,
+ message_id=self.message_id,
+ media=media,
+ reply_markup=reply_markup
+ )
+
+ def edit_reply_markup(self, reply_markup: "pyrogram.InlineKeyboardMarkup" = None) -> "Message":
+ """Bound method *edit_reply_markup* of :obj:`Message `
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.edit_message_reply_markup(
+ chat_id=message.chat.id,
+ message_id=message.message_id,
+ reply_markup=inline_reply_markup
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.edit_reply_markup(inline_reply_markup)
+
+ Args:
+ reply_markup (:obj:`InlineKeyboardMarkup`):
+ An InlineKeyboardMarkup object.
+
+ Returns:
+ On success, if edited message is sent by the bot, the edited
+ :obj:`Message ` is returned, otherwise True is returned.
+
+ Raises:
+ :class:`Error ` in case of a Telegram RPC error.
+ """
+ return self._client.edit_message_reply_markup(
+ chat_id=self.chat.id,
+ message_id=self.message_id,
+ reply_markup=reply_markup
+ )
+
+ def forward(self, chat_id: int or str, disable_notification: bool = None) -> "Message":
"""Bound method *forward* of :obj:`Message `.
Use as a shortcut for:
@@ -746,7 +2529,7 @@ class Message(PyrogramType, Update):
client.forward_messages(
chat_id=chat_id,
from_chat_id=message.chat.id,
- message_ids=message.message_id,
+ message_ids=message.message_id
)
Example:
@@ -875,7 +2658,7 @@ class Message(PyrogramType, Update):
``TimeoutError``: If, after clicking an inline button, the bot fails to answer within 10 seconds
"""
if isinstance(self.reply_markup, pyrogram.ReplyKeyboardMarkup):
- return self.reply(x)
+ return self.reply(x, quote=quote)
elif isinstance(self.reply_markup, pyrogram.InlineKeyboardMarkup):
if isinstance(x, int) and y is None:
try:
@@ -927,7 +2710,13 @@ class Message(PyrogramType, Update):
else:
raise ValueError("The message doesn't contain any keyboard")
- def download(self, file_name: str = "", block: bool = True, progress: callable = None, progress_args: tuple = ()):
+ def download(
+ self,
+ file_name: str = "",
+ block: bool = True,
+ progress: callable = None,
+ progress_args: tuple = ()
+ ) -> "Message":
"""Bound method *download* of :obj:`Message `.
Use as a shortcut for:
@@ -976,6 +2765,40 @@ class Message(PyrogramType, Update):
progress_args=progress_args,
)
+ def pin(self, disable_notification: bool = None) -> "Message":
+ """Bound method *pin* of :obj:`Message `.
+
+ Use as a shortcut for:
+
+ .. code-block:: python
+
+ client.pin_chat_message(
+ chat_id=message.chat.id,
+ message_id=message_id
+ )
+
+ Example:
+ .. code-block:: python
+
+ message.pin()
+
+ Args:
+ disable_notification (``bool``):
+ Pass True, if it is not necessary to send a notification to all chat members about the new pinned
+ message. Notifications are always disabled in channels.
+
+ Returns:
+ True on success.
+
+ Raises:
+ :class:`Error `
+ """
+ return self._client.pin_chat_message(
+ chat_id=self.chat.id,
+ message_id=self.message_id,
+ disable_notification=disable_notification
+ )
+
class Str(str):
def __init__(self, *args):
From 081b9b280a2a42d451b9a86b3daaa415bed6bfcd Mon Sep 17 00:00:00 2001
From: bakatrouble
Date: Thu, 21 Mar 2019 15:53:07 +0300
Subject: [PATCH 4/4] Add ability to forward messages as copies (#227)
* Add ability to forward messages as copies
* Add Messages.forward() method
* Update and clean up code
---
.../methods/messages/forward_messages.py | 85 +++++---
.../client/methods/messages/send_contact.py | 8 +-
pyrogram/client/style/html.py | 2 +-
pyrogram/client/style/markdown.py | 2 +-
.../types/messages_and_media/message.py | 181 ++++++++++++++----
.../types/messages_and_media/messages.py | 54 +++++-
pyrogram/client/types/pyrogram_type.py | 4 +-
7 files changed, 265 insertions(+), 71 deletions(-)
diff --git a/pyrogram/client/methods/messages/forward_messages.py b/pyrogram/client/methods/messages/forward_messages.py
index 35d406ae..39db4bee 100644
--- a/pyrogram/client/methods/messages/forward_messages.py
+++ b/pyrogram/client/methods/messages/forward_messages.py
@@ -29,7 +29,9 @@ class ForwardMessages(BaseClient):
chat_id: Union[int, str],
from_chat_id: Union[int, str],
message_ids: Iterable[int],
- disable_notification: bool = None
+ disable_notification: bool = None,
+ as_copy: bool = False,
+ remove_caption: bool = False
) -> "pyrogram.Messages":
"""Use this method to forward messages of any kind.
@@ -52,6 +54,15 @@ class ForwardMessages(BaseClient):
Sends the message silently.
Users will receive a notification with no sound.
+ as_copy (``bool``, *optional*):
+ Pass True to forward messages without the forward header (i.e.: send a copy of the message content).
+ Defaults to False.
+
+ remove_caption (``bool``, *optional*):
+ If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the
+ message. Has no effect if *as_copy* is not enabled.
+ Defaults to False.
+
Returns:
On success and in case *message_ids* was an iterable, the returned value will be a list of the forwarded
:obj:`Messages ` even if a list contains just one element, otherwise if
@@ -61,35 +72,55 @@ class ForwardMessages(BaseClient):
Raises:
:class:`Error ` in case of a Telegram RPC error.
"""
+
is_iterable = not isinstance(message_ids, int)
message_ids = list(message_ids) if is_iterable else [message_ids]
- r = self.send(
- functions.messages.ForwardMessages(
- to_peer=self.resolve_peer(chat_id),
- from_peer=self.resolve_peer(from_chat_id),
- id=message_ids,
- silent=disable_notification or None,
- random_id=[self.rnd_id() for _ in message_ids]
- )
- )
-
- messages = []
-
- users = {i.id: i for i in r.users}
- chats = {i.id: i for i in r.chats}
-
- for i in r.updates:
- if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
- messages.append(
- pyrogram.Message._parse(
- self, i.message,
- users, chats
+ if as_copy:
+ sent_messages = []
+ for chunk in [message_ids[i:i + 200] for i in range(0, len(message_ids), 200)]:
+ messages = self.get_messages(chat_id=from_chat_id, message_ids=chunk) # type: pyrogram.Messages
+ for message in messages.messages:
+ sent_messages.append(
+ message.forward(
+ chat_id,
+ disable_notification=disable_notification,
+ as_copy=True,
+ remove_caption=remove_caption
+ )
)
+ return pyrogram.Messages(
+ client=self,
+ total_count=len(sent_messages),
+ messages=sent_messages
+ ) if is_iterable else sent_messages[0]
+ else:
+ r = self.send(
+ functions.messages.ForwardMessages(
+ to_peer=self.resolve_peer(chat_id),
+ from_peer=self.resolve_peer(from_chat_id),
+ id=message_ids,
+ silent=disable_notification or None,
+ random_id=[self.rnd_id() for _ in message_ids]
)
+ )
- return pyrogram.Messages(
- client=self,
- total_count=len(messages),
- messages=messages
- ) if is_iterable else messages[0]
+ forwarded_messages = []
+
+ users = {i.id: i for i in r.users}
+ chats = {i.id: i for i in r.chats}
+
+ for i in r.updates:
+ if isinstance(i, (types.UpdateNewMessage, types.UpdateNewChannelMessage)):
+ forwarded_messages.append(
+ pyrogram.Message._parse(
+ self, i.message,
+ users, chats
+ )
+ )
+
+ return pyrogram.Messages(
+ client=self,
+ total_count=len(forwarded_messages),
+ messages=forwarded_messages
+ ) if is_iterable else forwarded_messages[0]
diff --git a/pyrogram/client/methods/messages/send_contact.py b/pyrogram/client/methods/messages/send_contact.py
index bed9a37e..14ce61ec 100644
--- a/pyrogram/client/methods/messages/send_contact.py
+++ b/pyrogram/client/methods/messages/send_contact.py
@@ -29,8 +29,8 @@ class SendContact(BaseClient):
chat_id: Union[int, str],
phone_number: str,
first_name: str,
- last_name: str = "",
- vcard: str = "",
+ last_name: str = None,
+ vcard: str = None,
disable_notification: bool = None,
reply_to_message_id: int = None,
reply_markup: Union[
@@ -83,8 +83,8 @@ class SendContact(BaseClient):
media=types.InputMediaContact(
phone_number=phone_number,
first_name=first_name,
- last_name=last_name,
- vcard=vcard
+ last_name=last_name or "",
+ vcard=vcard or ""
),
message="",
silent=disable_notification or None,
diff --git a/pyrogram/client/style/html.py b/pyrogram/client/style/html.py
index 040e770b..211716e1 100644
--- a/pyrogram/client/style/html.py
+++ b/pyrogram/client/style/html.py
@@ -40,7 +40,7 @@ class HTML:
def parse(self, message: str):
entities = []
- message = utils.add_surrogates(str(message))
+ message = utils.add_surrogates(str(message or ""))
offset = 0
for match in self.HTML_RE.finditer(message):
diff --git a/pyrogram/client/style/markdown.py b/pyrogram/client/style/markdown.py
index 04ce95c8..45037a35 100644
--- a/pyrogram/client/style/markdown.py
+++ b/pyrogram/client/style/markdown.py
@@ -56,7 +56,7 @@ class Markdown:
self.peers_by_id = peers_by_id
def parse(self, message: str):
- message = utils.add_surrogates(str(message)).strip()
+ message = utils.add_surrogates(str(message or "")).strip()
entities = []
offset = 0
diff --git a/pyrogram/client/types/messages_and_media/message.py b/pyrogram/client/types/messages_and_media/message.py
index 558efd38..4ead0d30 100644
--- a/pyrogram/client/types/messages_and_media/message.py
+++ b/pyrogram/client/types/messages_and_media/message.py
@@ -16,12 +16,13 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
+from functools import partial
from typing import List, Match, Union
import pyrogram
from pyrogram.api import types
from pyrogram.api.errors import MessageIdsEmpty
-from pyrogram.client.ext import ChatAction
+from pyrogram.client.ext import ChatAction, ParseMode
from pyrogram.client.types.input_media import InputMedia
from .contact import Contact
from .location import Location
@@ -33,6 +34,32 @@ from ..user_and_chats.chat import Chat
from ..user_and_chats.user import User
+class Str(str):
+ def __init__(self, *args):
+ super().__init__()
+
+ self._client = None
+ self._entities = None
+
+ def init(self, client, entities):
+ self._client = client
+ self._entities = entities
+
+ return self
+
+ @property
+ def text(self):
+ return self
+
+ @property
+ def markdown(self):
+ return self._client.markdown.unparse(self, self._entities)
+
+ @property
+ def html(self):
+ return self._client.html.unparse(self, self._entities)
+
+
class Message(PyrogramType, Update):
"""This object represents a message.
@@ -268,7 +295,7 @@ class Message(PyrogramType, Update):
edit_date: int = None,
media_group_id: str = None,
author_signature: str = None,
- text: str = None,
+ text: Str = None,
entities: List["pyrogram.MessageEntity"] = None,
caption_entities: List["pyrogram.MessageEntity"] = None,
audio: "pyrogram.Audio" = None,
@@ -280,7 +307,7 @@ class Message(PyrogramType, Update):
video: "pyrogram.Video" = None,
voice: "pyrogram.Voice" = None,
video_note: "pyrogram.VideoNote" = None,
- caption: str = None,
+ caption: Str = None,
contact: "pyrogram.Contact" = None,
location: "pyrogram.Location" = None,
venue: "pyrogram.Venue" = None,
@@ -2519,7 +2546,13 @@ class Message(PyrogramType, Update):
reply_markup=reply_markup
)
- def forward(self, chat_id: int or str, disable_notification: bool = None) -> "Message":
+ def forward(
+ self,
+ chat_id: int or str,
+ disable_notification: bool = None,
+ as_copy: bool = False,
+ remove_caption: bool = False
+ ) -> "Message":
"""Bound method *forward* of :obj:`Message `.
Use as a shortcut for:
@@ -2547,18 +2580,120 @@ class Message(PyrogramType, Update):
Sends the message silently.
Users will receive a notification with no sound.
+ as_copy (``bool``, *optional*):
+ Pass True to forward messages without the forward header (i.e.: send a copy of the message content).
+ Defaults to False.
+
+ remove_caption (``bool``, *optional*):
+ If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the
+ message. Has no effect if *as_copy* is not enabled.
+ Defaults to False.
+
Returns:
On success, the forwarded Message is returned.
Raises:
:class:`Error `
"""
- return self._client.forward_messages(
- chat_id=chat_id,
- from_chat_id=self.chat.id,
- message_ids=self.message_id,
- disable_notification=disable_notification
- )
+ if as_copy:
+ if self.service:
+ raise ValueError("Unable to copy service messages")
+
+ if self.game and not self._client.is_bot:
+ raise ValueError("Users cannot send messages with Game media type")
+
+ # TODO: Improve markdown parser. Currently html appears to be more stable, thus we use it here because users
+ # can"t choose.
+
+ if self.text:
+ return self._client.send_message(
+ chat_id,
+ text=self.text.html,
+ parse_mode="html",
+ disable_web_page_preview=not self.web_page,
+ disable_notification=disable_notification
+ )
+ elif self.media:
+ caption = self.caption.html if self.caption and not remove_caption else None
+
+ send_media = partial(
+ self._client.send_cached_media,
+ chat_id=chat_id,
+ disable_notification=disable_notification
+ )
+
+ if self.photo:
+ file_id = self.photo.sizes[-1].file_id
+ elif self.audio:
+ file_id = self.audio.file_id
+ elif self.document:
+ file_id = self.document.file_id
+ elif self.video:
+ file_id = self.video.file_id
+ elif self.animation:
+ file_id = self.animation.file_id
+ elif self.voice:
+ file_id = self.voice.file_id
+ elif self.sticker:
+ file_id = self.sticker.file_id
+ elif self.video_note:
+ file_id = self.video_note.file_id
+ elif self.contact:
+ return self._client.send_contact(
+ chat_id,
+ phone_number=self.contact.phone_number,
+ first_name=self.contact.first_name,
+ last_name=self.contact.last_name,
+ vcard=self.contact.vcard,
+ disable_notification=disable_notification
+ )
+ elif self.location:
+ return self._client.send_location(
+ chat_id,
+ latitude=self.location.latitude,
+ longitude=self.location.longitude,
+ disable_notification=disable_notification
+ )
+ elif self.venue:
+ return self._client.send_venue(
+ chat_id,
+ latitude=self.venue.location.latitude,
+ longitude=self.venue.location.longitude,
+ title=self.venue.title,
+ address=self.venue.address,
+ foursquare_id=self.venue.foursquare_id,
+ foursquare_type=self.venue.foursquare_type,
+ disable_notification=disable_notification
+ )
+ elif self.poll:
+ return self._client.send_poll(
+ chat_id,
+ question=self.poll.question,
+ options=[opt.text for opt in self.poll.options],
+ disable_notification=disable_notification
+ )
+ elif self.game:
+ return self._client.send_game(
+ chat_id,
+ game_short_name=self.game.short_name,
+ disable_notification=disable_notification
+ )
+ else:
+ raise ValueError("Unknown media type")
+
+ if self.sticker or self.video_note: # Sticker and VideoNote should have no caption
+ return send_media(file_id)
+ else:
+ return send_media(file_id=file_id, caption=caption, parse_mode=ParseMode.HTML)
+ else:
+ raise ValueError("Can't copy this message")
+ else:
+ return self._client.forward_messages(
+ chat_id=chat_id,
+ from_chat_id=self.chat.id,
+ message_ids=self.message_id,
+ disable_notification=disable_notification
+ )
def delete(self, revoke: bool = True):
"""Bound method *delete* of :obj:`Message `.
@@ -2798,29 +2933,3 @@ class Message(PyrogramType, Update):
message_id=self.message_id,
disable_notification=disable_notification
)
-
-
-class Str(str):
- def __init__(self, *args):
- super().__init__()
-
- self.client = None
- self.entities = None
-
- def init(self, client, entities):
- self.client = client
- self.entities = entities
-
- return self
-
- @property
- def text(self):
- return self
-
- @property
- def markdown(self):
- return self.client.markdown.unparse(self, self.entities)
-
- @property
- def html(self):
- return self.client.html.unparse(self, self.entities)
diff --git a/pyrogram/client/types/messages_and_media/messages.py b/pyrogram/client/types/messages_and_media/messages.py
index 9983a01d..aae31a82 100644
--- a/pyrogram/client/types/messages_and_media/messages.py
+++ b/pyrogram/client/types/messages_and_media/messages.py
@@ -16,7 +16,7 @@
# You should have received a copy of the GNU Lesser General Public License
# along with Pyrogram. If not, see .
-from typing import List
+from typing import List, Union
import pyrogram
from pyrogram.api import types
@@ -116,3 +116,55 @@ class Messages(PyrogramType, Update):
messages=parsed_messages,
client=client
)
+
+ def forward(
+ self,
+ chat_id: Union[int, str],
+ disable_notification: bool = None,
+ as_copy: bool = False,
+ remove_caption: bool = False
+ ):
+ """Bound method *forward* of :obj:`Message `.
+
+ Args:
+ chat_id (``int`` | ``str``):
+ Unique identifier (int) or username (str) of the target chat.
+ For your personal cloud (Saved Messages) you can simply use "me" or "self".
+ For a contact that exists in your Telegram address book you can use his phone number (str).
+
+ disable_notification (``bool``, *optional*):
+ Sends messages silently.
+ Users will receive a notification with no sound.
+
+ as_copy (``bool``, *optional*):
+ Pass True to forward messages without the forward header (i.e.: send a copy of the message content).
+ Defaults to False.
+
+ remove_caption (``bool``, *optional*):
+ If set to True and *as_copy* is enabled as well, media captions are not preserved when copying the
+ message. Has no effect if *as_copy* is not enabled.
+ Defaults to False.
+
+ Returns:
+ On success, a :class:`Messages ` containing forwarded messages is returned.
+
+ Raises:
+ :class:`Error `
+ """
+ forwarded_messages = []
+
+ for message in self.messages:
+ forwarded_messages.append(
+ message.forward(
+ chat_id=chat_id,
+ as_copy=as_copy,
+ disable_notification=disable_notification,
+ remove_caption=remove_caption
+ )
+ )
+
+ return Messages(
+ total_count=len(forwarded_messages),
+ messages=forwarded_messages,
+ client=self._client
+ )
diff --git a/pyrogram/client/types/pyrogram_type.py b/pyrogram/client/types/pyrogram_type.py
index d746e6a7..af828926 100644
--- a/pyrogram/client/types/pyrogram_type.py
+++ b/pyrogram/client/types/pyrogram_type.py
@@ -19,11 +19,13 @@
from collections import OrderedDict
from json import dumps
+import pyrogram
+
class PyrogramType:
__slots__ = ["_client"]
- def __init__(self, client):
+ def __init__(self, client: "pyrogram.client.ext.BaseClient"):
self._client = client
def __str__(self):