mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2024-11-22 09:26:37 +03:00
Update layer and move File out to types
This commit is contained in:
parent
4b2d252fe1
commit
e642ccfc4d
|
@ -1,15 +1,10 @@
|
||||||
from .bots import InlineResult, InlineResults
|
from .bots import InlineResult, InlineResults
|
||||||
from .client import Client
|
from .client import Client
|
||||||
from .files import File, InFileLike, MediaLike, OutFileLike
|
|
||||||
from .net import Config
|
from .net import Config
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"InlineResult",
|
"InlineResult",
|
||||||
"InlineResults",
|
"InlineResults",
|
||||||
"Client",
|
"Client",
|
||||||
"File",
|
|
||||||
"InFileLike",
|
|
||||||
"MediaLike",
|
|
||||||
"OutFileLike",
|
|
||||||
"Config",
|
"Config",
|
||||||
]
|
]
|
||||||
|
|
|
@ -106,8 +106,7 @@ class InlineResult(metaclass=NoPublicConstructor):
|
||||||
clear_draft=False,
|
clear_draft=False,
|
||||||
hide_via=False,
|
hide_via=False,
|
||||||
peer=peer,
|
peer=peer,
|
||||||
reply_to_msg_id=None,
|
reply_to=None,
|
||||||
top_msg_id=None,
|
|
||||||
random_id=random_id,
|
random_id=random_id,
|
||||||
query_id=self._raw_results.query_id,
|
query_id=self._raw_results.query_id,
|
||||||
id=self._raw.id,
|
id=self._raw.id,
|
||||||
|
|
|
@ -18,7 +18,18 @@ from typing import (
|
||||||
from ...mtsender import Sender
|
from ...mtsender import Sender
|
||||||
from ...session import ChatHashCache, MessageBox, PackedChat, Session
|
from ...session import ChatHashCache, MessageBox, PackedChat, Session
|
||||||
from ...tl import Request, abcs
|
from ...tl import Request, abcs
|
||||||
from ..types import AsyncList, ChatLike, LoginToken, Message, PasswordToken, User
|
from ..types import (
|
||||||
|
AsyncList,
|
||||||
|
ChatLike,
|
||||||
|
File,
|
||||||
|
InFileLike,
|
||||||
|
LoginToken,
|
||||||
|
MediaLike,
|
||||||
|
Message,
|
||||||
|
OutFileLike,
|
||||||
|
PasswordToken,
|
||||||
|
User,
|
||||||
|
)
|
||||||
from .account import edit_2fa, end_takeout, takeout
|
from .account import edit_2fa, end_takeout, takeout
|
||||||
from .auth import (
|
from .auth import (
|
||||||
bot_sign_in,
|
bot_sign_in,
|
||||||
|
@ -44,10 +55,6 @@ from .chats import (
|
||||||
)
|
)
|
||||||
from .dialogs import conversation, delete_dialog, edit_folder, iter_dialogs, iter_drafts
|
from .dialogs import conversation, delete_dialog, edit_folder, iter_dialogs, iter_drafts
|
||||||
from .files import (
|
from .files import (
|
||||||
File,
|
|
||||||
InFileLike,
|
|
||||||
MediaLike,
|
|
||||||
OutFileLike,
|
|
||||||
download,
|
download,
|
||||||
iter_download,
|
iter_download,
|
||||||
send_audio,
|
send_audio,
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
|
||||||
from mimetypes import guess_type
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import TYPE_CHECKING, Any, Coroutine, List, Optional, Protocol, Self, Union
|
from typing import TYPE_CHECKING, Optional, Union
|
||||||
|
|
||||||
from ...tl import abcs, functions, types
|
from ...tl import abcs, functions, types
|
||||||
from ..types import ChatLike, Message, NoPublicConstructor
|
from ..types import ChatLike, File, InFileLike, MediaLike, Message, OutFileLike
|
||||||
from ..utils import generate_random_id
|
from ..utils import generate_random_id
|
||||||
from .messages import parse_message
|
from .messages import parse_message
|
||||||
|
|
||||||
|
@ -20,206 +18,6 @@ MAX_CHUNK_SIZE = 512 * 1024
|
||||||
FILE_MIGRATE_ERROR = 303
|
FILE_MIGRATE_ERROR = 303
|
||||||
BIG_FILE_SIZE = 10 * 1024 * 1024
|
BIG_FILE_SIZE = 10 * 1024 * 1024
|
||||||
|
|
||||||
math_round = round
|
|
||||||
|
|
||||||
|
|
||||||
class InFileLike(Protocol):
|
|
||||||
"""
|
|
||||||
[File-like object](https://docs.python.org/3/glossary.html#term-file-like-object)
|
|
||||||
used for input only, where the `read` method can be `async`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def read(self, n: int) -> Union[bytes, Coroutine[Any, Any, bytes]]:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class OutFileLike(Protocol):
|
|
||||||
"""
|
|
||||||
[File-like object](https://docs.python.org/3/glossary.html#term-file-like-object)
|
|
||||||
used for output only, where the `write` method can be `async`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def write(self, data: bytes) -> Union[Any, Coroutine[Any, Any, Any]]:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
MediaLike = object
|
|
||||||
|
|
||||||
|
|
||||||
class File(metaclass=NoPublicConstructor):
|
|
||||||
"""
|
|
||||||
File information of uploaded media.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
path: Optional[Path],
|
|
||||||
file: Optional[InFileLike],
|
|
||||||
attributes: List[abcs.DocumentAttribute],
|
|
||||||
size: int,
|
|
||||||
name: str,
|
|
||||||
mime: str,
|
|
||||||
photo: bool,
|
|
||||||
muted: bool,
|
|
||||||
input_media: Optional[abcs.InputMedia],
|
|
||||||
):
|
|
||||||
self._path = path
|
|
||||||
self._file = file
|
|
||||||
self._attributes = attributes
|
|
||||||
self._size = size
|
|
||||||
self._name = name
|
|
||||||
self._mime = mime
|
|
||||||
self._photo = photo
|
|
||||||
self._muted = muted
|
|
||||||
self._input_file: Optional[abcs.InputFile] = None
|
|
||||||
self._input_media: Optional[abcs.InputMedia] = input_media
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def new(
|
|
||||||
cls,
|
|
||||||
path: Optional[Union[str, Path, File]] = None,
|
|
||||||
*,
|
|
||||||
url: Optional[str] = None,
|
|
||||||
file: Optional[InFileLike] = None,
|
|
||||||
size: Optional[int] = None,
|
|
||||||
name: Optional[str] = None,
|
|
||||||
mime_type: Optional[str] = None,
|
|
||||||
compress: bool = False,
|
|
||||||
animated: bool = False,
|
|
||||||
duration: Optional[float] = None,
|
|
||||||
voice: bool = False,
|
|
||||||
title: Optional[str] = None,
|
|
||||||
performer: Optional[str] = None,
|
|
||||||
emoji: Optional[str] = None,
|
|
||||||
emoji_sticker: Optional[str] = None,
|
|
||||||
width: Optional[int] = None,
|
|
||||||
height: Optional[int] = None,
|
|
||||||
round: bool = False,
|
|
||||||
supports_streaming: bool = False,
|
|
||||||
muted: bool = False,
|
|
||||||
) -> "File":
|
|
||||||
"""
|
|
||||||
Create file information that can later be sent as media.
|
|
||||||
|
|
||||||
If the path is a `File`, the rest of parameters are ignored, and
|
|
||||||
this existing instance is returned instead (the method is a no-op).
|
|
||||||
|
|
||||||
Only one of path, url or file must be specified.
|
|
||||||
|
|
||||||
If a local file path is not given, size and name must be specified.
|
|
||||||
|
|
||||||
The mime_type will be inferred from the name if it is omitted.
|
|
||||||
|
|
||||||
The rest of parameters are only used depending on the mime_type:
|
|
||||||
|
|
||||||
* For image/:
|
|
||||||
* width (required), in pixels, of the media.
|
|
||||||
* height (required), in pixels, of the media.
|
|
||||||
* For audio/:
|
|
||||||
* duration (required), in seconds, of the media. This will be rounded.
|
|
||||||
* voice, if it's a live recording.
|
|
||||||
* title, of the song.
|
|
||||||
* performer, with the name of the artist.
|
|
||||||
* For video/:
|
|
||||||
* duration (required), in seconds, of the media. This will be rounded.
|
|
||||||
* width (required), in pixels, of the media.
|
|
||||||
* height (required), in pixels, of the media.
|
|
||||||
* round, if it should be displayed as a round video.
|
|
||||||
* supports_streaming, if clients are able to stream the video.
|
|
||||||
* muted, if the sound from the video is or should be missing.
|
|
||||||
* For sticker:
|
|
||||||
* animated, if it's not a static image.
|
|
||||||
* emoji, as the alternative text for the sticker.
|
|
||||||
* stickerset, to which the sticker belongs.
|
|
||||||
|
|
||||||
If any of the required fields are missing, the attribute will not be sent.
|
|
||||||
"""
|
|
||||||
if isinstance(path, File):
|
|
||||||
return path
|
|
||||||
|
|
||||||
attributes: List[abcs.DocumentAttribute] = []
|
|
||||||
|
|
||||||
if sum((path is not None, url is not None, file is not None)) != 1:
|
|
||||||
raise ValueError("must specify exactly one of path, markdown or html")
|
|
||||||
|
|
||||||
if path is not None:
|
|
||||||
size = os.path.getsize(path)
|
|
||||||
name = os.path.basename(path)
|
|
||||||
|
|
||||||
if size is None:
|
|
||||||
raise ValueError("must specify size")
|
|
||||||
if name is None:
|
|
||||||
raise ValueError("must specify name")
|
|
||||||
|
|
||||||
if mime_type is None:
|
|
||||||
mime_type, _ = guess_type(name, strict=False)
|
|
||||||
if mime_type is None:
|
|
||||||
raise ValueError("must specify mime_type")
|
|
||||||
|
|
||||||
if sum((path is not None, url is not None, file is not None)) != 1:
|
|
||||||
raise ValueError("must specify exactly one of path, markdown or html")
|
|
||||||
|
|
||||||
attributes.append(types.DocumentAttributeFilename(file_name=name))
|
|
||||||
|
|
||||||
if mime_type.startswith("image/"):
|
|
||||||
if width is not None and height is not None:
|
|
||||||
attributes.append(types.DocumentAttributeImageSize(w=width, h=height))
|
|
||||||
elif mime_type.startswith("audio/"):
|
|
||||||
if duration is not None:
|
|
||||||
attributes.append(
|
|
||||||
types.DocumentAttributeAudio(
|
|
||||||
voice=voice,
|
|
||||||
duration=int(math_round(duration)),
|
|
||||||
title=title,
|
|
||||||
performer=performer,
|
|
||||||
waveform=None,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
elif mime_type.startswith("video/"):
|
|
||||||
if duration is not None and width is not None and height is not None:
|
|
||||||
attributes.append(
|
|
||||||
types.DocumentAttributeVideo(
|
|
||||||
round_message=round,
|
|
||||||
supports_streaming=supports_streaming,
|
|
||||||
duration=int(math_round(duration)),
|
|
||||||
w=width,
|
|
||||||
h=height,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise NotImplementedError("sticker")
|
|
||||||
|
|
||||||
photo = compress and mime_type.startswith("image/")
|
|
||||||
|
|
||||||
input_media: Optional[abcs.InputMedia]
|
|
||||||
if url is not None:
|
|
||||||
if photo:
|
|
||||||
input_media = types.InputMediaPhotoExternal(
|
|
||||||
spoiler=False, url=url, ttl_seconds=None
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
input_media = types.InputMediaDocumentExternal(
|
|
||||||
spoiler=False, url=url, ttl_seconds=None
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
input_media = None
|
|
||||||
|
|
||||||
return cls(
|
|
||||||
path=Path(path) if path is not None else None,
|
|
||||||
file=file,
|
|
||||||
attributes=attributes,
|
|
||||||
size=size,
|
|
||||||
name=name,
|
|
||||||
mime=mime_type,
|
|
||||||
photo=photo,
|
|
||||||
muted=muted,
|
|
||||||
input_media=input_media,
|
|
||||||
)
|
|
||||||
|
|
||||||
async def _read(self, n: int) -> bytes:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
|
|
||||||
async def send_photo(
|
async def send_photo(
|
||||||
self: Client,
|
self: Client,
|
||||||
|
@ -396,8 +194,7 @@ async def send_file(
|
||||||
noforwards=False,
|
noforwards=False,
|
||||||
update_stickersets_order=False,
|
update_stickersets_order=False,
|
||||||
peer=peer,
|
peer=peer,
|
||||||
reply_to_msg_id=None,
|
reply_to=None,
|
||||||
top_msg_id=None,
|
|
||||||
media=file_info._input_media,
|
media=file_info._input_media,
|
||||||
message=message,
|
message=message,
|
||||||
random_id=random_id,
|
random_id=random_id,
|
||||||
|
|
|
@ -57,8 +57,7 @@ async def send_message(
|
||||||
noforwards=False,
|
noforwards=False,
|
||||||
update_stickersets_order=False,
|
update_stickersets_order=False,
|
||||||
peer=peer,
|
peer=peer,
|
||||||
reply_to_msg_id=None,
|
reply_to=None,
|
||||||
top_msg_id=None,
|
|
||||||
message=message,
|
message=message,
|
||||||
random_id=random_id,
|
random_id=random_id,
|
||||||
reply_markup=None,
|
reply_markup=None,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from .async_list import AsyncList
|
from .async_list import AsyncList
|
||||||
from .chat import Channel, Chat, ChatLike, Group, RestrictionReason, User
|
from .chat import Channel, Chat, ChatLike, Group, RestrictionReason, User
|
||||||
|
from .file import File, InFileLike, MediaLike, OutFileLike
|
||||||
from .login_token import LoginToken
|
from .login_token import LoginToken
|
||||||
from .message import Message
|
from .message import Message
|
||||||
from .meta import NoPublicConstructor
|
from .meta import NoPublicConstructor
|
||||||
|
@ -13,6 +14,10 @@ __all__ = [
|
||||||
"Group",
|
"Group",
|
||||||
"RestrictionReason",
|
"RestrictionReason",
|
||||||
"User",
|
"User",
|
||||||
|
"File",
|
||||||
|
"InFileLike",
|
||||||
|
"MediaLike",
|
||||||
|
"OutFileLike",
|
||||||
"LoginToken",
|
"LoginToken",
|
||||||
"Message",
|
"Message",
|
||||||
"NoPublicConstructor",
|
"NoPublicConstructor",
|
||||||
|
|
|
@ -59,6 +59,9 @@ class User(metaclass=NoPublicConstructor):
|
||||||
premium=False,
|
premium=False,
|
||||||
attach_menu_enabled=False,
|
attach_menu_enabled=False,
|
||||||
bot_can_edit=False,
|
bot_can_edit=False,
|
||||||
|
close_friend=False,
|
||||||
|
stories_hidden=False,
|
||||||
|
stories_unavailable=False,
|
||||||
id=user.id,
|
id=user.id,
|
||||||
access_hash=None,
|
access_hash=None,
|
||||||
first_name=None,
|
first_name=None,
|
||||||
|
@ -73,6 +76,7 @@ class User(metaclass=NoPublicConstructor):
|
||||||
lang_code=None,
|
lang_code=None,
|
||||||
emoji_status=None,
|
emoji_status=None,
|
||||||
usernames=None,
|
usernames=None,
|
||||||
|
stories_max_id=None,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
elif isinstance(user, types.User):
|
elif isinstance(user, types.User):
|
||||||
|
|
299
client/src/telethon/_impl/client/types/file.py
Normal file
299
client/src/telethon/_impl/client/types/file.py
Normal file
|
@ -0,0 +1,299 @@
|
||||||
|
import os
|
||||||
|
from mimetypes import guess_type
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Coroutine, List, Optional, Protocol, Self, Union
|
||||||
|
|
||||||
|
from ...tl import abcs, types
|
||||||
|
from .meta import NoPublicConstructor
|
||||||
|
|
||||||
|
math_round = round
|
||||||
|
|
||||||
|
|
||||||
|
def photo_size_byte_count(size: abcs.PhotoSize) -> int:
|
||||||
|
if isinstance(size, types.PhotoCachedSize):
|
||||||
|
return len(size.bytes)
|
||||||
|
elif isinstance(size, types.PhotoPathSize):
|
||||||
|
return len(size.bytes)
|
||||||
|
elif isinstance(size, types.PhotoSize):
|
||||||
|
return size.size
|
||||||
|
elif isinstance(size, types.PhotoSizeEmpty):
|
||||||
|
return 0
|
||||||
|
elif isinstance(size, types.PhotoSizeProgressive):
|
||||||
|
return max(size.sizes)
|
||||||
|
elif isinstance(size, types.PhotoStrippedSize):
|
||||||
|
if len(size.bytes) < 3 or size.bytes[0] != 1:
|
||||||
|
return len(size.bytes)
|
||||||
|
|
||||||
|
return len(size.bytes) + 622
|
||||||
|
else:
|
||||||
|
raise RuntimeError("unexpected case")
|
||||||
|
|
||||||
|
|
||||||
|
MediaLike = object
|
||||||
|
|
||||||
|
|
||||||
|
class InFileLike(Protocol):
|
||||||
|
"""
|
||||||
|
[File-like object](https://docs.python.org/3/glossary.html#term-file-like-object)
|
||||||
|
used for input only, where the `read` method can be `async`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def read(self, n: int) -> Union[bytes, Coroutine[Any, Any, bytes]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OutFileLike(Protocol):
|
||||||
|
"""
|
||||||
|
[File-like object](https://docs.python.org/3/glossary.html#term-file-like-object)
|
||||||
|
used for output only, where the `write` method can be `async`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def write(self, data: bytes) -> Union[Any, Coroutine[Any, Any, Any]]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class File(metaclass=NoPublicConstructor):
|
||||||
|
"""
|
||||||
|
File information of uploaded media.
|
||||||
|
|
||||||
|
It is used both when sending files or accessing media in a `Message`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
path: Optional[Path],
|
||||||
|
file: Optional[InFileLike],
|
||||||
|
attributes: List[abcs.DocumentAttribute],
|
||||||
|
size: int,
|
||||||
|
name: str,
|
||||||
|
mime: str,
|
||||||
|
photo: bool,
|
||||||
|
muted: bool,
|
||||||
|
input_media: Optional[abcs.InputMedia],
|
||||||
|
raw: Optional[Union[types.MessageMediaDocument, types.MessageMediaPhoto]],
|
||||||
|
):
|
||||||
|
self._path = path
|
||||||
|
self._file = file
|
||||||
|
self._attributes = attributes
|
||||||
|
self._size = size
|
||||||
|
self._name = name
|
||||||
|
self._mime = mime
|
||||||
|
self._photo = photo
|
||||||
|
self._muted = muted
|
||||||
|
self._input_file: Optional[abcs.InputFile] = None
|
||||||
|
self._input_media: Optional[abcs.InputMedia] = input_media
|
||||||
|
self._raw = raw
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _try_from_raw(cls, raw: abcs.MessageMedia) -> Optional[Self]:
|
||||||
|
if isinstance(raw, types.MessageMediaDocument):
|
||||||
|
if isinstance(raw.document, types.Document):
|
||||||
|
return cls._create(
|
||||||
|
path=None,
|
||||||
|
file=None,
|
||||||
|
attributes=raw.document.attributes,
|
||||||
|
size=raw.document.size,
|
||||||
|
name=next(
|
||||||
|
(
|
||||||
|
a.file_name
|
||||||
|
for a in raw.document.attributes
|
||||||
|
if isinstance(a, types.DocumentAttributeFilename)
|
||||||
|
),
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
mime=raw.document.mime_type,
|
||||||
|
photo=False,
|
||||||
|
muted=next(
|
||||||
|
(
|
||||||
|
a.nosound
|
||||||
|
for a in raw.document.attributes
|
||||||
|
if isinstance(a, types.DocumentAttributeVideo)
|
||||||
|
),
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
input_media=types.InputMediaDocument(
|
||||||
|
spoiler=raw.spoiler,
|
||||||
|
id=types.InputDocument(
|
||||||
|
id=raw.document.id,
|
||||||
|
access_hash=raw.document.access_hash,
|
||||||
|
file_reference=raw.document.file_reference,
|
||||||
|
),
|
||||||
|
ttl_seconds=raw.ttl_seconds,
|
||||||
|
query=None,
|
||||||
|
),
|
||||||
|
raw=raw,
|
||||||
|
)
|
||||||
|
elif isinstance(raw, types.MessageMediaPhoto):
|
||||||
|
if isinstance(raw.photo, types.Photo):
|
||||||
|
return cls._create(
|
||||||
|
path=None,
|
||||||
|
file=None,
|
||||||
|
attributes=[],
|
||||||
|
size=max(map(photo_size_byte_count, raw.photo.sizes)),
|
||||||
|
name="",
|
||||||
|
mime="image/jpeg",
|
||||||
|
photo=True,
|
||||||
|
muted=False,
|
||||||
|
input_media=types.InputMediaPhoto(
|
||||||
|
spoiler=raw.spoiler,
|
||||||
|
id=types.InputPhoto(
|
||||||
|
id=raw.photo.id,
|
||||||
|
access_hash=raw.photo.access_hash,
|
||||||
|
file_reference=raw.photo.file_reference,
|
||||||
|
),
|
||||||
|
ttl_seconds=raw.ttl_seconds,
|
||||||
|
),
|
||||||
|
raw=raw,
|
||||||
|
)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(
|
||||||
|
cls,
|
||||||
|
path: Optional[Union[str, Path, Self]] = None,
|
||||||
|
*,
|
||||||
|
url: Optional[str] = None,
|
||||||
|
file: Optional[InFileLike] = None,
|
||||||
|
size: Optional[int] = None,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
mime_type: Optional[str] = None,
|
||||||
|
compress: bool = False,
|
||||||
|
animated: bool = False,
|
||||||
|
duration: Optional[float] = None,
|
||||||
|
voice: bool = False,
|
||||||
|
title: Optional[str] = None,
|
||||||
|
performer: Optional[str] = None,
|
||||||
|
emoji: Optional[str] = None,
|
||||||
|
emoji_sticker: Optional[str] = None,
|
||||||
|
width: Optional[int] = None,
|
||||||
|
height: Optional[int] = None,
|
||||||
|
round: bool = False,
|
||||||
|
supports_streaming: bool = False,
|
||||||
|
muted: bool = False,
|
||||||
|
) -> Self:
|
||||||
|
"""
|
||||||
|
Create file information that can later be sent as media.
|
||||||
|
|
||||||
|
If the path is a `File`, the rest of parameters are ignored, and
|
||||||
|
this existing instance is returned instead (the method is a no-op).
|
||||||
|
|
||||||
|
Only one of path, url or file must be specified.
|
||||||
|
|
||||||
|
If a local file path is not given, size and name must be specified.
|
||||||
|
|
||||||
|
The mime_type will be inferred from the name if it is omitted.
|
||||||
|
|
||||||
|
The rest of parameters are only used depending on the mime_type:
|
||||||
|
|
||||||
|
* For image/:
|
||||||
|
* width (required), in pixels, of the media.
|
||||||
|
* height (required), in pixels, of the media.
|
||||||
|
* For audio/:
|
||||||
|
* duration (required), in seconds, of the media. This will be rounded.
|
||||||
|
* voice, if it's a live recording.
|
||||||
|
* title, of the song.
|
||||||
|
* performer, with the name of the artist.
|
||||||
|
* For video/:
|
||||||
|
* duration (required), in seconds, of the media. This will be rounded.
|
||||||
|
* width (required), in pixels, of the media.
|
||||||
|
* height (required), in pixels, of the media.
|
||||||
|
* round, if it should be displayed as a round video.
|
||||||
|
* supports_streaming, if clients are able to stream the video.
|
||||||
|
* muted, if the sound from the video is or should be missing.
|
||||||
|
* For sticker:
|
||||||
|
* animated, if it's not a static image.
|
||||||
|
* emoji, as the alternative text for the sticker.
|
||||||
|
* stickerset, to which the sticker belongs.
|
||||||
|
|
||||||
|
If any of the required fields are missing, the attribute will not be sent.
|
||||||
|
"""
|
||||||
|
if isinstance(path, cls):
|
||||||
|
return path
|
||||||
|
assert not isinstance(path, File)
|
||||||
|
|
||||||
|
attributes: List[abcs.DocumentAttribute] = []
|
||||||
|
|
||||||
|
if sum((path is not None, url is not None, file is not None)) != 1:
|
||||||
|
raise ValueError("must specify exactly one of path, markdown or html")
|
||||||
|
|
||||||
|
if path is not None:
|
||||||
|
size = os.path.getsize(path)
|
||||||
|
name = os.path.basename(path)
|
||||||
|
|
||||||
|
if size is None:
|
||||||
|
raise ValueError("must specify size")
|
||||||
|
if name is None:
|
||||||
|
raise ValueError("must specify name")
|
||||||
|
|
||||||
|
if mime_type is None:
|
||||||
|
mime_type, _ = guess_type(name, strict=False)
|
||||||
|
if mime_type is None:
|
||||||
|
raise ValueError("must specify mime_type")
|
||||||
|
|
||||||
|
if sum((path is not None, url is not None, file is not None)) != 1:
|
||||||
|
raise ValueError("must specify exactly one of path, markdown or html")
|
||||||
|
|
||||||
|
attributes.append(types.DocumentAttributeFilename(file_name=name))
|
||||||
|
|
||||||
|
if mime_type.startswith("image/"):
|
||||||
|
if width is not None and height is not None:
|
||||||
|
attributes.append(types.DocumentAttributeImageSize(w=width, h=height))
|
||||||
|
elif mime_type.startswith("audio/"):
|
||||||
|
if duration is not None:
|
||||||
|
attributes.append(
|
||||||
|
types.DocumentAttributeAudio(
|
||||||
|
voice=voice,
|
||||||
|
duration=int(math_round(duration)),
|
||||||
|
title=title,
|
||||||
|
performer=performer,
|
||||||
|
waveform=None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif mime_type.startswith("video/"):
|
||||||
|
if duration is not None and width is not None and height is not None:
|
||||||
|
attributes.append(
|
||||||
|
types.DocumentAttributeVideo(
|
||||||
|
round_message=round,
|
||||||
|
supports_streaming=supports_streaming,
|
||||||
|
nosound=muted,
|
||||||
|
duration=int(math_round(duration)),
|
||||||
|
w=width,
|
||||||
|
h=height,
|
||||||
|
preload_prefix_size=None,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise NotImplementedError("sticker")
|
||||||
|
|
||||||
|
photo = compress and mime_type.startswith("image/")
|
||||||
|
|
||||||
|
input_media: Optional[abcs.InputMedia]
|
||||||
|
if url is not None:
|
||||||
|
if photo:
|
||||||
|
input_media = types.InputMediaPhotoExternal(
|
||||||
|
spoiler=False, url=url, ttl_seconds=None
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
input_media = types.InputMediaDocumentExternal(
|
||||||
|
spoiler=False, url=url, ttl_seconds=None
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
input_media = None
|
||||||
|
|
||||||
|
return cls._create(
|
||||||
|
path=Path(path) if path is not None else None,
|
||||||
|
file=file,
|
||||||
|
attributes=attributes,
|
||||||
|
size=size,
|
||||||
|
name=name,
|
||||||
|
mime=mime_type,
|
||||||
|
photo=photo,
|
||||||
|
muted=muted,
|
||||||
|
input_media=input_media,
|
||||||
|
raw=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
async def _read(self, n: int) -> bytes:
|
||||||
|
raise NotImplementedError
|
|
@ -3,6 +3,7 @@ from typing import Optional, Self
|
||||||
|
|
||||||
from ...tl import abcs, types
|
from ...tl import abcs, types
|
||||||
from .chat import Chat
|
from .chat import Chat
|
||||||
|
from .file import File
|
||||||
from .meta import NoPublicConstructor
|
from .meta import NoPublicConstructor
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,3 +40,39 @@ class Message(metaclass=NoPublicConstructor):
|
||||||
@property
|
@property
|
||||||
def chat(self) -> Chat:
|
def chat(self) -> Chat:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def _file(self) -> Optional[File]:
|
||||||
|
return (
|
||||||
|
File._try_from_raw(self._raw.media)
|
||||||
|
if isinstance(self._raw, types.Message) and self._raw.media
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def photo(self) -> Optional[File]:
|
||||||
|
photo = self._file()
|
||||||
|
return photo if photo and photo._photo else None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def audio(self) -> Optional[File]:
|
||||||
|
audio = self._file()
|
||||||
|
return (
|
||||||
|
audio
|
||||||
|
if audio
|
||||||
|
and any(
|
||||||
|
isinstance(a, types.DocumentAttributeAudio) for a in audio._attributes
|
||||||
|
)
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def video(self) -> Optional[File]:
|
||||||
|
audio = self._file()
|
||||||
|
return (
|
||||||
|
audio
|
||||||
|
if audio
|
||||||
|
and any(
|
||||||
|
isinstance(a, types.DocumentAttributeVideo) for a in audio._attributes
|
||||||
|
)
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
|
@ -405,11 +405,13 @@ class MessageBox:
|
||||||
|
|
||||||
gd = functions.updates.get_difference(
|
gd = functions.updates.get_difference(
|
||||||
pts=self.map[ENTRY_ACCOUNT].pts,
|
pts=self.map[ENTRY_ACCOUNT].pts,
|
||||||
|
pts_limit=None,
|
||||||
pts_total_limit=None,
|
pts_total_limit=None,
|
||||||
date=int(self.date.timestamp()),
|
date=int(self.date.timestamp()),
|
||||||
qts=self.map[ENTRY_SECRET].pts
|
qts=self.map[ENTRY_SECRET].pts
|
||||||
if ENTRY_SECRET in self.map
|
if ENTRY_SECRET in self.map
|
||||||
else NO_SEQ,
|
else NO_SEQ,
|
||||||
|
qts_limit=None,
|
||||||
)
|
)
|
||||||
if __debug__:
|
if __debug__:
|
||||||
self._trace("Requesting account difference %s", gd)
|
self._trace("Requesting account difference %s", gd)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user