Added possibility to reply into webhook with files (#1120)

This commit is contained in:
Alex Root Junior 2023-02-12 02:00:42 +02:00 committed by GitHub
parent 84bc0e347b
commit 58868ec627
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 126 additions and 30 deletions

View file

@ -13,7 +13,7 @@ from ..fsm.middleware import FSMContextMiddleware
from ..fsm.storage.base import BaseEventIsolation, BaseStorage
from ..fsm.storage.memory import DisabledEventIsolation, MemoryStorage
from ..fsm.strategy import FSMStrategy
from ..methods import GetUpdates, TelegramMethod
from ..methods import GetUpdates, Request, TelegramMethod
from ..types import Update, User
from ..types.update import UpdateTypeLookupError
from ..utils.backoff import Backoff, BackoffConfig
@ -351,7 +351,7 @@ class Dispatcher(Router):
async def feed_webhook_update(
self, bot: Bot, update: Union[Update, Dict[str, Any]], _timeout: float = 55, **kwargs: Any
) -> Optional[Dict[str, Any]]:
) -> Optional[Request]:
if not isinstance(update, Update): # Allow to use raw updates
update = Update(**update)
@ -397,8 +397,7 @@ class Dispatcher(Router):
# TODO: handle exceptions
response: Any = process_updates.result()
if isinstance(response, TelegramMethod):
request = response.build_request(bot=bot)
return request.render_webhook_request()
return response.build_request(bot=bot)
else:
process_updates.remove_done_callback(release_waiter)

View file

@ -33,12 +33,6 @@ class Request(BaseModel):
class Config(BaseConfig):
arbitrary_types_allowed = True
def render_webhook_request(self) -> Dict[str, Any]:
return {
"method": self.method,
**{key: value for key, value in self.data.items() if value is not None},
}
class Response(GenericModel, Generic[TelegramType]):
ok: bool

View file

@ -1,15 +1,17 @@
import asyncio
import secrets
from abc import ABC, abstractmethod
from asyncio import Transport
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, cast
from aiohttp import web
from aiohttp import MultipartWriter, web
from aiohttp.abc import Application
from aiohttp.typedefs import Handler
from aiohttp.web_middlewares import middleware
from aiogram import Bot, Dispatcher, loggers
from aiogram.methods import TelegramMethod
from aiogram.methods import Request, TelegramMethod
from aiogram.types import UNSET
from aiogram.webhook.security import IPFilter
@ -84,7 +86,10 @@ class BaseRequestHandler(ABC):
"""
def __init__(
self, dispatcher: Dispatcher, handle_in_background: bool = True, **data: Any
self,
dispatcher: Dispatcher,
handle_in_background: bool = False,
**data: Any,
) -> None:
"""
:param dispatcher: instance of :class:`aiogram.dispatcher.dispatcher.Dispatcher`
@ -138,15 +143,39 @@ class BaseRequestHandler(ABC):
)
return web.json_response({}, dumps=bot.session.json_dumps)
def _build_response_writer(self, bot: Bot, result: Optional[Request]) -> MultipartWriter:
writer = MultipartWriter(
"form-data",
boundary=f"webhookBoundary{secrets.token_urlsafe(16)}",
)
if not result:
return writer
payload = writer.append(result.method)
payload.set_content_disposition("form-data", name="method")
for key, value in result.data.items():
if value is None or value is UNSET:
continue
payload = writer.append(bot.session.prepare_value(value))
payload.set_content_disposition("form-data", name=key)
if not result.files:
return writer
for key, value in result.files.items():
payload = writer.append(value)
payload.set_content_disposition("form-data", name=key, filename=value.filename)
return writer
async def _handle_request(self, bot: Bot, request: web.Request) -> web.Response:
result = await self.dispatcher.feed_webhook_update(
bot,
await request.json(loads=bot.session.json_loads),
**self.data,
)
if result:
return web.json_response(result, dumps=bot.session.json_dumps)
return web.json_response({}, dumps=bot.session.json_dumps)
return web.Response(body=self._build_response_writer(bot=bot, result=result))
async def handle(self, request: web.Request) -> web.Response:
bot = await self.resolve_bot(request)