diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..5a02cb11 --- /dev/null +++ b/.flake8 @@ -0,0 +1,12 @@ +[flake8] +max-line-length = 80 +select = C,E,F,W,B,B950 +ignore = E501,W503,E203 +exclude = + .git + build + dist + venv + docs + *.egg-info + experiment.py diff --git a/README.md b/README.md index 9f977023..b3dd3a0b 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ [![Supported python versions](https://img.shields.io/pypi/pyversions/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) [![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-4.3-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api) [![Documentation Status](https://img.shields.io/readthedocs/pip/stable.svg?style=flat-square)](http://aiogram.readthedocs.io/en/latest/?badge=latest) +[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square)](https://github.com/python/black) [![Github issues](https://img.shields.io/github/issues/aiogram/aiogram.svg?style=flat-square)](https://github.com/aiogram/aiogram/issues) [![MIT License](https://img.shields.io/pypi/l/aiogram.svg?style=flat-square)](https://opensource.org/licenses/MIT) diff --git a/README.rst b/README.rst index 0377aad9..0070de61 100644 --- a/README.rst +++ b/README.rst @@ -26,9 +26,13 @@ AIOGramBot :alt: Telegram Bot API .. image:: https://img.shields.io/readthedocs/pip/stable.svg?style=flat-square - :target: http://aiogram.readthedocs.io/en/latest/?badge=latest + :target: http://aiogram.readthedocs.io/en/latest/?badge=latest?style=flat-square :alt: Documentation Status +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square + :target: https://github.com/python/black + :alt: Code style: Black + .. image:: https://img.shields.io/github/issues/aiogram/aiogram.svg?style=flat-square :target: https://github.com/aiogram/aiogram/issues :alt: Github issues diff --git a/aiogram/__init__.py b/aiogram/__init__.py index a1c2736b..5b183967 100644 --- a/aiogram/__init__.py +++ b/aiogram/__init__.py @@ -17,26 +17,26 @@ try: except ImportError: uvloop = None else: - if 'DISABLE_UVLOOP' not in os.environ: + if "DISABLE_UVLOOP" not in os.environ: asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) __all__ = [ - 'Bot', - 'Dispatcher', - '__api_version__', - '__version__', - 'bot', - 'contrib', - 'dispatcher', - 'exceptions', - 'executor', - 'filters', - 'helper', - 'md', - 'middlewares', - 'types', - 'utils' + "Bot", + "Dispatcher", + "__api_version__", + "__version__", + "bot", + "contrib", + "dispatcher", + "exceptions", + "executor", + "filters", + "helper", + "md", + "middlewares", + "types", + "utils", ] -__version__ = '2.2.1.dev1' -__api_version__ = '4.3' +__version__ = "2.2.1.dev1" +__api_version__ = "4.3" diff --git a/aiogram/__main__.py b/aiogram/__main__.py index bcbbbe0b..787f8f9a 100644 --- a/aiogram/__main__.py +++ b/aiogram/__main__.py @@ -18,7 +18,7 @@ class SysInfo: @property def python(self): - return sys.version.replace('\n', '') + return sys.version.replace("\n", "") @property def aiogram(self): @@ -57,27 +57,27 @@ class SysInfo: return aiohttp.__version__ def collect(self): - yield f'{self.python_implementation}: {self.python}' - yield f'OS: {self.os}' - yield f'aiogram: {self.aiogram}' - yield f'aiohttp: {self.aiohttp}' + yield f"{self.python_implementation}: {self.python}" + yield f"OS: {self.os}" + yield f"aiogram: {self.aiogram}" + yield f"aiohttp: {self.aiohttp}" uvloop = self.uvloop if uvloop: - yield f'uvloop: {uvloop}' + yield f"uvloop: {uvloop}" - yield f'JSON mode: {json.mode}' + yield f"JSON mode: {json.mode}" rapidjson = self.rapidjson if rapidjson: - yield f'rapidjson: {rapidjson}' + yield f"rapidjson: {rapidjson}" ujson = self.ujson if ujson: - yield f'ujson: {ujson}' + yield f"ujson: {ujson}" def __str__(self): - return '\n'.join(self.collect()) + return "\n".join(self.collect()) -if __name__ == '__main__': +if __name__ == "__main__": print(SysInfo()) diff --git a/aiogram/bot/__init__.py b/aiogram/bot/__init__.py index 252c465b..78d1719d 100644 --- a/aiogram/bot/__init__.py +++ b/aiogram/bot/__init__.py @@ -2,8 +2,4 @@ from . import api from .base import BaseBot from .bot import Bot -__all__ = [ - 'BaseBot', - 'Bot', - 'api' -] +__all__ = ["BaseBot", "Bot", "api"] diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index 6c51b295..580a4a69 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -10,7 +10,7 @@ from ..utils import json from ..utils.helper import Helper, HelperMode, Item # Main aiogram logger -log = logging.getLogger('aiogram') +log = logging.getLogger("aiogram") # API Url's API_URL = "https://api.telegram.org/bot{token}/{method}" @@ -25,11 +25,11 @@ def check_token(token: str) -> bool: :return: """ if any(x.isspace() for x in token): - raise exceptions.ValidationError('Token is invalid!') + raise exceptions.ValidationError("Token is invalid!") - left, sep, right = token.partition(':') + left, sep, right = token.partition(":") if (not sep) or (not left.isdigit()) or (len(left) < 3): - raise exceptions.ValidationError('Token is invalid!') + raise exceptions.ValidationError("Token is invalid!") return True @@ -51,19 +51,21 @@ def check_result(method_name: str, content_type: str, status_code: int, body: st """ log.debug('Response for %s: [%d] "%r"', method_name, status_code, body) - if content_type != 'application/json': - raise exceptions.NetworkError(f"Invalid response with content type {content_type}: \"{body}\"") + if content_type != "application/json": + raise exceptions.NetworkError( + f'Invalid response with content type {content_type}: "{body}"' + ) try: result_json = json.loads(body) except ValueError: result_json = {} - description = result_json.get('description') or body - parameters = types.ResponseParameters(**result_json.get('parameters', {}) or {}) + description = result_json.get("description") or body + parameters = types.ResponseParameters(**result_json.get("parameters", {}) or {}) if HTTPStatus.OK <= status_code <= HTTPStatus.IM_USED: - return result_json.get('result') + return result_json.get("result") elif parameters.retry_after: raise exceptions.RetryAfter(parameters.retry_after) elif parameters.migrate_to_chat_id: @@ -77,10 +79,12 @@ def check_result(method_name: str, content_type: str, status_code: int, body: st elif status_code in [HTTPStatus.UNAUTHORIZED, HTTPStatus.FORBIDDEN]: exceptions.Unauthorized.detect(description) elif status_code == HTTPStatus.REQUEST_ENTITY_TOO_LARGE: - raise exceptions.NetworkError('File too large for uploading. ' - 'Check telegram api limits https://core.telegram.org/bots/api#senddocument') + raise exceptions.NetworkError( + "File too large for uploading. " + "Check telegram api limits https://core.telegram.org/bots/api#senddocument" + ) elif status_code >= HTTPStatus.INTERNAL_SERVER_ERROR: - if 'restart' in description: + if "restart" in description: raise exceptions.RestartingTelegram() raise exceptions.TelegramAPIError(description) raise exceptions.TelegramAPIError(f"{description} [{status_code}]") @@ -95,9 +99,13 @@ async def make_request(session, token, method, data=None, files=None, **kwargs): req = compose_data(data, files) try: async with session.post(url, data=req, **kwargs) as response: - return check_result(method, response.content_type, response.status, await response.text()) + return check_result( + method, response.content_type, response.status, await response.text() + ) except aiohttp.ClientError as e: - raise exceptions.NetworkError(f"aiohttp client throws an error: {e.__class__.__name__}: {e}") + raise exceptions.NetworkError( + f"aiohttp client throws an error: {e.__class__.__name__}: {e}" + ) def guess_filename(obj): @@ -107,8 +115,8 @@ def guess_filename(obj): :param obj: :return: """ - name = getattr(obj, 'name', None) - if name and isinstance(name, str) and name[0] != '<' and name[-1] != '>': + name = getattr(obj, "name", None) + if name and isinstance(name, str) and name[0] != "<" and name[-1] != ">": return os.path.basename(name) @@ -132,7 +140,7 @@ def compose_data(params=None, files=None): if len(f) == 2: filename, fileobj = f else: - raise ValueError('Tuple must have exactly 2 elements: filename, fileobj') + raise ValueError("Tuple must have exactly 2 elements: filename, fileobj") elif isinstance(f, types.InputFile): filename, fileobj = f.filename, f.file else: @@ -149,6 +157,7 @@ class Methods(Helper): List is updated to Bot API 4.3 """ + mode = HelperMode.lowerCamelCase # Getting Updates diff --git a/aiogram/bot/base.py b/aiogram/bot/base.py index 85773e30..1a4cf5b2 100644 --- a/aiogram/bot/base.py +++ b/aiogram/bot/base.py @@ -20,19 +20,22 @@ class BaseBot: """ Base class for bot. It's raw bot. """ - _ctx_timeout = ContextVar('TelegramRequestTimeout') - _ctx_token = ContextVar('BotDifferentToken') + + _ctx_timeout = ContextVar("TelegramRequestTimeout") + _ctx_token = ContextVar("BotDifferentToken") def __init__( - self, - token: base.String, - loop: Optional[Union[asyncio.BaseEventLoop, asyncio.AbstractEventLoop]] = None, - connections_limit: Optional[base.Integer] = None, - proxy: Optional[base.String] = None, - proxy_auth: Optional[aiohttp.BasicAuth] = None, - validate_token: Optional[base.Boolean] = True, - parse_mode: typing.Optional[base.String] = None, - timeout: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] = None + self, + token: base.String, + loop: Optional[asyncio.AbstractEventLoop] = None, + connections_limit: Optional[base.Integer] = None, + proxy: Optional[base.String] = None, + proxy_auth: Optional[aiohttp.BasicAuth] = None, + validate_token: Optional[base.Boolean] = True, + parse_mode: typing.Optional[base.String] = None, + timeout: typing.Optional[ + typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout] + ] = None, ): """ Instructions how to get Bot token is found here: https://core.telegram.org/bots#3-how-do-i-create-a-bot @@ -72,7 +75,9 @@ class BaseBot: # aiohttp main session ssl_context = ssl.create_default_context(cafile=certifi.where()) - if isinstance(proxy, str) and (proxy.startswith('socks5://') or proxy.startswith('socks4://')): + if isinstance(proxy, str) and ( + proxy.startswith("socks5://") or proxy.startswith("socks4://") + ): from aiohttp_socks import SocksConnector from aiohttp_socks.helpers import parse_socks_url @@ -83,25 +88,36 @@ class BaseBot: if not password: password = proxy_auth.password - connector = SocksConnector(socks_ver=socks_ver, host=host, port=port, - username=username, password=password, - limit=connections_limit, ssl_context=ssl_context, - rdns=True, loop=self.loop) + connector = SocksConnector( + socks_ver=socks_ver, + host=host, + port=port, + username=username, + password=password, + limit=connections_limit, + ssl_context=ssl_context, + rdns=True, + loop=self.loop, + ) self.proxy = None self.proxy_auth = None else: - connector = aiohttp.TCPConnector(limit=connections_limit, ssl=ssl_context, loop=self.loop) + connector = aiohttp.TCPConnector( + limit=connections_limit, ssl=ssl_context, loop=self.loop + ) self._timeout = None self.timeout = timeout - self.session = aiohttp.ClientSession(connector=connector, loop=self.loop, json_serialize=json.dumps) + self.session = aiohttp.ClientSession( + connector=connector, loop=self.loop, json_serialize=json.dumps + ) self.parse_mode = parse_mode @staticmethod def _prepare_timeout( - value: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] + value: typing.Optional[typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]] ) -> typing.Optional[aiohttp.ClientTimeout]: if value is None or isinstance(value, aiohttp.ClientTimeout): return value @@ -123,7 +139,9 @@ class BaseBot: self.timeout = None @contextlib.contextmanager - def request_timeout(self, timeout: typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout]): + def request_timeout( + self, timeout: typing.Union[base.Integer, base.Float, aiohttp.ClientTimeout] + ): """ Context manager implements opportunity to change request timeout in current context @@ -162,9 +180,13 @@ class BaseBot: """ await self.session.close() - async def request(self, method: base.String, - data: Optional[Dict] = None, - files: Optional[Dict] = None, **kwargs) -> Union[List, Dict, base.Boolean]: + async def request( + self, + method: base.String, + data: Optional[Dict] = None, + files: Optional[Dict] = None, + **kwargs, + ) -> Union[List, Dict, base.Boolean]: """ Make an request to Telegram Bot API @@ -180,14 +202,26 @@ class BaseBot: :rtype: Union[List, Dict] :raise: :obj:`aiogram.exceptions.TelegramApiError` """ - return await api.make_request(self.session, self.__token, method, data, files, - proxy=self.proxy, proxy_auth=self.proxy_auth, timeout=self.timeout, **kwargs) + return await api.make_request( + self.session, + self.__token, + method, + data, + files, + proxy=self.proxy, + proxy_auth=self.proxy_auth, + timeout=self.timeout, + **kwargs, + ) - async def download_file(self, file_path: base.String, - destination: Optional[base.InputFile] = None, - timeout: Optional[base.Integer] = sentinel, - chunk_size: Optional[base.Integer] = 65536, - seek: Optional[base.Boolean] = True) -> Union[io.BytesIO, io.FileIO]: + async def download_file( + self, + file_path: base.String, + destination: Optional[base.InputFile] = None, + timeout: Optional[base.Integer] = sentinel, + chunk_size: Optional[base.Integer] = 65536, + seek: Optional[base.Boolean] = True, + ) -> Union[io.BytesIO, io.FileIO]: """ Download file by file_path to destination @@ -207,8 +241,10 @@ class BaseBot: url = self.get_file_url(file_path) - dest = destination if isinstance(destination, io.IOBase) else open(destination, 'wb') - async with self.session.get(url, timeout=timeout, proxy=self.proxy, proxy_auth=self.proxy_auth) as response: + dest = destination if isinstance(destination, io.IOBase) else open(destination, "wb") + async with self.session.get( + url, timeout=timeout, proxy=self.proxy, proxy_auth=self.proxy_auth + ) as response: while True: chunk = await response.content.read(chunk_size) if not chunk: @@ -247,19 +283,19 @@ class BaseBot: @property def parse_mode(self): - return getattr(self, '_parse_mode', None) + return getattr(self, "_parse_mode", None) @parse_mode.setter def parse_mode(self, value): if value is None: - setattr(self, '_parse_mode', None) + setattr(self, "_parse_mode", None) else: if not isinstance(value, str): raise TypeError(f"Parse mode must be str, not {type(value)}") value = value.lower() if value not in ParseMode.all(): raise ValueError(f"Parse mode must be one of {ParseMode.all()}") - setattr(self, '_parse_mode', value) + setattr(self, "_parse_mode", value) @parse_mode.deleter def parse_mode(self): diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index b0fc3725..cda742e1 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -21,9 +21,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :return: :class:`aiogram.types.User` """ - if not hasattr(self, '_me'): - setattr(self, '_me', await self.get_me()) - return getattr(self, '_me') + if not hasattr(self, "_me"): + setattr(self, "_me", await self.get_me()) + return getattr(self, "_me") @me.deleter def me(self): @@ -36,12 +36,17 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :return: :obj:`aiogram.types.User` """ - if hasattr(self, '_me'): - delattr(self, '_me') + if hasattr(self, "_me"): + delattr(self, "_me") - async def download_file_by_id(self, file_id: base.String, destination=None, - timeout: base.Integer = 30, chunk_size: base.Integer = 65536, - seek: base.Boolean = True): + async def download_file_by_id( + self, + file_id: base.String, + destination=None, + timeout: base.Integer = 30, + chunk_size: base.Integer = 65536, + seek: base.Boolean = True, + ): """ Download file by file_id to destination @@ -56,17 +61,24 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :return: destination """ file = await self.get_file(file_id) - return await self.download_file(file_path=file.file_path, destination=destination, - timeout=timeout, chunk_size=chunk_size, seek=seek) + return await self.download_file( + file_path=file.file_path, + destination=destination, + timeout=timeout, + chunk_size=chunk_size, + seek=seek, + ) # === Getting updates === # https://core.telegram.org/bots/api#getting-updates - async def get_updates(self, offset: typing.Union[base.Integer, None] = None, - limit: typing.Union[base.Integer, None] = None, - timeout: typing.Union[base.Integer, None] = None, - allowed_updates: - typing.Union[typing.List[base.String], None] = None) -> typing.List[types.Update]: + async def get_updates( + self, + offset: typing.Union[base.Integer, None] = None, + limit: typing.Union[base.Integer, None] = None, + timeout: typing.Union[base.Integer, None] = None, + allowed_updates: typing.Union[typing.List[base.String], None] = None, + ) -> typing.List[types.Update]: """ Use this method to receive incoming updates using long polling (wiki). @@ -93,10 +105,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_UPDATES, payload) return [types.Update(**update) for update in result] - async def set_webhook(self, url: base.String, - certificate: typing.Union[base.InputFile, None] = None, - max_connections: typing.Union[base.Integer, None] = None, - allowed_updates: typing.Union[typing.List[base.String], None] = None) -> base.Boolean: + async def set_webhook( + self, + url: base.String, + certificate: typing.Union[base.InputFile, None] = None, + max_connections: typing.Union[base.Integer, None] = None, + allowed_updates: typing.Union[typing.List[base.String], None] = None, + ) -> base.Boolean: """ Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, @@ -118,10 +133,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`base.Boolean` """ allowed_updates = prepare_arg(allowed_updates) - payload = generate_payload(**locals(), exclude=['certificate']) + payload = generate_payload(**locals(), exclude=["certificate"]) files = {} - prepare_file(payload, files, 'certificate', certificate) + prepare_file(payload, files, "certificate", certificate) result = await self.request(api.Methods.SET_WEBHOOK, payload, files) return result @@ -174,15 +189,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_ME, payload) return types.User(**result) - async def send_message(self, chat_id: typing.Union[base.Integer, base.String], text: base.String, - parse_mode: typing.Union[base.String, None] = None, - disable_web_page_preview: typing.Union[base.Boolean, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_message( + self, + chat_id: typing.Union[base.Integer, base.String], + text: base.String, + parse_mode: typing.Union[base.String, None] = None, + disable_web_page_preview: typing.Union[base.Boolean, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send text messages. @@ -211,14 +233,18 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) result = await self.request(api.Methods.SEND_MESSAGE, payload) return types.Message(**result) - async def forward_message(self, chat_id: typing.Union[base.Integer, base.String], - from_chat_id: typing.Union[base.Integer, base.String], message_id: base.Integer, - disable_notification: typing.Union[base.Boolean, None] = None) -> types.Message: + async def forward_message( + self, + chat_id: typing.Union[base.Integer, base.String], + from_chat_id: typing.Union[base.Integer, base.String], + message_id: base.Integer, + disable_notification: typing.Union[base.Boolean, None] = None, + ) -> types.Message: """ Use this method to forward messages of any kind. @@ -240,16 +266,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.FORWARD_MESSAGE, payload) return types.Message(**result) - async def send_photo(self, chat_id: typing.Union[base.Integer, base.String], - photo: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_photo( + self, + chat_id: typing.Union[base.Integer, base.String], + photo: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send photos. @@ -276,30 +308,36 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['photo']) + payload = generate_payload(**locals(), exclude=["photo"]) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) files = {} - prepare_file(payload, files, 'photo', photo) + prepare_file(payload, files, "photo", photo) result = await self.request(api.Methods.SEND_PHOTO, payload, files) return types.Message(**result) - async def send_audio(self, chat_id: typing.Union[base.Integer, base.String], - audio: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - duration: typing.Union[base.Integer, None] = None, - performer: typing.Union[base.String, None] = None, - title: typing.Union[base.String, None] = None, - thumb: typing.Union[base.InputFile, base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_audio( + self, + chat_id: typing.Union[base.Integer, base.String], + audio: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + duration: typing.Union[base.Integer, None] = None, + performer: typing.Union[base.String, None] = None, + title: typing.Union[base.String, None] = None, + thumb: typing.Union[base.InputFile, base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -337,27 +375,33 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['audio']) + payload = generate_payload(**locals(), exclude=["audio"]) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) files = {} - prepare_file(payload, files, 'audio', audio) + prepare_file(payload, files, "audio", audio) result = await self.request(api.Methods.SEND_AUDIO, payload, files) return types.Message(**result) - async def send_document(self, chat_id: typing.Union[base.Integer, base.String], - document: typing.Union[base.InputFile, base.String], - thumb: typing.Union[base.InputFile, base.String, None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_document( + self, + chat_id: typing.Union[base.Integer, base.String], + document: typing.Union[base.InputFile, base.String], + thumb: typing.Union[base.InputFile, base.String, None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send general files. @@ -388,31 +432,37 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['document']) + payload = generate_payload(**locals(), exclude=["document"]) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) files = {} - prepare_file(payload, files, 'document', document) + prepare_file(payload, files, "document", document) result = await self.request(api.Methods.SEND_DOCUMENT, payload, files) return types.Message(**result) - async def send_video(self, chat_id: typing.Union[base.Integer, base.String], - video: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - thumb: typing.Union[base.InputFile, base.String, None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - supports_streaming: typing.Union[base.Boolean, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_video( + self, + chat_id: typing.Union[base.Integer, base.String], + video: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + thumb: typing.Union[base.InputFile, base.String, None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + supports_streaming: typing.Union[base.Boolean, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -450,33 +500,39 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['video', 'thumb']) + payload = generate_payload(**locals(), exclude=["video", "thumb"]) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) files = {} - prepare_file(payload, files, 'video', video) - prepare_attachment(payload, files, 'thumb', thumb) + prepare_file(payload, files, "video", video) + prepare_attachment(payload, files, "thumb", thumb) result = await self.request(api.Methods.SEND_VIDEO, payload, files) return types.Message(**result) - async def send_animation(self, - chat_id: typing.Union[base.Integer, base.String], - animation: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply], None] = None - ) -> types.Message: + async def send_animation( + self, + chat_id: typing.Union[base.Integer, base.String], + animation: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + ], + None, + ] = None, + ) -> types.Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). @@ -521,23 +577,29 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): payload = generate_payload(**locals(), exclude=["animation", "thumb"]) files = {} - prepare_file(payload, files, 'animation', animation) - prepare_attachment(payload, files, 'thumb', thumb) + prepare_file(payload, files, "animation", animation) + prepare_attachment(payload, files, "thumb", thumb) result = await self.request(api.Methods.SEND_ANIMATION, payload, files) return types.Message(**result) - async def send_voice(self, chat_id: typing.Union[base.Integer, base.String], - voice: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - duration: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_voice( + self, + chat_id: typing.Union[base.Integer, base.String], + voice: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + duration: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -570,27 +632,33 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['voice']) + payload = generate_payload(**locals(), exclude=["voice"]) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) files = {} - prepare_file(payload, files, 'voice', voice) + prepare_file(payload, files, "voice", voice) result = await self.request(api.Methods.SEND_VOICE, payload, files) return types.Message(**result) - async def send_video_note(self, chat_id: typing.Union[base.Integer, base.String], - video_note: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - length: typing.Union[base.Integer, None] = None, - thumb: typing.Union[base.InputFile, base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_video_note( + self, + chat_id: typing.Union[base.Integer, base.String], + video_note: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + length: typing.Union[base.Integer, None] = None, + thumb: typing.Union[base.InputFile, base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. @@ -619,19 +687,21 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['video_note']) + payload = generate_payload(**locals(), exclude=["video_note"]) files = {} - prepare_file(payload, files, 'video_note', video_note) + prepare_file(payload, files, "video_note", video_note) result = await self.request(api.Methods.SEND_VIDEO_NOTE, payload, files) return types.Message(**result) - async def send_media_group(self, chat_id: typing.Union[base.Integer, base.String], - media: typing.Union[types.MediaGroup, typing.List], - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, - None] = None) -> typing.List[types.Message]: + async def send_media_group( + self, + chat_id: typing.Union[base.Integer, base.String], + media: typing.Union[types.MediaGroup, typing.List], + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + ) -> typing.List[types.Message]: """ Use this method to send a group of photos or videos as an album. @@ -655,20 +725,27 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): files = dict(media.get_files()) media = prepare_arg(media) - payload = generate_payload(**locals(), exclude=['files']) + payload = generate_payload(**locals(), exclude=["files"]) result = await self.request(api.Methods.SEND_MEDIA_GROUP, payload, files) return [types.Message(**message) for message in result] - async def send_location(self, chat_id: typing.Union[base.Integer, base.String], - latitude: base.Float, longitude: base.Float, - live_period: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_location( + self, + chat_id: typing.Union[base.Integer, base.String], + latitude: base.Float, + longitude: base.Float, + live_period: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send point on the map. @@ -699,12 +776,15 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_LOCATION, payload) return types.Message(**result) - async def edit_message_live_location(self, latitude: base.Float, longitude: base.Float, - chat_id: typing.Union[base.Integer, base.String, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + async def edit_message_live_location( + self, + latitude: base.Float, + longitude: base.Float, + chat_id: typing.Union[base.Integer, base.String, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message or base.Boolean: """ Use this method to edit live location messages sent by the bot or via the bot (for inline bots). A location can be edited until its live_period expires or editing is explicitly disabled by a call @@ -736,12 +816,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return result return types.Message(**result) - async def stop_message_live_location(self, - chat_id: typing.Union[base.Integer, base.String, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + async def stop_message_live_location( + self, + chat_id: typing.Union[base.Integer, base.String, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message or base.Boolean: """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires. @@ -768,17 +849,25 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return result return types.Message(**result) - async def send_venue(self, chat_id: typing.Union[base.Integer, base.String], - latitude: base.Float, longitude: base.Float, - title: base.String, address: base.String, - foursquare_id: typing.Union[base.String, None] = None, - foursquare_type: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_venue( + self, + chat_id: typing.Union[base.Integer, base.String], + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Union[base.String, None] = None, + foursquare_type: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send information about a venue. @@ -815,16 +904,23 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_VENUE, payload) return types.Message(**result) - async def send_contact(self, chat_id: typing.Union[base.Integer, base.String], - phone_number: base.String, first_name: base.String, - last_name: typing.Union[base.String, None] = None, - vcard: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_contact( + self, + chat_id: typing.Union[base.Integer, base.String], + phone_number: base.String, + first_name: base.String, + last_name: typing.Union[base.String, None] = None, + vcard: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send phone contacts. @@ -857,15 +953,21 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_CONTACT, payload) return types.Message(**result) - async def send_poll(self, chat_id: typing.Union[base.Integer, base.String], - question: base.String, - options: typing.List[base.String], - disable_notification: typing.Optional[base.Boolean], - reply_to_message_id: typing.Union[base.Integer, None], - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_poll( + self, + chat_id: typing.Union[base.Integer, base.String], + question: base.String, + options: typing.List[base.String], + disable_notification: typing.Optional[base.Boolean], + reply_to_message_id: typing.Union[base.Integer, None], + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send a native poll. A native poll can't be sent to a private chat. On success, the sent Message is returned. @@ -895,8 +997,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_POLL, payload) return types.Message(**result) - async def send_chat_action(self, chat_id: typing.Union[base.Integer, base.String], - action: base.String) -> base.Boolean: + async def send_chat_action( + self, chat_id: typing.Union[base.Integer, base.String], action: base.String + ) -> base.Boolean: """ Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less @@ -919,8 +1022,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_CHAT_ACTION, payload) return result - async def get_user_profile_photos(self, user_id: base.Integer, offset: typing.Union[base.Integer, None] = None, - limit: typing.Union[base.Integer, None] = None) -> types.UserProfilePhotos: + async def get_user_profile_photos( + self, + user_id: base.Integer, + offset: typing.Union[base.Integer, None] = None, + limit: typing.Union[base.Integer, None] = None, + ) -> types.UserProfilePhotos: """ Use this method to get a list of profile pictures for a user. Returns a UserProfilePhotos object. @@ -960,8 +1067,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_FILE, payload) return types.File(**result) - async def kick_chat_member(self, chat_id: typing.Union[base.Integer, base.String], user_id: base.Integer, - until_date: typing.Union[base.Integer, None] = None) -> base.Boolean: + async def kick_chat_member( + self, + chat_id: typing.Union[base.Integer, base.String], + user_id: base.Integer, + until_date: typing.Union[base.Integer, None] = None, + ) -> base.Boolean: """ Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the group @@ -990,8 +1101,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.KICK_CHAT_MEMBER, payload) return result - async def unban_chat_member(self, chat_id: typing.Union[base.Integer, base.String], - user_id: base.Integer) -> base.Boolean: + async def unban_chat_member( + self, chat_id: typing.Union[base.Integer, base.String], user_id: base.Integer + ) -> base.Boolean: """ Use this method to unban a previously kicked user in a supergroup or channel. ` The user will not return to the group or channel automatically, but will be able to join via link, etc. @@ -1012,13 +1124,16 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.UNBAN_CHAT_MEMBER, payload) return result - async def restrict_chat_member(self, chat_id: typing.Union[base.Integer, base.String], - user_id: base.Integer, - until_date: typing.Union[base.Integer, None] = None, - can_send_messages: typing.Union[base.Boolean, None] = None, - can_send_media_messages: typing.Union[base.Boolean, None] = None, - can_send_other_messages: typing.Union[base.Boolean, None] = None, - can_add_web_page_previews: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def restrict_chat_member( + self, + chat_id: typing.Union[base.Integer, base.String], + user_id: base.Integer, + until_date: typing.Union[base.Integer, None] = None, + can_send_messages: typing.Union[base.Boolean, None] = None, + can_send_media_messages: typing.Union[base.Boolean, None] = None, + can_send_other_messages: typing.Union[base.Boolean, None] = None, + can_add_web_page_previews: typing.Union[base.Boolean, None] = None, + ) -> base.Boolean: """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. @@ -1052,16 +1167,19 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.RESTRICT_CHAT_MEMBER, payload) return result - async def promote_chat_member(self, chat_id: typing.Union[base.Integer, base.String], - user_id: base.Integer, - can_change_info: typing.Union[base.Boolean, None] = None, - can_post_messages: typing.Union[base.Boolean, None] = None, - can_edit_messages: typing.Union[base.Boolean, None] = None, - can_delete_messages: typing.Union[base.Boolean, None] = None, - can_invite_users: typing.Union[base.Boolean, None] = None, - can_restrict_members: typing.Union[base.Boolean, None] = None, - can_pin_messages: typing.Union[base.Boolean, None] = None, - can_promote_members: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def promote_chat_member( + self, + chat_id: typing.Union[base.Integer, base.String], + user_id: base.Integer, + can_change_info: typing.Union[base.Boolean, None] = None, + can_post_messages: typing.Union[base.Boolean, None] = None, + can_edit_messages: typing.Union[base.Boolean, None] = None, + can_delete_messages: typing.Union[base.Boolean, None] = None, + can_invite_users: typing.Union[base.Boolean, None] = None, + can_restrict_members: typing.Union[base.Boolean, None] = None, + can_pin_messages: typing.Union[base.Boolean, None] = None, + can_promote_members: typing.Union[base.Boolean, None] = None, + ) -> base.Boolean: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1099,7 +1217,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.PROMOTE_CHAT_MEMBER, payload) return result - async def export_chat_invite_link(self, chat_id: typing.Union[base.Integer, base.String]) -> base.String: + async def export_chat_invite_link( + self, chat_id: typing.Union[base.Integer, base.String] + ) -> base.String: """ Use this method to generate a new invite link for a chat; any previously generated link is revoked. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1116,8 +1236,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.EXPORT_CHAT_INVITE_LINK, payload) return result - async def set_chat_photo(self, chat_id: typing.Union[base.Integer, base.String], - photo: base.InputFile) -> base.Boolean: + async def set_chat_photo( + self, chat_id: typing.Union[base.Integer, base.String], photo: base.InputFile + ) -> base.Boolean: """ Use this method to set a new profile photo for the chat. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1134,15 +1255,17 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :return: Returns True on success :rtype: :obj:`base.Boolean` """ - payload = generate_payload(**locals(), exclude=['photo']) + payload = generate_payload(**locals(), exclude=["photo"]) files = {} - prepare_file(payload, files, 'photo', photo) + prepare_file(payload, files, "photo", photo) result = await self.request(api.Methods.SET_CHAT_PHOTO, payload, files) return result - async def delete_chat_photo(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Boolean: + async def delete_chat_photo( + self, chat_id: typing.Union[base.Integer, base.String] + ) -> base.Boolean: """ Use this method to delete a chat photo. Photos can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1162,8 +1285,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.DELETE_CHAT_PHOTO, payload) return result - async def set_chat_title(self, chat_id: typing.Union[base.Integer, base.String], - title: base.String) -> base.Boolean: + async def set_chat_title( + self, chat_id: typing.Union[base.Integer, base.String], title: base.String + ) -> base.Boolean: """ Use this method to change the title of a chat. Titles can't be changed for private chats. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1185,8 +1309,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SET_CHAT_TITLE, payload) return result - async def set_chat_description(self, chat_id: typing.Union[base.Integer, base.String], - description: typing.Union[base.String, None] = None) -> base.Boolean: + async def set_chat_description( + self, + chat_id: typing.Union[base.Integer, base.String], + description: typing.Union[base.String, None] = None, + ) -> base.Boolean: """ Use this method to change the description of a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1205,8 +1332,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SET_CHAT_DESCRIPTION, payload) return result - async def pin_chat_message(self, chat_id: typing.Union[base.Integer, base.String], message_id: base.Integer, - disable_notification: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def pin_chat_message( + self, + chat_id: typing.Union[base.Integer, base.String], + message_id: base.Integer, + disable_notification: typing.Union[base.Boolean, None] = None, + ) -> base.Boolean: """ Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1228,7 +1359,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.PIN_CHAT_MESSAGE, payload) return result - async def unpin_chat_message(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Boolean: + async def unpin_chat_message( + self, chat_id: typing.Union[base.Integer, base.String] + ) -> base.Boolean: """ Use this method to unpin a message in a supergroup chat. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1278,8 +1411,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_CHAT, payload) return types.Chat(**result) - async def get_chat_administrators(self, chat_id: typing.Union[base.Integer, base.String] - ) -> typing.List[types.ChatMember]: + async def get_chat_administrators( + self, chat_id: typing.Union[base.Integer, base.String] + ) -> typing.List[types.ChatMember]: """ Use this method to get a list of administrators in a chat. @@ -1298,7 +1432,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_CHAT_ADMINISTRATORS, payload) return [types.ChatMember(**chatmember) for chatmember in result] - async def get_chat_members_count(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Integer: + async def get_chat_members_count( + self, chat_id: typing.Union[base.Integer, base.String] + ) -> base.Integer: """ Use this method to get the number of members in a chat. @@ -1314,8 +1450,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_CHAT_MEMBERS_COUNT, payload) return result - async def get_chat_member(self, chat_id: typing.Union[base.Integer, base.String], - user_id: base.Integer) -> types.ChatMember: + async def get_chat_member( + self, chat_id: typing.Union[base.Integer, base.String], user_id: base.Integer + ) -> types.ChatMember: """ Use this method to get information about a member of a chat. @@ -1333,8 +1470,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_CHAT_MEMBER, payload) return types.ChatMember(**result) - async def set_chat_sticker_set(self, chat_id: typing.Union[base.Integer, base.String], - sticker_set_name: base.String) -> base.Boolean: + async def set_chat_sticker_set( + self, chat_id: typing.Union[base.Integer, base.String], sticker_set_name: base.String + ) -> base.Boolean: """ Use this method to set a new group sticker set for a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1356,7 +1494,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SET_CHAT_STICKER_SET, payload) return result - async def delete_chat_sticker_set(self, chat_id: typing.Union[base.Integer, base.String]) -> base.Boolean: + async def delete_chat_sticker_set( + self, chat_id: typing.Union[base.Integer, base.String] + ) -> base.Boolean: """ Use this method to delete a group sticker set from a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1376,11 +1516,14 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.DELETE_CHAT_STICKER_SET, payload) return result - async def answer_callback_query(self, callback_query_id: base.String, - text: typing.Union[base.String, None] = None, - show_alert: typing.Union[base.Boolean, None] = None, - url: typing.Union[base.String, None] = None, - cache_time: typing.Union[base.Integer, None] = None) -> base.Boolean: + async def answer_callback_query( + self, + callback_query_id: base.String, + text: typing.Union[base.String, None] = None, + show_alert: typing.Union[base.Boolean, None] = None, + url: typing.Union[base.String, None] = None, + cache_time: typing.Union[base.Integer, None] = None, + ) -> base.Boolean: """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -1411,14 +1554,16 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.ANSWER_CALLBACK_QUERY, payload) return result - async def edit_message_text(self, text: base.String, - chat_id: typing.Union[base.Integer, base.String, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_web_page_preview: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + async def edit_message_text( + self, + text: base.String, + chat_id: typing.Union[base.Integer, base.String, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_web_page_preview: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message or base.Boolean: """ Use this method to edit text and game messages sent by the bot or via the bot (for inline bots). @@ -1447,20 +1592,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) result = await self.request(api.Methods.EDIT_MESSAGE_TEXT, payload) if isinstance(result, bool): return result return types.Message(**result) - async def edit_message_caption(self, chat_id: typing.Union[base.Integer, base.String, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + async def edit_message_caption( + self, + chat_id: typing.Union[base.Integer, base.String, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message or base.Boolean: """ Use this method to edit captions of messages sent by the bot or via the bot (for inline bots). @@ -1487,20 +1634,21 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): reply_markup = prepare_arg(reply_markup) payload = generate_payload(**locals()) if self.parse_mode: - payload.setdefault('parse_mode', self.parse_mode) + payload.setdefault("parse_mode", self.parse_mode) result = await self.request(api.Methods.EDIT_MESSAGE_CAPTION, payload) if isinstance(result, bool): return result return types.Message(**result) - async def edit_message_media(self, - media: types.InputMedia, - chat_id: typing.Union[typing.Union[base.Integer, base.String], None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, - ) -> typing.Union[types.Message, base.Boolean]: + async def edit_message_media( + self, + media: types.InputMedia, + chat_id: typing.Union[typing.Union[base.Integer, base.String], None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> typing.Union[types.Message, base.Boolean]: """ Use this method to edit audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -1540,12 +1688,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return result return types.Message(**result) - async def edit_message_reply_markup(self, - chat_id: typing.Union[base.Integer, base.String, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - None] = None) -> types.Message or base.Boolean: + async def edit_message_reply_markup( + self, + chat_id: typing.Union[base.Integer, base.String, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message or base.Boolean: """ Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots). @@ -1572,9 +1721,12 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return result return types.Message(**result) - async def stop_poll(self, chat_id: typing.Union[base.String, base.Integer], - message_id: base.Integer, - reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None) -> types.Poll: + async def stop_poll( + self, + chat_id: typing.Union[base.String, base.Integer], + message_id: base.Integer, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Poll: """ Use this method to stop a poll which was sent by the bot. On success, the stopped Poll with the final results is returned. @@ -1593,8 +1745,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.STOP_POLL, payload) return types.Poll(**result) - async def delete_message(self, chat_id: typing.Union[base.Integer, base.String], - message_id: base.Integer) -> base.Boolean: + async def delete_message( + self, chat_id: typing.Union[base.Integer, base.String], message_id: base.Integer + ) -> base.Boolean: """ Use this method to delete a message, including service messages, with the following limitations: - A message can only be deleted if it was sent less than 48 hours ago. @@ -1621,14 +1774,20 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): # === Stickers === # https://core.telegram.org/bots/api#stickers - async def send_sticker(self, chat_id: typing.Union[base.Integer, base.String], - sticker: typing.Union[base.InputFile, base.String], - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, - types.ReplyKeyboardRemove, - types.ForceReply, None] = None) -> types.Message: + async def send_sticker( + self, + chat_id: typing.Union[base.Integer, base.String], + sticker: typing.Union[base.InputFile, base.String], + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[ + types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, + types.ReplyKeyboardRemove, + types.ForceReply, + None, + ] = None, + ) -> types.Message: """ Use this method to send .webp stickers. @@ -1650,10 +1809,10 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ reply_markup = prepare_arg(reply_markup) - payload = generate_payload(**locals(), exclude=['sticker']) + payload = generate_payload(**locals(), exclude=["sticker"]) files = {} - prepare_file(payload, files, 'sticker', sticker) + prepare_file(payload, files, "sticker", sticker) result = await self.request(api.Methods.SEND_STICKER, payload, files) return types.Message(**result) @@ -1674,7 +1833,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.GET_STICKER_SET, payload) return types.StickerSet(**result) - async def upload_sticker_file(self, user_id: base.Integer, png_sticker: base.InputFile) -> types.File: + async def upload_sticker_file( + self, user_id: base.Integer, png_sticker: base.InputFile + ) -> types.File: """ Use this method to upload a .png file with a sticker for later use in createNewStickerSet and addStickerToSet methods (can be used multiple times). @@ -1689,18 +1850,24 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :return: Returns the uploaded File on success :rtype: :obj:`types.File` """ - payload = generate_payload(**locals(), exclude=['png_sticker']) + payload = generate_payload(**locals(), exclude=["png_sticker"]) files = {} - prepare_file(payload, files, 'png_sticker', png_sticker) + prepare_file(payload, files, "png_sticker", png_sticker) result = await self.request(api.Methods.UPLOAD_STICKER_FILE, payload, files) return types.File(**result) - async def create_new_sticker_set(self, user_id: base.Integer, name: base.String, title: base.String, - png_sticker: typing.Union[base.InputFile, base.String], emojis: base.String, - contains_masks: typing.Union[base.Boolean, None] = None, - mask_position: typing.Union[types.MaskPosition, None] = None) -> base.Boolean: + async def create_new_sticker_set( + self, + user_id: base.Integer, + name: base.String, + title: base.String, + png_sticker: typing.Union[base.InputFile, base.String], + emojis: base.String, + contains_masks: typing.Union[base.Boolean, None] = None, + mask_position: typing.Union[types.MaskPosition, None] = None, + ) -> base.Boolean: """ Use this method to create new sticker set owned by a user. The bot will be able to edit the created sticker set. @@ -1725,17 +1892,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`base.Boolean` """ mask_position = prepare_arg(mask_position) - payload = generate_payload(**locals(), exclude=['png_sticker']) + payload = generate_payload(**locals(), exclude=["png_sticker"]) files = {} - prepare_file(payload, files, 'png_sticker', png_sticker) + prepare_file(payload, files, "png_sticker", png_sticker) result = await self.request(api.Methods.CREATE_NEW_STICKER_SET, payload, files) return result - async def add_sticker_to_set(self, user_id: base.Integer, name: base.String, - png_sticker: typing.Union[base.InputFile, base.String], emojis: base.String, - mask_position: typing.Union[types.MaskPosition, None] = None) -> base.Boolean: + async def add_sticker_to_set( + self, + user_id: base.Integer, + name: base.String, + png_sticker: typing.Union[base.InputFile, base.String], + emojis: base.String, + mask_position: typing.Union[types.MaskPosition, None] = None, + ) -> base.Boolean: """ Use this method to add a new sticker to a set created by the bot. @@ -1756,15 +1928,17 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`base.Boolean` """ mask_position = prepare_arg(mask_position) - payload = generate_payload(**locals(), exclude=['png_sticker']) + payload = generate_payload(**locals(), exclude=["png_sticker"]) files = {} - prepare_file(payload, files, 'png_sticker', png_sticker) + prepare_file(payload, files, "png_sticker", png_sticker) result = await self.request(api.Methods.ADD_STICKER_TO_SET, payload, files) return result - async def set_sticker_position_in_set(self, sticker: base.String, position: base.Integer) -> base.Boolean: + async def set_sticker_position_in_set( + self, sticker: base.String, position: base.Integer + ) -> base.Boolean: """ Use this method to move a sticker in a set created by the bot to a specific position. @@ -1800,13 +1974,16 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.DELETE_STICKER_FROM_SET, payload) return result - async def answer_inline_query(self, inline_query_id: base.String, - results: typing.List[types.InlineQueryResult], - cache_time: typing.Union[base.Integer, None] = None, - is_personal: typing.Union[base.Boolean, None] = None, - next_offset: typing.Union[base.String, None] = None, - switch_pm_text: typing.Union[base.String, None] = None, - switch_pm_parameter: typing.Union[base.String, None] = None) -> base.Boolean: + async def answer_inline_query( + self, + inline_query_id: base.String, + results: typing.List[types.InlineQueryResult], + cache_time: typing.Union[base.Integer, None] = None, + is_personal: typing.Union[base.Boolean, None] = None, + next_offset: typing.Union[base.String, None] = None, + switch_pm_text: typing.Union[base.String, None] = None, + switch_pm_parameter: typing.Union[base.String, None] = None, + ) -> base.Boolean: """ Use this method to send answers to an inline query. No more than 50 results per query are allowed. @@ -1847,23 +2024,30 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): # === Payments === # https://core.telegram.org/bots/api#payments - async def send_invoice(self, chat_id: base.Integer, title: base.String, - description: base.String, payload: base.String, - provider_token: base.String, start_parameter: base.String, - currency: base.String, prices: typing.List[types.LabeledPrice], - provider_data: typing.Union[typing.Dict, None] = None, - photo_url: typing.Union[base.String, None] = None, - photo_size: typing.Union[base.Integer, None] = None, - photo_width: typing.Union[base.Integer, None] = None, - photo_height: typing.Union[base.Integer, None] = None, - need_name: typing.Union[base.Boolean, None] = None, - need_phone_number: typing.Union[base.Boolean, None] = None, - need_email: typing.Union[base.Boolean, None] = None, - need_shipping_address: typing.Union[base.Boolean, None] = None, - is_flexible: typing.Union[base.Boolean, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None) -> types.Message: + async def send_invoice( + self, + chat_id: base.Integer, + title: base.String, + description: base.String, + payload: base.String, + provider_token: base.String, + start_parameter: base.String, + currency: base.String, + prices: typing.List[types.LabeledPrice], + provider_data: typing.Union[typing.Dict, None] = None, + photo_url: typing.Union[base.String, None] = None, + photo_size: typing.Union[base.Integer, None] = None, + photo_width: typing.Union[base.Integer, None] = None, + photo_height: typing.Union[base.Integer, None] = None, + need_name: typing.Union[base.Boolean, None] = None, + need_phone_number: typing.Union[base.Boolean, None] = None, + need_email: typing.Union[base.Boolean, None] = None, + need_shipping_address: typing.Union[base.Boolean, None] = None, + is_flexible: typing.Union[base.Boolean, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message: """ Use this method to send invoices. @@ -1918,16 +2102,22 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - prices = prepare_arg([price.to_python() if hasattr(price, 'to_python') else price for price in prices]) + prices = prepare_arg( + [price.to_python() if hasattr(price, "to_python") else price for price in prices] + ) reply_markup = prepare_arg(reply_markup) payload_ = generate_payload(**locals()) result = await self.request(api.Methods.SEND_INVOICE, payload_) return types.Message(**result) - async def answer_shipping_query(self, shipping_query_id: base.String, ok: base.Boolean, - shipping_options: typing.Union[typing.List[types.ShippingOption], None] = None, - error_message: typing.Union[base.String, None] = None) -> base.Boolean: + async def answer_shipping_query( + self, + shipping_query_id: base.String, + ok: base.Boolean, + shipping_options: typing.Union[typing.List[types.ShippingOption], None] = None, + error_message: typing.Union[base.String, None] = None, + ) -> base.Boolean: """ If you sent an invoice requesting a shipping address and the parameter is_flexible was specified, the Bot API will send an Update with a shipping_query field to the bot. @@ -1950,17 +2140,25 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`base.Boolean` """ if shipping_options: - shipping_options = prepare_arg([shipping_option.to_python() - if hasattr(shipping_option, 'to_python') - else shipping_option - for shipping_option in shipping_options]) + shipping_options = prepare_arg( + [ + shipping_option.to_python() + if hasattr(shipping_option, "to_python") + else shipping_option + for shipping_option in shipping_options + ] + ) payload = generate_payload(**locals()) result = await self.request(api.Methods.ANSWER_SHIPPING_QUERY, payload) return result - async def answer_pre_checkout_query(self, pre_checkout_query_id: base.String, ok: base.Boolean, - error_message: typing.Union[base.String, None] = None) -> base.Boolean: + async def answer_pre_checkout_query( + self, + pre_checkout_query_id: base.String, + ok: base.Boolean, + error_message: typing.Union[base.String, None] = None, + ) -> base.Boolean: """ Once the user has confirmed their payment and shipping details, the Bot API sends the final confirmation in the form of an Update with the field pre_checkout_query. @@ -1990,9 +2188,9 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): # === Games === # https://core.telegram.org/bots/api#games - async def set_passport_data_errors(self, - user_id: base.Integer, - errors: typing.List[types.PassportElementError]) -> base.Boolean: + async def set_passport_data_errors( + self, user_id: base.Integer, errors: typing.List[types.PassportElementError] + ) -> base.Boolean: """ Informs a user that some of the Telegram Passport elements they provided contains errors. The user will not be able to re-submit their Passport to you until the errors are fixed @@ -2022,10 +2220,14 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): # === Games === # https://core.telegram.org/bots/api#games - async def send_game(self, chat_id: base.Integer, game_short_name: base.String, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_to_message_id: typing.Union[base.Integer, None] = None, - reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None) -> types.Message: + async def send_game( + self, + chat_id: base.Integer, + game_short_name: base.String, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_to_message_id: typing.Union[base.Integer, None] = None, + reply_markup: typing.Union[types.InlineKeyboardMarkup, None] = None, + ) -> types.Message: """ Use this method to send a game. @@ -2052,13 +2254,16 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): result = await self.request(api.Methods.SEND_GAME, payload) return types.Message(**result) - async def set_game_score(self, user_id: base.Integer, score: base.Integer, - force: typing.Union[base.Boolean, None] = None, - disable_edit_message: typing.Union[base.Boolean, None] = None, - chat_id: typing.Union[base.Integer, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, - None] = None) -> types.Message or base.Boolean: + async def set_game_score( + self, + user_id: base.Integer, + score: base.Integer, + force: typing.Union[base.Boolean, None] = None, + disable_edit_message: typing.Union[base.Boolean, None] = None, + chat_id: typing.Union[base.Integer, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + ) -> types.Message or base.Boolean: """ Use this method to set the score of the specified user in a game. @@ -2092,11 +2297,13 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): return result return types.Message(**result) - async def get_game_high_scores(self, user_id: base.Integer, - chat_id: typing.Union[base.Integer, None] = None, - message_id: typing.Union[base.Integer, None] = None, - inline_message_id: typing.Union[base.String, - None] = None) -> typing.List[types.GameHighScore]: + async def get_game_high_scores( + self, + user_id: base.Integer, + chat_id: typing.Union[base.Integer, None] = None, + message_id: typing.Union[base.Integer, None] = None, + inline_message_id: typing.Union[base.String, None] = None, + ) -> typing.List[types.GameHighScore]: """ Use this method to get data for high score tables. diff --git a/aiogram/contrib/fsm_storage/files.py b/aiogram/contrib/fsm_storage/files.py index f67a6f69..f8df078a 100644 --- a/aiogram/contrib/fsm_storage/files.py +++ b/aiogram/contrib/fsm_storage/files.py @@ -36,11 +36,11 @@ class JSONStorage(_FileStorage): """ def read(self, path: pathlib.Path): - with path.open('r') as f: + with path.open("r") as f: return json.load(f) def write(self, path: pathlib.Path): - with path.open('w') as f: + with path.open("w") as f: return json.dump(self.data, f, indent=4) @@ -50,9 +50,9 @@ class PickleStorage(_FileStorage): """ def read(self, path: pathlib.Path): - with path.open('rb') as f: + with path.open("rb") as f: return pickle.load(f) def write(self, path: pathlib.Path): - with path.open('wb') as f: + with path.open("wb") as f: return pickle.dump(self.data, f, protocol=pickle.HIGHEST_PROTOCOL) diff --git a/aiogram/contrib/fsm_storage/memory.py b/aiogram/contrib/fsm_storage/memory.py index 2940f3fa..98bad524 100644 --- a/aiogram/contrib/fsm_storage/memory.py +++ b/aiogram/contrib/fsm_storage/memory.py @@ -26,51 +26,70 @@ class MemoryStorage(BaseStorage): if chat_id not in self.data: self.data[chat_id] = {} if user_id not in self.data[chat_id]: - self.data[chat_id][user_id] = {'state': None, 'data': {}, 'bucket': {}} + self.data[chat_id][user_id] = {"state": None, "data": {}, "bucket": {}} return chat_id, user_id - async def get_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Optional[str]: + async def get_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Optional[str]: chat, user = self.resolve_address(chat=chat, user=user) - return self.data[chat][user]['state'] + return self.data[chat][user]["state"] - async def get_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Dict: + async def get_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Dict: chat, user = self.resolve_address(chat=chat, user=user) - return copy.deepcopy(self.data[chat][user]['data']) + return copy.deepcopy(self.data[chat][user]["data"]) - async def update_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - data: typing.Dict = None, **kwargs): + async def update_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + **kwargs, + ): if data is None: data = {} chat, user = self.resolve_address(chat=chat, user=user) - self.data[chat][user]['data'].update(data, **kwargs) + self.data[chat][user]["data"].update(data, **kwargs) - async def set_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - state: typing.AnyStr = None): + async def set_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state: typing.AnyStr = None, + ): chat, user = self.resolve_address(chat=chat, user=user) - self.data[chat][user]['state'] = state + self.data[chat][user]["state"] = state - async def set_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - data: typing.Dict = None): + async def set_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + ): chat, user = self.resolve_address(chat=chat, user=user) - self.data[chat][user]['data'] = copy.deepcopy(data) + self.data[chat][user]["data"] = copy.deepcopy(data) - async def reset_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - with_data: typing.Optional[bool] = True): + async def reset_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + with_data: typing.Optional[bool] = True, + ): await self.set_state(chat=chat, user=user, state=None) if with_data: await self.set_data(chat=chat, user=user, data={}) @@ -78,25 +97,35 @@ class MemoryStorage(BaseStorage): def has_bucket(self): return True - async def get_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[dict] = None) -> typing.Dict: + async def get_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[dict] = None, + ) -> typing.Dict: chat, user = self.resolve_address(chat=chat, user=user) - return copy.deepcopy(self.data[chat][user]['bucket']) + return copy.deepcopy(self.data[chat][user]["bucket"]) - async def set_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None): + async def set_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + ): chat, user = self.resolve_address(chat=chat, user=user) - self.data[chat][user]['bucket'] = copy.deepcopy(bucket) + self.data[chat][user]["bucket"] = copy.deepcopy(bucket) - async def update_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None, **kwargs): + async def update_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + **kwargs, + ): if bucket is None: bucket = {} chat, user = self.resolve_address(chat=chat, user=user) - self.data[chat][user]['bucket'].update(bucket, **kwargs) + self.data[chat][user]["bucket"].update(bucket, **kwargs) diff --git a/aiogram/contrib/fsm_storage/redis.py b/aiogram/contrib/fsm_storage/redis.py index c3a91f00..ef0897b8 100644 --- a/aiogram/contrib/fsm_storage/redis.py +++ b/aiogram/contrib/fsm_storage/redis.py @@ -11,9 +11,9 @@ import aioredis from ...dispatcher.storage import BaseStorage from ...utils import json -STATE_KEY = 'state' -STATE_DATA_KEY = 'data' -STATE_BUCKET_KEY = 'bucket' +STATE_KEY = "state" +STATE_DATA_KEY = "data" +STATE_BUCKET_KEY = "bucket" class RedisStorage(BaseStorage): @@ -36,7 +36,9 @@ class RedisStorage(BaseStorage): """ - def __init__(self, host='localhost', port=6379, db=None, password=None, ssl=None, loop=None, **kwargs): + def __init__( + self, host="localhost", port=6379, db=None, password=None, ssl=None, loop=None, **kwargs + ): self._host = host self._port = port self._db = db @@ -68,15 +70,22 @@ class RedisStorage(BaseStorage): # Use thread-safe asyncio Lock because this method without that is not safe async with self._connection_lock: if self._redis is None: - self._redis = await aioredis.create_connection((self._host, self._port), - db=self._db, password=self._password, ssl=self._ssl, - loop=self._loop, - **self._kwargs) + self._redis = await aioredis.create_connection( + (self._host, self._port), + db=self._db, + password=self._password, + ssl=self._ssl, + loop=self._loop, + **self._kwargs, + ) return self._redis - async def get_record(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None) -> typing.Dict: + async def get_record( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + ) -> typing.Dict: """ Get record from storage @@ -88,13 +97,20 @@ class RedisStorage(BaseStorage): addr = f"fsm:{chat}:{user}" conn = await self.redis() - data = await conn.execute('GET', addr) + data = await conn.execute("GET", addr) if data is None: - return {'state': None, 'data': {}} + return {"state": None, "data": {}} return json.loads(data) - async def set_record(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - state=None, data=None, bucket=None): + async def set_record( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state=None, + data=None, + bucket=None, + ): """ Write record to storage @@ -113,39 +129,65 @@ class RedisStorage(BaseStorage): chat, user = self.check_address(chat=chat, user=user) addr = f"fsm:{chat}:{user}" - record = {'state': state, 'data': data, 'bucket': bucket} + record = {"state": state, "data": data, "bucket": bucket} conn = await self.redis() - await conn.execute('SET', addr, json.dumps(record)) + await conn.execute("SET", addr, json.dumps(record)) - async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Optional[str]: + async def get_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Optional[str]: record = await self.get_record(chat=chat, user=user) - return record['state'] + return record["state"] - async def get_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Dict: + async def get_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Dict: record = await self.get_record(chat=chat, user=user) - return record['data'] + return record["data"] - async def set_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - state: typing.Optional[typing.AnyStr] = None): + async def set_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state: typing.Optional[typing.AnyStr] = None, + ): record = await self.get_record(chat=chat, user=user) - await self.set_record(chat=chat, user=user, state=state, data=record['data']) + await self.set_record(chat=chat, user=user, state=state, data=record["data"]) - async def set_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - data: typing.Dict = None): + async def set_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + ): record = await self.get_record(chat=chat, user=user) - await self.set_record(chat=chat, user=user, state=record['state'], data=data) + await self.set_record(chat=chat, user=user, state=record["state"], data=data) - async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - data: typing.Dict = None, **kwargs): + async def update_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + **kwargs, + ): if data is None: data = {} record = await self.get_record(chat=chat, user=user) - record_data = record.get('data', {}) + record_data = record.get("data", {}) record_data.update(data, **kwargs) - await self.set_record(chat=chat, user=user, state=record['state'], data=record_data) + await self.set_record(chat=chat, user=user, state=record["state"], data=record_data) async def get_states_list(self) -> typing.List[typing.Tuple[int]]: """ @@ -156,9 +198,9 @@ class RedisStorage(BaseStorage): conn = await self.redis() result = [] - keys = await conn.execute('KEYS', 'fsm:*') + keys = await conn.execute("KEYS", "fsm:*") for item in keys: - *_, chat, user = item.decode('utf-8').split(':') + *_, chat, user = item.decode("utf-8").split(":") result.append((chat, user)) return result @@ -173,33 +215,52 @@ class RedisStorage(BaseStorage): conn = await self.redis() if full: - await conn.execute('FLUSHDB') + await conn.execute("FLUSHDB") else: - keys = await conn.execute('KEYS', 'fsm:*') - await conn.execute('DEL', *keys) + keys = await conn.execute("KEYS", "fsm:*") + await conn.execute("DEL", *keys) def has_bucket(self): return True - async def get_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Dict: + async def get_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Dict: record = await self.get_record(chat=chat, user=user) - return record.get('bucket', {}) + return record.get("bucket", {}) - async def set_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None): + async def set_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + ): record = await self.get_record(chat=chat, user=user) - await self.set_record(chat=chat, user=user, state=record['state'], data=record['data'], bucket=bucket) + await self.set_record( + chat=chat, user=user, state=record["state"], data=record["data"], bucket=bucket + ) - async def update_bucket(self, *, chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None, **kwargs): + async def update_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + **kwargs, + ): record = await self.get_record(chat=chat, user=user) - record_bucket = record.get('bucket', {}) + record_bucket = record.get("bucket", {}) if bucket is None: bucket = {} record_bucket.update(bucket, **kwargs) - await self.set_record(chat=chat, user=user, state=record['state'], data=record_bucket, bucket=bucket) + await self.set_record( + chat=chat, user=user, state=record["state"], data=record_bucket, bucket=bucket + ) class RedisStorage2(BaseStorage): @@ -223,8 +284,18 @@ class RedisStorage2(BaseStorage): """ - def __init__(self, host='localhost', port=6379, db=None, password=None, ssl=None, - pool_size=10, loop=None, prefix='fsm', **kwargs): + def __init__( + self, + host="localhost", + port=6379, + db=None, + password=None, + ssl=None, + pool_size=10, + loop=None, + prefix="fsm", + **kwargs, + ): self._host = host self._port = port self._db = db @@ -247,14 +318,20 @@ class RedisStorage2(BaseStorage): # Use thread-safe asyncio Lock because this method without that is not safe async with self._connection_lock: if self._redis is None: - self._redis = await aioredis.create_redis_pool((self._host, self._port), - db=self._db, password=self._password, ssl=self._ssl, - minsize=1, maxsize=self._pool_size, - loop=self._loop, **self._kwargs) + self._redis = await aioredis.create_redis_pool( + (self._host, self._port), + db=self._db, + password=self._password, + ssl=self._ssl, + minsize=1, + maxsize=self._pool_size, + loop=self._loop, + **self._kwargs, + ) return self._redis def generate_key(self, *parts): - return ':'.join(self._prefix + tuple(map(str, parts))) + return ":".join(self._prefix + tuple(map(str, parts))) async def close(self): async with self._connection_lock: @@ -269,25 +346,40 @@ class RedisStorage2(BaseStorage): return await self._redis.wait_closed() return True - async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Optional[str]: + async def get_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Optional[str]: chat, user = self.check_address(chat=chat, user=user) key = self.generate_key(chat, user, STATE_KEY) redis = await self.redis() - return await redis.get(key, encoding='utf8') or None + return await redis.get(key, encoding="utf8") or None - async def get_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[dict] = None) -> typing.Dict: + async def get_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[dict] = None, + ) -> typing.Dict: chat, user = self.check_address(chat=chat, user=user) key = self.generate_key(chat, user, STATE_DATA_KEY) redis = await self.redis() - raw_result = await redis.get(key, encoding='utf8') + raw_result = await redis.get(key, encoding="utf8") if raw_result: return json.loads(raw_result) return default or {} - async def set_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - state: typing.Optional[typing.AnyStr] = None): + async def set_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state: typing.Optional[typing.AnyStr] = None, + ): chat, user = self.check_address(chat=chat, user=user) key = self.generate_key(chat, user, STATE_KEY) redis = await self.redis() @@ -296,15 +388,26 @@ class RedisStorage2(BaseStorage): else: await redis.set(key, state) - async def set_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - data: typing.Dict = None): + async def set_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + ): chat, user = self.check_address(chat=chat, user=user) key = self.generate_key(chat, user, STATE_DATA_KEY) redis = await self.redis() await redis.set(key, json.dumps(data)) - async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - data: typing.Dict = None, **kwargs): + async def update_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + **kwargs, + ): if data is None: data = {} temp_data = await self.get_data(chat=chat, user=user, default={}) @@ -314,26 +417,41 @@ class RedisStorage2(BaseStorage): def has_bucket(self): return True - async def get_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[dict] = None) -> typing.Dict: + async def get_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[dict] = None, + ) -> typing.Dict: chat, user = self.check_address(chat=chat, user=user) key = self.generate_key(chat, user, STATE_BUCKET_KEY) redis = await self.redis() - raw_result = await redis.get(key, encoding='utf8') + raw_result = await redis.get(key, encoding="utf8") if raw_result: return json.loads(raw_result) return default or {} - async def set_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None): + async def set_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + ): chat, user = self.check_address(chat=chat, user=user) key = self.generate_key(chat, user, STATE_BUCKET_KEY) redis = await self.redis() await redis.set(key, json.dumps(bucket)) - async def update_bucket(self, *, chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None, **kwargs): + async def update_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + **kwargs, + ): if bucket is None: bucket = {} temp_bucket = await self.get_bucket(chat=chat, user=user) @@ -352,7 +470,7 @@ class RedisStorage2(BaseStorage): if full: await conn.flushdb() else: - keys = await conn.keys(self.generate_key('*')) + keys = await conn.keys(self.generate_key("*")) await conn.delete(*keys) async def get_states_list(self) -> typing.List[typing.Tuple[int]]: @@ -364,9 +482,9 @@ class RedisStorage2(BaseStorage): conn = await self.redis() result = [] - keys = await conn.keys(self.generate_key('*', '*', STATE_KEY), encoding='utf8') + keys = await conn.keys(self.generate_key("*", "*", STATE_KEY), encoding="utf8") for item in keys: - *_, chat, user, _ = item.split(':') + *_, chat, user, _ = item.split(":") result.append((chat, user)) return result @@ -388,7 +506,7 @@ async def migrate_redis1_to_redis2(storage1: RedisStorage, storage2: RedisStorag if not isinstance(storage2, RedisStorage): raise TypeError(f"{type(storage2)} is not RedisStorage instance.") - log = logging.getLogger('aiogram.RedisStorage') + log = logging.getLogger("aiogram.RedisStorage") for chat, user in await storage1.get_states_list(): state = await storage1.get_state(chat=chat, user=user) diff --git a/aiogram/contrib/fsm_storage/rethinkdb.py b/aiogram/contrib/fsm_storage/rethinkdb.py index 38d24efa..d669cfff 100644 --- a/aiogram/contrib/fsm_storage/rethinkdb.py +++ b/aiogram/contrib/fsm_storage/rethinkdb.py @@ -7,10 +7,10 @@ from rethinkdb.asyncio_net.net_asyncio import Connection from ...dispatcher.storage import BaseStorage -__all__ = ['RethinkDBStorage'] +__all__ = ["RethinkDBStorage"] r = rethinkdb.RethinkDB() -r.set_loop_type('asyncio') +r.set_loop_type("asyncio") class RethinkDBStorage(BaseStorage): @@ -32,17 +32,19 @@ class RethinkDBStorage(BaseStorage): """ - def __init__(self, - host: str = 'localhost', - port: int = 28015, - db: str = 'aiogram', - table: str = 'aiogram', - auth_key: typing.Optional[str] = None, - user: typing.Optional[str] = None, - password: typing.Optional[str] = None, - timeout: int = 20, - ssl: typing.Optional[dict] = None, - loop: typing.Optional[asyncio.AbstractEventLoop] = None): + def __init__( + self, + host: str = "localhost", + port: int = 28015, + db: str = "aiogram", + table: str = "aiogram", + auth_key: typing.Optional[str] = None, + user: typing.Optional[str] = None, + password: typing.Optional[str] = None, + timeout: int = 20, + ssl: typing.Optional[dict] = None, + loop: typing.Optional[asyncio.AbstractEventLoop] = None, + ): self._host = host self._port = port self._db = db @@ -61,15 +63,17 @@ class RethinkDBStorage(BaseStorage): Get or create a connection. """ if self._conn is None: - self._conn = await r.connect(host=self._host, - port=self._port, - db=self._db, - auth_key=self._auth_key, - user=self._user, - password=self._password, - timeout=self._timeout, - ssl=self._ssl, - io_loop=self._loop) + self._conn = await r.connect( + host=self._host, + port=self._port, + db=self._db, + auth_key=self._auth_key, + user=self._user, + password=self._password, + timeout=self._timeout, + ssl=self._ssl, + io_loop=self._loop, + ) return self._conn @contextlib.asynccontextmanager @@ -90,64 +94,126 @@ class RethinkDBStorage(BaseStorage): """ pass - async def get_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Optional[str]: + async def get_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Optional[str]: chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: - return await r.table(self._table).get(chat)[user]['state'].default(default or None).run(conn) + return ( + await r.table(self._table) + .get(chat)[user]["state"] + .default(default or None) + .run(conn) + ) - async def get_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Dict: + async def get_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Dict: chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: - return await r.table(self._table).get(chat)[user]['data'].default(default or {}).run(conn) + return ( + await r.table(self._table).get(chat)[user]["data"].default(default or {}).run(conn) + ) - async def set_state(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - state: typing.Optional[typing.AnyStr] = None): + async def set_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state: typing.Optional[typing.AnyStr] = None, + ): chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: - await r.table(self._table).insert({'id': chat, user: {'state': state}}, conflict="update").run(conn) + await r.table(self._table).insert( + {"id": chat, user: {"state": state}}, conflict="update" + ).run(conn) - async def set_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - data: typing.Dict = None): + async def set_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + ): chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: if await r.table(self._table).get(chat).run(conn): - await r.table(self._table).get(chat).update({user: {'data': r.literal(data)}}).run(conn) + await r.table(self._table).get(chat).update({user: {"data": r.literal(data)}}).run( + conn + ) else: - await r.table(self._table).insert({'id': chat, user: {'data': data}}).run(conn) + await r.table(self._table).insert({"id": chat, user: {"data": data}}).run(conn) - async def update_data(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - data: typing.Dict = None, - **kwargs): + async def update_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + **kwargs, + ): chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: - await r.table(self._table).insert({'id': chat, user: {'data': data}}, conflict="update").run(conn) + await r.table(self._table).insert( + {"id": chat, user: {"data": data}}, conflict="update" + ).run(conn) def has_bucket(self): return True - async def get_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - default: typing.Optional[dict] = None) -> typing.Dict: + async def get_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[dict] = None, + ) -> typing.Dict: chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: - return await r.table(self._table).get(chat)[user]['bucket'].default(default or {}).run(conn) + return ( + await r.table(self._table) + .get(chat)[user]["bucket"] + .default(default or {}) + .run(conn) + ) - async def set_bucket(self, *, chat: typing.Union[str, int, None] = None, user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None): + async def set_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + ): chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: if await r.table(self._table).get(chat).run(conn): - await r.table(self._table).get(chat).update({user: {'bucket': r.literal(bucket)}}).run(conn) + await r.table(self._table).get(chat).update( + {user: {"bucket": r.literal(bucket)}} + ).run(conn) else: - await r.table(self._table).insert({'id': chat, user: {'bucket': bucket}}).run(conn) + await r.table(self._table).insert({"id": chat, user: {"bucket": bucket}}).run(conn) - async def update_bucket(self, *, chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, bucket: typing.Dict = None, - **kwargs): + async def update_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + **kwargs, + ): chat, user = map(str, self.check_address(chat=chat, user=user)) async with self.connection() as conn: - await r.table(self._table).insert({'id': chat, user: {'bucket': bucket}}, conflict="update").run(conn) + await r.table(self._table).insert( + {"id": chat, user: {"bucket": bucket}}, conflict="update" + ).run(conn) async def get_states_list(self) -> typing.List[typing.Tuple[int, int]]: """ @@ -161,7 +227,7 @@ class RethinkDBStorage(BaseStorage): items = (await r.table(self._table).run(conn)).items for item in items: - chat = int(item.pop('id')) + chat = int(item.pop("id")) for key in item.keys(): user = int(key) result.append((chat, user)) diff --git a/aiogram/contrib/middlewares/environment.py b/aiogram/contrib/middlewares/environment.py index 0427a739..3b08c507 100644 --- a/aiogram/contrib/middlewares/environment.py +++ b/aiogram/contrib/middlewares/environment.py @@ -11,15 +11,11 @@ class EnvironmentMiddleware(BaseMiddleware): def update_data(self, data): dp = self.manager.dispatcher - data.update( - bot=dp.bot, - dispatcher=dp, - loop=dp.loop - ) + data.update(bot=dp.bot, dispatcher=dp, loop=dp.loop) if self.context: data.update(self.context) async def trigger(self, action, args): - if 'error' not in action and action.startswith('pre_process_'): + if "error" not in action and action.startswith("pre_process_"): self.update_data(args[-1]) return True diff --git a/aiogram/contrib/middlewares/fsm.py b/aiogram/contrib/middlewares/fsm.py index e3550a34..0823bfbd 100644 --- a/aiogram/contrib/middlewares/fsm.py +++ b/aiogram/contrib/middlewares/fsm.py @@ -6,7 +6,7 @@ from aiogram.dispatcher.storage import FSMContext class FSMMiddleware(LifetimeControllerMiddleware): - skip_patterns = ['error', 'update'] + skip_patterns = ["error", "update"] def __init__(self): super(FSMMiddleware, self).__init__() @@ -14,10 +14,10 @@ class FSMMiddleware(LifetimeControllerMiddleware): async def pre_process(self, obj, data, *args): proxy = await FSMSStorageProxy.create(self.manager.dispatcher.current_state()) - data['state_data'] = proxy + data["state_data"] = proxy async def post_process(self, obj, data, *args): - proxy = data.get('state_data', None) + proxy = data.get("state_data", None) if isinstance(proxy, FSMSStorageProxy): await proxy.save() diff --git a/aiogram/contrib/middlewares/i18n.py b/aiogram/contrib/middlewares/i18n.py index 264bc653..8c9ddb3f 100644 --- a/aiogram/contrib/middlewares/i18n.py +++ b/aiogram/contrib/middlewares/i18n.py @@ -23,9 +23,9 @@ class I18nMiddleware(BaseMiddleware): >>> _ = i18n = I18nMiddleware(DOMAIN_NAME, LOCALES_DIR) """ - ctx_locale = ContextVar('ctx_user_locale', default=None) + ctx_locale = ContextVar("ctx_user_locale", default=None) - def __init__(self, domain, path=None, default='en'): + def __init__(self, domain, path=None, default="en"): """ :param domain: domain :param path: path where located all *.mo files @@ -34,7 +34,7 @@ class I18nMiddleware(BaseMiddleware): super(I18nMiddleware, self).__init__() if path is None: - path = os.path.join(os.getcwd(), 'locales') + path = os.path.join(os.getcwd(), "locales") self.domain = domain self.path = path @@ -53,12 +53,12 @@ class I18nMiddleware(BaseMiddleware): for name in os.listdir(self.path): if not os.path.isdir(os.path.join(self.path, name)): continue - mo_path = os.path.join(self.path, name, 'LC_MESSAGES', self.domain + '.mo') + mo_path = os.path.join(self.path, name, "LC_MESSAGES", self.domain + ".mo") if os.path.exists(mo_path): - with open(mo_path, 'rb') as fp: + with open(mo_path, "rb") as fp: translations[name] = gettext.GNUTranslations(fp) - elif os.path.exists(mo_path[:-2] + 'po'): + elif os.path.exists(mo_path[:-2] + "po"): raise RuntimeError(f"Found locale '{name} but this language is not compiled!") return translations @@ -134,7 +134,7 @@ class I18nMiddleware(BaseMiddleware): if locale: *_, data = args - language = data['locale'] = locale.language + language = data["locale"] = locale.language return language async def trigger(self, action, args): @@ -145,9 +145,7 @@ class I18nMiddleware(BaseMiddleware): :param args: event arguments :return: """ - if 'update' not in action \ - and 'error' not in action \ - and action.startswith('pre_process'): + if "update" not in action and "error" not in action and action.startswith("pre_process"): locale = await self.get_user_locale(action, args) self.ctx_locale.set(locale) return True diff --git a/aiogram/contrib/middlewares/logging.py b/aiogram/contrib/middlewares/logging.py index 1a3566c6..2fa8d58b 100644 --- a/aiogram/contrib/middlewares/logging.py +++ b/aiogram/contrib/middlewares/logging.py @@ -5,7 +5,7 @@ import logging from aiogram import types from aiogram.dispatcher.middlewares import BaseMiddleware -HANDLED_STR = ['Unhandled', 'Handled'] +HANDLED_STR = ["Unhandled", "Handled"] class LoggingMiddleware(BaseMiddleware): @@ -18,123 +18,181 @@ class LoggingMiddleware(BaseMiddleware): super(LoggingMiddleware, self).__init__() def check_timeout(self, obj): - start = obj.conf.get('_start', None) + start = obj.conf.get("_start", None) if start: - del obj.conf['_start'] + del obj.conf["_start"] return round((time.time() - start) * 1000) return -1 async def on_pre_process_update(self, update: types.Update, data: dict): - update.conf['_start'] = time.time() + update.conf["_start"] = time.time() self.logger.debug(f"Received update [ID:{update.update_id}]") async def on_post_process_update(self, update: types.Update, result, data: dict): timeout = self.check_timeout(update) if timeout > 0: - self.logger.info(f"Process update [ID:{update.update_id}]: [success] (in {timeout} ms)") + self.logger.info( + f"Process update [ID:{update.update_id}]: [success] (in {timeout} ms)" + ) async def on_pre_process_message(self, message: types.Message, data: dict): - self.logger.info(f"Received message [ID:{message.message_id}] in chat [{message.chat.type}:{message.chat.id}]") + self.logger.info( + f"Received message [ID:{message.message_id}] in chat [{message.chat.type}:{message.chat.id}]" + ) async def on_post_process_message(self, message: types.Message, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"message [ID:{message.message_id}] in chat [{message.chat.type}:{message.chat.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"message [ID:{message.message_id}] in chat [{message.chat.type}:{message.chat.id}]" + ) async def on_pre_process_edited_message(self, edited_message, data: dict): - self.logger.info(f"Received edited message [ID:{edited_message.message_id}] " - f"in chat [{edited_message.chat.type}:{edited_message.chat.id}]") + self.logger.info( + f"Received edited message [ID:{edited_message.message_id}] " + f"in chat [{edited_message.chat.type}:{edited_message.chat.id}]" + ) async def on_post_process_edited_message(self, edited_message, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"edited message [ID:{edited_message.message_id}] " - f"in chat [{edited_message.chat.type}:{edited_message.chat.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"edited message [ID:{edited_message.message_id}] " + f"in chat [{edited_message.chat.type}:{edited_message.chat.id}]" + ) async def on_pre_process_channel_post(self, channel_post: types.Message, data: dict): - self.logger.info(f"Received channel post [ID:{channel_post.message_id}] " - f"in channel [ID:{channel_post.chat.id}]") + self.logger.info( + f"Received channel post [ID:{channel_post.message_id}] " + f"in channel [ID:{channel_post.chat.id}]" + ) async def on_post_process_channel_post(self, channel_post: types.Message, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"channel post [ID:{channel_post.message_id}] " - f"in chat [{channel_post.chat.type}:{channel_post.chat.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"channel post [ID:{channel_post.message_id}] " + f"in chat [{channel_post.chat.type}:{channel_post.chat.id}]" + ) - async def on_pre_process_edited_channel_post(self, edited_channel_post: types.Message, data: dict): - self.logger.info(f"Received edited channel post [ID:{edited_channel_post.message_id}] " - f"in channel [ID:{edited_channel_post.chat.id}]") + async def on_pre_process_edited_channel_post( + self, edited_channel_post: types.Message, data: dict + ): + self.logger.info( + f"Received edited channel post [ID:{edited_channel_post.message_id}] " + f"in channel [ID:{edited_channel_post.chat.id}]" + ) - async def on_post_process_edited_channel_post(self, edited_channel_post: types.Message, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"edited channel post [ID:{edited_channel_post.message_id}] " - f"in channel [ID:{edited_channel_post.chat.id}]") + async def on_post_process_edited_channel_post( + self, edited_channel_post: types.Message, results, data: dict + ): + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"edited channel post [ID:{edited_channel_post.message_id}] " + f"in channel [ID:{edited_channel_post.chat.id}]" + ) async def on_pre_process_inline_query(self, inline_query: types.InlineQuery, data: dict): - self.logger.info(f"Received inline query [ID:{inline_query.id}] " - f"from user [ID:{inline_query.from_user.id}]") + self.logger.info( + f"Received inline query [ID:{inline_query.id}] " + f"from user [ID:{inline_query.from_user.id}]" + ) - async def on_post_process_inline_query(self, inline_query: types.InlineQuery, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"inline query [ID:{inline_query.id}] " - f"from user [ID:{inline_query.from_user.id}]") + async def on_post_process_inline_query( + self, inline_query: types.InlineQuery, results, data: dict + ): + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"inline query [ID:{inline_query.id}] " + f"from user [ID:{inline_query.from_user.id}]" + ) - async def on_pre_process_chosen_inline_result(self, chosen_inline_result: types.ChosenInlineResult, data: dict): - self.logger.info(f"Received chosen inline result [Inline msg ID:{chosen_inline_result.inline_message_id}] " - f"from user [ID:{chosen_inline_result.from_user.id}] " - f"result [ID:{chosen_inline_result.result_id}]") + async def on_pre_process_chosen_inline_result( + self, chosen_inline_result: types.ChosenInlineResult, data: dict + ): + self.logger.info( + f"Received chosen inline result [Inline msg ID:{chosen_inline_result.inline_message_id}] " + f"from user [ID:{chosen_inline_result.from_user.id}] " + f"result [ID:{chosen_inline_result.result_id}]" + ) - async def on_post_process_chosen_inline_result(self, chosen_inline_result, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"chosen inline result [Inline msg ID:{chosen_inline_result.inline_message_id}] " - f"from user [ID:{chosen_inline_result.from_user.id}] " - f"result [ID:{chosen_inline_result.result_id}]") + async def on_post_process_chosen_inline_result( + self, chosen_inline_result, results, data: dict + ): + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"chosen inline result [Inline msg ID:{chosen_inline_result.inline_message_id}] " + f"from user [ID:{chosen_inline_result.from_user.id}] " + f"result [ID:{chosen_inline_result.result_id}]" + ) async def on_pre_process_callback_query(self, callback_query: types.CallbackQuery, data: dict): if callback_query.message: if callback_query.message.from_user: - self.logger.info(f"Received callback query [ID:{callback_query.id}] " - f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] " - f"from user [ID:{callback_query.message.from_user.id}]") + self.logger.info( + f"Received callback query [ID:{callback_query.id}] " + f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] " + f"from user [ID:{callback_query.message.from_user.id}]" + ) else: - self.logger.info(f"Received callback query [ID:{callback_query.id}] " - f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]") + self.logger.info( + f"Received callback query [ID:{callback_query.id}] " + f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]" + ) else: - self.logger.info(f"Received callback query [ID:{callback_query.id}] " - f"from inline message [ID:{callback_query.inline_message_id}] " - f"from user [ID:{callback_query.from_user.id}]") + self.logger.info( + f"Received callback query [ID:{callback_query.id}] " + f"from inline message [ID:{callback_query.inline_message_id}] " + f"from user [ID:{callback_query.from_user.id}]" + ) async def on_post_process_callback_query(self, callback_query, results, data: dict): if callback_query.message: if callback_query.message.from_user: - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"callback query [ID:{callback_query.id}] " - f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] " - f"from user [ID:{callback_query.message.from_user.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"callback query [ID:{callback_query.id}] " + f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}] " + f"from user [ID:{callback_query.message.from_user.id}]" + ) else: - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"callback query [ID:{callback_query.id}] " - f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"callback query [ID:{callback_query.id}] " + f"in chat [{callback_query.message.chat.type}:{callback_query.message.chat.id}]" + ) else: - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"callback query [ID:{callback_query.id}] " - f"from inline message [ID:{callback_query.inline_message_id}] " - f"from user [ID:{callback_query.from_user.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"callback query [ID:{callback_query.id}] " + f"from inline message [ID:{callback_query.inline_message_id}] " + f"from user [ID:{callback_query.from_user.id}]" + ) async def on_pre_process_shipping_query(self, shipping_query: types.ShippingQuery, data: dict): - self.logger.info(f"Received shipping query [ID:{shipping_query.id}] " - f"from user [ID:{shipping_query.from_user.id}]") + self.logger.info( + f"Received shipping query [ID:{shipping_query.id}] " + f"from user [ID:{shipping_query.from_user.id}]" + ) async def on_post_process_shipping_query(self, shipping_query, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"shipping query [ID:{shipping_query.id}] " - f"from user [ID:{shipping_query.from_user.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"shipping query [ID:{shipping_query.id}] " + f"from user [ID:{shipping_query.from_user.id}]" + ) - async def on_pre_process_pre_checkout_query(self, pre_checkout_query: types.PreCheckoutQuery, data: dict): - self.logger.info(f"Received pre-checkout query [ID:{pre_checkout_query.id}] " - f"from user [ID:{pre_checkout_query.from_user.id}]") + async def on_pre_process_pre_checkout_query( + self, pre_checkout_query: types.PreCheckoutQuery, data: dict + ): + self.logger.info( + f"Received pre-checkout query [ID:{pre_checkout_query.id}] " + f"from user [ID:{pre_checkout_query.from_user.id}]" + ) async def on_post_process_pre_checkout_query(self, pre_checkout_query, results, data: dict): - self.logger.debug(f"{HANDLED_STR[bool(len(results))]} " - f"pre-checkout query [ID:{pre_checkout_query.id}] " - f"from user [ID:{pre_checkout_query.from_user.id}]") + self.logger.debug( + f"{HANDLED_STR[bool(len(results))]} " + f"pre-checkout query [ID:{pre_checkout_query.id}] " + f"from user [ID:{pre_checkout_query.from_user.id}]" + ) async def on_pre_process_error(self, update, error, data: dict): timeout = self.check_timeout(update) @@ -168,7 +226,7 @@ class LoggingFilter(logging.Filter): """ - def __init__(self, name='', prefix='tg', include_content=False): + def __init__(self, name="", prefix="tg", include_content=False): """ :param name: :param prefix: prefix for all records @@ -200,34 +258,34 @@ class LoggingFilter(logging.Filter): :param update: :return: """ - yield 'update_id', update.update_id + yield "update_id", update.update_id if update.message: - yield 'update_type', 'message' + yield "update_type", "message" yield from self.process_message(update.message) if update.edited_message: - yield 'update_type', 'edited_message' + yield "update_type", "edited_message" yield from self.process_message(update.edited_message) if update.channel_post: - yield 'update_type', 'channel_post' + yield "update_type", "channel_post" yield from self.process_message(update.channel_post) if update.edited_channel_post: - yield 'update_type', 'edited_channel_post' + yield "update_type", "edited_channel_post" yield from self.process_message(update.edited_channel_post) if update.inline_query: - yield 'update_type', 'inline_query' + yield "update_type", "inline_query" yield from self.process_inline_query(update.inline_query) if update.chosen_inline_result: - yield 'update_type', 'chosen_inline_result' + yield "update_type", "chosen_inline_result" yield from self.process_chosen_inline_result(update.chosen_inline_result) if update.callback_query: - yield 'update_type', 'callback_query' + yield "update_type", "callback_query" yield from self.process_callback_query(update.callback_query) if update.shipping_query: - yield 'update_type', 'shipping_query' + yield "update_type", "shipping_query" yield from self.process_shipping_query(update.shipping_query) if update.pre_checkout_query: - yield 'update_type', 'pre_checkout_query' + yield "update_type", "pre_checkout_query" yield from self.process_pre_checkout_query(update.pre_checkout_query) def make_prefix(self, prefix, iterable): @@ -254,11 +312,11 @@ class LoggingFilter(logging.Filter): if not user: return - yield 'user_id', user.id + yield "user_id", user.id if self.include_content: - yield 'user_full_name', user.full_name + yield "user_full_name", user.full_name if user.username: - yield 'user_name', f"@{user.username}" + yield "user_name", f"@{user.username}" def process_chat(self, chat: types.Chat): """ @@ -270,15 +328,15 @@ class LoggingFilter(logging.Filter): if not chat: return - yield 'chat_id', chat.id - yield 'chat_type', chat.type + yield "chat_id", chat.id + yield "chat_type", chat.type if self.include_content: - yield 'chat_title', chat.full_name + yield "chat_title", chat.full_name if chat.username: - yield 'chat_name', f"@{chat.username}" + yield "chat_name", f"@{chat.username}" def process_message(self, message: types.Message): - yield 'message_content_type', message.content_type + yield "message_content_type", message.content_type yield from self.process_user(message.from_user) yield from self.process_chat(message.chat) @@ -286,82 +344,84 @@ class LoggingFilter(logging.Filter): return if message.reply_to_message: - yield from self.make_prefix('reply_to', self.process_message(message.reply_to_message)) + yield from self.make_prefix("reply_to", self.process_message(message.reply_to_message)) if message.forward_from: - yield from self.make_prefix('forward_from', self.process_user(message.forward_from)) + yield from self.make_prefix("forward_from", self.process_user(message.forward_from)) if message.forward_from_chat: - yield from self.make_prefix('forward_from_chat', self.process_chat(message.forward_from_chat)) + yield from self.make_prefix( + "forward_from_chat", self.process_chat(message.forward_from_chat) + ) if message.forward_from_message_id: - yield 'message_forward_from_message_id', message.forward_from_message_id + yield "message_forward_from_message_id", message.forward_from_message_id if message.forward_date: - yield 'message_forward_date', message.forward_date + yield "message_forward_date", message.forward_date if message.edit_date: - yield 'message_edit_date', message.edit_date + yield "message_edit_date", message.edit_date if message.media_group_id: - yield 'message_media_group_id', message.media_group_id + yield "message_media_group_id", message.media_group_id if message.author_signature: - yield 'message_author_signature', message.author_signature + yield "message_author_signature", message.author_signature if message.text: - yield 'text', message.text or message.caption - yield 'html_text', message.html_text + yield "text", message.text or message.caption + yield "html_text", message.html_text elif message.audio: - yield 'audio', message.audio.file_id + yield "audio", message.audio.file_id elif message.animation: - yield 'animation', message.animation.file_id + yield "animation", message.animation.file_id elif message.document: - yield 'document', message.document.file_id + yield "document", message.document.file_id elif message.game: - yield 'game', message.game.title + yield "game", message.game.title elif message.photo: - yield 'photo', message.photo[-1].file_id + yield "photo", message.photo[-1].file_id elif message.sticker: - yield 'sticker', message.sticker.file_id + yield "sticker", message.sticker.file_id elif message.video: - yield 'video', message.video.file_id + yield "video", message.video.file_id elif message.video_note: - yield 'video_note', message.video_note.file_id + yield "video_note", message.video_note.file_id elif message.voice: - yield 'voice', message.voice.file_id + yield "voice", message.voice.file_id elif message.contact: - yield 'contact_full_name', message.contact.full_name - yield 'contact_phone_number', message.contact.phone_number + yield "contact_full_name", message.contact.full_name + yield "contact_phone_number", message.contact.phone_number elif message.venue: - yield 'venue_address', message.venue.address - yield 'location_latitude', message.venue.location.latitude - yield 'location_longitude', message.venue.location.longitude + yield "venue_address", message.venue.address + yield "location_latitude", message.venue.location.latitude + yield "location_longitude", message.venue.location.longitude elif message.location: - yield 'location_latitude', message.location.latitude - yield 'location_longitude', message.location.longitude + yield "location_latitude", message.location.latitude + yield "location_longitude", message.location.longitude elif message.new_chat_members: - yield 'new_chat_members', [user.id for user in message.new_chat_members] + yield "new_chat_members", [user.id for user in message.new_chat_members] elif message.left_chat_member: - yield 'left_chat_member', [user.id for user in message.new_chat_members] + yield "left_chat_member", [user.id for user in message.new_chat_members] elif message.invoice: - yield 'invoice_title', message.invoice.title - yield 'invoice_description', message.invoice.description - yield 'invoice_start_parameter', message.invoice.start_parameter - yield 'invoice_currency', message.invoice.currency - yield 'invoice_total_amount', message.invoice.total_amount + yield "invoice_title", message.invoice.title + yield "invoice_description", message.invoice.description + yield "invoice_start_parameter", message.invoice.start_parameter + yield "invoice_currency", message.invoice.currency + yield "invoice_total_amount", message.invoice.total_amount elif message.successful_payment: - yield 'successful_payment_currency', message.successful_payment.currency - yield 'successful_payment_total_amount', message.successful_payment.total_amount - yield 'successful_payment_invoice_payload', message.successful_payment.invoice_payload - yield 'successful_payment_shipping_option_id', message.successful_payment.shipping_option_id - yield 'successful_payment_telegram_payment_charge_id', message.successful_payment.telegram_payment_charge_id - yield 'successful_payment_provider_payment_charge_id', message.successful_payment.provider_payment_charge_id + yield "successful_payment_currency", message.successful_payment.currency + yield "successful_payment_total_amount", message.successful_payment.total_amount + yield "successful_payment_invoice_payload", message.successful_payment.invoice_payload + yield "successful_payment_shipping_option_id", message.successful_payment.shipping_option_id + yield "successful_payment_telegram_payment_charge_id", message.successful_payment.telegram_payment_charge_id + yield "successful_payment_provider_payment_charge_id", message.successful_payment.provider_payment_charge_id elif message.connected_website: - yield 'connected_website', message.connected_website + yield "connected_website", message.connected_website elif message.migrate_from_chat_id: - yield 'migrate_from_chat_id', message.migrate_from_chat_id + yield "migrate_from_chat_id", message.migrate_from_chat_id elif message.migrate_to_chat_id: - yield 'migrate_to_chat_id', message.migrate_to_chat_id + yield "migrate_to_chat_id", message.migrate_to_chat_id elif message.pinned_message: - yield from self.make_prefix('pinned_message', message.pinned_message) + yield from self.make_prefix("pinned_message", message.pinned_message) elif message.new_chat_title: - yield 'new_chat_title', message.new_chat_title + yield "new_chat_title", message.new_chat_title elif message.new_chat_photo: - yield 'new_chat_photo', message.new_chat_photo[-1].file_id + yield "new_chat_photo", message.new_chat_photo[-1].file_id # elif message.delete_chat_photo: # yield 'delete_chat_photo', message.delete_chat_photo # elif message.group_chat_created: @@ -370,53 +430,55 @@ class LoggingFilter(logging.Filter): # yield 'passport_data', message.passport_data def process_inline_query(self, inline_query: types.InlineQuery): - yield 'inline_query_id', inline_query.id + yield "inline_query_id", inline_query.id yield from self.process_user(inline_query.from_user) if self.include_content: - yield 'inline_query_text', inline_query.query + yield "inline_query_text", inline_query.query if inline_query.location: - yield 'location_latitude', inline_query.location.latitude - yield 'location_longitude', inline_query.location.longitude + yield "location_latitude", inline_query.location.latitude + yield "location_longitude", inline_query.location.longitude if inline_query.offset: - yield 'inline_query_offset', inline_query.offset + yield "inline_query_offset", inline_query.offset def process_chosen_inline_result(self, chosen_inline_result: types.ChosenInlineResult): - yield 'chosen_inline_result_id', chosen_inline_result.result_id + yield "chosen_inline_result_id", chosen_inline_result.result_id yield from self.process_user(chosen_inline_result.from_user) if self.include_content: - yield 'inline_query_text', chosen_inline_result.query + yield "inline_query_text", chosen_inline_result.query if chosen_inline_result.location: - yield 'location_latitude', chosen_inline_result.location.latitude - yield 'location_longitude', chosen_inline_result.location.longitude + yield "location_latitude", chosen_inline_result.location.latitude + yield "location_longitude", chosen_inline_result.location.longitude def process_callback_query(self, callback_query: types.CallbackQuery): yield from self.process_user(callback_query.from_user) - yield 'callback_query_data', callback_query.data + yield "callback_query_data", callback_query.data if callback_query.message: - yield from self.make_prefix('callback_query_message', self.process_message(callback_query.message)) + yield from self.make_prefix( + "callback_query_message", self.process_message(callback_query.message) + ) if callback_query.inline_message_id: - yield 'callback_query_inline_message_id', callback_query.inline_message_id + yield "callback_query_inline_message_id", callback_query.inline_message_id if callback_query.chat_instance: - yield 'callback_query_chat_instance', callback_query.chat_instance + yield "callback_query_chat_instance", callback_query.chat_instance if callback_query.game_short_name: - yield 'callback_query_game_short_name', callback_query.game_short_name + yield "callback_query_game_short_name", callback_query.game_short_name def process_shipping_query(self, shipping_query: types.ShippingQuery): - yield 'shipping_query_id', shipping_query.id + yield "shipping_query_id", shipping_query.id yield from self.process_user(shipping_query.from_user) if self.include_content: - yield 'shipping_query_invoice_payload', shipping_query.invoice_payload + yield "shipping_query_invoice_payload", shipping_query.invoice_payload def process_pre_checkout_query(self, pre_checkout_query: types.PreCheckoutQuery): - yield 'pre_checkout_query_id', pre_checkout_query.id + yield "pre_checkout_query_id", pre_checkout_query.id yield from self.process_user(pre_checkout_query.from_user) if self.include_content: - yield 'pre_checkout_query_currency', pre_checkout_query.currency - yield 'pre_checkout_query_total_amount', pre_checkout_query.total_amount - yield 'pre_checkout_query_invoice_payload', pre_checkout_query.invoice_payload - yield 'pre_checkout_query_shipping_option_id', pre_checkout_query.shipping_option_id + yield "pre_checkout_query_currency", pre_checkout_query.currency + yield "pre_checkout_query_total_amount", pre_checkout_query.total_amount + yield "pre_checkout_query_invoice_payload", pre_checkout_query.invoice_payload + yield "pre_checkout_query_shipping_option_id", pre_checkout_query.shipping_option_id diff --git a/aiogram/dispatcher/__init__.py b/aiogram/dispatcher/__init__.py index 6ad43bbe..7c039873 100644 --- a/aiogram/dispatcher/__init__.py +++ b/aiogram/dispatcher/__init__.py @@ -6,12 +6,12 @@ from . import webhook from .dispatcher import Dispatcher, FSMContext, DEFAULT_RATE_LIMIT __all__ = [ - 'DEFAULT_RATE_LIMIT', - 'Dispatcher', - 'FSMContext', - 'filters', - 'handler', - 'middlewares', - 'storage', - 'webhook' + "DEFAULT_RATE_LIMIT", + "Dispatcher", + "FSMContext", + "filters", + "handler", + "middlewares", + "storage", + "webhook", ] diff --git a/aiogram/dispatcher/dispatcher.py b/aiogram/dispatcher/dispatcher.py index e11ff536..65ddfed3 100644 --- a/aiogram/dispatcher/dispatcher.py +++ b/aiogram/dispatcher/dispatcher.py @@ -8,12 +8,29 @@ import typing import aiohttp from aiohttp.helpers import sentinel -from .filters import Command, ContentTypeFilter, ExceptionsFilter, FiltersFactory, HashTag, Regexp, \ - RegexpCommandsFilter, StateFilter, Text +from .filters import ( + Command, + ContentTypeFilter, + ExceptionsFilter, + FiltersFactory, + HashTag, + Regexp, + RegexpCommandsFilter, + StateFilter, + Text, +) from .handler import Handler from .middlewares import MiddlewareManager -from .storage import BaseStorage, DELTA, DisabledStorage, EXCEEDED_COUNT, FSMContext, \ - LAST_CALL, RATE_LIMIT, RESULT +from .storage import ( + BaseStorage, + DELTA, + DisabledStorage, + EXCEEDED_COUNT, + FSMContext, + LAST_CALL, + RATE_LIMIT, + RESULT, +) from .webhook import BaseResponse from .. import types from ..bot import Bot @@ -22,7 +39,7 @@ from ..utils.mixins import ContextInstanceMixin, DataMixin log = logging.getLogger(__name__) -DEFAULT_RATE_LIMIT = .1 +DEFAULT_RATE_LIMIT = 0.1 class Dispatcher(DataMixin, ContextInstanceMixin): @@ -33,13 +50,21 @@ class Dispatcher(DataMixin, ContextInstanceMixin): inline queries, chosen inline results, callback queries, shipping queries, pre-checkout queries. """ - def __init__(self, bot, loop=None, storage: typing.Optional[BaseStorage] = None, - run_tasks_by_default: bool = False, - throttling_rate_limit=DEFAULT_RATE_LIMIT, no_throttle_error=False, - filters_factory=None): + def __init__( + self, + bot, + loop=None, + storage: typing.Optional[BaseStorage] = None, + run_tasks_by_default: bool = False, + throttling_rate_limit=DEFAULT_RATE_LIMIT, + no_throttle_error=False, + filters_factory=None, + ): if not isinstance(bot, Bot): - raise TypeError(f"Argument 'bot' must be an instance of Bot, not '{type(bot).__name__}'") + raise TypeError( + f"Argument 'bot' must be an instance of Bot, not '{type(bot).__name__}'" + ) if loop is None: loop = bot.loop @@ -57,18 +82,18 @@ class Dispatcher(DataMixin, ContextInstanceMixin): self.no_throttle_error = no_throttle_error self.filters_factory: FiltersFactory = filters_factory - self.updates_handler = Handler(self, middleware_key='update') - self.message_handlers = Handler(self, middleware_key='message') - self.edited_message_handlers = Handler(self, middleware_key='edited_message') - self.channel_post_handlers = Handler(self, middleware_key='channel_post') - self.edited_channel_post_handlers = Handler(self, middleware_key='edited_channel_post') - self.inline_query_handlers = Handler(self, middleware_key='inline_query') - self.chosen_inline_result_handlers = Handler(self, middleware_key='chosen_inline_result') - self.callback_query_handlers = Handler(self, middleware_key='callback_query') - self.shipping_query_handlers = Handler(self, middleware_key='shipping_query') - self.pre_checkout_query_handlers = Handler(self, middleware_key='pre_checkout_query') - self.poll_handlers = Handler(self, middleware_key='poll') - self.errors_handlers = Handler(self, once=False, middleware_key='error') + self.updates_handler = Handler(self, middleware_key="update") + self.message_handlers = Handler(self, middleware_key="message") + self.edited_message_handlers = Handler(self, middleware_key="edited_message") + self.channel_post_handlers = Handler(self, middleware_key="channel_post") + self.edited_channel_post_handlers = Handler(self, middleware_key="edited_channel_post") + self.inline_query_handlers = Handler(self, middleware_key="inline_query") + self.chosen_inline_result_handlers = Handler(self, middleware_key="chosen_inline_result") + self.callback_query_handlers = Handler(self, middleware_key="callback_query") + self.shipping_query_handlers = Handler(self, middleware_key="shipping_query") + self.pre_checkout_query_handlers = Handler(self, middleware_key="pre_checkout_query") + self.poll_handlers = Handler(self, middleware_key="poll") + self.errors_handlers = Handler(self, once=False, middleware_key="error") self.middleware = MiddlewareManager(self) @@ -83,37 +108,57 @@ class Dispatcher(DataMixin, ContextInstanceMixin): def _setup_filters(self): filters_factory = self.filters_factory - filters_factory.bind(StateFilter, exclude_event_handlers=[ - self.errors_handlers, - self.poll_handlers - ]) - filters_factory.bind(ContentTypeFilter, event_handlers=[ - self.message_handlers, self.edited_message_handlers, - self.channel_post_handlers, self.edited_channel_post_handlers, - ]), - filters_factory.bind(Command, event_handlers=[ - self.message_handlers, self.edited_message_handlers - ]) - filters_factory.bind(Text, event_handlers=[ - self.message_handlers, self.edited_message_handlers, - self.channel_post_handlers, self.edited_channel_post_handlers, - self.callback_query_handlers, self.poll_handlers - ]) - filters_factory.bind(HashTag, event_handlers=[ - self.message_handlers, self.edited_message_handlers, - self.channel_post_handlers, self.edited_channel_post_handlers - ]) - filters_factory.bind(Regexp, event_handlers=[ - self.message_handlers, self.edited_message_handlers, - self.channel_post_handlers, self.edited_channel_post_handlers, - self.callback_query_handlers, self.poll_handlers - ]) - filters_factory.bind(RegexpCommandsFilter, event_handlers=[ - self.message_handlers, self.edited_message_handlers - ]) - filters_factory.bind(ExceptionsFilter, event_handlers=[ - self.errors_handlers - ]) + filters_factory.bind( + StateFilter, exclude_event_handlers=[self.errors_handlers, self.poll_handlers] + ) + filters_factory.bind( + ContentTypeFilter, + event_handlers=[ + self.message_handlers, + self.edited_message_handlers, + self.channel_post_handlers, + self.edited_channel_post_handlers, + ], + ), + filters_factory.bind( + Command, event_handlers=[self.message_handlers, self.edited_message_handlers] + ) + filters_factory.bind( + Text, + event_handlers=[ + self.message_handlers, + self.edited_message_handlers, + self.channel_post_handlers, + self.edited_channel_post_handlers, + self.callback_query_handlers, + self.poll_handlers, + ], + ) + filters_factory.bind( + HashTag, + event_handlers=[ + self.message_handlers, + self.edited_message_handlers, + self.channel_post_handlers, + self.edited_channel_post_handlers, + ], + ) + filters_factory.bind( + Regexp, + event_handlers=[ + self.message_handlers, + self.edited_message_handlers, + self.channel_post_handlers, + self.edited_channel_post_handlers, + self.callback_query_handlers, + self.poll_handlers, + ], + ) + filters_factory.bind( + RegexpCommandsFilter, + event_handlers=[self.message_handlers, self.edited_message_handlers], + ) + filters_factory.bind(ExceptionsFilter, event_handlers=[self.errors_handlers]) def __del__(self): self.stop_polling() @@ -209,13 +254,15 @@ class Dispatcher(DataMixin, ContextInstanceMixin): return await self.bot.delete_webhook() - async def start_polling(self, - timeout=20, - relax=0.1, - limit=None, - reset_webhook=None, - fast: typing.Optional[bool] = True, - error_sleep: int = 5): + async def start_polling( + self, + timeout=20, + relax=0.1, + limit=None, + reset_webhook=None, + fast: typing.Optional[bool] = True, + error_sleep: int = 5, + ): """ Start long-polling @@ -227,9 +274,9 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :return: """ if self._polling: - raise RuntimeError('Polling already started') + raise RuntimeError("Polling already started") - log.info('Start polling.') + log.info("Start polling.") # context.set_value(MODE, LONG_POLLING) Dispatcher.set_current(self) @@ -245,16 +292,20 @@ class Dispatcher(DataMixin, ContextInstanceMixin): try: current_request_timeout = self.bot.timeout if current_request_timeout is not sentinel and timeout is not None: - request_timeout = aiohttp.ClientTimeout(total=current_request_timeout.total + timeout or 1) + request_timeout = aiohttp.ClientTimeout( + total=current_request_timeout.total + timeout or 1 + ) else: request_timeout = None while self._polling: try: with self.bot.request_timeout(request_timeout): - updates = await self.bot.get_updates(limit=limit, offset=offset, timeout=timeout) + updates = await self.bot.get_updates( + limit=limit, offset=offset, timeout=timeout + ) except: - log.exception('Cause exception while getting updates.') + log.exception("Cause exception while getting updates.") await asyncio.sleep(error_sleep) continue @@ -269,7 +320,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): finally: self._close_waiter._set_result(None) - log.warning('Polling is stopped.') + log.warning("Polling is stopped.") async def _process_polling_updates(self, updates, fast: typing.Optional[bool] = True): """ @@ -288,7 +339,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): try: asyncio.gather(*need_to_call) except TelegramAPIError: - log.exception('Cause exception while processing updates.') + log.exception("Cause exception while processing updates.") def stop_polling(self): """ @@ -296,8 +347,8 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :return: """ - if hasattr(self, '_polling') and self._polling: - log.info('Stop polling...') + if hasattr(self, "_polling") and self._polling: + log.info("Stop polling...") self._polling = False async def wait_closed(self): @@ -316,8 +367,17 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ return self._polling - def register_message_handler(self, callback, *custom_filters, commands=None, regexp=None, content_types=None, - state=None, run_task=None, **kwargs): + def register_message_handler( + self, + callback, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Register handler for message @@ -343,17 +403,27 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param state: :return: decorated function """ - filters_set = self.filters_factory.resolve(self.message_handlers, - *custom_filters, - commands=commands, - regexp=regexp, - content_types=content_types, - state=state, - **kwargs) + filters_set = self.filters_factory.resolve( + self.message_handlers, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + **kwargs, + ) self.message_handlers.register(self._wrap_async_task(callback, run_task), filters_set) - def message_handler(self, *custom_filters, commands=None, regexp=None, content_types=None, state=None, - run_task=None, **kwargs): + def message_handler( + self, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Decorator for message handler @@ -424,15 +494,31 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_message_handler(callback, *custom_filters, - commands=commands, regexp=regexp, content_types=content_types, - state=state, run_task=run_task, **kwargs) + self.register_message_handler( + callback, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + run_task=run_task, + **kwargs, + ) return callback return decorator - def register_edited_message_handler(self, callback, *custom_filters, commands=None, regexp=None, content_types=None, - state=None, run_task=None, **kwargs): + def register_edited_message_handler( + self, + callback, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Register handler for edited message @@ -446,17 +532,29 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param kwargs: :return: decorated function """ - filters_set = self.filters_factory.resolve(self.edited_message_handlers, - *custom_filters, - commands=commands, - regexp=regexp, - content_types=content_types, - state=state, - **kwargs) - self.edited_message_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + filters_set = self.filters_factory.resolve( + self.edited_message_handlers, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + **kwargs, + ) + self.edited_message_handlers.register( + self._wrap_async_task(callback, run_task), filters_set + ) - def edited_message_handler(self, *custom_filters, commands=None, regexp=None, content_types=None, - state=None, run_task=None, **kwargs): + def edited_message_handler( + self, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Decorator for edited message handler @@ -479,14 +577,31 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_edited_message_handler(callback, *custom_filters, commands=commands, regexp=regexp, - content_types=content_types, state=state, run_task=run_task, **kwargs) + self.register_edited_message_handler( + callback, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + run_task=run_task, + **kwargs, + ) return callback return decorator - def register_channel_post_handler(self, callback, *custom_filters, commands=None, regexp=None, content_types=None, - state=None, run_task=None, **kwargs): + def register_channel_post_handler( + self, + callback, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Register handler for channel post @@ -500,17 +615,27 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param kwargs: :return: decorated function """ - filters_set = self.filters_factory.resolve(self.channel_post_handlers, - *custom_filters, - commands=commands, - regexp=regexp, - content_types=content_types, - state=state, - **kwargs) + filters_set = self.filters_factory.resolve( + self.channel_post_handlers, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + **kwargs, + ) self.channel_post_handlers.register(self._wrap_async_task(callback, run_task), filters_set) - def channel_post_handler(self, *custom_filters, commands=None, regexp=None, content_types=None, - state=None, run_task=None, **kwargs): + def channel_post_handler( + self, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Decorator for channel post handler @@ -525,14 +650,31 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_channel_post_handler(callback, *custom_filters, commands=commands, regexp=regexp, - content_types=content_types, state=state, run_task=run_task, **kwargs) + self.register_channel_post_handler( + callback, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + run_task=run_task, + **kwargs, + ) return callback return decorator - def register_edited_channel_post_handler(self, callback, *custom_filters, commands=None, regexp=None, - content_types=None, state=None, run_task=None, **kwargs): + def register_edited_channel_post_handler( + self, + callback, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Register handler for edited channel post @@ -546,17 +688,29 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param kwargs: :return: decorated function """ - filters_set = self.filters_factory.resolve(self.edited_message_handlers, - *custom_filters, - commands=commands, - regexp=regexp, - content_types=content_types, - state=state, - **kwargs) - self.edited_channel_post_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + filters_set = self.filters_factory.resolve( + self.edited_message_handlers, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + **kwargs, + ) + self.edited_channel_post_handlers.register( + self._wrap_async_task(callback, run_task), filters_set + ) - def edited_channel_post_handler(self, *custom_filters, commands=None, regexp=None, content_types=None, - state=None, run_task=None, **kwargs): + def edited_channel_post_handler( + self, + *custom_filters, + commands=None, + regexp=None, + content_types=None, + state=None, + run_task=None, + **kwargs, + ): """ Decorator for edited channel post handler @@ -571,14 +725,23 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_edited_channel_post_handler(callback, *custom_filters, commands=commands, regexp=regexp, - content_types=content_types, state=state, run_task=run_task, - **kwargs) + self.register_edited_channel_post_handler( + callback, + *custom_filters, + commands=commands, + regexp=regexp, + content_types=content_types, + state=state, + run_task=run_task, + **kwargs, + ) return callback return decorator - def register_inline_handler(self, callback, *custom_filters, state=None, run_task=None, **kwargs): + def register_inline_handler( + self, callback, *custom_filters, state=None, run_task=None, **kwargs + ): """ Register handler for inline query @@ -597,10 +760,9 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ if custom_filters is None: custom_filters = [] - filters_set = self.filters_factory.resolve(self.inline_query_handlers, - *custom_filters, - state=state, - **kwargs) + filters_set = self.filters_factory.resolve( + self.inline_query_handlers, *custom_filters, state=state, **kwargs + ) self.inline_query_handlers.register(self._wrap_async_task(callback, run_task), filters_set) def inline_handler(self, *custom_filters, state=None, run_task=None, **kwargs): @@ -622,12 +784,16 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_inline_handler(callback, *custom_filters, state=state, run_task=run_task, **kwargs) + self.register_inline_handler( + callback, *custom_filters, state=state, run_task=run_task, **kwargs + ) return callback return decorator - def register_chosen_inline_handler(self, callback, *custom_filters, state=None, run_task=None, **kwargs): + def register_chosen_inline_handler( + self, callback, *custom_filters, state=None, run_task=None, **kwargs + ): """ Register handler for chosen inline query @@ -646,11 +812,12 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ if custom_filters is None: custom_filters = [] - filters_set = self.filters_factory.resolve(self.chosen_inline_result_handlers, - *custom_filters, - state=state, - **kwargs) - self.chosen_inline_result_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + filters_set = self.filters_factory.resolve( + self.chosen_inline_result_handlers, *custom_filters, state=state, **kwargs + ) + self.chosen_inline_result_handlers.register( + self._wrap_async_task(callback, run_task), filters_set + ) def chosen_inline_handler(self, *custom_filters, state=None, run_task=None, **kwargs): """ @@ -671,12 +838,16 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_chosen_inline_handler(callback, *custom_filters, state=state, run_task=run_task, **kwargs) + self.register_chosen_inline_handler( + callback, *custom_filters, state=state, run_task=run_task, **kwargs + ) return callback return decorator - def register_callback_query_handler(self, callback, *custom_filters, state=None, run_task=None, **kwargs): + def register_callback_query_handler( + self, callback, *custom_filters, state=None, run_task=None, **kwargs + ): """ Register handler for callback query @@ -692,11 +863,12 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param run_task: run callback in task (no wait results) :param kwargs: """ - filters_set = self.filters_factory.resolve(self.callback_query_handlers, - *custom_filters, - state=state, - **kwargs) - self.callback_query_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + filters_set = self.filters_factory.resolve( + self.callback_query_handlers, *custom_filters, state=state, **kwargs + ) + self.callback_query_handlers.register( + self._wrap_async_task(callback, run_task), filters_set + ) def callback_query_handler(self, *custom_filters, state=None, run_task=None, **kwargs): """ @@ -716,13 +888,16 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_callback_query_handler(callback, *custom_filters, state=state, run_task=run_task, **kwargs) + self.register_callback_query_handler( + callback, *custom_filters, state=state, run_task=run_task, **kwargs + ) return callback return decorator - def register_shipping_query_handler(self, callback, *custom_filters, state=None, run_task=None, - **kwargs): + def register_shipping_query_handler( + self, callback, *custom_filters, state=None, run_task=None, **kwargs + ): """ Register handler for shipping query @@ -738,11 +913,12 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param run_task: run callback in task (no wait results) :param kwargs: """ - filters_set = self.filters_factory.resolve(self.shipping_query_handlers, - *custom_filters, - state=state, - **kwargs) - self.shipping_query_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + filters_set = self.filters_factory.resolve( + self.shipping_query_handlers, *custom_filters, state=state, **kwargs + ) + self.shipping_query_handlers.register( + self._wrap_async_task(callback, run_task), filters_set + ) def shipping_query_handler(self, *custom_filters, state=None, run_task=None, **kwargs): """ @@ -762,12 +938,16 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_shipping_query_handler(callback, *custom_filters, state=state, run_task=run_task, **kwargs) + self.register_shipping_query_handler( + callback, *custom_filters, state=state, run_task=run_task, **kwargs + ) return callback return decorator - def register_pre_checkout_query_handler(self, callback, *custom_filters, state=None, run_task=None, **kwargs): + def register_pre_checkout_query_handler( + self, callback, *custom_filters, state=None, run_task=None, **kwargs + ): """ Register handler for pre-checkout query @@ -783,11 +963,12 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param run_task: run callback in task (no wait results) :param kwargs: """ - filters_set = self.filters_factory.resolve(self.pre_checkout_query_handlers, - *custom_filters, - state=state, - **kwargs) - self.pre_checkout_query_handlers.register(self._wrap_async_task(callback, run_task), filters_set) + filters_set = self.filters_factory.resolve( + self.pre_checkout_query_handlers, *custom_filters, state=state, **kwargs + ) + self.pre_checkout_query_handlers.register( + self._wrap_async_task(callback, run_task), filters_set + ) def pre_checkout_query_handler(self, *custom_filters, state=None, run_task=None, **kwargs): """ @@ -807,27 +988,27 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_pre_checkout_query_handler(callback, *custom_filters, state=state, run_task=run_task, - **kwargs) + self.register_pre_checkout_query_handler( + callback, *custom_filters, state=state, run_task=run_task, **kwargs + ) return callback return decorator def register_poll_handler(self, callback, *custom_filters, run_task=None, **kwargs): - filters_set = self.filters_factory.resolve(self.poll_handlers, - *custom_filters, - **kwargs) + filters_set = self.filters_factory.resolve(self.poll_handlers, *custom_filters, **kwargs) self.poll_handlers.register(self._wrap_async_task(callback, run_task), filters_set) def poll_handler(self, *custom_filters, run_task=None, **kwargs): def decorator(callback): - self.register_poll_handler(callback, *custom_filters, run_task=run_task, - **kwargs) + self.register_poll_handler(callback, *custom_filters, run_task=run_task, **kwargs) return callback return decorator - def register_errors_handler(self, callback, *custom_filters, exception=None, run_task=None, **kwargs): + def register_errors_handler( + self, callback, *custom_filters, exception=None, run_task=None, **kwargs + ): """ Register handler for errors @@ -835,10 +1016,9 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :param exception: you can make handler for specific errors type :param run_task: run callback in task (no wait results) """ - filters_set = self.filters_factory.resolve(self.errors_handlers, - *custom_filters, - exception=exception, - **kwargs) + filters_set = self.filters_factory.resolve( + self.errors_handlers, *custom_filters, exception=exception, **kwargs + ) self.errors_handlers.register(self._wrap_async_task(callback, run_task), filters_set) def errors_handler(self, *custom_filters, exception=None, run_task=None, **kwargs): @@ -851,15 +1031,22 @@ class Dispatcher(DataMixin, ContextInstanceMixin): """ def decorator(callback): - self.register_errors_handler(self._wrap_async_task(callback, run_task), - *custom_filters, exception=exception, **kwargs) + self.register_errors_handler( + self._wrap_async_task(callback, run_task), + *custom_filters, + exception=exception, + **kwargs, + ) return callback return decorator - def current_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None) -> FSMContext: + def current_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + ) -> FSMContext: """ Get current state for user in chat as context @@ -897,7 +1084,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :return: bool """ if not self.storage.has_bucket(): - raise RuntimeError('This storage does not provide Leaky Bucket') + raise RuntimeError("This storage does not provide Leaky Bucket") if no_error is None: no_error = self.no_throttle_error @@ -951,7 +1138,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :return: """ if not self.storage.has_bucket(): - raise RuntimeError('This storage does not provide Leaky Bucket') + raise RuntimeError("This storage does not provide Leaky Bucket") if user is None and chat is None: user = types.User.get_current() @@ -971,7 +1158,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): :return: """ if not self.storage.has_bucket(): - raise RuntimeError('This storage does not provide Leaky Bucket') + raise RuntimeError("This storage does not provide Leaky Bucket") if user is None and chat is None: user = types.User.get_current() @@ -979,7 +1166,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): bucket = await self.storage.get_bucket(chat=chat, user=user) if bucket and key in bucket: - del bucket['key'] + del bucket["key"] await self.storage.set_bucket(chat=chat, user=user, bucket=bucket) return True return False @@ -1005,8 +1192,7 @@ class Dispatcher(DataMixin, ContextInstanceMixin): try: response = task.result() except Exception as e: - self.loop.create_task( - self.errors_handlers.notify(types.Update.get_current(), e)) + self.loop.create_task(self.errors_handlers.notify(types.Update.get_current(), e)) else: if isinstance(response, BaseResponse): self.loop.create_task(response.execute_response(self.bot)) diff --git a/aiogram/dispatcher/filters/__init__.py b/aiogram/dispatcher/filters/__init__.py index 2ae959cf..4752f56a 100644 --- a/aiogram/dispatcher/filters/__init__.py +++ b/aiogram/dispatcher/filters/__init__.py @@ -1,30 +1,51 @@ -from .builtin import Command, CommandHelp, CommandPrivacy, CommandSettings, CommandStart, ContentTypeFilter, \ - ExceptionsFilter, HashTag, Regexp, RegexpCommandsFilter, StateFilter, Text +from .builtin import ( + Command, + CommandHelp, + CommandPrivacy, + CommandSettings, + CommandStart, + ContentTypeFilter, + ExceptionsFilter, + HashTag, + Regexp, + RegexpCommandsFilter, + StateFilter, + Text, +) from .factory import FiltersFactory -from .filters import AbstractFilter, BoundFilter, Filter, FilterNotPassed, FilterRecord, execute_filter, \ - check_filters, get_filter_spec, get_filters_spec +from .filters import ( + AbstractFilter, + BoundFilter, + Filter, + FilterNotPassed, + FilterRecord, + execute_filter, + check_filters, + get_filter_spec, + get_filters_spec, +) __all__ = [ - 'AbstractFilter', - 'BoundFilter', - 'Command', - 'CommandStart', - 'CommandHelp', - 'CommandPrivacy', - 'CommandSettings', - 'ContentTypeFilter', - 'ExceptionsFilter', - 'HashTag', - 'Filter', - 'FilterNotPassed', - 'FilterRecord', - 'FiltersFactory', - 'RegexpCommandsFilter', - 'Regexp', - 'StateFilter', - 'Text', - 'get_filter_spec', - 'get_filters_spec', - 'execute_filter', - 'check_filters' + "AbstractFilter", + "BoundFilter", + "Command", + "CommandStart", + "CommandHelp", + "CommandPrivacy", + "CommandSettings", + "ContentTypeFilter", + "ExceptionsFilter", + "HashTag", + "Filter", + "FilterNotPassed", + "FilterRecord", + "FiltersFactory", + "RegexpCommandsFilter", + "Regexp", + "StateFilter", + "Text", + "get_filter_spec", + "get_filters_spec", + "execute_filter", + "check_filters", ] diff --git a/aiogram/dispatcher/filters/builtin.py b/aiogram/dispatcher/filters/builtin.py index 011b9b67..c7526b7b 100644 --- a/aiogram/dispatcher/filters/builtin.py +++ b/aiogram/dispatcher/filters/builtin.py @@ -21,10 +21,13 @@ class Command(Filter): By default this filter is registered for messages and edited messages handlers. """ - def __init__(self, commands: Union[Iterable, str], - prefixes: Union[Iterable, str] = '/', - ignore_case: bool = True, - ignore_mention: bool = False): + def __init__( + self, + commands: Union[Iterable, str], + prefixes: Union[Iterable, str] = "/", + ignore_case: bool = True, + ignore_mention: bool = False, + ): """ Filter can be initialized from filters factory or by simply creating instance of this class. @@ -66,30 +69,38 @@ class Command(Filter): :return: config or empty dict """ config = {} - if 'commands' in full_config: - config['commands'] = full_config.pop('commands') - if config and 'commands_prefix' in full_config: - config['prefixes'] = full_config.pop('commands_prefix') - if config and 'commands_ignore_mention' in full_config: - config['ignore_mention'] = full_config.pop('commands_ignore_mention') + if "commands" in full_config: + config["commands"] = full_config.pop("commands") + if config and "commands_prefix" in full_config: + config["prefixes"] = full_config.pop("commands_prefix") + if config and "commands_ignore_mention" in full_config: + config["ignore_mention"] = full_config.pop("commands_ignore_mention") return config async def check(self, message: types.Message): - return await self.check_command(message, self.commands, self.prefixes, self.ignore_case, self.ignore_mention) + return await self.check_command( + message, self.commands, self.prefixes, self.ignore_case, self.ignore_mention + ) @staticmethod - async def check_command(message: types.Message, commands, prefixes, ignore_case=True, ignore_mention=False): + async def check_command( + message: types.Message, commands, prefixes, ignore_case=True, ignore_mention=False + ): full_command = message.text.split()[0] - prefix, (command, _, mention) = full_command[0], full_command[1:].partition('@') + prefix, (command, _, mention) = (full_command[0], full_command[1:].partition("@")) - if not ignore_mention and mention and (await message.bot.me).username.lower() != mention.lower(): + if ( + not ignore_mention + and mention + and (await message.bot.me).username.lower() != mention.lower() + ): return False elif prefix not in prefixes: return False elif (command.lower() if ignore_case else command) not in commands: return False - return {'command': Command.CommandObj(command=command, prefix=prefix, mention=mention)} + return {"command": Command.CommandObj(command=command, prefix=prefix, mention=mention)} @dataclass class CommandObj: @@ -100,9 +111,9 @@ class Command(Filter): """ """Command prefix""" - prefix: str = '/' + prefix: str = "/" """Command without prefix and mention""" - command: str = '' + command: str = "" """Mention (if available)""" mention: str = None """Command argument""" @@ -126,9 +137,9 @@ class Command(Filter): """ line = self.prefix + self.command if self.mentioned: - line += '@' + self.mention + line += "@" + self.mention if self.args: - line += ' ' + self.args + line += " " + self.args return line @@ -149,7 +160,7 @@ class CommandStart(Command): :param deep_link: string or compiled regular expression (by ``re.compile(...)``). """ - super(CommandStart, self).__init__(['start']) + super(CommandStart, self).__init__(["start"]) self.deep_link = deep_link async def check(self, message: types.Message): @@ -167,7 +178,7 @@ class CommandStart(Command): match = self.deep_link.match(message.get_args()) if match: - return {'deep_link': match} + return {"deep_link": match} return False return check @@ -179,7 +190,7 @@ class CommandHelp(Command): """ def __init__(self): - super(CommandHelp, self).__init__(['help']) + super(CommandHelp, self).__init__(["help"]) class CommandSettings(Command): @@ -188,7 +199,7 @@ class CommandSettings(Command): """ def __init__(self): - super(CommandSettings, self).__init__(['settings']) + super(CommandSettings, self).__init__(["settings"]) class CommandPrivacy(Command): @@ -197,7 +208,7 @@ class CommandPrivacy(Command): """ def __init__(self): - super(CommandPrivacy, self).__init__(['privacy']) + super(CommandPrivacy, self).__init__(["privacy"]) class Text(Filter): @@ -205,12 +216,14 @@ class Text(Filter): Simple text filter """ - def __init__(self, - equals: Optional[Union[str, LazyProxy]] = None, - contains: Optional[Union[str, LazyProxy]] = None, - startswith: Optional[Union[str, LazyProxy]] = None, - endswith: Optional[Union[str, LazyProxy]] = None, - ignore_case=False): + def __init__( + self, + equals: Optional[Union[str, LazyProxy]] = None, + contains: Optional[Union[str, LazyProxy]] = None, + startswith: Optional[Union[str, LazyProxy]] = None, + endswith: Optional[Union[str, LazyProxy]] = None, + ignore_case=False, + ): """ Check text for one of pattern. Only one mode can be used in one filter. @@ -223,11 +236,18 @@ class Text(Filter): # Only one mode can be used. check it. check = sum(map(bool, (equals, contains, startswith, endswith))) if check > 1: - args = "' and '".join([arg[0] for arg in [('equals', equals), - ('contains', contains), - ('startswith', startswith), - ('endswith', endswith) - ] if arg[1]]) + args = "' and '".join( + [ + arg[0] + for arg in [ + ("equals", equals), + ("contains", contains), + ("startswith", startswith), + ("endswith", endswith), + ] + if arg[1] + ] + ) raise ValueError(f"Arguments '{args}' cannot be used together.") elif check == 0: raise ValueError(f"No one mode is specified!") @@ -240,18 +260,18 @@ class Text(Filter): @classmethod def validate(cls, full_config: Dict[str, Any]): - if 'text' in full_config: - return {'equals': full_config.pop('text')} - elif 'text_contains' in full_config: - return {'contains': full_config.pop('text_contains')} - elif 'text_startswith' in full_config: - return {'startswith': full_config.pop('text_startswith')} - elif 'text_endswith' in full_config: - return {'endswith': full_config.pop('text_endswith')} + if "text" in full_config: + return {"equals": full_config.pop("text")} + elif "text_contains" in full_config: + return {"contains": full_config.pop("text_contains")} + elif "text_startswith" in full_config: + return {"startswith": full_config.pop("text_startswith")} + elif "text_endswith" in full_config: + return {"endswith": full_config.pop("text_endswith")} async def check(self, obj: Union[Message, CallbackQuery, InlineQuery]): if isinstance(obj, Message): - text = obj.text or obj.caption or '' + text = obj.text or obj.caption or "" if not text and obj.poll: text = obj.poll.question elif isinstance(obj, CallbackQuery): @@ -287,7 +307,7 @@ class HashTag(Filter): def __init__(self, hashtags=None, cashtags=None): if not hashtags and not cashtags: - raise ValueError('No one hashtag or cashtag is specified!') + raise ValueError("No one hashtag or cashtag is specified!") if hashtags is None: hashtags = [] @@ -307,10 +327,10 @@ class HashTag(Filter): @classmethod def validate(cls, full_config: Dict[str, Any]): config = {} - if 'hashtags' in full_config: - config['hashtags'] = full_config.pop('hashtags') - if 'cashtags' in full_config: - config['cashtags'] = full_config.pop('cashtags') + if "hashtags" in full_config: + config["hashtags"] = full_config.pop("hashtags") + if "cashtags" in full_config: + config["cashtags"] = full_config.pop("cashtags") return config async def check(self, message: types.Message): @@ -324,9 +344,13 @@ class HashTag(Filter): return False hashtags, cashtags = self._get_tags(text, entities) - if self.hashtags and set(hashtags) & set(self.hashtags) \ - or self.cashtags and set(cashtags) & set(self.cashtags): - return {'hashtags': hashtags, 'cashtags': cashtags} + if ( + self.hashtags + and set(hashtags) & set(self.hashtags) + or self.cashtags + and set(cashtags) & set(self.cashtags) + ): + return {"hashtags": hashtags, "cashtags": cashtags} def _get_tags(self, text, entities): hashtags = [] @@ -334,11 +358,11 @@ class HashTag(Filter): for entity in entities: if entity.type == types.MessageEntityType.HASHTAG: - value = entity.get_text(text).lstrip('#') + value = entity.get_text(text).lstrip("#") hashtags.append(value) elif entity.type == types.MessageEntityType.CASHTAG: - value = entity.get_text(text).lstrip('$') + value = entity.get_text(text).lstrip("$") cashtags.append(value) return hashtags, cashtags @@ -356,12 +380,12 @@ class Regexp(Filter): @classmethod def validate(cls, full_config: Dict[str, Any]): - if 'regexp' in full_config: - return {'regexp': full_config.pop('regexp')} + if "regexp" in full_config: + return {"regexp": full_config.pop("regexp")} async def check(self, obj: Union[Message, CallbackQuery]): if isinstance(obj, Message): - content = obj.text or obj.caption or '' + content = obj.text or obj.caption or "" if not content and obj.poll: content = obj.poll.question elif isinstance(obj, CallbackQuery) and obj.data: @@ -372,7 +396,7 @@ class Regexp(Filter): match = self.regexp.search(content) if match: - return {'regexp': match} + return {"regexp": match} return False @@ -381,17 +405,19 @@ class RegexpCommandsFilter(BoundFilter): Check commands by regexp in message """ - key = 'regexp_commands' + key = "regexp_commands" def __init__(self, regexp_commands): - self.regexp_commands = [re.compile(command, flags=re.IGNORECASE | re.MULTILINE) for command in regexp_commands] + self.regexp_commands = [ + re.compile(command, flags=re.IGNORECASE | re.MULTILINE) for command in regexp_commands + ] async def check(self, message): if not message.is_command(): return False command = message.text.split()[0][1:] - command, _, mention = command.partition('@') + command, _, mention = command.partition("@") if mention and mention != (await message.bot.me).username: return False @@ -399,7 +425,7 @@ class RegexpCommandsFilter(BoundFilter): for command in self.regexp_commands: search = command.search(message.text) if search: - return {'regexp_command': search} + return {"regexp_command": search} return False @@ -408,7 +434,7 @@ class ContentTypeFilter(BoundFilter): Check message content type """ - key = 'content_types' + key = "content_types" required = True default = types.ContentTypes.TEXT @@ -416,18 +442,21 @@ class ContentTypeFilter(BoundFilter): self.content_types = content_types async def check(self, message): - return types.ContentType.ANY in self.content_types or \ - message.content_type in self.content_types + return ( + types.ContentType.ANY in self.content_types + or message.content_type in self.content_types + ) class StateFilter(BoundFilter): """ Check user state """ - key = 'state' + + key = "state" required = True - ctx_state = ContextVar('user_state') + ctx_state = ContextVar("user_state") def __init__(self, dispatcher, state): from aiogram.dispatcher.filters.state import State, StatesGroup @@ -435,7 +464,7 @@ class StateFilter(BoundFilter): self.dispatcher = dispatcher states = [] if not isinstance(state, (list, set, tuple, frozenset)) or state is None: - state = [state, ] + state = [state] for item in state: if isinstance(item, State): states.append(item.state) @@ -446,11 +475,14 @@ class StateFilter(BoundFilter): self.states = states def get_target(self, obj): - return getattr(getattr(obj, 'chat', None), 'id', None), getattr(getattr(obj, 'from_user', None), 'id', None) + return ( + getattr(getattr(obj, "chat", None), "id", None), + getattr(getattr(obj, "from_user", None), "id", None), + ) async def check(self, obj): - if '*' in self.states: - return {'state': self.dispatcher.current_state()} + if "*" in self.states: + return {"state": self.dispatcher.current_state()} try: state = self.ctx_state.get() @@ -461,11 +493,11 @@ class StateFilter(BoundFilter): state = await self.dispatcher.storage.get_state(chat=chat, user=user) self.ctx_state.set(state) if state in self.states: - return {'state': self.dispatcher.current_state(), 'raw_state': state} + return {"state": self.dispatcher.current_state(), "raw_state": state} else: if state in self.states: - return {'state': self.dispatcher.current_state(), 'raw_state': state} + return {"state": self.dispatcher.current_state(), "raw_state": state} return False @@ -475,7 +507,7 @@ class ExceptionsFilter(BoundFilter): Filter for exceptions """ - key = 'exception' + key = "exception" def __init__(self, exception): self.exception = exception diff --git a/aiogram/dispatcher/filters/factory.py b/aiogram/dispatcher/filters/factory.py index 89e3e792..57468475 100644 --- a/aiogram/dispatcher/filters/factory.py +++ b/aiogram/dispatcher/filters/factory.py @@ -13,10 +13,13 @@ class FiltersFactory: self._dispatcher = dispatcher self._registered: typing.List[FilterRecord] = [] - def bind(self, callback: typing.Union[typing.Callable, AbstractFilter], - validator: typing.Optional[typing.Callable] = None, - event_handlers: typing.Optional[typing.List[Handler]] = None, - exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None): + def bind( + self, + callback: typing.Union[typing.Callable, AbstractFilter], + validator: typing.Optional[typing.Callable] = None, + event_handlers: typing.Optional[typing.List[Handler]] = None, + exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None, + ): """ Register filter @@ -38,8 +41,9 @@ class FiltersFactory: if record.callback == callback: self._registered.remove(record) - def resolve(self, event_handler, *custom_filters, **full_config - ) -> typing.List[typing.Union[typing.Callable, AbstractFilter]]: + def resolve( + self, event_handler, *custom_filters, **full_config + ) -> typing.List[typing.Union[typing.Callable, AbstractFilter]]: """ Resolve filters to filters-set @@ -49,8 +53,11 @@ class FiltersFactory: :return: """ filters_set = [] - filters_set.extend(self._resolve_registered(event_handler, - {k: v for k, v in full_config.items() if v is not None})) + filters_set.extend( + self._resolve_registered( + event_handler, {k: v for k, v in full_config.items() if v is not None} + ) + ) if custom_filters: filters_set.extend(custom_filters) @@ -70,4 +77,4 @@ class FiltersFactory: yield filter_ if full_config: - raise NameError('Invalid filter name(s): \'' + '\', '.join(full_config.keys()) + '\'') + raise NameError("Invalid filter name(s): '" + "', ".join(full_config.keys()) + "'") diff --git a/aiogram/dispatcher/filters/filters.py b/aiogram/dispatcher/filters/filters.py index 46e44fc9..04f30c3a 100644 --- a/aiogram/dispatcher/filters/filters.py +++ b/aiogram/dispatcher/filters/filters.py @@ -13,9 +13,11 @@ def wrap_async(func): async def async_wrapper(*args, **kwargs): return func(*args, **kwargs) - if inspect.isawaitable(func) \ - or inspect.iscoroutinefunction(func) \ - or isinstance(func, AbstractFilter): + if ( + inspect.isawaitable(func) + or inspect.iscoroutinefunction(func) + or isinstance(func, AbstractFilter) + ): return func return async_wrapper @@ -23,14 +25,16 @@ def wrap_async(func): def get_filter_spec(dispatcher, filter_: callable): kwargs = {} if not callable(filter_): - raise TypeError('Filter must be callable and/or awaitable!') + raise TypeError("Filter must be callable and/or awaitable!") spec = inspect.getfullargspec(filter_) - if 'dispatcher' in spec: - kwargs['dispatcher'] = dispatcher - if inspect.isawaitable(filter_) \ - or inspect.iscoroutinefunction(filter_) \ - or isinstance(filter_, AbstractFilter): + if "dispatcher" in spec: + kwargs["dispatcher"] = dispatcher + if ( + inspect.isawaitable(filter_) + or inspect.iscoroutinefunction(filter_) + or isinstance(filter_, AbstractFilter) + ): return FilterObj(filter=filter_, kwargs=kwargs, is_async=True) else: return FilterObj(filter=filter_, kwargs=kwargs, is_async=False) @@ -82,12 +86,17 @@ class FilterRecord: Filters record for factory """ - def __init__(self, callback: typing.Callable, - validator: typing.Optional[typing.Callable] = None, - event_handlers: typing.Optional[typing.Iterable[Handler]] = None, - exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None): + def __init__( + self, + callback: typing.Callable, + validator: typing.Optional[typing.Callable] = None, + event_handlers: typing.Optional[typing.Iterable[Handler]] = None, + exclude_event_handlers: typing.Optional[typing.Iterable[Handler]] = None, + ): if event_handlers and exclude_event_handlers: - raise ValueError("'event_handlers' and 'exclude_event_handlers' arguments cannot be used together.") + raise ValueError( + "'event_handlers' and 'exclude_event_handlers' arguments cannot be used together." + ) self.callback = callback self.event_handlers = event_handlers @@ -100,17 +109,17 @@ class FilterRecord: elif issubclass(callback, AbstractFilter): self.resolver = callback.validate else: - raise RuntimeError('validator is required!') + raise RuntimeError("validator is required!") def resolve(self, dispatcher, event_handler, full_config): if not self._check_event_handler(event_handler): return config = self.resolver(full_config) if config: - if 'dispatcher' not in config: + if "dispatcher" not in config: spec = inspect.getfullargspec(self.callback) - if 'dispatcher' in spec.args: - config['dispatcher'] = dispatcher + if "dispatcher" in spec.args: + config["dispatcher"] = dispatcher for key in config: if key in full_config: @@ -133,7 +142,9 @@ class AbstractFilter(abc.ABC): @classmethod @abc.abstractmethod - def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]: + def validate( + cls, full_config: typing.Dict[str, typing.Any] + ) -> typing.Optional[typing.Dict[str, typing.Any]]: """ Validate and parse config. @@ -184,7 +195,9 @@ class Filter(AbstractFilter): """ @classmethod - def validate(cls, full_config: typing.Dict[str, typing.Any]) -> typing.Optional[typing.Dict[str, typing.Any]]: + def validate( + cls, full_config: typing.Dict[str, typing.Any] + ) -> typing.Optional[typing.Dict[str, typing.Any]]: """ Here method ``validate`` is optional. If you need to use filter from filters factory you need to override this method. @@ -228,7 +241,7 @@ class BoundFilter(Filter): class _LogicFilter(Filter): @classmethod def validate(cls, full_config: typing.Dict[str, typing.Any]): - raise ValueError('That filter can\'t be used in filters factory!') + raise ValueError("That filter can't be used in filters factory!") class NotFilter(_LogicFilter): @@ -240,7 +253,6 @@ class NotFilter(_LogicFilter): class AndFilter(_LogicFilter): - def __init__(self, *targets): self.targets = list(wrap_async(target) for target in targets) diff --git a/aiogram/dispatcher/filters/state.py b/aiogram/dispatcher/filters/state.py index afe08e64..495f9fde 100644 --- a/aiogram/dispatcher/filters/state.py +++ b/aiogram/dispatcher/filters/state.py @@ -17,7 +17,7 @@ class State: @property def group(self): if not self._group: - raise RuntimeError('This state is not in any group.') + raise RuntimeError("This state is not in any group.") return self._group def get_root(self): @@ -27,19 +27,19 @@ class State: def state(self): if self._state is None: return None - elif self._state == '*': + elif self._state == "*": return self._state elif self._group_name is None and self._group: group = self._group.__full_group_name__ elif self._group_name: group = self._group_name else: - group = '@' + group = "@" return f"{group}:{self._state}" def set_parent(self, group): if not issubclass(group, StatesGroup): - raise ValueError('Group must be subclass of StatesGroup') + raise ValueError("Group must be subclass of StatesGroup") self._group = group def __set_name__(self, owner, name): @@ -89,7 +89,7 @@ class StatesGroupMeta(type): @property def __full_group_name__(cls): if cls._parent: - return cls._parent.__full_group_name__ + '.' + cls._group_name + return cls._parent.__full_group_name__ + "." + cls._group_name return cls._group_name @property @@ -195,4 +195,4 @@ class StatesGroup(metaclass=StatesGroupMeta): default_state = State() -any_state = State(state='*') +any_state = State(state="*") diff --git a/aiogram/dispatcher/handler.py b/aiogram/dispatcher/handler.py index 17b715d1..58f7fa80 100644 --- a/aiogram/dispatcher/handler.py +++ b/aiogram/dispatcher/handler.py @@ -3,8 +3,8 @@ from contextvars import ContextVar from dataclasses import dataclass from typing import Optional, Iterable -ctx_data = ContextVar('ctx_handler_data') -current_handler = ContextVar('current_handler') +ctx_data = ContextVar("ctx_handler_data") +current_handler = ContextVar("current_handler") @dataclass @@ -23,7 +23,7 @@ class CancelHandler(Exception): def _get_spec(func: callable): - while hasattr(func, '__wrapped__'): # Try to resolve decorated callbacks + while hasattr(func, "__wrapped__"): # Try to resolve decorated callbacks func = func.__wrapped__ spec = inspect.getfullargspec(func) @@ -47,6 +47,7 @@ class Handler: def register(self, handler, filters=None, index=None): from .filters import get_filters_spec + """ Register callback @@ -80,7 +81,7 @@ class Handler: if handler is registered: self.handlers.remove(handler_obj) return True - raise ValueError('This handler is not registered!') + raise ValueError("This handler is not registered!") async def notify(self, *args): """ @@ -98,7 +99,9 @@ class Handler: if self.middleware_key: try: - await self.dispatcher.middleware.trigger(f"pre_process_{self.middleware_key}", args + (data,)) + await self.dispatcher.middleware.trigger( + f"pre_process_{self.middleware_key}", args + (data,) + ) except CancelHandler: # Allow to cancel current event return results @@ -112,7 +115,9 @@ class Handler: ctx_token = current_handler.set(handler_obj.handler) try: if self.middleware_key: - await self.dispatcher.middleware.trigger(f"process_{self.middleware_key}", args + (data,)) + await self.dispatcher.middleware.trigger( + f"process_{self.middleware_key}", args + (data,) + ) partial_data = _check_spec(handler_obj.spec, data) response = await handler_obj.handler(*args, **partial_data) if response is not None: @@ -127,8 +132,9 @@ class Handler: current_handler.reset(ctx_token) finally: if self.middleware_key: - await self.dispatcher.middleware.trigger(f"post_process_{self.middleware_key}", - args + (results, data,)) + await self.dispatcher.middleware.trigger( + f"post_process_{self.middleware_key}", args + (results, data) + ) return results diff --git a/aiogram/dispatcher/middlewares.py b/aiogram/dispatcher/middlewares.py index dba3db4c..7d195607 100644 --- a/aiogram/dispatcher/middlewares.py +++ b/aiogram/dispatcher/middlewares.py @@ -1,7 +1,7 @@ import logging import typing -log = logging.getLogger('aiogram.Middleware') +log = logging.getLogger("aiogram.Middleware") class MiddlewareManager: @@ -29,9 +29,11 @@ class MiddlewareManager: :return: """ if not isinstance(middleware, BaseMiddleware): - raise TypeError(f"`middleware` must be an instance of BaseMiddleware, not {type(middleware)}") + raise TypeError( + f"`middleware` must be an instance of BaseMiddleware, not {type(middleware)}" + ) if middleware.is_configured(): - raise ValueError('That middleware is already used!') + raise ValueError("That middleware is already used!") self.applications.append(middleware) middleware.setup(self) @@ -67,7 +69,7 @@ class BaseMiddleware: Instance of MiddlewareManager """ if self._manager is None: - raise RuntimeError('Middleware is not configured!') + raise RuntimeError("Middleware is not configured!") return self._manager def setup(self, manager): @@ -119,9 +121,9 @@ class LifetimeControllerMiddleware(BaseMiddleware): return False obj, *args, data = args - if action.startswith('pre_process_'): + if action.startswith("pre_process_"): await self.pre_process(obj, data, *args) - elif action.startswith('post_process_'): + elif action.startswith("post_process_"): await self.post_process(obj, data, *args) else: return False diff --git a/aiogram/dispatcher/storage.py b/aiogram/dispatcher/storage.py index a2992322..c0c108f2 100644 --- a/aiogram/dispatcher/storage.py +++ b/aiogram/dispatcher/storage.py @@ -5,13 +5,13 @@ from ..utils.deprecated import warn_deprecated as warn from ..utils.exceptions import FSMStorageWarning # Leak bucket -KEY = 'key' -LAST_CALL = 'called_at' -RATE_LIMIT = 'rate_limit' -RESULT = 'result' -EXCEEDED_COUNT = 'exceeded' -DELTA = 'delta' -THROTTLE_MANAGER = '$throttle_manager' +KEY = "key" +LAST_CALL = "called_at" +RATE_LIMIT = "rate_limit" +RESULT = "result" +EXCEEDED_COUNT = "exceeded" +DELTA = "delta" +THROTTLE_MANAGER = "$throttle_manager" class BaseStorage: @@ -38,9 +38,12 @@ class BaseStorage: raise NotImplementedError @classmethod - def check_address(cls, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None) -> (typing.Union[str, int], typing.Union[str, int]): + def check_address( + cls, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + ) -> (typing.Union[str, int], typing.Union[str, int]): """ In all storage's methods chat or user is always required. If one of them is not provided, you have to set missing value based on the provided one. @@ -52,7 +55,7 @@ class BaseStorage: :return: """ if chat is None and user is None: - raise ValueError('`user` or `chat` parameter is required but no one is provided!') + raise ValueError("`user` or `chat` parameter is required but no one is provided!") if user is None and chat is not None: user = chat @@ -60,10 +63,13 @@ class BaseStorage: chat = user return chat, user - async def get_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Optional[str]: + async def get_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Optional[str]: """ Get current state of user in chat. Return `default` if no record is found. @@ -77,10 +83,13 @@ class BaseStorage: """ raise NotImplementedError - async def get_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[typing.Dict] = None) -> typing.Dict: + async def get_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[typing.Dict] = None, + ) -> typing.Dict: """ Get state-data for user in chat. Return `default` if no data is provided in storage. @@ -94,10 +103,13 @@ class BaseStorage: """ raise NotImplementedError - async def set_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - state: typing.Optional[typing.AnyStr] = None): + async def set_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state: typing.Optional[typing.AnyStr] = None, + ): """ Set new state for user in chat @@ -110,10 +122,13 @@ class BaseStorage: """ raise NotImplementedError - async def set_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - data: typing.Dict = None): + async def set_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + ): """ Set data for user in chat @@ -126,11 +141,14 @@ class BaseStorage: """ raise NotImplementedError - async def update_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - data: typing.Dict = None, - **kwargs): + async def update_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + **kwargs, + ): """ Update data for user in chat @@ -147,9 +165,12 @@ class BaseStorage: """ raise NotImplementedError - async def reset_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None): + async def reset_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + ): """ Reset data for user in chat. @@ -162,10 +183,13 @@ class BaseStorage: """ await self.set_data(chat=chat, user=user, data={}) - async def reset_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - with_data: typing.Optional[bool] = True): + async def reset_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + with_data: typing.Optional[bool] = True, + ): """ Reset state for user in chat. You may desire to use this method when finishing conversations. @@ -183,9 +207,12 @@ class BaseStorage: if with_data: await self.set_data(chat=chat, user=user, data={}) - async def finish(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None): + async def finish( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + ): """ Finish conversation for user in chat. @@ -201,10 +228,13 @@ class BaseStorage: def has_bucket(self): return False - async def get_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[dict] = None) -> typing.Dict: + async def get_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[dict] = None, + ) -> typing.Dict: """ Get bucket for user in chat. Return `default` if no data is provided in storage. @@ -218,10 +248,13 @@ class BaseStorage: """ raise NotImplementedError - async def set_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None): + async def set_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + ): """ Set bucket for user in chat @@ -234,11 +267,14 @@ class BaseStorage: """ raise NotImplementedError - async def update_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - bucket: typing.Dict = None, - **kwargs): + async def update_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + bucket: typing.Dict = None, + **kwargs, + ): """ Update bucket for user in chat @@ -255,9 +291,12 @@ class BaseStorage: """ raise NotImplementedError - async def reset_bucket(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None): + async def reset_bucket( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + ): """ Reset bucket dor user in chat. @@ -292,7 +331,9 @@ class FSMContext: return str(value) async def get_state(self, default: typing.Optional[str] = None) -> typing.Optional[str]: - return await self.storage.get_state(chat=self.chat, user=self.user, default=self._resolve_state(default)) + return await self.storage.get_state( + chat=self.chat, user=self.user, default=self._resolve_state(default) + ) async def get_data(self, default: typing.Optional[str] = None) -> typing.Dict: return await self.storage.get_data(chat=self.chat, user=self.user, default=default) @@ -301,7 +342,9 @@ class FSMContext: await self.storage.update_data(chat=self.chat, user=self.user, data=data, **kwargs) async def set_state(self, state: typing.Union[typing.AnyStr, None] = None): - await self.storage.set_state(chat=self.chat, user=self.user, state=self._resolve_state(state)) + await self.storage.set_state( + chat=self.chat, user=self.user, state=self._resolve_state(state) + ) async def set_data(self, data: typing.Dict = None): await self.storage.set_data(chat=self.chat, user=self.user, data=data) @@ -338,7 +381,7 @@ class FSMContextProxy: def _check_closed(self): if self._closed: - raise LookupError('Proxy is closed!') + raise LookupError("Proxy is closed!") @classmethod async def create(cls, fsm_context: FSMContext): @@ -447,7 +490,7 @@ class FSMContextProxy: readable_state = f"'{self.state}'" if self.state else "" result = f"{self.__class__.__name__} state = {readable_state}, data = {self._data}" if self._closed: - result += ', closed = True' + result += ", closed = True" return result @@ -462,39 +505,58 @@ class DisabledStorage(BaseStorage): async def wait_closed(self): pass - async def get_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Optional[str]: + async def get_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Optional[str]: return None - async def get_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - default: typing.Optional[str] = None) -> typing.Dict: + async def get_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + default: typing.Optional[str] = None, + ) -> typing.Dict: self._warn() return {} - async def update_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - data: typing.Dict = None, **kwargs): + async def update_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + **kwargs, + ): self._warn() - async def set_state(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - state: typing.Optional[typing.AnyStr] = None): + async def set_state( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + state: typing.Optional[typing.AnyStr] = None, + ): self._warn() - async def set_data(self, *, - chat: typing.Union[str, int, None] = None, - user: typing.Union[str, int, None] = None, - data: typing.Dict = None): + async def set_data( + self, + *, + chat: typing.Union[str, int, None] = None, + user: typing.Union[str, int, None] = None, + data: typing.Dict = None, + ): self._warn() @staticmethod def _warn(): - warn(f"You haven’t set any storage yet so no states and no data will be saved. \n" - f"You can connect MemoryStorage for debug purposes or non-essential data.", - FSMStorageWarning, 5) + warn( + f"You haven’t set any storage yet so no states and no data will be saved. \n" + f"You can connect MemoryStorage for debug purposes or non-essential data.", + FSMStorageWarning, + 5, + ) diff --git a/aiogram/dispatcher/webhook.py b/aiogram/dispatcher/webhook.py index 4c06c2af..b298c660 100644 --- a/aiogram/dispatcher/webhook.py +++ b/aiogram/dispatcher/webhook.py @@ -20,18 +20,18 @@ from ..utils.deprecated import warn_deprecated as warn from ..utils.exceptions import TimeoutWarning from ..utils.payload import prepare_arg -DEFAULT_WEB_PATH = '/webhook' -DEFAULT_ROUTE_NAME = 'webhook_handler' -BOT_DISPATCHER_KEY = 'BOT_DISPATCHER' +DEFAULT_WEB_PATH = "/webhook" +DEFAULT_ROUTE_NAME = "webhook_handler" +BOT_DISPATCHER_KEY = "BOT_DISPATCHER" RESPONSE_TIMEOUT = 55 -WEBHOOK = 'webhook' -WEBHOOK_CONNECTION = 'WEBHOOK_CONNECTION' -WEBHOOK_REQUEST = 'WEBHOOK_REQUEST' +WEBHOOK = "webhook" +WEBHOOK_CONNECTION = "WEBHOOK_CONNECTION" +WEBHOOK_REQUEST = "WEBHOOK_REQUEST" -TELEGRAM_SUBNET_1 = ipaddress.IPv4Network('149.154.160.0/20') -TELEGRAM_SUBNET_2 = ipaddress.IPv4Network('91.108.4.0/22') +TELEGRAM_SUBNET_1 = ipaddress.IPv4Network("149.154.160.0/20") +TELEGRAM_SUBNET_2 = ipaddress.IPv4Network("91.108.4.0/22") allowed_ips = set() @@ -99,6 +99,7 @@ class WebhookRequestHandler(web.View): dp = self.request.app[BOT_DISPATCHER_KEY] try: from aiogram import Bot, Dispatcher + Dispatcher.set_current(dp) Bot.set_current(dp.bot) except RuntimeError: @@ -140,20 +141,20 @@ class WebhookRequestHandler(web.View): if response: web_response = response.get_web_response() else: - web_response = web.Response(text='ok') + web_response = web.Response(text="ok") - if self.request.app.get('RETRY_AFTER', None): - web_response.headers['Retry-After'] = self.request.app['RETRY_AFTER'] + if self.request.app.get("RETRY_AFTER", None): + web_response.headers["Retry-After"] = self.request.app["RETRY_AFTER"] return web_response async def get(self): self.validate_ip() - return web.Response(text='') + return web.Response(text="") async def head(self): self.validate_ip() - return web.Response(text='') + return web.Response(text="") async def process_update(self, update): """ @@ -200,10 +201,12 @@ class WebhookRequestHandler(web.View): :param task: :return: """ - warn(f"Detected slow response into webhook. " - f"(Greater than {RESPONSE_TIMEOUT} seconds)\n" - f"Recommended to use 'async_task' decorator from Dispatcher for handler with long timeouts.", - TimeoutWarning) + warn( + f"Detected slow response into webhook. " + f"(Greater than {RESPONSE_TIMEOUT} seconds)\n" + f"Recommended to use 'async_task' decorator from Dispatcher for handler with long timeouts.", + TimeoutWarning, + ) dispatcher = self.get_dispatcher() loop = dispatcher.loop @@ -212,7 +215,8 @@ class WebhookRequestHandler(web.View): results = task.result() except Exception as e: loop.create_task( - dispatcher.errors_handlers.notify(dispatcher, types.Update.get_current(), e)) + dispatcher.errors_handlers.notify(dispatcher, types.Update.get_current(), e) + ) else: response = self.get_response(results) if response is not None: @@ -238,12 +242,12 @@ class WebhookRequestHandler(web.View): :return: """ # For reverse proxy (nginx) - forwarded_for = self.request.headers.get('X-Forwarded-For', None) + forwarded_for = self.request.headers.get("X-Forwarded-For", None) if forwarded_for: return forwarded_for, _check_ip(forwarded_for) # For default method - peer_name = self.request.transport.get_extra_info('peername') + peer_name = self.request.transport.get_extra_info("peername") if peer_name is not None: host, _ = peer_name return host, _check_ip(host) @@ -255,7 +259,7 @@ class WebhookRequestHandler(web.View): """ Check ip if that is needed. Raise web.HTTPUnauthorized for not allowed hosts. """ - if self.request.app.get('_check_ip', False): + if self.request.app.get("_check_ip", False): ip_address, accept = self.check_ip() if not accept: raise web.HTTPUnauthorized() @@ -275,7 +279,9 @@ class GoneRequestHandler(web.View): raise HTTPGone() -def configure_app(dispatcher, app: web.Application, path=DEFAULT_WEB_PATH, route_name=DEFAULT_ROUTE_NAME): +def configure_app( + dispatcher, app: web.Application, path=DEFAULT_WEB_PATH, route_name=DEFAULT_ROUTE_NAME +): """ You can prepare web.Application for working with webhook handler. @@ -285,7 +291,7 @@ def configure_app(dispatcher, app: web.Application, path=DEFAULT_WEB_PATH, route :param route_name: Name of webhook handler route :return: """ - app.router.add_route('*', path, WebhookRequestHandler, name=route_name) + app.router.add_route("*", path, WebhookRequestHandler, name=route_name) app[BOT_DISPATCHER_KEY] = dispatcher @@ -338,7 +344,7 @@ class BaseResponse: :return: """ - return {'method': self.method, **self.cleanup()} + return {"method": self.method, **self.cleanup()} def get_web_response(self): """ @@ -364,6 +370,7 @@ class BaseResponse: async def __call__(self, bot=None): if bot is None: from aiogram import Bot + bot = Bot.get_current() return await self.execute_response(bot) @@ -386,10 +393,17 @@ class ReplyToMixin: :param message: :obj:`int` or :obj:`types.Message` :return: self """ - setattr(self, 'reply_to_message_id', message.message_id if isinstance(message, types.Message) else message) + setattr( + self, + "reply_to_message_id", + message.message_id if isinstance(message, types.Message) else message, + ) return self - def to(self, target: typing.Union[types.Message, types.Chat, types.base.Integer, types.base.String]): + def to( + self, + target: typing.Union[types.Message, types.Chat, types.base.Integer, types.base.String], + ): """ Send to chat @@ -405,7 +419,7 @@ class ReplyToMixin: else: raise TypeError(f"Bad type of target. ({type(target)})") - setattr(self, 'chat_id', chat_id) + setattr(self, "chat_id", chat_id) return self @@ -416,7 +430,7 @@ class DisableNotificationMixin: :return: """ - setattr(self, 'disable_notification', True) + setattr(self, "disable_notification", True) return self @@ -427,7 +441,7 @@ class DisableWebPagePreviewMixin: :return: """ - setattr(self, 'disable_web_page_preview', True) + setattr(self, "disable_web_page_preview", True) return self @@ -438,7 +452,7 @@ class ParseModeMixin: :return: """ - setattr(self, 'parse_mode', ParseMode.HTML) + setattr(self, "parse_mode", ParseMode.HTML) return self def as_markdown(self): @@ -447,7 +461,7 @@ class ParseModeMixin: :return: """ - setattr(self, 'parse_mode', ParseMode.MARKDOWN) + setattr(self, "parse_mode", ParseMode.MARKDOWN) return self @staticmethod @@ -458,31 +472,48 @@ class ParseModeMixin: :return: """ from aiogram import Bot + bot = Bot.get_current() if bot is not None: return bot.parse_mode -class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificationMixin, DisableWebPagePreviewMixin): +class SendMessage( + BaseResponse, + ReplyToMixin, + ParseModeMixin, + DisableNotificationMixin, + DisableWebPagePreviewMixin, +): """ You can send message with webhook by using this instance of this object. All arguments is equal with Bot.send_message method. """ - __slots__ = ('chat_id', 'text', 'parse_mode', - 'disable_web_page_preview', 'disable_notification', - 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "text", + "parse_mode", + "disable_web_page_preview", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_MESSAGE - def __init__(self, chat_id: Union[Integer, String] = None, - text: String = None, - parse_mode: Optional[String] = None, - disable_web_page_preview: Optional[Boolean] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String] = None, + text: String = None, + parse_mode: Optional[String] = None, + disable_web_page_preview: Optional[Boolean] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -498,7 +529,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user. """ if text is None: - text = '' + text = "" if parse_mode is None: parse_mode = self._global_parse_mode() @@ -512,16 +543,16 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio def prepare(self) -> dict: return { - 'chat_id': self.chat_id, - 'text': self.text, - 'parse_mode': self.parse_mode, - 'disable_web_page_preview': self.disable_web_page_preview, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "text": self.text, + "parse_mode": self.parse_mode, + "disable_web_page_preview": self.disable_web_page_preview, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } - def write(self, *text, sep=' '): + def write(self, *text, sep=" "): """ Write text to response @@ -532,7 +563,7 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio self.text += markdown.text(*text, sep) return self - def write_ln(self, *text, sep=' '): + def write_ln(self, *text, sep=" "): """ Write line @@ -540,9 +571,9 @@ class SendMessage(BaseResponse, ReplyToMixin, ParseModeMixin, DisableNotificatio :param sep: :return: """ - if self.text and self.text[-1] != '\n': - self.text += '\n' - self.text += markdown.text(*text, sep) + '\n' + if self.text and self.text[-1] != "\n": + self.text += "\n" + self.text += markdown.text(*text, sep) + "\n" return self @@ -550,14 +581,18 @@ class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin): """ Use that response type for forward messages of any kind on to webhook. """ - __slots__ = ('chat_id', 'from_chat_id', 'message_id', 'disable_notification') + + __slots__ = ("chat_id", "from_chat_id", "message_id", "disable_notification") method = api.Methods.FORWARD_MESSAGE - def __init__(self, chat_id: Union[Integer, String] = None, - from_chat_id: Union[Integer, String] = None, - message_id: Integer = None, - disable_notification: Optional[Boolean] = None): + def __init__( + self, + chat_id: Union[Integer, String] = None, + from_chat_id: Union[Integer, String] = None, + message_id: Integer = None, + disable_notification: Optional[Boolean] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -579,16 +614,16 @@ class ForwardMessage(BaseResponse, ReplyToMixin, DisableNotificationMixin): :param message: :return: """ - setattr(self, 'from_chat_id', message.chat.id) - setattr(self, 'message_id', message.message_id) + setattr(self, "from_chat_id", message.chat.id) + setattr(self, "message_id", message.message_id) return self def prepare(self) -> dict: return { - 'chat_id': self.chat_id, - 'from_chat_id': self.from_chat_id, - 'message_id': self.message_id, - 'disable_notification': self.disable_notification + "chat_id": self.chat_id, + "from_chat_id": self.from_chat_id, + "message_id": self.message_id, + "disable_notification": self.disable_notification, } @@ -597,17 +632,28 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send photo on to webhook. """ - __slots__ = ('chat_id', 'photo', 'caption', 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "photo", + "caption", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_PHOTO - def __init__(self, chat_id: Union[Integer, String], - photo: String, - caption: Optional[String] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + photo: String, + caption: Optional[String] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -632,12 +678,12 @@ class SendPhoto(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'photo': self.photo, - 'caption': self.caption, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "photo": self.photo, + "caption": self.caption, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -646,21 +692,34 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send audio on to webhook. """ - __slots__ = ('chat_id', 'audio', 'caption', 'duration', 'performer', 'title', - 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "audio", + "caption", + "duration", + "performer", + "title", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_AUDIO - def __init__(self, chat_id: Union[Integer, String], - audio: String, - caption: Optional[String] = None, - duration: Optional[Integer] = None, - performer: Optional[String] = None, - title: Optional[String] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + audio: String, + caption: Optional[String] = None, + duration: Optional[Integer] = None, + performer: Optional[String] = None, + title: Optional[String] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -691,15 +750,15 @@ class SendAudio(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'audio': self.audio, - 'caption': self.caption, - 'duration': self.duration, - 'performer': self.performer, - 'title': self.title, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "audio": self.audio, + "caption": self.caption, + "duration": self.duration, + "performer": self.performer, + "title": self.title, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -708,17 +767,28 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send document on to webhook. """ - __slots__ = ('chat_id', 'document', 'caption', 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "document", + "caption", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_DOCUMENT - def __init__(self, chat_id: Union[Integer, String], - document: String, - caption: Optional[String] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + document: String, + caption: Optional[String] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -744,12 +814,12 @@ class SendDocument(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'document': self.document, - 'caption': self.caption, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup), + "chat_id": self.chat_id, + "document": self.document, + "caption": self.caption, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -758,21 +828,34 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send video on to webhook. """ - __slots__ = ('chat_id', 'video', 'duration', 'width', 'height', 'caption', 'disable_notification', - 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "video", + "duration", + "width", + "height", + "caption", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_VIDEO - def __init__(self, chat_id: Union[Integer, String], - video: String, - duration: Optional[Integer] = None, - width: Optional[Integer] = None, - height: Optional[Integer] = None, - caption: Optional[String] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + video: String, + duration: Optional[Integer] = None, + width: Optional[Integer] = None, + height: Optional[Integer] = None, + caption: Optional[String] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -804,15 +887,15 @@ class SendVideo(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'video': self.video, - 'duration': self.duration, - 'width': self.width, - 'height': self.height, - 'caption': self.caption, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "video": self.video, + "duration": self.duration, + "width": self.width, + "height": self.height, + "caption": self.caption, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -821,19 +904,30 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send voice on to webhook. """ - __slots__ = ('chat_id', 'voice', 'caption', 'duration', 'disable_notification', - 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "voice", + "caption", + "duration", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_VOICE - def __init__(self, chat_id: Union[Integer, String], - voice: String, - caption: Optional[String] = None, - duration: Optional[Integer] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + voice: String, + caption: Optional[String] = None, + duration: Optional[Integer] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -860,13 +954,13 @@ class SendVoice(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'voice': self.voice, - 'caption': self.caption, - 'duration': self.duration, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "voice": self.voice, + "caption": self.caption, + "duration": self.duration, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -875,19 +969,30 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send video note on to webhook. """ - __slots__ = ('chat_id', 'video_note', 'duration', 'length', 'disable_notification', - 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "video_note", + "duration", + "length", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_VIDEO_NOTE - def __init__(self, chat_id: Union[Integer, String], - video_note: String, - duration: Optional[Integer] = None, - length: Optional[Integer] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + video_note: String, + duration: Optional[Integer] = None, + length: Optional[Integer] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -913,13 +1018,13 @@ class SendVideoNote(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'video_note': self.video_note, - 'duration': self.duration, - 'length': self.length, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "video_note": self.video_note, + "duration": self.duration, + "length": self.length, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -928,14 +1033,17 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use this method to send a group of photos or videos as an album. """ - __slots__ = ('chat_id', 'media', 'disable_notification', 'reply_to_message_id') + __slots__ = ("chat_id", "media", "disable_notification", "reply_to_message_id") method = api.Methods.SEND_MEDIA_GROUP - def __init__(self, chat_id: Union[Integer, String], - media: Union[types.MediaGroup, List] = None, - disable_notification: typing.Union[Boolean, None] = None, - reply_to_message_id: typing.Union[Integer, None] = None): + def __init__( + self, + chat_id: Union[Integer, String], + media: Union[types.MediaGroup, List] = None, + disable_notification: typing.Union[Boolean, None] = None, + reply_to_message_id: typing.Union[Integer, None] = None, + ): """ Use this method to send a group of photos or videos as an album. @@ -966,15 +1074,15 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): files = dict(self.media.get_files()) if files: - raise TypeError('Allowed only file ID or URL\'s') + raise TypeError("Allowed only file ID or URL's") media = prepare_arg(self.media) return { - 'chat_id': self.chat_id, - 'media': media, - 'disable_notifications': self.disable_notifications, - 'reply_to_message_id': self.reply_to_message_id + "chat_id": self.chat_id, + "media": media, + "disable_notifications": self.disable_notifications, + "reply_to_message_id": self.reply_to_message_id, } def attach_photo(self, photo: String, caption: String = None): @@ -988,8 +1096,14 @@ class SendMediaGroup(BaseResponse, ReplyToMixin, DisableNotificationMixin): self.media.attach_photo(photo, caption) return self - def attach_video(self, video: String, caption: String = None, width: Integer = None, - height: Integer = None, duration: Integer = None): + def attach_video( + self, + video: String, + caption: String = None, + width: Integer = None, + height: Integer = None, + duration: Integer = None, + ): """ Attach video @@ -1009,16 +1123,28 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send location on to webhook. """ - __slots__ = ('chat_id', 'latitude', 'longitude', 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "latitude", + "longitude", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_LOCATION - def __init__(self, chat_id: Union[Integer, String], - latitude: Float, longitude: Float, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + latitude: Float, + longitude: Float, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1040,12 +1166,12 @@ class SendLocation(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'latitude': self.latitude, - 'longitude': self.longitude, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "latitude": self.latitude, + "longitude": self.longitude, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1054,21 +1180,34 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send venue on to webhook. """ - __slots__ = ('chat_id', 'latitude', 'longitude', 'title', 'address', 'foursquare_id', - 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "latitude", + "longitude", + "title", + "address", + "foursquare_id", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_VENUE - def __init__(self, chat_id: Union[Integer, String], - latitude: Float, - longitude: Float, - title: String, - address: String, - foursquare_id: Optional[String] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + latitude: Float, + longitude: Float, + title: String, + address: String, + foursquare_id: Optional[String] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1096,15 +1235,15 @@ class SendVenue(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'latitude': self.latitude, - 'longitude': self.longitude, - 'title': self.title, - 'address': self.address, - 'foursquare_id': self.foursquare_id, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "latitude": self.latitude, + "longitude": self.longitude, + "title": self.title, + "address": self.address, + "foursquare_id": self.foursquare_id, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1113,19 +1252,30 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send contact on to webhook. """ - __slots__ = ('chat_id', 'phone_number', 'first_name', 'last_name', 'disable_notification', - 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "phone_number", + "first_name", + "last_name", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_CONTACT - def __init__(self, chat_id: Union[Integer, String], - phone_number: String, - first_name: String, - last_name: Optional[String] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[Union[ - types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + phone_number: String, + first_name: String, + last_name: Optional[String] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1149,13 +1299,13 @@ class SendContact(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'phone_number': self.phone_number, - 'first_name': self.first_name, - 'last_name': self.last_name, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "phone_number": self.phone_number, + "first_name": self.first_name, + "last_name": self.last_name, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1164,7 +1314,7 @@ class SendChatAction(BaseResponse): Use that response type for send chat action on to webhook. """ - __slots__ = ('chat_id', 'action') + __slots__ = ("chat_id", "action") method = api.Methods.SEND_CHAT_ACTION @@ -1181,10 +1331,7 @@ class SendChatAction(BaseResponse): self.action = action def prepare(self): - return { - 'chat_id': self.chat_id, - 'action': self.action - } + return {"chat_id": self.chat_id, "action": self.action} class KickChatMember(BaseResponse): @@ -1192,14 +1339,16 @@ class KickChatMember(BaseResponse): Use that response type for kick chat member on to webhook. """ - __slots__ = ('chat_id', 'user_id', 'until_date') + __slots__ = ("chat_id", "user_id", "until_date") method = api.Methods.KICK_CHAT_MEMBER - def __init__(self, chat_id: Union[Integer, String], - user_id: Integer, - until_date: Optional[ - Union[Integer, datetime.datetime, datetime.timedelta]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + user_id: Integer, + until_date: Optional[Union[Integer, datetime.datetime, datetime.timedelta]] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target group or username of the target supergroup or channel (in the format @channelusername) @@ -1213,9 +1362,9 @@ class KickChatMember(BaseResponse): def prepare(self): return { - 'chat_id': self.chat_id, - 'user_id': self.user_id, - 'until_date': prepare_arg(self.until_date) + "chat_id": self.chat_id, + "user_id": self.user_id, + "until_date": prepare_arg(self.until_date), } @@ -1224,7 +1373,7 @@ class UnbanChatMember(BaseResponse): Use that response type for unban chat member on to webhook. """ - __slots__ = ('chat_id', 'user_id') + __slots__ = ("chat_id", "user_id") method = api.Methods.UNBAN_CHAT_MEMBER @@ -1238,10 +1387,7 @@ class UnbanChatMember(BaseResponse): self.user_id = user_id def prepare(self): - return { - 'chat_id': self.chat_id, - 'user_id': self.user_id - } + return {"chat_id": self.chat_id, "user_id": self.user_id} class RestrictChatMember(BaseResponse): @@ -1249,18 +1395,28 @@ class RestrictChatMember(BaseResponse): Use that response type for restrict chat member on to webhook. """ - __slots__ = ('chat_id', 'user_id', 'until_date', 'can_send_messages', 'can_send_media_messages', - 'can_send_other_messages', 'can_add_web_page_previews') + __slots__ = ( + "chat_id", + "user_id", + "until_date", + "can_send_messages", + "can_send_media_messages", + "can_send_other_messages", + "can_add_web_page_previews", + ) method = api.Methods.RESTRICT_CHAT_MEMBER - def __init__(self, chat_id: Union[Integer, String], - user_id: Integer, - until_date: Optional[Union[Integer, datetime.datetime, datetime.timedelta]] = None, - can_send_messages: Optional[Boolean] = None, - can_send_media_messages: Optional[Boolean] = None, - can_send_other_messages: Optional[Boolean] = None, - can_add_web_page_previews: Optional[Boolean] = None): + def __init__( + self, + chat_id: Union[Integer, String], + user_id: Integer, + until_date: Optional[Union[Integer, datetime.datetime, datetime.timedelta]] = None, + can_send_messages: Optional[Boolean] = None, + can_send_media_messages: Optional[Boolean] = None, + can_send_other_messages: Optional[Boolean] = None, + can_add_web_page_previews: Optional[Boolean] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1287,13 +1443,13 @@ class RestrictChatMember(BaseResponse): def prepare(self): return { - 'chat_id': self.chat_id, - 'user_id': self.user_id, - 'until_date': prepare_arg(self.until_date), - 'can_send_messages': self.can_send_messages, - 'can_send_media_messages': self.can_send_media_messages, - 'can_send_other_messages': self.can_send_other_messages, - 'can_add_web_page_previews': self.can_add_web_page_previews + "chat_id": self.chat_id, + "user_id": self.user_id, + "until_date": prepare_arg(self.until_date), + "can_send_messages": self.can_send_messages, + "can_send_media_messages": self.can_send_media_messages, + "can_send_other_messages": self.can_send_other_messages, + "can_add_web_page_previews": self.can_add_web_page_previews, } @@ -1302,22 +1458,34 @@ class PromoteChatMember(BaseResponse): Use that response type for promote chat member on to webhook. """ - __slots__ = ('chat_id', 'user_id', 'can_change_info', 'can_post_messages', 'can_edit_messages', - 'can_delete_messages', 'can_invite_users', 'can_restrict_members', 'can_pin_messages', - 'can_promote_members') + __slots__ = ( + "chat_id", + "user_id", + "can_change_info", + "can_post_messages", + "can_edit_messages", + "can_delete_messages", + "can_invite_users", + "can_restrict_members", + "can_pin_messages", + "can_promote_members", + ) method = api.Methods.PROMOTE_CHAT_MEMBER - def __init__(self, chat_id: Union[Integer, String], - user_id: Integer, - can_change_info: Optional[Boolean] = None, - can_post_messages: Optional[Boolean] = None, - can_edit_messages: Optional[Boolean] = None, - can_delete_messages: Optional[Boolean] = None, - can_invite_users: Optional[Boolean] = None, - can_restrict_members: Optional[Boolean] = None, - can_pin_messages: Optional[Boolean] = None, - can_promote_members: Optional[Boolean] = None): + def __init__( + self, + chat_id: Union[Integer, String], + user_id: Integer, + can_change_info: Optional[Boolean] = None, + can_post_messages: Optional[Boolean] = None, + can_edit_messages: Optional[Boolean] = None, + can_delete_messages: Optional[Boolean] = None, + can_invite_users: Optional[Boolean] = None, + can_restrict_members: Optional[Boolean] = None, + can_pin_messages: Optional[Boolean] = None, + can_promote_members: Optional[Boolean] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1348,16 +1516,16 @@ class PromoteChatMember(BaseResponse): def prepare(self): return { - 'chat_id': self.chat_id, - 'user_id': self.user_id, - 'can_change_info': self.can_change_info, - 'can_post_messages': self.can_post_messages, - 'can_edit_messages': self.can_edit_messages, - 'can_delete_messages': self.can_delete_messages, - 'can_invite_users': self.can_invite_users, - 'can_restrict_members': self.can_restrict_members, - 'can_pin_messages': self.can_pin_messages, - 'can_promote_members': self.can_promote_members + "chat_id": self.chat_id, + "user_id": self.user_id, + "can_change_info": self.can_change_info, + "can_post_messages": self.can_post_messages, + "can_edit_messages": self.can_edit_messages, + "can_delete_messages": self.can_delete_messages, + "can_invite_users": self.can_invite_users, + "can_restrict_members": self.can_restrict_members, + "can_pin_messages": self.can_pin_messages, + "can_promote_members": self.can_promote_members, } @@ -1366,7 +1534,7 @@ class DeleteChatPhoto(BaseResponse): Use that response type for delete chat photo on to webhook. """ - __slots__ = ('chat_id',) + __slots__ = ("chat_id",) method = api.Methods.DELETE_CHAT_PHOTO @@ -1378,9 +1546,7 @@ class DeleteChatPhoto(BaseResponse): self.chat_id = chat_id def prepare(self): - return { - 'chat_id': self.chat_id - } + return {"chat_id": self.chat_id} class SetChatTitle(BaseResponse): @@ -1388,7 +1554,7 @@ class SetChatTitle(BaseResponse): Use that response type for set chat title on to webhook. """ - __slots__ = ('chat_id', 'title') + __slots__ = ("chat_id", "title") method = api.Methods.SET_CHAT_TITLE @@ -1402,10 +1568,7 @@ class SetChatTitle(BaseResponse): self.title = title def prepare(self): - return { - 'chat_id': self.chat_id, - 'title': self.title - } + return {"chat_id": self.chat_id, "title": self.title} class SetChatDescription(BaseResponse): @@ -1413,7 +1576,7 @@ class SetChatDescription(BaseResponse): Use that response type for set chat description on to webhook. """ - __slots__ = ('chat_id', 'description') + __slots__ = ("chat_id", "description") method = api.Methods.SET_CHAT_DESCRIPTION @@ -1427,10 +1590,7 @@ class SetChatDescription(BaseResponse): self.description = description def prepare(self): - return { - 'chat_id': self.chat_id, - 'description': self.description - } + return {"chat_id": self.chat_id, "description": self.description} class PinChatMessage(BaseResponse, DisableNotificationMixin): @@ -1438,12 +1598,16 @@ class PinChatMessage(BaseResponse, DisableNotificationMixin): Use that response type for pin chat message on to webhook. """ - __slots__ = ('chat_id', 'message_id', 'disable_notification') + __slots__ = ("chat_id", "message_id", "disable_notification") method = api.Methods.PIN_CHAT_MESSAGE - def __init__(self, chat_id: Union[Integer, String], message_id: Integer, - disable_notification: Optional[Boolean] = None): + def __init__( + self, + chat_id: Union[Integer, String], + message_id: Integer, + disable_notification: Optional[Boolean] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target supergroup (in the format @supergroupusername) @@ -1457,9 +1621,9 @@ class PinChatMessage(BaseResponse, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'message_id': self.message_id, - 'disable_notification': self.disable_notification, + "chat_id": self.chat_id, + "message_id": self.message_id, + "disable_notification": self.disable_notification, } @@ -1468,7 +1632,7 @@ class UnpinChatMessage(BaseResponse): Use that response type for unpin chat message on to webhook. """ - __slots__ = ('chat_id',) + __slots__ = ("chat_id",) method = api.Methods.UNPIN_CHAT_MESSAGE @@ -1480,9 +1644,7 @@ class UnpinChatMessage(BaseResponse): self.chat_id = chat_id def prepare(self): - return { - 'chat_id': self.chat_id - } + return {"chat_id": self.chat_id} class LeaveChat(BaseResponse): @@ -1490,7 +1652,7 @@ class LeaveChat(BaseResponse): Use that response type for leave chat on to webhook. """ - __slots__ = ('chat_id',) + __slots__ = ("chat_id",) method = api.Methods.LEAVE_CHAT @@ -1502,9 +1664,7 @@ class LeaveChat(BaseResponse): self.chat_id = chat_id def prepare(self): - return { - 'chat_id': self.chat_id - } + return {"chat_id": self.chat_id} class AnswerCallbackQuery(BaseResponse): @@ -1512,15 +1672,18 @@ class AnswerCallbackQuery(BaseResponse): Use that response type for answer callback query on to webhook. """ - __slots__ = ('callback_query_id', 'text', 'show_alert', 'url', 'cache_time') + __slots__ = ("callback_query_id", "text", "show_alert", "url", "cache_time") method = api.Methods.ANSWER_CALLBACK_QUERY - def __init__(self, callback_query_id: String, - text: Optional[String] = None, - show_alert: Optional[Boolean] = None, - url: Optional[String] = None, - cache_time: Optional[Integer] = None): + def __init__( + self, + callback_query_id: String, + text: Optional[String] = None, + show_alert: Optional[Boolean] = None, + url: Optional[String] = None, + cache_time: Optional[Integer] = None, + ): """ :param callback_query_id: String - Unique identifier for the query to be answered :param text: String (Optional) - Text of the notification. If not specified, nothing will be shown to the user, @@ -1544,11 +1707,11 @@ class AnswerCallbackQuery(BaseResponse): def prepare(self): return { - 'callback_query_id': self.callback_query_id, - 'text': self.text, - 'show_alert': self.show_alert, - 'url': self.url, - 'cache_time': self.cache_time + "callback_query_id": self.callback_query_id, + "text": self.text, + "show_alert": self.show_alert, + "url": self.url, + "cache_time": self.cache_time, } @@ -1557,18 +1720,28 @@ class EditMessageText(BaseResponse, ParseModeMixin, DisableWebPagePreviewMixin): Use that response type for edit message text on to webhook. """ - __slots__ = ('chat_id', 'message_id', 'inline_message_id', 'text', 'parse_mode', - 'disable_web_page_preview', 'reply_markup') + __slots__ = ( + "chat_id", + "message_id", + "inline_message_id", + "text", + "parse_mode", + "disable_web_page_preview", + "reply_markup", + ) method = api.Methods.EDIT_MESSAGE_TEXT - def __init__(self, text: String, - chat_id: Optional[Union[Integer, String]] = None, - message_id: Optional[Integer] = None, - inline_message_id: Optional[String] = None, - parse_mode: Optional[String] = None, - disable_web_page_preview: Optional[Boolean] = None, - reply_markup: Optional[types.InlineKeyboardMarkup] = None): + def __init__( + self, + text: String, + chat_id: Optional[Union[Integer, String]] = None, + message_id: Optional[Integer] = None, + inline_message_id: Optional[String] = None, + parse_mode: Optional[String] = None, + disable_web_page_preview: Optional[Boolean] = None, + reply_markup: Optional[types.InlineKeyboardMarkup] = None, + ): """ :param chat_id: Union[Integer, String] (Optional) - Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel @@ -1597,13 +1770,13 @@ class EditMessageText(BaseResponse, ParseModeMixin, DisableWebPagePreviewMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'message_id': self.message_id, - 'inline_message_id': self.inline_message_id, - 'text': self.text, - 'parse_mode': self.parse_mode, - 'disable_web_page_preview': self.disable_web_page_preview, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "message_id": self.message_id, + "inline_message_id": self.inline_message_id, + "text": self.text, + "parse_mode": self.parse_mode, + "disable_web_page_preview": self.disable_web_page_preview, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1612,15 +1785,18 @@ class EditMessageCaption(BaseResponse): Use that response type for edit message caption on to webhook. """ - __slots__ = ('chat_id', 'message_id', 'inline_message_id', 'caption', 'reply_markup') + __slots__ = ("chat_id", "message_id", "inline_message_id", "caption", "reply_markup") method = api.Methods.EDIT_MESSAGE_CAPTION - def __init__(self, chat_id: Optional[Union[Integer, String]] = None, - message_id: Optional[Integer] = None, - inline_message_id: Optional[String] = None, - caption: Optional[String] = None, - reply_markup: Optional[types.InlineKeyboardMarkup] = None): + def __init__( + self, + chat_id: Optional[Union[Integer, String]] = None, + message_id: Optional[Integer] = None, + inline_message_id: Optional[String] = None, + caption: Optional[String] = None, + reply_markup: Optional[types.InlineKeyboardMarkup] = None, + ): """ :param chat_id: Union[Integer, String] (Optional) - Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel @@ -1640,11 +1816,11 @@ class EditMessageCaption(BaseResponse): def prepare(self): return { - 'chat_id': self.chat_id, - 'message_id': self.message_id, - 'inline_message_id': self.inline_message_id, - 'caption': self.caption, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "message_id": self.message_id, + "inline_message_id": self.inline_message_id, + "caption": self.caption, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1653,14 +1829,17 @@ class EditMessageReplyMarkup(BaseResponse): Use that response type for edit message reply markup on to webhook. """ - __slots__ = ('chat_id', 'message_id', 'inline_message_id', 'reply_markup') + __slots__ = ("chat_id", "message_id", "inline_message_id", "reply_markup") method = api.Methods.EDIT_MESSAGE_REPLY_MARKUP - def __init__(self, chat_id: Optional[Union[Integer, String]] = None, - message_id: Optional[Integer] = None, - inline_message_id: Optional[String] = None, - reply_markup: Optional[types.InlineKeyboardMarkup] = None): + def __init__( + self, + chat_id: Optional[Union[Integer, String]] = None, + message_id: Optional[Integer] = None, + inline_message_id: Optional[String] = None, + reply_markup: Optional[types.InlineKeyboardMarkup] = None, + ): """ :param chat_id: Union[Integer, String] (Optional) - Required if inline_message_id is not specified. Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1677,10 +1856,10 @@ class EditMessageReplyMarkup(BaseResponse): def prepare(self): return { - 'chat_id': self.chat_id, - 'message_id': self.message_id, - 'inline_message_id': self.inline_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "message_id": self.message_id, + "inline_message_id": self.inline_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1689,7 +1868,7 @@ class DeleteMessage(BaseResponse): Use that response type for delete message on to webhook. """ - __slots__ = ('chat_id', 'message_id') + __slots__ = ("chat_id", "message_id") method = api.Methods.DELETE_MESSAGE @@ -1703,10 +1882,7 @@ class DeleteMessage(BaseResponse): self.message_id = message_id def prepare(self): - return { - 'chat_id': self.chat_id, - 'message_id': self.message_id - } + return {"chat_id": self.chat_id, "message_id": self.message_id} class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin): @@ -1714,17 +1890,26 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send sticker on to webhook. """ - __slots__ = ('chat_id', 'sticker', 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "sticker", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_STICKER - def __init__(self, chat_id: Union[Integer, String], - sticker: String, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[ - Union[types.InlineKeyboardMarkup, - types.ReplyKeyboardMarkup, Dict, String]] = None): + def __init__( + self, + chat_id: Union[Integer, String], + sticker: String, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[ + Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, Dict, String] + ] = None, + ): """ :param chat_id: Union[Integer, String] - Unique identifier for the target chat or username of the target channel (in the format @channelusername) @@ -1747,11 +1932,11 @@ class SendSticker(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'sticker': self.sticker, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "sticker": self.sticker, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -1760,16 +1945,28 @@ class CreateNewStickerSet(BaseResponse): Use that response type for create new sticker set on to webhook. """ - __slots__ = ('user_id', 'name', 'title', 'png_sticker', 'emojis', 'contains_masks', 'mask_position') + __slots__ = ( + "user_id", + "name", + "title", + "png_sticker", + "emojis", + "contains_masks", + "mask_position", + ) method = api.Methods.CREATE_NEW_STICKER_SET - def __init__(self, user_id: Integer, - name: String, title: String, - png_sticker: String, - emojis: String, - contains_masks: Optional[Boolean] = None, - mask_position: Optional[types.MaskPosition] = None): + def __init__( + self, + user_id: Integer, + name: String, + title: String, + png_sticker: String, + emojis: String, + contains_masks: Optional[Boolean] = None, + mask_position: Optional[types.MaskPosition] = None, + ): """ :param user_id: Integer - User identifier of created sticker set owner :param name: String - Short name of sticker set, to be used in t.me/addstickers/ URLs (e.g., animals). @@ -1796,13 +1993,13 @@ class CreateNewStickerSet(BaseResponse): def prepare(self): return { - 'user_id': self.user_id, - 'name': self.name, - 'title': self.title, - 'png_sticker': self.png_sticker, - 'emojis': self.emojis, - 'contains_masks': self.contains_masks, - 'mask_position': self.mask_position + "user_id": self.user_id, + "name": self.name, + "title": self.title, + "png_sticker": self.png_sticker, + "emojis": self.emojis, + "contains_masks": self.contains_masks, + "mask_position": self.mask_position, } @@ -1811,15 +2008,18 @@ class AddStickerToSet(BaseResponse): Use that response type for add sticker to set on to webhook. """ - __slots__ = ('user_id', 'name', 'png_sticker', 'emojis', 'mask_position') + __slots__ = ("user_id", "name", "png_sticker", "emojis", "mask_position") method = api.Methods.ADD_STICKER_TO_SET - def __init__(self, user_id: Integer, - name: String, - png_sticker: String, - emojis: String, - mask_position: Optional[types.MaskPosition] = None): + def __init__( + self, + user_id: Integer, + name: String, + png_sticker: String, + emojis: String, + mask_position: Optional[types.MaskPosition] = None, + ): """ :param user_id: Integer - User identifier of sticker set owner :param name: String - Sticker set name @@ -1839,11 +2039,11 @@ class AddStickerToSet(BaseResponse): def prepare(self): return { - 'user_id': self.user_id, - 'name': self.name, - 'png_sticker': self.png_sticker, - 'emojis': self.emojis, - 'mask_position': prepare_arg(self.mask_position) + "user_id": self.user_id, + "name": self.name, + "png_sticker": self.png_sticker, + "emojis": self.emojis, + "mask_position": prepare_arg(self.mask_position), } @@ -1852,7 +2052,7 @@ class SetStickerPositionInSet(BaseResponse): Use that response type for set sticker position in set on to webhook. """ - __slots__ = ('sticker', 'position') + __slots__ = ("sticker", "position") method = api.Methods.SET_STICKER_POSITION_IN_SET @@ -1865,10 +2065,7 @@ class SetStickerPositionInSet(BaseResponse): self.position = position def prepare(self): - return { - 'sticker': self.sticker, - 'position': self.position - } + return {"sticker": self.sticker, "position": self.position} class DeleteStickerFromSet(BaseResponse): @@ -1876,7 +2073,7 @@ class DeleteStickerFromSet(BaseResponse): Use that response type for delete sticker from set on to webhook. """ - __slots__ = ('sticker',) + __slots__ = ("sticker",) method = api.Methods.DELETE_STICKER_FROM_SET @@ -1887,9 +2084,7 @@ class DeleteStickerFromSet(BaseResponse): self.sticker = sticker def prepare(self): - return { - 'sticker': self.sticker - } + return {"sticker": self.sticker} class AnswerInlineQuery(BaseResponse): @@ -1897,18 +2092,28 @@ class AnswerInlineQuery(BaseResponse): Use that response type for answer inline query on to webhook. """ - __slots__ = ('inline_query_id', 'results', 'cache_time', 'is_personal', 'next_offset', - 'switch_pm_text', 'switch_pm_parameter') + __slots__ = ( + "inline_query_id", + "results", + "cache_time", + "is_personal", + "next_offset", + "switch_pm_text", + "switch_pm_parameter", + ) method = api.Methods.ANSWER_INLINE_QUERY - def __init__(self, inline_query_id: String, - results: [types.InlineQueryResult], - cache_time: Optional[Integer] = None, - is_personal: Optional[Boolean] = None, - next_offset: Optional[String] = None, - switch_pm_text: Optional[String] = None, - switch_pm_parameter: Optional[String] = None): + def __init__( + self, + inline_query_id: String, + results: [types.InlineQueryResult], + cache_time: Optional[Integer] = None, + is_personal: Optional[Boolean] = None, + next_offset: Optional[String] = None, + switch_pm_text: Optional[String] = None, + switch_pm_parameter: Optional[String] = None, + ): """ :param inline_query_id: String - Unique identifier for the answered query :param results: [types.InlineQueryResult] - A JSON-serialized array of results for the inline query @@ -1945,13 +2150,13 @@ class AnswerInlineQuery(BaseResponse): def prepare(self): return { - 'inline_query_id': self.inline_query_id, - 'results': prepare_arg(self.results), - 'cache_time': self.cache_time, - 'is_personal': self.is_personal, - 'next_offset': self.next_offset, - 'switch_pm_text': self.switch_pm_text, - 'switch_pm_parameter': self.switch_pm_parameter, + "inline_query_id": self.inline_query_id, + "results": prepare_arg(self.results), + "cache_time": self.cache_time, + "is_personal": self.is_personal, + "next_offset": self.next_offset, + "switch_pm_text": self.switch_pm_text, + "switch_pm_parameter": self.switch_pm_parameter, } @@ -1960,33 +2165,54 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send invoice on to webhook. """ - __slots__ = ('chat_id', 'title', 'description', 'payload', 'provider_token', 'start_parameter', - 'currency', 'prices', 'photo_url', 'photo_size', 'photo_width', 'photo_height', - 'need_name', 'need_phone_number', 'need_email', 'need_shipping_address', 'is_flexible', - 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "title", + "description", + "payload", + "provider_token", + "start_parameter", + "currency", + "prices", + "photo_url", + "photo_size", + "photo_width", + "photo_height", + "need_name", + "need_phone_number", + "need_email", + "need_shipping_address", + "is_flexible", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_INVOICE - def __init__(self, chat_id: Integer, - title: String, - description: String, - payload: String, - provider_token: String, - start_parameter: String, - currency: String, - prices: [types.LabeledPrice], - photo_url: Optional[String] = None, - photo_size: Optional[Integer] = None, - photo_width: Optional[Integer] = None, - photo_height: Optional[Integer] = None, - need_name: Optional[Boolean] = None, - need_phone_number: Optional[Boolean] = None, - need_email: Optional[Boolean] = None, - need_shipping_address: Optional[Boolean] = None, - is_flexible: Optional[Boolean] = None, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[types.InlineKeyboardMarkup] = None): + def __init__( + self, + chat_id: Integer, + title: String, + description: String, + payload: String, + provider_token: String, + start_parameter: String, + currency: String, + prices: [types.LabeledPrice], + photo_url: Optional[String] = None, + photo_size: Optional[Integer] = None, + photo_width: Optional[Integer] = None, + photo_height: Optional[Integer] = None, + need_name: Optional[Boolean] = None, + need_phone_number: Optional[Boolean] = None, + need_email: Optional[Boolean] = None, + need_shipping_address: Optional[Boolean] = None, + is_flexible: Optional[Boolean] = None, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[types.InlineKeyboardMarkup] = None, + ): """ :param chat_id: Integer - Unique identifier for the target private chat :param title: String - Product name, 1-32 characters @@ -2041,26 +2267,26 @@ class SendInvoice(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'title': self.title, - 'description': self.description, - 'payload': self.payload, - 'provider_token': self.provider_token, - 'start_parameter': self.start_parameter, - 'currency': self.currency, - 'prices': prepare_arg(self.prices), - 'photo_url': self.photo_url, - 'photo_size': self.photo_size, - 'photo_width': self.photo_width, - 'photo_height': self.photo_height, - 'need_name': self.need_name, - 'need_phone_number': self.need_phone_number, - 'need_email': self.need_email, - 'need_shipping_address': self.need_shipping_address, - 'is_flexible': self.is_flexible, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup), + "chat_id": self.chat_id, + "title": self.title, + "description": self.description, + "payload": self.payload, + "provider_token": self.provider_token, + "start_parameter": self.start_parameter, + "currency": self.currency, + "prices": prepare_arg(self.prices), + "photo_url": self.photo_url, + "photo_size": self.photo_size, + "photo_width": self.photo_width, + "photo_height": self.photo_height, + "need_name": self.need_name, + "need_phone_number": self.need_phone_number, + "need_email": self.need_email, + "need_shipping_address": self.need_shipping_address, + "is_flexible": self.is_flexible, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } @@ -2069,14 +2295,17 @@ class AnswerShippingQuery(BaseResponse): Use that response type for answer shipping query on to webhook. """ - __slots__ = ('shipping_query_id', 'ok', 'shipping_options', 'error_message') + __slots__ = ("shipping_query_id", "ok", "shipping_options", "error_message") method = api.Methods.ANSWER_SHIPPING_QUERY - def __init__(self, shipping_query_id: String, - ok: Boolean, - shipping_options: Optional[typing.List[types.ShippingOption]] = None, - error_message: Optional[String] = None): + def __init__( + self, + shipping_query_id: String, + ok: Boolean, + shipping_options: Optional[typing.List[types.ShippingOption]] = None, + error_message: Optional[String] = None, + ): """ :param shipping_query_id: String - Unique identifier for the query to be answered :param ok: Boolean - Specify True if delivery to the specified address is possible and @@ -2095,10 +2324,10 @@ class AnswerShippingQuery(BaseResponse): def prepare(self): return { - 'shipping_query_id': self.shipping_query_id, - 'ok': self.ok, - 'shipping_options': prepare_arg(self.shipping_options), - 'error_message': self.error_message + "shipping_query_id": self.shipping_query_id, + "ok": self.ok, + "shipping_options": prepare_arg(self.shipping_options), + "error_message": self.error_message, } @@ -2107,13 +2336,13 @@ class AnswerPreCheckoutQuery(BaseResponse): Use that response type for answer pre checkout query on to webhook. """ - __slots__ = ('pre_checkout_query_id', 'ok', 'error_message') + __slots__ = ("pre_checkout_query_id", "ok", "error_message") method = api.Methods.ANSWER_PRE_CHECKOUT_QUERY - def __init__(self, pre_checkout_query_id: String, - ok: Boolean, - error_message: Optional[String] = None): + def __init__( + self, pre_checkout_query_id: String, ok: Boolean, error_message: Optional[String] = None + ): """ :param pre_checkout_query_id: String - Unique identifier for the query to be answered :param ok: Boolean - Specify True if everything is alright (goods are available, etc.) @@ -2130,9 +2359,9 @@ class AnswerPreCheckoutQuery(BaseResponse): def prepare(self): return { - 'pre_checkout_query_id': self.pre_checkout_query_id, - 'ok': self.ok, - 'error_message': self.error_message + "pre_checkout_query_id": self.pre_checkout_query_id, + "ok": self.ok, + "error_message": self.error_message, } @@ -2141,15 +2370,24 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin): Use that response type for send game on to webhook. """ - __slots__ = ('chat_id', 'game_short_name', 'disable_notification', 'reply_to_message_id', 'reply_markup') + __slots__ = ( + "chat_id", + "game_short_name", + "disable_notification", + "reply_to_message_id", + "reply_markup", + ) method = api.Methods.SEND_GAME - def __init__(self, chat_id: Integer, - game_short_name: String, - disable_notification: Optional[Boolean] = None, - reply_to_message_id: Optional[Integer] = None, - reply_markup: Optional[types.InlineKeyboardMarkup] = None): + def __init__( + self, + chat_id: Integer, + game_short_name: String, + disable_notification: Optional[Boolean] = None, + reply_to_message_id: Optional[Integer] = None, + reply_markup: Optional[types.InlineKeyboardMarkup] = None, + ): """ :param chat_id: Integer - Unique identifier for the target chat :param game_short_name: String - Short name of the game, serves as the unique identifier for the game. @@ -2168,9 +2406,9 @@ class SendGame(BaseResponse, ReplyToMixin, DisableNotificationMixin): def prepare(self): return { - 'chat_id': self.chat_id, - 'game_short_name': self.game_short_name, - 'disable_notification': self.disable_notification, - 'reply_to_message_id': self.reply_to_message_id, - 'reply_markup': prepare_arg(self.reply_markup) + "chat_id": self.chat_id, + "game_short_name": self.game_short_name, + "disable_notification": self.disable_notification, + "reply_to_message_id": self.reply_to_message_id, + "reply_markup": prepare_arg(self.reply_markup), } diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 5395e486..2cecc82c 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -19,17 +19,46 @@ from .game import Game from .game_high_score import GameHighScore from .inline_keyboard import InlineKeyboardButton, InlineKeyboardMarkup from .inline_query import InlineQuery -from .inline_query_result import InlineQueryResult, InlineQueryResultArticle, InlineQueryResultAudio, \ - InlineQueryResultCachedAudio, InlineQueryResultCachedDocument, InlineQueryResultCachedGif, \ - InlineQueryResultCachedMpeg4Gif, InlineQueryResultCachedPhoto, InlineQueryResultCachedSticker, \ - InlineQueryResultCachedVideo, InlineQueryResultCachedVoice, InlineQueryResultContact, InlineQueryResultDocument, \ - InlineQueryResultGame, InlineQueryResultGif, InlineQueryResultLocation, InlineQueryResultMpeg4Gif, \ - InlineQueryResultPhoto, InlineQueryResultVenue, InlineQueryResultVideo, InlineQueryResultVoice +from .inline_query_result import ( + InlineQueryResult, + InlineQueryResultArticle, + InlineQueryResultAudio, + InlineQueryResultCachedAudio, + InlineQueryResultCachedDocument, + InlineQueryResultCachedGif, + InlineQueryResultCachedMpeg4Gif, + InlineQueryResultCachedPhoto, + InlineQueryResultCachedSticker, + InlineQueryResultCachedVideo, + InlineQueryResultCachedVoice, + InlineQueryResultContact, + InlineQueryResultDocument, + InlineQueryResultGame, + InlineQueryResultGif, + InlineQueryResultLocation, + InlineQueryResultMpeg4Gif, + InlineQueryResultPhoto, + InlineQueryResultVenue, + InlineQueryResultVideo, + InlineQueryResultVoice, +) from .input_file import InputFile -from .input_media import InputMedia, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, \ - InputMediaVideo, MediaGroup -from .input_message_content import InputContactMessageContent, InputLocationMessageContent, InputMessageContent, \ - InputTextMessageContent, InputVenueMessageContent +from .input_media import ( + InputMedia, + InputMediaAnimation, + InputMediaAudio, + InputMediaDocument, + InputMediaPhoto, + InputMediaVideo, + MediaGroup, +) +from .input_message_content import ( + InputContactMessageContent, + InputLocationMessageContent, + InputMessageContent, + InputTextMessageContent, + InputVenueMessageContent, +) from .invoice import Invoice from .labeled_price import LabeledPrice from .location import Location @@ -39,9 +68,15 @@ from .message import ContentType, ContentTypes, Message, ParseMode from .message_entity import MessageEntity, MessageEntityType from .order_info import OrderInfo from .passport_data import PassportData -from .passport_element_error import PassportElementError, PassportElementErrorDataField, PassportElementErrorFile, \ - PassportElementErrorFiles, PassportElementErrorFrontSide, PassportElementErrorReverseSide, \ - PassportElementErrorSelfie +from .passport_element_error import ( + PassportElementError, + PassportElementErrorDataField, + PassportElementErrorFile, + PassportElementErrorFiles, + PassportElementErrorFrontSide, + PassportElementErrorReverseSide, + PassportElementErrorSelfie, +) from .passport_file import PassportFile from .photo_size import PhotoSize from .poll import PollOption, Poll @@ -64,107 +99,107 @@ from .voice import Voice from .webhook_info import WebhookInfo __all__ = ( - 'AllowedUpdates', - 'Animation', - 'Audio', - 'AuthWidgetData', - 'CallbackGame', - 'CallbackQuery', - 'Chat', - 'ChatActions', - 'ChatMember', - 'ChatMemberStatus', - 'ChatPhoto', - 'ChatType', - 'ChosenInlineResult', - 'Contact', - 'ContentType', - 'ContentTypes', - 'Document', - 'EncryptedCredentials', - 'EncryptedPassportElement', - 'File', - 'ForceReply', - 'Game', - 'GameHighScore', - 'InlineKeyboardButton', - 'InlineKeyboardMarkup', - 'InlineQuery', - 'InlineQueryResult', - 'InlineQueryResultArticle', - 'InlineQueryResultAudio', - 'InlineQueryResultCachedAudio', - 'InlineQueryResultCachedDocument', - 'InlineQueryResultCachedGif', - 'InlineQueryResultCachedMpeg4Gif', - 'InlineQueryResultCachedPhoto', - 'InlineQueryResultCachedSticker', - 'InlineQueryResultCachedVideo', - 'InlineQueryResultCachedVoice', - 'InlineQueryResultContact', - 'InlineQueryResultDocument', - 'InlineQueryResultGame', - 'InlineQueryResultGif', - 'InlineQueryResultLocation', - 'InlineQueryResultMpeg4Gif', - 'InlineQueryResultPhoto', - 'InlineQueryResultVenue', - 'InlineQueryResultVideo', - 'InlineQueryResultVoice', - 'InputContactMessageContent', - 'InputFile', - 'InputLocationMessageContent', - 'InputMedia', - 'InputMediaAnimation', - 'InputMediaAudio', - 'InputMediaDocument', - 'InputMediaPhoto', - 'InputMediaVideo', - 'InputMessageContent', - 'InputTextMessageContent', - 'InputVenueMessageContent', - 'Invoice', - 'KeyboardButton', - 'LabeledPrice', - 'Location', - 'LoginUrl', - 'MaskPosition', - 'MediaGroup', - 'Message', - 'MessageEntity', - 'MessageEntityType', - 'OrderInfo', - 'ParseMode', - 'PassportData', - 'PassportElementError', - 'PassportElementErrorDataField', - 'PassportElementErrorFile', - 'PassportElementErrorFiles', - 'PassportElementErrorFrontSide', - 'PassportElementErrorReverseSide', - 'PassportElementErrorSelfie', - 'PassportFile', - 'PhotoSize', - 'Poll', - 'PollOption', - 'PreCheckoutQuery', - 'ReplyKeyboardMarkup', - 'ReplyKeyboardRemove', - 'ResponseParameters', - 'ShippingAddress', - 'ShippingOption', - 'ShippingQuery', - 'Sticker', - 'StickerSet', - 'SuccessfulPayment', - 'Update', - 'User', - 'UserProfilePhotos', - 'Venue', - 'Video', - 'VideoNote', - 'Voice', - 'WebhookInfo', - 'base', - 'fields', + "AllowedUpdates", + "Animation", + "Audio", + "AuthWidgetData", + "CallbackGame", + "CallbackQuery", + "Chat", + "ChatActions", + "ChatMember", + "ChatMemberStatus", + "ChatPhoto", + "ChatType", + "ChosenInlineResult", + "Contact", + "ContentType", + "ContentTypes", + "Document", + "EncryptedCredentials", + "EncryptedPassportElement", + "File", + "ForceReply", + "Game", + "GameHighScore", + "InlineKeyboardButton", + "InlineKeyboardMarkup", + "InlineQuery", + "InlineQueryResult", + "InlineQueryResultArticle", + "InlineQueryResultAudio", + "InlineQueryResultCachedAudio", + "InlineQueryResultCachedDocument", + "InlineQueryResultCachedGif", + "InlineQueryResultCachedMpeg4Gif", + "InlineQueryResultCachedPhoto", + "InlineQueryResultCachedSticker", + "InlineQueryResultCachedVideo", + "InlineQueryResultCachedVoice", + "InlineQueryResultContact", + "InlineQueryResultDocument", + "InlineQueryResultGame", + "InlineQueryResultGif", + "InlineQueryResultLocation", + "InlineQueryResultMpeg4Gif", + "InlineQueryResultPhoto", + "InlineQueryResultVenue", + "InlineQueryResultVideo", + "InlineQueryResultVoice", + "InputContactMessageContent", + "InputFile", + "InputLocationMessageContent", + "InputMedia", + "InputMediaAnimation", + "InputMediaAudio", + "InputMediaDocument", + "InputMediaPhoto", + "InputMediaVideo", + "InputMessageContent", + "InputTextMessageContent", + "InputVenueMessageContent", + "Invoice", + "KeyboardButton", + "LabeledPrice", + "Location", + "LoginUrl", + "MaskPosition", + "MediaGroup", + "Message", + "MessageEntity", + "MessageEntityType", + "OrderInfo", + "ParseMode", + "PassportData", + "PassportElementError", + "PassportElementErrorDataField", + "PassportElementErrorFile", + "PassportElementErrorFiles", + "PassportElementErrorFrontSide", + "PassportElementErrorReverseSide", + "PassportElementErrorSelfie", + "PassportFile", + "PhotoSize", + "Poll", + "PollOption", + "PreCheckoutQuery", + "ReplyKeyboardMarkup", + "ReplyKeyboardRemove", + "ResponseParameters", + "ShippingAddress", + "ShippingOption", + "ShippingQuery", + "Sticker", + "StickerSet", + "SuccessfulPayment", + "Update", + "User", + "UserProfilePhotos", + "Venue", + "Video", + "VideoNote", + "Voice", + "WebhookInfo", + "base", + "fields", ) diff --git a/aiogram/types/audio.py b/aiogram/types/audio.py index 9423d02c..ccd89be8 100644 --- a/aiogram/types/audio.py +++ b/aiogram/types/audio.py @@ -10,6 +10,7 @@ class Audio(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#audio """ + file_id: base.String = fields.Field() duration: base.Integer = fields.Field() performer: base.String = fields.Field() diff --git a/aiogram/types/auth_widget_data.py b/aiogram/types/auth_widget_data.py index 6353e239..a6b9c18a 100644 --- a/aiogram/types/auth_widget_data.py +++ b/aiogram/types/auth_widget_data.py @@ -26,11 +26,11 @@ class AuthWidgetData(base.TelegramObject): """ try: query = dict(request.query) - query['id'] = int(query['id']) - query['auth_date'] = int(query['auth_date']) + query["id"] = int(query["id"]) + query["auth_date"] = int(query["auth_date"]) widget = AuthWidgetData(**query) except (ValueError, KeyError): - raise web.HTTPBadRequest(text='Invalid auth data') + raise web.HTTPBadRequest(text="Invalid auth data") else: return widget @@ -41,7 +41,7 @@ class AuthWidgetData(base.TelegramObject): def full_name(self): result = self.first_name if self.last_name: - result += ' ' + result += " " result += self.last_name return result diff --git a/aiogram/types/base.py b/aiogram/types/base.py index 97f67b16..b4a76964 100644 --- a/aiogram/types/base.py +++ b/aiogram/types/base.py @@ -10,24 +10,33 @@ from .fields import BaseField from ..utils import json from ..utils.mixins import ContextInstanceMixin -__all__ = ('MetaTelegramObject', 'TelegramObject', 'InputFile', 'String', 'Integer', 'Float', 'Boolean') +__all__ = ( + "MetaTelegramObject", + "TelegramObject", + "InputFile", + "String", + "Integer", + "Float", + "Boolean", +) -PROPS_ATTR_NAME = '_props' -VALUES_ATTR_NAME = '_values' -ALIASES_ATTR_NAME = '_aliases' +PROPS_ATTR_NAME = "_props" +VALUES_ATTR_NAME = "_values" +ALIASES_ATTR_NAME = "_aliases" # Binding of builtin types -InputFile = TypeVar('InputFile', 'InputFile', io.BytesIO, io.FileIO, str) -String = TypeVar('String', bound=str) -Integer = TypeVar('Integer', bound=int) -Float = TypeVar('Float', bound=float) -Boolean = TypeVar('Boolean', bound=bool) +InputFile = TypeVar("InputFile", "InputFile", io.BytesIO, io.FileIO, str) +String = TypeVar("String", bound=str) +Integer = TypeVar("Integer", bound=int) +Float = TypeVar("Float", bound=float) +Boolean = TypeVar("Boolean", bound=bool) class MetaTelegramObject(type): """ Metaclass for telegram objects """ + _objects = {} def __new__(mcs, name, bases, namespace, **kwargs): @@ -46,7 +55,9 @@ class MetaTelegramObject(type): aliases.update(getattr(base, ALIASES_ATTR_NAME)) # Scan current object for props - for name, prop in ((name, prop) for name, prop in namespace.items() if isinstance(prop, BaseField)): + for name, prop in ( + (name, prop) for name, prop in namespace.items() if isinstance(prop, BaseField) + ): props[prop.alias] = prop if prop.default is not None: values[prop.alias] = prop.default @@ -147,9 +158,11 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject): bot = Bot.get_current() if bot is None: - raise RuntimeError("Can't get bot instance from context. " - "You can fix it with setting current instance: " - "'Bot.set_current(bot_instance)'") + raise RuntimeError( + "Can't get bot instance from context. " + "You can fix it with setting current instance: " + "'Bot.set_current(bot_instance)'" + ) return bot def to_python(self) -> typing.Dict: @@ -219,7 +232,7 @@ class TelegramObject(ContextInstanceMixin, metaclass=MetaTelegramObject): :return: """ if key in self.props: - return self.props[key].set_value(self, value, self.conf.get('parent', None)) + return self.props[key].set_value(self, value, self.conf.get("parent", None)) raise KeyError(key) def __contains__(self, item): diff --git a/aiogram/types/callback_game.py b/aiogram/types/callback_game.py index 4cd0d5f5..254df324 100644 --- a/aiogram/types/callback_game.py +++ b/aiogram/types/callback_game.py @@ -7,4 +7,5 @@ class CallbackGame(base.TelegramObject): https://core.telegram.org/bots/api#callbackgame """ + pass diff --git a/aiogram/types/callback_query.py b/aiogram/types/callback_query.py index 51ba1f17..19dcb373 100644 --- a/aiogram/types/callback_query.py +++ b/aiogram/types/callback_query.py @@ -20,18 +20,22 @@ class CallbackQuery(base.TelegramObject): https://core.telegram.org/bots/api#callbackquery """ + id: base.String = fields.Field() - from_user: User = fields.Field(alias='from', base=User) + from_user: User = fields.Field(alias="from", base=User) message: Message = fields.Field(base=Message) inline_message_id: base.String = fields.Field() chat_instance: base.String = fields.Field() data: base.String = fields.Field() game_short_name: base.String = fields.Field() - async def answer(self, text: typing.Union[base.String, None] = None, - show_alert: typing.Union[base.Boolean, None] = None, - url: typing.Union[base.String, None] = None, - cache_time: typing.Union[base.Integer, None] = None): + async def answer( + self, + text: typing.Union[base.String, None] = None, + show_alert: typing.Union[base.Boolean, None] = None, + url: typing.Union[base.String, None] = None, + cache_time: typing.Union[base.Integer, None] = None, + ): """ Use this method to send answers to callback queries sent from inline keyboards. The answer will be displayed to the user as a notification at the top of the chat screen or as an alert. @@ -54,8 +58,13 @@ class CallbackQuery(base.TelegramObject): :type cache_time: :obj:`typing.Union[base.Integer, None]` :return: On success, True is returned. :rtype: :obj:`base.Boolean`""" - await self.bot.answer_callback_query(callback_query_id=self.id, text=text, - show_alert=show_alert, url=url, cache_time=cache_time) + await self.bot.answer_callback_query( + callback_query_id=self.id, + text=text, + show_alert=show_alert, + url=url, + cache_time=cache_time, + ) def __hash__(self): return hash(self.id) diff --git a/aiogram/types/chat.py b/aiogram/types/chat.py index cd34f1be..d854c074 100644 --- a/aiogram/types/chat.py +++ b/aiogram/types/chat.py @@ -16,6 +16,7 @@ class Chat(base.TelegramObject): https://core.telegram.org/bots/api#chat """ + id: base.Integer = fields.Field() type: base.String = fields.Field() title: base.String = fields.Field() @@ -26,7 +27,7 @@ class Chat(base.TelegramObject): photo: ChatPhoto = fields.Field(base=ChatPhoto) description: base.String = fields.Field() invite_link: base.String = fields.Field() - pinned_message: 'Message' = fields.Field(base='Message') + pinned_message: "Message" = fields.Field(base="Message") sticker_set_name: base.String = fields.Field() can_set_sticker_set: base.Boolean = fields.Field() @@ -38,7 +39,7 @@ class Chat(base.TelegramObject): if self.type == ChatType.PRIVATE: full_name = self.first_name if self.last_name: - full_name += ' ' + self.last_name + full_name += " " + self.last_name return full_name return self.title @@ -48,7 +49,7 @@ class Chat(base.TelegramObject): Get mention if a Chat has a username, or get full name if this is a Private Chat, otherwise None is returned """ if self.username: - return '@' + self.username + return "@" + self.username if self.type == ChatType.PRIVATE: return self.full_name return None @@ -56,7 +57,7 @@ class Chat(base.TelegramObject): @property def user_url(self): if self.type != ChatType.PRIVATE: - raise TypeError('`user_url` property is only available in private chats!') + raise TypeError("`user_url` property is only available in private chats!") return f"tg://user?id={self.id}" @@ -79,7 +80,7 @@ class Chat(base.TelegramObject): return f"tg://user?id={self.id}" if self.username: - return f'https://t.me/{self.username}' + return f"https://t.me/{self.username}" if self.invite_link: return self.invite_link @@ -161,8 +162,9 @@ class Chat(base.TelegramObject): """ return await self.bot.delete_chat_description(self.id, description) - async def kick(self, user_id: base.Integer, - until_date: typing.Union[base.Integer, None] = None): + async def kick( + self, user_id: base.Integer, until_date: typing.Union[base.Integer, None] = None + ): """ Use this method to kick a user from a group, a supergroup or a channel. In the case of supergroups and channels, the user will not be able to return to the group @@ -201,12 +203,15 @@ class Chat(base.TelegramObject): """ return await self.bot.unban_chat_member(self.id, user_id=user_id) - async def restrict(self, user_id: base.Integer, - until_date: typing.Union[base.Integer, None] = None, - can_send_messages: typing.Union[base.Boolean, None] = None, - can_send_media_messages: typing.Union[base.Boolean, None] = None, - can_send_other_messages: typing.Union[base.Boolean, None] = None, - can_add_web_page_previews: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def restrict( + self, + user_id: base.Integer, + until_date: typing.Union[base.Integer, None] = None, + can_send_messages: typing.Union[base.Boolean, None] = None, + can_send_media_messages: typing.Union[base.Boolean, None] = None, + can_send_other_messages: typing.Union[base.Boolean, None] = None, + can_add_web_page_previews: typing.Union[base.Boolean, None] = None, + ) -> base.Boolean: """ Use this method to restrict a user in a supergroup. The bot must be an administrator in the supergroup for this to work and must have the appropriate admin rights. @@ -232,21 +237,28 @@ class Chat(base.TelegramObject): :return: Returns True on success. :rtype: :obj:`base.Boolean` """ - return await self.bot.restrict_chat_member(self.id, user_id=user_id, until_date=until_date, - can_send_messages=can_send_messages, - can_send_media_messages=can_send_media_messages, - can_send_other_messages=can_send_other_messages, - can_add_web_page_previews=can_add_web_page_previews) + return await self.bot.restrict_chat_member( + self.id, + user_id=user_id, + until_date=until_date, + can_send_messages=can_send_messages, + can_send_media_messages=can_send_media_messages, + can_send_other_messages=can_send_other_messages, + can_add_web_page_previews=can_add_web_page_previews, + ) - async def promote(self, user_id: base.Integer, - can_change_info: typing.Union[base.Boolean, None] = None, - can_post_messages: typing.Union[base.Boolean, None] = None, - can_edit_messages: typing.Union[base.Boolean, None] = None, - can_delete_messages: typing.Union[base.Boolean, None] = None, - can_invite_users: typing.Union[base.Boolean, None] = None, - can_restrict_members: typing.Union[base.Boolean, None] = None, - can_pin_messages: typing.Union[base.Boolean, None] = None, - can_promote_members: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def promote( + self, + user_id: base.Integer, + can_change_info: typing.Union[base.Boolean, None] = None, + can_post_messages: typing.Union[base.Boolean, None] = None, + can_edit_messages: typing.Union[base.Boolean, None] = None, + can_delete_messages: typing.Union[base.Boolean, None] = None, + can_invite_users: typing.Union[base.Boolean, None] = None, + can_restrict_members: typing.Union[base.Boolean, None] = None, + can_pin_messages: typing.Union[base.Boolean, None] = None, + can_promote_members: typing.Union[base.Boolean, None] = None, + ) -> base.Boolean: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -277,16 +289,18 @@ class Chat(base.TelegramObject): :return: Returns True on success. :rtype: :obj:`base.Boolean` """ - return await self.bot.promote_chat_member(self.id, - user_id=user_id, - can_change_info=can_change_info, - can_post_messages=can_post_messages, - can_edit_messages=can_edit_messages, - can_delete_messages=can_delete_messages, - can_invite_users=can_invite_users, - can_restrict_members=can_restrict_members, - can_pin_messages=can_pin_messages, - can_promote_members=can_promote_members) + return await self.bot.promote_chat_member( + self.id, + user_id=user_id, + can_change_info=can_change_info, + can_post_messages=can_post_messages, + can_edit_messages=can_edit_messages, + can_delete_messages=can_delete_messages, + can_invite_users=can_invite_users, + can_restrict_members=can_restrict_members, + can_pin_messages=can_pin_messages, + can_promote_members=can_promote_members, + ) async def pin_message(self, message_id: int, disable_notification: bool = False): """ @@ -422,9 +436,9 @@ class ChatType(helper.Helper): @staticmethod def _check(obj, chat_types) -> bool: - if hasattr(obj, 'chat'): + if hasattr(obj, "chat"): obj = obj.chat - if not hasattr(obj, 'type'): + if not hasattr(obj, "type"): return False return obj.type in chat_types @@ -511,12 +525,13 @@ class ChatActions(helper.Helper): @classmethod async def _do(cls, action: str, sleep=None): from aiogram import Bot + await Bot.get_current().send_chat_action(Chat.get_current().id, action) if sleep: await asyncio.sleep(sleep) @classmethod - def calc_timeout(cls, text, timeout=.8): + def calc_timeout(cls, text, timeout=0.8): """ Calculate timeout for text diff --git a/aiogram/types/chat_member.py b/aiogram/types/chat_member.py index 12789462..c2f714d9 100644 --- a/aiogram/types/chat_member.py +++ b/aiogram/types/chat_member.py @@ -13,6 +13,7 @@ class ChatMember(base.TelegramObject): https://core.telegram.org/bots/api#chatmember """ + user: User = fields.Field(base=User) status: base.String = fields.Field() until_date: datetime.datetime = fields.DateTimeField() @@ -32,9 +33,12 @@ class ChatMember(base.TelegramObject): can_add_web_page_previews: base.Boolean = fields.Field() def is_admin(self): - warnings.warn('`is_admin` method deprecated due to updates in Bot API 4.2. ' - 'This method renamed to `is_chat_admin` and will be available until aiogram 2.3', - DeprecationWarning, stacklevel=2) + warnings.warn( + "`is_admin` method deprecated due to updates in Bot API 4.2. " + "This method renamed to `is_chat_admin` and will be available until aiogram 2.3", + DeprecationWarning, + stacklevel=2, + ) return self.is_chat_admin() def is_chat_admin(self): @@ -62,16 +66,22 @@ class ChatMemberStatus(helper.Helper): @classmethod def is_admin(cls, role): - warnings.warn('`is_admin` method deprecated due to updates in Bot API 4.2. ' - 'This method renamed to `is_chat_admin` and will be available until aiogram 2.3', - DeprecationWarning, stacklevel=2) + warnings.warn( + "`is_admin` method deprecated due to updates in Bot API 4.2. " + "This method renamed to `is_chat_admin` and will be available until aiogram 2.3", + DeprecationWarning, + stacklevel=2, + ) return cls.is_chat_admin(role) @classmethod def is_member(cls, role): - warnings.warn('`is_member` method deprecated due to updates in Bot API 4.2. ' - 'This method renamed to `is_chat_member` and will be available until aiogram 2.3', - DeprecationWarning, stacklevel=2) + warnings.warn( + "`is_member` method deprecated due to updates in Bot API 4.2. " + "This method renamed to `is_chat_member` and will be available until aiogram 2.3", + DeprecationWarning, + stacklevel=2, + ) return cls.is_chat_member(role) @classmethod diff --git a/aiogram/types/chat_photo.py b/aiogram/types/chat_photo.py index 08775d93..00c1cedf 100644 --- a/aiogram/types/chat_photo.py +++ b/aiogram/types/chat_photo.py @@ -11,10 +11,13 @@ class ChatPhoto(base.TelegramObject): https://core.telegram.org/bots/api#chatphoto """ + small_file_id: base.String = fields.Field() big_file_id: base.String = fields.Field() - async def download_small(self, destination=None, timeout=30, chunk_size=65536, seek=True, make_dirs=True): + async def download_small( + self, destination=None, timeout=30, chunk_size=65536, seek=True, make_dirs=True + ): """ Download file @@ -38,10 +41,17 @@ class ChatPhoto(base.TelegramObject): if is_path and make_dirs: os.makedirs(os.path.dirname(destination), exist_ok=True) - return await self.bot.download_file(file_path=file.file_path, destination=destination, timeout=timeout, - chunk_size=chunk_size, seek=seek) + return await self.bot.download_file( + file_path=file.file_path, + destination=destination, + timeout=timeout, + chunk_size=chunk_size, + seek=seek, + ) - async def download_big(self, destination=None, timeout=30, chunk_size=65536, seek=True, make_dirs=True): + async def download_big( + self, destination=None, timeout=30, chunk_size=65536, seek=True, make_dirs=True + ): """ Download file @@ -65,8 +75,13 @@ class ChatPhoto(base.TelegramObject): if is_path and make_dirs: os.makedirs(os.path.dirname(destination), exist_ok=True) - return await self.bot.download_file(file_path=file.file_path, destination=destination, timeout=timeout, - chunk_size=chunk_size, seek=seek) + return await self.bot.download_file( + file_path=file.file_path, + destination=destination, + timeout=timeout, + chunk_size=chunk_size, + seek=seek, + ) async def get_small_file(self): return await self.bot.get_file(self.small_file_id) diff --git a/aiogram/types/chosen_inline_result.py b/aiogram/types/chosen_inline_result.py index 246b071d..24c59cb5 100644 --- a/aiogram/types/chosen_inline_result.py +++ b/aiogram/types/chosen_inline_result.py @@ -15,8 +15,9 @@ class ChosenInlineResult(base.TelegramObject): https://core.telegram.org/bots/api#choseninlineresult """ + result_id: base.String = fields.Field() - from_user: User = fields.Field(alias='from', base=User) + from_user: User = fields.Field(alias="from", base=User) location: Location = fields.Field(base=Location) inline_message_id: base.String = fields.Field() query: base.String = fields.Field() diff --git a/aiogram/types/contact.py b/aiogram/types/contact.py index b70045b9..a3cfc15b 100644 --- a/aiogram/types/contact.py +++ b/aiogram/types/contact.py @@ -8,6 +8,7 @@ class Contact(base.TelegramObject): https://core.telegram.org/bots/api#contact """ + phone_number: base.String = fields.Field() first_name: base.String = fields.Field() last_name: base.String = fields.Field() @@ -18,7 +19,7 @@ class Contact(base.TelegramObject): def full_name(self): name = self.first_name if self.last_name is not None: - name += ' ' + self.last_name + name += " " + self.last_name return name def __hash__(self): diff --git a/aiogram/types/document.py b/aiogram/types/document.py index 32d943d8..f640bc27 100644 --- a/aiogram/types/document.py +++ b/aiogram/types/document.py @@ -10,6 +10,7 @@ class Document(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#document """ + file_id: base.String = fields.Field() thumb: PhotoSize = fields.Field(base=PhotoSize) file_name: base.String = fields.Field() diff --git a/aiogram/types/fields.py b/aiogram/types/fields.py index e0d5b892..37b562f5 100644 --- a/aiogram/types/fields.py +++ b/aiogram/types/fields.py @@ -1,7 +1,7 @@ import abc import datetime -__all__ = ('BaseField', 'Field', 'ListField', 'DateTimeField', 'TextField', 'ListOfLists') +__all__ = ("BaseField", "Field", "ListField", "DateTimeField", "TextField", "ListOfLists") class BaseField(metaclass=abc.ABCMeta): @@ -29,7 +29,7 @@ class BaseField(metaclass=abc.ABCMeta): self.alias = name def resolve_base(self, instance): - if self.base_object is None or hasattr(self.base_object, 'telegram_types'): + if self.base_object is None or hasattr(self.base_object, "telegram_types"): return elif isinstance(self.base_object, str): self.base_object = instance.telegram_types.get(self.base_object) @@ -100,16 +100,18 @@ class Field(BaseField): """ def serialize(self, value): - if self.base_object is not None and hasattr(value, 'to_python'): + if self.base_object is not None and hasattr(value, "to_python"): return value.to_python() return value def deserialize(self, value, parent=None): - if isinstance(value, dict) \ - and self.base_object is not None \ - and not hasattr(value, 'base_object') \ - and not hasattr(value, 'to_python'): - return self.base_object(conf={'parent': parent}, **value) + if ( + isinstance(value, dict) + and self.base_object is not None + and not hasattr(value, "base_object") + and not hasattr(value, "to_python") + ): + return self.base_object(conf={"parent": parent}, **value) return value @@ -119,7 +121,7 @@ class ListField(Field): """ def __init__(self, *args, **kwargs): - default = kwargs.pop('default', None) + default = kwargs.pop("default", None) if default is None: default = [] @@ -154,7 +156,7 @@ class ListOfLists(Field): def deserialize(self, value, parent=None): result = [] deserialize = super(ListOfLists, self).deserialize - if hasattr(value, '__iter__'): + if hasattr(value, "__iter__"): for row in value: row_result = [] for item in row: diff --git a/aiogram/types/file.py b/aiogram/types/file.py index f3269f29..0bcd1f63 100644 --- a/aiogram/types/file.py +++ b/aiogram/types/file.py @@ -16,6 +16,7 @@ class File(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#file """ + file_id: base.String = fields.Field() file_size: base.Integer = fields.Field() file_path: base.String = fields.Field() diff --git a/aiogram/types/force_reply.py b/aiogram/types/force_reply.py index 97ec16c6..cafb6432 100644 --- a/aiogram/types/force_reply.py +++ b/aiogram/types/force_reply.py @@ -22,6 +22,7 @@ class ForceReply(base.TelegramObject): https://core.telegram.org/bots/api#forcereply """ + force_reply: base.Boolean = fields.Field(default=True) selective: base.Boolean = fields.Field() diff --git a/aiogram/types/game.py b/aiogram/types/game.py index f5861d90..b6845464 100644 --- a/aiogram/types/game.py +++ b/aiogram/types/game.py @@ -15,6 +15,7 @@ class Game(base.TelegramObject): https://core.telegram.org/bots/api#game """ + title: base.String = fields.Field() description: base.String = fields.Field() photo: typing.List[PhotoSize] = fields.ListField(base=PhotoSize) diff --git a/aiogram/types/game_high_score.py b/aiogram/types/game_high_score.py index 43f9c1c9..5244c9ea 100644 --- a/aiogram/types/game_high_score.py +++ b/aiogram/types/game_high_score.py @@ -11,6 +11,7 @@ class GameHighScore(base.TelegramObject): https://core.telegram.org/bots/api#gamehighscore """ + position: base.Integer = fields.Field() user: User = fields.Field(base=User) score: base.Integer = fields.Field() diff --git a/aiogram/types/inline_keyboard.py b/aiogram/types/inline_keyboard.py index 97ad35da..5f1652fe 100644 --- a/aiogram/types/inline_keyboard.py +++ b/aiogram/types/inline_keyboard.py @@ -15,26 +15,29 @@ class InlineKeyboardMarkup(base.TelegramObject): https://core.telegram.org/bots/api#inlinekeyboardmarkup """ - inline_keyboard: 'typing.List[typing.List[InlineKeyboardButton]]' = fields.ListOfLists(base='InlineKeyboardButton') + + inline_keyboard: "typing.List[typing.List[InlineKeyboardButton]]" = fields.ListOfLists( + base="InlineKeyboardButton" + ) def __init__(self, row_width=3, inline_keyboard=None, **kwargs): if inline_keyboard is None: inline_keyboard = [] - conf = kwargs.pop('conf', {}) or {} - conf['row_width'] = row_width + conf = kwargs.pop("conf", {}) or {} + conf["row_width"] = row_width - super(InlineKeyboardMarkup, self).__init__(**kwargs, - conf=conf, - inline_keyboard=inline_keyboard) + super(InlineKeyboardMarkup, self).__init__( + **kwargs, conf=conf, inline_keyboard=inline_keyboard + ) @property def row_width(self): - return self.conf.get('row_width', 3) + return self.conf.get("row_width", 3) @row_width.setter def row_width(self, value): - self.conf['row_width'] = value + self.conf["row_width"] = value def add(self, *args): """ @@ -89,6 +92,7 @@ class InlineKeyboardButton(base.TelegramObject): https://core.telegram.org/bots/api#inlinekeyboardbutton """ + text: base.String = fields.Field() url: base.String = fields.Field() login_url: LoginUrl = fields.Field(base=LoginUrl) @@ -98,19 +102,26 @@ class InlineKeyboardButton(base.TelegramObject): callback_game: CallbackGame = fields.Field(base=CallbackGame) pay: base.Boolean = fields.Field() - def __init__(self, text: base.String, - url: base.String = None, - login_url: LoginUrl = None, - callback_data: base.String = None, - switch_inline_query: base.String = None, - switch_inline_query_current_chat: base.String = None, - callback_game: CallbackGame = None, - pay: base.Boolean = None, **kwargs): - super(InlineKeyboardButton, self).__init__(text=text, - url=url, - login_url=login_url, - callback_data=callback_data, - switch_inline_query=switch_inline_query, - switch_inline_query_current_chat=switch_inline_query_current_chat, - callback_game=callback_game, - pay=pay, **kwargs) + def __init__( + self, + text: base.String, + url: base.String = None, + login_url: LoginUrl = None, + callback_data: base.String = None, + switch_inline_query: base.String = None, + switch_inline_query_current_chat: base.String = None, + callback_game: CallbackGame = None, + pay: base.Boolean = None, + **kwargs, + ): + super(InlineKeyboardButton, self).__init__( + text=text, + url=url, + login_url=login_url, + callback_data=callback_data, + switch_inline_query=switch_inline_query, + switch_inline_query_current_chat=switch_inline_query_current_chat, + callback_game=callback_game, + pay=pay, + **kwargs, + ) diff --git a/aiogram/types/inline_query.py b/aiogram/types/inline_query.py index 379394a0..611da173 100644 --- a/aiogram/types/inline_query.py +++ b/aiogram/types/inline_query.py @@ -15,19 +15,22 @@ class InlineQuery(base.TelegramObject): https://core.telegram.org/bots/api#inlinequery """ + id: base.String = fields.Field() - from_user: User = fields.Field(alias='from', base=User) + from_user: User = fields.Field(alias="from", base=User) location: Location = fields.Field(base=Location) query: base.String = fields.Field() offset: base.String = fields.Field() - async def answer(self, - results: typing.List[InlineQueryResult], - cache_time: typing.Union[base.Integer, None] = None, - is_personal: typing.Union[base.Boolean, None] = None, - next_offset: typing.Union[base.String, None] = None, - switch_pm_text: typing.Union[base.String, None] = None, - switch_pm_parameter: typing.Union[base.String, None] = None): + async def answer( + self, + results: typing.List[InlineQueryResult], + cache_time: typing.Union[base.Integer, None] = None, + is_personal: typing.Union[base.Boolean, None] = None, + next_offset: typing.Union[base.String, None] = None, + switch_pm_text: typing.Union[base.String, None] = None, + switch_pm_parameter: typing.Union[base.String, None] = None, + ): """ Use this method to send answers to an inline query. No more than 50 results per query are allowed. @@ -57,10 +60,12 @@ class InlineQuery(base.TelegramObject): :return: On success, True is returned :rtype: :obj:`base.Boolean` """ - return await self.bot.answer_inline_query(self.id, - results=results, - cache_time=cache_time, - is_personal=is_personal, - next_offset=next_offset, - switch_pm_text=switch_pm_text, - switch_pm_parameter=switch_pm_parameter) + return await self.bot.answer_inline_query( + self.id, + results=results, + cache_time=cache_time, + is_personal=is_personal, + next_offset=next_offset, + switch_pm_text=switch_pm_text, + switch_pm_parameter=switch_pm_parameter, + ) diff --git a/aiogram/types/inline_query_result.py b/aiogram/types/inline_query_result.py index a80352d7..19746bd4 100644 --- a/aiogram/types/inline_query_result.py +++ b/aiogram/types/inline_query_result.py @@ -14,6 +14,7 @@ class InlineQueryResult(base.TelegramObject): https://core.telegram.org/bots/api#inlinequeryresult """ + id: base.String = fields.Field() reply_markup: InlineKeyboardMarkup = fields.Field(base=InlineKeyboardMarkup) @@ -24,8 +25,8 @@ class InlineQueryResult(base.TelegramObject): pass def __init__(self, **kwargs): - if 'parse_mode' in kwargs and kwargs['parse_mode'] is None: - kwargs['parse_mode'] = self.safe_get_parse_mode() + if "parse_mode" in kwargs and kwargs["parse_mode"] is None: + kwargs["parse_mode"] = self.safe_get_parse_mode() super(InlineQueryResult, self).__init__(**kwargs) @@ -35,7 +36,8 @@ class InlineQueryResultArticle(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultarticle """ - type: base.String = fields.Field(alias='type', default='article') + + type: base.String = fields.Field(alias="type", default="article") title: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) url: base.String = fields.Field() @@ -45,22 +47,32 @@ class InlineQueryResultArticle(InlineQueryResult): thumb_width: base.Integer = fields.Field() thumb_height: base.Integer = fields.Field() - def __init__(self, *, - id: base.String, - title: base.String, - input_message_content: InputMessageContent, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - url: typing.Optional[base.String] = None, - hide_url: typing.Optional[base.Boolean] = None, - description: typing.Optional[base.String] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None): - super(InlineQueryResultArticle, self).__init__(id=id, title=title, - input_message_content=input_message_content, - reply_markup=reply_markup, url=url, hide_url=hide_url, - description=description, thumb_url=thumb_url, - thumb_width=thumb_width, thumb_height=thumb_height) + def __init__( + self, + *, + id: base.String, + title: base.String, + input_message_content: InputMessageContent, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + url: typing.Optional[base.String] = None, + hide_url: typing.Optional[base.Boolean] = None, + description: typing.Optional[base.String] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + ): + super(InlineQueryResultArticle, self).__init__( + id=id, + title=title, + input_message_content=input_message_content, + reply_markup=reply_markup, + url=url, + hide_url=hide_url, + description=description, + thumb_url=thumb_url, + thumb_width=thumb_width, + thumb_height=thumb_height, + ) class InlineQueryResultPhoto(InlineQueryResult): @@ -73,7 +85,8 @@ class InlineQueryResultPhoto(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultphoto """ - type: base.String = fields.Field(alias='type', default='photo') + + type: base.String = fields.Field(alias="type", default="photo") photo_url: base.String = fields.Field() thumb_url: base.String = fields.Field() photo_width: base.Integer = fields.Field() @@ -83,22 +96,32 @@ class InlineQueryResultPhoto(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - photo_url: base.String, - thumb_url: base.String, - photo_width: typing.Optional[base.Integer] = None, - photo_height: typing.Optional[base.Integer] = None, - title: typing.Optional[base.String] = None, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultPhoto, self).__init__(id=id, photo_url=photo_url, thumb_url=thumb_url, - photo_width=photo_width, photo_height=photo_height, title=title, - description=description, caption=caption, - reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + photo_url: base.String, + thumb_url: base.String, + photo_width: typing.Optional[base.Integer] = None, + photo_height: typing.Optional[base.Integer] = None, + title: typing.Optional[base.String] = None, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultPhoto, self).__init__( + id=id, + photo_url=photo_url, + thumb_url=thumb_url, + photo_width=photo_width, + photo_height=photo_height, + title=title, + description=description, + caption=caption, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultGif(InlineQueryResult): @@ -111,7 +134,8 @@ class InlineQueryResultGif(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultgif """ - type: base.String = fields.Field(alias='type', default='gif') + + type: base.String = fields.Field(alias="type", default="gif") gif_url: base.String = fields.Field() gif_width: base.Integer = fields.Field() gif_height: base.Integer = fields.Field() @@ -121,23 +145,34 @@ class InlineQueryResultGif(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - gif_url: base.String, - gif_width: typing.Optional[base.Integer] = None, - gif_height: typing.Optional[base.Integer] = None, - gif_duration: typing.Optional[base.Integer] = None, - thumb_url: typing.Optional[base.String] = None, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultGif, self).__init__(id=id, gif_url=gif_url, gif_width=gif_width, - gif_height=gif_height, gif_duration=gif_duration, - thumb_url=thumb_url, title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + gif_url: base.String, + gif_width: typing.Optional[base.Integer] = None, + gif_height: typing.Optional[base.Integer] = None, + gif_duration: typing.Optional[base.Integer] = None, + thumb_url: typing.Optional[base.String] = None, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultGif, self).__init__( + id=id, + gif_url=gif_url, + gif_width=gif_width, + gif_height=gif_height, + gif_duration=gif_duration, + thumb_url=thumb_url, + title=title, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultMpeg4Gif(InlineQueryResult): @@ -150,7 +185,8 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultmpeg4gif """ - type: base.String = fields.Field(alias='type', default='mpeg4_gif') + + type: base.String = fields.Field(alias="type", default="mpeg4_gif") mpeg4_url: base.String = fields.Field() mpeg4_width: base.Integer = fields.Field() mpeg4_height: base.Integer = fields.Field() @@ -160,23 +196,34 @@ class InlineQueryResultMpeg4Gif(InlineQueryResult): caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - mpeg4_url: base.String, - thumb_url: base.String, - mpeg4_width: typing.Optional[base.Integer] = None, - mpeg4_height: typing.Optional[base.Integer] = None, - mpeg4_duration: typing.Optional[base.Integer] = None, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultMpeg4Gif, self).__init__(id=id, mpeg4_url=mpeg4_url, mpeg4_width=mpeg4_width, - mpeg4_height=mpeg4_height, mpeg4_duration=mpeg4_duration, - thumb_url=thumb_url, title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + mpeg4_url: base.String, + thumb_url: base.String, + mpeg4_width: typing.Optional[base.Integer] = None, + mpeg4_height: typing.Optional[base.Integer] = None, + mpeg4_duration: typing.Optional[base.Integer] = None, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultMpeg4Gif, self).__init__( + id=id, + mpeg4_url=mpeg4_url, + mpeg4_width=mpeg4_width, + mpeg4_height=mpeg4_height, + mpeg4_duration=mpeg4_duration, + thumb_url=thumb_url, + title=title, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultVideo(InlineQueryResult): @@ -192,7 +239,8 @@ class InlineQueryResultVideo(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultvideo """ - type: base.String = fields.Field(alias='type', default='video') + + type: base.String = fields.Field(alias="type", default="video") video_url: base.String = fields.Field() mime_type: base.String = fields.Field() thumb_url: base.String = fields.Field() @@ -204,26 +252,38 @@ class InlineQueryResultVideo(InlineQueryResult): description: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - video_url: base.String, - mime_type: base.String, - thumb_url: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - video_width: typing.Optional[base.Integer] = None, - video_height: typing.Optional[base.Integer] = None, - video_duration: typing.Optional[base.Integer] = None, - description: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultVideo, self).__init__(id=id, video_url=video_url, mime_type=mime_type, - thumb_url=thumb_url, title=title, caption=caption, - video_width=video_width, video_height=video_height, - video_duration=video_duration, description=description, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + video_url: base.String, + mime_type: base.String, + thumb_url: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + video_width: typing.Optional[base.Integer] = None, + video_height: typing.Optional[base.Integer] = None, + video_duration: typing.Optional[base.Integer] = None, + description: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultVideo, self).__init__( + id=id, + video_url=video_url, + mime_type=mime_type, + thumb_url=thumb_url, + title=title, + caption=caption, + video_width=video_width, + video_height=video_height, + video_duration=video_duration, + description=description, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultAudio(InlineQueryResult): @@ -237,7 +297,8 @@ class InlineQueryResultAudio(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultaudio """ - type: base.String = fields.Field(alias='type', default='audio') + + type: base.String = fields.Field(alias="type", default="audio") audio_url: base.String = fields.Field() title: base.String = fields.Field() caption: base.String = fields.Field() @@ -245,21 +306,30 @@ class InlineQueryResultAudio(InlineQueryResult): audio_duration: base.Integer = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - audio_url: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - performer: typing.Optional[base.String] = None, - audio_duration: typing.Optional[base.Integer] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultAudio, self).__init__(id=id, audio_url=audio_url, title=title, - caption=caption, parse_mode=parse_mode, - performer=performer, audio_duration=audio_duration, - reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + audio_url: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + performer: typing.Optional[base.String] = None, + audio_duration: typing.Optional[base.Integer] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultAudio, self).__init__( + id=id, + audio_url=audio_url, + title=title, + caption=caption, + parse_mode=parse_mode, + performer=performer, + audio_duration=audio_duration, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultVoice(InlineQueryResult): @@ -275,26 +345,36 @@ class InlineQueryResultVoice(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultvoice """ - type: base.String = fields.Field(alias='type', default='voice') + + type: base.String = fields.Field(alias="type", default="voice") voice_url: base.String = fields.Field() title: base.String = fields.Field() caption: base.String = fields.Field() voice_duration: base.Integer = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - voice_url: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - voice_duration: typing.Optional[base.Integer] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultVoice, self).__init__(id=id, voice_url=voice_url, title=title, - caption=caption, voice_duration=voice_duration, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + voice_url: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + voice_duration: typing.Optional[base.Integer] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultVoice, self).__init__( + id=id, + voice_url=voice_url, + title=title, + caption=caption, + voice_duration=voice_duration, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultDocument(InlineQueryResult): @@ -309,7 +389,8 @@ class InlineQueryResultDocument(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultdocument """ - type: base.String = fields.Field(alias='type', default='document') + + type: base.String = fields.Field(alias="type", default="document") title: base.String = fields.Field() caption: base.String = fields.Field() document_url: base.String = fields.Field() @@ -320,25 +401,36 @@ class InlineQueryResultDocument(InlineQueryResult): thumb_width: base.Integer = fields.Field() thumb_height: base.Integer = fields.Field() - def __init__(self, *, - id: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - document_url: typing.Optional[base.String] = None, - mime_type: typing.Optional[base.String] = None, - description: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None): - super(InlineQueryResultDocument, self).__init__(id=id, title=title, caption=caption, - document_url=document_url, mime_type=mime_type, - description=description, reply_markup=reply_markup, - input_message_content=input_message_content, - thumb_url=thumb_url, thumb_width=thumb_width, - thumb_height=thumb_height, parse_mode=parse_mode) + def __init__( + self, + *, + id: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + document_url: typing.Optional[base.String] = None, + mime_type: typing.Optional[base.String] = None, + description: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + ): + super(InlineQueryResultDocument, self).__init__( + id=id, + title=title, + caption=caption, + document_url=document_url, + mime_type=mime_type, + description=description, + reply_markup=reply_markup, + input_message_content=input_message_content, + thumb_url=thumb_url, + thumb_width=thumb_width, + thumb_height=thumb_height, + parse_mode=parse_mode, + ) class InlineQueryResultLocation(InlineQueryResult): @@ -354,7 +446,8 @@ class InlineQueryResultLocation(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultlocation """ - type: base.String = fields.Field(alias='type', default='location') + + type: base.String = fields.Field(alias="type", default="location") latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() title: base.String = fields.Field() @@ -364,23 +457,32 @@ class InlineQueryResultLocation(InlineQueryResult): thumb_width: base.Integer = fields.Field() thumb_height: base.Integer = fields.Field() - def __init__(self, *, - id: base.String, - latitude: base.Float, - longitude: base.Float, - title: base.String, - live_period: typing.Optional[base.Integer] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None): - super(InlineQueryResultLocation, self).__init__(id=id, latitude=latitude, longitude=longitude, - title=title, live_period=live_period, - reply_markup=reply_markup, - input_message_content=input_message_content, - thumb_url=thumb_url, thumb_width=thumb_width, - thumb_height=thumb_height) + def __init__( + self, + *, + id: base.String, + latitude: base.Float, + longitude: base.Float, + title: base.String, + live_period: typing.Optional[base.Integer] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + ): + super(InlineQueryResultLocation, self).__init__( + id=id, + latitude=latitude, + longitude=longitude, + title=title, + live_period=live_period, + reply_markup=reply_markup, + input_message_content=input_message_content, + thumb_url=thumb_url, + thumb_width=thumb_width, + thumb_height=thumb_height, + ) class InlineQueryResultVenue(InlineQueryResult): @@ -395,7 +497,8 @@ class InlineQueryResultVenue(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultvenue """ - type: base.String = fields.Field(alias='type', default='venue') + + type: base.String = fields.Field(alias="type", default="venue") latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() title: base.String = fields.Field() @@ -407,25 +510,36 @@ class InlineQueryResultVenue(InlineQueryResult): thumb_height: base.Integer = fields.Field() foursquare_type: base.String = fields.Field() - def __init__(self, *, - id: base.String, - latitude: base.Float, - longitude: base.Float, - title: base.String, - address: base.String, - foursquare_id: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None, - foursquare_type: typing.Optional[base.String] = None): - super(InlineQueryResultVenue, self).__init__(id=id, latitude=latitude, longitude=longitude, - title=title, address=address, foursquare_id=foursquare_id, - reply_markup=reply_markup, - input_message_content=input_message_content, thumb_url=thumb_url, - thumb_width=thumb_width, thumb_height=thumb_height, - foursquare_type=foursquare_type) + def __init__( + self, + *, + id: base.String, + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + foursquare_type: typing.Optional[base.String] = None, + ): + super(InlineQueryResultVenue, self).__init__( + id=id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + reply_markup=reply_markup, + input_message_content=input_message_content, + thumb_url=thumb_url, + thumb_width=thumb_width, + thumb_height=thumb_height, + foursquare_type=foursquare_type, + ) class InlineQueryResultContact(InlineQueryResult): @@ -440,7 +554,8 @@ class InlineQueryResultContact(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcontact """ - type: base.String = fields.Field(alias='type', default='contact') + + type: base.String = fields.Field(alias="type", default="contact") phone_number: base.String = fields.Field() first_name: base.String = fields.Field() last_name: base.String = fields.Field() @@ -451,23 +566,32 @@ class InlineQueryResultContact(InlineQueryResult): thumb_height: base.Integer = fields.Field() foursquare_type: base.String = fields.Field() - def __init__(self, *, - id: base.String, - phone_number: base.String, - first_name: base.String, - last_name: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None, - thumb_url: typing.Optional[base.String] = None, - thumb_width: typing.Optional[base.Integer] = None, - thumb_height: typing.Optional[base.Integer] = None, - foursquare_type: typing.Optional[base.String] = None): - super(InlineQueryResultContact, self).__init__(id=id, phone_number=phone_number, - first_name=first_name, last_name=last_name, - reply_markup=reply_markup, - input_message_content=input_message_content, thumb_url=thumb_url, - thumb_width=thumb_width, thumb_height=thumb_height, - foursquare_type=foursquare_type) + def __init__( + self, + *, + id: base.String, + phone_number: base.String, + first_name: base.String, + last_name: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + thumb_url: typing.Optional[base.String] = None, + thumb_width: typing.Optional[base.Integer] = None, + thumb_height: typing.Optional[base.Integer] = None, + foursquare_type: typing.Optional[base.String] = None, + ): + super(InlineQueryResultContact, self).__init__( + id=id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + reply_markup=reply_markup, + input_message_content=input_message_content, + thumb_url=thumb_url, + thumb_width=thumb_width, + thumb_height=thumb_height, + foursquare_type=foursquare_type, + ) class InlineQueryResultGame(InlineQueryResult): @@ -479,15 +603,20 @@ class InlineQueryResultGame(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultgame """ - type: base.String = fields.Field(alias='type', default='game') + + type: base.String = fields.Field(alias="type", default="game") game_short_name: base.String = fields.Field() - def __init__(self, *, - id: base.String, - game_short_name: base.String, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None): - super(InlineQueryResultGame, self).__init__(id=id, game_short_name=game_short_name, - reply_markup=reply_markup) + def __init__( + self, + *, + id: base.String, + game_short_name: base.String, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + ): + super(InlineQueryResultGame, self).__init__( + id=id, game_short_name=game_short_name, reply_markup=reply_markup + ) class InlineQueryResultCachedPhoto(InlineQueryResult): @@ -500,26 +629,36 @@ class InlineQueryResultCachedPhoto(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedphoto """ - type: base.String = fields.Field(alias='type', default='photo') + + type: base.String = fields.Field(alias="type", default="photo") photo_file_id: base.String = fields.Field() title: base.String = fields.Field() description: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - photo_file_id: base.String, - title: typing.Optional[base.String] = None, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedPhoto, self).__init__(id=id, photo_file_id=photo_file_id, title=title, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + photo_file_id: base.String, + title: typing.Optional[base.String] = None, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedPhoto, self).__init__( + id=id, + photo_file_id=photo_file_id, + title=title, + description=description, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedGif(InlineQueryResult): @@ -532,24 +671,33 @@ class InlineQueryResultCachedGif(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedgif """ - type: base.String = fields.Field(alias='type', default='gif') + + type: base.String = fields.Field(alias="type", default="gif") gif_file_id: base.String = fields.Field() title: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - gif_file_id: base.String, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedGif, self).__init__(id=id, gif_file_id=gif_file_id, - title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + gif_file_id: base.String, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedGif, self).__init__( + id=id, + gif_file_id=gif_file_id, + title=title, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): @@ -562,24 +710,33 @@ class InlineQueryResultCachedMpeg4Gif(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedmpeg4gif """ - type: base.String = fields.Field(alias='type', default='mpeg4_gif') + + type: base.String = fields.Field(alias="type", default="mpeg4_gif") mpeg4_file_id: base.String = fields.Field() title: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - mpeg4_file_id: base.String, - title: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedMpeg4Gif, self).__init__(id=id, mpeg4_file_id=mpeg4_file_id, - title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + mpeg4_file_id: base.String, + title: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedMpeg4Gif, self).__init__( + id=id, + mpeg4_file_id=mpeg4_file_id, + title=title, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedSticker(InlineQueryResult): @@ -595,18 +752,25 @@ class InlineQueryResultCachedSticker(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedsticker """ - type: base.String = fields.Field(alias='type', default='sticker') + + type: base.String = fields.Field(alias="type", default="sticker") sticker_file_id: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - sticker_file_id: base.String, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedSticker, self).__init__(id=id, sticker_file_id=sticker_file_id, - reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + sticker_file_id: base.String, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedSticker, self).__init__( + id=id, + sticker_file_id=sticker_file_id, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedDocument(InlineQueryResult): @@ -621,27 +785,36 @@ class InlineQueryResultCachedDocument(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcacheddocument """ - type: base.String = fields.Field(alias='type', default='document') + + type: base.String = fields.Field(alias="type", default="document") title: base.String = fields.Field() document_file_id: base.String = fields.Field() description: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - title: base.String, - document_file_id: base.String, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedDocument, self).__init__(id=id, title=title, - document_file_id=document_file_id, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + title: base.String, + document_file_id: base.String, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedDocument, self).__init__( + id=id, + title=title, + document_file_id=document_file_id, + description=description, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedVideo(InlineQueryResult): @@ -654,26 +827,36 @@ class InlineQueryResultCachedVideo(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedvideo """ - type: base.String = fields.Field(alias='type', default='video') + + type: base.String = fields.Field(alias="type", default="video") video_file_id: base.String = fields.Field() title: base.String = fields.Field() description: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - video_file_id: base.String, - title: base.String, - description: typing.Optional[base.String] = None, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedVideo, self).__init__(id=id, video_file_id=video_file_id, title=title, - description=description, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + video_file_id: base.String, + title: base.String, + description: typing.Optional[base.String] = None, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedVideo, self).__init__( + id=id, + video_file_id=video_file_id, + title=title, + description=description, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedVoice(InlineQueryResult): @@ -688,24 +871,33 @@ class InlineQueryResultCachedVoice(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedvoice """ - type: base.String = fields.Field(alias='type', default='voice') + + type: base.String = fields.Field(alias="type", default="voice") voice_file_id: base.String = fields.Field() title: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - voice_file_id: base.String, - title: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedVoice, self).__init__(id=id, voice_file_id=voice_file_id, - title=title, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + voice_file_id: base.String, + title: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedVoice, self).__init__( + id=id, + voice_file_id=voice_file_id, + title=title, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) class InlineQueryResultCachedAudio(InlineQueryResult): @@ -721,19 +913,27 @@ class InlineQueryResultCachedAudio(InlineQueryResult): https://core.telegram.org/bots/api#inlinequeryresultcachedaudio """ - type: base.String = fields.Field(alias='type', default='audio') + + type: base.String = fields.Field(alias="type", default="audio") audio_file_id: base.String = fields.Field() caption: base.String = fields.Field() input_message_content: InputMessageContent = fields.Field(base=InputMessageContent) - def __init__(self, *, - id: base.String, - audio_file_id: base.String, - caption: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - reply_markup: typing.Optional[InlineKeyboardMarkup] = None, - input_message_content: typing.Optional[InputMessageContent] = None): - super(InlineQueryResultCachedAudio, self).__init__(id=id, audio_file_id=audio_file_id, - caption=caption, parse_mode=parse_mode, - reply_markup=reply_markup, - input_message_content=input_message_content) + def __init__( + self, + *, + id: base.String, + audio_file_id: base.String, + caption: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + reply_markup: typing.Optional[InlineKeyboardMarkup] = None, + input_message_content: typing.Optional[InputMessageContent] = None, + ): + super(InlineQueryResultCachedAudio, self).__init__( + id=id, + audio_file_id=audio_file_id, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + input_message_content=input_message_content, + ) diff --git a/aiogram/types/input_file.py b/aiogram/types/input_file.py index 3c397395..a6959295 100644 --- a/aiogram/types/input_file.py +++ b/aiogram/types/input_file.py @@ -12,7 +12,7 @@ from ..bot import api CHUNK_SIZE = 65536 -log = logging.getLogger('aiogram') +log = logging.getLogger("aiogram") class InputFile(base.TelegramObject): @@ -35,7 +35,7 @@ class InputFile(base.TelegramObject): super(InputFile, self).__init__(conf=conf) if isinstance(path_or_bytesio, str): # As path - self._file = open(path_or_bytesio, 'rb') + self._file = open(path_or_bytesio, "rb") self._path = path_or_bytesio if filename is None: filename = os.path.split(path_or_bytesio)[-1] @@ -46,7 +46,7 @@ class InputFile(base.TelegramObject): self._path = None self._file = path_or_bytesio else: - raise TypeError('Not supported file type.') + raise TypeError("Not supported file type.") self._filename = filename @@ -56,7 +56,7 @@ class InputFile(base.TelegramObject): """ Close file descriptor """ - if not hasattr(self, '_file'): + if not hasattr(self, "_file"): return if inspect.iscoroutinefunction(self._file.close): @@ -123,7 +123,7 @@ class InputFile(base.TelegramObject): :param filename: :param chunk_size: """ - with open(filename, 'wb') as fp: + with open(filename, "wb") as fp: while True: # Chunk writer data = self.file.read(chunk_size) @@ -143,11 +143,11 @@ class InputFile(base.TelegramObject): __repr__ = __str__ def to_python(self): - raise TypeError('Object of this type is not exportable!') + raise TypeError("Object of this type is not exportable!") @classmethod def to_object(cls, data): - raise TypeError('Object of this type is not importable!') + raise TypeError("Object of this type is not importable!") class _WebPipe: @@ -165,7 +165,7 @@ class _WebPipe: @property def name(self): if not self._name: - *_, part = self.url.rpartition('/') + *_, part = self.url.rpartition("/") if part: self._name = part else: @@ -206,7 +206,7 @@ class _WebPipe: async def read(self, chunk_size=-1): if not self._response: - raise LookupError('I/O operation on closed stream') + raise LookupError("I/O operation on closed stream") response: aiohttp.ClientResponse = self._response reader: aiohttp.StreamReader = response.content @@ -214,6 +214,6 @@ class _WebPipe: def __str__(self): result = f"WebPipe url='{self.url}', name='{self.name}'" - return '<' + result + '>' + return "<" + result + ">" __repr__ = __str__ diff --git a/aiogram/types/input_media.py b/aiogram/types/input_media.py index 7bb58a7a..9517a797 100644 --- a/aiogram/types/input_media.py +++ b/aiogram/types/input_media.py @@ -6,7 +6,7 @@ from . import base from . import fields from .input_file import InputFile -ATTACHMENT_PREFIX = 'attach://' +ATTACHMENT_PREFIX = "attach://" class InputMedia(base.TelegramObject): @@ -22,9 +22,12 @@ class InputMedia(base.TelegramObject): https://core.telegram.org/bots/api#inputmedia """ - type: base.String = fields.Field(default='photo') - media: base.String = fields.Field(alias='media', on_change='_media_changed') - thumb: typing.Union[base.InputFile, base.String] = fields.Field(alias='thumb', on_change='_thumb_changed') + + type: base.String = fields.Field(default="photo") + media: base.String = fields.Field(alias="media", on_change="_media_changed") + thumb: typing.Union[base.InputFile, base.String] = fields.Field( + alias="thumb", on_change="_thumb_changed" + ) caption: base.String = fields.Field() parse_mode: base.Boolean = fields.Field() @@ -32,13 +35,13 @@ class InputMedia(base.TelegramObject): self._thumb_file = None self._media_file = None - media = kwargs.pop('media', None) + media = kwargs.pop("media", None) if isinstance(media, (io.IOBase, InputFile)): self.file = media elif media is not None: self.media = media - thumb = kwargs.pop('thumb', None) + thumb = kwargs.pop("thumb", None) if isinstance(thumb, (io.IOBase, InputFile)): self.thumb_file = thumb elif thumb is not None: @@ -58,7 +61,7 @@ class InputMedia(base.TelegramObject): @file.setter def file(self, file: io.IOBase): - self.media = 'attach://' + secrets.token_urlsafe(16) + self.media = "attach://" + secrets.token_urlsafe(16) self._media_file = file @file.deleter @@ -67,7 +70,7 @@ class InputMedia(base.TelegramObject): self._media_file = None def _media_changed(self, value): - if value is None or isinstance(value, str) and not value.startswith('attach://'): + if value is None or isinstance(value, str) and not value.startswith("attach://"): self._media_file = None @property @@ -76,7 +79,7 @@ class InputMedia(base.TelegramObject): @thumb_file.setter def thumb_file(self, file: io.IOBase): - self.thumb = 'attach://' + secrets.token_urlsafe(16) + self.thumb = "attach://" + secrets.token_urlsafe(16) self._thumb_file = file @thumb_file.deleter @@ -85,7 +88,7 @@ class InputMedia(base.TelegramObject): self._thumb_file = None def _thumb_changed(self, value): - if value is None or isinstance(value, str) and not value.startswith('attach://'): + if value is None or isinstance(value, str) and not value.startswith("attach://"): self._thumb_file = None def get_files(self): @@ -106,14 +109,28 @@ class InputMediaAnimation(InputMedia): height: base.Integer = fields.Field() duration: base.Integer = fields.Field() - def __init__(self, media: base.InputFile, - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None, - parse_mode: base.Boolean = None, **kwargs): - super(InputMediaAnimation, self).__init__(type='animation', media=media, thumb=thumb, caption=caption, - width=width, height=height, duration=duration, - parse_mode=parse_mode, conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + width: base.Integer = None, + height: base.Integer = None, + duration: base.Integer = None, + parse_mode: base.Boolean = None, + **kwargs, + ): + super(InputMediaAnimation, self).__init__( + type="animation", + media=media, + thumb=thumb, + caption=caption, + width=width, + height=height, + duration=duration, + parse_mode=parse_mode, + conf=kwargs, + ) class InputMediaDocument(InputMedia): @@ -123,11 +140,22 @@ class InputMediaDocument(InputMedia): https://core.telegram.org/bots/api#inputmediadocument """ - def __init__(self, media: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, parse_mode: base.Boolean = None, **kwargs): - super(InputMediaDocument, self).__init__(type='document', media=media, thumb=thumb, - caption=caption, parse_mode=parse_mode, - conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + parse_mode: base.Boolean = None, + **kwargs, + ): + super(InputMediaDocument, self).__init__( + type="document", + media=media, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + conf=kwargs, + ) class InputMediaAudio(InputMedia): @@ -143,18 +171,32 @@ class InputMediaAudio(InputMedia): performer: base.String = fields.Field() title: base.String = fields.Field() - def __init__(self, media: base.InputFile, - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - width: base.Integer = None, height: base.Integer = None, - duration: base.Integer = None, - performer: base.String = None, - title: base.String = None, - parse_mode: base.Boolean = None, **kwargs): - super(InputMediaAudio, self).__init__(type='audio', media=media, thumb=thumb, caption=caption, - width=width, height=height, duration=duration, - performer=performer, title=title, - parse_mode=parse_mode, conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + width: base.Integer = None, + height: base.Integer = None, + duration: base.Integer = None, + performer: base.String = None, + title: base.String = None, + parse_mode: base.Boolean = None, + **kwargs, + ): + super(InputMediaAudio, self).__init__( + type="audio", + media=media, + thumb=thumb, + caption=caption, + width=width, + height=height, + duration=duration, + performer=performer, + title=title, + parse_mode=parse_mode, + conf=kwargs, + ) class InputMediaPhoto(InputMedia): @@ -164,11 +206,22 @@ class InputMediaPhoto(InputMedia): https://core.telegram.org/bots/api#inputmediaphoto """ - def __init__(self, media: base.InputFile, thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, parse_mode: base.Boolean = None, **kwargs): - super(InputMediaPhoto, self).__init__(type='photo', media=media, thumb=thumb, - caption=caption, parse_mode=parse_mode, - conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + parse_mode: base.Boolean = None, + **kwargs, + ): + super(InputMediaPhoto, self).__init__( + type="photo", + media=media, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + conf=kwargs, + ) class InputMediaVideo(InputMedia): @@ -177,21 +230,36 @@ class InputMediaVideo(InputMedia): https://core.telegram.org/bots/api#inputmediavideo """ + width: base.Integer = fields.Field() height: base.Integer = fields.Field() duration: base.Integer = fields.Field() supports_streaming: base.Boolean = fields.Field() - def __init__(self, media: base.InputFile, - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None, - parse_mode: base.Boolean = None, - supports_streaming: base.Boolean = None, **kwargs): - super(InputMediaVideo, self).__init__(type='video', media=media, thumb=thumb, caption=caption, - width=width, height=height, duration=duration, - parse_mode=parse_mode, - supports_streaming=supports_streaming, conf=kwargs) + def __init__( + self, + media: base.InputFile, + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + width: base.Integer = None, + height: base.Integer = None, + duration: base.Integer = None, + parse_mode: base.Boolean = None, + supports_streaming: base.Boolean = None, + **kwargs, + ): + super(InputMediaVideo, self).__init__( + type="video", + media=media, + thumb=thumb, + caption=caption, + width=width, + height=height, + duration=duration, + parse_mode=parse_mode, + supports_streaming=supports_streaming, + conf=kwargs, + ) class MediaGroup(base.TelegramObject): @@ -199,7 +267,9 @@ class MediaGroup(base.TelegramObject): Helper for sending media group """ - def __init__(self, medias: typing.Optional[typing.List[typing.Union[InputMedia, typing.Dict]]] = None): + def __init__( + self, medias: typing.Optional[typing.List[typing.Union[InputMedia, typing.Dict]]] = None + ): super(MediaGroup, self).__init__() self.media = [] @@ -222,13 +292,13 @@ class MediaGroup(base.TelegramObject): :param media: """ if isinstance(media, dict): - if 'type' not in media: + if "type" not in media: raise ValueError(f"Invalid media!") - media_type = media['type'] - if media_type == 'photo': + media_type = media["type"] + if media_type == "photo": media = InputMediaPhoto(**media) - elif media_type == 'video': + elif media_type == "video": media = InputMediaVideo(**media) # elif media_type == 'document': # media = InputMediaDocument(**media) @@ -240,9 +310,11 @@ class MediaGroup(base.TelegramObject): raise TypeError(f"Invalid media type '{media_type}'!") elif not isinstance(media, InputMedia): - raise TypeError(f"Media must be an instance of InputMedia or dict, not {type(media).__name__}") + raise TypeError( + f"Media must be an instance of InputMedia or dict, not {type(media).__name__}" + ) - elif media.type in ['document', 'audio', 'animation']: + elif media.type in ["document", "audio", "animation"]: raise ValueError(f"This type of media is not supported by media groups!") self.media.append(media) @@ -313,8 +385,9 @@ class MediaGroup(base.TelegramObject): self.attach(document) ''' - def attach_photo(self, photo: typing.Union[InputMediaPhoto, base.InputFile], - caption: base.String = None): + def attach_photo( + self, photo: typing.Union[InputMediaPhoto, base.InputFile], caption: base.String = None + ): """ Attach photo @@ -325,10 +398,15 @@ class MediaGroup(base.TelegramObject): photo = InputMediaPhoto(media=photo, caption=caption) self.attach(photo) - def attach_video(self, video: typing.Union[InputMediaVideo, base.InputFile], - thumb: typing.Union[base.InputFile, base.String] = None, - caption: base.String = None, - width: base.Integer = None, height: base.Integer = None, duration: base.Integer = None): + def attach_video( + self, + video: typing.Union[InputMediaVideo, base.InputFile], + thumb: typing.Union[base.InputFile, base.String] = None, + caption: base.String = None, + width: base.Integer = None, + height: base.Integer = None, + duration: base.Integer = None, + ): """ Attach video @@ -339,8 +417,14 @@ class MediaGroup(base.TelegramObject): :param duration: """ if not isinstance(video, InputMedia): - video = InputMediaVideo(media=video, thumb=thumb, caption=caption, - width=width, height=height, duration=duration) + video = InputMediaVideo( + media=video, + thumb=thumb, + caption=caption, + width=width, + height=height, + duration=duration, + ) self.attach(video) def to_python(self) -> typing.List: diff --git a/aiogram/types/input_message_content.py b/aiogram/types/input_message_content.py index 736a4454..cea1c1af 100644 --- a/aiogram/types/input_message_content.py +++ b/aiogram/types/input_message_content.py @@ -12,6 +12,7 @@ class InputMessageContent(base.TelegramObject): https://core.telegram.org/bots/api#inputmessagecontent """ + pass @@ -24,16 +25,21 @@ class InputContactMessageContent(InputMessageContent): https://core.telegram.org/bots/api#inputcontactmessagecontent """ + phone_number: base.String = fields.Field() first_name: base.String = fields.Field() last_name: base.String = fields.Field() vcard: base.String = fields.Field() - def __init__(self, phone_number: base.String, - first_name: typing.Optional[base.String] = None, - last_name: typing.Optional[base.String] = None): - super(InputContactMessageContent, self).__init__(phone_number=phone_number, first_name=first_name, - last_name=last_name) + def __init__( + self, + phone_number: base.String, + first_name: typing.Optional[base.String] = None, + last_name: typing.Optional[base.String] = None, + ): + super(InputContactMessageContent, self).__init__( + phone_number=phone_number, first_name=first_name, last_name=last_name + ) class InputLocationMessageContent(InputMessageContent): @@ -45,11 +51,11 @@ class InputLocationMessageContent(InputMessageContent): https://core.telegram.org/bots/api#inputlocationmessagecontent """ + latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() - def __init__(self, latitude: base.Float, - longitude: base.Float): + def __init__(self, latitude: base.Float, longitude: base.Float): super(InputLocationMessageContent, self).__init__(latitude=latitude, longitude=longitude) @@ -59,6 +65,7 @@ class InputTextMessageContent(InputMessageContent): https://core.telegram.org/bots/api#inputtextmessagecontent """ + message_text: base.String = fields.Field() parse_mode: base.String = fields.Field() disable_web_page_preview: base.Boolean = fields.Field() @@ -69,14 +76,20 @@ class InputTextMessageContent(InputMessageContent): except RuntimeError: pass - def __init__(self, message_text: typing.Optional[base.String] = None, - parse_mode: typing.Optional[base.String] = None, - disable_web_page_preview: typing.Optional[base.Boolean] = None): + def __init__( + self, + message_text: typing.Optional[base.String] = None, + parse_mode: typing.Optional[base.String] = None, + disable_web_page_preview: typing.Optional[base.Boolean] = None, + ): if parse_mode is None: parse_mode = self.safe_get_parse_mode() - super(InputTextMessageContent, self).__init__(message_text=message_text, parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview) + super(InputTextMessageContent, self).__init__( + message_text=message_text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + ) class InputVenueMessageContent(InputMessageContent): @@ -88,16 +101,25 @@ class InputVenueMessageContent(InputMessageContent): https://core.telegram.org/bots/api#inputvenuemessagecontent """ + latitude: base.Float = fields.Field() longitude: base.Float = fields.Field() title: base.String = fields.Field() address: base.String = fields.Field() foursquare_id: base.String = fields.Field() - def __init__(self, latitude: typing.Optional[base.Float] = None, - longitude: typing.Optional[base.Float] = None, - title: typing.Optional[base.String] = None, - address: typing.Optional[base.String] = None, - foursquare_id: typing.Optional[base.String] = None): - super(InputVenueMessageContent, self).__init__(latitude=latitude, longitude=longitude, title=title, - address=address, foursquare_id=foursquare_id) + def __init__( + self, + latitude: typing.Optional[base.Float] = None, + longitude: typing.Optional[base.Float] = None, + title: typing.Optional[base.String] = None, + address: typing.Optional[base.String] = None, + foursquare_id: typing.Optional[base.String] = None, + ): + super(InputVenueMessageContent, self).__init__( + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + ) diff --git a/aiogram/types/invoice.py b/aiogram/types/invoice.py index 172fcbc4..4112e1a3 100644 --- a/aiogram/types/invoice.py +++ b/aiogram/types/invoice.py @@ -8,6 +8,7 @@ class Invoice(base.TelegramObject): https://core.telegram.org/bots/api#invoice """ + title: base.String = fields.Field() description: base.String = fields.Field() start_parameter: base.String = fields.Field() diff --git a/aiogram/types/labeled_price.py b/aiogram/types/labeled_price.py index f2cbd38b..c1cbd48e 100644 --- a/aiogram/types/labeled_price.py +++ b/aiogram/types/labeled_price.py @@ -8,6 +8,7 @@ class LabeledPrice(base.TelegramObject): https://core.telegram.org/bots/api#labeledprice """ + label: base.String = fields.Field() amount: base.Integer = fields.Field() diff --git a/aiogram/types/location.py b/aiogram/types/location.py index ea2f81c4..a1df1829 100644 --- a/aiogram/types/location.py +++ b/aiogram/types/location.py @@ -8,5 +8,6 @@ class Location(base.TelegramObject): https://core.telegram.org/bots/api#location """ + longitude: base.Float = fields.Field() latitude: base.Float = fields.Field() diff --git a/aiogram/types/login_url.py b/aiogram/types/login_url.py index c0dd6133..1c9468e6 100644 --- a/aiogram/types/login_url.py +++ b/aiogram/types/login_url.py @@ -10,21 +10,24 @@ class LoginUrl(base.TelegramObject): https://core.telegram.org/bots/api#loginurl """ + url: base.String = fields.Field() forward_text: base.String = fields.Field() bot_username: base.String = fields.Field() request_write_access: base.Boolean = fields.Field() - def __init__(self, - url: base.String, - forward_text: base.String = None, - bot_username: base.String = None, - request_write_access: base.Boolean = None, - **kwargs): + def __init__( + self, + url: base.String, + forward_text: base.String = None, + bot_username: base.String = None, + request_write_access: base.Boolean = None, + **kwargs, + ): super(LoginUrl, self).__init__( url=url, forward_text=forward_text, bot_username=bot_username, request_write_access=request_write_access, - **kwargs + **kwargs, ) diff --git a/aiogram/types/mask_position.py b/aiogram/types/mask_position.py index e0620463..57003798 100644 --- a/aiogram/types/mask_position.py +++ b/aiogram/types/mask_position.py @@ -8,6 +8,7 @@ class MaskPosition(base.TelegramObject): https://core.telegram.org/bots/api#maskposition """ + point: base.String = fields.Field() x_shift: base.Float = fields.Field() y_shift: base.Float = fields.Field() diff --git a/aiogram/types/message.py b/aiogram/types/message.py index 7637cf42..87d95d90 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -41,8 +41,9 @@ class Message(base.TelegramObject): https://core.telegram.org/bots/api#message """ + message_id: base.Integer = fields.Field() - from_user: User = fields.Field(alias='from', base=User) + from_user: User = fields.Field(alias="from", base=User) date: datetime.datetime = fields.DateTimeField() chat: Chat = fields.Field(base=Chat) forward_from: User = fields.Field(base=User) @@ -50,7 +51,7 @@ class Message(base.TelegramObject): forward_from_message_id: base.Integer = fields.Field() forward_signature: base.String = fields.Field() forward_date: datetime.datetime = fields.DateTimeField() - reply_to_message: Message = fields.Field(base='Message') + reply_to_message: Message = fields.Field(base="Message") edit_date: datetime.datetime = fields.DateTimeField() media_group_id: base.String = fields.Field() author_signature: base.String = fields.Field() @@ -81,7 +82,7 @@ class Message(base.TelegramObject): channel_chat_created: base.Boolean = fields.Field() migrate_to_chat_id: base.Integer = fields.Field() migrate_from_chat_id: base.Integer = fields.Field() - pinned_message: Message = fields.Field(base='Message') + pinned_message: Message = fields.Field(base="Message") invoice: Invoice = fields.Field(base=Invoice) successful_payment: SuccessfulPayment = fields.Field(base=SuccessfulPayment) connected_website: base.String = fields.Field() @@ -155,7 +156,7 @@ class Message(base.TelegramObject): :return: bool """ - return self.text and self.text.startswith('/') + return self.text and self.text.startswith("/") def get_full_command(self): """ @@ -164,7 +165,7 @@ class Message(base.TelegramObject): :return: tuple of (command, args) """ if self.is_command(): - command, _, args = self.text.partition(' ') + command, _, args = self.text.partition(" ") return command, args def get_command(self, pure=False): @@ -177,7 +178,7 @@ class Message(base.TelegramObject): if command: command = command[0] if pure: - command, _, _ = command[1:].partition('@') + command, _, _ = command[1:].partition("@") return command def get_args(self): @@ -207,30 +208,30 @@ class Message(base.TelegramObject): if not entities: return quote_fn(text) - if not sys.maxunicode == 0xffff: - text = text.encode('utf-16-le') + if not sys.maxunicode == 0xFFFF: + text = text.encode("utf-16-le") - result = '' + result = "" offset = 0 for entity in sorted(entities, key=lambda item: item.offset): entity_text = entity.parse(text, as_html=as_html) - if sys.maxunicode == 0xffff: - part = text[offset:entity.offset] + if sys.maxunicode == 0xFFFF: + part = text[offset : entity.offset] result += quote_fn(part) + entity_text else: - part = text[offset * 2:entity.offset * 2] - result += quote_fn(part.decode('utf-16-le')) + entity_text + part = text[offset * 2 : entity.offset * 2] + result += quote_fn(part.decode("utf-16-le")) + entity_text offset = entity.offset + entity.length - if sys.maxunicode == 0xffff: + if sys.maxunicode == 0xFFFF: part = text[offset:] result += quote_fn(part) else: - part = text[offset * 2:] - result += quote_fn(part.decode('utf-16-le')) + part = text[offset * 2 :] + result += quote_fn(part.decode("utf-16-le")) return result @@ -260,9 +261,9 @@ class Message(base.TelegramObject): :return: str """ if self.chat.type not in [ChatType.SUPER_GROUP, ChatType.CHANNEL]: - raise TypeError('Invalid chat type!') + raise TypeError("Invalid chat type!") elif not self.chat.username: - raise TypeError('This chat does not have @username') + raise TypeError("This chat does not have @username") return f"https://t.me/{self.chat.username}/{self.message_id}" @@ -285,15 +286,17 @@ class Message(base.TelegramObject): return md.hlink(text, url) return md.link(text, url) - async def answer(self, text: base.String, - parse_mode: typing.Union[base.String, None] = None, - disable_web_page_preview: typing.Union[base.Boolean, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer( + self, + text: base.String, + parse_mode: typing.Union[base.String, None] = None, + disable_web_page_preview: typing.Union[base.Boolean, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Answer to this message @@ -315,23 +318,27 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.send_message(chat_id=self.chat.id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_message( + chat_id=self.chat.id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_photo(self, photo: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_photo( + self, + photo: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send photos. @@ -355,26 +362,30 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.send_photo(chat_id=self.chat.id, - photo=photo, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_photo( + chat_id=self.chat.id, + photo=photo, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_audio(self, audio: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - duration: typing.Union[base.Integer, None] = None, - performer: typing.Union[base.String, None] = None, - title: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_audio( + self, + audio: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + duration: typing.Union[base.Integer, None] = None, + performer: typing.Union[base.String, None] = None, + title: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -406,30 +417,34 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_audio(chat_id=self.chat.id, - audio=audio, - caption=caption, - parse_mode=parse_mode, - duration=duration, - performer=performer, - title=title, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_audio( + chat_id=self.chat.id, + audio=audio, + caption=caption, + parse_mode=parse_mode, + duration=duration, + performer=performer, + title=title, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_animation(self, animation: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_animation( + self, + animation: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). @@ -466,27 +481,31 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.send_animation(self.chat.id, - animation=animation, - duration=duration, - width=width, - height=height, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_animation( + self.chat.id, + animation=animation, + duration=duration, + width=width, + height=height, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_document(self, document: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_document( + self, + document: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send general files. @@ -511,26 +530,30 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_document(chat_id=self.chat.id, - document=document, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_document( + chat_id=self.chat.id, + document=document, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_video(self, video: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_video( + self, + video: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -560,27 +583,31 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_video(chat_id=self.chat.id, - video=video, - duration=duration, - width=width, - height=height, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_video( + chat_id=self.chat.id, + video=video, + duration=duration, + width=width, + height=height, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_voice(self, voice: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - duration: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_voice( + self, + voice: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + duration: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -609,24 +636,28 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_voice(chat_id=self.chat.id, - voice=voice, - caption=caption, - parse_mode=parse_mode, - duration=duration, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_voice( + chat_id=self.chat.id, + voice=voice, + caption=caption, + parse_mode=parse_mode, + duration=duration, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_video_note(self, video_note: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - length: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_video_note( + self, + video_note: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + length: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. @@ -649,17 +680,22 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_video_note(chat_id=self.chat.id, - video_note=video_note, - duration=duration, - length=length, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_video_note( + chat_id=self.chat.id, + video_note=video_note, + duration=duration, + length=length, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_media_group(self, media: typing.Union[MediaGroup, typing.List], - disable_notification: typing.Union[base.Boolean, None] = None, - reply: base.Boolean = False) -> typing.List[Message]: + async def answer_media_group( + self, + media: typing.Union[MediaGroup, typing.List], + disable_notification: typing.Union[base.Boolean, None] = None, + reply: base.Boolean = False, + ) -> typing.List[Message]: """ Use this method to send a group of photos or videos as an album. @@ -673,20 +709,24 @@ class Message(base.TelegramObject): :return: On success, an array of the sent Messages is returned. :rtype: typing.List[types.Message] """ - return await self.bot.send_media_group(self.chat.id, - media=media, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None) + return await self.bot.send_media_group( + self.chat.id, + media=media, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + ) - async def answer_location(self, - latitude: base.Float, longitude: base.Float, - live_period: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_location( + self, + latitude: base.Float, + longitude: base.Float, + live_period: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send point on the map. @@ -708,24 +748,29 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_location(chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - live_period=live_period, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_location( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + live_period=live_period, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_venue(self, - latitude: base.Float, longitude: base.Float, - title: base.String, address: base.String, - foursquare_id: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_venue( + self, + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send information about a venue. @@ -751,24 +796,29 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_venue(chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_venue( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_contact(self, phone_number: base.String, - first_name: base.String, last_name: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_contact( + self, + phone_number: base.String, + first_name: base.String, + last_name: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send phone contacts. @@ -790,20 +840,25 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_contact(chat_id=self.chat.id, - phone_number=phone_number, - first_name=first_name, last_name=last_name, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_contact( + chat_id=self.chat.id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def answer_sticker(self, sticker: typing.Union[base.InputFile, base.String], - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = False) -> Message: + async def answer_sticker( + self, + sticker: typing.Union[base.InputFile, base.String], + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = False, + ) -> Message: """ Use this method to send .webp stickers. @@ -821,21 +876,25 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_sticker(chat_id=self.chat.id, - sticker=sticker, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_sticker( + chat_id=self.chat.id, + sticker=sticker, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply(self, text: base.String, - parse_mode: typing.Union[base.String, None] = None, - disable_web_page_preview: typing.Union[base.Boolean, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply( + self, + text: base.String, + parse_mode: typing.Union[base.String, None] = None, + disable_web_page_preview: typing.Union[base.Boolean, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Reply to this message @@ -857,23 +916,27 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.send_message(chat_id=self.chat.id, - text=text, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_message( + chat_id=self.chat.id, + text=text, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_photo(self, photo: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_photo( + self, + photo: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send photos. @@ -897,26 +960,30 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.send_photo(chat_id=self.chat.id, - photo=photo, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_photo( + chat_id=self.chat.id, + photo=photo, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_audio(self, audio: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - duration: typing.Union[base.Integer, None] = None, - performer: typing.Union[base.String, None] = None, - title: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_audio( + self, + audio: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + duration: typing.Union[base.Integer, None] = None, + performer: typing.Union[base.String, None] = None, + title: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send audio files, if you want Telegram clients to display them in the music player. Your audio must be in the .mp3 format. @@ -948,30 +1015,34 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_audio(chat_id=self.chat.id, - audio=audio, - caption=caption, - parse_mode=parse_mode, - duration=duration, - performer=performer, - title=title, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_audio( + chat_id=self.chat.id, + audio=audio, + caption=caption, + parse_mode=parse_mode, + duration=duration, + performer=performer, + title=title, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def send_animation(self, animation: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def send_animation( + self, + animation: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). @@ -1008,35 +1079,41 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - warn_deprecated('"Message.send_animation" method will be removed in 2.3 version.\n' - 'Use "Message.reply_animation" instead.', - stacklevel=8) + warn_deprecated( + '"Message.send_animation" method will be removed in 2.3 version.\n' + 'Use "Message.reply_animation" instead.', + stacklevel=8, + ) - return await self.bot.send_animation(self.chat.id, - animation=animation, - duration=duration, - width=width, - height=height, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_animation( + self.chat.id, + animation=animation, + duration=duration, + width=width, + height=height, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_animation(self, animation: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_animation( + self, + animation: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + thumb: typing.Union[typing.Union[base.InputFile, base.String], None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send animation files (GIF or H.264/MPEG-4 AVC video without sound). @@ -1073,27 +1150,31 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.send_animation(self.chat.id, - animation=animation, - duration=duration, - width=width, - height=height, - thumb=thumb, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_animation( + self.chat.id, + animation=animation, + duration=duration, + width=width, + height=height, + thumb=thumb, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_document(self, document: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_document( + self, + document: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send general files. @@ -1118,26 +1199,30 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_document(chat_id=self.chat.id, - document=document, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_document( + chat_id=self.chat.id, + document=document, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_video(self, video: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - width: typing.Union[base.Integer, None] = None, - height: typing.Union[base.Integer, None] = None, - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_video( + self, + video: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + width: typing.Union[base.Integer, None] = None, + height: typing.Union[base.Integer, None] = None, + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -1167,27 +1252,31 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_video(chat_id=self.chat.id, - video=video, - duration=duration, - width=width, - height=height, - caption=caption, - parse_mode=parse_mode, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_video( + chat_id=self.chat.id, + video=video, + duration=duration, + width=width, + height=height, + caption=caption, + parse_mode=parse_mode, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_voice(self, voice: typing.Union[base.InputFile, base.String], - caption: typing.Union[base.String, None] = None, - parse_mode: typing.Union[base.String, None] = None, - duration: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_voice( + self, + voice: typing.Union[base.InputFile, base.String], + caption: typing.Union[base.String, None] = None, + parse_mode: typing.Union[base.String, None] = None, + duration: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. @@ -1216,24 +1305,28 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_voice(chat_id=self.chat.id, - voice=voice, - caption=caption, - parse_mode=parse_mode, - duration=duration, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_voice( + chat_id=self.chat.id, + voice=voice, + caption=caption, + parse_mode=parse_mode, + duration=duration, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_video_note(self, video_note: typing.Union[base.InputFile, base.String], - duration: typing.Union[base.Integer, None] = None, - length: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_video_note( + self, + video_note: typing.Union[base.InputFile, base.String], + duration: typing.Union[base.Integer, None] = None, + length: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ As of v.4.0, Telegram clients support rounded square mp4 videos of up to 1 minute long. Use this method to send video messages. @@ -1256,17 +1349,22 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_video_note(chat_id=self.chat.id, - video_note=video_note, - duration=duration, - length=length, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_video_note( + chat_id=self.chat.id, + video_note=video_note, + duration=duration, + length=length, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_media_group(self, media: typing.Union[MediaGroup, typing.List], - disable_notification: typing.Union[base.Boolean, None] = None, - reply: base.Boolean = True) -> typing.List[Message]: + async def reply_media_group( + self, + media: typing.Union[MediaGroup, typing.List], + disable_notification: typing.Union[base.Boolean, None] = None, + reply: base.Boolean = True, + ) -> typing.List[Message]: """ Use this method to send a group of photos or videos as an album. @@ -1280,20 +1378,24 @@ class Message(base.TelegramObject): :return: On success, an array of the sent Messages is returned. :rtype: typing.List[types.Message] """ - return await self.bot.send_media_group(self.chat.id, - media=media, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None) + return await self.bot.send_media_group( + self.chat.id, + media=media, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + ) - async def reply_location(self, - latitude: base.Float, longitude: base.Float, - live_period: typing.Union[base.Integer, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_location( + self, + latitude: base.Float, + longitude: base.Float, + live_period: typing.Union[base.Integer, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send point on the map. @@ -1315,24 +1417,29 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_location(chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - live_period=live_period, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_location( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + live_period=live_period, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def send_venue(self, - latitude: base.Float, longitude: base.Float, - title: base.String, address: base.String, - foursquare_id: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def send_venue( + self, + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send information about a venue. @@ -1358,30 +1465,37 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - warn_deprecated('"Message.send_venue" method will be removed in 2.3 version.\n' - 'Use "Message.reply_venue" instead.', - stacklevel=8) + warn_deprecated( + '"Message.send_venue" method will be removed in 2.3 version.\n' + 'Use "Message.reply_venue" instead.', + stacklevel=8, + ) - return await self.bot.send_venue(chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_venue( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_venue(self, - latitude: base.Float, longitude: base.Float, - title: base.String, address: base.String, - foursquare_id: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_venue( + self, + latitude: base.Float, + longitude: base.Float, + title: base.String, + address: base.String, + foursquare_id: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send information about a venue. @@ -1407,24 +1521,29 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_venue(chat_id=self.chat.id, - latitude=latitude, - longitude=longitude, - title=title, - address=address, - foursquare_id=foursquare_id, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_venue( + chat_id=self.chat.id, + latitude=latitude, + longitude=longitude, + title=title, + address=address, + foursquare_id=foursquare_id, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def send_contact(self, phone_number: base.String, - first_name: base.String, last_name: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def send_contact( + self, + phone_number: base.String, + first_name: base.String, + last_name: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send phone contacts. @@ -1446,25 +1565,33 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - warn_deprecated('"Message.send_contact" method will be removed in 2.3 version.\n' - 'Use "Message.reply_contact" instead.', - stacklevel=8) + warn_deprecated( + '"Message.send_contact" method will be removed in 2.3 version.\n' + 'Use "Message.reply_contact" instead.', + stacklevel=8, + ) - return await self.bot.send_contact(chat_id=self.chat.id, - phone_number=phone_number, - first_name=first_name, last_name=last_name, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_contact( + chat_id=self.chat.id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_contact(self, phone_number: base.String, - first_name: base.String, last_name: typing.Union[base.String, None] = None, - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_contact( + self, + phone_number: base.String, + first_name: base.String, + last_name: typing.Union[base.String, None] = None, + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send phone contacts. @@ -1486,20 +1613,25 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_contact(chat_id=self.chat.id, - phone_number=phone_number, - first_name=first_name, last_name=last_name, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_contact( + chat_id=self.chat.id, + phone_number=phone_number, + first_name=first_name, + last_name=last_name, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def reply_sticker(self, sticker: typing.Union[base.InputFile, base.String], - disable_notification: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - ReplyKeyboardMarkup, - ReplyKeyboardRemove, - ForceReply, None] = None, - reply: base.Boolean = True) -> Message: + async def reply_sticker( + self, + sticker: typing.Union[base.InputFile, base.String], + disable_notification: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[ + InlineKeyboardMarkup, ReplyKeyboardMarkup, ReplyKeyboardRemove, ForceReply, None + ] = None, + reply: base.Boolean = True, + ) -> Message: """ Use this method to send .webp stickers. @@ -1517,14 +1649,19 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned. :rtype: :obj:`types.Message` """ - return await self.bot.send_sticker(chat_id=self.chat.id, - sticker=sticker, - disable_notification=disable_notification, - reply_to_message_id=self.message_id if reply else None, - reply_markup=reply_markup) + return await self.bot.send_sticker( + chat_id=self.chat.id, + sticker=sticker, + disable_notification=disable_notification, + reply_to_message_id=self.message_id if reply else None, + reply_markup=reply_markup, + ) - async def forward(self, chat_id: typing.Union[base.Integer, base.String], - disable_notification: typing.Union[base.Boolean, None] = None) -> Message: + async def forward( + self, + chat_id: typing.Union[base.Integer, base.String], + disable_notification: typing.Union[base.Boolean, None] = None, + ) -> Message: """ Forward this message @@ -1537,13 +1674,17 @@ class Message(base.TelegramObject): :return: On success, the sent Message is returned :rtype: :obj:`types.Message` """ - return await self.bot.forward_message(chat_id, self.chat.id, self.message_id, disable_notification) + return await self.bot.forward_message( + chat_id, self.chat.id, self.message_id, disable_notification + ) - async def edit_text(self, text: base.String, - parse_mode: typing.Union[base.String, None] = None, - disable_web_page_preview: typing.Union[base.Boolean, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - None] = None) -> typing.Union[Message, base.Boolean]: + async def edit_text( + self, + text: base.String, + parse_mode: typing.Union[base.String, None] = None, + disable_web_page_preview: typing.Union[base.Boolean, None] = None, + reply_markup: typing.Union[InlineKeyboardMarkup, None] = None, + ) -> typing.Union[Message, base.Boolean]: """ Use this method to edit text and game messages sent by the bot or via the bot (for inline bots). @@ -1562,16 +1703,21 @@ class Message(base.TelegramObject): the edited Message is returned, otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.edit_message_text(text=text, - chat_id=self.chat.id, message_id=self.message_id, - parse_mode=parse_mode, - disable_web_page_preview=disable_web_page_preview, - reply_markup=reply_markup) + return await self.bot.edit_message_text( + text=text, + chat_id=self.chat.id, + message_id=self.message_id, + parse_mode=parse_mode, + disable_web_page_preview=disable_web_page_preview, + reply_markup=reply_markup, + ) - async def edit_caption(self, caption: base.String, - parse_mode: typing.Union[base.String, None] = None, - reply_markup: typing.Union[InlineKeyboardMarkup, - None] = None) -> typing.Union[Message, base.Boolean]: + async def edit_caption( + self, + caption: base.String, + parse_mode: typing.Union[base.String, None] = None, + reply_markup: typing.Union[InlineKeyboardMarkup, None] = None, + ) -> typing.Union[Message, base.Boolean]: """ Use this method to edit captions of messages sent by the bot or via the bot (for inline bots). @@ -1588,12 +1734,17 @@ class Message(base.TelegramObject): otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.edit_message_caption(chat_id=self.chat.id, message_id=self.message_id, caption=caption, - parse_mode=parse_mode, reply_markup=reply_markup) + return await self.bot.edit_message_caption( + chat_id=self.chat.id, + message_id=self.message_id, + caption=caption, + parse_mode=parse_mode, + reply_markup=reply_markup, + ) - async def edit_media(self, media: InputMedia, - reply_markup: typing.Union[InlineKeyboardMarkup, - None] = None) -> typing.Union[Message, base.Boolean]: + async def edit_media( + self, media: InputMedia, reply_markup: typing.Union[InlineKeyboardMarkup, None] = None + ) -> typing.Union[Message, base.Boolean]: """ Use this method to edit audio, document, photo, or video messages. If a message is a part of a message album, then it can be edited only to a photo or a video. @@ -1614,12 +1765,16 @@ class Message(base.TelegramObject): otherwise True is returned :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.edit_message_media(media=media, chat_id=self.chat.id, message_id=self.message_id, - reply_markup=reply_markup) + return await self.bot.edit_message_media( + media=media, + chat_id=self.chat.id, + message_id=self.message_id, + reply_markup=reply_markup, + ) - async def edit_reply_markup(self, - reply_markup: typing.Union[InlineKeyboardMarkup, - None] = None) -> typing.Union[Message, base.Boolean]: + async def edit_reply_markup( + self, reply_markup: typing.Union[InlineKeyboardMarkup, None] = None + ) -> typing.Union[Message, base.Boolean]: """ Use this method to edit only the reply markup of messages sent by the bot or via the bot (for inline bots). @@ -1631,8 +1786,9 @@ class Message(base.TelegramObject): otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.edit_message_reply_markup(chat_id=self.chat.id, message_id=self.message_id, - reply_markup=reply_markup) + return await self.bot.edit_message_reply_markup( + chat_id=self.chat.id, message_id=self.message_id, reply_markup=reply_markup + ) async def delete_reply_markup(self) -> typing.Union[Message, base.Boolean]: """ @@ -1642,12 +1798,16 @@ class Message(base.TelegramObject): otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.edit_message_reply_markup(chat_id=self.chat.id, message_id=self.message_id) + return await self.bot.edit_message_reply_markup( + chat_id=self.chat.id, message_id=self.message_id + ) - async def edit_live_location(self, latitude: base.Float, - longitude: base.Float, - reply_markup: typing.Union[InlineKeyboardMarkup, - None] = None) -> typing.Union[Message, base.Boolean]: + async def edit_live_location( + self, + latitude: base.Float, + longitude: base.Float, + reply_markup: typing.Union[InlineKeyboardMarkup, None] = None, + ) -> typing.Union[Message, base.Boolean]: """ Use this method to edit live location messages sent by the bot or via the bot (for inline bots). A location can be edited until its live_period expires or editing is explicitly disabled by a call @@ -1665,13 +1825,17 @@ class Message(base.TelegramObject): otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.edit_message_live_location(latitude=latitude, longitude=longitude, - chat_id=self.chat.id, message_id=self.message_id, - reply_markup=reply_markup) + return await self.bot.edit_message_live_location( + latitude=latitude, + longitude=longitude, + chat_id=self.chat.id, + message_id=self.message_id, + reply_markup=reply_markup, + ) - async def stop_live_location(self, - reply_markup: typing.Union[InlineKeyboardMarkup, - None] = None) -> typing.Union[Message, base.Boolean]: + async def stop_live_location( + self, reply_markup: typing.Union[InlineKeyboardMarkup, None] = None + ) -> typing.Union[Message, base.Boolean]: """ Use this method to stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires. @@ -1684,8 +1848,9 @@ class Message(base.TelegramObject): otherwise True is returned. :rtype: :obj:`typing.Union[types.Message, base.Boolean]` """ - return await self.bot.stop_message_live_location(chat_id=self.chat.id, message_id=self.message_id, - reply_markup=reply_markup) + return await self.bot.stop_message_live_location( + chat_id=self.chat.id, message_id=self.message_id, reply_markup=reply_markup + ) async def delete(self) -> base.Boolean: """ @@ -1704,7 +1869,9 @@ class Message(base.TelegramObject): """ return await self.bot.delete_message(self.chat.id, self.message_id) - async def pin(self, disable_notification: typing.Union[base.Boolean, None] = None) -> base.Boolean: + async def pin( + self, disable_notification: typing.Union[base.Boolean, None] = None + ) -> base.Boolean: """ Use this method to pin a message in a supergroup. The bot must be an administrator in the chat for this to work and must have the appropriate admin rights. @@ -1751,6 +1918,7 @@ class ContentType(helper.Helper): :key: UNKNOWN :key: ANY """ + mode = helper.HelperMode.snake_case TEXT = helper.Item() # text @@ -1813,6 +1981,7 @@ class ContentTypes(helper.Helper): :key: UNKNOWN :key: ANY """ + mode = helper.HelperMode.snake_case TEXT = helper.ListItem() # text diff --git a/aiogram/types/message_entity.py b/aiogram/types/message_entity.py index abb4f060..2c0712e5 100644 --- a/aiogram/types/message_entity.py +++ b/aiogram/types/message_entity.py @@ -12,6 +12,7 @@ class MessageEntity(base.TelegramObject): https://core.telegram.org/bots/api#messageentity """ + type: base.String = fields.Field() offset: base.Integer = fields.Field() length: base.Integer = fields.Field() @@ -25,16 +26,16 @@ class MessageEntity(base.TelegramObject): :param text: full text :return: part of text """ - if sys.maxunicode == 0xffff: - return text[self.offset:self.offset + self.length] + if sys.maxunicode == 0xFFFF: + return text[self.offset : self.offset + self.length] if not isinstance(text, bytes): - entity_text = text.encode('utf-16-le') + entity_text = text.encode("utf-16-le") else: entity_text = text - entity_text = entity_text[self.offset * 2:(self.offset + self.length) * 2] - return entity_text.decode('utf-16-le') + entity_text = entity_text[self.offset * 2 : (self.offset + self.length) * 2] + return entity_text.decode("utf-16-le") def parse(self, text, as_html=True): """ @@ -95,6 +96,7 @@ class MessageEntityType(helper.Helper): :key: TEXT_LINK :key: TEXT_MENTION """ + mode = helper.HelperMode.snake_case MENTION = helper.Item() # mention - @username diff --git a/aiogram/types/mixins.py b/aiogram/types/mixins.py index f11a1760..40c1093d 100644 --- a/aiogram/types/mixins.py +++ b/aiogram/types/mixins.py @@ -7,7 +7,9 @@ class Downloadable: Mixin for files """ - async def download(self, destination=None, timeout=30, chunk_size=65536, seek=True, make_dirs=True): + async def download( + self, destination=None, timeout=30, chunk_size=65536, seek=True, make_dirs=True + ): """ Download file @@ -31,8 +33,13 @@ class Downloadable: if is_path and make_dirs: os.makedirs(os.path.dirname(destination), exist_ok=True) - return await self.bot.download_file(file_path=file.file_path, destination=destination, timeout=timeout, - chunk_size=chunk_size, seek=seek) + return await self.bot.download_file( + file_path=file.file_path, + destination=destination, + timeout=timeout, + chunk_size=chunk_size, + seek=seek, + ) async def get_file(self): """ @@ -40,7 +47,7 @@ class Downloadable: :return: :obj:`aiogram.types.File` """ - if hasattr(self, 'file_path'): + if hasattr(self, "file_path"): return self else: return await self.bot.get_file(self.file_id) diff --git a/aiogram/types/order_info.py b/aiogram/types/order_info.py index 73bac9e2..535cdd0f 100644 --- a/aiogram/types/order_info.py +++ b/aiogram/types/order_info.py @@ -9,6 +9,7 @@ class OrderInfo(base.TelegramObject): https://core.telegram.org/bots/api#orderinfo """ + name: base.String = fields.Field() phone_number: base.String = fields.Field() email: base.String = fields.Field() diff --git a/aiogram/types/passport_element_error.py b/aiogram/types/passport_element_error.py index f673ba16..aff60775 100644 --- a/aiogram/types/passport_element_error.py +++ b/aiogram/types/passport_element_error.py @@ -28,10 +28,17 @@ class PassportElementErrorDataField(PassportElementError): field_name: base.String = fields.Field() data_hash: base.String = fields.Field() - def __init__(self, source: base.String, type: base.String, field_name: base.String, - data_hash: base.String, message: base.String): - super(PassportElementErrorDataField, self).__init__(source=source, type=type, field_name=field_name, - data_hash=data_hash, message=message) + def __init__( + self, + source: base.String, + type: base.String, + field_name: base.String, + data_hash: base.String, + message: base.String, + ): + super(PassportElementErrorDataField, self).__init__( + source=source, type=type, field_name=field_name, data_hash=data_hash, message=message + ) class PassportElementErrorFile(PassportElementError): @@ -44,9 +51,12 @@ class PassportElementErrorFile(PassportElementError): file_hash: base.String = fields.Field() - def __init__(self, source: base.String, type: base.String, file_hash: base.String, message: base.String): - super(PassportElementErrorFile, self).__init__(source=source, type=type, file_hash=file_hash, - message=message) + def __init__( + self, source: base.String, type: base.String, file_hash: base.String, message: base.String + ): + super(PassportElementErrorFile, self).__init__( + source=source, type=type, file_hash=file_hash, message=message + ) class PassportElementErrorFiles(PassportElementError): @@ -59,10 +69,16 @@ class PassportElementErrorFiles(PassportElementError): file_hashes: typing.List[base.String] = fields.ListField() - def __init__(self, source: base.String, type: base.String, file_hashes: typing.List[base.String], - message: base.String): - super(PassportElementErrorFiles, self).__init__(source=source, type=type, file_hashes=file_hashes, - message=message) + def __init__( + self, + source: base.String, + type: base.String, + file_hashes: typing.List[base.String], + message: base.String, + ): + super(PassportElementErrorFiles, self).__init__( + source=source, type=type, file_hashes=file_hashes, message=message + ) class PassportElementErrorFrontSide(PassportElementError): @@ -75,9 +91,12 @@ class PassportElementErrorFrontSide(PassportElementError): file_hash: base.String = fields.Field() - def __init__(self, source: base.String, type: base.String, file_hash: base.String, message: base.String): - super(PassportElementErrorFrontSide, self).__init__(source=source, type=type, file_hash=file_hash, - message=message) + def __init__( + self, source: base.String, type: base.String, file_hash: base.String, message: base.String + ): + super(PassportElementErrorFrontSide, self).__init__( + source=source, type=type, file_hash=file_hash, message=message + ) class PassportElementErrorReverseSide(PassportElementError): @@ -90,9 +109,12 @@ class PassportElementErrorReverseSide(PassportElementError): file_hash: base.String = fields.Field() - def __init__(self, source: base.String, type: base.String, file_hash: base.String, message: base.String): - super(PassportElementErrorReverseSide, self).__init__(source=source, type=type, file_hash=file_hash, - message=message) + def __init__( + self, source: base.String, type: base.String, file_hash: base.String, message: base.String + ): + super(PassportElementErrorReverseSide, self).__init__( + source=source, type=type, file_hash=file_hash, message=message + ) class PassportElementErrorSelfie(PassportElementError): @@ -105,6 +127,9 @@ class PassportElementErrorSelfie(PassportElementError): file_hash: base.String = fields.Field() - def __init__(self, source: base.String, type: base.String, file_hash: base.String, message: base.String): - super(PassportElementErrorSelfie, self).__init__(source=source, type=type, file_hash=file_hash, - message=message) + def __init__( + self, source: base.String, type: base.String, file_hash: base.String, message: base.String + ): + super(PassportElementErrorSelfie, self).__init__( + source=source, type=type, file_hash=file_hash, message=message + ) diff --git a/aiogram/types/photo_size.py b/aiogram/types/photo_size.py index c7ba59b6..72c8c52b 100644 --- a/aiogram/types/photo_size.py +++ b/aiogram/types/photo_size.py @@ -9,6 +9,7 @@ class PhotoSize(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#photosize """ + file_id: base.String = fields.Field() width: base.Integer = fields.Field() height: base.Integer = fields.Field() diff --git a/aiogram/types/pre_checkout_query.py b/aiogram/types/pre_checkout_query.py index 4786ce48..0da1ded8 100644 --- a/aiogram/types/pre_checkout_query.py +++ b/aiogram/types/pre_checkout_query.py @@ -17,8 +17,9 @@ class PreCheckoutQuery(base.TelegramObject): https://core.telegram.org/bots/api#precheckoutquery """ + id: base.String = fields.Field() - from_user: User = fields.Field(alias='from', base=User) + from_user: User = fields.Field(alias="from", base=User) currency: base.String = fields.Field() total_amount: base.Integer = fields.Field() invoice_payload: base.String = fields.Field() diff --git a/aiogram/types/reply_keyboard.py b/aiogram/types/reply_keyboard.py index 8eda21f9..8d6a8578 100644 --- a/aiogram/types/reply_keyboard.py +++ b/aiogram/types/reply_keyboard.py @@ -10,27 +10,37 @@ class ReplyKeyboardMarkup(base.TelegramObject): https://core.telegram.org/bots/api#replykeyboardmarkup """ - keyboard: 'typing.List[typing.List[KeyboardButton]]' = fields.ListOfLists(base='KeyboardButton', default=[]) + + keyboard: "typing.List[typing.List[KeyboardButton]]" = fields.ListOfLists( + base="KeyboardButton", default=[] + ) resize_keyboard: base.Boolean = fields.Field() one_time_keyboard: base.Boolean = fields.Field() selective: base.Boolean = fields.Field() - def __init__(self, keyboard: 'typing.List[typing.List[KeyboardButton]]' = None, - resize_keyboard: base.Boolean = None, - one_time_keyboard: base.Boolean = None, - selective: base.Boolean = None, - row_width: base.Integer = 3): - super(ReplyKeyboardMarkup, self).__init__(keyboard=keyboard, resize_keyboard=resize_keyboard, - one_time_keyboard=one_time_keyboard, selective=selective, - conf={'row_width': row_width}) + def __init__( + self, + keyboard: "typing.List[typing.List[KeyboardButton]]" = None, + resize_keyboard: base.Boolean = None, + one_time_keyboard: base.Boolean = None, + selective: base.Boolean = None, + row_width: base.Integer = 3, + ): + super(ReplyKeyboardMarkup, self).__init__( + keyboard=keyboard, + resize_keyboard=resize_keyboard, + one_time_keyboard=one_time_keyboard, + selective=selective, + conf={"row_width": row_width}, + ) @property def row_width(self): - return self.conf.get('row_width', 3) + return self.conf.get("row_width", 3) @row_width.setter def row_width(self, value): - self.conf['row_width'] = value + self.conf["row_width"] = value def add(self, *args): """ @@ -86,16 +96,20 @@ class KeyboardButton(base.TelegramObject): https://core.telegram.org/bots/api#keyboardbutton """ + text: base.String = fields.Field() request_contact: base.Boolean = fields.Field() request_location: base.Boolean = fields.Field() - def __init__(self, text: base.String, - request_contact: base.Boolean = None, - request_location: base.Boolean = None): - super(KeyboardButton, self).__init__(text=text, - request_contact=request_contact, - request_location=request_location) + def __init__( + self, + text: base.String, + request_contact: base.Boolean = None, + request_location: base.Boolean = None, + ): + super(KeyboardButton, self).__init__( + text=text, request_contact=request_contact, request_location=request_location + ) class ReplyKeyboardRemove(base.TelegramObject): @@ -104,6 +118,7 @@ class ReplyKeyboardRemove(base.TelegramObject): https://core.telegram.org/bots/api#replykeyboardremove """ + remove_keyboard: base.Boolean = fields.Field(default=True) selective: base.Boolean = fields.Field() diff --git a/aiogram/types/response_parameters.py b/aiogram/types/response_parameters.py index 8f40cc11..43a9b49d 100644 --- a/aiogram/types/response_parameters.py +++ b/aiogram/types/response_parameters.py @@ -8,5 +8,6 @@ class ResponseParameters(base.TelegramObject): https://core.telegram.org/bots/api#responseparameters """ + migrate_to_chat_id: base.Integer = fields.Field() retry_after: base.Integer = fields.Field() diff --git a/aiogram/types/shipping_address.py b/aiogram/types/shipping_address.py index 8c8d3418..48119bcf 100644 --- a/aiogram/types/shipping_address.py +++ b/aiogram/types/shipping_address.py @@ -8,6 +8,7 @@ class ShippingAddress(base.TelegramObject): https://core.telegram.org/bots/api#shippingaddress """ + country_code: base.String = fields.Field() state: base.String = fields.Field() city: base.String = fields.Field() diff --git a/aiogram/types/shipping_option.py b/aiogram/types/shipping_option.py index 4ec915c5..243b8163 100644 --- a/aiogram/types/shipping_option.py +++ b/aiogram/types/shipping_option.py @@ -11,11 +11,14 @@ class ShippingOption(base.TelegramObject): https://core.telegram.org/bots/api#shippingoption """ + id: base.String = fields.Field() title: base.String = fields.Field() prices: typing.List[LabeledPrice] = fields.ListField(base=LabeledPrice) - def __init__(self, id: base.String, title: base.String, prices: typing.List[LabeledPrice] = None): + def __init__( + self, id: base.String, title: base.String, prices: typing.List[LabeledPrice] = None + ): if prices is None: prices = [] diff --git a/aiogram/types/shipping_query.py b/aiogram/types/shipping_query.py index 0de540a3..c7935c2b 100644 --- a/aiogram/types/shipping_query.py +++ b/aiogram/types/shipping_query.py @@ -10,8 +10,9 @@ class ShippingQuery(base.TelegramObject): https://core.telegram.org/bots/api#shippingquery """ + id: base.String = fields.Field() - from_user: User = fields.Field(alias='from', base=User) + from_user: User = fields.Field(alias="from", base=User) invoice_payload: base.String = fields.Field() shipping_address: ShippingAddress = fields.Field(base=ShippingAddress) diff --git a/aiogram/types/sticker.py b/aiogram/types/sticker.py index b2fd7ef6..f3c4591c 100644 --- a/aiogram/types/sticker.py +++ b/aiogram/types/sticker.py @@ -11,6 +11,7 @@ class Sticker(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#sticker """ + file_id: base.String = fields.Field() width: base.Integer = fields.Field() height: base.Integer = fields.Field() diff --git a/aiogram/types/sticker_set.py b/aiogram/types/sticker_set.py index 9d302bae..eed073ab 100644 --- a/aiogram/types/sticker_set.py +++ b/aiogram/types/sticker_set.py @@ -11,6 +11,7 @@ class StickerSet(base.TelegramObject): https://core.telegram.org/bots/api#stickerset """ + name: base.String = fields.Field() title: base.String = fields.Field() contains_masks: base.Boolean = fields.Field() diff --git a/aiogram/types/successful_payment.py b/aiogram/types/successful_payment.py index 41cd32da..bca329cc 100644 --- a/aiogram/types/successful_payment.py +++ b/aiogram/types/successful_payment.py @@ -9,6 +9,7 @@ class SuccessfulPayment(base.TelegramObject): https://core.telegram.org/bots/api#successfulpayment """ + currency: base.String = fields.Field() total_amount: base.Integer = fields.Field() invoice_payload: base.String = fields.Field() diff --git a/aiogram/types/update.py b/aiogram/types/update.py index 9f8ce0fb..7f95b597 100644 --- a/aiogram/types/update.py +++ b/aiogram/types/update.py @@ -19,6 +19,7 @@ class Update(base.TelegramObject): https://core.telegram.org/bots/api#update """ + update_id: base.Integer = fields.Field() message: Message = fields.Field(base=Message) edited_message: Message = fields.Field(base=Message) @@ -47,6 +48,7 @@ class AllowedUpdates(helper.Helper): Example: >>> bot.get_updates(allowed_updates=AllowedUpdates.MESSAGE + AllowedUpdates.EDITED_MESSAGE) """ + mode = helper.HelperMode.snake_case MESSAGE = helper.ListItem() # message diff --git a/aiogram/types/user.py b/aiogram/types/user.py index 441c275f..ca260626 100644 --- a/aiogram/types/user.py +++ b/aiogram/types/user.py @@ -13,6 +13,7 @@ class User(base.TelegramObject): https://core.telegram.org/bots/api#user """ + id: base.Integer = fields.Field() is_bot: base.Boolean = fields.Field() first_name: base.String = fields.Field() @@ -29,7 +30,7 @@ class User(base.TelegramObject): """ full_name = self.first_name if self.last_name: - full_name += ' ' + self.last_name + full_name += " " + self.last_name return full_name @property @@ -41,7 +42,7 @@ class User(base.TelegramObject): :return: str """ if self.username: - return '@' + self.username + return "@" + self.username return self.full_name @property @@ -53,16 +54,16 @@ class User(base.TelegramObject): """ if not self.language_code: return None - if not hasattr(self, '_locale'): - setattr(self, '_locale', babel.core.Locale.parse(self.language_code, sep='-')) - return getattr(self, '_locale') + if not hasattr(self, "_locale"): + setattr(self, "_locale", babel.core.Locale.parse(self.language_code, sep="-")) + return getattr(self, "_locale") @property def url(self): return f"tg://user?id={self.id}" def get_mention(self, name=None, as_html=None): - if as_html is None and self.bot.parse_mode and self.bot.parse_mode.lower() == 'html': + if as_html is None and self.bot.parse_mode and self.bot.parse_mode.lower() == "html": as_html = True if name is None: diff --git a/aiogram/types/user_profile_photos.py b/aiogram/types/user_profile_photos.py index 259f669c..21304da3 100644 --- a/aiogram/types/user_profile_photos.py +++ b/aiogram/types/user_profile_photos.py @@ -11,5 +11,6 @@ class UserProfilePhotos(base.TelegramObject): https://core.telegram.org/bots/api#userprofilephotos """ + total_count: base.Integer = fields.Field() photos: typing.List[typing.List[PhotoSize]] = fields.ListOfLists(base=PhotoSize) diff --git a/aiogram/types/venue.py b/aiogram/types/venue.py index f7b2a277..1335f0ad 100644 --- a/aiogram/types/venue.py +++ b/aiogram/types/venue.py @@ -9,6 +9,7 @@ class Venue(base.TelegramObject): https://core.telegram.org/bots/api#venue """ + location: Location = fields.Field(base=Location) title: base.String = fields.Field() address: base.String = fields.Field() diff --git a/aiogram/types/video.py b/aiogram/types/video.py index bf5187cd..996eb55f 100644 --- a/aiogram/types/video.py +++ b/aiogram/types/video.py @@ -10,6 +10,7 @@ class Video(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#video """ + file_id: base.String = fields.Field() width: base.Integer = fields.Field() height: base.Integer = fields.Field() diff --git a/aiogram/types/video_note.py b/aiogram/types/video_note.py index 9665b6bc..ea7bd5c1 100644 --- a/aiogram/types/video_note.py +++ b/aiogram/types/video_note.py @@ -10,6 +10,7 @@ class VideoNote(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#videonote """ + file_id: base.String = fields.Field() length: base.Integer = fields.Field() duration: base.Integer = fields.Field() diff --git a/aiogram/types/voice.py b/aiogram/types/voice.py index 621f2247..df2a56a4 100644 --- a/aiogram/types/voice.py +++ b/aiogram/types/voice.py @@ -9,6 +9,7 @@ class Voice(base.TelegramObject, mixins.Downloadable): https://core.telegram.org/bots/api#voice """ + file_id: base.String = fields.Field() duration: base.Integer = fields.Field() mime_type: base.String = fields.Field() diff --git a/aiogram/types/webhook_info.py b/aiogram/types/webhook_info.py index 995d3aaa..82424358 100644 --- a/aiogram/types/webhook_info.py +++ b/aiogram/types/webhook_info.py @@ -10,6 +10,7 @@ class WebhookInfo(base.TelegramObject): https://core.telegram.org/bots/api#webhookinfo """ + url: base.String = fields.Field() has_custom_certificate: base.Boolean = fields.Field() pending_update_count: base.Integer = fields.Field() diff --git a/aiogram/utils/auth_widget.py b/aiogram/utils/auth_widget.py index b9084eb1..24a18698 100644 --- a/aiogram/utils/auth_widget.py +++ b/aiogram/utils/auth_widget.py @@ -18,10 +18,10 @@ def generate_hash(data: dict, token: str) -> str: :return: """ secret = hashlib.sha256() - secret.update(token.encode('utf-8')) + secret.update(token.encode("utf-8")) sorted_params = collections.OrderedDict(sorted(data.items())) - msg = '\n'.join("{}={}".format(k, v) for k, v in sorted_params.items() if k != 'hash') - return hmac.new(secret.digest(), msg.encode('utf-8'), digestmod=hashlib.sha256).hexdigest() + msg = "\n".join("{}={}".format(k, v) for k, v in sorted_params.items() if k != "hash") + return hmac.new(secret.digest(), msg.encode("utf-8"), digestmod=hashlib.sha256).hexdigest() def check_token(data: dict, token: str) -> bool: @@ -32,5 +32,5 @@ def check_token(data: dict, token: str) -> bool: :param token: :return: """ - param_hash = data.get('hash', '') or '' + param_hash = data.get("hash", "") or "" return param_hash == generate_hash(data, token) diff --git a/aiogram/utils/callback_data.py b/aiogram/utils/callback_data.py index 916d08c4..81ef1cc7 100644 --- a/aiogram/utils/callback_data.py +++ b/aiogram/utils/callback_data.py @@ -26,15 +26,15 @@ class CallbackData: Callback data factory """ - def __init__(self, prefix, *parts, sep=':'): + def __init__(self, prefix, *parts, sep=":"): if not isinstance(prefix, str): raise TypeError(f"Prefix must be instance of str not {type(prefix).__name__}") elif not prefix: - raise ValueError('Prefix can\'t be empty') + raise ValueError("Prefix can't be empty") elif sep in prefix: raise ValueError(f"Separator '{sep}' can't be used in prefix") elif not parts: - raise TypeError('Parts is not passed!') + raise TypeError("Parts is not passed!") self.prefix = prefix self.sep = sep @@ -72,11 +72,11 @@ class CallbackData: data.append(value) if args or kwargs: - raise TypeError('Too many arguments is passed!') + raise TypeError("Too many arguments is passed!") callback_data = self.sep.join(data) if len(callback_data) > 64: - raise ValueError('Resulted callback data is too long!') + raise ValueError("Resulted callback data is too long!") return callback_data @@ -91,9 +91,9 @@ class CallbackData: if prefix != self.prefix: raise ValueError("Passed callback data can't be parsed with that prefix.") elif len(parts) != len(self._part_names): - raise ValueError('Invalid parts count!') + raise ValueError("Invalid parts count!") - result = {'@': prefix} + result = {"@": prefix} result.update(zip(self._part_names, parts)) return result @@ -117,7 +117,7 @@ class CallbackDataFilter(Filter): @classmethod def validate(cls, full_config: typing.Dict[str, typing.Any]): - raise ValueError('That filter can\'t be used in filters factory!') + raise ValueError("That filter can't be used in filters factory!") async def check(self, query: types.CallbackQuery): try: @@ -132,4 +132,4 @@ class CallbackDataFilter(Filter): else: if value != data.get(key): return False - return {'callback_data': data} + return {"callback_data": data} diff --git a/aiogram/utils/deprecated.py b/aiogram/utils/deprecated.py index 1ea2561d..65b62a22 100644 --- a/aiogram/utils/deprecated.py +++ b/aiogram/utils/deprecated.py @@ -34,7 +34,7 @@ def deprecated(reason): @functools.wraps(func) def wrapper(*args, **kwargs): warn_deprecated(msg.format(name=func.__name__, reason=reason)) - warnings.simplefilter('default', DeprecationWarning) + warnings.simplefilter("default", DeprecationWarning) return func(*args, **kwargs) return wrapper @@ -70,6 +70,6 @@ def deprecated(reason): def warn_deprecated(message, warning=DeprecationWarning, stacklevel=2): - warnings.simplefilter('always', warning) + warnings.simplefilter("always", warning) warnings.warn(message, category=warning, stacklevel=stacklevel) - warnings.simplefilter('default', warning) + warnings.simplefilter("default", warning) diff --git a/aiogram/utils/exceptions.py b/aiogram/utils/exceptions.py index afd623cc..83352521 100644 --- a/aiogram/utils/exceptions.py +++ b/aiogram/utils/exceptions.py @@ -90,13 +90,13 @@ import time # TODO: Use exceptions detector from `aiograph`. -_PREFIXES = ['error: ', '[error]: ', 'bad request: ', 'conflict: ', 'not found: '] +_PREFIXES = ["error: ", "[error]: ", "bad request: ", "conflict: ", "not found: "] def _clean_message(text): for prefix in _PREFIXES: if text.startswith(prefix): - text = text[len(prefix):] + text = text[len(prefix) :] return (text[0].upper() + text[1:]).strip() @@ -106,7 +106,7 @@ class TelegramAPIError(Exception): class _MatchErrorMixin: - match = '' + match = "" text = None __subclasses = [] @@ -166,67 +166,72 @@ class MessageNotModified(MessageError): """ Will be raised when you try to set new text is equals to current text. """ - match = 'message is not modified' + + match = "message is not modified" class MessageToForwardNotFound(MessageError): """ Will be raised when you try to forward very old or deleted or unknown message. """ - match = 'message to forward not found' + + match = "message to forward not found" class MessageToDeleteNotFound(MessageError): """ Will be raised when you try to delete very old or deleted or unknown message. """ - match = 'message to delete not found' + + match = "message to delete not found" class MessageToReplyNotFound(MessageError): """ Will be raised when you try to reply to very old or deleted or unknown message. """ - match = 'message to reply not found' + + match = "message to reply not found" class MessageIdentifierNotSpecified(MessageError): - match = 'message identifier is not specified' + match = "message identifier is not specified" class MessageTextIsEmpty(MessageError): - match = 'Message text is empty' + match = "Message text is empty" class MessageCantBeEdited(MessageError): - match = 'message can\'t be edited' + match = "message can't be edited" class MessageCantBeDeleted(MessageError): - match = 'message can\'t be deleted' + match = "message can't be deleted" class MessageToEditNotFound(MessageError): - match = 'message to edit not found' + match = "message to edit not found" class MessageIsTooLong(MessageError): - match = 'message is too long' + match = "message is too long" class ToMuchMessages(MessageError): """ Will be raised when you try to send media group with more than 10 items. """ - match = 'Too much messages to send as an album' + + match = "Too much messages to send as an album" class ObjectExpectedAsReplyMarkup(BadRequest): - match = 'object expected as reply markup' + match = "object expected as reply markup" class InlineKeyboardExpected(BadRequest): - match = 'inline keyboard expected' + match = "inline keyboard expected" class PollError(BadRequest): @@ -238,7 +243,7 @@ class PollCantBeStopped(PollError): class PollHasAlreadyBeenClosed(PollError): - match = 'poll has already been closed' + match = "poll has already been closed" class PollsCantBeSentToPrivateChats(PollError): @@ -277,109 +282,112 @@ class MessageWithPollNotFound(PollError, MessageError): """ Will be raised when you try to stop poll with message without poll """ - match = 'message with poll to stop not found' + + match = "message with poll to stop not found" class MessageIsNotAPoll(PollError, MessageError): """ Will be raised when you try to stop poll with message without poll """ - match = 'message is not a poll' + + match = "message is not a poll" class ChatNotFound(BadRequest): - match = 'chat not found' + match = "chat not found" class ChatIdIsEmpty(BadRequest): - match = 'chat_id is empty' + match = "chat_id is empty" class InvalidUserId(BadRequest): - match = 'user_id_invalid' - text = 'Invalid user id' + match = "user_id_invalid" + text = "Invalid user id" class ChatDescriptionIsNotModified(BadRequest): - match = 'chat description is not modified' + match = "chat description is not modified" class InvalidQueryID(BadRequest): - match = 'query is too old and response timeout expired or query id is invalid' + match = "query is too old and response timeout expired or query id is invalid" class InvalidPeerID(BadRequest): - match = 'PEER_ID_INVALID' - text = 'Invalid peer ID' + match = "PEER_ID_INVALID" + text = "Invalid peer ID" class InvalidHTTPUrlContent(BadRequest): - match = 'Failed to get HTTP URL content' + match = "Failed to get HTTP URL content" class ButtonURLInvalid(BadRequest): - match = 'BUTTON_URL_INVALID' - text = 'Button URL invalid' + match = "BUTTON_URL_INVALID" + text = "Button URL invalid" class URLHostIsEmpty(BadRequest): - match = 'URL host is empty' + match = "URL host is empty" class StartParamInvalid(BadRequest): - match = 'START_PARAM_INVALID' - text = 'Start param invalid' + match = "START_PARAM_INVALID" + text = "Start param invalid" class ButtonDataInvalid(BadRequest): - match = 'BUTTON_DATA_INVALID' - text = 'Button data invalid' + match = "BUTTON_DATA_INVALID" + text = "Button data invalid" class WrongFileIdentifier(BadRequest): - match = 'wrong file identifier/HTTP URL specified' + match = "wrong file identifier/HTTP URL specified" class GroupDeactivated(BadRequest): - match = 'group is deactivated' + match = "group is deactivated" class PhotoAsInputFileRequired(BadRequest): """ Will be raised when you try to set chat photo from file ID. """ - match = 'Photo should be uploaded as an InputFile' + + match = "Photo should be uploaded as an InputFile" class InvalidStickersSet(BadRequest): - match = 'STICKERSET_INVALID' - text = 'Stickers set is invalid' + match = "STICKERSET_INVALID" + text = "Stickers set is invalid" class NoStickerInRequest(BadRequest): - match = 'there is no sticker in the request' + match = "there is no sticker in the request" class ChatAdminRequired(BadRequest): - match = 'CHAT_ADMIN_REQUIRED' - text = 'Admin permissions is required!' + match = "CHAT_ADMIN_REQUIRED" + text = "Admin permissions is required!" class NeedAdministratorRightsInTheChannel(BadRequest): - match = 'need administrator rights in the channel chat' - text = 'Admin permissions is required!' + match = "need administrator rights in the channel chat" + text = "Admin permissions is required!" class NotEnoughRightsToPinMessage(BadRequest): - match = 'not enough rights to pin a message' + match = "not enough rights to pin a message" class MethodNotAvailableInPrivateChats(BadRequest): - match = 'method is available only for supergroups and channel' + match = "method is available only for supergroups and channel" class CantDemoteChatCreator(BadRequest): - match = 'can\'t demote chat creator' + match = "can't demote chat creator" class CantRestrictSelf(BadRequest): @@ -388,34 +396,34 @@ class CantRestrictSelf(BadRequest): class NotEnoughRightsToRestrict(BadRequest): - match = 'not enough rights to restrict/unrestrict chat member' + match = "not enough rights to restrict/unrestrict chat member" class PhotoDimensions(BadRequest): - match = 'PHOTO_INVALID_DIMENSIONS' - text = 'Invalid photo dimensions' + match = "PHOTO_INVALID_DIMENSIONS" + text = "Invalid photo dimensions" class UnavailableMembers(BadRequest): - match = 'supergroup members are unavailable' + match = "supergroup members are unavailable" class TypeOfFileMismatch(BadRequest): - match = 'type of file mismatch' + match = "type of file mismatch" class WrongRemoteFileIdSpecified(BadRequest): - match = 'wrong remote file id specified' + match = "wrong remote file id specified" class PaymentProviderInvalid(BadRequest): - match = 'PAYMENT_PROVIDER_INVALID' - text = 'payment provider invalid' + match = "PAYMENT_PROVIDER_INVALID" + text = "payment provider invalid" class CurrencyTotalAmountInvalid(BadRequest): - match = 'currency_total_amount_invalid' - text = 'currency total amount invalid' + match = "currency_total_amount_invalid" + text = "currency total amount invalid" class BadWebhook(BadRequest): @@ -423,44 +431,44 @@ class BadWebhook(BadRequest): class WebhookRequireHTTPS(BadWebhook): - match = 'HTTPS url must be provided for webhook' - text = 'bad webhook: ' + match + match = "HTTPS url must be provided for webhook" + text = "bad webhook: " + match class BadWebhookPort(BadWebhook): - match = 'Webhook can be set up only on ports 80, 88, 443 or 8443' - text = 'bad webhook: ' + match + match = "Webhook can be set up only on ports 80, 88, 443 or 8443" + text = "bad webhook: " + match class BadWebhookAddrInfo(BadWebhook): - match = 'getaddrinfo: Temporary failure in name resolution' - text = 'bad webhook: ' + match + match = "getaddrinfo: Temporary failure in name resolution" + text = "bad webhook: " + match class BadWebhookNoAddressAssociatedWithHostname(BadWebhook): - match = 'failed to resolve host: no address associated with hostname' + match = "failed to resolve host: no address associated with hostname" class CantParseUrl(BadRequest): - match = 'can\'t parse URL' + match = "can't parse URL" class UnsupportedUrlProtocol(BadRequest): - match = 'unsupported URL protocol' + match = "unsupported URL protocol" class CantParseEntities(BadRequest): - match = 'can\'t parse entities' + match = "can't parse entities" class ResultIdDuplicate(BadRequest): - match = 'result_id_duplicate' - text = 'Result ID duplicate' + match = "result_id_duplicate" + text = "Result ID duplicate" class BotDomainInvalid(BadRequest): - match = 'bot_domain_invalid' - text = 'Invalid bot domain' + match = "bot_domain_invalid" + text = "Invalid bot domain" class NotFound(TelegramAPIError, _MatchErrorMixin): @@ -468,7 +476,7 @@ class NotFound(TelegramAPIError, _MatchErrorMixin): class MethodNotKnown(NotFound): - match = 'method not found' + match = "method not found" class ConflictError(TelegramAPIError, _MatchErrorMixin): @@ -476,13 +484,15 @@ class ConflictError(TelegramAPIError, _MatchErrorMixin): class TerminatedByOtherGetUpdates(ConflictError): - match = 'terminated by other getUpdates request' - text = 'Terminated by other getUpdates request; ' \ - 'Make sure that only one bot instance is running' + match = "terminated by other getUpdates request" + text = ( + "Terminated by other getUpdates request; " + "Make sure that only one bot instance is running" + ) class CantGetUpdates(ConflictError): - match = 'can\'t use getUpdates method while webhook is active' + match = "can't use getUpdates method while webhook is active" class Unauthorized(TelegramAPIError, _MatchErrorMixin): @@ -490,23 +500,23 @@ class Unauthorized(TelegramAPIError, _MatchErrorMixin): class BotKicked(Unauthorized): - match = 'Bot was kicked from a chat' + match = "Bot was kicked from a chat" class BotBlocked(Unauthorized): - match = 'bot was blocked by the user' + match = "bot was blocked by the user" class UserDeactivated(Unauthorized): - match = 'user is deactivated' + match = "user is deactivated" class CantInitiateConversation(Unauthorized): - match = 'bot can\'t initiate conversation with a user' + match = "bot can't initiate conversation with a user" class CantTalkWithBots(Unauthorized): - match = 'bot can\'t send messages to bots' + match = "bot can't send messages to bots" class NetworkError(TelegramAPIError): @@ -515,34 +525,43 @@ class NetworkError(TelegramAPIError): class RestartingTelegram(TelegramAPIError): def __init__(self): - super(RestartingTelegram, self).__init__('The Telegram Bot API service is restarting. Wait few second.') + super(RestartingTelegram, self).__init__( + "The Telegram Bot API service is restarting. Wait few second." + ) class RetryAfter(TelegramAPIError): def __init__(self, retry_after): - super(RetryAfter, self).__init__(f"Flood control exceeded. Retry in {retry_after} seconds.") + super(RetryAfter, self).__init__( + f"Flood control exceeded. Retry in {retry_after} seconds." + ) self.timeout = retry_after class MigrateToChat(TelegramAPIError): def __init__(self, chat_id): - super(MigrateToChat, self).__init__(f"The group has been migrated to a supergroup. New id: {chat_id}.") + super(MigrateToChat, self).__init__( + f"The group has been migrated to a supergroup. New id: {chat_id}." + ) self.migrate_to_chat_id = chat_id class Throttled(TelegramAPIError): def __init__(self, **kwargs): from ..dispatcher.storage import DELTA, EXCEEDED_COUNT, KEY, LAST_CALL, RATE_LIMIT, RESULT - self.key = kwargs.pop(KEY, '') + + self.key = kwargs.pop(KEY, "") self.called_at = kwargs.pop(LAST_CALL, time.time()) self.rate = kwargs.pop(RATE_LIMIT, None) self.result = kwargs.pop(RESULT, False) self.exceeded_count = kwargs.pop(EXCEEDED_COUNT, 0) self.delta = kwargs.pop(DELTA, 0) - self.user = kwargs.pop('user', None) - self.chat = kwargs.pop('chat', None) + self.user = kwargs.pop("user", None) + self.chat = kwargs.pop("chat", None) def __str__(self): - return f"Rate limit exceeded! (Limit: {self.rate} s, " \ - f"exceeded: {self.exceeded_count}, " \ + return ( + f"Rate limit exceeded! (Limit: {self.rate} s, " + f"exceeded: {self.exceeded_count}, " f"time delta: {round(self.delta, 3)} s)" + ) diff --git a/aiogram/utils/executor.py b/aiogram/utils/executor.py index 65594371..f4834901 100644 --- a/aiogram/utils/executor.py +++ b/aiogram/utils/executor.py @@ -12,7 +12,7 @@ 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' +APP_EXECUTOR_KEY = "APP_EXECUTOR" def _setup_callbacks(executor, on_startup=None, on_shutdown=None): @@ -22,8 +22,17 @@ def _setup_callbacks(executor, on_startup=None, on_shutdown=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, fast=True): +def start_polling( + dispatcher, + *, + loop=None, + skip_updates=False, + reset_webhook=True, + on_startup=None, + on_shutdown=None, + timeout=20, + fast=True, +): """ Start bot in long-polling mode @@ -41,11 +50,19 @@ def start_polling(dispatcher, *, loop=None, skip_updates=False, reset_webhook=Tr executor.start_polling(reset_webhook=reset_webhook, timeout=timeout, fast=fast) -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): +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 @@ -61,17 +78,32 @@ def set_webhook(dispatcher: Dispatcher, webhook_path: str, *, loop: Optional[asy :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) + 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): +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 @@ -86,20 +118,21 @@ def start_webhook(dispatcher, webhook_path, *, loop=None, skip_updates=None, :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 = 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): +def start(dispatcher, future, *, loop=None, skip_updates=None, on_startup=None, on_shutdown=None): """ Execute Future. @@ -142,6 +175,7 @@ class Executor: self._freeze = False from aiogram import Bot, Dispatcher + Bot.set_current(dispatcher.bot) Dispatcher.set_current(dispatcher) @@ -160,7 +194,7 @@ class Executor: @property def web_app(self) -> web.Application: if self._web_app is None: - raise RuntimeError('web.Application() is not configured!') + raise RuntimeError("web.Application() is not configured!") return self._web_app def on_startup(self, callback: callable, polling=True, webhook=True): @@ -173,7 +207,7 @@ class Executor: """ self._check_frozen() if not webhook and not polling: - warn('This action has no effect!', UserWarning) + warn("This action has no effect!", UserWarning) return if isinstance(callback, (list, tuple, set)): @@ -196,7 +230,7 @@ class Executor: """ self._check_frozen() if not webhook and not polling: - warn('This action has no effect!', UserWarning) + warn("This action has no effect!", UserWarning) return if isinstance(callback, (list, tuple, set)): @@ -211,7 +245,7 @@ class Executor: def _check_frozen(self): if self.frozen: - raise RuntimeError('Executor is frozen!') + raise RuntimeError("Executor is frozen!") def _prepare_polling(self): self._check_frozen() @@ -219,7 +253,9 @@ class Executor: # self.loop.set_task_factory(context.task_factory) - def _prepare_webhook(self, path=None, handler=WebhookRequestHandler, route_name=DEFAULT_ROUTE_NAME, app=None): + def _prepare_webhook( + self, path=None, handler=WebhookRequestHandler, route_name=DEFAULT_ROUTE_NAME, app=None + ): self._check_frozen() self._freeze = True @@ -233,14 +269,14 @@ class Executor: raise RuntimeError("web.Application() is already configured!") if self.retry_after: - app['RETRY_AFTER'] = 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) + app.router.add_route("*", path, handler, name=route_name) async def _wrap_callback(cb, _): return await cb(self.dispatcher) @@ -258,10 +294,15 @@ class Executor: app[APP_EXECUTOR_KEY] = self app[BOT_DISPATCHER_KEY] = self.dispatcher app[self._identity] = datetime.datetime.now() - app['_check_ip'] = self.check_ip + 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): + 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 @@ -277,8 +318,13 @@ class Executor: 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): + def start_webhook( + self, + webhook_path=None, + request_handler=WebhookRequestHandler, + route_name=DEFAULT_ROUTE_NAME, + **kwargs, + ): """ Start bot in webhook mode @@ -288,7 +334,9 @@ class Executor: :param kwargs: :return: """ - self.set_webhook(webhook_path=webhook_path, request_handler=request_handler, route_name=route_name) + 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, fast=True): @@ -303,7 +351,11 @@ class Executor: try: loop.run_until_complete(self._startup_polling()) - loop.create_task(self.dispatcher.start_polling(reset_webhook=reset_webhook, timeout=timeout, fast=fast)) + loop.create_task( + self.dispatcher.start_polling( + reset_webhook=reset_webhook, timeout=timeout, fast=fast + ) + ) loop.run_forever() except (KeyboardInterrupt, SystemExit): # loop.stop() diff --git a/aiogram/utils/helper.py b/aiogram/utils/helper.py index eeabca7c..2ee14260 100644 --- a/aiogram/utils/helper.py +++ b/aiogram/utils/helper.py @@ -16,7 +16,7 @@ Example: class Helper: - mode = '' + mode = "" @classmethod def all(cls): @@ -37,13 +37,13 @@ class Helper: class HelperMode(Helper): - mode = 'original' + mode = "original" - SCREAMING_SNAKE_CASE = 'SCREAMING_SNAKE_CASE' - lowerCamelCase = 'lowerCamelCase' - CamelCase = 'CamelCase' - snake_case = 'snake_case' - lowercase = 'lowercase' + SCREAMING_SNAKE_CASE = "SCREAMING_SNAKE_CASE" + lowerCamelCase = "lowerCamelCase" + CamelCase = "CamelCase" + snake_case = "snake_case" + lowercase = "lowercase" @classmethod def all(cls): @@ -65,10 +65,10 @@ class HelperMode(Helper): """ if text.isupper(): return text - result = '' + result = "" for pos, symbol in enumerate(text): if symbol.isupper() and pos > 0: - result += '_' + symbol + result += "_" + symbol else: result += symbol.upper() return result @@ -94,10 +94,10 @@ class HelperMode(Helper): :param first_upper: first symbol must be upper? :return: """ - result = '' + result = "" need_upper = False for pos, symbol in enumerate(text): - if symbol == '_' and pos > 0: + if symbol == "_" and pos > 0: need_upper = True else: if need_upper: @@ -123,7 +123,7 @@ class HelperMode(Helper): elif mode == cls.snake_case: return cls._snake_case(text) elif mode == cls.lowercase: - return cls._snake_case(text).replace('_', '') + return cls._snake_case(text).replace("_", "") elif mode == cls.lowerCamelCase: return cls._camel_case(text) elif mode == cls.CamelCase: @@ -149,10 +149,10 @@ class Item: def __set_name__(self, owner, name): if not name.isupper(): - raise NameError('Name for helper item must be in uppercase!') + raise NameError("Name for helper item must be in uppercase!") if not self._value: - if hasattr(owner, 'mode'): - self._value = HelperMode.apply(name, getattr(owner, 'mode')) + if hasattr(owner, "mode"): + self._value = HelperMode.apply(name, getattr(owner, "mode")) class ListItem(Item): diff --git a/aiogram/utils/json.py b/aiogram/utils/json.py index b2305b88..42fdb8f3 100644 --- a/aiogram/utils/json.py +++ b/aiogram/utils/json.py @@ -1,14 +1,14 @@ import importlib import os -JSON = 'json' -RAPIDJSON = 'rapidjson' -UJSON = 'ujson' +JSON = "json" +RAPIDJSON = "rapidjson" +UJSON = "ujson" # Detect mode mode = JSON for json_lib in (RAPIDJSON, UJSON): - if 'DISABLE_' + json_lib.upper() in os.environ: + if "DISABLE_" + json_lib.upper() in os.environ: continue try: @@ -20,30 +20,35 @@ for json_lib in (RAPIDJSON, UJSON): break if mode == RAPIDJSON: - def dumps(data): - return json.dumps(data, ensure_ascii=False, number_mode=json.NM_NATIVE, - datetime_mode=json.DM_ISO8601 | json.DM_NAIVE_IS_UTC) + def dumps(data): + return json.dumps( + data, + ensure_ascii=False, + number_mode=json.NM_NATIVE, + datetime_mode=json.DM_ISO8601 | json.DM_NAIVE_IS_UTC, + ) def loads(data): - return json.loads(data, number_mode=json.NM_NATIVE, - datetime_mode=json.DM_ISO8601 | json.DM_NAIVE_IS_UTC) + return json.loads( + data, number_mode=json.NM_NATIVE, datetime_mode=json.DM_ISO8601 | json.DM_NAIVE_IS_UTC + ) + elif mode == UJSON: + def loads(data): return json.loads(data) - def dumps(data): return json.dumps(data, ensure_ascii=False) + else: import json - def dumps(data): return json.dumps(data, ensure_ascii=False) - def loads(data): return json.loads(data) diff --git a/aiogram/utils/markdown.py b/aiogram/utils/markdown.py index 89a23d94..ba8356e7 100644 --- a/aiogram/utils/markdown.py +++ b/aiogram/utils/markdown.py @@ -1,37 +1,32 @@ -LIST_MD_SYMBOLS = '*_`[' +LIST_MD_SYMBOLS = "*_`[" MD_SYMBOLS = ( (LIST_MD_SYMBOLS[0], LIST_MD_SYMBOLS[0]), (LIST_MD_SYMBOLS[1], LIST_MD_SYMBOLS[1]), (LIST_MD_SYMBOLS[2], LIST_MD_SYMBOLS[2]), - (LIST_MD_SYMBOLS[2] * 3 + '\n', '\n' + LIST_MD_SYMBOLS[2] * 3), - ('', ''), - ('', ''), - ('', ''), - ('
', '
'), + (LIST_MD_SYMBOLS[2] * 3 + "\n", "\n" + LIST_MD_SYMBOLS[2] * 3), + ("", ""), + ("", ""), + ("", ""), + ("
", "
"), ) -HTML_QUOTES_MAP = { - '<': '<', - '>': '>', - '&': '&', - '"': '"' -} +HTML_QUOTES_MAP = {"<": "<", ">": ">", "&": "&", '"': """} _HQS = HTML_QUOTES_MAP.keys() # HQS for HTML QUOTES SYMBOLS -def _join(*content, sep=' '): +def _join(*content, sep=" "): return sep.join(map(str, content)) def _escape(s, symbols=LIST_MD_SYMBOLS): for symbol in symbols: - s = s.replace(symbol, '\\' + symbol) + s = s.replace(symbol, "\\" + symbol) return s -def _md(string, symbols=('', '')): +def _md(string, symbols=("", "")): start, end = symbols return start + string + end @@ -47,13 +42,13 @@ def quote_html(content): :param content: str :return: str """ - new_content = '' + new_content = "" for symbol in content: new_content += HTML_QUOTES_MAP[symbol] if symbol in _HQS else symbol return new_content -def text(*content, sep=' '): +def text(*content, sep=" "): """ Join all elements with a separator @@ -64,7 +59,7 @@ def text(*content, sep=' '): return _join(*content, sep=sep) -def bold(*content, sep=' '): +def bold(*content, sep=" "): """ Make bold text (Markdown) @@ -75,7 +70,7 @@ def bold(*content, sep=' '): return _md(_join(*content, sep=sep), symbols=MD_SYMBOLS[0]) -def hbold(*content, sep=' '): +def hbold(*content, sep=" "): """ Make bold text (HTML) @@ -86,7 +81,7 @@ def hbold(*content, sep=' '): return _md(quote_html(_join(*content, sep=sep)), symbols=MD_SYMBOLS[4]) -def italic(*content, sep=' '): +def italic(*content, sep=" "): """ Make italic text (Markdown) @@ -97,7 +92,7 @@ def italic(*content, sep=' '): return _md(_join(*content, sep=sep), symbols=MD_SYMBOLS[1]) -def hitalic(*content, sep=' '): +def hitalic(*content, sep=" "): """ Make italic text (HTML) @@ -108,7 +103,7 @@ def hitalic(*content, sep=' '): return _md(quote_html(_join(*content, sep=sep)), symbols=MD_SYMBOLS[5]) -def code(*content, sep=' '): +def code(*content, sep=" "): """ Make mono-width text (Markdown) @@ -119,7 +114,7 @@ def code(*content, sep=' '): return _md(_join(*content, sep=sep), symbols=MD_SYMBOLS[2]) -def hcode(*content, sep=' '): +def hcode(*content, sep=" "): """ Make mono-width text (HTML) @@ -130,7 +125,7 @@ def hcode(*content, sep=' '): return _md(quote_html(_join(*content, sep=sep)), symbols=MD_SYMBOLS[6]) -def pre(*content, sep='\n'): +def pre(*content, sep="\n"): """ Make mono-width text block (Markdown) @@ -141,7 +136,7 @@ def pre(*content, sep='\n'): return _md(_join(*content, sep=sep), symbols=MD_SYMBOLS[3]) -def hpre(*content, sep='\n'): +def hpre(*content, sep="\n"): """ Make mono-width text block (HTML) @@ -174,7 +169,7 @@ def hlink(title, url): return '{1}'.format(url, quote_html(title)) -def escape_md(*content, sep=' '): +def escape_md(*content, sep=" "): """ Escape markdown text diff --git a/aiogram/utils/mixins.py b/aiogram/utils/mixins.py index 776479bd..a2c872a2 100644 --- a/aiogram/utils/mixins.py +++ b/aiogram/utils/mixins.py @@ -1,16 +1,16 @@ import contextvars from typing import TypeVar, Type -__all__ = ('DataMixin', 'ContextInstanceMixin') +__all__ = ("DataMixin", "ContextInstanceMixin") class DataMixin: @property def data(self): - data = getattr(self, '_data', None) + data = getattr(self, "_data", None) if data is None: data = {} - setattr(self, '_data', data) + setattr(self, "_data", data) return data def __getitem__(self, item): @@ -26,12 +26,12 @@ class DataMixin: return self.data.get(key, default) -T = TypeVar('T') +T = TypeVar("T") class ContextInstanceMixin: def __init_subclass__(cls, **kwargs): - cls.__context_instance = contextvars.ContextVar('instance_' + cls.__name__) + cls.__context_instance = contextvars.ContextVar("instance_" + cls.__name__) return cls @classmethod @@ -43,5 +43,7 @@ class ContextInstanceMixin: @classmethod def set_current(cls: Type[T], value: T): if not isinstance(value, cls): - raise TypeError(f"Value should be instance of '{cls.__name__}' not '{type(value).__name__}'") + raise TypeError( + f"Value should be instance of '{cls.__name__}' not '{type(value).__name__}'" + ) cls.__context_instance.set(value) diff --git a/aiogram/utils/parts.py b/aiogram/utils/parts.py index e03f7bcc..7c4f51c5 100644 --- a/aiogram/utils/parts.py +++ b/aiogram/utils/parts.py @@ -12,7 +12,7 @@ def split_text(text: str, length: int = MAX_MESSAGE_LENGTH) -> typing.List[str]: :return: list of parts :rtype: :obj:`typing.List[str]` """ - return [text[i:i + length] for i in range(0, len(text), length)] + return [text[i : i + length] for i in range(0, len(text), length)] def safe_split_text(text: str, length: int = MAX_MESSAGE_LENGTH) -> typing.List[str]: @@ -30,7 +30,7 @@ def safe_split_text(text: str, length: int = MAX_MESSAGE_LENGTH) -> typing.List[ while temp_text: if len(temp_text) > length: try: - split_pos = temp_text[:length].rindex(' ') + split_pos = temp_text[:length].rindex(" ") except ValueError: split_pos = length if split_pos < length // 4 * 3: @@ -56,4 +56,4 @@ def paginate(data: typing.Iterable, page: int = 0, limit: int = 10) -> typing.It :return: sliced object :rtype: :obj:`typing.Iterable` """ - return data[page * limit:page * limit + limit] + return data[page * limit : page * limit + limit] diff --git a/aiogram/utils/payload.py b/aiogram/utils/payload.py index 45643553..db86eed0 100644 --- a/aiogram/utils/payload.py +++ b/aiogram/utils/payload.py @@ -6,7 +6,7 @@ from babel.support import LazyProxy from aiogram import types from . import json -DEFAULT_FILTER = ['self', 'cls'] +DEFAULT_FILTER = ["self", "cls"] def generate_payload(exclude=None, **kwargs): @@ -21,10 +21,11 @@ def generate_payload(exclude=None, **kwargs): """ if exclude is None: exclude = [] - return {key: value for key, value in kwargs.items() if - key not in exclude + DEFAULT_FILTER - and value is not None - and not key.startswith('_')} + return { + key: value + for key, value in kwargs.items() + if key not in exclude + DEFAULT_FILTER and value is not None and not key.startswith("_") + } def _normalize(obj): @@ -38,7 +39,7 @@ def _normalize(obj): return [_normalize(item) for item in obj] elif isinstance(obj, dict): return {k: _normalize(v) for k, v in obj.items() if v is not None} - elif hasattr(obj, 'to_python'): + elif hasattr(obj, "to_python"): return obj.to_python() return obj @@ -52,7 +53,7 @@ def prepare_arg(value): """ if value is None: return value - elif isinstance(value, (list, dict)) or hasattr(value, 'to_python'): + elif isinstance(value, (list, dict)) or hasattr(value, "to_python"): return json.dumps(_normalize(value)) elif isinstance(value, datetime.timedelta): now = datetime.datetime.now() diff --git a/docs/source/index.rst b/docs/source/index.rst index 89cdbf79..a39ad5eb 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -27,9 +27,13 @@ Welcome to aiogram's documentation! :alt: Telegram Bot API .. image:: https://img.shields.io/readthedocs/pip/stable.svg?style=flat-square - :target: http://aiogram.readthedocs.io/en/latest/?badge=latest + :target: http://aiogram.readthedocs.io/en/latest/?badge=latest?style=flat-square :alt: Documentation Status + .. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=flat-square + :target: https://github.com/python/black + :alt: Code style: Black + .. image:: https://img.shields.io/github/issues/aiogram/aiogram.svg?style=flat-square :target: https://github.com/aiogram/aiogram/issues :alt: Github issues diff --git a/examples/adwanced_executor_example.py b/examples/adwanced_executor_example.py index 521783f1..c32bfa2d 100644 --- a/examples/adwanced_executor_example.py +++ b/examples/adwanced_executor_example.py @@ -35,16 +35,18 @@ from aiogram.utils.executor import start_polling, start_webhook logging.basicConfig(level=logging.INFO) # Configure arguments parser. -parser = argparse.ArgumentParser(description='Python telegram bot') -parser.add_argument('--token', '-t', nargs='?', type=str, default=None, help='Set working directory') -parser.add_argument('--sock', help='UNIX Socket path') -parser.add_argument('--host', help='Webserver host') -parser.add_argument('--port', type=int, help='Webserver port') -parser.add_argument('--cert', help='Path to SSL certificate') -parser.add_argument('--pkey', help='Path to SSL private key') -parser.add_argument('--host-name', help='Set webhook host name') -parser.add_argument('--webhook-port', type=int, help='Port for webhook (default=port)') -parser.add_argument('--webhook-path', default='/webhook', help='Port for webhook (default=port)') +parser = argparse.ArgumentParser(description="Python telegram bot") +parser.add_argument( + "--token", "-t", nargs="?", type=str, default=None, help="Set working directory" +) +parser.add_argument("--sock", help="UNIX Socket path") +parser.add_argument("--host", help="Webserver host") +parser.add_argument("--port", type=int, help="Webserver port") +parser.add_argument("--cert", help="Path to SSL certificate") +parser.add_argument("--pkey", help="Path to SSL private key") +parser.add_argument("--host-name", help="Set webhook host name") +parser.add_argument("--webhook-port", type=int, help="Port for webhook (default=port)") +parser.add_argument("--webhook-path", default="/webhook", help="Port for webhook (default=port)") async def cmd_start(message: types.Message): @@ -53,7 +55,7 @@ async def cmd_start(message: types.Message): def setup_handlers(dispatcher: Dispatcher): # This example has only one messages handler - dispatcher.register_message_handler(cmd_start, commands=['start', 'welcome']) + dispatcher.register_message_handler(cmd_start, commands=["start", "welcome"]) async def on_startup(dispatcher, url=None, cert=None): @@ -73,7 +75,7 @@ async def on_startup(dispatcher, url=None, cert=None): # Set new URL for webhook if cert: - with open(cert, 'rb') as cert_file: + with open(cert, "rb") as cert_file: await bot.set_webhook(url, certificate=cert_file) else: await bot.set_webhook(url) @@ -83,7 +85,7 @@ async def on_startup(dispatcher, url=None, cert=None): async def on_shutdown(dispatcher): - print('Shutdown.') + print("Shutdown.") def main(arguments): @@ -99,8 +101,8 @@ def main(arguments): webhook_path = args.webhook_path # Fi webhook path - if not webhook_path.startswith('/'): - webhook_path = '/' + webhook_path + if not webhook_path.startswith("/"): + webhook_path = "/" + webhook_path # Generate webhook URL webhook_url = f"https://{host_name}:{webhook_port}{webhook_path}" @@ -116,15 +118,21 @@ def main(arguments): else: ssl_context = None - start_webhook(dispatcher, webhook_path, - on_startup=functools.partial(on_startup, url=webhook_url, cert=cert), - on_shutdown=on_shutdown, - host=host, port=port, path=sock, ssl_context=ssl_context) + start_webhook( + dispatcher, + webhook_path, + on_startup=functools.partial(on_startup, url=webhook_url, cert=cert), + on_shutdown=on_shutdown, + host=host, + port=port, + path=sock, + ssl_context=ssl_context, + ) else: start_polling(dispatcher, on_startup=on_startup, on_shutdown=on_shutdown) -if __name__ == '__main__': +if __name__ == "__main__": argv = sys.argv[1:] if not len(argv): diff --git a/examples/broadcast_example.py b/examples/broadcast_example.py index 9e654d44..1f41e8bc 100644 --- a/examples/broadcast_example.py +++ b/examples/broadcast_example.py @@ -4,10 +4,10 @@ import logging from aiogram import Bot, Dispatcher, types from aiogram.utils import exceptions, executor -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" logging.basicConfig(level=logging.INFO) -log = logging.getLogger('broadcast') +log = logging.getLogger("broadcast") loop = asyncio.get_event_loop() bot = Bot(token=API_TOKEN, loop=loop, parse_mode=types.ParseMode.HTML) @@ -61,15 +61,15 @@ async def broadcaster() -> int: count = 0 try: for user_id in get_users(): - if await send_message(user_id, 'Hello!'): + if await send_message(user_id, "Hello!"): count += 1 - await asyncio.sleep(.05) # 20 messages per second (Limit: 30 messages per second) + await asyncio.sleep(0.05) # 20 messages per second (Limit: 30 messages per second) finally: log.info(f"{count} messages successful sent.") return count -if __name__ == '__main__': +if __name__ == "__main__": # Execute broadcaster executor.start(dp, broadcaster()) diff --git a/examples/callback_data_factory.py b/examples/callback_data_factory.py index 3dd7d35e..a76d5d21 100644 --- a/examples/callback_data_factory.py +++ b/examples/callback_data_factory.py @@ -11,7 +11,7 @@ from aiogram.utils.exceptions import MessageNotModified, Throttled logging.basicConfig(level=logging.INFO) -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" loop = asyncio.get_event_loop() bot = Bot(token=API_TOKEN, loop=loop, parse_mode=types.ParseMode.HTML) @@ -21,16 +21,17 @@ dp.middleware.setup(LoggingMiddleware()) POSTS = { str(uuid.uuid4()): { - 'title': f"Post {index}", - 'body': 'Lorem ipsum dolor sit amet, ' - 'consectetur adipiscing elit, ' - 'sed do eiusmod tempor incididunt ut ' - 'labore et dolore magna aliqua', - 'votes': random.randint(-2, 5) - } for index in range(1, 6) + "title": f"Post {index}", + "body": "Lorem ipsum dolor sit amet, " + "consectetur adipiscing elit, " + "sed do eiusmod tempor incididunt ut " + "labore et dolore magna aliqua", + "votes": random.randint(-2, 5), + } + for index in range(1, 6) } -posts_cb = CallbackData('post', 'id', 'action') # post:: +posts_cb = CallbackData("post", "id", "action") # post:: def get_keyboard() -> types.InlineKeyboardMarkup: @@ -41,69 +42,73 @@ def get_keyboard() -> types.InlineKeyboardMarkup: for post_id, post in POSTS.items(): markup.add( types.InlineKeyboardButton( - post['title'], - callback_data=posts_cb.new(id=post_id, action='view')) + post["title"], callback_data=posts_cb.new(id=post_id, action="view") + ) ) return markup def format_post(post_id: str, post: dict) -> (str, types.InlineKeyboardMarkup): - text = f"{md.hbold(post['title'])}\n" \ - f"{md.quote_html(post['body'])}\n" \ - f"\n" \ + text = ( + f"{md.hbold(post['title'])}\n" + f"{md.quote_html(post['body'])}\n" + f"\n" f"Votes: {post['votes']}" + ) markup = types.InlineKeyboardMarkup() markup.row( - types.InlineKeyboardButton('👍', callback_data=posts_cb.new(id=post_id, action='like')), - types.InlineKeyboardButton('👎', callback_data=posts_cb.new(id=post_id, action='unlike')), + types.InlineKeyboardButton("👍", callback_data=posts_cb.new(id=post_id, action="like")), + types.InlineKeyboardButton("👎", callback_data=posts_cb.new(id=post_id, action="unlike")), + ) + markup.add( + types.InlineKeyboardButton("<< Back", callback_data=posts_cb.new(id="-", action="list")) ) - markup.add(types.InlineKeyboardButton('<< Back', callback_data=posts_cb.new(id='-', action='list'))) return text, markup -@dp.message_handler(commands='start') +@dp.message_handler(commands="start") async def cmd_start(message: types.Message): - await message.reply('Posts', reply_markup=get_keyboard()) + await message.reply("Posts", reply_markup=get_keyboard()) -@dp.callback_query_handler(posts_cb.filter(action='list')) +@dp.callback_query_handler(posts_cb.filter(action="list")) async def query_show_list(query: types.CallbackQuery): - await query.message.edit_text('Posts', reply_markup=get_keyboard()) + await query.message.edit_text("Posts", reply_markup=get_keyboard()) -@dp.callback_query_handler(posts_cb.filter(action='view')) +@dp.callback_query_handler(posts_cb.filter(action="view")) async def query_view(query: types.CallbackQuery, callback_data: dict): - post_id = callback_data['id'] + post_id = callback_data["id"] post = POSTS.get(post_id, None) if not post: - return await query.answer('Unknown post!') + return await query.answer("Unknown post!") text, markup = format_post(post_id, post) await query.message.edit_text(text, reply_markup=markup) -@dp.callback_query_handler(posts_cb.filter(action=['like', 'unlike'])) +@dp.callback_query_handler(posts_cb.filter(action=["like", "unlike"])) async def query_post_vote(query: types.CallbackQuery, callback_data: dict): try: - await dp.throttle('vote', rate=1) + await dp.throttle("vote", rate=1) except Throttled: - return await query.answer('Too many requests.') + return await query.answer("Too many requests.") - post_id = callback_data['id'] - action = callback_data['action'] + post_id = callback_data["id"] + action = callback_data["action"] post = POSTS.get(post_id, None) if not post: - return await query.answer('Unknown post!') + return await query.answer("Unknown post!") - if action == 'like': - post['votes'] += 1 - elif action == 'unlike': - post['votes'] -= 1 + if action == "like": + post["votes"] += 1 + elif action == "unlike": + post["votes"] -= 1 - await query.answer('Voted.') + await query.answer("Voted.") text, markup = format_post(post_id, post) await query.message.edit_text(text, reply_markup=markup) @@ -113,5 +118,5 @@ async def message_not_modified_handler(update, error): return True -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/check_user_language.py b/examples/check_user_language.py index bd0ba7f9..5bc54f17 100644 --- a/examples/check_user_language.py +++ b/examples/check_user_language.py @@ -7,7 +7,7 @@ import logging from aiogram import Bot, Dispatcher, executor, md, types -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" logging.basicConfig(level=logging.INFO) @@ -20,14 +20,17 @@ dp = Dispatcher(bot) async def check_language(message: types.Message): locale = message.from_user.locale - await message.reply(md.text( - md.bold('Info about your language:'), - md.text(' 🔸', md.bold('Code:'), md.italic(locale.locale)), - md.text(' 🔸', md.bold('Territory:'), md.italic(locale.territory or 'Unknown')), - md.text(' 🔸', md.bold('Language name:'), md.italic(locale.language_name)), - md.text(' 🔸', md.bold('English language name:'), md.italic(locale.english_name)), - sep='\n')) + await message.reply( + md.text( + md.bold("Info about your language:"), + md.text(" 🔸", md.bold("Code:"), md.italic(locale.locale)), + md.text(" 🔸", md.bold("Territory:"), md.italic(locale.territory or "Unknown")), + md.text(" 🔸", md.bold("Language name:"), md.italic(locale.language_name)), + md.text(" 🔸", md.bold("English language name:"), md.italic(locale.english_name)), + sep="\n", + ) + ) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/echo_bot.py b/examples/echo_bot.py index 27dc70d9..536dc62c 100644 --- a/examples/echo_bot.py +++ b/examples/echo_bot.py @@ -7,7 +7,7 @@ import logging from aiogram import Bot, Dispatcher, executor, types -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" # Configure logging logging.basicConfig(level=logging.INFO) @@ -17,7 +17,7 @@ bot = Bot(token=API_TOKEN) dp = Dispatcher(bot) -@dp.message_handler(commands=['start', 'help']) +@dp.message_handler(commands=["start", "help"]) async def send_welcome(message: types.Message): """ This handler will be called when client send `/start` or `/help` commands. @@ -25,11 +25,15 @@ async def send_welcome(message: types.Message): await message.reply("Hi!\nI'm EchoBot!\nPowered by aiogram.") -@dp.message_handler(regexp='(^cat[s]?$|puss)') +@dp.message_handler(regexp="(^cat[s]?$|puss)") async def cats(message: types.Message): - with open('data/cats.jpg', 'rb') as photo: - await bot.send_photo(message.chat.id, photo, caption='Cats is here 😺', - reply_to_message_id=message.message_id) + with open("data/cats.jpg", "rb") as photo: + await bot.send_photo( + message.chat.id, + photo, + caption="Cats is here 😺", + reply_to_message_id=message.message_id, + ) @dp.message_handler() @@ -37,5 +41,5 @@ async def echo(message: types.Message): await bot.send_message(message.chat.id, message.text) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, skip_updates=True) diff --git a/examples/finite_state_machine_example.py b/examples/finite_state_machine_example.py index 58b8053c..c2787ff5 100644 --- a/examples/finite_state_machine_example.py +++ b/examples/finite_state_machine_example.py @@ -9,7 +9,7 @@ from aiogram.dispatcher.filters.state import State, StatesGroup from aiogram.types import ParseMode from aiogram.utils import executor -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" loop = asyncio.get_event_loop() @@ -27,7 +27,7 @@ class Form(StatesGroup): gender = State() # Will be represented in storage as 'Form:gender' -@dp.message_handler(commands=['start']) +@dp.message_handler(commands=["start"]) async def cmd_start(message: types.Message): """ Conversation's entry point @@ -39,9 +39,11 @@ async def cmd_start(message: types.Message): # You can use state '*' if you need to handle all states -@dp.message_handler(state='*', commands=['cancel']) -@dp.message_handler(lambda message: message.text.lower() == 'cancel', state='*') -async def cancel_handler(message: types.Message, state: FSMContext, raw_state: Optional[str] = None): +@dp.message_handler(state="*", commands=["cancel"]) +@dp.message_handler(lambda message: message.text.lower() == "cancel", state="*") +async def cancel_handler( + message: types.Message, state: FSMContext, raw_state: Optional[str] = None +): """ Allow user to cancel any action """ @@ -51,7 +53,7 @@ async def cancel_handler(message: types.Message, state: FSMContext, raw_state: O # Cancel state and inform user about it await state.finish() # And remove keyboard (just in case) - await message.reply('Canceled.', reply_markup=types.ReplyKeyboardRemove()) + await message.reply("Canceled.", reply_markup=types.ReplyKeyboardRemove()) @dp.message_handler(state=Form.name) @@ -60,7 +62,7 @@ async def process_name(message: types.Message, state: FSMContext): Process user name """ async with state.proxy() as data: - data['name'] = message.text + data["name"] = message.text await Form.next() await message.reply("How old are you?") @@ -89,7 +91,9 @@ async def process_age(message: types.Message, state: FSMContext): await message.reply("What is your gender?", reply_markup=markup) -@dp.message_handler(lambda message: message.text not in ["Male", "Female", "Other"], state=Form.gender) +@dp.message_handler( + lambda message: message.text not in ["Male", "Female", "Other"], state=Form.gender +) async def failed_process_gender(message: types.Message): """ In this example gender has to be one of: Male, Female, Other. @@ -100,21 +104,27 @@ async def failed_process_gender(message: types.Message): @dp.message_handler(state=Form.gender) async def process_gender(message: types.Message, state: FSMContext): async with state.proxy() as data: - data['gender'] = message.text + data["gender"] = message.text # Remove keyboard markup = types.ReplyKeyboardRemove() # And send message - await bot.send_message(message.chat.id, md.text( - md.text('Hi! Nice to meet you,', md.bold(data['name'])), - md.text('Age:', data['age']), - md.text('Gender:', data['gender']), - sep='\n'), reply_markup=markup, parse_mode=ParseMode.MARKDOWN) + await bot.send_message( + message.chat.id, + md.text( + md.text("Hi! Nice to meet you,", md.bold(data["name"])), + md.text("Age:", data["age"]), + md.text("Gender:", data["gender"]), + sep="\n", + ), + reply_markup=markup, + parse_mode=ParseMode.MARKDOWN, + ) # Finish conversation data.state = None -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/i18n_example.py b/examples/i18n_example.py index 6469ed5b..2a353d58 100644 --- a/examples/i18n_example.py +++ b/examples/i18n_example.py @@ -24,11 +24,11 @@ from pathlib import Path from aiogram import Bot, Dispatcher, executor, types from aiogram.contrib.middlewares.i18n import I18nMiddleware -TOKEN = 'BOT TOKEN HERE' -I18N_DOMAIN = 'mybot' +TOKEN = "BOT TOKEN HERE" +I18N_DOMAIN = "mybot" BASE_DIR = Path(__file__).parent -LOCALES_DIR = BASE_DIR / 'locales' +LOCALES_DIR = BASE_DIR / "locales" bot = Bot(TOKEN, parse_mode=types.ParseMode.HTML) dp = Dispatcher(bot) @@ -41,16 +41,16 @@ dp.middleware.setup(i18n) _ = i18n.gettext -@dp.message_handler(commands=['start']) +@dp.message_handler(commands=["start"]) async def cmd_start(message: types.Message): # Simply use `_('message')` instead of `'message'` and never use f-strings for translatable texts. - await message.reply(_('Hello, {user}!').format(user=message.from_user.full_name)) + await message.reply(_("Hello, {user}!").format(user=message.from_user.full_name)) -@dp.message_handler(commands=['lang']) +@dp.message_handler(commands=["lang"]) async def cmd_lang(message: types.Message, locale): - await message.reply(_('Your current language: {language}').format(language=locale)) + await message.reply(_("Your current language: {language}").format(language=locale)) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, skip_updates=True) diff --git a/examples/inline_bot.py b/examples/inline_bot.py index 4a771210..ce3a3c1c 100644 --- a/examples/inline_bot.py +++ b/examples/inline_bot.py @@ -3,7 +3,7 @@ import logging from aiogram import Bot, types, Dispatcher, executor -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" logging.basicConfig(level=logging.DEBUG) @@ -14,11 +14,12 @@ dp = Dispatcher(bot) @dp.inline_handler() async def inline_echo(inline_query: types.InlineQuery): - input_content = types.InputTextMessageContent(inline_query.query or 'echo') - item = types.InlineQueryResultArticle(id='1', title='echo', - input_message_content=input_content) + input_content = types.InputTextMessageContent(inline_query.query or "echo") + item = types.InlineQueryResultArticle( + id="1", title="echo", input_message_content=input_content + ) await bot.answer_inline_query(inline_query.id, results=[item], cache_time=1) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/media_group.py b/examples/media_group.py index b1f5246a..d2a2282a 100644 --- a/examples/media_group.py +++ b/examples/media_group.py @@ -2,7 +2,7 @@ import asyncio from aiogram import Bot, Dispatcher, executor, filters, types -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" loop = asyncio.get_event_loop() bot = Bot(token=API_TOKEN, loop=loop) @@ -24,13 +24,13 @@ async def send_welcome(message: types.Message): media = types.MediaGroup() # Attach local file - media.attach_photo(types.InputFile('data/cat.jpg'), 'Cat!') + media.attach_photo(types.InputFile("data/cat.jpg"), "Cat!") # More local files and more cats! - media.attach_photo(types.InputFile('data/cats.jpg'), 'More cats!') + media.attach_photo(types.InputFile("data/cats.jpg"), "More cats!") # You can also use URL's # For example: get random puss: - media.attach_photo('http://lorempixel.com/400/200/cats/', 'Random cat.') + media.attach_photo("http://lorempixel.com/400/200/cats/", "Random cat.") # And you can also use file ID: # media.attach_photo('', 'cat-cat-cat.') @@ -39,5 +39,5 @@ async def send_welcome(message: types.Message): await message.reply_media_group(media=media) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/middleware_and_antiflood.py b/examples/middleware_and_antiflood.py index c579aecc..c1c96b73 100644 --- a/examples/middleware_and_antiflood.py +++ b/examples/middleware_and_antiflood.py @@ -7,7 +7,7 @@ from aiogram.dispatcher.handler import CancelHandler, current_handler from aiogram.dispatcher.middlewares import BaseMiddleware from aiogram.utils.exceptions import Throttled -TOKEN = 'BOT TOKEN HERE' +TOKEN = "BOT TOKEN HERE" loop = asyncio.get_event_loop() @@ -28,9 +28,9 @@ def rate_limit(limit: int, key=None): """ def decorator(func): - setattr(func, 'throttling_rate_limit', limit) + setattr(func, "throttling_rate_limit", limit) if key: - setattr(func, 'throttling_key', key) + setattr(func, "throttling_key", key) return func return decorator @@ -41,7 +41,7 @@ class ThrottlingMiddleware(BaseMiddleware): Simple middleware """ - def __init__(self, limit=DEFAULT_RATE_LIMIT, key_prefix='antiflood_'): + def __init__(self, limit=DEFAULT_RATE_LIMIT, key_prefix="antiflood_"): self.rate_limit = limit self.prefix = key_prefix super(ThrottlingMiddleware, self).__init__() @@ -59,8 +59,8 @@ class ThrottlingMiddleware(BaseMiddleware): dispatcher = Dispatcher.get_current() # If handler was configured, get rate limit and key from handler if handler: - limit = getattr(handler, 'throttling_rate_limit', self.rate_limit) - key = getattr(handler, 'throttling_key', f"{self.prefix}_{handler.__name__}") + limit = getattr(handler, "throttling_rate_limit", self.rate_limit) + key = getattr(handler, "throttling_key", f"{self.prefix}_{handler.__name__}") else: limit = self.rate_limit key = f"{self.prefix}_message" @@ -85,7 +85,7 @@ class ThrottlingMiddleware(BaseMiddleware): handler = current_handler.get() dispatcher = Dispatcher.get_current() if handler: - key = getattr(handler, 'throttling_key', f"{self.prefix}_{handler.__name__}") + key = getattr(handler, "throttling_key", f"{self.prefix}_{handler.__name__}") else: key = f"{self.prefix}_message" @@ -94,7 +94,7 @@ class ThrottlingMiddleware(BaseMiddleware): # Prevent flooding if throttled.exceeded_count <= 2: - await message.reply('Too many requests! ') + await message.reply("Too many requests! ") # Sleep. await asyncio.sleep(delta) @@ -104,17 +104,19 @@ class ThrottlingMiddleware(BaseMiddleware): # If current message is not last with current key - do not send message if thr.exceeded_count == throttled.exceeded_count: - await message.reply('Unlocked.') + await message.reply("Unlocked.") -@dp.message_handler(commands=['start']) -@rate_limit(5, 'start') # this is not required but you can configure throttling manager for current handler using it +@dp.message_handler(commands=["start"]) +@rate_limit( + 5, "start" +) # this is not required but you can configure throttling manager for current handler using it async def cmd_test(message: types.Message): # You can use this command every 5 seconds - await message.reply('Test passed! You can use this command every 5 seconds.') + await message.reply("Test passed! You can use this command every 5 seconds.") -if __name__ == '__main__': +if __name__ == "__main__": # Setup middleware dp.middleware.setup(ThrottlingMiddleware()) diff --git a/examples/payments.py b/examples/payments.py index e8e37011..00a4e833 100644 --- a/examples/payments.py +++ b/examples/payments.py @@ -6,8 +6,8 @@ from aiogram.dispatcher import Dispatcher from aiogram.types.message import ContentTypes from aiogram.utils import executor -BOT_TOKEN = 'BOT TOKEN HERE' -PAYMENTS_PROVIDER_TOKEN = '123456789:TEST:1234567890abcdef1234567890abcdef' +BOT_TOKEN = "BOT TOKEN HERE" +PAYMENTS_PROVIDER_TOKEN = "123456789:TEST:1234567890abcdef1234567890abcdef" loop = asyncio.get_event_loop() bot = Bot(BOT_TOKEN) @@ -15,85 +15,107 @@ dp = Dispatcher(bot, loop=loop) # Setup prices prices = [ - types.LabeledPrice(label='Working Time Machine', amount=5750), - types.LabeledPrice(label='Gift wrapping', amount=500) + types.LabeledPrice(label="Working Time Machine", amount=5750), + types.LabeledPrice(label="Gift wrapping", amount=500), ] # Setup shipping options shipping_options = [ - types.ShippingOption(id='instant', title='WorldWide Teleporter').add(types.LabeledPrice('Teleporter', 1000)), - types.ShippingOption(id='pickup', title='Local pickup').add(types.LabeledPrice('Pickup', 300)) + types.ShippingOption(id="instant", title="WorldWide Teleporter").add( + types.LabeledPrice("Teleporter", 1000) + ), + types.ShippingOption(id="pickup", title="Local pickup").add(types.LabeledPrice("Pickup", 300)), ] -@dp.message_handler(commands=['start']) +@dp.message_handler(commands=["start"]) async def cmd_start(message: types.Message): - await bot.send_message(message.chat.id, - "Hello, I'm the demo merchant bot." - " I can sell you a Time Machine." - " Use /buy to order one, /terms for Terms and Conditions") + await bot.send_message( + message.chat.id, + "Hello, I'm the demo merchant bot." + " I can sell you a Time Machine." + " Use /buy to order one, /terms for Terms and Conditions", + ) -@dp.message_handler(commands=['terms']) +@dp.message_handler(commands=["terms"]) async def cmd_terms(message: types.Message): - await bot.send_message(message.chat.id, - 'Thank you for shopping with our demo bot. We hope you like your new time machine!\n' - '1. If your time machine was not delivered on time, please rethink your concept of time' - ' and try again.\n' - '2. If you find that your time machine is not working, kindly contact our future service' - ' workshops on Trappist-1e. They will be accessible anywhere between' - ' May 2075 and November 4000 C.E.\n' - '3. If you would like a refund, kindly apply for one yesterday and we will have sent it' - ' to you immediately.') + await bot.send_message( + message.chat.id, + "Thank you for shopping with our demo bot. We hope you like your new time machine!\n" + "1. If your time machine was not delivered on time, please rethink your concept of time" + " and try again.\n" + "2. If you find that your time machine is not working, kindly contact our future service" + " workshops on Trappist-1e. They will be accessible anywhere between" + " May 2075 and November 4000 C.E.\n" + "3. If you would like a refund, kindly apply for one yesterday and we will have sent it" + " to you immediately.", + ) -@dp.message_handler(commands=['buy']) +@dp.message_handler(commands=["buy"]) async def cmd_buy(message: types.Message): - await bot.send_message(message.chat.id, - "Real cards won't work with me, no money will be debited from your account." - " Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`" - "\n\nThis is your demo invoice:", parse_mode='Markdown') - await bot.send_invoice(message.chat.id, title='Working Time Machine', - description='Want to visit your great-great-great-grandparents?' - ' Make a fortune at the races?' - ' Shake hands with Hammurabi and take a stroll in the Hanging Gardens?' - ' Order our Working Time Machine today!', - provider_token=PAYMENTS_PROVIDER_TOKEN, - currency='usd', - photo_url='https://images.fineartamerica.com/images-medium-large/2-the-time-machine-dmitriy-khristenko.jpg', - photo_height=512, # !=0/None or picture won't be shown - photo_width=512, - photo_size=512, - is_flexible=True, # True If you need to set up Shipping Fee - prices=prices, - start_parameter='time-machine-example', - payload='HAPPY FRIDAYS COUPON') + await bot.send_message( + message.chat.id, + "Real cards won't work with me, no money will be debited from your account." + " Use this test card number to pay for your Time Machine: `4242 4242 4242 4242`" + "\n\nThis is your demo invoice:", + parse_mode="Markdown", + ) + await bot.send_invoice( + message.chat.id, + title="Working Time Machine", + description="Want to visit your great-great-great-grandparents?" + " Make a fortune at the races?" + " Shake hands with Hammurabi and take a stroll in the Hanging Gardens?" + " Order our Working Time Machine today!", + provider_token=PAYMENTS_PROVIDER_TOKEN, + currency="usd", + photo_url="https://images.fineartamerica.com/images-medium-large/2-the-time-machine-dmitriy-khristenko.jpg", + photo_height=512, # !=0/None or picture won't be shown + photo_width=512, + photo_size=512, + is_flexible=True, # True If you need to set up Shipping Fee + prices=prices, + start_parameter="time-machine-example", + payload="HAPPY FRIDAYS COUPON", + ) @dp.shipping_query_handler(func=lambda query: True) async def shipping(shipping_query: types.ShippingQuery): - await bot.answer_shipping_query(shipping_query.id, ok=True, shipping_options=shipping_options, - error_message='Oh, seems like our Dog couriers are having a lunch right now.' - ' Try again later!') + await bot.answer_shipping_query( + shipping_query.id, + ok=True, + shipping_options=shipping_options, + error_message="Oh, seems like our Dog couriers are having a lunch right now." + " Try again later!", + ) @dp.pre_checkout_query_handler(func=lambda query: True) async def checkout(pre_checkout_query: types.PreCheckoutQuery): - await bot.answer_pre_checkout_query(pre_checkout_query.id, ok=True, - error_message="Aliens tried to steal your card's CVV," - " but we successfully protected your credentials," - " try to pay again in a few minutes, we need a small rest.") + await bot.answer_pre_checkout_query( + pre_checkout_query.id, + ok=True, + error_message="Aliens tried to steal your card's CVV," + " but we successfully protected your credentials," + " try to pay again in a few minutes, we need a small rest.", + ) @dp.message_handler(content_types=ContentTypes.SUCCESSFUL_PAYMENT) async def got_payment(message: types.Message): - await bot.send_message(message.chat.id, - 'Hoooooray! Thanks for payment! We will proceed your order for `{} {}`' - ' as fast as possible! Stay in touch.' - '\n\nUse /buy again to get a Time Machine for your friend!'.format( - message.successful_payment.total_amount / 100, message.successful_payment.currency), - parse_mode='Markdown') + await bot.send_message( + message.chat.id, + "Hoooooray! Thanks for payment! We will proceed your order for `{} {}`" + " as fast as possible! Stay in touch." + "\n\nUse /buy again to get a Time Machine for your friend!".format( + message.successful_payment.total_amount / 100, message.successful_payment.currency + ), + parse_mode="Markdown", + ) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp, loop=loop) diff --git a/examples/proxy_and_emojize.py b/examples/proxy_and_emojize.py index 7e4452ee..7c78eed9 100644 --- a/examples/proxy_and_emojize.py +++ b/examples/proxy_and_emojize.py @@ -11,8 +11,8 @@ from aiogram.utils.executor import start_polling from aiogram.utils.markdown import bold, code, italic, text # Configure bot here -API_TOKEN = 'BOT TOKEN HERE' -PROXY_URL = 'http://PROXY_URL' # Or 'socks5://...' +API_TOKEN = "BOT TOKEN HERE" +PROXY_URL = "http://PROXY_URL" # Or 'socks5://...' # If authentication is required in your proxy then uncomment next line and change login/password for it # PROXY_AUTH = aiohttp.BasicAuth(login='login', password='password') @@ -21,7 +21,7 @@ PROXY_URL = 'http://PROXY_URL' # Or 'socks5://...' # Also you can use Socks5 proxy but you need manually install aiohttp_socks package. # Get my ip URL -GET_IP_URL = 'http://bot.whatismyipaddress.com/' +GET_IP_URL = "http://bot.whatismyipaddress.com/" logging.basicConfig(level=logging.INFO) @@ -36,22 +36,24 @@ async def fetch(url, proxy=None, proxy_auth=None): return await response.text() -@dp.message_handler(commands=['start']) +@dp.message_handler(commands=["start"]) async def cmd_start(message: types.Message): content = [] # Make request (without proxy) ip = await fetch(GET_IP_URL) - content.append(text(':globe_showing_Americas:', bold('IP:'), code(ip))) + content.append(text(":globe_showing_Americas:", bold("IP:"), code(ip))) # This line is formatted to '🌎 *IP:* `YOUR IP`' # Make request through proxy ip = await fetch(GET_IP_URL, bot.proxy, bot.proxy_auth) - content.append(text(':locked_with_key:', bold('IP:'), code(ip), italic('via proxy'))) + content.append(text(":locked_with_key:", bold("IP:"), code(ip), italic("via proxy"))) # This line is formatted to '🔐 *IP:* `YOUR IP` _via proxy_' # Send content - await bot.send_message(message.chat.id, emojize(text(*content, sep='\n')), parse_mode=ParseMode.MARKDOWN) + await bot.send_message( + message.chat.id, emojize(text(*content, sep="\n")), parse_mode=ParseMode.MARKDOWN + ) # In this example you can see emoji codes: ":globe_showing_Americas:" and ":locked_with_key:" # You can find full emoji cheat sheet at https://www.webpagefx.com/tools/emoji-cheat-sheet/ @@ -61,5 +63,5 @@ async def cmd_start(message: types.Message): # For example emojize('Moon face :new_moon_face:') is transformed to 'Moon face 🌚' -if __name__ == '__main__': +if __name__ == "__main__": start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/regexp_commands_filter_example.py b/examples/regexp_commands_filter_example.py index 86ccba55..260a513a 100644 --- a/examples/regexp_commands_filter_example.py +++ b/examples/regexp_commands_filter_example.py @@ -2,14 +2,16 @@ from aiogram import Bot, types from aiogram.dispatcher import Dispatcher, filters from aiogram.utils import executor -bot = Bot(token='TOKEN') +bot = Bot(token="TOKEN") dp = Dispatcher(bot) -@dp.message_handler(filters.RegexpCommandsFilter(regexp_commands=['item_([0-9]*)'])) +@dp.message_handler(filters.RegexpCommandsFilter(regexp_commands=["item_([0-9]*)"])) async def send_welcome(message: types.Message, regexp_command): - await message.reply("You have requested an item with number: {}".format(regexp_command.group(1))) + await message.reply( + "You have requested an item with number: {}".format(regexp_command.group(1)) + ) -if __name__ == '__main__': +if __name__ == "__main__": executor.start_polling(dp) diff --git a/examples/throtling_example.py b/examples/throtling_example.py index b979a979..31e962ec 100644 --- a/examples/throtling_example.py +++ b/examples/throtling_example.py @@ -13,7 +13,7 @@ from aiogram.dispatcher import Dispatcher from aiogram.utils.exceptions import Throttled from aiogram.utils.executor import start_polling -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" logging.basicConfig(level=logging.INFO) @@ -26,18 +26,18 @@ storage = MemoryStorage() dp = Dispatcher(bot, storage=storage) -@dp.message_handler(commands=['start', 'help']) +@dp.message_handler(commands=["start", "help"]) async def send_welcome(message: types.Message): try: # Execute throttling manager with rate-limit equal to 2 seconds for key "start" - await dp.throttle('start', rate=2) + await dp.throttle("start", rate=2) except Throttled: # If request is throttled, the `Throttled` exception will be raised - await message.reply('Too many requests!') + await message.reply("Too many requests!") else: # Otherwise do something await message.reply("Hi!\nI'm EchoBot!\nPowered by aiogram.") -if __name__ == '__main__': +if __name__ == "__main__": start_polling(dp, loop=loop, skip_updates=True) diff --git a/examples/webhook_example.py b/examples/webhook_example.py index 86520988..db4fbb50 100644 --- a/examples/webhook_example.py +++ b/examples/webhook_example.py @@ -16,26 +16,28 @@ from aiogram.dispatcher.webhook import get_new_configured_app, SendMessage from aiogram.types import ChatType, ParseMode, ContentTypes from aiogram.utils.markdown import hbold, bold, text, link -TOKEN = 'BOT TOKEN HERE' +TOKEN = "BOT TOKEN HERE" -WEBHOOK_HOST = 'example.com' # Domain name or IP addres which your bot is located. +WEBHOOK_HOST = "example.com" # Domain name or IP addres which your bot is located. WEBHOOK_PORT = 443 # Telegram Bot API allows only for usage next ports: 443, 80, 88 or 8443 -WEBHOOK_URL_PATH = '/webhook' # Part of URL +WEBHOOK_URL_PATH = "/webhook" # Part of URL # This options needed if you use self-signed SSL certificate # Instructions: https://core.telegram.org/bots/self-signed -WEBHOOK_SSL_CERT = './webhook_cert.pem' # Path to the ssl certificate -WEBHOOK_SSL_PRIV = './webhook_pkey.pem' # Path to the ssl private key +WEBHOOK_SSL_CERT = "./webhook_cert.pem" # Path to the ssl certificate +WEBHOOK_SSL_PRIV = "./webhook_pkey.pem" # Path to the ssl private key WEBHOOK_URL = f"https://{WEBHOOK_HOST}:{WEBHOOK_PORT}{WEBHOOK_URL_PATH}" # Web app settings: # Use LAN address to listen webhooks # User any available port in range from 1024 to 49151 if you're using proxy, or WEBHOOK_PORT if you're using direct webhook handling -WEBAPP_HOST = 'localhost' +WEBAPP_HOST = "localhost" WEBAPP_PORT = 3001 -BAD_CONTENT = ContentTypes.PHOTO & ContentTypes.DOCUMENT & ContentTypes.STICKER & ContentTypes.AUDIO +BAD_CONTENT = ( + ContentTypes.PHOTO & ContentTypes.DOCUMENT & ContentTypes.STICKER & ContentTypes.AUDIO +) loop = asyncio.get_event_loop() bot = Bot(TOKEN, loop=loop) @@ -46,19 +48,27 @@ dp = Dispatcher(bot, storage=storage) async def cmd_start(message: types.Message): # Yep. aiogram allows to respond into webhook. # https://core.telegram.org/bots/api#making-requests-when-getting-updates - return SendMessage(chat_id=message.chat.id, text='Hi from webhook!', - reply_to_message_id=message.message_id) + return SendMessage( + chat_id=message.chat.id, text="Hi from webhook!", reply_to_message_id=message.message_id + ) async def cmd_about(message: types.Message): # In this function markdown utils are userd for formatting message text - return SendMessage(message.chat.id, text( - bold('Hi! I\'m just a simple telegram bot.'), - '', - text('I\'m powered by', bold('Python', Version(*sys.version_info[:]))), - text('With', link(text('aiogram', aiogram.VERSION), 'https://github.com/aiogram/aiogram')), - sep='\n' - ), parse_mode=ParseMode.MARKDOWN) + return SendMessage( + message.chat.id, + text( + bold("Hi! I'm just a simple telegram bot."), + "", + text("I'm powered by", bold("Python", Version(*sys.version_info[:]))), + text( + "With", + link(text("aiogram", aiogram.VERSION), "https://github.com/aiogram/aiogram"), + ), + sep="\n", + ), + parse_mode=ParseMode.MARKDOWN, + ) async def cancel(message: types.Message): @@ -68,7 +78,7 @@ async def cancel(message: types.Message): # If current user in any state - cancel it. if await state.get_state() is not None: await state.set_state(state=None) - return SendMessage(message.chat.id, 'Current action is canceled.') + return SendMessage(message.chat.id, "Current action is canceled.") # Otherwise do nothing @@ -76,8 +86,10 @@ async def unknown(message: types.Message): """ Handler for unknown messages. """ - return SendMessage(message.chat.id, - f"I don\'t know what to do with content type `{message.content_type()}`. Sorry :c") + return SendMessage( + message.chat.id, + f"I don't know what to do with content type `{message.content_type()}`. Sorry :c", + ) async def cmd_id(message: types.Message): @@ -94,43 +106,53 @@ async def cmd_id(message: types.Message): target = message.from_user chat = message.chat - result_msg = [hbold('Info about user:'), - f"First name: {target.first_name}"] + result_msg = [hbold("Info about user:"), f"First name: {target.first_name}"] if target.last_name: result_msg.append(f"Last name: {target.last_name}") if target.username: result_msg.append(f"Username: {target.mention}") result_msg.append(f"User ID: {target.id}") - result_msg.extend([hbold('Chat:'), - f"Type: {chat.type}", - f"Chat ID: {chat.id}"]) + result_msg.extend([hbold("Chat:"), f"Type: {chat.type}", f"Chat ID: {chat.id}"]) if chat.type != ChatType.PRIVATE: result_msg.append(f"Title: {chat.title}") else: result_msg.append(f"Title: {chat.full_name}") - return SendMessage(message.chat.id, '\n'.join(result_msg), reply_to_message_id=message.message_id, - parse_mode=ParseMode.HTML) + return SendMessage( + message.chat.id, + "\n".join(result_msg), + reply_to_message_id=message.message_id, + parse_mode=ParseMode.HTML, + ) async def on_startup(app): # Demonstrate one of the available methods for registering handlers # This command available only in main state (state=None) - dp.register_message_handler(cmd_start, commands=['start']) + dp.register_message_handler(cmd_start, commands=["start"]) # This handler is available in all states at any time. - dp.register_message_handler(cmd_about, commands=['help', 'about'], state='*') - dp.register_message_handler(unknown, content_types=BAD_CONTENT, - func=lambda message: message.chat.type == ChatType.PRIVATE) + dp.register_message_handler(cmd_about, commands=["help", "about"], state="*") + dp.register_message_handler( + unknown, + content_types=BAD_CONTENT, + func=lambda message: message.chat.type == ChatType.PRIVATE, + ) # You are able to register one function handler for multiple conditions - dp.register_message_handler(cancel, commands=['cancel'], state='*') - dp.register_message_handler(cancel, func=lambda message: message.text.lower().strip() in ['cancel'], state='*') + dp.register_message_handler(cancel, commands=["cancel"], state="*") + dp.register_message_handler( + cancel, func=lambda message: message.text.lower().strip() in ["cancel"], state="*" + ) - dp.register_message_handler(cmd_id, commands=['id'], state='*') - dp.register_message_handler(cmd_id, func=lambda message: message.forward_from or - message.reply_to_message and - message.chat.type == ChatType.PRIVATE, state='*') + dp.register_message_handler(cmd_id, commands=["id"], state="*") + dp.register_message_handler( + cmd_id, + func=lambda message: message.forward_from + or message.reply_to_message + and message.chat.type == ChatType.PRIVATE, + state="*", + ) # Get current webhook status webhook = await bot.get_webhook_info() @@ -142,7 +164,7 @@ async def on_startup(app): await bot.delete_webhook() # Set new URL for webhook - await bot.set_webhook(WEBHOOK_URL, certificate=open(WEBHOOK_SSL_CERT, 'rb')) + await bot.set_webhook(WEBHOOK_URL, certificate=open(WEBHOOK_SSL_CERT, "rb")) # If you want to use free certificate signed by LetsEncrypt you need to set only URL without sending certificate. @@ -158,7 +180,7 @@ async def on_shutdown(app): await dp.storage.wait_closed() -if __name__ == '__main__': +if __name__ == "__main__": # Get instance of :class:`aiohttp.web.Application` with configured router. app = get_new_configured_app(dispatcher=dp, path=WEBHOOK_URL_PATH) diff --git a/examples/webhook_example_2.py b/examples/webhook_example_2.py index 75b29c75..742dedb4 100644 --- a/examples/webhook_example_2.py +++ b/examples/webhook_example_2.py @@ -5,15 +5,15 @@ from aiogram import Bot, types from aiogram.dispatcher import Dispatcher from aiogram.utils.executor import start_webhook -API_TOKEN = 'BOT TOKEN HERE' +API_TOKEN = "BOT TOKEN HERE" # webhook settings -WEBHOOK_HOST = 'https://your.domain' -WEBHOOK_PATH = '/path/to/api' +WEBHOOK_HOST = "https://your.domain" +WEBHOOK_PATH = "/path/to/api" WEBHOOK_URL = f"{WEBHOOK_HOST}{WEBHOOK_PATH}" # webserver settings -WEBAPP_HOST = 'localhost' # or ip +WEBAPP_HOST = "localhost" # or ip WEBAPP_PORT = 3001 logging.basicConfig(level=logging.INFO) @@ -38,6 +38,13 @@ async def on_shutdown(dp): pass -if __name__ == '__main__': - start_webhook(dispatcher=dp, webhook_path=WEBHOOK_PATH, on_startup=on_startup, on_shutdown=on_shutdown, - skip_updates=True, host=WEBAPP_HOST, port=WEBAPP_PORT) +if __name__ == "__main__": + start_webhook( + dispatcher=dp, + webhook_path=WEBHOOK_PATH, + on_startup=on_startup, + on_shutdown=on_shutdown, + skip_updates=True, + host=WEBAPP_HOST, + port=WEBAPP_PORT, + ) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..6168acfe --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[tool.black] +line-length = 99 +target-version = ['py37', 'py38'] +exclude = ''' +( + /( + \.eggs + | \.git + | \.tox + | build + | dist + | venv + | docs + )/ + | foo.py +) +''' \ No newline at end of file diff --git a/setup.py b/setup.py index b5c9e61c..0dde5602 100755 --- a/setup.py +++ b/setup.py @@ -15,7 +15,9 @@ WORK_DIR = pathlib.Path(__file__).parent # Check python version MINIMAL_PY_VERSION = (3, 7) if sys.version_info < MINIMAL_PY_VERSION: - raise RuntimeError('aiogram works only with Python {}+'.format('.'.join(map(str, MINIMAL_PY_VERSION)))) + raise RuntimeError( + "aiogram works only with Python {}+".format(".".join(map(str, MINIMAL_PY_VERSION))) + ) def get_version(): @@ -24,11 +26,11 @@ def get_version(): :return: str """ - txt = (WORK_DIR / 'aiogram' / '__init__.py').read_text('utf-8') + txt = (WORK_DIR / "aiogram" / "__init__.py").read_text("utf-8") try: return re.findall(r"^__version__ = '([^']+)'\r?$", txt, re.M)[0] except IndexError: - raise RuntimeError('Unable to determine version.') + raise RuntimeError("Unable to determine version.") def get_description(): @@ -38,7 +40,7 @@ def get_description(): :return: description :rtype: str """ - with open('README.rst', 'r', encoding='utf-8') as f: + with open("README.rst", "r", encoding="utf-8") as f: return f.read() @@ -50,36 +52,36 @@ def get_requirements(filename=None): :rtype: list """ if filename is None: - filename = 'requirements.txt' + filename = "requirements.txt" file = WORK_DIR / filename - install_reqs = parse_requirements(str(file), session='hack') + install_reqs = parse_requirements(str(file), session="hack") return [str(ir.req) for ir in install_reqs] setup( - name='aiogram', + name="aiogram", version=get_version(), - packages=find_packages(exclude=('tests', 'tests.*', 'examples.*', 'docs',)), - url='https://github.com/aiogram/aiogram', - license='MIT', - author='Alex Root Junior', - requires_python='>=3.7', - author_email='jroot.junior@gmail.com', - description='Is a pretty simple and fully asynchronous library for Telegram Bot API', + packages=find_packages(exclude=("tests", "tests.*", "examples.*", "docs")), + url="https://github.com/aiogram/aiogram", + license="MIT", + author="Alex Root Junior", + requires_python=">=3.7", + author_email="jroot.junior@gmail.com", + description="Is a pretty simple and fully asynchronous library for Telegram Bot API", long_description=get_description(), classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Framework :: AsyncIO', - 'Intended Audience :: Developers', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python :: 3.7', - 'Topic :: Software Development :: Libraries :: Application Frameworks', + "Development Status :: 5 - Production/Stable", + "Environment :: Console", + "Framework :: AsyncIO", + "Intended Audience :: Developers", + "Intended Audience :: System Administrators", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3.7", + "Topic :: Software Development :: Libraries :: Application Frameworks", ], install_requires=get_requirements(), - package_data={'': ['requirements.txt']}, + package_data={"": ["requirements.txt"]}, include_package_data=False, ) diff --git a/tests/__init__.py b/tests/__init__.py index 262c9395..aacf3564 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -2,7 +2,7 @@ import aresponses from aiogram import Bot -TOKEN = '123456789:AABBCCDDEEFFaabbccddeeff-1234567890' +TOKEN = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890" class FakeTelegram(aresponses.ResponsesMockServer): @@ -15,11 +15,11 @@ class FakeTelegram(aresponses.ResponsesMockServer): async def __aenter__(self): await super().__aenter__() - _response = self.Response(text=self._body, headers=self._headers, status=200, reason='OK') + _response = self.Response(text=self._body, headers=self._headers, status=200, reason="OK") self.add(self.ANY, response=_response) async def __aexit__(self, exc_type, exc_val, exc_tb): - if hasattr(self, 'monkeypatch'): + if hasattr(self, "monkeypatch"): self.monkeypatch.undo() await super().__aexit__(exc_type, exc_val, exc_tb) @@ -27,14 +27,16 @@ class FakeTelegram(aresponses.ResponsesMockServer): def parse_data(message_dict): import json - _body = '{"ok":true,"result":' + json.dumps(message_dict) + '}' - _headers = {'Server': 'nginx/1.12.2', - 'Date': 'Tue, 03 Apr 2018 16:59:54 GMT', - 'Content-Type': 'application/json', - 'Content-Length': str(len(_body)), - 'Connection': 'keep-alive', - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', - 'Access-Control-Expose-Headers': 'Content-Length,Content-Type,Date,Server,Connection', - 'Strict-Transport-Security': 'max-age=31536000; includeSubdomains'} + _body = '{"ok":true,"result":' + json.dumps(message_dict) + "}" + _headers = { + "Server": "nginx/1.12.2", + "Date": "Tue, 03 Apr 2018 16:59:54 GMT", + "Content-Type": "application/json", + "Content-Length": str(len(_body)), + "Connection": "keep-alive", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Expose-Headers": "Content-Length,Content-Type,Date,Server,Connection", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains", + } return _body, _headers diff --git a/tests/states_group.py b/tests/states_group.py index 8593cea3..bec48a94 100644 --- a/tests/states_group.py +++ b/tests/states_group.py @@ -13,15 +13,15 @@ class MyGroup(StatesGroup): sub_state_1 = State() sub_state_2 = State() - in_custom_group = State(group_name='custom_group') + in_custom_group = State(group_name="custom_group") class NewGroup(StatesGroup): spam = State() - renamed_state = State(state='spam_state') + renamed_state = State(state="spam_state") -alone_state = State('alone') -alone_in_group = State('alone', group_name='home') +alone_state = State("alone") +alone_in_group = State("alone", group_name="home") def test_default_state(): @@ -29,64 +29,76 @@ def test_default_state(): def test_any_state(): - assert any_state.state == '*' + assert any_state.state == "*" def test_alone_state(): - assert alone_state.state == '@:alone' - assert alone_in_group.state == 'home:alone' + assert alone_state.state == "@:alone" + assert alone_in_group.state == "home:alone" def test_group_names(): - assert MyGroup.__group_name__ == 'MyGroup' - assert MyGroup.__full_group_name__ == 'MyGroup' + assert MyGroup.__group_name__ == "MyGroup" + assert MyGroup.__full_group_name__ == "MyGroup" - assert MyGroup.MySubGroup.__group_name__ == 'MySubGroup' - assert MyGroup.MySubGroup.__full_group_name__ == 'MyGroup.MySubGroup' + assert MyGroup.MySubGroup.__group_name__ == "MySubGroup" + assert MyGroup.MySubGroup.__full_group_name__ == "MyGroup.MySubGroup" - assert MyGroup.MySubGroup.NewGroup.__group_name__ == 'NewGroup' - assert MyGroup.MySubGroup.NewGroup.__full_group_name__ == 'MyGroup.MySubGroup.NewGroup' + assert MyGroup.MySubGroup.NewGroup.__group_name__ == "NewGroup" + assert MyGroup.MySubGroup.NewGroup.__full_group_name__ == "MyGroup.MySubGroup.NewGroup" def test_custom_group_in_group(): - assert MyGroup.MySubGroup.in_custom_group.state == 'custom_group:in_custom_group' + assert MyGroup.MySubGroup.in_custom_group.state == "custom_group:in_custom_group" def test_custom_state_name_in_group(): - assert MyGroup.MySubGroup.NewGroup.renamed_state.state == 'MyGroup.MySubGroup.NewGroup:spam_state' + assert ( + MyGroup.MySubGroup.NewGroup.renamed_state.state == "MyGroup.MySubGroup.NewGroup:spam_state" + ) def test_group_states_names(): assert len(MyGroup.states) == 3 assert len(MyGroup.all_states) == 9 - assert MyGroup.states_names == ('MyGroup:state', 'MyGroup:state_1', 'MyGroup:state_2') + assert MyGroup.states_names == ("MyGroup:state", "MyGroup:state_1", "MyGroup:state_2") assert MyGroup.MySubGroup.states_names == ( - 'MyGroup.MySubGroup:sub_state', 'MyGroup.MySubGroup:sub_state_1', 'MyGroup.MySubGroup:sub_state_2', - 'custom_group:in_custom_group') + "MyGroup.MySubGroup:sub_state", + "MyGroup.MySubGroup:sub_state_1", + "MyGroup.MySubGroup:sub_state_2", + "custom_group:in_custom_group", + ) assert MyGroup.MySubGroup.NewGroup.states_names == ( - 'MyGroup.MySubGroup.NewGroup:spam', 'MyGroup.MySubGroup.NewGroup:spam_state') + "MyGroup.MySubGroup.NewGroup:spam", + "MyGroup.MySubGroup.NewGroup:spam_state", + ) assert MyGroup.all_states_names == ( - 'MyGroup:state', 'MyGroup:state_1', 'MyGroup:state_2', - 'MyGroup.MySubGroup:sub_state', - 'MyGroup.MySubGroup:sub_state_1', - 'MyGroup.MySubGroup:sub_state_2', - 'custom_group:in_custom_group', - 'MyGroup.MySubGroup.NewGroup:spam', - 'MyGroup.MySubGroup.NewGroup:spam_state') + "MyGroup:state", + "MyGroup:state_1", + "MyGroup:state_2", + "MyGroup.MySubGroup:sub_state", + "MyGroup.MySubGroup:sub_state_1", + "MyGroup.MySubGroup:sub_state_2", + "custom_group:in_custom_group", + "MyGroup.MySubGroup.NewGroup:spam", + "MyGroup.MySubGroup.NewGroup:spam_state", + ) assert MyGroup.MySubGroup.all_states_names == ( - 'MyGroup.MySubGroup:sub_state', - 'MyGroup.MySubGroup:sub_state_1', - 'MyGroup.MySubGroup:sub_state_2', - 'custom_group:in_custom_group', - 'MyGroup.MySubGroup.NewGroup:spam', - 'MyGroup.MySubGroup.NewGroup:spam_state') + "MyGroup.MySubGroup:sub_state", + "MyGroup.MySubGroup:sub_state_1", + "MyGroup.MySubGroup:sub_state_2", + "custom_group:in_custom_group", + "MyGroup.MySubGroup.NewGroup:spam", + "MyGroup.MySubGroup.NewGroup:spam_state", + ) assert MyGroup.MySubGroup.NewGroup.all_states_names == ( - 'MyGroup.MySubGroup.NewGroup:spam', - 'MyGroup.MySubGroup.NewGroup:spam_state') + "MyGroup.MySubGroup.NewGroup:spam", + "MyGroup.MySubGroup.NewGroup:spam_state", + ) def test_root_element(): diff --git a/tests/test_bot.py b/tests/test_bot.py index 448f8dda..6f64fc13 100644 --- a/tests/test_bot.py +++ b/tests/test_bot.py @@ -3,7 +3,7 @@ import pytest from aiogram import Bot, types -TOKEN = '123456789:AABBCCDDEEFFaabbccddeeff-1234567890' +TOKEN = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890" class FakeTelegram(aresponses.ResponsesMockServer): @@ -13,23 +13,25 @@ class FakeTelegram(aresponses.ResponsesMockServer): async def __aenter__(self): await super().__aenter__() - _response = self.Response(text=self._body, headers=self._headers, status=200, reason='OK') + _response = self.Response(text=self._body, headers=self._headers, status=200, reason="OK") self.add(self.ANY, response=_response) @staticmethod def parse_data(message_dict): import json - _body = '{"ok":true,"result":' + json.dumps(message_dict) + '}' - _headers = {'Server': 'nginx/1.12.2', - 'Date': 'Tue, 03 Apr 2018 16:59:54 GMT', - 'Content-Type': 'application/json', - 'Content-Length': str(len(_body)), - 'Connection': 'keep-alive', - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', - 'Access-Control-Expose-Headers': 'Content-Length,Content-Type,Date,Server,Connection', - 'Strict-Transport-Security': 'max-age=31536000; includeSubdomains'} + _body = '{"ok":true,"result":' + json.dumps(message_dict) + "}" + _headers = { + "Server": "nginx/1.12.2", + "Date": "Tue, 03 Apr 2018 16:59:54 GMT", + "Content-Type": "application/json", + "Content-Length": str(len(_body)), + "Connection": "keep-alive", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, OPTIONS", + "Access-Control-Expose-Headers": "Content-Length,Content-Type,Date,Server,Connection", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains", + } return _body, _headers @@ -46,6 +48,7 @@ async def bot(event_loop): async def test_get_me(bot: Bot, event_loop): """ getMe method test """ from .types.dataset import USER + user = types.User(**USER) async with FakeTelegram(message_dict=USER, loop=event_loop): @@ -57,6 +60,7 @@ async def test_get_me(bot: Bot, event_loop): async def test_send_message(bot: Bot, event_loop): """ sendMessage method test """ from .types.dataset import MESSAGE + msg = types.Message(**MESSAGE) async with FakeTelegram(message_dict=MESSAGE, loop=event_loop): @@ -68,11 +72,15 @@ async def test_send_message(bot: Bot, event_loop): async def test_forward_message(bot: Bot, event_loop): """ forwardMessage method test """ from .types.dataset import FORWARDED_MESSAGE + msg = types.Message(**FORWARDED_MESSAGE) async with FakeTelegram(message_dict=FORWARDED_MESSAGE, loop=event_loop): - result = await bot.forward_message(chat_id=msg.chat.id, from_chat_id=msg.forward_from_chat.id, - message_id=msg.forward_from_message_id) + result = await bot.forward_message( + chat_id=msg.chat.id, + from_chat_id=msg.forward_from_chat.id, + message_id=msg.forward_from_message_id, + ) assert result == msg @@ -80,12 +88,18 @@ async def test_forward_message(bot: Bot, event_loop): async def test_send_photo(bot: Bot, event_loop): """ sendPhoto method test with file_id """ from .types.dataset import MESSAGE_WITH_PHOTO, PHOTO + msg = types.Message(**MESSAGE_WITH_PHOTO) photo = types.PhotoSize(**PHOTO) async with FakeTelegram(message_dict=MESSAGE_WITH_PHOTO, loop=event_loop): - result = await bot.send_photo(msg.chat.id, photo=photo.file_id, caption=msg.caption, - parse_mode=types.ParseMode.HTML, disable_notification=False) + result = await bot.send_photo( + msg.chat.id, + photo=photo.file_id, + caption=msg.caption, + parse_mode=types.ParseMode.HTML, + disable_notification=False, + ) assert result == msg @@ -93,12 +107,20 @@ async def test_send_photo(bot: Bot, event_loop): async def test_send_audio(bot: Bot, event_loop): """ sendAudio method test with file_id """ from .types.dataset import MESSAGE_WITH_AUDIO + msg = types.Message(**MESSAGE_WITH_AUDIO) async with FakeTelegram(message_dict=MESSAGE_WITH_AUDIO, loop=event_loop): - result = await bot.send_audio(chat_id=msg.chat.id, audio=msg.audio.file_id, caption=msg.caption, - parse_mode=types.ParseMode.HTML, duration=msg.audio.duration, - performer=msg.audio.performer, title=msg.audio.title, disable_notification=False) + result = await bot.send_audio( + chat_id=msg.chat.id, + audio=msg.audio.file_id, + caption=msg.caption, + parse_mode=types.ParseMode.HTML, + duration=msg.audio.duration, + performer=msg.audio.performer, + title=msg.audio.title, + disable_notification=False, + ) assert result == msg @@ -106,11 +128,17 @@ async def test_send_audio(bot: Bot, event_loop): async def test_send_document(bot: Bot, event_loop): """ sendDocument method test with file_id """ from .types.dataset import MESSAGE_WITH_DOCUMENT + msg = types.Message(**MESSAGE_WITH_DOCUMENT) async with FakeTelegram(message_dict=MESSAGE_WITH_DOCUMENT, loop=event_loop): - result = await bot.send_document(chat_id=msg.chat.id, document=msg.document.file_id, caption=msg.caption, - parse_mode=types.ParseMode.HTML, disable_notification=False) + result = await bot.send_document( + chat_id=msg.chat.id, + document=msg.document.file_id, + caption=msg.caption, + parse_mode=types.ParseMode.HTML, + disable_notification=False, + ) assert result == msg @@ -118,14 +146,22 @@ async def test_send_document(bot: Bot, event_loop): async def test_send_video(bot: Bot, event_loop): """ sendVideo method test with file_id """ from .types.dataset import MESSAGE_WITH_VIDEO, VIDEO + msg = types.Message(**MESSAGE_WITH_VIDEO) video = types.Video(**VIDEO) async with FakeTelegram(message_dict=MESSAGE_WITH_VIDEO, loop=event_loop): - result = await bot.send_video(chat_id=msg.chat.id, video=video.file_id, duration=video.duration, - width=video.width, height=video.height, caption=msg.caption, - parse_mode=types.ParseMode.HTML, supports_streaming=True, - disable_notification=False) + result = await bot.send_video( + chat_id=msg.chat.id, + video=video.file_id, + duration=video.duration, + width=video.width, + height=video.height, + caption=msg.caption, + parse_mode=types.ParseMode.HTML, + supports_streaming=True, + disable_notification=False, + ) assert result == msg @@ -133,13 +169,19 @@ async def test_send_video(bot: Bot, event_loop): async def test_send_voice(bot: Bot, event_loop): """ sendVoice method test with file_id """ from .types.dataset import MESSAGE_WITH_VOICE, VOICE + msg = types.Message(**MESSAGE_WITH_VOICE) voice = types.Voice(**VOICE) async with FakeTelegram(message_dict=MESSAGE_WITH_VOICE, loop=event_loop): - result = await bot.send_voice(chat_id=msg.chat.id, voice=voice.file_id, caption=msg.caption, - parse_mode=types.ParseMode.HTML, duration=voice.duration, - disable_notification=False) + result = await bot.send_voice( + chat_id=msg.chat.id, + voice=voice.file_id, + caption=msg.caption, + parse_mode=types.ParseMode.HTML, + duration=voice.duration, + disable_notification=False, + ) assert result == msg @@ -147,13 +189,18 @@ async def test_send_voice(bot: Bot, event_loop): async def test_send_video_note(bot: Bot, event_loop): """ sendVideoNote method test with file_id """ from .types.dataset import MESSAGE_WITH_VIDEO_NOTE, VIDEO_NOTE + msg = types.Message(**MESSAGE_WITH_VIDEO_NOTE) video_note = types.VideoNote(**VIDEO_NOTE) async with FakeTelegram(message_dict=MESSAGE_WITH_VIDEO_NOTE, loop=event_loop): - result = await bot.send_video_note(chat_id=msg.chat.id, video_note=video_note.file_id, - duration=video_note.duration, length=video_note.length, - disable_notification=False) + result = await bot.send_video_note( + chat_id=msg.chat.id, + video_note=video_note.file_id, + duration=video_note.duration, + length=video_note.length, + disable_notification=False, + ) assert result == msg @@ -161,11 +208,17 @@ async def test_send_video_note(bot: Bot, event_loop): async def test_send_media_group(bot: Bot, event_loop): """ sendMediaGroup method test with file_id """ from .types.dataset import MESSAGE_WITH_MEDIA_GROUP, PHOTO + msg = types.Message(**MESSAGE_WITH_MEDIA_GROUP) photo = types.PhotoSize(**PHOTO) - media = [types.InputMediaPhoto(media=photo.file_id), types.InputMediaPhoto(media=photo.file_id)] + media = [ + types.InputMediaPhoto(media=photo.file_id), + types.InputMediaPhoto(media=photo.file_id), + ] - async with FakeTelegram(message_dict=[MESSAGE_WITH_MEDIA_GROUP, MESSAGE_WITH_MEDIA_GROUP], loop=event_loop): + async with FakeTelegram( + message_dict=[MESSAGE_WITH_MEDIA_GROUP, MESSAGE_WITH_MEDIA_GROUP], loop=event_loop + ): result = await bot.send_media_group(msg.chat.id, media=media, disable_notification=False) assert len(result) == len(media) assert result.pop().media_group_id @@ -175,12 +228,18 @@ async def test_send_media_group(bot: Bot, event_loop): async def test_send_location(bot: Bot, event_loop): """ sendLocation method test """ from .types.dataset import MESSAGE_WITH_LOCATION, LOCATION + msg = types.Message(**MESSAGE_WITH_LOCATION) location = types.Location(**LOCATION) async with FakeTelegram(message_dict=MESSAGE_WITH_LOCATION, loop=event_loop): - result = await bot.send_location(msg.chat.id, latitude=location.latitude, longitude=location.longitude, - live_period=10, disable_notification=False) + result = await bot.send_location( + msg.chat.id, + latitude=location.latitude, + longitude=location.longitude, + live_period=10, + disable_notification=False, + ) assert result == msg @@ -188,13 +247,18 @@ async def test_send_location(bot: Bot, event_loop): async def test_edit_message_live_location_by_bot(bot: Bot, event_loop): """ editMessageLiveLocation method test """ from .types.dataset import MESSAGE_WITH_LOCATION, LOCATION + msg = types.Message(**MESSAGE_WITH_LOCATION) location = types.Location(**LOCATION) # editing bot message async with FakeTelegram(message_dict=MESSAGE_WITH_LOCATION, loop=event_loop): - result = await bot.edit_message_live_location(chat_id=msg.chat.id, message_id=msg.message_id, - latitude=location.latitude, longitude=location.longitude) + result = await bot.edit_message_live_location( + chat_id=msg.chat.id, + message_id=msg.message_id, + latitude=location.latitude, + longitude=location.longitude, + ) assert result == msg @@ -202,13 +266,18 @@ async def test_edit_message_live_location_by_bot(bot: Bot, event_loop): async def test_edit_message_live_location_by_user(bot: Bot, event_loop): """ editMessageLiveLocation method test """ from .types.dataset import MESSAGE_WITH_LOCATION, LOCATION + msg = types.Message(**MESSAGE_WITH_LOCATION) location = types.Location(**LOCATION) # editing user's message async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.edit_message_live_location(chat_id=msg.chat.id, message_id=msg.message_id, - latitude=location.latitude, longitude=location.longitude) + result = await bot.edit_message_live_location( + chat_id=msg.chat.id, + message_id=msg.message_id, + latitude=location.latitude, + longitude=location.longitude, + ) assert isinstance(result, bool) and result is True @@ -216,11 +285,14 @@ async def test_edit_message_live_location_by_user(bot: Bot, event_loop): async def test_stop_message_live_location_by_bot(bot: Bot, event_loop): """ stopMessageLiveLocation method test """ from .types.dataset import MESSAGE_WITH_LOCATION + msg = types.Message(**MESSAGE_WITH_LOCATION) # stopping bot message async with FakeTelegram(message_dict=MESSAGE_WITH_LOCATION, loop=event_loop): - result = await bot.stop_message_live_location(chat_id=msg.chat.id, message_id=msg.message_id) + result = await bot.stop_message_live_location( + chat_id=msg.chat.id, message_id=msg.message_id + ) assert result == msg @@ -228,11 +300,14 @@ async def test_stop_message_live_location_by_bot(bot: Bot, event_loop): async def test_stop_message_live_location_by_user(bot: Bot, event_loop): """ stopMessageLiveLocation method test """ from .types.dataset import MESSAGE_WITH_LOCATION + msg = types.Message(**MESSAGE_WITH_LOCATION) # stopping user's message async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.stop_message_live_location(chat_id=msg.chat.id, message_id=msg.message_id) + result = await bot.stop_message_live_location( + chat_id=msg.chat.id, message_id=msg.message_id + ) assert isinstance(result, bool) assert result is True @@ -241,14 +316,21 @@ async def test_stop_message_live_location_by_user(bot: Bot, event_loop): async def test_send_venue(bot: Bot, event_loop): """ sendVenue method test """ from .types.dataset import MESSAGE_WITH_VENUE, VENUE, LOCATION + msg = types.Message(**MESSAGE_WITH_VENUE) location = types.Location(**LOCATION) venue = types.Venue(**VENUE) async with FakeTelegram(message_dict=MESSAGE_WITH_VENUE, loop=event_loop): - result = await bot.send_venue(msg.chat.id, latitude=location.latitude, longitude=location.longitude, - title=venue.title, address=venue.address, foursquare_id=venue.foursquare_id, - disable_notification=False) + result = await bot.send_venue( + msg.chat.id, + latitude=location.latitude, + longitude=location.longitude, + title=venue.title, + address=venue.address, + foursquare_id=venue.foursquare_id, + disable_notification=False, + ) assert result == msg @@ -256,12 +338,18 @@ async def test_send_venue(bot: Bot, event_loop): async def test_send_contact(bot: Bot, event_loop): """ sendContact method test """ from .types.dataset import MESSAGE_WITH_CONTACT, CONTACT + msg = types.Message(**MESSAGE_WITH_CONTACT) contact = types.Contact(**CONTACT) async with FakeTelegram(message_dict=MESSAGE_WITH_CONTACT, loop=event_loop): - result = await bot.send_contact(msg.chat.id, phone_number=contact.phone_number, first_name=contact.first_name, - last_name=contact.last_name, disable_notification=False) + result = await bot.send_contact( + msg.chat.id, + phone_number=contact.phone_number, + first_name=contact.first_name, + last_name=contact.last_name, + disable_notification=False, + ) assert result == msg @@ -269,6 +357,7 @@ async def test_send_contact(bot: Bot, event_loop): async def test_send_chat_action(bot: Bot, event_loop): """ sendChatAction method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): @@ -281,6 +370,7 @@ async def test_send_chat_action(bot: Bot, event_loop): async def test_get_user_profile_photo(bot: Bot, event_loop): """ getUserProfilePhotos method test """ from .types.dataset import USER_PROFILE_PHOTOS, USER + user = types.User(**USER) async with FakeTelegram(message_dict=USER_PROFILE_PHOTOS, loop=event_loop): @@ -292,6 +382,7 @@ async def test_get_user_profile_photo(bot: Bot, event_loop): async def test_get_file(bot: Bot, event_loop): """ getFile method test """ from .types.dataset import FILE + file = types.File(**FILE) async with FakeTelegram(message_dict=FILE, loop=event_loop): @@ -303,6 +394,7 @@ async def test_get_file(bot: Bot, event_loop): async def test_kick_chat_member(bot: Bot, event_loop): """ kickChatMember method test """ from .types.dataset import USER, CHAT + user = types.User(**USER) chat = types.Chat(**CHAT) @@ -316,6 +408,7 @@ async def test_kick_chat_member(bot: Bot, event_loop): async def test_unban_chat_member(bot: Bot, event_loop): """ unbanChatMember method test """ from .types.dataset import USER, CHAT + user = types.User(**USER) chat = types.Chat(**CHAT) @@ -329,13 +422,20 @@ async def test_unban_chat_member(bot: Bot, event_loop): async def test_restrict_chat_member(bot: Bot, event_loop): """ restrictChatMember method test """ from .types.dataset import USER, CHAT + user = types.User(**USER) chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.restrict_chat_member(chat_id=chat.id, user_id=user.id, can_add_web_page_previews=False, - can_send_media_messages=False, can_send_messages=False, - can_send_other_messages=False, until_date=123) + result = await bot.restrict_chat_member( + chat_id=chat.id, + user_id=user.id, + can_add_web_page_previews=False, + can_send_media_messages=False, + can_send_messages=False, + can_send_other_messages=False, + until_date=123, + ) assert isinstance(result, bool) assert result is True @@ -344,14 +444,23 @@ async def test_restrict_chat_member(bot: Bot, event_loop): async def test_promote_chat_member(bot: Bot, event_loop): """ promoteChatMember method test """ from .types.dataset import USER, CHAT + user = types.User(**USER) chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.promote_chat_member(chat_id=chat.id, user_id=user.id, can_change_info=True, - can_delete_messages=True, can_edit_messages=True, - can_invite_users=True, can_pin_messages=True, can_post_messages=True, - can_promote_members=True, can_restrict_members=True) + result = await bot.promote_chat_member( + chat_id=chat.id, + user_id=user.id, + can_change_info=True, + can_delete_messages=True, + can_edit_messages=True, + can_invite_users=True, + can_pin_messages=True, + can_post_messages=True, + can_promote_members=True, + can_restrict_members=True, + ) assert isinstance(result, bool) assert result is True @@ -360,6 +469,7 @@ async def test_promote_chat_member(bot: Bot, event_loop): async def test_export_chat_invite_link(bot: Bot, event_loop): """ exportChatInviteLink method test """ from .types.dataset import CHAT, INVITE_LINK + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=INVITE_LINK, loop=event_loop): @@ -371,6 +481,7 @@ async def test_export_chat_invite_link(bot: Bot, event_loop): async def test_delete_chat_photo(bot: Bot, event_loop): """ deleteChatPhoto method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): @@ -383,10 +494,11 @@ async def test_delete_chat_photo(bot: Bot, event_loop): async def test_set_chat_title(bot: Bot, event_loop): """ setChatTitle method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.set_chat_title(chat_id=chat.id, title='Test title') + result = await bot.set_chat_title(chat_id=chat.id, title="Test title") assert isinstance(result, bool) assert result is True @@ -395,10 +507,11 @@ async def test_set_chat_title(bot: Bot, event_loop): async def test_set_chat_description(bot: Bot, event_loop): """ setChatDescription method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.set_chat_description(chat_id=chat.id, description='Test description') + result = await bot.set_chat_description(chat_id=chat.id, description="Test description") assert isinstance(result, bool) assert result is True @@ -407,11 +520,13 @@ async def test_set_chat_description(bot: Bot, event_loop): async def test_pin_chat_message(bot: Bot, event_loop): """ pinChatMessage method test """ from .types.dataset import MESSAGE + message = types.Message(**MESSAGE) async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.pin_chat_message(chat_id=message.chat.id, message_id=message.message_id, - disable_notification=False) + result = await bot.pin_chat_message( + chat_id=message.chat.id, message_id=message.message_id, disable_notification=False + ) assert isinstance(result, bool) assert result is True @@ -420,6 +535,7 @@ async def test_pin_chat_message(bot: Bot, event_loop): async def test_unpin_chat_message(bot: Bot, event_loop): """ unpinChatMessage method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): @@ -432,6 +548,7 @@ async def test_unpin_chat_message(bot: Bot, event_loop): async def test_leave_chat(bot: Bot, event_loop): """ leaveChat method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): @@ -444,6 +561,7 @@ async def test_leave_chat(bot: Bot, event_loop): async def test_get_chat(bot: Bot, event_loop): """ getChat method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=CHAT, loop=event_loop): @@ -455,6 +573,7 @@ async def test_get_chat(bot: Bot, event_loop): async def test_get_chat_administrators(bot: Bot, event_loop): """ getChatAdministrators method test """ from .types.dataset import CHAT, CHAT_MEMBER + chat = types.Chat(**CHAT) member = types.ChatMember(**CHAT_MEMBER) @@ -468,6 +587,7 @@ async def test_get_chat_administrators(bot: Bot, event_loop): async def test_get_chat_members_count(bot: Bot, event_loop): """ getChatMembersCount method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) count = 5 @@ -480,6 +600,7 @@ async def test_get_chat_members_count(bot: Bot, event_loop): async def test_get_chat_member(bot: Bot, event_loop): """ getChatMember method test """ from .types.dataset import CHAT, CHAT_MEMBER + chat = types.Chat(**CHAT) member = types.ChatMember(**CHAT_MEMBER) @@ -493,10 +614,13 @@ async def test_get_chat_member(bot: Bot, event_loop): async def test_set_chat_sticker_set(bot: Bot, event_loop): """ setChatStickerSet method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.set_chat_sticker_set(chat_id=chat.id, sticker_set_name='aiogram_stickers') + result = await bot.set_chat_sticker_set( + chat_id=chat.id, sticker_set_name="aiogram_stickers" + ) assert isinstance(result, bool) assert result is True @@ -505,6 +629,7 @@ async def test_set_chat_sticker_set(bot: Bot, event_loop): async def test_delete_chat_sticker_set(bot: Bot, event_loop): """ setChatStickerSet method test """ from .types.dataset import CHAT + chat = types.Chat(**CHAT) async with FakeTelegram(message_dict=True, loop=event_loop): @@ -518,7 +643,7 @@ async def test_answer_callback_query(bot: Bot, event_loop): """ answerCallbackQuery method test """ async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.answer_callback_query(callback_query_id='QuERyId', text='Test Answer') + result = await bot.answer_callback_query(callback_query_id="QuERyId", text="Test Answer") assert isinstance(result, bool) assert result is True @@ -527,11 +652,14 @@ async def test_answer_callback_query(bot: Bot, event_loop): async def test_edit_message_text_by_bot(bot: Bot, event_loop): """ editMessageText method test """ from .types.dataset import EDITED_MESSAGE + msg = types.Message(**EDITED_MESSAGE) # message by bot async with FakeTelegram(message_dict=EDITED_MESSAGE, loop=event_loop): - result = await bot.edit_message_text(text=msg.text, chat_id=msg.chat.id, message_id=msg.message_id) + result = await bot.edit_message_text( + text=msg.text, chat_id=msg.chat.id, message_id=msg.message_id + ) assert result == msg @@ -539,10 +667,13 @@ async def test_edit_message_text_by_bot(bot: Bot, event_loop): async def test_edit_message_text_by_user(bot: Bot, event_loop): """ editMessageText method test """ from .types.dataset import EDITED_MESSAGE + msg = types.Message(**EDITED_MESSAGE) # message by user async with FakeTelegram(message_dict=True, loop=event_loop): - result = await bot.edit_message_text(text=msg.text, chat_id=msg.chat.id, message_id=msg.message_id) + result = await bot.edit_message_text( + text=msg.text, chat_id=msg.chat.id, message_id=msg.message_id + ) assert isinstance(result, bool) assert result is True diff --git a/tests/test_dispatcher.py b/tests/test_dispatcher.py index 6ebaf472..f41d1a51 100644 --- a/tests/test_dispatcher.py +++ b/tests/test_dispatcher.py @@ -8,8 +8,7 @@ pytestmark = pytest.mark.asyncio @pytest.yield_fixture() async def bot(event_loop): """ Bot fixture """ - _bot = Bot(token='123456789:AABBCCDDEEFFaabbccddeeff-1234567890', - loop=event_loop) + _bot = Bot(token="123456789:AABBCCDDEEFFaabbccddeeff-1234567890", loop=event_loop) yield _bot await _bot.close() @@ -25,7 +24,7 @@ class TestDispatcherInit: dp = Dispatcher(bot=bot) assert isinstance(dp, Dispatcher) - @pytest.mark.parametrize("bot_instance", [None, Bot, 123, 'abc']) + @pytest.mark.parametrize("bot_instance", [None, Bot, 123, "abc"]) async def test_wrong_bot_instance(self, bot_instance): """ User provides wrong data to 'bot' argument. diff --git a/tests/test_message.py b/tests/test_message.py index 996529f3..9b9af582 100644 --- a/tests/test_message.py +++ b/tests/test_message.py @@ -26,6 +26,7 @@ async def message(bot, event_loop): :type event_loop: BaseEventLoop """ from .types.dataset import MESSAGE + msg = types.Message(**MESSAGE) async with FakeTelegram(message_dict=MESSAGE, loop=event_loop): @@ -44,4 +45,4 @@ class TestMiscCases: :return: RuntimeError with reason and help """ with pytest.raises(RuntimeError): - await message.edit_text('test_calling_bot_not_from_context') + await message.edit_text("test_calling_bot_not_from_context") diff --git a/tests/test_token.py b/tests/test_token.py index b8a6087f..f11ff9e4 100644 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -3,24 +3,24 @@ import pytest from aiogram.bot import api from aiogram.utils import auth_widget, exceptions -VALID_TOKEN = '123456789:AABBCCDDEEFFaabbccddeeff-1234567890' -INVALID_TOKEN = '123456789:AABBCCDDEEFFaabbccddeeff 123456789' # Space in token and wrong length +VALID_TOKEN = "123456789:AABBCCDDEEFFaabbccddeeff-1234567890" +INVALID_TOKEN = "123456789:AABBCCDDEEFFaabbccddeeff 123456789" # Space in token and wrong length VALID_DATA = { - 'date': 1525385236, - 'first_name': 'Test', - 'last_name': 'User', - 'id': 123456789, - 'username': 'username', - 'hash': '69a9871558fbbe4cd0dbaba52fa1cc4f38315d3245b7504381a64139fb024b5b' + "date": 1525385236, + "first_name": "Test", + "last_name": "User", + "id": 123456789, + "username": "username", + "hash": "69a9871558fbbe4cd0dbaba52fa1cc4f38315d3245b7504381a64139fb024b5b", } INVALID_DATA = { - 'date': 1525385237, - 'first_name': 'Test', - 'last_name': 'User', - 'id': 123456789, - 'username': 'username', - 'hash': '69a9871558fbbe4cd0dbaba52fa1cc4f38315d3245b7504381a64139fb024b5b' + "date": 1525385237, + "first_name": "Test", + "last_name": "User", + "id": 123456789, + "username": "username", + "hash": "69a9871558fbbe4cd0dbaba52fa1cc4f38315d3245b7504381a64139fb024b5b", } diff --git a/tests/types/dataset.py b/tests/types/dataset.py index 4167eae1..acfb2a34 100644 --- a/tests/types/dataset.py +++ b/tests/types/dataset.py @@ -8,7 +8,7 @@ USER = { "first_name": "FirstName", "last_name": "LastName", "username": "username", - "language_code": "ru" + "language_code": "ru", } CHAT = { @@ -16,14 +16,14 @@ CHAT = { "first_name": "FirstName", "last_name": "LastName", "username": "username", - "type": "private" + "type": "private", } PHOTO = { "file_id": "AgADBAADFak0G88YZAf8OAug7bHyS9x2ZxkABHVfpJywcloRAAGAAQABAg", "file_size": 1101, "width": 90, - "height": 51 + "height": 51, } AUDIO = { @@ -32,7 +32,7 @@ AUDIO = { "title": "The Best Song", "performer": "The Best Singer", "file_id": "CQADAgADbQEAAsnrIUpNoRRNsH7_hAI", - "file_size": 9507774 + "file_size": 9507774, } CHAT_MEMBER = { @@ -44,20 +44,16 @@ CHAT_MEMBER = { "can_invite_users": True, "can_restrict_members": True, "can_pin_messages": True, - "can_promote_members": False + "can_promote_members": False, } -CONTACT = { - "phone_number": "88005553535", - "first_name": "John", - "last_name": "Smith", -} +CONTACT = {"phone_number": "88005553535", "first_name": "John", "last_name": "Smith"} DOCUMENT = { "file_name": "test.docx", "mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "file_id": "BQADAgADpgADy_JxS66XQTBRHFleAg", - "file_size": 21331 + "file_size": 21331, } ANIMATION = { @@ -65,74 +61,46 @@ ANIMATION = { "mime_type": "video/mp4", "thumb": PHOTO, "file_id": "CgADBAAD4DUAAoceZAe2WiE9y0crrAI", - "file_size": 65837 + "file_size": 65837, } -ENTITY_BOLD = { - "offset": 5, - "length": 2, - "type": "bold" -} +ENTITY_BOLD = {"offset": 5, "length": 2, "type": "bold"} -ENTITY_ITALIC = { - "offset": 8, - "length": 1, - "type": "italic" -} +ENTITY_ITALIC = {"offset": 8, "length": 1, "type": "italic"} -ENTITY_LINK = { - "offset": 10, - "length": 6, - "type": "text_link", - "url": "http://google.com/" -} +ENTITY_LINK = {"offset": 10, "length": 6, "type": "text_link", "url": "http://google.com/"} -ENTITY_CODE = { - "offset": 17, - "length": 7, - "type": "code" -} +ENTITY_CODE = {"offset": 17, "length": 7, "type": "code"} -ENTITY_PRE = { - "offset": 30, - "length": 4, - "type": "pre" -} +ENTITY_PRE = {"offset": 30, "length": 4, "type": "pre"} -ENTITY_MENTION = { - "offset": 47, - "length": 9, - "type": "mention" -} +ENTITY_MENTION = {"offset": 47, "length": 9, "type": "mention"} GAME = { "title": "Karate Kido", "description": "No trees were harmed in the making of this game :)", "photo": [PHOTO, PHOTO, PHOTO], - "animation": ANIMATION + "animation": ANIMATION, } INVOICE = { "title": "Working Time Machine", "description": "Want to visit your great-great-great-grandparents? " - "Make a fortune at the races? " - "Shake hands with Hammurabi and take a stroll in the Hanging Gardens? " - "Order our Working Time Machine today!", + "Make a fortune at the races? " + "Shake hands with Hammurabi and take a stroll in the Hanging Gardens? " + "Order our Working Time Machine today!", "start_parameter": "time-machine-example", "currency": "USD", - "total_amount": 6250 + "total_amount": 6250, } -LOCATION = { - "latitude": 50.693416, - "longitude": 30.624605 -} +LOCATION = {"latitude": 50.693416, "longitude": 30.624605} VENUE = { "location": LOCATION, "title": "Venue Name", "address": "Venue Address", - "foursquare_id": "4e6f2cec483bad563d150f98" + "foursquare_id": "4e6f2cec483bad563d150f98", } SHIPPING_ADDRESS = { @@ -141,7 +109,7 @@ SHIPPING_ADDRESS = { "city": "DefaultCity", "street_line1": "Central", "street_line2": "Middle", - "post_code": "424242" + "post_code": "424242", } STICKER = { @@ -153,10 +121,10 @@ STICKER = { "file_id": "AAbbCCddEEffGGhh1234567890", "file_size": 1234, "width": 128, - "height": 128 + "height": 128, }, "file_id": "AAbbCCddEEffGGhh1234567890", - "file_size": 12345 + "file_size": 12345, } SUCCESSFUL_PAYMENT = { @@ -164,7 +132,7 @@ SUCCESSFUL_PAYMENT = { "total_amount": 6250, "invoice_payload": "HAPPY FRIDAYS COUPON", "telegram_payment_charge_id": "_", - "provider_payment_charge_id": "12345678901234_test" + "provider_payment_charge_id": "12345678901234_test", } VIDEO = { @@ -174,7 +142,7 @@ VIDEO = { "mime_type": "video/quicktime", "thumb": PHOTO, "file_id": "BAADAgpAADdawy_JxS72kRvV3cortAg", - "file_size": 10099782 + "file_size": 10099782, } VIDEO_NOTE = { @@ -182,14 +150,14 @@ VIDEO_NOTE = { "length": 240, "thumb": PHOTO, "file_id": "AbCdEfGhIjKlMnOpQrStUvWxYz", - "file_size": 186562 + "file_size": 186562, } VOICE = { "duration": 1, "mime_type": "audio/ogg", "file_id": "AwADawAgADADy_JxS2gopIVIIxlhAg", - "file_size": 4321 + "file_size": 4321, } CALLBACK_QUERY = {} @@ -206,7 +174,7 @@ EDITED_MESSAGE = { "chat": CHAT, "date": 1508825372, "edit_date": 1508825379, - "text": "hi there (edited)" + "text": "hi there (edited)", } FORWARDED_MESSAGE = { @@ -218,8 +186,15 @@ FORWARDED_MESSAGE = { "forward_from_message_id": 123, "forward_date": 1522749037, "text": "Forwarded text with entities from public channel ", - "entities": [ENTITY_BOLD, ENTITY_CODE, ENTITY_ITALIC, ENTITY_LINK, - ENTITY_LINK, ENTITY_MENTION, ENTITY_PRE] + "entities": [ + ENTITY_BOLD, + ENTITY_CODE, + ENTITY_ITALIC, + ENTITY_LINK, + ENTITY_LINK, + ENTITY_MENTION, + ENTITY_PRE, + ], } INLINE_QUERY = {} @@ -229,7 +204,7 @@ MESSAGE = { "from": USER, "chat": CHAT, "date": 1508709711, - "text": "Hi, world!" + "text": "Hi, world!", } MESSAGE_WITH_AUDIO = { @@ -238,7 +213,7 @@ MESSAGE_WITH_AUDIO = { "chat": CHAT, "date": 1508739776, "audio": AUDIO, - "caption": "This is my favourite song" + "caption": "This is my favourite song", } MESSAGE_WITH_AUTHOR_SIGNATURE = {} @@ -250,7 +225,7 @@ MESSAGE_WITH_CONTACT = { "from": USER, "chat": CHAT, "date": 1522850298, - "contact": CONTACT + "contact": CONTACT, } MESSAGE_WITH_DELETE_CHAT_PHOTO = {} @@ -261,7 +236,7 @@ MESSAGE_WITH_DOCUMENT = { "chat": CHAT, "date": 1508768012, "document": DOCUMENT, - "caption": "Read my document" + "caption": "Read my document", } MESSAGE_WITH_EDIT_DATE = {} @@ -273,7 +248,7 @@ MESSAGE_WITH_GAME = { "from": USER, "chat": CHAT, "date": 1508824810, - "game": GAME + "game": GAME, } MESSAGE_WITH_GROUP_CHAT_CREATED = {} @@ -283,7 +258,7 @@ MESSAGE_WITH_INVOICE = { "from": USER, "chat": CHAT, "date": 1508761719, - "invoice": INVOICE + "invoice": INVOICE, } MESSAGE_WITH_LEFT_CHAT_MEMBER = {} @@ -293,7 +268,7 @@ MESSAGE_WITH_LOCATION = { "from": USER, "chat": CHAT, "date": 1508755473, - "location": LOCATION + "location": LOCATION, } MESSAGE_WITH_MIGRATE_TO_CHAT_ID = { @@ -301,7 +276,7 @@ MESSAGE_WITH_MIGRATE_TO_CHAT_ID = { "from": USER, "chat": CHAT, "date": 1526943253, - "migrate_to_chat_id": -1234567890987 + "migrate_to_chat_id": -1234567890987, } MESSAGE_WITH_MIGRATE_FROM_CHAT_ID = { @@ -309,7 +284,7 @@ MESSAGE_WITH_MIGRATE_FROM_CHAT_ID = { "from": USER, "chat": CHAT, "date": 1526943253, - "migrate_from_chat_id": -123456789 + "migrate_from_chat_id": -123456789, } MESSAGE_WITH_NEW_CHAT_MEMBERS = {} @@ -324,7 +299,7 @@ MESSAGE_WITH_PHOTO = { "chat": CHAT, "date": 1508825154, "photo": [PHOTO, PHOTO, PHOTO, PHOTO], - "caption": "photo description" + "caption": "photo description", } MESSAGE_WITH_MEDIA_GROUP = { @@ -333,7 +308,7 @@ MESSAGE_WITH_MEDIA_GROUP = { "chat": CHAT, "date": 1522843665, "media_group_id": "12182749320567362", - "photo": [PHOTO, PHOTO, PHOTO, PHOTO] + "photo": [PHOTO, PHOTO, PHOTO, PHOTO], } MESSAGE_WITH_PINNED_MESSAGE = {} @@ -345,7 +320,7 @@ MESSAGE_WITH_STICKER = { "from": USER, "chat": CHAT, "date": 1508771450, - "sticker": STICKER + "sticker": STICKER, } MESSAGE_WITH_SUCCESSFUL_PAYMENT = { @@ -353,7 +328,7 @@ MESSAGE_WITH_SUCCESSFUL_PAYMENT = { "from": USER, "chat": CHAT, "date": 1508761169, - "successful_payment": SUCCESSFUL_PAYMENT + "successful_payment": SUCCESSFUL_PAYMENT, } MESSAGE_WITH_SUPERGROUP_CHAT_CREATED = {} @@ -364,7 +339,7 @@ MESSAGE_WITH_VENUE = { "chat": CHAT, "date": 1522849819, "location": LOCATION, - "venue": VENUE + "venue": VENUE, } MESSAGE_WITH_VIDEO = { @@ -373,7 +348,7 @@ MESSAGE_WITH_VIDEO = { "chat": CHAT, "date": 1508756494, "video": VIDEO, - "caption": "description" + "caption": "description", } MESSAGE_WITH_VIDEO_NOTE = { @@ -381,7 +356,7 @@ MESSAGE_WITH_VIDEO_NOTE = { "from": USER, "chat": CHAT, "date": 1522835890, - "video_note": VIDEO_NOTE + "video_note": VIDEO_NOTE, } MESSAGE_WITH_VOICE = { @@ -389,7 +364,7 @@ MESSAGE_WITH_VOICE = { "from": USER, "chat": CHAT, "date": 1508768403, - "voice": VOICE + "voice": VOICE, } PRE_CHECKOUT_QUERY = { @@ -397,7 +372,7 @@ PRE_CHECKOUT_QUERY = { "from": USER, "currency": "USD", "total_amount": 6250, - "invoice_payload": "HAPPY FRIDAYS COUPON" + "invoice_payload": "HAPPY FRIDAYS COUPON", } REPLY_MESSAGE = { @@ -406,37 +381,22 @@ REPLY_MESSAGE = { "chat": CHAT, "date": 1508751866, "reply_to_message": MESSAGE, - "text": "Reply to quoted message" + "text": "Reply to quoted message", } SHIPPING_QUERY = { "id": "262181558684397422", "from": USER, "invoice_payload": "HAPPY FRIDAYS COUPON", - "shipping_address": SHIPPING_ADDRESS + "shipping_address": SHIPPING_ADDRESS, } -USER_PROFILE_PHOTOS = { - "total_count": 1, "photos": [ - [PHOTO, PHOTO, PHOTO] - ] -} +USER_PROFILE_PHOTOS = {"total_count": 1, "photos": [[PHOTO, PHOTO, PHOTO]]} -FILE = { - "file_id": "XXXYYYZZZ", - "file_size": 5254, - "file_path": "voice\/file_8" -} +FILE = {"file_id": "XXXYYYZZZ", "file_size": 5254, "file_path": "voice/file_8"} -INVITE_LINK = 'https://t.me/joinchat/AbCdEfjKILDADwdd123' +INVITE_LINK = "https://t.me/joinchat/AbCdEfjKILDADwdd123" -UPDATE = { - "update_id": 123456789, - "message": MESSAGE -} +UPDATE = {"update_id": 123456789, "message": MESSAGE} -WEBHOOK_INFO = { - "url": "", - "has_custom_certificate": False, - "pending_update_count": 0 -} +WEBHOOK_INFO = {"url": "", "has_custom_certificate": False, "pending_update_count": 0} diff --git a/tests/types/test_animation.py b/tests/types/test_animation.py index 96b44174..d8f7cb54 100644 --- a/tests/types/test_animation.py +++ b/tests/types/test_animation.py @@ -12,28 +12,28 @@ def test_export(): def test_file_name(): assert isinstance(animation.file_name, str) - assert animation.file_name == ANIMATION['file_name'] + assert animation.file_name == ANIMATION["file_name"] def test_mime_type(): assert isinstance(animation.mime_type, str) - assert animation.mime_type == ANIMATION['mime_type'] + assert animation.mime_type == ANIMATION["mime_type"] def test_file_id(): assert isinstance(animation.file_id, str) # assert hash(animation) == ANIMATION['file_id'] - assert animation.file_id == ANIMATION['file_id'] + assert animation.file_id == ANIMATION["file_id"] def test_file_size(): assert isinstance(animation.file_size, int) - assert animation.file_size == ANIMATION['file_size'] + assert animation.file_size == ANIMATION["file_size"] def test_thumb(): assert isinstance(animation.thumb, types.PhotoSize) - assert animation.thumb.file_id == ANIMATION['thumb']['file_id'] - assert animation.thumb.width == ANIMATION['thumb']['width'] - assert animation.thumb.height == ANIMATION['thumb']['height'] - assert animation.thumb.file_size == ANIMATION['thumb']['file_size'] + assert animation.thumb.file_id == ANIMATION["thumb"]["file_id"] + assert animation.thumb.width == ANIMATION["thumb"]["width"] + assert animation.thumb.height == ANIMATION["thumb"]["height"] + assert animation.thumb.file_size == ANIMATION["thumb"]["file_size"] diff --git a/tests/types/test_chat.py b/tests/types/test_chat.py index 1caa228d..a7a7c937 100644 --- a/tests/types/test_chat.py +++ b/tests/types/test_chat.py @@ -12,35 +12,36 @@ def test_export(): def test_id(): assert isinstance(chat.id, int) - assert chat.id == CHAT['id'] + assert chat.id == CHAT["id"] # assert hash(chat) == CHAT['id'] def test_name(): assert isinstance(chat.first_name, str) - assert chat.first_name == CHAT['first_name'] + assert chat.first_name == CHAT["first_name"] assert isinstance(chat.last_name, str) - assert chat.last_name == CHAT['last_name'] + assert chat.last_name == CHAT["last_name"] assert isinstance(chat.username, str) - assert chat.username == CHAT['username'] + assert chat.username == CHAT["username"] def test_type(): assert isinstance(chat.type, str) - assert chat.type == CHAT['type'] + assert chat.type == CHAT["type"] def test_chat_types(): - assert types.ChatType.PRIVATE == 'private' - assert types.ChatType.GROUP == 'group' - assert types.ChatType.SUPER_GROUP == 'supergroup' - assert types.ChatType.CHANNEL == 'channel' + assert types.ChatType.PRIVATE == "private" + assert types.ChatType.GROUP == "group" + assert types.ChatType.SUPER_GROUP == "supergroup" + assert types.ChatType.CHANNEL == "channel" def test_chat_type_filters(): from . import test_message + assert types.ChatType.is_private(test_message.message) assert not types.ChatType.is_group(test_message.message) assert not types.ChatType.is_super_group(test_message.message) @@ -49,13 +50,13 @@ def test_chat_type_filters(): def test_chat_actions(): - assert types.ChatActions.TYPING == 'typing' - assert types.ChatActions.UPLOAD_PHOTO == 'upload_photo' - assert types.ChatActions.RECORD_VIDEO == 'record_video' - assert types.ChatActions.UPLOAD_VIDEO == 'upload_video' - assert types.ChatActions.RECORD_AUDIO == 'record_audio' - assert types.ChatActions.UPLOAD_AUDIO == 'upload_audio' - assert types.ChatActions.UPLOAD_DOCUMENT == 'upload_document' - assert types.ChatActions.FIND_LOCATION == 'find_location' - assert types.ChatActions.RECORD_VIDEO_NOTE == 'record_video_note' - assert types.ChatActions.UPLOAD_VIDEO_NOTE == 'upload_video_note' + assert types.ChatActions.TYPING == "typing" + assert types.ChatActions.UPLOAD_PHOTO == "upload_photo" + assert types.ChatActions.RECORD_VIDEO == "record_video" + assert types.ChatActions.UPLOAD_VIDEO == "upload_video" + assert types.ChatActions.RECORD_AUDIO == "record_audio" + assert types.ChatActions.UPLOAD_AUDIO == "upload_audio" + assert types.ChatActions.UPLOAD_DOCUMENT == "upload_document" + assert types.ChatActions.FIND_LOCATION == "find_location" + assert types.ChatActions.RECORD_VIDEO_NOTE == "record_video_note" + assert types.ChatActions.UPLOAD_VIDEO_NOTE == "upload_video_note" diff --git a/tests/types/test_document.py b/tests/types/test_document.py index 64b53360..686f0481 100644 --- a/tests/types/test_document.py +++ b/tests/types/test_document.py @@ -12,23 +12,23 @@ def test_export(): def test_file_name(): assert isinstance(document.file_name, str) - assert document.file_name == DOCUMENT['file_name'] + assert document.file_name == DOCUMENT["file_name"] def test_mime_type(): assert isinstance(document.mime_type, str) - assert document.mime_type == DOCUMENT['mime_type'] + assert document.mime_type == DOCUMENT["mime_type"] def test_file_id(): assert isinstance(document.file_id, str) # assert hash(document) == DOCUMENT['file_id'] - assert document.file_id == DOCUMENT['file_id'] + assert document.file_id == DOCUMENT["file_id"] def test_file_size(): assert isinstance(document.file_size, int) - assert document.file_size == DOCUMENT['file_size'] + assert document.file_size == DOCUMENT["file_size"] def test_thumb(): diff --git a/tests/types/test_game.py b/tests/types/test_game.py index 9a051160..8d0433cd 100644 --- a/tests/types/test_game.py +++ b/tests/types/test_game.py @@ -12,17 +12,17 @@ def test_export(): def test_title(): assert isinstance(game.title, str) - assert game.title == GAME['title'] + assert game.title == GAME["title"] def test_description(): assert isinstance(game.description, str) - assert game.description == GAME['description'] + assert game.description == GAME["description"] def test_photo(): assert isinstance(game.photo, list) - assert len(game.photo) == len(GAME['photo']) + assert len(game.photo) == len(GAME["photo"]) assert all(map(lambda t: isinstance(t, types.PhotoSize), game.photo)) diff --git a/tests/types/test_message.py b/tests/types/test_message.py index 8751d064..e25c338d 100644 --- a/tests/types/test_message.py +++ b/tests/types/test_message.py @@ -14,26 +14,26 @@ def test_export(): def test_message_id(): # assert hash(message) == MESSAGE['message_id'] - assert message.message_id == MESSAGE['message_id'] - assert message['message_id'] == MESSAGE['message_id'] + assert message.message_id == MESSAGE["message_id"] + assert message["message_id"] == MESSAGE["message_id"] def test_from(): assert isinstance(message.from_user, types.User) - assert message.from_user == message['from'] + assert message.from_user == message["from"] def test_chat(): assert isinstance(message.chat, types.Chat) - assert message.chat == message['chat'] + assert message.chat == message["chat"] def test_date(): assert isinstance(message.date, datetime.datetime) - assert int(message.date.timestamp()) == MESSAGE['date'] - assert message.date == message['date'] + assert int(message.date.timestamp()) == MESSAGE["date"] + assert message.date == message["date"] def test_text(): - assert message.text == MESSAGE['text'] - assert message['text'] == MESSAGE['text'] + assert message.text == MESSAGE["text"] + assert message["text"] == MESSAGE["text"] diff --git a/tests/types/test_photo.py b/tests/types/test_photo.py index 73d87fb7..36e965d5 100644 --- a/tests/types/test_photo.py +++ b/tests/types/test_photo.py @@ -12,16 +12,16 @@ def test_export(): def test_file_id(): assert isinstance(photo.file_id, str) - assert photo.file_id == PHOTO['file_id'] + assert photo.file_id == PHOTO["file_id"] def test_file_size(): assert isinstance(photo.file_size, int) - assert photo.file_size == PHOTO['file_size'] + assert photo.file_size == PHOTO["file_size"] def test_size(): assert isinstance(photo.width, int) assert isinstance(photo.height, int) - assert photo.width == PHOTO['width'] - assert photo.height == PHOTO['height'] + assert photo.width == PHOTO["width"] + assert photo.height == PHOTO["height"] diff --git a/tests/types/test_update.py b/tests/types/test_update.py index 6b724a23..adda3123 100644 --- a/tests/types/test_update.py +++ b/tests/types/test_update.py @@ -13,7 +13,7 @@ def test_export(): def test_update_id(): assert isinstance(update.update_id, int) # assert hash(update) == UPDATE['update_id'] - assert update.update_id == UPDATE['update_id'] + assert update.update_id == UPDATE["update_id"] def test_message(): diff --git a/tests/types/test_user.py b/tests/types/test_user.py index 585e6fc0..37861205 100644 --- a/tests/types/test_user.py +++ b/tests/types/test_user.py @@ -14,24 +14,24 @@ def test_export(): def test_id(): assert isinstance(user.id, int) - assert user.id == USER['id'] + assert user.id == USER["id"] # assert hash(user) == USER['id'] def test_bot(): assert isinstance(user.is_bot, bool) - assert user.is_bot == USER['is_bot'] + assert user.is_bot == USER["is_bot"] def test_name(): - assert user.first_name == USER['first_name'] - assert user.last_name == USER['last_name'] - assert user.username == USER['username'] + assert user.first_name == USER["first_name"] + assert user.last_name == USER["last_name"] + assert user.username == USER["username"] def test_language_code(): - assert user.language_code == USER['language_code'] - assert user.locale == Locale.parse(USER['language_code'], sep='-') + assert user.language_code == USER["language_code"] + assert user.locale == Locale.parse(USER["language_code"], sep="-") def test_full_name(): @@ -40,8 +40,10 @@ def test_full_name(): def test_mention(): assert user.mention == f"@{USER['username']}" - assert user.get_mention('foo', as_html=False) == f"[foo](tg://user?id={USER['id']})" - assert user.get_mention('foo', as_html=True) == f"foo" + assert user.get_mention("foo", as_html=False) == f"[foo](tg://user?id={USER['id']})" + assert ( + user.get_mention("foo", as_html=True) == f"foo" + ) def test_url():