U
    hɔ                     @   s   d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlmZ ddlm	Z	 ddl
mZmZmZ ddlmZmZmZ zd dlZd dlZW n ek
r   dZY nX ejrddlmZ G d	d
 d
ZdddZG dd dZdS )    N)BytesIO   )AES)utilshelpershints)types	functionscustom   )TelegramClientc                   @   s(   e Zd ZdZdd Zdd Zdd ZdS )	
_CacheTypez<Like functools.partial but pretends to be the wrapped class.c                 C   s
   || _ d S N_cls)selfcls r   ;/tmp/pip-unpacked-wheel-c81u5j2r/telethon/client/uploads.py__init__   s    z_CacheType.__init__c                 O   s   | j |ddi|S )NZfile_reference    r   )r   argskwargsr   r   r   __call__   s    z_CacheType.__call__c                 C   s
   | j |kS r   r   )r   otherr   r   r   __eq__!   s    z_CacheType.__eq__N)__name__
__module____qualname____doc__r   r   r   r   r   r   r   r      s   r    
     r"   r"   c              	   C   s  |r t d ks t| tjr$|  s$| S t| tr8t| } t| tjrd|  }| dtj	 |  }n(t| t
rtj| rtj| }nd }z`z:t j| }zd|jd i}W n tk
r   i }Y nX |jdkr,|j|kr|j|kr|r|dkrndr| W W S |||ft jj |}	nh|||ft jj t jd|j|}	d }
|jr|jdkrv|d}
n|dd}
|	j||
d t }|	j|d	fd
di| |d d|_|W W $S  t k
r   |  Y W S X W 5 t| tjr| | X d S )Nr   ZexifZRGBi FZRGBAA)maskZJPEGZprogressiveTza.jpg)!PIL
isinstanceioIOBaseseekablebytesr   tellseekSEEK_ENDstrospathexistsgetsizeZImageopeninfoKeyErrormodewidthheightZ	thumbnailZLANCZOSnewsizeZhas_transparency_dataZ
getchannelconvertZpastesavenameIOError)fileis_imager7   r8   
backgroundZold_posbeforeimager   resultr$   bufferr   r   r   _resize_photo_if_needed%   sZ    




0


rF   c                   @   s8  e Zd Zdddd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 f eeed	d
ddeeej	ejej
ej ej
ej
ej  f  eeej	d eeeddeeej	d ej	e ejej
ej ejf dddZddej	d ej	e dddZddddddddddeeeeeed	dd
ddZdddZdS ) UploadMethodsNFTr   )captionforce_document	file_sizeclear_draftprogress_callbackreply_to
attributesthumballow_cache
parse_modeformatting_entities
voice_note
video_notebuttonssilentrA   supports_streamingschedule
comment_tottlnosound_videosend_asmessage_effect_idr   zhints.EntityLikez=typing.Union[hints.FileLike, typing.Sequence[hints.FileLike]]zhints.ProgressCallbackzhints.MessageIDLikez,typing.Sequence[types.TypeDocumentAttribute]zhints.FileLikezhints.MarkupLikezhints.DateLikez typing.Union[int, types.Message])r   entityr?   rH   rI   rJ   rK   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   rV   rA   rW   rX   rY   rZ   r[   r\   r]   returnc          $         sD   st d |sd}|s"g }| |I dH }|dk	rR| ||I dH \}}n
t|}t rldstdn fdd}t|r|}n|g}tdd |D r|}nt|r|g}nt d	td
d |D st dg } rh|| j| dd |dd |dd |||||||||||dI dH 7 } dd  |dd }|dd }d7 q|S |rx|}n| 	||I dH \}}| j
 |||	||
|||||dI dH \}} }!| st d | |}"|dkrdnt|}tjj|| ||||"|||||r"| |I dH nd|d}#| |#| |#I dH |S )u-  
        Sends message with the given file to the specified entity.

        .. note::

            If the ``hachoir3`` package (``hachoir`` module) is installed,
            it will be used to determine metadata from audio and video files.

            If the ``pillow`` package is installed and you are sending a photo,
            it will be resized to fit within the maximum dimensions allowed
            by Telegram to avoid ``errors.PhotoInvalidDimensionsError``. This
            cannot be done if you are sending :tl:`InputFile`, however.

        Arguments
            entity (`entity`):
                Who will receive the file.

            file (`str` | `bytes` | `file` | `media`):
                The file to send, which can be one of:

                * A local file path to an in-disk file. The file name
                  will be the path's base name.

                * A `bytes` byte array with the file's data to send
                  (for example, by using ``text.encode('utf-8')``).
                  A default file name will be used.

                * A bytes `io.IOBase` stream over the file to send
                  (for example, by using ``open(file, 'rb')``).
                  Its ``.name`` property will be used for the file name,
                  or a default if it doesn't have one.

                * An external URL to a file over the internet. This will
                  send the file as "external" media, and Telegram is the
                  one that will fetch the media and send it.

                * A Bot API-like ``file_id``. You can convert previously
                  sent media to file IDs for later reusing with
                  `telethon.utils.pack_bot_file_id`.

                * A handle to an existing file (for example, if you sent a
                  message with media before, you can use its ``message.media``
                  as a file here).

                * A handle to an uploaded file (from `upload_file`).

                * A :tl:`InputMedia` instance. For example, if you want to
                  send a dice use :tl:`InputMediaDice`, or if you want to
                  send a contact use :tl:`InputMediaContact`.

                To send an album, you should provide a list in this parameter.

                If a list or similar is provided, the files in it will be
                sent as an album in the order in which they appear, sliced
                in chunks of 10 if more than 10 are given.

            caption (`str`, optional):
                Optional caption for the sent media message. When sending an
                album, the caption may be a list of strings, which will be
                assigned to the files pairwise.

            force_document (`bool`, optional):
                If left to `False` and the file is a path that ends with
                the extension of an image file or a video file, it will be
                sent as such. Otherwise always as a document.

            file_size (`int`, optional):
                The size of the file to be uploaded if it needs to be uploaded,
                which will be determined automatically if not specified.

                If the file size can't be determined beforehand, the entire
                file will be read in-memory to find out how large it is.

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

            progress_callback (`callable`, optional):
                A callback function accepting two parameters:
                ``(sent bytes, total)``.

            reply_to (`int` | `Message <telethon.tl.custom.message.Message>`):
                Same as `reply_to` from `send_message`.

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

            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.


            allow_cache (`bool`, optional):
                This parameter currently does nothing, but is kept for
                backward-compatibility (and it may get its use back in
                the future).

            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):
                Optional formatting entities for the sent media message. When sending an album,
                `formatting_entities` can be a list of lists, where each inner list contains
                `types.TypeMessageEntity`. Each inner list will be assigned to the corresponding
                file in a pairwise manner with the caption. If provided, the ``parse_mode``
                parameter will be ignored.

            voice_note (`bool`, optional):
                If `True` the audio will be sent as a voice note.

            video_note (`bool`, optional):
                If `True` the video will be sent as a video note,
                also known as a round video message.

            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.

            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 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 file 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.

            ttl (`int`. optional):
                The Time-To-Live of the file (also known as "self-destruct timer"
                or "self-destructing media"). If set, files can only be viewed for
                a short period of time before they disappear from the message
                history automatically.

                The value must be at least 1 second, and at most 60 seconds,
                otherwise Telegram will ignore this parameter.

                Not all types of media can be used with this parameter, such
                as text documents, which will fail with ``TtlMediaInvalidError``.

            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 `Message <telethon.tl.custom.message.Message>` (or messages)
            containing the sent file, or messages if a list of them was passed.

        Example
            .. code-block:: python

                # Normal files like photos
                await client.send_file(chat, '/my/photos/me.jpg', caption="It's me!")
                # or
                await client.send_message(chat, "It's me!", file='/my/photos/me.jpg')

                # Voice notes or round videos
                await client.send_file(chat, '/my/songs/song.mp3', voice_note=True)
                await client.send_file(chat, '/my/videos/video.mp4', video_note=True)

                # Custom thumbnails
                await client.send_file(chat, '/my/documents/doc.txt', thumb='photo.jpg')

                # Only documents
                await client.send_file(chat, '/my/photos/photo.png', force_document=True)

                # Albums
                await client.send_file(chat, [
                    '/my/photos/holiday1.jpg',
                    '/my/photos/holiday2.jpg',
                    '/my/drawings/portrait.png'
                ])

                # Printing upload progress
                def callback(current, total):
                    print('Uploaded', current, 'out of', total,
                          'bytes: {:.2%}'.format(current / total))

                await client.send_file(chat, file, progress_callback=callback)

                # Dices, including dart and other future emoji
                from telethon.tl import types
                await client.send_file(chat, types.InputMediaDice(''))
                await client.send_file(chat, types.InputMediaDice('🎯'))

                # Contacts
                await client.send_file(chat, types.InputMediaContact(
                    phone_number='+34 123 456 789',
                    first_name='Example',
                    last_name='',
                    vcard=''
                ))
        zCannot use {!r} as file Nr   c                    s   |  t  S r   lenstr?   rL   
sent_countr   r   <lambda>  r   z)UploadMethods.send_file.<locals>.<lambda>c                 s   s   | ]}t |V  qd S r   )r   is_list_like.0objr   r   r   	<genexpr>  s     z*UploadMethods.send_file.<locals>.<genexpr>zFThe formatting_entities argument must be a list or a sequence of listsc                 s   s$   | ]}|D ]}t |tjV  q
qd S r   )r&   r   TypeMessageEntity)rk   Zsublistentr   r   r   rm     s       z;All entities must be instances of <types.TypeMessageEntity>
   )rH   rR   rL   rM   rQ   rV   rX   rW   rK   rI   rA   r\   r]   )rI   rJ   rL   rN   rP   rO   rS   rT   rW   rZ   r[   )
rM   messageentitiesZreply_markuprV   schedule_daterK   rA   r\   effect)	TypeErrorformatget_input_entityZ_get_comment_datar   get_message_idri   all_send_album_parse_message_text_file_to_mediaZbuild_reply_markupr   InputReplyToMessager	   messagesZSendMediaRequest_get_response_message)$r   r^   r?   rH   rI   rJ   rK   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   rV   rA   rW   rX   rY   rZ   r[   r\   r]   r   used_callbackcaptionsrD   msg_entitiesfile_handlemediarC   Zmarkuprequestr   rf   r   	send_fileq   s      


 
 
 
      
     
      zUploadMethods.send_filer`   )r   r\   r]   c                     s  |  |I dH }t|s |f}tdd |D s8|f}g }|rvtjt|t|dd}|D ]\}}|||f q\n*t|D ] }|| |pd|I dH  q~t	|}sdn fdd}g }t
 D ]\}| j||
|||dd	I dH \}}}t|tjtjfr4| tjj||d
I dH }t|j}n>t|tjtjfrr| tjj||d
I dH }tj|j|
d}|r| \}}nd\}}|tj|||d qtjj||dkrdnt||||	|||r|  |I dH nd|d	}| |I dH }dd |D }| |||S )z,Specialized version of .send_file for albumsNc                 s   s   | ]}t |tV  qd S r   )r&   listrj   r   r   r   rm     s     z,UploadMethods._send_album.<locals>.<genexpr>)	fillvaluer`   c                    s&   | |krd n
| |  t  S )Nr   ra   rc   filesrL   rg   r   r   rh     r   z+UploadMethods._send_album.<locals>.<lambda>T)rW   rI   rZ   rL   r[   )r   )rW   )r`   N)rq   rr   )rM   Zmulti_mediarV   rs   rK   rA   r\   rt   c                 S   s   g | ]
}|j qS r   )Z	random_id)rk   mr   r   r   
<listcomp>5  s     z-UploadMethods._send_album.<locals>.<listcomp>)rw   r   ri   ry   	itertoolszip_longestreversedappendr{   rx   	enumerater|   r&   r   InputMediaUploadedPhotoInputMediaPhotoExternalr	   r~   ZUploadMediaRequestget_input_mediaZphotoInputMediaUploadedDocumentInputMediaDocumentExternaldocumentpopZInputSingleMediaZSendMultiMediaRequestr}   r   ) r   r^   r   rH   rR   rL   rM   rQ   rV   rX   rW   rK   rI   rA   rZ   r\   r]   r   Zcapt_with_entZmsg_captionr   cr   r   r?   fhZfm_rr   rD   Z
random_idsr   r   r   rz     s~    

      
    zUploadMethods._send_album)part_size_kbrJ   	file_name	use_cachekeyivrL   ztypes.TypeInputFile)
r   r?   r   rJ   r   r   r   r   rL   r_   c             
      sL  t |tjtjfr|S d}	tj||d4 I dH }
|
j}|sHt|}|dkrXt	dt
|d }|d dkrxt	dt }|s|
jpt|}tj|d s|t|
7 }|d	k}t }|| d
 | }| jt d||| d}	t|D ]}t|
|I dH }t |ts,tdt|t||krV||d
 k rVt	d||	t|7 }	|r||r|t |||}|s|!| |rt"j#$||||}nt"j#%|||}| |I dH }|r | jt &d|d
 | |rt||	|I dH  qt'd|qW 5 Q I dH R X |r4t|||S t(j)|||||dS dS )a  
        Uploads a file to Telegram's servers, without sending it.

        .. note::

            Generally, you want to use `send_file` instead.

        This method returns a handle (an instance of :tl:`InputFile` or
        :tl:`InputFileBig`, as required) which can be later used before
        it expires (they are usable during less than a day).

        Uploading a file will simply return a "handle" to the file stored
        remotely in the Telegram servers, which can be later used on. This
        will **not** upload the file to your own chat or any chat at all.

        Arguments
            file (`str` | `bytes` | `file`):
                The path of the file, byte array, or stream that will be sent.
                Note that if a byte array or a stream is given, a filename
                or its type won't be inferred, and it will be sent as an
                "unnamed application/octet-stream".

            part_size_kb (`int`, optional):
                Chunk size when uploading files. The larger, the less
                requests will be made (up to 512KB maximum).

            file_size (`int`, optional):
                The size of the file to be uploaded, which will be determined
                automatically if not specified.

                If the file size can't be determined beforehand, the entire
                file will be read in-memory to find out how large it is.

            file_name (`str`, optional):
                The file name which will be used on the resulting InputFile.
                If not specified, the name will be taken from the ``file``
                and if this is not a `str`, it will be ``"unnamed"``.

            use_cache (`type`, optional):
                This parameter currently does nothing, but is kept for
                backward-compatibility (and it may get its use back in
                the future).

            key ('bytes', optional):
                In case of an encrypted upload (secret chats) a key is supplied

            iv ('bytes', optional):
                In case of an encrypted upload (secret chats) an iv is supplied

            progress_callback (`callable`, optional):
                A callback function accepting two parameters:
                ``(sent bytes, total)``.

                When sending an album, the callback will receive a number
                between 0 and the amount of files as the "sent" parameter,
                and the amount of files as the "total". Note that the first
                parameter will be a floating point number to indicate progress
                within a file (e.g. ``2.5`` means it has sent 50% of the third
                file, because it's between 2 and 3).

        Returns
            :tl:`InputFileBig` if the file size is larger than 10MB,
            `InputSizedFile <telethon.tl.custom.inputsizedfile.InputSizedFile>`
            (subclass of :tl:`InputFile`) otherwise.

        Example
            .. code-block:: python

                # Photos as photo and document
                file = await client.upload_file('photo.jpg')
                await client.send_file(chat, file)                       # sends as photo
                await client.send_file(chat, file, force_document=True)  # sends as document

                file.name = 'not a photo.jpg'
                await client.send_file(chat, file, force_document=True)  # document, new name

                # As song or as voice note
                file = await client.upload_file('song.ogg')
                await client.send_file(chat, file)                   # sends as song
                await client.send_file(chat, file, voice_note=True)  # sends as voice note
        r   rJ   Ni   z,The part size must be less or equal to 512KBi   z.The part size must be evenly divisible by 1024i   r   z-Uploading file of %d bytes in %d chunks of %dzMfile descriptor returned {}, not bytes (you must open the file in bytes mode)zQread less than {} before reaching the end; either `file_size` or `read` are wrongzUploaded %d/%dzFailed to upload file part {}.)md5r:   )*r&   r   	InputFileInputFileBigr   Z_FileStreamrJ   r   Zget_appropriated_part_size
ValueErrorintZgenerate_random_longr=   r.   r/   r0   splitextZ_get_extensionhashlibr   _logr   r4   rangeZ_maybe_awaitreadr*   ru   rv   typerb   r   Zencrypt_igeupdater	   uploadZSaveBigFilePartRequestZSaveFilePartRequestdebugRuntimeErrorr
   ZInputSizedFile)r   r?   r   rJ   r   r   r   r   rL   posstreamZ	part_sizeZfile_idZis_bigZhash_md5
part_countZ
part_indexpartr   rD   r   r   r   upload_file8  s    \
  
          zUploadMethods.upload_filec                    s  |sdS t |tjr t| }t|}|d kr<|o:| }t |tttj	tj
fst|dsz"d tj||||||	|
|d|fW S  tk
r   d d |f Y S X d }d }t |tj	tj
fr|}nt |trtj|r| jt||||dI d H }nRtd|r&|rtj||d}ntj||d}nt|}|rDtj||d}|rLn|sbtd|n|rxtj||d}ntj||||o| ||	|
|d	\}}|sd }n.t |tjrt| }| j||d
I d H }|dd dkr|nd }tj|||||o
| ||d}|||fS )N)NNNr   )Zis_photorN   rI   rS   rT   rW   rZ   )rJ   rL   z	https?://)ttl_seconds)rZ   z`Failed to convert {} to media. Not an existing file, an HTTP URL or a valid bot-API-like file ID)	mime_typerN   rI   rS   rT   rW   rO   r   /r   video)r?   r   rN   rO   Z
force_filer   r[   )r&   pathlibPathr.   absoluter   r@   r*   r   r   r   hasattrr   ru   r/   r0   isfiler   rF   rematchr   r   Zresolve_bot_file_idr   rv   r   Zget_attributessplitr   )r   r?   rI   rJ   rL   rN   rO   rP   rS   rT   rW   r   Zas_imagerZ   r[   r@   r   r   Zbot_filer   r   r   r|     s    

	




	zUploadMethods._file_to_media)r`   NNNr   NNNNFNNNN)FNNNNTFFFNNNN)r   r   r   typingUnionr.   Sequenceboolr   OptionalListr   rn   Anyr   rz   floatr   r*   r   r|   r   r   r   r   rG   m   s   	  n                    _  >                     rG   )r    r    r!   )r   r'   r   r/   r   r   r   r   Zcryptor   r`   r   r   r   tlr   r	   r
   r%   Z	PIL.ImageImportErrorTYPE_CHECKINGZtelegramclientr   r   rF   rG   r   r   r   r   <module>   s.   
     
H