mirror of
https://github.com/aiogram/aiogram.git
synced 2026-04-08 16:37:47 +00:00
Allow to send files
This commit is contained in:
parent
9d980c4b61
commit
5797ab6c99
5 changed files with 91 additions and 20 deletions
|
|
@ -19,7 +19,7 @@ class Request(BaseModel):
|
|||
method: str
|
||||
|
||||
data: Dict[str, Optional[Any]]
|
||||
files: Optional[Dict[str, Union[io.BytesIO, bytes, InputFile]]]
|
||||
files: Optional[Dict[str, InputFile]]
|
||||
|
||||
class Config(BaseConfig):
|
||||
arbitrary_types_allowed = True
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from typing import Optional, TypeVar
|
||||
|
||||
from aiohttp import ClientSession, FormData
|
||||
from pydantic import BaseModel
|
||||
|
||||
from .base import BaseSession, TelegramAPIServer, PRODUCTION
|
||||
from ..methods import TelegramMethod, Request
|
||||
from ..methods import Request, TelegramMethod
|
||||
from .base import PRODUCTION, BaseSession, TelegramAPIServer
|
||||
|
||||
T = TypeVar('T')
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class AiohttpSession(BaseSession):
|
||||
|
|
@ -28,11 +27,11 @@ class AiohttpSession(BaseSession):
|
|||
if value is None:
|
||||
continue
|
||||
if isinstance(value, bool):
|
||||
print("elif isinstance(value, bool):", key, value)
|
||||
form.add_field(key, value)
|
||||
else:
|
||||
print("else:", key, value)
|
||||
form.add_field(key, str(value))
|
||||
for key, value in request.files.items():
|
||||
form.add_field(key, value, filename=value.filename or key)
|
||||
return form
|
||||
|
||||
async def make_request(self, token: str, call: TelegramMethod[T]) -> T:
|
||||
|
|
@ -46,6 +45,5 @@ class AiohttpSession(BaseSession):
|
|||
raw_result = await response.json()
|
||||
|
||||
response = call.build_response(raw_result)
|
||||
if not response.ok:
|
||||
self.raise_for_status(response)
|
||||
self.raise_for_status(response)
|
||||
return response.result
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import abc
|
||||
import asyncio
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
from pydantic.dataclasses import dataclass
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
from aiogram.api.methods import Response, TelegramMethod
|
||||
from pydantic.dataclasses import dataclass
|
||||
|
||||
T = TypeVar('T')
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -22,8 +21,8 @@ class TelegramAPIServer:
|
|||
|
||||
|
||||
PRODUCTION = TelegramAPIServer(
|
||||
base='https://api.telegram.org/bot{token}/{method}',
|
||||
file='https://api.telegram.org/file/bot{token}/{path}'
|
||||
base="https://api.telegram.org/bot{token}/{method}",
|
||||
file="https://api.telegram.org/file/bot{token}/{path}",
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -32,7 +31,9 @@ class BaseSession(abc.ABC, Generic[T]):
|
|||
self.api = api
|
||||
|
||||
def raise_for_status(self, response: Response[T]):
|
||||
print(f"ERROR: {response}")
|
||||
if response.ok:
|
||||
return
|
||||
raise Exception(response.description)
|
||||
|
||||
@abc.abstractmethod
|
||||
async def close(self):
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ from .webhook_info import WebhookInfo
|
|||
|
||||
__all__ = (
|
||||
"TelegramObject",
|
||||
"InputFile",
|
||||
"Update",
|
||||
"WebhookInfo",
|
||||
"User",
|
||||
|
|
@ -135,7 +136,6 @@ __all__ = (
|
|||
"InputMediaAnimation",
|
||||
"InputMediaAudio",
|
||||
"InputMediaDocument",
|
||||
"InputFile",
|
||||
"Sticker",
|
||||
"StickerSet",
|
||||
"MaskPosition",
|
||||
|
|
@ -195,5 +195,5 @@ __all__ = (
|
|||
)
|
||||
|
||||
# Load typing forward refs for every TelegramObject
|
||||
for entity in __all__[1:]:
|
||||
for entity in __all__[2:]:
|
||||
globals()[entity].update_forward_refs(**globals())
|
||||
|
|
|
|||
|
|
@ -1,11 +1,83 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from .base import TelegramObject
|
||||
import io
|
||||
import os
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from typing import Optional, Union
|
||||
|
||||
import aiofiles as aiofiles
|
||||
|
||||
DEFAULT_CHUNK_SIZE = 64 * 1024 # 64 kb
|
||||
|
||||
|
||||
class InputFile(TelegramObject):
|
||||
class InputFile(ABC):
|
||||
"""
|
||||
This object represents the contents of a file to be uploaded. Must be posted using multipart/form-data in the usual way that files are uploaded via the browser.
|
||||
|
||||
Source: https://core.telegram.org/bots/api#inputfile
|
||||
"""
|
||||
|
||||
def __init__(self, filename: Optional[str] = None, chunk_size: int = DEFAULT_CHUNK_SIZE):
|
||||
self.filename = filename
|
||||
self.chunk_size = chunk_size
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
yield
|
||||
|
||||
@abstractmethod
|
||||
async def read(self, chunk_size: int):
|
||||
pass
|
||||
|
||||
async def __aiter__(self):
|
||||
async for chunk in self.read(self.chunk_size):
|
||||
yield chunk
|
||||
|
||||
|
||||
class BufferedInputFile(InputFile):
|
||||
def __init__(self, file: bytes, filename: str, chunk_size: int = DEFAULT_CHUNK_SIZE):
|
||||
super().__init__(filename=filename, chunk_size=chunk_size)
|
||||
|
||||
self.data = file
|
||||
|
||||
@classmethod
|
||||
def from_file(
|
||||
cls,
|
||||
path: Union[str, Path],
|
||||
filename: Optional[str] = None,
|
||||
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
||||
):
|
||||
if filename is None:
|
||||
filename = os.path.basename(path)
|
||||
with open(path, "rb") as f:
|
||||
data = f.read()
|
||||
return cls(data, filename=filename, chunk_size=chunk_size)
|
||||
|
||||
async def read(self, chunk_size: int):
|
||||
buffer = io.BytesIO(self.data)
|
||||
chunk = buffer.read(chunk_size)
|
||||
while chunk:
|
||||
yield chunk
|
||||
chunk = buffer.read(chunk_size)
|
||||
|
||||
|
||||
class FSInputFile(InputFile):
|
||||
def __init__(
|
||||
self,
|
||||
path: Union[str, Path],
|
||||
filename: Optional[str] = None,
|
||||
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
||||
):
|
||||
if filename is None:
|
||||
filename = os.path.basename(path)
|
||||
super().__init__(filename=filename, chunk_size=chunk_size)
|
||||
|
||||
self.path = path
|
||||
|
||||
async def read(self, chunk_size: int):
|
||||
async with aiofiles.open(self.path, "rb") as f:
|
||||
chunk = await f.read(chunk_size)
|
||||
while chunk:
|
||||
yield chunk
|
||||
chunk = await f.read(chunk_size)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue