Telegram API 5.2 support (#572)

* feat: version number update

* feat: add InputInvoiceMessageContent type

* refactor: every param on a new line

* feat: add `max_tip_amount` and `suggested_tip_amounts` to `sendInvoice`

* feat: `start_parameter` of `sendInvoice` became optional

* refactor: reorder params

* feat: add `chat_type` to `InlineQuery`

* feat: add `VoiceChatScheduled`

* feat: add `voice_chat_scheduled` to `Message`

* fix: sendChatAction documentation update

* feat: add `record_voice` and `upload_voice` to `ChatActions`

* feat: allow sending invoices to group, supergroup and channel
This commit is contained in:
Oleg A 2021-04-28 01:22:57 +03:00 committed by GitHub
parent cce29ba532
commit ea28e2a77a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 185 additions and 26 deletions

View file

@ -6,7 +6,7 @@
[![PyPi status](https://img.shields.io/pypi/status/aiogram.svg?style=flat-square)](https://pypi.python.org/pypi/aiogram) [![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) [![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) [![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-5.1-blue.svg?style=flat-square&logo=telegram)](https://core.telegram.org/bots/api) [![Telegram Bot API](https://img.shields.io/badge/Telegram%20Bot%20API-5.2-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) [![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) [![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) [![MIT License](https://img.shields.io/pypi/l/aiogram.svg?style=flat-square)](https://opensource.org/licenses/MIT)

View file

@ -21,7 +21,7 @@ AIOGramBot
:target: https://pypi.python.org/pypi/aiogram :target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions :alt: Supported python versions
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.1-blue.svg?style=flat-square&logo=telegram .. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.2-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api :target: https://core.telegram.org/bots/api
:alt: Telegram Bot API :alt: Telegram Bot API

View file

@ -43,5 +43,5 @@ __all__ = (
'utils', 'utils',
) )
__version__ = '2.12.2' __version__ = '2.13.0'
__api_version__ = '5.1' __api_version__ = '5.2'

View file

@ -189,7 +189,7 @@ class Methods(Helper):
""" """
Helper for Telegram API Methods listed on https://core.telegram.org/bots/api Helper for Telegram API Methods listed on https://core.telegram.org/bots/api
List is updated to Bot API 5.1 List is updated to Bot API 5.2
""" """
mode = HelperMode.lowerCamelCase mode = HelperMode.lowerCamelCase

View file

@ -1484,19 +1484,36 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
async def send_chat_action(self, chat_id: typing.Union[base.Integer, base.String], async def send_chat_action(self, chat_id: typing.Union[base.Integer, base.String],
action: base.String) -> base.Boolean: action: base.String) -> base.Boolean:
""" """
Use this method when you need to tell the user that something is happening on the bot's side. Use this method when you need to tell the user that something is
The status is set for 5 seconds or less happening on the bot's side. The status is set for 5 seconds or
(when a message arrives from your bot, Telegram clients clear its typing status). less (when a message arrives from your bot, Telegram clients
clear its typing status). Returns True on success.
We only recommend using this method when a response from the bot will take Example: The ImageBot needs some time to process a request and
a noticeable amount of time to arrive. upload the image. Instead of sending a text message along the
lines of Retrieving image, please wait, the bot may use
sendChatAction with action = upload_photo. The user will see a
sending photo status for the bot.
We only recommend using this method when a response from the bot
will take a noticeable amount of time to arrive.
Source: https://core.telegram.org/bots/api#sendchataction Source: https://core.telegram.org/bots/api#sendchataction
:param chat_id: Unique identifier for the target chat or username of the target channel :param chat_id: Unique identifier for the target chat or
username of the target channel (in the format
@channelusername)
:type chat_id: :obj:`typing.Union[base.Integer, base.String]` :type chat_id: :obj:`typing.Union[base.Integer, base.String]`
:param action: Type of action to broadcast
:param action: Type of action to broadcast. Choose one,
depending on what the user is about to receive: `typing` for
text messages, `upload_photo` for photos, `record_video` or
`upload_video` for videos, `record_voice` or `upload_voice`
for voice notes, `upload_document` for general files,
`find_location` for location data, `record_video_note` or
`upload_video_note` for video notes.
:type action: :obj:`base.String` :type action: :obj:`base.String`
:return: Returns True on success :return: Returns True on success
:rtype: :obj:`base.Boolean` :rtype: :obj:`base.Boolean`
""" """
@ -2780,10 +2797,19 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
# === Payments === # === Payments ===
# https://core.telegram.org/bots/api#payments # https://core.telegram.org/bots/api#payments
async def send_invoice(self, chat_id: base.Integer, title: base.String, async def send_invoice(self,
description: base.String, payload: base.String, chat_id: typing.Union[base.Integer, base.String],
provider_token: base.String, start_parameter: base.String, title: base.String,
currency: base.String, prices: typing.List[types.LabeledPrice], description: base.String,
payload: base.String,
provider_token: base.String,
currency: base.String,
prices: typing.List[types.LabeledPrice],
max_tip_amount: typing.Optional[base.Integer] = None,
suggested_tip_amounts: typing.Optional[
typing.List[base.Integer]
] = None,
start_parameter: typing.Optional[base.String] = None,
provider_data: typing.Optional[typing.Dict] = None, provider_data: typing.Optional[typing.Dict] = None,
photo_url: typing.Optional[base.String] = None, photo_url: typing.Optional[base.String] = None,
photo_size: typing.Optional[base.Integer] = None, photo_size: typing.Optional[base.Integer] = None,
@ -2799,14 +2825,17 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
disable_notification: typing.Optional[base.Boolean] = None, disable_notification: typing.Optional[base.Boolean] = None,
reply_to_message_id: typing.Optional[base.Integer] = None, reply_to_message_id: typing.Optional[base.Integer] = None,
allow_sending_without_reply: typing.Optional[base.Boolean] = None, allow_sending_without_reply: typing.Optional[base.Boolean] = None,
reply_markup: typing.Optional[types.InlineKeyboardMarkup] = None) -> types.Message: reply_markup: typing.Optional[types.InlineKeyboardMarkup] = None,
) -> types.Message:
""" """
Use this method to send invoices. Use this method to send invoices.
Source: https://core.telegram.org/bots/api#sendinvoice Source: https://core.telegram.org/bots/api#sendinvoice
:param chat_id: Unique identifier for the target private chat :param chat_id: Unique identifier for the target chat or
:type chat_id: :obj:`base.Integer` username of the target channel (in the format
@channelusername)
:type chat_id: :obj:`typing.Union[base.Integer, base.String]`
:param title: Product name, 1-32 characters :param title: Product name, 1-32 characters
:type title: :obj:`base.String` :type title: :obj:`base.String`
@ -2821,10 +2850,6 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
:param provider_token: Payments provider token, obtained via Botfather :param provider_token: Payments provider token, obtained via Botfather
:type provider_token: :obj:`base.String` :type provider_token: :obj:`base.String`
:param start_parameter: Unique deep-linking parameter that can be used to generate this
invoice when used as a start parameter
:type start_parameter: :obj:`base.String`
:param currency: Three-letter ISO 4217 currency code, see more on currencies :param currency: Three-letter ISO 4217 currency code, see more on currencies
:type currency: :obj:`base.String` :type currency: :obj:`base.String`
@ -2832,6 +2857,32 @@ class Bot(BaseBot, DataMixin, ContextInstanceMixin):
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
:type prices: :obj:`typing.List[types.LabeledPrice]` :type prices: :obj:`typing.List[types.LabeledPrice]`
:param max_tip_amount: The maximum accepted amount for tips in
the smallest units of the currency (integer, not
float/double). For example, for a maximum tip of US$ 1.45
pass max_tip_amount = 145. See the exp parameter in
currencies.json, it shows the number of digits past the
decimal point for each currency (2 for the majority of
currencies). Defaults to 0
:type max_tip_amount: :obj:`typing.Optional[base.Integer]`
:param suggested_tip_amounts: A JSON-serialized array of suggested
amounts of tips in the smallest units of the currency
(integer, not float/double). At most 4 suggested tip amounts
can be specified. The suggested tip amounts must be
positive, passed in a strictly increased order and must not
exceed max_tip_amount.
:type suggested_tip_amounts: :obj:`typing.Optional[typing.List[base.Integer]]`
:param start_parameter: Unique deep-linking parameter. If left
empty, forwarded copies of the sent message will have a Pay
button, allowing multiple users to pay directly from the
forwarded message, using the same invoice. If non-empty,
forwarded copies of the sent message will have a URL button
with a deep link to the bot (instead of a Pay button), with
the value used as the start parameter
:type start_parameter: :obj:`typing.Optional[base.String]`
:param provider_data: JSON-encoded data about the invoice, which will be shared with the payment provider :param provider_data: JSON-encoded data about the invoice, which will be shared with the payment provider
:type provider_data: :obj:`typing.Optional[typing.Dict]` :type provider_data: :obj:`typing.Optional[typing.Dict]`

View file

@ -35,7 +35,7 @@ from .input_file import InputFile
from .input_media import InputMedia, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, \ from .input_media import InputMedia, InputMediaAnimation, InputMediaAudio, InputMediaDocument, InputMediaPhoto, \
InputMediaVideo, MediaGroup InputMediaVideo, MediaGroup
from .input_message_content import InputContactMessageContent, InputLocationMessageContent, InputMessageContent, \ from .input_message_content import InputContactMessageContent, InputLocationMessageContent, InputMessageContent, \
InputTextMessageContent, InputVenueMessageContent InputTextMessageContent, InputVenueMessageContent, InputInvoiceMessageContent
from .invoice import Invoice from .invoice import Invoice
from .labeled_price import LabeledPrice from .labeled_price import LabeledPrice
from .location import Location from .location import Location
@ -72,6 +72,7 @@ from .video_note import VideoNote
from .voice import Voice from .voice import Voice
from .voice_chat_ended import VoiceChatEnded from .voice_chat_ended import VoiceChatEnded
from .voice_chat_participants_invited import VoiceChatParticipantsInvited from .voice_chat_participants_invited import VoiceChatParticipantsInvited
from .voice_chat_scheduled import VoiceChatScheduled
from .voice_chat_started import VoiceChatStarted from .voice_chat_started import VoiceChatStarted
from .webhook_info import WebhookInfo from .webhook_info import WebhookInfo
@ -131,6 +132,7 @@ __all__ = (
'InlineQueryResultVideo', 'InlineQueryResultVideo',
'InlineQueryResultVoice', 'InlineQueryResultVoice',
'InputContactMessageContent', 'InputContactMessageContent',
'InputInvoiceMessageContent',
'InputFile', 'InputFile',
'InputLocationMessageContent', 'InputLocationMessageContent',
'InputMedia', 'InputMedia',
@ -191,6 +193,7 @@ __all__ = (
'Voice', 'Voice',
'VoiceChatEnded', 'VoiceChatEnded',
'VoiceChatParticipantsInvited', 'VoiceChatParticipantsInvited',
'VoiceChatScheduled',
'VoiceChatStarted', 'VoiceChatStarted',
'WebhookInfo', 'WebhookInfo',
'base', 'base',

View file

@ -732,6 +732,8 @@ class ChatActions(helper.Helper):
UPLOAD_VIDEO: str = helper.Item() # upload_video UPLOAD_VIDEO: str = helper.Item() # upload_video
RECORD_AUDIO: str = helper.Item() # record_audio RECORD_AUDIO: str = helper.Item() # record_audio
UPLOAD_AUDIO: str = helper.Item() # upload_audio UPLOAD_AUDIO: str = helper.Item() # upload_audio
RECORD_VOICE: str = helper.Item() # record_voice
UPLOAD_VOICE: str = helper.Item() # upload_voice
UPLOAD_DOCUMENT: str = helper.Item() # upload_document UPLOAD_DOCUMENT: str = helper.Item() # upload_document
FIND_LOCATION: str = helper.Item() # find_location FIND_LOCATION: str = helper.Item() # find_location
RECORD_VIDEO_NOTE: str = helper.Item() # record_video_note RECORD_VIDEO_NOTE: str = helper.Item() # record_video_note
@ -817,6 +819,26 @@ class ChatActions(helper.Helper):
""" """
await cls._do(cls.UPLOAD_AUDIO, sleep) await cls._do(cls.UPLOAD_AUDIO, sleep)
@classmethod
async def record_voice(cls, sleep=None):
"""
Do record voice
:param sleep: sleep timeout
:return:
"""
await cls._do(cls.RECORD_VOICE, sleep)
@classmethod
async def upload_voice(cls, sleep=None):
"""
Do upload voice
:param sleep: sleep timeout
:return:
"""
await cls._do(cls.UPLOAD_VOICE, sleep)
@classmethod @classmethod
async def upload_document(cls, sleep=None): async def upload_document(cls, sleep=None):
""" """

View file

@ -17,9 +17,10 @@ class InlineQuery(base.TelegramObject):
""" """
id: base.String = fields.Field() 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() query: base.String = fields.Field()
offset: base.String = fields.Field() offset: base.String = fields.Field()
chat_type: base.String = fields.Field()
location: Location = fields.Field(base=Location)
async def answer(self, async def answer(self,
results: typing.List[InlineQueryResult], results: typing.List[InlineQueryResult],

View file

@ -3,6 +3,8 @@ import typing
from . import base from . import base
from . import fields from . import fields
from .message_entity import MessageEntity from .message_entity import MessageEntity
from .labeled_price import LabeledPrice
from ..utils.payload import generate_payload
class InputMessageContent(base.TelegramObject): class InputMessageContent(base.TelegramObject):
@ -44,6 +46,66 @@ class InputContactMessageContent(InputMessageContent):
) )
class InputInvoiceMessageContent(InputMessageContent):
"""
Represents the content of an invoice message to be sent as the
result of an inline query.
https://core.telegram.org/bots/api#inputinvoicemessagecontent
"""
title: base.String = fields.Field()
description: base.String = fields.Field()
payload: base.String = fields.Field()
provider_token: base.String = fields.Field()
currency: base.String = fields.Field()
prices: typing.List[LabeledPrice] = fields.ListField(base=LabeledPrice)
max_tip_amount: typing.Optional[base.Integer] = fields.Field()
suggested_tip_amounts: typing.Optional[
typing.List[base.Integer]
] = fields.ListField(base=base.Integer)
provider_data: typing.Optional[base.String] = fields.Field()
photo_url: typing.Optional[base.String] = fields.Field()
photo_size: typing.Optional[base.Integer] = fields.Field()
photo_width: typing.Optional[base.Integer] = fields.Field()
photo_height: typing.Optional[base.Integer] = fields.Field()
need_name: typing.Optional[base.Boolean] = fields.Field()
need_phone_number: typing.Optional[base.Boolean] = fields.Field()
need_email: typing.Optional[base.Boolean] = fields.Field()
need_shipping_address: typing.Optional[base.Boolean] = fields.Field()
send_phone_number_to_provider: typing.Optional[base.Boolean] = fields.Field()
send_email_to_provider: typing.Optional[base.Boolean] = fields.Field()
is_flexible: typing.Optional[base.Boolean] = fields.Field()
def __init__(
self,
title: base.String,
description: base.String,
payload: base.String,
provider_token: base.String,
currency: base.String,
prices: typing.List[LabeledPrice] = None,
max_tip_amount: typing.Optional[base.Integer] = None,
suggested_tip_amounts: typing.Optional[typing.List[base.Integer]] = None,
provider_data: typing.Optional[base.String] = None,
photo_url: typing.Optional[base.String] = None,
photo_size: typing.Optional[base.Integer] = None,
photo_width: typing.Optional[base.Integer] = None,
photo_height: typing.Optional[base.Integer] = None,
need_name: typing.Optional[base.Boolean] = None,
need_phone_number: typing.Optional[base.Boolean] = None,
need_email: typing.Optional[base.Boolean] = None,
need_shipping_address: typing.Optional[base.Boolean] = None,
send_phone_number_to_provider: typing.Optional[base.Boolean] = None,
send_email_to_provider: typing.Optional[base.Boolean] = None,
is_flexible: typing.Optional[base.Boolean] = None,
):
if prices is None:
prices = []
payload = generate_payload(**locals())
super().__init__(**payload)
class InputLocationMessageContent(InputMessageContent): class InputLocationMessageContent(InputMessageContent):
""" """
Represents the content of a location message to be sent as the result of an inline query. Represents the content of a location message to be sent as the result of an inline query.

View file

@ -34,6 +34,7 @@ from .video_note import VideoNote
from .voice import Voice from .voice import Voice
from .voice_chat_ended import VoiceChatEnded from .voice_chat_ended import VoiceChatEnded
from .voice_chat_participants_invited import VoiceChatParticipantsInvited from .voice_chat_participants_invited import VoiceChatParticipantsInvited
from .voice_chat_scheduled import VoiceChatScheduled
from .voice_chat_started import VoiceChatStarted from .voice_chat_started import VoiceChatStarted
from ..utils import helper from ..utils import helper
from ..utils import markdown as md from ..utils import markdown as md
@ -98,6 +99,7 @@ class Message(base.TelegramObject):
connected_website: base.String = fields.Field() connected_website: base.String = fields.Field()
passport_data: PassportData = fields.Field(base=PassportData) passport_data: PassportData = fields.Field(base=PassportData)
proximity_alert_triggered: ProximityAlertTriggered = fields.Field(base=ProximityAlertTriggered) proximity_alert_triggered: ProximityAlertTriggered = fields.Field(base=ProximityAlertTriggered)
voice_chat_scheduled: VoiceChatScheduled = fields.Field(base=VoiceChatScheduled)
voice_chat_started: VoiceChatStarted = fields.Field(base=VoiceChatStarted) voice_chat_started: VoiceChatStarted = fields.Field(base=VoiceChatStarted)
voice_chat_ended: VoiceChatEnded = fields.Field(base=VoiceChatEnded) voice_chat_ended: VoiceChatEnded = fields.Field(base=VoiceChatEnded)
voice_chat_participants_invited: VoiceChatParticipantsInvited = fields.Field(base=VoiceChatParticipantsInvited) voice_chat_participants_invited: VoiceChatParticipantsInvited = fields.Field(base=VoiceChatParticipantsInvited)
@ -166,6 +168,8 @@ class Message(base.TelegramObject):
return ContentType.PASSPORT_DATA return ContentType.PASSPORT_DATA
if self.proximity_alert_triggered: if self.proximity_alert_triggered:
return ContentType.PROXIMITY_ALERT_TRIGGERED return ContentType.PROXIMITY_ALERT_TRIGGERED
if self.voice_chat_scheduled:
return ContentType.VOICE_CHAT_SCHEDULED
if self.voice_chat_started: if self.voice_chat_started:
return ContentType.VOICE_CHAT_STARTED return ContentType.VOICE_CHAT_STARTED
if self.voice_chat_ended: if self.voice_chat_ended:
@ -3033,6 +3037,7 @@ class ContentType(helper.Helper):
GROUP_CHAT_CREATED = helper.Item() # group_chat_created GROUP_CHAT_CREATED = helper.Item() # group_chat_created
PASSPORT_DATA = helper.Item() # passport_data PASSPORT_DATA = helper.Item() # passport_data
PROXIMITY_ALERT_TRIGGERED = helper.Item() # proximity_alert_triggered PROXIMITY_ALERT_TRIGGERED = helper.Item() # proximity_alert_triggered
VOICE_CHAT_SCHEDULED = helper.Item() # voice_chat_scheduled
VOICE_CHAT_STARTED = helper.Item() # voice_chat_started VOICE_CHAT_STARTED = helper.Item() # voice_chat_started
VOICE_CHAT_ENDED = helper.Item() # voice_chat_ended VOICE_CHAT_ENDED = helper.Item() # voice_chat_ended
VOICE_CHAT_PARTICIPANTS_INVITED = helper.Item() # voice_chat_participants_invited VOICE_CHAT_PARTICIPANTS_INVITED = helper.Item() # voice_chat_participants_invited

View file

@ -0,0 +1,15 @@
from datetime import datetime
from . import base
from . import fields
from .user import User
class VoiceChatScheduled(base.TelegramObject):
"""
This object represents a service message about a voice chat scheduled in the chat.
https://core.telegram.org/bots/api#voicechatscheduled
"""
start_date: datetime = fields.DateTimeField()

View file

@ -22,7 +22,7 @@ Welcome to aiogram's documentation!
:target: https://pypi.python.org/pypi/aiogram :target: https://pypi.python.org/pypi/aiogram
:alt: Supported python versions :alt: Supported python versions
.. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.1-blue.svg?style=flat-square&logo=telegram .. image:: https://img.shields.io/badge/Telegram%20Bot%20API-5.2-blue.svg?style=flat-square&logo=telegram
:target: https://core.telegram.org/bots/api :target: https://core.telegram.org/bots/api
:alt: Telegram Bot API :alt: Telegram Bot API