async def delete_profile_photos( self: "pyrogram.Client", photo_ids: Union[str, List[str]] ) -> bool: """Delete your own profile photos. Parameters: photo_ids (``str`` | List of ``str``): A single :obj:`~pyrogram.types.Photo` id as string or multiple ids as list of strings for deleting more than one photos at once. Returns: ``bool``: True on success. Example: .. code-block:: python # Get the photos to be deleted photos = await app.get_profile_photos("me") # Delete one photo await app.delete_profile_photos(photos[0].file_id) # Delete the rest of the photos await app.delete_profile_photos([p.file_id for p in photos[1:]]) """ photo_ids = photo_ids if isinstance(photo_ids, list) else [photo_ids] input_photos = [utils.get_input_media_from_file_id(i, FileType.PHOTO).id for i in photo_ids] return bool(await self.invoke( raw.functions.photos.DeletePhotos( id=input_photos ) ))
async def write(self): document = get_input_media_from_file_id(self.file_id) return raw.types.InputBotInlineResultDocument( id=self.id, type=self.type, title=self.title, description=self.description, document=document.id, send_message=( await self.input_message_content.write(self.reply_markup) if self.input_message_content else raw.types.InputBotInlineMessageMediaAuto( reply_markup=self.reply_markup.write() if self.reply_markup else None, **await(Parser(None)).parse(self.caption, self.parse_mode) ) ) )
async def write(self, client: "pyrogram.Client"): audio = utils.get_input_media_from_file_id(self.file_id, FileType.AUDIO) message, entities = (await utils.parse_text_entities( client, self.caption, self.parse_mode, self.caption_entities)).values() print(audio) return raw.types.InputBotInlineResultDocument( id=self.id, type=self.type, document=audio, send_message=(await self.input_message_content.write( client, self.reply_markup) if self.input_message_content else raw.types.InputBotInlineMessageMediaAuto( reply_markup=await self.reply_markup.write( client) if self.reply_markup else None, message=message, entities=entities)))
async def send_media_group( self, chat_id: Union[int, str], media: List[Union["types.InputMediaPhoto", "types.InputMediaVideo"]], disable_notification: bool = None, reply_to_message_id: int = None ) -> List["types.Message"]: """Send a group of photos or videos as an album. Parameters: 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). media (List of :obj:`~pyrogram.types.InputMediaPhoto` and :obj:`~pyrogram.types.InputMediaVideo`): A list describing photos and videos to be sent, must include 2–10 items. 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: List of :obj:`~pyrogram.types.Message`: On success, a list of the sent messages is returned. Example: .. code-block:: python from pyrogram import InputMediaPhoto, InputMediaVideo app.send_media_group( "me", [ InputMediaPhoto("photo1.jpg"), InputMediaPhoto("photo2.jpg", caption="photo caption"), InputMediaVideo("video.mp4", caption="a video") ] ) """ multi_media = [] for i in media: if isinstance(i, types.InputMediaPhoto): if os.path.isfile(i.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedPhoto( file=await self.save_file(i.media) ) ) ) media = raw.types.InputMediaPhoto( id=raw.types.InputPhoto( id=media.photo.id, access_hash=media.photo.access_hash, file_reference=media.photo.file_reference ) ) elif re.match("^https?://", i.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaPhotoExternal( url=i.media ) ) ) media = raw.types.InputMediaPhoto( id=raw.types.InputPhoto( id=media.photo.id, access_hash=media.photo.access_hash, file_reference=media.photo.file_reference ) ) else: media = utils.get_input_media_from_file_id(i.media, i.file_ref, 2) elif isinstance(i, types.InputMediaVideo): if os.path.isfile(i.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), mime_type=self.guess_mime_type(i.media) or "video/mp4", attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=i.supports_streaming or None, duration=i.duration, w=i.width, h=i.height ), raw.types.DocumentAttributeFilename(file_name=os.path.basename(i.media)) ] ) ) ) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference ) ) elif re.match("^https?://", i.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaDocumentExternal( url=i.media ) ) ) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference ) ) else: media = utils.get_input_media_from_file_id(i.media, i.file_ref, 4) multi_media.append( raw.types.InputSingleMedia( media=media, random_id=self.rnd_id(), **await self.parser.parse(i.caption, i.parse_mode) ) ) r = await self.send( raw.functions.messages.SendMultiMedia( peer=await self.resolve_peer(chat_id), multi_media=multi_media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id ), sleep_threshold=60 ) return await utils.parse_messages( self, raw.types.messages.Messages( messages=[m.message for m in filter( lambda u: isinstance(u, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage)), r.updates )], users=r.users, chats=r.chats ) )
async def send_cached_media( self, chat_id: Union[int, str], file_id: str, caption: str = "", parse_mode: Union[str, None] = object, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None ) -> Union["types.Message", None]: """Send any media stored on the Telegram servers using a file_id. This convenience method works with any valid file_id only. It does the same as calling the relevant method for sending media using a file_id, thus saving you from the hassle of using the correct method for the media the file_id is pointing to. Parameters: 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). file_id (``str``): Media to send. Pass a file_id as string to send a media that exists on the Telegram servers. caption (``str``, *optional*): Media caption, 0-1024 characters. parse_mode (``str``, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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: :obj:`~pyrogram.types.Message`: On success, the sent media message is returned. Example: .. code-block:: python app.send_cached_media("me", "CAADBAADzg4AAvLQYAEz_x2EOgdRwBYE") """ r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=utils.get_input_media_from_file_id(file_id), silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, **await self.parser.parse(caption, parse_mode))) for i in r.updates: if isinstance( i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage))
async def send_voice( self, chat_id: Union[int, str], voice: Union[str, BinaryIO], caption: str = "", parse_mode: Optional[str] = object, caption_entities: List["types.MessageEntity"] = None, duration: int = 0, disable_notification: bool = None, send_in_background: bool = True, reply_to_message_id: int = None, schedule_date: int = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None, progress: callable = None, progress_args: tuple = () ) -> Optional["types.Message"]: """Send audio files. Parameters: 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). voice (``str`` | ``BinaryIO``): 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, pass a file path as string to upload a new audio that exists on your local machine, or pass a binary file-like object with its attribute ".name" set for in-memory uploads. caption (``str``, *optional*): Voice message caption, 0-1024 characters. parse_mode (``str``, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. 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. send_in_background (``bool``, *optional*): Sends the message in background. Defaults to True. reply_to_message_id (``int``, *optional*): If the message is a reply, ID of the original message schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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 file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope; for example, a Message object or a Client instance in order to edit the message with the updated progress status. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total 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: :obj:`~pyrogram.types.Message` | ``None``: On success, the sent voice message is returned, otherwise, in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. Example: .. code-block:: python # Send voice note by uploading from local file app.send_voice("me", "voice.ogg") # Add caption to the voice note app.send_voice("me", "voice.ogg", caption="voice note") # Set voice note duration app.send_voice("me", "voice.ogg", duration=20) """ file = None try: if isinstance(voice, str): if os.path.isfile(voice): file = await self.save_file(voice, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(voice) or "audio/mpeg", file=file, attributes=[ raw.types.DocumentAttributeAudio(voice=True, duration=duration) ]) elif re.match("^https?://", voice): media = raw.types.InputMediaDocumentExternal(url=voice) else: media = utils.get_input_media_from_file_id( voice, FileType.VOICE) else: file = await self.save_file(voice, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(voice.name) or "audio/mpeg", file=file, attributes=[ raw.types.DocumentAttributeAudio(voice=True, duration=duration) ]) while True: try: r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=media, silent=disable_notification or None, background=send_in_background, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, **await utils.parse_text_entities(self, caption, parse_mode, caption_entities))) except FilePartMissing as e: await self.save_file(voice, file_id=file.id, file_part=e.x) else: for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage)) except StopTransmission: return None
async def send_animation( self, chat_id: Union[int, str], animation: Union[str, BinaryIO], file_ref: str = None, caption: str = "", unsave: bool = False, parse_mode: Union[str, None] = object, duration: int = 0, width: int = 0, height: int = 0, thumb: Union[str, BinaryIO] = None, file_name: str = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None, progress: callable = None, progress_args: tuple = () ) -> Union["types.Message", None]: """Send animation files (animation or H.264/MPEG-4 AVC video without sound). Parameters: 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). animation (``str`` | ``BinaryIO``): 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, pass a file path as string to upload a new animation that exists on your local machine, or pass a binary file-like object with its attribute ".name" set for in-memory uploads. file_ref (``str``, *optional*): A valid file reference obtained by a recently fetched media message. To be used in combination with a file id in case a file reference is needed. caption (``str``, *optional*): Animation caption, 0-1024 characters. unsave (``bool``, *optional*): By default, the server will save into your own collection any new animation you send. Pass True to automatically unsave the sent animation. Defaults to False. parse_mode (``str``, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. duration (``int``, *optional*): Duration of sent animation in seconds. width (``int``, *optional*): Animation width. height (``int``, *optional*): Animation height. thumb (``str`` | ``BinaryIO``, *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 320 pixels. Thumbnails can't be reused and can be only uploaded as a new file. file_name (``str``, *optional*): File name of the animation sent. Defaults to file's path basename. 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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 file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope; for example, a Message object or a Client instance in order to edit the message with the updated progress status. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total 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: :obj:`~pyrogram.types.Message` | ``None``: On success, the sent animation message is returned, otherwise, in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. Example: .. code-block:: python # Send animation by uploading from local file app.send_animation("me", "animation.gif") # Add caption to the animation app.send_animation("me", "animation.gif", caption="cat") # Unsave the animation once is sent app.send_animation("me", "animation.gif", unsave=True) # Keep track of the progress while uploading def progress(current, total): print(f"{current * 100 / total:.1f}%") app.send_animation("me", "animation.gif", progress=progress) """ file = None try: if isinstance(animation, str): if os.path.isfile(animation): thumb = await self.save_file(thumb) file = await self.save_file(animation, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(animation) or "video/mp4", file=file, thumb=thumb, attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=True, duration=duration, w=width, h=height), raw.types.DocumentAttributeFilename( file_name=file_name or os.path.basename(animation)), raw.types.DocumentAttributeAnimated() ]) elif re.match("^https?://", animation): media = raw.types.InputMediaDocumentExternal(url=animation) else: media = utils.get_input_media_from_file_id( animation, file_ref, 10) else: thumb = await self.save_file(thumb) file = await self.save_file(animation, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(animation.name) or "video/mp4", file=file, thumb=thumb, attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=True, duration=duration, w=width, h=height), raw.types.DocumentAttributeFilename( file_name=animation.name), raw.types.DocumentAttributeAnimated() ]) while True: try: r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, **await self.parser.parse(caption, parse_mode))) except FilePartMissing as e: await self.save_file(animation, file_id=file.id, file_part=e.x) else: for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): message = await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage)) if unsave: document = message.animation or message.document document_id = utils.get_input_media_from_file_id( document.file_id, document.file_ref).id await self.send( raw.functions.messages.SaveGif( id=document_id, unsave=True)) return message except StopTransmission: return None
async def set_chat_photo( self: "pyrogram.Client", chat_id: Union[int, str], *, photo: Union[str, BinaryIO] = None, video: Union[str, BinaryIO] = None, video_start_ts: float = None, ) -> bool: """Set a new chat photo or video (H.264/MPEG-4 AVC video, max 5 seconds). The ``photo`` and ``video`` arguments are mutually exclusive. Pass either one as named argument (see examples below). You must be an administrator in the chat for this to work and must have the appropriate admin rights. Parameters: chat_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target chat. photo (``str`` | ``BinaryIO``, *optional*): New chat photo. You can pass a :obj:`~pyrogram.types.Photo` file_id, a file path to upload a new photo from your local machine or a binary file-like object with its attribute ".name" set for in-memory uploads. video (``str`` | ``BinaryIO``, *optional*): New chat video. You can pass a :obj:`~pyrogram.types.Video` file_id, a file path to upload a new video from your local machine or a binary file-like object with its attribute ".name" set for in-memory uploads. video_start_ts (``float``, *optional*): The timestamp in seconds of the video frame to use as photo profile preview. Returns: ``bool``: True on success. Raises: ValueError: if a chat_id belongs to user. Example: .. code-block:: python # Set chat photo using a local file await app.set_chat_photo(chat_id, photo="photo.jpg") # Set chat photo using an exiting Photo file_id await app.set_chat_photo(chat_id, photo=photo.file_id) # Set chat video using a local file await app.set_chat_photo(chat_id, video="video.mp4") # Set chat photo using an exiting Video file_id await app.set_chat_photo(chat_id, video=video.file_id) """ peer = await self.resolve_peer(chat_id) if isinstance(photo, str): if os.path.isfile(photo): photo = raw.types.InputChatUploadedPhoto( file=await self.save_file(photo), video=await self.save_file(video), video_start_ts=video_start_ts, ) else: photo = utils.get_input_media_from_file_id( photo, FileType.PHOTO) photo = raw.types.InputChatPhoto(id=photo.id) else: photo = raw.types.InputChatUploadedPhoto( file=await self.save_file(photo), video=await self.save_file(video), video_start_ts=video_start_ts, ) if isinstance(peer, raw.types.InputPeerChat): await self.invoke( raw.functions.messages.EditChatPhoto( chat_id=peer.chat_id, photo=photo, )) elif isinstance(peer, raw.types.InputPeerChannel): await self.invoke( raw.functions.channels.EditPhoto(channel=peer, photo=photo)) else: raise ValueError(f'The chat_id "{chat_id}" belongs to a user') return True
async def send_document( self, chat_id: Union[int, str], document: Union[str, BinaryIO], thumb: Union[str, BinaryIO] = None, caption: str = "", parse_mode: Optional[str] = object, caption_entities: List["types.MessageEntity"] = None, file_name: str = None, force_document: bool = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, protect_content: bool = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None, progress: callable = None, progress_args: tuple = () ) -> Optional["types.Message"]: """Send generic files. Parameters: 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). document (``str`` | ``BinaryIO``): 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, pass a file path as string to upload a new file that exists on your local machine, or pass a binary file-like object with its attribute ".name" set for in-memory uploads. thumb (``str`` | ``BinaryIO``, *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 320 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*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. file_name (``str``, *optional*): File name of the document sent. Defaults to file's path basename. force_document (``bool``, *optional*): Pass True to force sending files as document. Useful for video files that need to be sent as document messages instead of video messages. Defaults to False. 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. protect_content (``bool``, *optional*): Protects the contents of the sent message from forwarding and saving. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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 file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope; for example, a Message object or a Client instance in order to edit the message with the updated progress status. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total 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: :obj:`~pyrogram.types.Message` | ``None``: On success, the sent document message is returned, otherwise, in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. Example: .. code-block:: python # Send document by uploading from local file app.send_document("me", "document.zip") # Add caption to the document file app.send_document("me", "document.zip", caption="document caption") # Keep track of the progress while uploading def progress(current, total): print(f"{current * 100 / total:.1f}%") app.send_document("me", "document.zip", progress=progress) """ file = None try: if isinstance(document, str): if os.path.isfile(document): thumb = await self.save_file(thumb) file = await self.save_file(document, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(document) or "application/zip", file=file, force_file=force_document or None, thumb=thumb, attributes=[ raw.types.DocumentAttributeFilename( file_name=file_name or os.path.basename(document)) ]) elif re.match("^https?://", document): media = raw.types.InputMediaDocumentExternal(url=document) else: media = utils.get_input_media_from_file_id( document, FileType.DOCUMENT) else: thumb = await self.save_file(thumb) file = await self.save_file(document, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(file_name or document.name) or "application/zip", file=file, thumb=thumb, attributes=[ raw.types.DocumentAttributeFilename( file_name=file_name or document.name) ]) while True: try: r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, noforwards=protect_content, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities(self, caption, parse_mode, caption_entities))) except FilePartMissing as e: await self.save_file(document, file_id=file.id, file_part=e.x) else: for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage)) except StopTransmission: return None
async def edit_inline_media( self, inline_message_id: str, media: "types.InputMedia", reply_markup: "types.InlineKeyboardMarkup" = None ) -> bool: """Edit inline animation, audio, document, photo or video messages. When the inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id or specify a URL. Parameters: inline_message_id (``str``): Required if *chat_id* and *message_id* are not specified. Identifier of the inline message. media (:obj:`~pyrogram.types.InputMedia`): One of the InputMedia objects describing an animation, audio, document, photo or video. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. Returns: ``bool``: On success, True is returned. Example: .. code-block:: python from pyrogram import InputMediaPhoto, InputMediaVideo, InputMediaAudio # Bots only # Replace the current media with a local photo app.edit_inline_media(inline_message_id, InputMediaPhoto("new_photo.jpg")) # Replace the current media with a local video app.edit_inline_media(inline_message_id, InputMediaVideo("new_video.mp4")) # Replace the current media with a local audio app.edit_inline_media(inline_message_id, InputMediaAudio("new_audio.mp3")) """ caption = media.caption parse_mode = media.parse_mode if isinstance(media, types.InputMediaPhoto): if re.match("^https?://", media.media): media = raw.types.InputMediaPhotoExternal( url=media.media ) else: media = utils.get_input_media_from_file_id(media.media, media.file_ref, 2) elif isinstance(media, types.InputMediaVideo): if re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal( url=media.media ) else: media = utils.get_input_media_from_file_id(media.media, media.file_ref, 4) elif isinstance(media, types.InputMediaAudio): if re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal( url=media.media ) else: media = utils.get_input_media_from_file_id(media.media, media.file_ref, 9) elif isinstance(media, types.InputMediaAnimation): if re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal( url=media.media ) else: media = utils.get_input_media_from_file_id(media.media, media.file_ref, 10) elif isinstance(media, types.InputMediaDocument): if re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal( url=media.media ) else: media = utils.get_input_media_from_file_id(media.media, media.file_ref, 5) unpacked = utils.unpack_inline_message_id(inline_message_id) dc_id = unpacked.dc_id session = await get_session(self, dc_id) return await session.send( raw.functions.messages.EditInlineBotMessage( id=unpacked, media=media, reply_markup=reply_markup.write() if reply_markup else None, **await self.parser.parse(caption, parse_mode) ) )
async def copy_media_group( self, chat_id: Union[int, str], from_chat_id: Union[int, str], message_id: int, captions: Union[List[str], str] = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, ) -> List["types.Message"]: """Copy a media group by providing one of the message ids. Parameters: 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). from_chat_id (``int`` | ``str``): Unique identifier (int) or username (str) of the source chat where the original media group was sent. 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). message_id (``int``): Message identifier in the chat specified in *from_chat_id*. captions (``str`` | List of ``str`` , *optional*): New caption for media, 0-1024 characters after entities parsing for each media. If not specified, the original caption is kept. Pass "" (empty string) to remove the caption. If a ``string`` is passed, it becomes a caption only for the first media. If a list of ``string`` passed, each element becomes caption for each media element. You can pass ``None`` in list to keep the original caption (see examples below). 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. Returns: List of :obj:`~pyrogram.types.Message`: On success, a list of copied messages is returned. Example: .. code-block:: python # Copy a media group app.copy_media_group("me", source_chat, message_id) app.copy_media_group("me", source_chat, message_id, captions="single caption") app.copy_media_group("me", source_chat, message_id, captions=["caption 1", None, ""]) """ media_group = await self.get_media_group(from_chat_id, message_id) multi_media = [] for i, message in enumerate(media_group): if message.photo: file_id = message.photo.file_id elif message.audio: file_id = message.audio.file_id elif message.document: file_id = message.document.file_id elif message.video: file_id = message.video.file_id else: raise ValueError("Message with this type can't be copied.") media = utils.get_input_media_from_file_id(file_id=file_id) multi_media.append( raw.types.InputSingleMedia( media=media, random_id=self.rnd_id(), **await self.parser.parse( captions[i] if isinstance(captions, list) and i < len(captions) and captions[i] else captions if isinstance(captions, str) and i == 0 else message.caption if message.caption and message.caption != "None" and not type(captions) is str else "") ) ) r = await self.send( raw.functions.messages.SendMultiMedia( peer=await self.resolve_peer(chat_id), multi_media=multi_media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, schedule_date=schedule_date ), sleep_threshold=60 ) return await utils.parse_messages( self, raw.types.messages.Messages( messages=[m.message for m in filter( lambda u: isinstance(u, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)), r.updates )], users=r.users, chats=r.chats ) )
async def send_video_note( self, chat_id: Union[int, str], video_note: Union[str, BinaryIO], duration: int = 0, length: int = 1, thumb: Union[str, BinaryIO] = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None, progress: callable = None, progress_args: tuple = () ) -> Optional["types.Message"]: """Send video messages. Parameters: 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). video_note (``str`` | ``BinaryIO``): Video note to send. Pass a file_id as string to send a video note that exists on the Telegram servers, pass a file path as string to upload a new video note that exists on your local machine, or pass a binary file-like object with its attribute ".name" set for in-memory uploads. Sending video notes by a URL is currently unsupported. duration (``int``, *optional*): Duration of sent video in seconds. length (``int``, *optional*): Video width and height. thumb (``str`` | ``BinaryIO``, *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 320 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 schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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 file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope; for example, a Message object or a Client instance in order to edit the message with the updated progress status. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total 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: :obj:`~pyrogram.types.Message` | ``None``: On success, the sent video note message is returned, otherwise, in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. Example: .. code-block:: python # Send video note by uploading from local file app.send_video_note("me", "video_note.mp4") # Set video note length app.send_video_note("me", "video_note.mp4", length=25) """ file = None try: if isinstance(video_note, str): if os.path.isfile(video_note): thumb = await self.save_file(thumb) file = await self.save_file(video_note, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(video_note) or "video/mp4", file=file, thumb=thumb, attributes=[ raw.types.DocumentAttributeVideo( round_message=True, duration=duration, w=length, h=length) ]) else: media = utils.get_input_media_from_file_id( video_note, FileType.VIDEO_NOTE) else: thumb = await self.save_file(thumb) file = await self.save_file(video_note, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(video_note.name) or "video/mp4", file=file, thumb=thumb, attributes=[ raw.types.DocumentAttributeVideo(round_message=True, duration=duration, w=length, h=length) ]) while True: try: r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, message="")) except FilePartMissing as e: await self.save_file(video_note, file_id=file.id, file_part=e.x) else: for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage)) except StopTransmission: return None
async def send_media_group( self: "pyrogram.Client", chat_id: Union[int, str], media: List[Union["types.InputMediaPhoto", "types.InputMediaVideo", "types.InputMediaAudio", "types.InputMediaDocument"]], disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: datetime = None, protect_content: bool = None, ) -> List["types.Message"]: """Send a group of photos or videos as an album. Parameters: 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). media (List of :obj:`~pyrogram.types.InputMediaPhoto`, :obj:`~pyrogram.types.InputMediaVideo`, :obj:`~pyrogram.types.InputMediaAudio` and :obj:`~pyrogram.types.InputMediaDocument`): A list describing photos and videos to be sent, must include 2–10 items. 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. schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. protect_content (``bool``, *optional*): Protects the contents of the sent message from forwarding and saving. Returns: List of :obj:`~pyrogram.types.Message`: On success, a list of the sent messages is returned. Example: .. code-block:: python from pyrogram.types import InputMediaPhoto, InputMediaVideo await app.send_media_group( "me", [ InputMediaPhoto("photo1.jpg"), InputMediaPhoto("photo2.jpg", caption="photo caption"), InputMediaVideo("video.mp4", caption="video caption") ] ) """ multi_media = [] for i in media: if isinstance(i, types.InputMediaPhoto): if isinstance(i.media, str): if os.path.isfile(i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedPhoto( file=await self.save_file(i.media)))) media = raw.types.InputMediaPhoto( id=raw.types.InputPhoto( id=media.photo.id, access_hash=media.photo.access_hash, file_reference=media.photo.file_reference)) elif re.match("^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaPhotoExternal( url=i.media))) media = raw.types.InputMediaPhoto( id=raw.types.InputPhoto( id=media.photo.id, access_hash=media.photo.access_hash, file_reference=media.photo.file_reference)) else: media = utils.get_input_media_from_file_id( i.media, FileType.PHOTO) else: media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedPhoto( file=await self.save_file(i.media)))) media = raw.types.InputMediaPhoto(id=raw.types.InputPhoto( id=media.photo.id, access_hash=media.photo.access_hash, file_reference=media.photo.file_reference)) elif isinstance(i, types.InputMediaVideo): if isinstance(i.media, str): if os.path.isfile(i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), mime_type=self.guess_mime_type(i.media) or "video/mp4", attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=i. supports_streaming or None, duration=i.duration, w=i.width, h=i.height), raw.types.DocumentAttributeFilename( file_name=os.path.basename( i.media)) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaDocumentExternal( url=i.media))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) else: media = utils.get_input_media_from_file_id( i.media, FileType.VIDEO) else: media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), mime_type=self.guess_mime_type( getattr(i.media, "name", "video.mp4")) or "video/mp4", attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=i.supports_streaming or None, duration=i.duration, w=i.width, h=i.height), raw.types.DocumentAttributeFilename( file_name=getattr( i.media, "name", "video.mp4")) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif isinstance(i, types.InputMediaAudio): if isinstance(i.media, str): if os.path.isfile(i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(i.media) or "audio/mpeg", file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), attributes=[ raw.types.DocumentAttributeAudio( duration=i.duration, performer=i.performer, title=i.title), raw.types.DocumentAttributeFilename( file_name=os.path.basename( i.media)) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaDocumentExternal( url=i.media))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) else: media = utils.get_input_media_from_file_id( i.media, FileType.AUDIO) else: media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type( getattr(i.media, "name", "audio.mp3")) or "audio/mpeg", file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), attributes=[ raw.types.DocumentAttributeAudio( duration=i.duration, performer=i.performer, title=i.title), raw.types.DocumentAttributeFilename( file_name=getattr( i.media, "name", "audio.mp3")) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif isinstance(i, types.InputMediaDocument): if isinstance(i.media, str): if os.path.isfile(i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(i.media) or "application/zip", file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), attributes=[ raw.types.DocumentAttributeFilename( file_name=os.path.basename( i.media)) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", i.media): media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaDocumentExternal( url=i.media))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) else: media = utils.get_input_media_from_file_id( i.media, FileType.DOCUMENT) else: media = await self.invoke( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type( getattr(i.media, "name", "file.zip")) or "application/zip", file=await self.save_file(i.media), thumb=await self.save_file(i.thumb), attributes=[ raw.types.DocumentAttributeFilename( file_name=getattr( i.media, "name", "file.zip")) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) else: raise ValueError( f"{i.__class__.__name__} is not a supported type for send_media_group" ) multi_media.append( raw.types.InputSingleMedia(media=media, random_id=self.rnd_id(), **await self.parser.parse( i.caption, i.parse_mode))) r = await self.invoke(raw.functions.messages.SendMultiMedia( peer=await self.resolve_peer(chat_id), multi_media=multi_media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content), sleep_threshold=60) return await utils.parse_messages( self, raw.types.messages.Messages(messages=[ m.message for m in filter( lambda u: isinstance(u, (raw.types.UpdateNewMessage, raw. types.UpdateNewChannelMessage, raw .types.UpdateNewScheduledMessage) ), r.updates) ], users=r.users, chats=r.chats))
async def send_photo( self, chat_id: Union[int, str], photo: Union[str, BinaryIO], file_ref: str = None, caption: str = "", parse_mode: Union[str, None] = object, ttl_seconds: int = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None, progress: callable = None, progress_args: tuple = () ) -> Union["types.Message", None]: """Send photos. Parameters: 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). photo (``str`` | ``BinaryIO``): 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, pass a file path as string to upload a new photo that exists on your local machine, or pass a binary file-like object with its attribute ".name" set for in-memory uploads. file_ref (``str``, *optional*): A valid file reference obtained by a recently fetched media message. To be used in combination with a file id in case a file reference is needed. caption (``str``, *optional*): Photo caption, 0-1024 characters. parse_mode (``str``, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. Pass "markdown" or "md" to enable Markdown-style parsing only. Pass "html" to enable HTML-style parsing only. Pass None to completely disable style parsing. 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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 file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope; for example, a Message object or a Client instance in order to edit the message with the updated progress status. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total 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: :obj:`~pyrogram.types.Message` | ``None``: On success, the sent photo message is returned, otherwise, in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. Example: .. code-block:: python # Send photo by uploading from local file app.send_photo("me", "photo.jpg") # Send photo by uploading from URL app.send_photo("me", "https://i.imgur.com/BQBTP7d.png") # Add caption to a photo app.send_photo("me", "photo.jpg", caption="Holidays!") # Send self-destructing photo app.send_photo("me", "photo.jpg", ttl_seconds=10) """ file = None try: if isinstance(photo, str): if os.path.isfile(photo): file = await self.save_file(photo, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedPhoto( file=file, ttl_seconds=ttl_seconds) elif re.match("^https?://", photo): media = raw.types.InputMediaPhotoExternal( url=photo, ttl_seconds=ttl_seconds) else: media = utils.get_input_media_from_file_id( photo, file_ref, 2) else: file = await self.save_file(photo, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedPhoto( file=file, ttl_seconds=ttl_seconds) while True: try: r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, **await self.parser.parse(caption, parse_mode))) except FilePartMissing as e: await self.save_file(photo, file_id=file.id, file_part=e.x) else: for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage)) except pyrogram.StopTransmission: return None
async def send_cached_media( self: "pyrogram.Client", chat_id: Union[int, str], file_id: str, caption: str = "", parse_mode: Optional["enums.ParseMode"] = None, caption_entities: List["types.MessageEntity"] = None, disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: datetime = None, protect_content: bool = None, reply_markup: Union[ "types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply" ] = None ) -> Optional["types.Message"]: """Send any media stored on the Telegram servers using a file_id. This convenience method works with any valid file_id only. It does the same as calling the relevant method for sending media using a file_id, thus saving you from the hassle of using the correct method for the media the file_id is pointing to. Parameters: 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). file_id (``str``): Media to send. Pass a file_id as string to send a media that exists on the Telegram servers. caption (``str``, *optional*): Media caption, 0-1024 characters. parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. caption_entities (List of :obj:`~pyrogram.types.MessageEntity`): List of special entities that appear in the caption, which can be specified instead of *parse_mode*. 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. schedule_date (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. protect_content (``bool``, *optional*): Protects the contents of the sent message from forwarding and saving. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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: :obj:`~pyrogram.types.Message`: On success, the sent media message is returned. Example: .. code-block:: python await app.send_cached_media("me", file_id) """ r = await self.invoke( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=utils.get_input_media_from_file_id(file_id), silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content, reply_markup=await reply_markup.write(self) if reply_markup else None, **await utils.parse_text_entities(self, caption, parse_mode, caption_entities) ) ) for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance(i, raw.types.UpdateNewScheduledMessage) )
async def edit_inline_media( self: "pyrogram.Client", inline_message_id: str, media: "types.InputMedia", reply_markup: "types.InlineKeyboardMarkup" = None) -> bool: """Edit inline animation, audio, document, photo or video messages. When the inline message is edited, a new file can't be uploaded. Use a previously uploaded file via its file_id or specify a URL. Parameters: inline_message_id (``str``): Required if *chat_id* and *message_id* are not specified. Identifier of the inline message. media (:obj:`~pyrogram.types.InputMedia`): One of the InputMedia objects describing an animation, audio, document, photo or video. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. Returns: ``bool``: On success, True is returned. Example: .. code-block:: python from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaAudio # Bots only # Replace the current media with a local photo await app.edit_inline_media(inline_message_id, InputMediaPhoto("new_photo.jpg")) # Replace the current media with a local video await app.edit_inline_media(inline_message_id, InputMediaVideo("new_video.mp4")) # Replace the current media with a local audio await app.edit_inline_media(inline_message_id, InputMediaAudio("new_audio.mp3")) """ caption = media.caption parse_mode = media.parse_mode if isinstance(media, types.InputMediaPhoto): if os.path.isfile(media.media): media = raw.types.InputMediaUploadedPhoto( file=await self.save_file(media.media)) elif re.match("^https?://", media.media): media = raw.types.InputMediaPhotoExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.PHOTO) elif isinstance(media, types.InputMediaVideo): if os.path.isfile(media.media): media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "video/mp4", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=media.supports_streaming or None, duration=media.duration, w=media.width, h=media.height), raw.types.DocumentAttributeFilename( file_name=os.path.basename(media.media)) ]) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.VIDEO) elif isinstance(media, types.InputMediaAudio): if os.path.isfile(media.media): media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "audio/mpeg", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeAudio( duration=media.duration, performer=media.performer, title=media.title), raw.types.DocumentAttributeFilename( file_name=os.path.basename(media.media)) ]) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.AUDIO) elif isinstance(media, types.InputMediaAnimation): if os.path.isfile(media.media): media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "video/mp4", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=True, duration=media.duration, w=media.width, h=media.height), raw.types.DocumentAttributeFilename( file_name=os.path.basename(media.media)), raw.types.DocumentAttributeAnimated() ]) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.ANIMATION) elif isinstance(media, types.InputMediaDocument): if os.path.isfile(media.media): media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "application/zip", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeFilename( file_name=os.path.basename(media.media)) ]) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.DOCUMENT) unpacked = utils.unpack_inline_message_id(inline_message_id) dc_id = unpacked.dc_id session = await get_session(self, dc_id) return await session.invoke( raw.functions.messages.EditInlineBotMessage( id=unpacked, media=media, reply_markup=await reply_markup.write(self) if reply_markup else None, **await self.parser.parse(caption, parse_mode)), sleep_threshold=self.sleep_threshold)
async def send_sticker( self, chat_id: Union[int, str], sticker: Union[str, BinaryIO], disable_notification: bool = None, reply_to_message_id: int = None, schedule_date: int = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None, progress: callable = None, progress_args: tuple = () ) -> Optional["types.Message"]: """Send static .webp or animated .tgs stickers. Parameters: 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). sticker (``str`` | ``BinaryIO``): 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, pass a file path as string to upload a new sticker that exists on your local machine, or pass a binary file-like object with its attribute ".name" set for in-memory uploads. 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. schedule_date (``int``, *optional*): Date when the message will be automatically sent. Unix time. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardMarkup` | :obj:`~pyrogram.types.ReplyKeyboardRemove` | :obj:`~pyrogram.types.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 file transmission progress. The function must take *(current, total)* as positional arguments (look at Other Parameters below for a detailed description) and will be called back each time a new file chunk has been successfully transmitted. progress_args (``tuple``, *optional*): Extra custom arguments for the progress callback function. You can pass anything you need to be available in the progress callback scope; for example, a Message object or a Client instance in order to edit the message with the updated progress status. Other Parameters: current (``int``): The amount of bytes transmitted so far. total (``int``): The total 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: :obj:`~pyrogram.types.Message` | ``None``: On success, the sent sticker message is returned, otherwise, in case the upload is deliberately stopped with :meth:`~pyrogram.Client.stop_transmission`, None is returned. Example: .. code-block:: python # Send sticker by uploading from local file app.send_sticker("me", "sticker.webp") # Send sticker using file_id app.send_sticker("me", "CAADBAADzg4AAvLQYAEz_x2EOgdRwBYE") """ file = None try: if isinstance(sticker, str): if os.path.isfile(sticker): file = await self.save_file(sticker, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(sticker) or "image/webp", file=file, attributes=[ raw.types.DocumentAttributeFilename( file_name=os.path.basename(sticker)) ]) elif re.match("^https?://", sticker): media = raw.types.InputMediaDocumentExternal(url=sticker) else: media = utils.get_input_media_from_file_id( sticker, FileType.STICKER) else: file = await self.save_file(sticker, progress=progress, progress_args=progress_args) media = raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(sticker.name) or "image/webp", file=file, attributes=[ raw.types.DocumentAttributeFilename( file_name=sticker.name) ]) while True: try: r = await self.send( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=media, silent=disable_notification or None, reply_to_msg_id=reply_to_message_id, random_id=self.rnd_id(), schedule_date=schedule_date, reply_markup=reply_markup.write() if reply_markup else None, message="")) except FilePartMissing as e: await self.save_file(sticker, file_id=file.id, file_part=e.x) else: for i in r.updates: if isinstance(i, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): return await types.Message._parse( self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats}, is_scheduled=isinstance( i, raw.types.UpdateNewScheduledMessage)) except StopTransmission: return None
async def edit_message_media( self, chat_id: Union[int, str], message_id: int, media: "types.InputMedia", reply_markup: "types.InlineKeyboardMarkup" = None, file_name: str = None) -> "types.Message": """Edit animation, audio, document, photo or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. Otherwise, the message type can be changed arbitrarily. Parameters: 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). message_id (``int``): Message identifier in the chat specified in chat_id. media (:obj:`~pyrogram.types.InputMedia`): One of the InputMedia objects describing an animation, audio, document, photo or video. reply_markup (:obj:`~pyrogram.types.InlineKeyboardMarkup`, *optional*): An InlineKeyboardMarkup object. file_name (``str``, *optional*): File name of the media to be sent. Not applicable to photos. Defaults to file's path basename. Returns: :obj:`~pyrogram.types.Message`: On success, the edited message is returned. Example: .. code-block:: python from pyrogram.types import InputMediaPhoto, InputMediaVideo, InputMediaAudio # Replace the current media with a local photo app.edit_message_media(chat_id, message_id, InputMediaPhoto("new_photo.jpg")) # Replace the current media with a local video app.edit_message_media(chat_id, message_id, InputMediaVideo("new_video.mp4")) # Replace the current media with a local audio app.edit_message_media(chat_id, message_id, InputMediaAudio("new_audio.mp3")) """ caption = media.caption parse_mode = media.parse_mode message, entities = None, None if caption is not None: message, entities = (await self.parser.parse(caption, parse_mode)).values() if isinstance(media, types.InputMediaPhoto): if os.path.isfile(media.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedPhoto( file=await self.save_file(media.media)))) media = raw.types.InputMediaPhoto(id=raw.types.InputPhoto( id=media.photo.id, access_hash=media.photo.access_hash, file_reference=media.photo.file_reference)) elif re.match("^https?://", media.media): media = raw.types.InputMediaPhotoExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.PHOTO) elif isinstance(media, types.InputMediaVideo): if os.path.isfile(media.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "video/mp4", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=media.supports_streaming or None, duration=media.duration, w=media.width, h=media.height), raw.types.DocumentAttributeFilename( file_name=file_name or os.path.basename(media.media)) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.VIDEO) elif isinstance(media, types.InputMediaAudio): if os.path.isfile(media.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "audio/mpeg", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeAudio( duration=media.duration, performer=media.performer, title=media.title), raw.types.DocumentAttributeFilename( file_name=file_name or os.path.basename(media.media)) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.AUDIO) elif isinstance(media, types.InputMediaAnimation): if os.path.isfile(media.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "video/mp4", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeVideo( supports_streaming=True, duration=media.duration, w=media.width, h=media.height), raw.types.DocumentAttributeFilename( file_name=file_name or os.path.basename(media.media)), raw.types.DocumentAttributeAnimated() ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.ANIMATION) elif isinstance(media, types.InputMediaDocument): if os.path.isfile(media.media): media = await self.send( raw.functions.messages.UploadMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaUploadedDocument( mime_type=self.guess_mime_type(media.media) or "application/zip", thumb=await self.save_file(media.thumb), file=await self.save_file(media.media), attributes=[ raw.types.DocumentAttributeFilename( file_name=file_name or os.path.basename(media.media)) ]))) media = raw.types.InputMediaDocument( id=raw.types.InputDocument( id=media.document.id, access_hash=media.document.access_hash, file_reference=media.document.file_reference)) elif re.match("^https?://", media.media): media = raw.types.InputMediaDocumentExternal(url=media.media) else: media = utils.get_input_media_from_file_id( media.media, FileType.DOCUMENT) r = await self.send( raw.functions.messages.EditMessage( peer=await self.resolve_peer(chat_id), id=message_id, media=media, reply_markup=await reply_markup.write(self) if reply_markup else None, message=message, entities=entities)) for i in r.updates: if isinstance(i, (raw.types.UpdateEditMessage, raw.types.UpdateEditChannelMessage)): return await types.Message._parse(self, i.message, {i.id: i for i in r.users}, {i.id: i for i in r.chats})