async def edit_chat_invite_link( self: "pyrogram.Client", chat_id: Union[int, str], invite_link: str, name: str = None, expire_date: datetime = None, member_limit: int = None, creates_join_request: bool = None) -> "types.ChatInviteLink": """Edit a non-primary invite link. 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 for the target chat or username of the target channel/supergroup (in the format @username). invite_link (``str``): The invite link to edit name (``str``, *optional*): Invite link name. expire_date (:py:obj:`~datetime.datetime`, *optional*): Point in time when the link will expire. Defaults to None (no change), pass None to set no expiration date. member_limit (``int``, *optional*): Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999. Defaults to None (no change), pass 0 to set no member limit. creates_join_request (``bool``, *optional*): True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified. Returns: :obj:`~pyrogram.types.ChatInviteLink`: On success, the new invite link is returned Example: .. code-block:: python # Edit the member limit of a link link = await app.edit_chat_invite_link(chat_id, invite_link, member_limit=5) # Set no expiration date of a link link = await app.edit_chat_invite_link(chat_id, invite_link, expire_date=0) """ r = await self.invoke( raw.functions.messages.EditExportedChatInvite( peer=await self.resolve_peer(chat_id), link=invite_link, expire_date=utils.datetime_to_timestamp(expire_date), usage_limit=member_limit, title=name, request_needed=creates_join_request)) users = {i.id: i for i in r.users} return types.ChatInviteLink._parse(self, r.invite, users)
async def create_chat_invite_link( self: "pyrogram.Client", chat_id: Union[int, str], name: str = None, expire_date: datetime = None, member_limit: int = None, creates_join_request: bool = None) -> "types.ChatInviteLink": """Create an additional invite link for a chat. You must be an administrator in the chat for this to work and must have the appropriate admin rights. The link can be revoked using the method :meth:`~pyrogram.Client.revoke_chat_invite_link`. Parameters: chat_id (``int`` | ``str``): Unique identifier for the target chat or username of the target channel/supergroup (in the format @username). name (``str``, *optional*): Invite link name. expire_date (:py:obj:`~datetime.datetime`, *optional*): Point in time when the link will expire. Defaults to None (no expiration date). member_limit (``int``, *optional*): Maximum number of users that can be members of the chat simultaneously after joining the chat via this invite link; 1-99999. Defaults to None (no member limit). creates_join_request (``bool``, *optional*): True, if users joining the chat via the link need to be approved by chat administrators. If True, member_limit can't be specified. Returns: :obj:`~pyrogram.types.ChatInviteLink`: On success, the new invite link is returned. Example: .. code-block:: python # Create a new link without limits link = await app.create_chat_invite_link(chat_id) # Create a new link for up to 3 new users link = await app.create_chat_invite_link(chat_id, member_limit=3) """ r = await self.invoke( raw.functions.messages.ExportChatInvite( peer=await self.resolve_peer(chat_id), expire_date=utils.datetime_to_timestamp(expire_date), usage_limit=member_limit, title=name, request_needed=creates_join_request)) return types.ChatInviteLink._parse(self, r)
async def get_chunk( *, client: "pyrogram.Client", chat_id: Union[int, str], limit: int = 0, offset: int = 0, from_message_id: int = 0, from_date: datetime = utils.zero_datetime() ): messages = await client.invoke( raw.functions.messages.GetHistory( peer=await client.resolve_peer(chat_id), offset_id=from_message_id, offset_date=utils.datetime_to_timestamp(from_date), add_offset=offset, limit=limit, max_id=0, min_id=0, hash=0 ), sleep_threshold=60 ) return await utils.parse_messages(client, messages, replies=0)
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_animation( self: "pyrogram.Client", chat_id: Union[int, str], animation: Union[str, BinaryIO], caption: str = "", unsave: bool = False, parse_mode: Optional["enums.ParseMode"] = None, caption_entities: List["types.MessageEntity"] = None, 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: datetime = 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 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. 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 (: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*. 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 (: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. 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 await app.send_animation("me", "animation.gif") # Add caption to the animation await app.send_animation("me", "animation.gif", caption="animation caption") # Unsave the animation once is sent await app.send_animation("me", "animation.gif", unsave=True) # Keep track of the progress while uploading async def progress(current, total): print(f"{current * 100 / total:.1f}%") await 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, FileType.ANIMATION) 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(file_name or 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=file_name or animation.name), raw.types.DocumentAttributeAnimated() ]) while True: try: r = await self.invoke( 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=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))) except FilePartMissing as e: await self.save_file(animation, file_id=file.id, file_part=e.value) 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, FileType.ANIMATION).id await self.invoke( raw.functions.messages.SaveGif( id=document_id, unsave=True)) return message except StopTransmission: return None
async def send_dice( self: "pyrogram.Client", chat_id: Union[int, str], emoji: str = "🎲", 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 a dice with a random value from 1 to 6. 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). emoji (``str``, *optional*): Emoji on which the dice throw animation is based. Currently, must be one of "🎲", "🎯", "🏀", "⚽", "🎳", or "🎰". Dice can have values 1-6 for "🎲", "🎯" and "🎳", values 1-5 for "🏀" and "⚽", and values 1-64 for "🎰". Defaults to "🎲". 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 dice message is returned. Example: .. code-block:: python # Send a dice await app.send_dice(chat_id) # Send a dart await app.send_dice(chat_id, "🎯") # Send a basketball await app.send_dice(chat_id, "🏀") """ r = await self.invoke( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaDice(emoticon=emoji), 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, message="")) 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_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 send_photo( self: "pyrogram.Client", chat_id: Union[int, str], photo: Union[str, BinaryIO], caption: str = "", parse_mode: Optional["enums.ParseMode"] = None, caption_entities: List["types.MessageEntity"] = None, ttl_seconds: int = 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, progress: Callable = None, progress_args: tuple = () ) -> Optional["types.Message"]: """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. caption (``str``, *optional*): Photo 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*. 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 (: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. 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 await app.send_photo("me", "photo.jpg") # Send photo by uploading from URL await app.send_photo("me", "https://example.com/example.jpg) # Add caption to a photo await app.send_photo("me", "photo.jpg", caption="Caption") # Send self-destructing photo await 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, FileType.PHOTO, ttl_seconds=ttl_seconds) 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.invoke( 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=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))) except FilePartMissing as e: await self.save_file(photo, file_id=file.id, file_part=e.value) 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 forward_messages( self: "pyrogram.Client", chat_id: Union[int, str], from_chat_id: Union[int, str], message_ids: Union[int, Iterable[int]], disable_notification: bool = None, schedule_date: datetime = None, protect_content: bool = None ) -> Union["types.Message", List["types.Message"]]: """Forward messages of any kind. 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 message 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_ids (``int`` | Iterable of ``int``): An iterable of message identifiers in the chat specified in *from_chat_id* or a single message id. disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. 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: :obj:`~pyrogram.types.Message` | List of :obj:`~pyrogram.types.Message`: In case *message_ids* was not a list, a single message is returned, otherwise a list of messages is returned. Example: .. code-block:: python # Forward a single message await app.forward_messages(to_chat, from_chat, 123) # Forward multiple messages at once await app.forward_messages(to_chat, from_chat, [1, 2, 3]) """ is_iterable = not isinstance(message_ids, int) message_ids = list(message_ids) if is_iterable else [message_ids] r = await self.invoke( raw.functions.messages.ForwardMessages( to_peer=await self.resolve_peer(chat_id), from_peer=await self.resolve_peer(from_chat_id), id=message_ids, silent=disable_notification or None, random_id=[self.rnd_id() for _ in message_ids], schedule_date=utils.datetime_to_timestamp(schedule_date), noforwards=protect_content)) 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, (raw.types.UpdateNewMessage, raw.types.UpdateNewChannelMessage, raw.types.UpdateNewScheduledMessage)): forwarded_messages.append(await types.Message._parse( self, i.message, users, chats)) return types.List( forwarded_messages) if is_iterable else forwarded_messages[0]
async def send_location( self: "pyrogram.Client", chat_id: Union[int, str], latitude: float, longitude: float, 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 ) -> "types.Message": """Send points on the map. 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). latitude (``float``): Latitude of the location. longitude (``float``): Longitude of the location. 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 location message is returned. Example: .. code-block:: python app.send_location("me", latitude, longitude) """ r = await self.invoke( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaGeoPoint( geo_point=raw.types.InputGeoPoint(lat=latitude, long=longitude)), message="", 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)) 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_poll( self: "pyrogram.Client", chat_id: Union[int, str], question: str, options: List[str], is_anonymous: bool = True, type: "enums.PollType" = enums.PollType.REGULAR, allows_multiple_answers: bool = None, correct_option_id: int = None, explanation: str = None, explanation_parse_mode: "enums.ParseMode" = None, explanation_entities: List["types.MessageEntity"] = None, open_period: int = None, close_date: datetime = None, is_closed: bool = None, disable_notification: bool = None, protect_content: bool = None, reply_to_message_id: int = None, schedule_date: datetime = None, reply_markup: Union["types.InlineKeyboardMarkup", "types.ReplyKeyboardMarkup", "types.ReplyKeyboardRemove", "types.ForceReply"] = None ) -> "types.Message": """Send a new poll. 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). question (``str``): Poll question, 1-255 characters. options (List of ``str``): List of answer options, 2-10 strings 1-100 characters each. is_anonymous (``bool``, *optional*): True, if the poll needs to be anonymous. Defaults to True. type (:obj`~pyrogram.enums.PollType`, *optional*): Poll type, :obj:`~pyrogram.enums.PollType.QUIZ` or :obj:`~pyrogram.enums.PollType.REGULAR`. Defaults to :obj:`~pyrogram.enums.PollType.REGULAR`. allows_multiple_answers (``bool``, *optional*): True, if the poll allows multiple answers, ignored for polls in quiz mode. Defaults to False. correct_option_id (``int``, *optional*): 0-based identifier of the correct answer option, required for polls in quiz mode. explanation (``str``, *optional*): Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing. explanation_parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. explanation_entities (List of :obj:`~pyrogram.types.MessageEntity`): List of special entities that appear in the poll explanation, which can be specified instead of *parse_mode*. open_period (``int``, *optional*): Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with *close_date*. close_date (:py:obj:`~datetime.datetime`, *optional*): Point in time when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with *open_period*. is_closed (``bool``, *optional*): Pass True, if the poll needs to be immediately closed. This can be useful for poll preview. disable_notification (``bool``, *optional*): Sends the message silently. Users will receive a notification with no sound. protect_content (``bool``, *optional*): Protects the contents of the sent message from forwarding and saving. 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. 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 poll message is returned. Example: .. code-block:: python await app.send_poll(chat_id, "Is this a poll question?", ["Yes", "No", "Maybe"]) """ message, entities = (await utils.parse_text_entities( self, explanation, explanation_parse_mode, explanation_entities)).values() # For some reason passing None or [] as solution_entities will lead to INPUT_CONSTRUCTOR_INVALID_00 # Add a dummy message entity with no length as workaround solution = message or None solution_entities = entities or ([ raw.types.MessageEntityBold(offset=0, length=0) ] if solution else None) r = await self.invoke( raw.functions.messages.SendMedia( peer=await self.resolve_peer(chat_id), media=raw.types.InputMediaPoll( poll=raw.types.Poll( id=self.rnd_id(), question=question, answers=[ raw.types.PollAnswer(text=text, option=bytes([i])) for i, text in enumerate(options) ], closed=is_closed, public_voters=not is_anonymous, multiple_choice=allows_multiple_answers, quiz=type == enums.PollType.QUIZ or False, close_period=open_period, close_date=utils.datetime_to_timestamp(close_date)), correct_answers=[bytes([correct_option_id])] if correct_option_id is not None else None, solution=solution, solution_entities=solution_entities), message="", silent=disable_notification, 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)) 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_video_note( self: "pyrogram.Client", 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: datetime = 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 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 (: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. 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 await app.send_video_note("me", "video_note.mp4") # Set video note length await 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.invoke( 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=utils.datetime_to_timestamp( schedule_date), noforwards=protect_content, reply_markup=await reply_markup.write(self) if reply_markup else None, message="")) except FilePartMissing as e: await self.save_file(video_note, file_id=file.id, file_part=e.value) 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 get_dialogs( self: "pyrogram.Client", limit: int = 0 ) -> Optional[AsyncGenerator["types.Dialog", None]]: """Get a user's dialogs sequentially. Parameters: limit (``int``, *optional*): Limits the number of dialogs to be retrieved. By default, no limit is applied and all dialogs are returned. Returns: ``Generator``: A generator yielding :obj:`~pyrogram.types.Dialog` objects. Example: .. code-block:: python # Iterate through all dialogs async for dialog in app.get_dialogs(): print(dialog.chat.first_name or dialog.chat.title) """ current = 0 total = limit or (1 << 31) - 1 limit = min(100, total) offset_date = 0 offset_id = 0 offset_peer = raw.types.InputPeerEmpty() while True: r = await self.invoke( raw.functions.messages.GetDialogs( offset_date=offset_date, offset_id=offset_id, offset_peer=offset_peer, limit=limit, hash=0 ), sleep_threshold=60 ) users = {i.id: i for i in r.users} chats = {i.id: i for i in r.chats} messages = {} for message in r.messages: if isinstance(message, raw.types.MessageEmpty): continue chat_id = utils.get_peer_id(message.peer_id) messages[chat_id] = await types.Message._parse(self, message, users, chats) dialogs = [] for dialog in r.dialogs: if not isinstance(dialog, raw.types.Dialog): continue dialogs.append(types.Dialog._parse(self, dialog, messages, users, chats)) if not dialogs: return last = dialogs[-1] offset_id = last.top_message.id offset_date = utils.datetime_to_timestamp(last.top_message.date) offset_peer = await self.resolve_peer(last.chat.id) for dialog in dialogs: yield dialog current += 1 if current >= total: return
async def copy_media_group( self: "pyrogram.Client", 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: datetime = 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 (:py:obj:`~datetime.datetime`, *optional*): Date when the message will be automatically sent. Returns: List of :obj:`~pyrogram.types.Message`: On success, a list of copied messages is returned. Example: .. code-block:: python # Copy a media group await app.copy_media_group(to_chat, from_chat, 123) await app.copy_media_group(to_chat, from_chat, 123, captions="single caption") await app.copy_media_group(to_chat, from_chat, 123, 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.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) ), 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 restrict_chat_member( self: "pyrogram.Client", chat_id: Union[int, str], user_id: Union[int, str], permissions: "types.ChatPermissions", until_date: datetime = utils.zero_datetime() ) -> "types.Chat": """Restrict a user in a supergroup. You must be an administrator in the supergroup for this to work and must have the appropriate admin rights. Pass True for all permissions to lift restrictions from a user. Parameters: chat_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target chat. user_id (``int`` | ``str``): Unique identifier (int) or username (str) of the target user. For a contact that exists in your Telegram address book you can use his phone number (str). permissions (:obj:`~pyrogram.types.ChatPermissions`): New user permissions. until_date (:py:obj:`~datetime.datetime`, *optional*): Date when the user will be unbanned. If user is banned for more than 366 days or less than 30 seconds from the current time they are considered to be banned forever. Defaults to epoch (ban forever). Returns: :obj:`~pyrogram.types.Chat`: On success, a chat object is returned. Example: .. code-block:: python from datetime import datetime, timedelta from pyrogram.types import ChatPermissions # Completely restrict chat member (mute) forever await app.restrict_chat_member(chat_id, user_id, ChatPermissions()) # Chat member muted for 24h await app.restrict_chat_member(chat_id, user_id, ChatPermissions(), datetime.now() + timedelta(days=1)) # Chat member can only send text messages await app.restrict_chat_member(chat_id, user_id, ChatPermissions(can_send_messages=True)) """ r = await self.invoke( raw.functions.channels.EditBanned( channel=await self.resolve_peer(chat_id), participant=await self.resolve_peer(user_id), banned_rights=raw.types.ChatBannedRights( until_date=utils.datetime_to_timestamp(until_date), send_messages=not permissions.can_send_messages, send_media=not permissions.can_send_media_messages, send_stickers=not permissions.can_send_other_messages, send_gifs=not permissions.can_send_other_messages, send_games=not permissions.can_send_other_messages, send_inline=not permissions.can_send_other_messages, embed_links=not permissions.can_add_web_page_previews, send_polls=not permissions.can_send_polls, change_info=not permissions.can_change_info, invite_users=not permissions.can_invite_users, pin_messages=not permissions.can_pin_messages, ))) return types.Chat._parse_chat(self, r.chats[0])
async def send_message( self: "pyrogram.Client", chat_id: Union[int, str], text: str, parse_mode: Optional["enums.ParseMode"] = None, entities: List["types.MessageEntity"] = None, disable_web_page_preview: bool = 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 ) -> "types.Message": """Send text 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). text (``str``): Text of the message to be sent. parse_mode (:obj:`~pyrogram.enums.ParseMode`, *optional*): By default, texts are parsed using both Markdown and HTML styles. You can combine both syntaxes together. entities (List of :obj:`~pyrogram.types.MessageEntity`): List of special entities that appear in message text, which can be specified instead of *parse_mode*. disable_web_page_preview (``bool``, *optional*): Disables link previews for links in this message. 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 text message is returned. Example: .. code-block:: python # Simple example await app.send_message("me", "Message sent with **Pyrogram**!") # Disable web page previews await app.send_message("me", "https://docs.pyrogram.org", disable_web_page_preview=True) # Reply to a message using its id await app.send_message("me", "this is a reply", reply_to_message_id=123) .. code-block:: python # For bots only, send messages with keyboards attached from pyrogram.types import ( ReplyKeyboardMarkup, InlineKeyboardMarkup, InlineKeyboardButton) # Send a normal keyboard await app.send_message( chat_id, "Look at that button!", reply_markup=ReplyKeyboardMarkup([["Nice!"]])) # Send an inline keyboard await app.send_message( chat_id, "These are inline buttons", reply_markup=InlineKeyboardMarkup( [ [InlineKeyboardButton("Data", callback_data="callback_data")], [InlineKeyboardButton("Docs", url="https://docs.pyrogram.org")] ])) """ message, entities = (await utils.parse_text_entities(self, text, parse_mode, entities)).values() r = await self.invoke( raw.functions.messages.SendMessage( peer=await self.resolve_peer(chat_id), no_webpage=disable_web_page_preview or None, 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), reply_markup=await reply_markup.write(self) if reply_markup else None, message=message, entities=entities, noforwards=protect_content)) if isinstance(r, raw.types.UpdateShortSentMessage): peer = await self.resolve_peer(chat_id) peer_id = (peer.user_id if isinstance( peer, raw.types.InputPeerUser) else -peer.chat_id) return types.Message(id=r.id, chat=types.Chat(id=peer_id, type=enums.ChatType.PRIVATE, client=self), text=message, date=utils.timestamp_to_datetime(r.date), outgoing=r.out, reply_markup=reply_markup, entities=[ types.MessageEntity._parse( None, entity, {}) for entity in entities ], client=self) 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))