U
    hE                     @   s   d dl Z d dlZd dlZd dlZddlmZmZmZmZ ddl	m
Z
 ddlmZmZ dZejrjddlmZ G d	d
 d
e
ZG dd de
ZG dd dZdS )    N   )helpersutilserrorshints)RequestIter)types	functionsd      )TelegramClientc                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )_MessagesIterzL
    Common factor for all requests that need to iterate over messages.
    c                    s,  |r| j |I d H | _nd | _| jr.td| jrdt||}|rV|rV|| dkrVt|std}n"t||}|r|r|| dkrt| jr|r|d7 }n|sd}|r| j |I d H }| j |I d H | _	nd | _	| js|rt
 | _|d krt
 }nt|tr| n|}| jsDtjj|	p&d|d |dt
 |dd| _n2|r`tjj|dd| _n|
d k	rtjj| j|
|||ddddd	| _n|	d k	st|t
jr|rVt| j}|tjjkrd }nd | _	tjj| j|	pd|d |||dddd|d	| _t|t
jsv|rv|	sv|sv| j j| jd|d
2 z3 d H W }|jd | j_q46 n tjj| jd||dd|dd| _| jdkr|  | jI d H }t|t
jjr|j | _!nt"|dt#|j| _!t| j$d kr| jdkrdnd| _$| jr | j j%t&8  _%|| _%|| _'|| _(| jrdntd| _)d S )NzCannot reverse global searchr   inf r   )qfiltermin_datemax_dateoffset_rateoffset_peer	offset_idlimit)peerhash)	r   msg_idr   offset_date
add_offsetr   max_idmin_idr   )r   r   r   r   r   r   r   r   r   r   r   from_id)r   )r   r   r   r   r   r   r   r   counti  )*clientget_input_entityentityreverse
ValueErrormaxStopAsyncIterationfloatget_peer_idr   r   InputPeerEmptyZInputMessagesFilterEmpty
isinstancetyper	   messagesSearchGlobalRequestrequestZGetScheduledHistoryRequestZGetRepliesRequestr   _entity_type_EntityTypeUSERSearchRequestiter_messagesidr   ZGetHistoryRequestr   MessagesNotModifiedr    totalgetattrlen	wait_timer   _MAX_CHUNK_SIZEr   r   last_id)selfr#   r   r   r   	from_userr   r   r   searchreply_to	scheduledtymresult rE   </tmp/pip-unpacked-wheel-c81u5j2r/telethon/client/messages.py_init   s    









  
z_MessagesIter._initc                    sJ  t | jt| j_| jr4| jjtkr4| j| jj | j_| | jI d H }t|dt	|j
| _dd t|j|jD }| jrt|j
n|j
}|D ]Z}t|tjs| jr|j| jkrq| |s dS |j| _|| j|| j | j| qt|tj
jrdS |j
r"| js&|j
d j| jjkr&dS | jrB| | jd | ndS d S )Nr    c                 S   s   i | ]}t ||qS rE   r   r)   .0xrE   rE   rF   
<dictcomp>   s    z2_MessagesIter._load_next_chunk.<locals>.<dictcomp>Tr   )minleftr;   r/   r   r$   r   r!   r8   r9   r-   r7   	itertoolschainuserschatsreversedr+   r   MessageEmptyr   Z	sender_id_message_in_ranger5   r<   _finish_initr#   bufferappendZMessages_update_offset)r=   rentitiesr-   messagerE   rE   rF   _load_next_chunk   s8    

&z_MessagesIter._load_next_chunkc                 C   sJ   | j rF| jr*|j| jks$|j| jkrFdS n|j| jksB|j| jkrFdS dS )z
        Determine whether the given message is in the range or
        it should be ignored (and avoid loading more chunks).
        FT)r#   r$   r5   r<   r   r   )r=   r]   rE   rE   rF   rV      s    z_MessagesIter._message_in_rangec                 C   s   |j | j_| jr | j jd7  _t| jtjjr:d| j_n
|j	| j_
t| jtjjr|jrf|j| j_nt | j_t|dd| j_dS )zT
        After making the request, update its offset with the last message.
        r   NZ	next_rater   )r5   r/   r   r$   r+   r	   r-   r3   r   dater   r.   Z
input_chatr   r   r*   r8   r   )r=   Zlast_messageresponserE   rE   rF   rZ      s    


z_MessagesIter._update_offsetN)__name__
__module____qualname____doc__rG   r^   rV   rZ   rE   rE   rE   rF   r      s    >r   c                   @   s   e Zd Zdd Zdd ZdS )_IDsIterc                    s~   t || _| jrtt|n|| _d| _|r>| j|I d H nd | _	| j	rVt
| j	nd | _| jd krz| jdkrtdnd| _d S )Nr   i,  
   )r9   r7   r$   listrT   _ids_offsetr!   r"   _entityr   r0   _tyr:   r   )r=   r#   idsrE   rE   rF   rG     s    

z_IDsIter._initc                    sZ  | j | j| jt  }|st|  jt7  _d }| jtjjkrz | t	j
| j|I d H }W q tjk
r   tjt|}Y qX n2| t	j|I d H }| jr| j| jI d H }t|tjjr| jdd |D  d S dd t|j|jD }|jD ]R}t|tjs&|r4|j|kr4| jd  n|| j|| j | j| qd S )Nc                 s   s   | ]
}d V  qd S NrE   )rJ   _rE   rE   rF   	<genexpr>=  s     z,_IDsIter._load_next_chunk.<locals>.<genexpr>c                 S   s   i | ]}t ||qS rE   rH   rI   rE   rE   rF   rL   @  s    z-_IDsIter._load_next_chunk.<locals>.<dictcomp>)rh   ri   r;   r'   rk   r   r1   CHANNELr!   r	   channelsZGetMessagesRequestrj   r   ZMessageIdsEmptyErrorr   r-   r6   r9   	_get_peerr+   rX   extendrP   rQ   rR   rS   rU   peer_idrY   rW   )r=   rl   r   r[   r\   r]   rE   rE   rF   r^   (  s<    
z_IDsIter._load_next_chunkN)ra   rb   rc   rG   r^   rE   rE   rE   rF   re     s   re   c                   @   s<  e Zd ZdCddddddddddddddddedeeeeedded	eeed
dddZdej	dej
d f dddZeee_ddddddZdDddddddddddddddddddddddddej
e ej
ejej  eddeeej
d eeeddeej
d ej
e dd d!d"ZdEdddddddd#ddd$deeeedeed%d&d'd(ZdFddddddddddd)
dd*d+eedej
ejej  eddeej
d eddd,d-d.Zdd/ddd$ed0d1d2d3ZdGdddd4ddd$eeeed5d6d7Zddd8ddd9eed:d;d<ZdHdd=ddd9ed>d?d@Zddd8dAdBZdS )IMessageMethodsNr   F)r   r   r   r   r   r?   r   r>   r:   rl   r$   r@   rA   r   zhints.EntityLikezhints.DateLikezMtyping.Union[types.TypeMessagesFilter, typing.Type[types.TypeMessagesFilter]]z'typing.Union[int, typing.Sequence[int]]z%typing.Union[_MessagesIter, _IDsIter])r=   r#   r   r   r   r   r   r   r?   r   r>   r:   rl   r$   r@   rA   returnc                C   sV   |dk	r0t |s|g}t| ||t|||dS t| ||||||||
|||	|||dS )a  
        Iterator over the messages for the given chat.

        The default order is from newest to oldest, but this
        behaviour can be changed with the `reverse` parameter.

        If either `search`, `filter` or `from_user` are provided,
        :tl:`messages.Search` will be used instead of :tl:`messages.getHistory`.

        .. note::

            Telegram's flood wait limit for :tl:`GetHistoryRequest` seems to
            be around 30 seconds per 10 requests, therefore a sleep of 1
            second is the default for this limit (or above).

        Arguments
            entity (`entity`):
                The entity from whom to retrieve the message history.

                It may be `None` to perform a global search, or
                to get messages by their ID from no particular chat.
                Note that some of the offsets will not work if this
                is the case.

                Note that if you want to perform a global search,
                you **must** set a non-empty `search` string, a `filter`.
                or `from_user`.

            limit (`int` | `None`, optional):
                Number of messages to be retrieved. Due to limitations with
                the API retrieving more than 3000 messages will take longer
                than half a minute (or even more based on previous calls).

                The limit may also be `None`, which would eventually return
                the whole history.

            offset_date (`datetime`):
                Offset date (messages *previous* to this date will be
                retrieved). Exclusive.

            offset_id (`int`):
                Offset message ID (only messages *previous* to the given
                ID will be retrieved). Exclusive.

            max_id (`int`):
                All the messages with a higher (newer) ID or equal to this will
                be excluded.

            min_id (`int`):
                All the messages with a lower (older) ID or equal to this will
                be excluded.

            add_offset (`int`):
                Additional message offset (all of the specified offsets +
                this offset = older messages).

            search (`str`):
                The string to be used as a search query.

            filter (:tl:`MessagesFilter` | `type`):
                The filter to use when returning messages. For instance,
                :tl:`InputMessagesFilterPhotos` would yield only messages
                containing photos.

            from_user (`entity`):
                Only messages from this entity will be returned.

            wait_time (`int`):
                Wait time (in seconds) between different
                :tl:`GetHistoryRequest`. Use this parameter to avoid hitting
                the ``FloodWaitError`` as needed. If left to `None`, it will
                default to 1 second only if the limit is higher than 3000.

                If the ``ids`` parameter is used, this time will default
                to 10 seconds only if the amount of IDs is higher than 300.

            ids (`int`, `list`):
                A single integer ID (or several IDs) for the message that
                should be returned. This parameter takes precedence over
                the rest (which will be ignored if this is set). This can
                for instance be used to get the message with ID 123 from
                a channel. Note that if the message doesn't exist, `None`
                will appear in its place, so that zipping the list of IDs
                with the messages can match one-to-one.

                .. note::

                    At the time of writing, Telegram will **not** return
                    :tl:`MessageEmpty` for :tl:`InputMessageReplyTo` IDs that
                    failed (i.e. the message is not replying to any, or is
                    replying to a deleted message). This means that it is
                    **not** possible to match messages one-by-one, so be
                    careful if you use non-integers in this parameter.

            reverse (`bool`, optional):
                If set to `True`, the messages will be returned in reverse
                order (from oldest to newest, instead of the default newest
                to oldest). This also means that the meaning of `offset_id`
                and `offset_date` parameters is reversed, although they will
                still be exclusive. `min_id` becomes equivalent to `offset_id`
                instead of being `max_id` as well since messages are returned
                in ascending order.

                You cannot use this if both `entity` and `ids` are `None`.

            reply_to (`int`, optional):
                If set to a message ID, the messages that reply to this ID
                will be returned. This feature is also known as comments in
                posts of broadcast channels, or viewing threads in groups.

                This feature can only be used in broadcast channels and their
                linked megagroups. Using it in a chat or private conversation
                will result in ``telethon.errors.PeerIdInvalidError`` to occur.

                When using this parameter, the ``filter`` and ``search``
                parameters have no effect, since Telegram's API doesn't
                support searching messages in replies.

                .. note::

                    This feature is used to get replies to a message in the
                    *discussion* group. If the same broadcast channel sends
                    a message and replies to it itself, that reply will not
                    be included in the results.

            scheduled (`bool`, optional):
                If set to `True`, messages which are scheduled will be returned.
                All other parameter will be ignored for this, except `entity`.

        Yields
            Instances of `Message <telethon.tl.custom.message.Message>`.

        Example
            .. code-block:: python

                # From most-recent to oldest
                async for message in client.iter_messages(chat):
                    print(message.id, message.text)

                # From oldest to most-recent
                async for message in client.iter_messages(chat, reverse=True):
                    print(message.id, message.text)

                # Filter by sender
                async for message in client.iter_messages(chat, from_user='me'):
                    print(message.text)

                # Server-side search with fuzzy text
                async for message in client.iter_messages(chat, search='hello'):
                    print(message.id)

                # Filter by message type:
                from telethon.tl.types import InputMessagesFilterPhotos
                async for message in client.iter_messages(chat, filter=InputMessagesFilterPhotos):
                    print(message.photo)

                # Getting comments from a post in a channel:
                async for message in client.iter_messages(channel, reply_to=123):
                    print(message.chat.title, message.text)
        N)r!   r$   r:   r   r#   rl   )r!   r$   r:   r   r#   r   r   r   r>   r   r   r   r?   r@   rA   )r   is_list_likere   r9   r   )r=   r#   r   r   r   r   r   r   r?   r   r>   r:   rl   r$   r@   rA   rE   rE   rF   r4   Z  s:     4
	zMessageMethods.iter_messageszhints.TotalListztypes.Message)r=   rv   c                    s   t |dkr6d|kr6d|kr.d|kr.d|d< nd|d< | j||}|d}|rxt|sx|2 z3 dH W }|  S 6 dS | I dH S )a'  
        Same as `iter_messages()`, but returns a
        `TotalList <telethon.helpers.TotalList>` instead.

        If the `limit` is not set, it will be 1 by default unless both
        `min_id` **and** `max_id` are set (as *named* arguments), in
        which case the entire range will be returned.

        This is so because any integer limit would be rather arbitrary and
        it's common to only want to fetch one message, but if a range is
        specified it makes sense that it should return the entirety of it.

        If `ids` is present in the *named* arguments and is not a list,
        a single `Message <telethon.tl.custom.message.Message>` will be
        returned for convenience instead of a list.

        Example
            .. code-block:: python

                # Get 0 photos and print the total to show how many photos there are
                from telethon.tl.types import InputMessagesFilterPhotos
                photos = await client.get_messages(chat, 0, filter=InputMessagesFilterPhotos)
                print(photos.total)

                # Get all the photos
                photos = await client.get_messages(chat, None, filter=InputMessagesFilterPhotos)

                # Get messages by ID:
                message_1337 = await client.get_messages(chat, ids=1337)
        r   r   r   r   Nrl   )r9   r4   getr   rw   Zcollect)r=   argskwargsitrl   r]   rE   rE   rF   get_messages,  s    !


zMessageMethods.get_messagesz typing.Union[int, types.Message])r=   r#   r]   c                    sZ   | t jj|t|dI d H }t|jdd d t fdd|jD }t| j	fS )N)r   r   c                 S   s   | j S rm   r5   )msgrE   rE   rF   <lambda>n      z2MessageMethods._get_comment_data.<locals>.<lambda>keyc                 3   s    | ]}|j  jjkr|V  qd S rm   )r5   rt   Z
channel_idrJ   crC   rE   rF   ro   o  s      z3MessageMethods._get_comment_data.<locals>.<genexpr>)
r	   r-   ZGetDiscussionMessageRequestr   get_message_idrN   nextrS   Zget_input_peerr5   )r=   r#   r]   r[   chatrE   r   rF   _get_comment_datae  s    z MessageMethods._get_comment_datar   rE   T)r@   
attributes
parse_modeformatting_entitieslink_previewfilethumbforce_documentclear_draftbuttonssilent
backgroundsupports_streamingschedule
comment_tonosound_videosend_asmessage_effect_idzhints.MessageLikez,typing.Sequence[types.TypeDocumentAttribute]z=typing.Union[hints.FileLike, typing.Sequence[hints.FileLike]]zhints.FileLikezhints.MarkupLike)r=   r#   r]   r@   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rv   c                   sv  |dk	rZt |tjr$|p|j}|j}| j|||||||
|	|||||||||||dI dH S | |I dH }|dk	r| ||I dH \}}n
t	|}t |tjr||dkr|j
}n
| |}|dkr|j}|jrt |jtjs| j||j|j|||||jd|||dI dH S tjj||jp"d|||dkr4dnt|||j|t |jtj ||rj| |I dH nd|d}|j}n|dkr| ||I dH \}}|stdtjj|||| |dkrdnt||||| |||r| |I dH nd|d}| |I dH }t |tjrhtj|j| |I dH ||j|j|j|j|j
|j|jd
}|| i | |S | |||S )	a%  
        Sends a message to the specified user, chat or channel.

        The default parse mode is the same as the official applications
        (a custom flavour of markdown). ``**bold**, `code` or __italic__``
        are available. In addition you can send ``[links](https://example.com)``
        and ``[mentions](@username)`` (or using IDs like in the Bot API:
        ``[mention](tg://user?id=123456789)``) and ``pre`` blocks with three
        backticks.

        Sending a ``/start`` command with a parameter (like ``?start=data``)
        is also done through this method. Simply send ``'/start data'`` to
        the bot.

        See also `Message.respond() <telethon.tl.custom.message.Message.respond>`
        and `Message.reply() <telethon.tl.custom.message.Message.reply>`.

        Arguments
            entity (`entity`):
                To who will it be sent.

            message (`str` | `Message <telethon.tl.custom.message.Message>`):
                The message to be sent, or another message object to resend.

                The maximum length for a message is 35,000 bytes or 4,096
                characters. Longer messages will not be sliced automatically,
                and you should slice them manually if the text to send is
                longer than said length.

            reply_to (`int` | `Message <telethon.tl.custom.message.Message>`, optional):
                Whether to reply to a message or not. If an integer is provided,
                it should be the ID of the message that it should reply to.

            attributes (`list`, optional):
                Optional attributes that override the inferred ones, like
                :tl:`DocumentAttributeFilename` and so on.

            parse_mode (`object`, optional):
                See the `TelegramClient.parse_mode
                <telethon.client.messageparse.MessageParseMethods.parse_mode>`
                property for allowed values. Markdown parsing will be used by
                default.

            formatting_entities (`list`, optional):
                A list of message formatting entities. When provided, the ``parse_mode`` is ignored.

            link_preview (`bool`, optional):
                Should the link preview be shown?

            file (`file`, optional):
                Sends a message with a file attached (e.g. a photo,
                video, audio or document). The ``message`` may be empty.

            thumb (`str` | `bytes` | `file`, optional):
                Optional JPEG thumbnail (for documents). **Telegram will
                ignore this parameter** unless you pass a ``.jpg`` file!
                The file must also be small in dimensions and in disk size.
                Successful thumbnails were files below 20kB and 320x320px.
                Width/height and dimensions/size ratios may be important.
                For Telegram to accept a thumbnail, you must provide the
                dimensions of the underlying media through ``attributes=``
                with :tl:`DocumentAttributesVideo` or by installing the
                optional ``hachoir`` dependency.

            force_document (`bool`, optional):
                Whether to send the given file as a document or not.

            clear_draft (`bool`, optional):
                Whether the existing draft should be cleared or not.

            buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
                The matrix (list of lists), row list or button to be shown
                after sending the message. This parameter will only work if
                you have signed in as a bot. You can also pass your own
                :tl:`ReplyMarkup` here.

                All the following limits apply together:

                * There can be 100 buttons at most (any more are ignored).
                * There can be 8 buttons per row at most (more are ignored).
                * The maximum callback data per button is 64 bytes.
                * The maximum data that can be embedded in total is just
                  over 4KB, shared between inline callback data and text.

            silent (`bool`, optional):
                Whether the message should notify people in a broadcast
                channel or not. Defaults to `False`, which means it will
                notify them. Set it to `True` to alter this behaviour.

            background (`bool`, optional):
                Whether the message should be send in background.

            supports_streaming (`bool`, optional):
                Whether the sent video supports streaming or not. Note that
                Telegram only recognizes as streamable some formats like MP4,
                and others like AVI or MKV will not work. You should convert
                these to MP4 before sending if you want them to be streamable.
                Unsupported formats will result in ``VideoContentTypeError``.

            schedule (`hints.DateLike`, optional):
                If set, the message won't send immediately, and instead
                it will be scheduled to be automatically sent at a later
                time.

            comment_to (`int` | `Message <telethon.tl.custom.message.Message>`, optional):
                Similar to ``reply_to``, but replies in the linked group of a
                broadcast channel instead (effectively leaving a "comment to"
                the specified message).

                This parameter takes precedence over ``reply_to``. If there is
                no linked chat, `telethon.errors.sgIdInvalidError` is raised.

            nosound_video (`bool`, optional):
                Only applicable when sending a video file without an audio
                track. If set to ``True``, the video will be displayed in
                Telegram as a video. If set to ``False``, Telegram will attempt
                to display the video as an animated gif. (It may still display
                as a video due to other factors.) The value is ignored if set
                on non-video files. This is set to ``True`` for albums, as gifs
                cannot be sent in albums.

            send_as (`entity`):
                Unique identifier (int) or username (str) of the chat or channel to send the message as.
                You can use this to send the message on behalf of a chat or channel where you have appropriate permissions.
                Use the GetSendAs to return the list of message sender identifiers, which can be used to send messages in the chat,
                This setting applies to the current message and will remain effective for future messages unless explicitly changed.
                To set this behavior permanently for all messages, use SaveDefaultSendAs.

            message_effect_id (`int`, optional):
                Unique identifier of the message effect to be added to the message; for private chats only

        Returns
            The sent `custom.Message <telethon.tl.custom.message.Message>`.

        Example
            .. code-block:: python

                # Markdown is the default
                await client.send_message('me', 'Hello **world**!')

                # Default to another parse mode
                client.parse_mode = 'html'

                await client.send_message('me', 'Some <b>bold</b> and <i>italic</i> text')
                await client.send_message('me', 'An <a href="https://example.com">URL</a>')
                # code and pre tags also work, but those break the documentation :)
                await client.send_message('me', '<a href="tg://user?id=me">Mentions</a>')

                # Explicit parse mode
                # No parse mode by default
                client.parse_mode = None

                # ...but here I want markdown
                await client.send_message('me', 'Hello, **world**!', parse_mode='md')

                # ...and here I need HTML
                await client.send_message('me', 'Hello, <i>world</i>!', parse_mode='html')

                # If you logged in as a bot account, you can send buttons
                from telethon import events, Button

                @client.on(events.CallbackQuery)
                async def callback(event):
                    await event.edit('Thank you for clicking {}!'.format(event.data))

                # Single inline button
                await client.send_message(chat, 'A single button, with "clk1" as data',
                                          buttons=Button.inline('Click me', b'clk1'))

                # Matrix of inline buttons
                await client.send_message(chat, 'Pick one from this grid', buttons=[
                    [Button.inline('Left'), Button.inline('Right')],
                    [Button.url('Check this site!', 'https://example.com')]
                ])

                # Reply keyboard
                await client.send_message(chat, 'Welcome', buttons=[
                    Button.text('Thanks!', resize=True, single_use=True),
                    Button.request_phone('Send phone'),
                    Button.request_location('Send location')
                ])

                # Forcing replies or clearing buttons.
                await client.send_message(chat, 'Reply to me', buttons=Button.force_reply())
                await client.send_message(chat, 'Bye Keyboard!', buttons=Button.clear())

                # Scheduling a message to be sent after 5 minutes
                from datetime import timedelta
                await client.send_message(chat, 'Hi, future!', schedule=timedelta(minutes=5))
        N)captionr@   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )
r   r   r   r@   r   r   r   r   r   r   r   )r   r]   r   r   r@   reply_markupr\   r   
no_webpageschedule_dater   effectz5The message cannot be empty unless a file is provided)r   r]   r\   r   r@   r   r   r   r   r   r   r   )
r5   rt   r]   r_   outmediar\   r   
ttl_periodr@   )r+   r   Messager\   r]   Z	send_filer"   r   r   r   r   build_reply_markupr   r   ZMessageMediaWebPager	   r-   ZSendMessageRequestZInputReplyToMessage_parse_message_textr%   ZUpdateShortSentMessager5   rr   r_   r   r   r@   rW   _get_response_message)r=   r#   r]   r@   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   Zmarkupr/   rD   rE   rE   rF   send_messager  s     W
          


  
 
zMessageMethods.send_message)r   with_my_scorer   as_albumr   drop_authordrop_media_captionszGtyping.Union[hints.MessageIDLike, typing.Sequence[hints.MessageIDLike]]ztyping.Sequence[types.Message])r=   r#   r-   	from_peerr   r   r   r   r   r   r   rv   c                   s   |dk	rt d t| }|r(|f}| |I dH }|r^| |I dH }| |I dH  nd  fdd}g }tj||dD ]\}}t|}t	|d t
r|}n(|p| |d jI dH }dd |D }tjj||||||||	|
d		}| |I dH }|| ||| q|r|d S |S )
a  
        Forwards the given messages to the specified entity.

        If you want to "forward" a message without the forward header
        (the "forwarded from" text), you should use `send_message` with
        the original message instead. This will send a copy of it.

        See also `Message.forward_to() <telethon.tl.custom.message.Message.forward_to>`.

        Arguments
            entity (`entity`):
                To which entity the message(s) will be forwarded.

            messages (`list` | `int` | `Message <telethon.tl.custom.message.Message>`):
                The message(s) to forward, or their integer IDs.

            from_peer (`entity`):
                If the given messages are integer IDs and not instances
                of the ``Message`` class, this *must* be specified in
                order for the forward to work. This parameter indicates
                the entity from which the messages should be forwarded.

            silent (`bool`, optional):
                Whether the message should notify people with sound or not.
                Defaults to `False` (send with a notification sound unless
                the person has the chat muted). Set it to `True` to alter
                this behaviour.

            background (`bool`, optional):
                Whether the message should be forwarded in background.

            with_my_score (`bool`, optional):
                Whether forwarded should contain your game score.

            as_album (`bool`, optional):
                This flag no longer has any effect.

            schedule (`hints.DateLike`, optional):
                If set, the message(s) won't forward immediately, and
                instead they will be scheduled to be automatically sent
                at a later time.

            drop_author (`bool`, optional):
                Whether to forward messages without quoting the original author.

            drop_media_captions (`bool`, optional):
                Whether to strip captions from media. Setting this to `True` requires that `drop_author` also be set to `True`.

        Returns
            The list of forwarded `Message <telethon.tl.custom.message.Message>`,
            or a single one if a list wasn't provided as input.

            Note that if all messages are invalid (i.e. deleted) the call
            will fail with ``MessageIdInvalidError``. If only some are
            invalid, the list will have `None` instead of those messages.

        Example
            .. code-block:: python

                # a single one
                await client.forward_messages(chat, message)
                # or
                await client.forward_messages(chat, message_id, from_chat)
                # or
                await message.forward_to(chat)

                # multiple
                await client.forward_messages(chat, messages)
                # or
                await client.forward_messages(chat, message_ids, from_chat)

                # Forwarding as a copy
                await client.send_message(chat, message)
        Nz@the as_album argument is deprecated and no longer has any effectc                    sH   t | tr  d k	r S tdn$t | tjr2| jS tdt| d S )Nz/from_peer must be given if integer IDs are usedz"Cannot forward messages of type {})	r+   intr%   r   r   Zchat_id	TypeErrorformatr,   r   Zfrom_peer_idrE   rF   get_key  s    

z0MessageMethods.forward_messages.<locals>.get_keyr   r   c                 S   s   g | ]
}|j qS rE   r}   rJ   rC   rE   rE   rF   
<listcomp>*  s     z3MessageMethods.forward_messages.<locals>.<listcomp>)	r   r5   Zto_peerr   r   r   r   r   r   )warningswarnr   rw   r"   r)   rP   groupbyrg   r+   r   rt   r	   r-   ZForwardMessagesRequestrs   r   )r=   r#   r-   r   r   r   r   r   r   r   r   Zsingler   sentZ_chat_idchunkr   reqrD   rE   r   rF   forward_messages  s@    X
zMessageMethods.forward_messages)
r   r   r   r   r   r   r   r   r   r   z-typing.Union[hints.EntityLike, types.Message]z;typing.Union[int, types.Message, types.InputMessageID, str])r=   r#   r]   textr   r   r   r   r   r   r   r   r   r   rv   c       
      
      sb  t |tjtjfr |p|}|}nt |tjr:|}|}|j}|dkrX| ||I dH \}}| j|||	||
dI dH \}}}t |tjtjfrtj	j
||| ||| |d}| jj|jk}| rz(| |jI dH }| ||I dH W S | |I dH  X n| |I dH S | |I dH }tj	j|t||| ||| ||d}| || |I dH |}|S )a  
        Edits the given message to change its text or media.

        See also `Message.edit() <telethon.tl.custom.message.Message.edit>`.

        Arguments
            entity (`entity` | `Message <telethon.tl.custom.message.Message>`):
                From which chat to edit the message. This can also be
                the message to be edited, and the entity will be inferred
                from it, so the next parameter will be assumed to be the
                message text.

                You may also pass a :tl:`InputBotInlineMessageID` or :tl:`InputBotInlineMessageID64`,
                which is the only way to edit messages that were sent
                after the user selects an inline query result.

            message (`int` | `Message <telethon.tl.custom.message.Message>` | :tl:`InputMessageID` | `str`):
                The ID of the message (or `Message
                <telethon.tl.custom.message.Message>` itself) to be edited.
                If the `entity` was a `Message
                <telethon.tl.custom.message.Message>`, then this message
                will be treated as the new text.

            text (`str`, optional):
                The new text of the message. Does nothing if the `entity`
                was a `Message <telethon.tl.custom.message.Message>`.

            parse_mode (`object`, optional):
                See the `TelegramClient.parse_mode
                <telethon.client.messageparse.MessageParseMethods.parse_mode>`
                property for allowed values. Markdown parsing will be used by
                default.

            attributes (`list`, optional):
                Optional attributes that override the inferred ones, like
                :tl:`DocumentAttributeFilename` and so on.

            formatting_entities (`list`, optional):
                A list of message formatting entities. When provided, the ``parse_mode`` is ignored.

            link_preview (`bool`, optional):
                Should the link preview be shown?

            file (`str` | `bytes` | `file` | `media`, optional):
                The file object that should replace the existing media
                in the message.

            thumb (`str` | `bytes` | `file`, optional):
                Optional JPEG thumbnail (for documents). **Telegram will
                ignore this parameter** unless you pass a ``.jpg`` file!
                The file must also be small in dimensions and in disk size.
                Successful thumbnails were files below 20kB and 320x320px.
                Width/height and dimensions/size ratios may be important.
                For Telegram to accept a thumbnail, you must provide the
                dimensions of the underlying media through ``attributes=``
                with :tl:`DocumentAttributesVideo` or by installing the
                optional ``hachoir`` dependency.

            force_document (`bool`, optional):
                Whether to send the given file as a document or not.

            buttons (`list`, `custom.Button <telethon.tl.custom.button.Button>`, :tl:`KeyboardButton`):
                The matrix (list of lists), row list or button to be shown
                after sending the message. This parameter will only work if
                you have signed in as a bot. You can also pass your own
                :tl:`ReplyMarkup` here.

            supports_streaming (`bool`, optional):
                Whether the sent video supports streaming or not. Note that
                Telegram only recognizes as streamable some formats like MP4,
                and others like AVI or MKV will not work. You should convert
                these to MP4 before sending if you want them to be streamable.
                Unsupported formats will result in ``VideoContentTypeError``.

            schedule (`hints.DateLike`, optional):
                If set, the message won't be edited immediately, and instead
                it will be scheduled to be automatically edited at a later
                time.

                Note that this parameter will have no effect if you are
                trying to edit a message that was sent via inline bots.

        Returns
            The edited `Message <telethon.tl.custom.message.Message>`,
            unless `entity` was a :tl:`InputBotInlineMessageID` or :tl:`InputBotInlineMessageID64` in which
            case this method returns a boolean.

        Raises
            ``MessageAuthorRequiredError`` if you're not the author of the
            message but tried editing it anyway.

            ``MessageNotModifiedError`` if the contents of the message were
            not modified at all.

            ``MessageIdInvalidError`` if the ID of the message is invalid
            (the ID itself may be correct, but the message with that ID
            cannot be edited). For example, when trying to edit messages
            with a reply markup (or clear markup) this error will be raised.

        Example
            .. code-block:: python

                message = await client.send_message(chat, 'hello')

                await client.edit_message(chat, message, 'hello!')
                # or
                await client.edit_message(chat, message.id, 'hello!!')
                # or
                await client.edit_message(message, 'hello!!!')
        N)r   r   r   r   )r5   r]   r   r\   r   r   )r   r5   r]   r   r\   r   r   r   )r+   r   ZInputBotInlineMessageIDZInputBotInlineMessageID64r   rt   r   Z_file_to_mediar	   r-   ZEditInlineBotMessageRequestr   sessionZdc_idZ_return_exported_senderZ_borrow_exported_senderZ_callr"   ZEditMessageRequestr   r   r   )r=   r#   r]   r   r   r   r   r   r   r   r   r   r   r   Zfile_handler   imager/   ZexportedZsenderr~   rE   rE   rF   edit_message<  sX    

zMessageMethods.edit_messagerevokez0typing.Sequence[types.messages.AffectedMessages])r=   r#   message_idsr   rv   c                   s   t |s|f}dd |D } r>|  I dH  t }ntjj}|tjjkrt|  fddt |D I dH S | fddt |D I dH S dS )a4  
        Deletes the given messages, optionally "for everyone".

        See also `Message.delete() <telethon.tl.custom.message.Message.delete>`.

        .. warning::

            This method does **not** validate that the message IDs belong
            to the chat that you passed! It's possible for the method to
            delete messages from different private chats and small group
            chats at once, so make sure to pass the right IDs.

        Arguments
            entity (`entity`):
                From who the message will be deleted. This can actually
                be `None` for normal chats, but **must** be present
                for channels and megagroups.

            message_ids (`list` | `int` | `Message <telethon.tl.custom.message.Message>`):
                The IDs (or ID) or messages to be deleted.

            revoke (`bool`, optional):
                Whether the message should be deleted for everyone or not.
                By default it has the opposite behaviour of official clients,
                and it will delete the message for everyone.

                `Since 24 March 2019
                <https://telegram.org/blog/unsend-privacy-emoji>`_, you can
                also revoke messages of any age (i.e. messages sent long in
                the past) the *other* person sent in private conversations
                (and of course your messages too).

                Disabling this has no effect on channels or megagroups,
                since it will unconditionally delete the message for everyone.

        Returns
            A list of :tl:`AffectedMessages`, each item being the result
            for the delete calls of the messages in chunks of 100 each.

        Example
            .. code-block:: python

                await client.delete_messages(chat, messages)
        c                 s   s2   | ]*}t |tjtjtjfr"|jnt|V  qd S rm   )r+   r   r   ZMessageServicerU   r5   r   r   rE   rE   rF   ro   #  s     z1MessageMethods.delete_messages.<locals>.<genexpr>Nc                    s   g | ]}t j t|qS rE   )r	   rq   DeleteMessagesRequestrg   r   )r#   rE   rF   r   1  s
    z2MessageMethods.delete_messages.<locals>.<listcomp>c                    s   g | ]}t jt| qS rE   )r	   r-   r   rg   r   r   rE   rF   r   4  s
    )	r   rw   r"   r   r0   r1   r2   rp   chunks)r=   r#   r   r   rB   rE   )r#   r   rF   delete_messages  s     2
zMessageMethods.delete_messages)r   clear_mentionsclear_reactions)r=   r#   r]   r   r   r   rv   c                   s   |dkr6|sd}n$t |r0tdd |D }n|j}| |I dH }|rp| tj|I dH  |dkrp|spdS |r| tj|I dH  |dkrdS |dk	rt	
|t	jjkr| tjjt ||dI dH S | tjj||dI dH S dS )u  
        Marks messages as read and optionally clears mentions.

        This effectively marks a message as read (or more than one) in the
        given conversation.

        If neither message nor maximum ID are provided, all messages will be
        marked as read by assuming that ``max_id = 0``.

        If a message or maximum ID is provided, all the messages up to and
        including such ID will be marked as read (for all messages whose ID
        ≤ max_id).

        See also `Message.mark_read() <telethon.tl.custom.message.Message.mark_read>`.

        Arguments
            entity (`entity`):
                The chat where these messages are located.

            message (`list` | `Message <telethon.tl.custom.message.Message>`):
                Either a list of messages or a single message.

            max_id (`int`):
                Until which message should the read acknowledge be sent for.
                This has priority over the ``message`` parameter.

            clear_mentions (`bool`):
                Whether the mention badge should be cleared (so that
                there are no more mentions) or not for the given entity.

                If no message is provided, this will be the only action
                taken.

            clear_reactions (`bool`):
                Whether the reactions badge should be cleared (so that
                there are no more reaction notifications) or not for the given entity.

                If no message is provided, this will be the only action
                taken.

        Example
            .. code-block:: python

                # using a Message object
                await client.send_read_acknowledge(chat, message)
                # ...or using the int ID of a Message
                await client.send_read_acknowledge(chat, message_id)
                # ...or passing a list of messages to mark as read
                await client.send_read_acknowledge(chat, messages)
        Nr   c                 s   s   | ]}|j V  qd S rm   r}   )rJ   r~   rE   rE   rF   ro   z  s     z7MessageMethods.send_read_acknowledge.<locals>.<genexpr>T)r   F)r   rw   r&   r5   r"   r	   r-   ZReadMentionsRequestZReadReactionsRequestr   r0   r1   rp   rq   ZReadHistoryRequestZget_input_channel)r=   r#   r]   r   r   r   rE   rE   rF   send_read_acknowledge;  s4    :
  z$MessageMethods.send_read_acknowledge)notify
pm_onesidez$typing.Optional[hints.MessageIDLike]r=   r#   r]   r   r   c                   s   | j ||d||dI dH S )a  
        Pins a message in a chat.

        The default behaviour is to *not* notify members, unlike the
        official applications.

        See also `Message.pin() <telethon.tl.custom.message.Message.pin>`.

        Arguments
            entity (`entity`):
                The chat where the message should be pinned.

            message (`int` | `Message <telethon.tl.custom.message.Message>`):
                The message or the message ID to pin. If it's
                `None`, all messages will be unpinned instead.

            notify (`bool`, optional):
                Whether the pin should notify people or not.

            pm_oneside (`bool`, optional):
                Whether the message should be pinned for everyone or not.
                By default it has the opposite behaviour of official clients,
                and it will pin the message for both sides, in private chats.

        Example
            .. code-block:: python

                # Send and pin a message to annoy everyone
                message = await client.send_message(chat, 'Pinotifying is fun!')
                await client.pin_message(chat, message, notify=True)
        F)unpinr   r   N_pinr   rE   rE   rF   pin_message  s    'zMessageMethods.pin_message)r   r=   r#   r]   r   c                   s   | j ||d|dI dH S )a  
        Unpins a message in a chat.

        If no message ID is specified, all pinned messages will be unpinned.

        See also `Message.unpin() <telethon.tl.custom.message.Message.unpin>`.

        Arguments
            entity (`entity`):
                The chat where the message should be pinned.

            message (`int` | `Message <telethon.tl.custom.message.Message>`):
                The message or the message ID to unpin. If it's
                `None`, all messages will be unpinned instead.

        Example
            .. code-block:: python

                # Unpin all messages from a chat
                await client.unpin_message(chat)
        T)r   r   Nr   r   rE   rE   rF   unpin_message  s    zMessageMethods.unpin_messagec                   s   t |pd}| |I d H }|dkr@| tj|I d H  d S tjj||| ||d}| |I d H }|sp|jstd S | |||S )Nr   )r   r5   r   r   r   )	r   r   r"   r	   r-   ZUnpinAllMessagesRequestZUpdatePinnedMessageRequestZupdatesr   )r=   r#   r]   r   r   r   r/   rD   rE   rE   rF   r     s     
zMessageMethods._pin)N)r   )N)NN)N)N)ra   rb   rc   r(   r   strboolr4   typingUnionOptionalr|   inspect	signature__signature__r   Listr   ZTypeMessageEntityr   r   r   r   r   r   r   r   rE   rE   rE   rF   ru   T  sr  	  T3   E     8 P  \, ru   )r   rP   r   r   r   r   r   r   r   Zrequestiterr   tlr   r	   r;   TYPE_CHECKINGZtelegramclientr   r   re   ru   rE   rE   rE   rF   <module>   s     8