School/IKEA_scraper/.venv/Lib/site-packages/aiogram/utils/executor.py
Kristofers Solo dca634b9b9 Changed name
2021-09-12 18:39:57 +03:00

400 lines
13 KiB
Python

import asyncio
import datetime
import functools
import secrets
from typing import Callable, Union, Optional, Any, List
from warnings import warn
from aiohttp import web
from aiohttp.web_app import Application
from ..bot.api import log
from ..dispatcher.dispatcher import Dispatcher
from ..dispatcher.webhook import BOT_DISPATCHER_KEY, DEFAULT_ROUTE_NAME, WebhookRequestHandler
APP_EXECUTOR_KEY = 'APP_EXECUTOR'
def _setup_callbacks(executor: 'Executor', on_startup=None, on_shutdown=None):
if on_startup is not None:
executor.on_startup(on_startup)
if on_shutdown is not None:
executor.on_shutdown(on_shutdown)
def start_polling(dispatcher, *, loop=None, skip_updates=False, reset_webhook=True,
on_startup=None, on_shutdown=None, timeout=20, relax=0.1, fast=True,
allowed_updates: Optional[List[str]] = None):
"""
Start bot in long-polling mode
:param dispatcher:
:param loop:
:param skip_updates:
:param reset_webhook:
:param on_startup:
:param on_shutdown:
:param timeout:
:param relax:
:param fast:
:param allowed_updates:
"""
executor = Executor(dispatcher, skip_updates=skip_updates, loop=loop)
_setup_callbacks(executor, on_startup, on_shutdown)
executor.start_polling(
reset_webhook=reset_webhook,
timeout=timeout,
relax=relax,
fast=fast,
allowed_updates=allowed_updates
)
def set_webhook(dispatcher: Dispatcher, webhook_path: str, *, loop: Optional[asyncio.AbstractEventLoop] = None,
skip_updates: bool = None, on_startup: Optional[Callable] = None,
on_shutdown: Optional[Callable] = None, check_ip: bool = False,
retry_after: Optional[Union[str, int]] = None, route_name: str = DEFAULT_ROUTE_NAME,
web_app: Optional[Application] = None):
"""
Set webhook for bot
:param dispatcher: Dispatcher
:param webhook_path: str
:param loop: Optional[asyncio.AbstractEventLoop] (default: None)
:param skip_updates: bool (default: None)
:param on_startup: Optional[Callable] (default: None)
:param on_shutdown: Optional[Callable] (default: None)
:param check_ip: bool (default: False)
:param retry_after: Optional[Union[str, int]] See https://tools.ietf.org/html/rfc7231#section-7.1.3 (default: None)
:param route_name: str (default: 'webhook_handler')
:param web_app: Optional[Application] (default: None)
:return:
"""
executor = Executor(dispatcher, skip_updates=skip_updates, check_ip=check_ip, retry_after=retry_after,
loop=loop)
_setup_callbacks(executor, on_startup, on_shutdown)
executor.set_webhook(webhook_path, route_name=route_name, web_app=web_app)
return executor
def start_webhook(dispatcher, webhook_path, *, loop=None, skip_updates=None,
on_startup=None, on_shutdown=None, check_ip=False, retry_after=None, route_name=DEFAULT_ROUTE_NAME,
**kwargs):
"""
Start bot in webhook mode
:param dispatcher:
:param webhook_path:
:param loop:
:param skip_updates:
:param on_startup:
:param on_shutdown:
:param check_ip:
:param route_name:
:param kwargs:
:return:
"""
executor = set_webhook(dispatcher=dispatcher,
webhook_path=webhook_path,
loop=loop,
skip_updates=skip_updates,
on_startup=on_startup,
on_shutdown=on_shutdown,
check_ip=check_ip,
retry_after=retry_after,
route_name=route_name)
executor.run_app(**kwargs)
def start(dispatcher, future, *, loop=None, skip_updates=None,
on_startup=None, on_shutdown=None):
"""
Execute Future.
:param dispatcher: instance of Dispatcher
:param future: future
:param loop: instance of AbstractEventLoop
:param skip_updates:
:param on_startup:
:param on_shutdown:
:return:
"""
executor = Executor(dispatcher, skip_updates=skip_updates, loop=loop)
_setup_callbacks(executor, on_startup, on_shutdown)
return executor.start(future)
class Executor:
"""
Main executor class
"""
def __init__(self, dispatcher, skip_updates=None, check_ip=False, retry_after=None, loop=None):
if loop is not None:
self._loop = loop
self.dispatcher = dispatcher
self.skip_updates = skip_updates
self.check_ip = check_ip
self.retry_after = retry_after
self._identity = secrets.token_urlsafe(16)
self._web_app = None
self._on_startup_webhook = []
self._on_startup_polling = []
self._on_shutdown_webhook = []
self._on_shutdown_polling = []
self._freeze = False
from aiogram import Bot, Dispatcher
Bot.set_current(dispatcher.bot)
Dispatcher.set_current(dispatcher)
@property
def loop(self) -> asyncio.AbstractEventLoop:
return getattr(self, "_loop", asyncio.get_event_loop())
@property
def frozen(self):
return self._freeze
def set_web_app(self, application: web.Application):
"""
Change instance of aiohttp.web.Application
:param application:
"""
self._web_app = application
@property
def web_app(self) -> web.Application:
if self._web_app is None:
raise RuntimeError('web.Application() is not configured!')
return self._web_app
def on_startup(self, callback: callable, polling=True, webhook=True):
"""
Register a callback for the startup process
:param callback:
:param polling: use with polling
:param webhook: use with webhook
"""
self._check_frozen()
if not webhook and not polling:
warn('This action has no effect!', UserWarning)
return
if isinstance(callback, (list, tuple, set)):
for cb in callback:
self.on_startup(cb, polling, webhook)
return
if polling:
self._on_startup_polling.append(callback)
if webhook:
self._on_startup_webhook.append(callback)
def on_shutdown(self, callback: callable, polling=True, webhook=True):
"""
Register a callback for the shutdown process
:param callback:
:param polling: use with polling
:param webhook: use with webhook
"""
self._check_frozen()
if not webhook and not polling:
warn('This action has no effect!', UserWarning)
return
if isinstance(callback, (list, tuple, set)):
for cb in callback:
self.on_shutdown(cb, polling, webhook)
return
if polling:
self._on_shutdown_polling.append(callback)
if webhook:
self._on_shutdown_webhook.append(callback)
def _check_frozen(self):
if self.frozen:
raise RuntimeError('Executor is frozen!')
def _prepare_polling(self):
self._check_frozen()
self._freeze = True
# self.loop.set_task_factory(context.task_factory)
def _prepare_webhook(self, path=None, handler=WebhookRequestHandler, route_name=DEFAULT_ROUTE_NAME, app=None):
self._check_frozen()
self._freeze = True
# self.loop.set_task_factory(context.task_factory)
if app is not None:
self._web_app = app
elif self._web_app is None:
self._web_app = app = web.Application()
else:
raise RuntimeError("web.Application() is already configured!")
if self.retry_after:
app['RETRY_AFTER'] = self.retry_after
if self._identity == app.get(self._identity):
# App is already configured
return
if path is not None:
app.router.add_route('*', path, handler, name=route_name)
async def _wrap_callback(cb, _):
return await cb(self.dispatcher)
for callback in self._on_startup_webhook:
app.on_startup.append(functools.partial(_wrap_callback, callback))
# for callback in self._on_shutdown_webhook:
# app.on_shutdown.append(functools.partial(_wrap_callback, callback))
async def _on_shutdown(_):
await self._shutdown_webhook()
app.on_shutdown.append(_on_shutdown)
app[APP_EXECUTOR_KEY] = self
app[BOT_DISPATCHER_KEY] = self.dispatcher
app[self._identity] = datetime.datetime.now()
app['_check_ip'] = self.check_ip
def set_webhook(self, webhook_path: Optional[str] = None, request_handler: Any = WebhookRequestHandler,
route_name: str = DEFAULT_ROUTE_NAME, web_app: Optional[Application] = None):
"""
Set webhook for bot
:param webhook_path: Optional[str] (default: None)
:param request_handler: Any (default: WebhookRequestHandler)
:param route_name: str Name of webhook handler route (default: 'webhook_handler')
:param web_app: Optional[Application] (default: None)
:return:
"""
self._prepare_webhook(webhook_path, request_handler, route_name, web_app)
self.loop.run_until_complete(self._startup_webhook())
def run_app(self, **kwargs):
web.run_app(self._web_app, **kwargs)
def start_webhook(self, webhook_path=None, request_handler=WebhookRequestHandler, route_name=DEFAULT_ROUTE_NAME,
**kwargs):
"""
Start bot in webhook mode
:param webhook_path:
:param request_handler:
:param route_name: Name of webhook handler route
:param kwargs:
:return:
"""
self.set_webhook(webhook_path=webhook_path, request_handler=request_handler, route_name=route_name)
self.run_app(**kwargs)
def start_polling(self, reset_webhook=None, timeout=20, relax=0.1, fast=True,
allowed_updates: Optional[List[str]] = None):
"""
Start bot in long-polling mode
:param reset_webhook:
:param timeout:
"""
self._prepare_polling()
loop: asyncio.AbstractEventLoop = self.loop
try:
loop.run_until_complete(self._startup_polling())
loop.create_task(self.dispatcher.start_polling(reset_webhook=reset_webhook, timeout=timeout,
relax=relax, fast=fast, allowed_updates=allowed_updates))
loop.run_forever()
except (KeyboardInterrupt, SystemExit):
# loop.stop()
pass
finally:
loop.run_until_complete(self._shutdown_polling())
log.warning("Goodbye!")
def start(self, future):
"""
Execute Future.
Return the Future's result, or raise its exception.
:param future:
:return:
"""
self._check_frozen()
self._freeze = True
loop: asyncio.AbstractEventLoop = self.loop
try:
loop.run_until_complete(self._startup_polling())
result = loop.run_until_complete(future)
except (KeyboardInterrupt, SystemExit):
result = None
loop.stop()
finally:
loop.run_until_complete(self._shutdown_polling())
log.warning("Goodbye!")
return result
async def _skip_updates(self):
await self.dispatcher.reset_webhook(True)
await self.dispatcher.skip_updates()
log.warning(f'Updates were skipped successfully.')
async def _welcome(self):
user = await self.dispatcher.bot.me
log.info(f"Bot: {user.full_name} [@{user.username}]")
async def _shutdown(self):
self.dispatcher.stop_polling()
await self.dispatcher.storage.close()
await self.dispatcher.storage.wait_closed()
await self.dispatcher.bot.session.close()
async def _startup_polling(self):
await self._welcome()
if self.skip_updates:
await self._skip_updates()
for callback in self._on_startup_polling:
await callback(self.dispatcher)
async def _shutdown_polling(self, wait_closed=False):
for callback in self._on_shutdown_polling:
await callback(self.dispatcher)
await self._shutdown()
if wait_closed:
await self.dispatcher.wait_closed()
async def _shutdown_webhook(self, wait_closed=False):
for callback in self._on_shutdown_webhook:
await callback(self.dispatcher)
await self._shutdown()
if wait_closed:
await self.dispatcher.wait_closed()
async def _startup_webhook(self):
await self._welcome()
if self.skip_updates:
await self._skip_updates()