diff --git a/README.md b/README.md index a4c5b5fc..74de8a8d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![PyPi status](https://img.shields.io/pypi/status/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) [![Downloads](https://img.shields.io/pypi/dm/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) [![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.7-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api) +[![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-4.8-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api) [![Documentation Status](https://img.shields.io/readthedocs/aiogram?style=flat-square)](http://docs.aiogram.dev/en/latest/?badge=latest) [![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 6d5d1f69..78dc071c 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,7 @@ AIOGramBot :target: https://pypi.python.org/pypi/aiogram :alt: Supported python versions -.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.7-blue.svg?style=flat-square&logo=telegram +.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.8-blue.svg?style=flat-square&logo=telegram :target: https://core.telegram.org/bots/api :alt: Telegram Bot API diff --git a/aiogram/__init__.py b/aiogram/__init__.py index aabf331e..f06827cd 100644 --- a/aiogram/__init__.py +++ b/aiogram/__init__.py @@ -38,5 +38,5 @@ __all__ = [ 'utils' ] -__version__ = '2.7' -__api_version__ = '4.7' +__version__ = '2.8' +__api_version__ = '4.8' diff --git a/aiogram/bot/api.py b/aiogram/bot/api.py index 49ad849b..3b341ec9 100644 --- a/aiogram/bot/api.py +++ b/aiogram/bot/api.py @@ -153,7 +153,7 @@ class Methods(Helper): """ Helper for Telegram API Methods listed on https://core.telegram.org/bots/api - List is updated to Bot API 4.7 + List is updated to Bot API 4.8 """ mode = HelperMode.lowerCamelCase diff --git a/aiogram/bot/bot.py b/aiogram/bot/bot.py index 10b7ef7c..41f30af1 100644 --- a/aiogram/bot/bot.py +++ b/aiogram/bot/bot.py @@ -870,6 +870,11 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): type: typing.Optional[base.String] = None, allows_multiple_answers: typing.Optional[base.Boolean] = None, correct_option_id: typing.Optional[base.Integer] = None, + explanation: typing.Optional[base.String] = None, + explanation_parse_mode: typing.Optional[base.String] = None, + open_period: typing.Union[base.Integer, None] = None, + close_date: typing.Union[ + base.Integer, datetime.datetime, datetime.timedelta, None] = None, is_closed: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None, reply_to_message_id: typing.Optional[base.Integer] = None, @@ -888,17 +893,25 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param question: Poll question, 1-255 characters :type question: :obj:`base.String` :param options: List of answer options, 2-10 strings 1-100 characters each - :param options: :obj:`typing.List[base.String]` + :type options: :obj:`typing.List[base.String]` :param is_anonymous: True, if the poll needs to be anonymous, defaults to True - :param is_anonymous: :obj:`typing.Optional[base.Boolean]` + :type is_anonymous: :obj:`typing.Optional[base.Boolean]` :param type: Poll type, “quiz” or “regular”, defaults to “regular” - :param type: :obj:`typing.Optional[base.String]` + :type type: :obj:`typing.Optional[base.String]` :param allows_multiple_answers: True, if the poll allows multiple answers, ignored for polls in quiz mode, defaults to False - :param allows_multiple_answers: :obj:`typing.Optional[base.Boolean]` + :type allows_multiple_answers: :obj:`typing.Optional[base.Boolean]` :param correct_option_id: 0-based identifier of the correct answer option, required for polls in quiz mode - :param correct_option_id: :obj:`typing.Optional[base.Integer]` + :type correct_option_id: :obj:`typing.Optional[base.Integer]` + :param explanation: Text that is shown when a user chooses an incorrect answer or taps on the lamp icon in a quiz-style poll, 0-200 characters with at most 2 line feeds after entities parsing + :type explanation: :obj:`typing.Optional[base.String]` + :param explanation_parse_mode: Mode for parsing entities in the explanation. See formatting options for more details. + :type explanation_parse_mode: :obj:`typing.Optional[base.String]` + :param open_period: Amount of time in seconds the poll will be active after creation, 5-600. Can't be used together with close_date. + :type open_period: :obj:`typing.Union[base.Integer, None]` + :param close_date: Point in time (Unix timestamp) when the poll will be automatically closed. Must be at least 5 and no more than 600 seconds in the future. Can't be used together with open_period. + :type close_date: :obj:`typing.Union[base.Integer, datetime.datetime, datetime.timedelta, None]` :param is_closed: Pass True, if the poll needs to be immediately closed - :param is_closed: :obj:`typing.Optional[base.Boolean]` + :type is_closed: :obj:`typing.Optional[base.Boolean]` :param disable_notification: Sends the message silently. Users will receive a notification with no sound. :type disable_notification: :obj:`typing.Optional[Boolean]` :param reply_to_message_id: If the message is a reply, ID of the original message @@ -911,13 +924,18 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :rtype: :obj:`types.Message` """ options = prepare_arg(options) + open_period = prepare_arg(open_period) + close_date = prepare_arg(close_date) payload = generate_payload(**locals()) + if self.parse_mode: + payload.setdefault('explanation_parse_mode', self.parse_mode) result = await self.request(api.Methods.SEND_POLL, payload) return types.Message(**result) async def send_dice(self, chat_id: typing.Union[base.Integer, base.String], disable_notification: typing.Union[base.Boolean, None] = None, + emoji: typing.Union[base.String, None] = None, reply_to_message_id: typing.Union[base.Integer, None] = None, reply_markup: typing.Union[types.InlineKeyboardMarkup, types.ReplyKeyboardMarkup, @@ -933,6 +951,8 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin): :param chat_id: Unique identifier for the target chat or username of the target channel :type chat_id: :obj:`typing.Union[base.Integer, base.String]` + :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of “🎲” or “🎯”. Defauts to “🎲” + :type emoji: :obj:`typing.Union[base.String, None]` :param disable_notification: Sends the message silently. Users will receive a notification with no sound :type disable_notification: :obj:`typing.Union[base.Boolean, None]` :param reply_to_message_id: If the message is a reply, ID of the original message diff --git a/aiogram/dispatcher/webhook.py b/aiogram/dispatcher/webhook.py index a717b486..ed2ebf99 100644 --- a/aiogram/dispatcher/webhook.py +++ b/aiogram/dispatcher/webhook.py @@ -182,7 +182,7 @@ class WebhookRequestHandler(web.View): try: try: await waiter - except asyncio.futures.CancelledError: + except asyncio.CancelledError: fut.remove_done_callback(cb) fut.cancel() raise diff --git a/aiogram/types/__init__.py b/aiogram/types/__init__.py index 2cd15ac6..1221ec72 100644 --- a/aiogram/types/__init__.py +++ b/aiogram/types/__init__.py @@ -12,7 +12,7 @@ from .chat_permissions import ChatPermissions from .chat_photo import ChatPhoto from .chosen_inline_result import ChosenInlineResult from .contact import Contact -from .dice import Dice +from .dice import Dice, DiceEmoji from .document import Document from .encrypted_credentials import EncryptedCredentials from .encrypted_passport_element import EncryptedPassportElement @@ -85,6 +85,7 @@ __all__ = ( 'ContentType', 'ContentTypes', 'Dice', + 'DiceEmoji', 'Document', 'EncryptedCredentials', 'EncryptedPassportElement', diff --git a/aiogram/types/dice.py b/aiogram/types/dice.py index 9d0e22d0..6dfb190f 100644 --- a/aiogram/types/dice.py +++ b/aiogram/types/dice.py @@ -9,5 +9,10 @@ class Dice(base.TelegramObject): https://core.telegram.org/bots/api#dice """ - + emoji: base.String = fields.Field() value: base.Integer = fields.Field() + + +class DiceEmoji: + DICE = '🎲' + DART = '🎯' diff --git a/aiogram/types/message.py b/aiogram/types/message.py index d50eaf28..ddbccde6 100644 --- a/aiogram/types/message.py +++ b/aiogram/types/message.py @@ -150,7 +150,6 @@ class Message(base.TelegramObject): if self.passport_data: return ContentType.PASSPORT_DATA - return ContentType.UNKNOWN def is_command(self): @@ -810,6 +809,39 @@ class Message(base.TelegramObject): reply_to_message_id=self.message_id if reply else None, reply_markup=reply_markup) + async def answer_dice(self, emoji: 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 a dice, which will have a random value from 1 to 6. + On success, the sent Message is returned. + (Yes, we're aware of the “proper” singular of die. + But it's awkward, and we decided to help it change. One dice at a time!) + + Source: https://core.telegram.org/bots/api#senddice + + :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of “🎲” or “🎯”. Defauts to “🎲” + :type emoji: :obj:`typing.Union[base.String, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, + custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user + :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :param reply: fill 'reply_to_message_id' + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` + """ + return await self.bot.send_dice(chat_id=self.chat.id, + disable_notification=disable_notification, + emoji=emoji, + 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, @@ -1352,6 +1384,38 @@ class Message(base.TelegramObject): reply_to_message_id=self.message_id if reply else None, reply_markup=reply_markup) + async def reply_dice(self, emoji: 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 a dice, which will have a random value from 1 to 6. + On success, the sent Message is returned. + (Yes, we're aware of the “proper” singular of die. + But it's awkward, and we decided to help it change. One dice at a time!) + + Source: https://core.telegram.org/bots/api#senddice + + :param emoji: Emoji on which the dice throw animation is based. Currently, must be one of “🎲” or “🎯”. Defauts to “🎲” + :type emoji: :obj:`typing.Union[base.String, None]` + :param disable_notification: Sends the message silently. Users will receive a notification with no sound. + :type disable_notification: :obj:`typing.Union[base.Boolean, None]` + :param reply_markup: Additional interface options. A JSON-serialized object for an inline keyboard, + custom reply keyboard, instructions to remove reply keyboard or to force a reply from the user + :type reply_markup: :obj:`typing.Union[types.InlineKeyboardMarkup, + types.ReplyKeyboardMarkup, types.ReplyKeyboardRemove, types.ForceReply, None]` + :param reply: fill 'reply_to_message_id' + :return: On success, the sent Message is returned. + :rtype: :obj:`types.Message` + """ + return await self.bot.send_dice(chat_id=self.chat.id, + 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: """ diff --git a/aiogram/types/poll.py b/aiogram/types/poll.py index 86b41d7e..a709edcd 100644 --- a/aiogram/types/poll.py +++ b/aiogram/types/poll.py @@ -1,8 +1,11 @@ +import datetime import typing -from ..utils import helper from . import base, fields +from .message_entity import MessageEntity from .user import User +from ..utils import helper +from ..utils.text_decorations import html_decoration, markdown_decoration class PollOption(base.TelegramObject): @@ -44,6 +47,33 @@ class Poll(base.TelegramObject): type: base.String = fields.Field() allows_multiple_answers: base.Boolean = fields.Field() correct_option_id: base.Integer = fields.Field() + explanation: base.String = fields.Field() + explanation_entities: base.String = fields.ListField(base=MessageEntity) + open_period: base.Integer = fields.Field() + close_date: datetime.datetime = fields.DateTimeField() + + def parse_entities(self, as_html=True): + text_decorator = html_decoration if as_html else markdown_decoration + + return text_decorator.unparse(self.explanation or '', self.explanation_entities or []) + + @property + def md_explanation(self) -> str: + """ + Explanation formatted as markdown. + + :return: str + """ + return self.parse_entities(False) + + @property + def html_explanation(self) -> str: + """ + Explanation formatted as HTML + + :return: str + """ + return self.parse_entities() class PollType(helper.Helper): diff --git a/docs/source/index.rst b/docs/source/index.rst index fb1c9595..b18d386e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,7 +22,7 @@ Welcome to aiogram's documentation! :target: https://pypi.python.org/pypi/aiogram :alt: Supported python versions - .. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.7-blue.svg?style=flat-square&logo=telegram + .. image:: https://img.shields.io/badge/Telegram%20Bot%20API-4.8-blue.svg?style=flat-square&logo=telegram :target: https://core.telegram.org/bots/api :alt: Telegram Bot API