1
0
mirror of https://github.com/eternnoir/pyTelegramBotAPI.git synced 2023-08-10 21:12:57 +03:00

Merge pull request #1584 from coder2020official/master

Bot API 6.1 update
This commit is contained in:
Badiboy 2022-06-23 10:50:39 +03:00 committed by GitHub
commit 24b1129c8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 242 additions and 15 deletions

View File

@ -11,7 +11,7 @@
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p> <p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Both synchronous and asynchronous.</p> <p align="center">Both synchronous and asynchronous.</p>
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#april-16-2022">6.0</a>! ## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#june-20-2022">6.1</a>!
<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2> <h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
@ -727,6 +727,7 @@ Result will be:
## API conformance ## API conformance
* ✔ [Bot API 6.1](https://core.telegram.org/bots/api#june-20-2022)
* ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022) * ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022)
* ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022) * ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022)
* ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) * ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021)

View File

@ -22,7 +22,7 @@ copyright = '2022, coder2020official'
author = 'coder2020official' author = 'coder2020official'
# The full version, including alpha/beta/rc tags # The full version, including alpha/beta/rc tags
release = '4.5.1' release = '4.6.0'
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------

View File

@ -261,7 +261,7 @@ class TeleBot:
self.reply_backend.load_handlers(filename, del_file_after_loading) self.reply_backend.load_handlers(filename, del_file_after_loading)
def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None,
drop_pending_updates = None, timeout=None): drop_pending_updates = None, timeout=None, secret_token=None):
""" """
Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an 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, update for the bot, we will send an HTTPS POST request to the specified url,
@ -286,10 +286,11 @@ class TeleBot:
resolved through DNS resolved through DNS
:param drop_pending_updates: Pass True to drop all pending updates :param drop_pending_updates: Pass True to drop all pending updates
:param timeout: Integer. Request connection timeout :param timeout: Integer. Request connection timeout
:param secret_token: Secret token to be used to verify the webhook request.
:return: API reply. :return: API reply.
""" """
return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, return apihelper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address,
drop_pending_updates, timeout) drop_pending_updates, timeout, secret_token)
def delete_webhook(self, drop_pending_updates=None, timeout=None): def delete_webhook(self, drop_pending_updates=None, timeout=None):
""" """
@ -2462,6 +2463,69 @@ class TeleBot:
max_tip_amount, suggested_tip_amounts, protect_content) max_tip_amount, suggested_tip_amounts, protect_content)
return types.Message.de_json(result) return types.Message.de_json(result)
def create_invoice_link(self,
title: str, description: str, payload:str, provider_token: str,
currency: str, prices: List[types.LabeledPrice],
max_tip_amount: Optional[int] = None,
suggested_tip_amounts: Optional[List[int]]=None,
provider_data: Optional[str]=None,
photo_url: Optional[str]=None,
photo_size: Optional[int]=None,
photo_width: Optional[int]=None,
photo_height: Optional[int]=None,
need_name: Optional[bool]=None,
need_phone_number: Optional[bool]=None,
need_email: Optional[bool]=None,
need_shipping_address: Optional[bool]=None,
send_phone_number_to_provider: Optional[bool]=None,
send_email_to_provider: Optional[bool]=None,
is_flexible: Optional[bool]=None) -> str:
"""
Use this method to create a link for an invoice.
Returns the created invoice link as String on success.
Telegram documentation:
https://core.telegram.org/bots/api#createinvoicelink
:param title: Product name, 1-32 characters
:param description: Product description, 1-255 characters
:param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user,
use for your internal processes.
:param provider_token: Payments provider token, obtained via @Botfather
:param currency: Three-letter ISO 4217 currency code,
see https://core.telegram.org/bots/payments#supported-currencies
:param prices: Price breakdown, a list of components
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
:param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency
:param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider.
A detailed description of required fields should be provided by the payment provider.
:param photo_url: URL of the product photo for the invoice. Can be a photo of the goods
:param photo_size: Photo size in bytes
:param photo_width: Photo width
:param photo_height: Photo height
:param need_name: Pass True, if you require the user's full name to complete the order
:param need_phone_number: Pass True, if you require the user's phone number to complete the order
:param need_email: Pass True, if you require the user's email to complete the order
:param need_shipping_address: Pass True, if you require the user's shipping address to complete the order
:param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider
:param send_email_to_provider: Pass True, if user's email address should be sent to provider
:param is_flexible: Pass True, if the final price depends on the shipping method
:return: Created invoice link as String on success.
"""
result = apihelper.create_invoice_link(
self.token, title, description, payload, provider_token,
currency, prices, max_tip_amount, suggested_tip_amounts, provider_data,
photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number,
need_email, need_shipping_address, send_phone_number_to_provider,
send_email_to_provider, is_flexible)
return result
# noinspection PyShadowingBuiltins # noinspection PyShadowingBuiltins
# TODO: rewrite this method like in API # TODO: rewrite this method like in API
def send_poll( def send_poll(

View File

@ -268,7 +268,7 @@ def send_message(
def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None,
drop_pending_updates = None, timeout=None): drop_pending_updates = None, timeout=None, secret_token=None):
method_url = r'setWebhook' method_url = r'setWebhook'
payload = { payload = {
'url': url if url else "", 'url': url if url else "",
@ -286,6 +286,8 @@ def set_webhook(token, url=None, certificate=None, max_connections=None, allowed
payload['drop_pending_updates'] = drop_pending_updates payload['drop_pending_updates'] = drop_pending_updates
if timeout: if timeout:
payload['timeout'] = timeout payload['timeout'] = timeout
if secret_token:
payload['secret_token'] = secret_token
return _make_request(token, method_url, params=payload, files=files) return _make_request(token, method_url, params=payload, files=files)
@ -1622,6 +1624,45 @@ def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResul
return _make_request(token, method_url, params=payload, method='post') return _make_request(token, method_url, params=payload, method='post')
def create_invoice_link(token, title, description, payload, provider_token,
currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None,
photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None,
need_email=None, need_shipping_address=None, send_phone_number_to_provider=None,
send_email_to_provider=None, is_flexible=None):
method_url = r'createInvoiceLink'
payload = {'title': title, 'description': description, 'payload': payload, 'provider_token': provider_token,
'currency': currency, 'prices': _convert_list_json_serializable(prices)}
if max_tip_amount:
payload['max_tip_amount'] = max_tip_amount
if suggested_tip_amounts:
payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts)
if provider_data:
payload['provider_data'] = provider_data
if photo_url:
payload['photo_url'] = photo_url
if photo_size:
payload['photo_size'] = photo_size
if photo_width:
payload['photo_width'] = photo_width
if photo_height:
payload['photo_height'] = photo_height
if need_name is not None:
payload['need_name'] = need_name
if need_phone_number is not None:
payload['need_phone_number'] = need_phone_number
if need_email is not None:
payload['need_email'] = need_email
if need_shipping_address is not None:
payload['need_shipping_address'] = need_shipping_address
if send_phone_number_to_provider is not None:
payload['send_phone_number_to_provider'] = send_phone_number_to_provider
if send_email_to_provider is not None:
payload['send_email_to_provider'] = send_email_to_provider
if is_flexible is not None:
payload['is_flexible'] = is_flexible
return _make_request(token, method_url, params=payload, method='post')
# noinspection PyShadowingBuiltins # noinspection PyShadowingBuiltins
def send_poll( def send_poll(
token, chat_id, token, chat_id,

View File

@ -1384,7 +1384,7 @@ class AsyncTeleBot:
self.current_states = StatePickleStorage(file_path=filename) self.current_states = StatePickleStorage(file_path=filename)
async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, async def set_webhook(self, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None,
drop_pending_updates = None, timeout=None): drop_pending_updates = None, timeout=None, secret_token=None):
""" """
Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an 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, update for the bot, we will send an HTTPS POST request to the specified url,
@ -1409,10 +1409,11 @@ class AsyncTeleBot:
resolved through DNS resolved through DNS
:param drop_pending_updates: Pass True to drop all pending updates :param drop_pending_updates: Pass True to drop all pending updates
:param timeout: Integer. Request connection timeout :param timeout: Integer. Request connection timeout
:param secret_token: Secret token to be used to verify the webhook
:return: :return:
""" """
return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address, return await asyncio_helper.set_webhook(self.token, url, certificate, max_connections, allowed_updates, ip_address,
drop_pending_updates, timeout) drop_pending_updates, timeout, secret_token)
@ -3066,6 +3067,67 @@ class AsyncTeleBot:
max_tip_amount, suggested_tip_amounts, protect_content) max_tip_amount, suggested_tip_amounts, protect_content)
return types.Message.de_json(result) return types.Message.de_json(result)
async def create_invoice_link(self,
title: str, description: str, payload:str, provider_token: str,
currency: str, prices: List[types.LabeledPrice],
max_tip_amount: Optional[int] = None,
suggested_tip_amounts: Optional[List[int]]=None,
provider_data: Optional[str]=None,
photo_url: Optional[str]=None,
photo_size: Optional[int]=None,
photo_width: Optional[int]=None,
photo_height: Optional[int]=None,
need_name: Optional[bool]=None,
need_phone_number: Optional[bool]=None,
need_email: Optional[bool]=None,
need_shipping_address: Optional[bool]=None,
send_phone_number_to_provider: Optional[bool]=None,
send_email_to_provider: Optional[bool]=None,
is_flexible: Optional[bool]=None) -> str:
"""
Use this method to create a link for an invoice.
Returns the created invoice link as String on success.
Telegram documentation:
https://core.telegram.org/bots/api#createinvoicelink
:param title: Product name, 1-32 characters
:param description: Product description, 1-255 characters
:param payload: Bot-defined invoice payload, 1-128 bytes. This will not be displayed to the user,
use for your internal processes.
:param provider_token: Payments provider token, obtained via @Botfather
:param currency: Three-letter ISO 4217 currency code,
see https://core.telegram.org/bots/payments#supported-currencies
:param prices: Price breakdown, a list of components
(e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.)
:param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency
:param suggested_tip_amounts: A JSON-serialized array of suggested amounts of tips in the smallest
:param provider_data: A JSON-serialized data about the invoice, which will be shared with the payment provider.
A detailed description of required fields should be provided by the payment provider.
:param photo_url: URL of the product photo for the invoice. Can be a photo of the goods
:param photo_size: Photo size in bytes
:param photo_width: Photo width
:param photo_height: Photo height
:param need_name: Pass True, if you require the user's full name to complete the order
:param need_phone_number: Pass True, if you require the user's phone number to complete the order
:param need_email: Pass True, if you require the user's email to complete the order
:param need_shipping_address: Pass True, if you require the user's shipping address to complete the order
:param send_phone_number_to_provider: Pass True, if user's phone number should be sent to provider
:param send_email_to_provider: Pass True, if user's email address should be sent to provider
:param is_flexible: Pass True, if the final price depends on the shipping method
:return: Created invoice link as String on success.
"""
result = await asyncio_helper.create_invoice_link(
self.token, title, description, payload, provider_token,
currency, prices, max_tip_amount, suggested_tip_amounts, provider_data,
photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number,
need_email, need_shipping_address, send_phone_number_to_provider,
send_email_to_provider, is_flexible)
return result
# noinspection PyShadowingBuiltins # noinspection PyShadowingBuiltins
async def send_poll( async def send_poll(
self, chat_id: Union[int, str], question: str, options: List[str], self, chat_id: Union[int, str], question: str, options: List[str],

View File

@ -171,7 +171,7 @@ async def download_file(token, file_path):
async def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None, async def set_webhook(token, url=None, certificate=None, max_connections=None, allowed_updates=None, ip_address=None,
drop_pending_updates = None, timeout=None): drop_pending_updates = None, timeout=None, secret_token=None):
method_url = r'setWebhook' method_url = r'setWebhook'
payload = { payload = {
'url': url if url else "", 'url': url if url else "",
@ -189,6 +189,8 @@ async def set_webhook(token, url=None, certificate=None, max_connections=None, a
payload['drop_pending_updates'] = drop_pending_updates payload['drop_pending_updates'] = drop_pending_updates
if timeout: if timeout:
payload['timeout'] = timeout payload['timeout'] = timeout
if secret_token:
payload['secret_token'] = secret_token
return await _process_request(token, method_url, params=payload, files=files) return await _process_request(token, method_url, params=payload, files=files)
@ -1599,6 +1601,47 @@ async def delete_sticker_from_set(token, sticker):
return await _process_request(token, method_url, params=payload, method='post') return await _process_request(token, method_url, params=payload, method='post')
async def create_invoice_link(token, title, description, payload, provider_token,
currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None,
photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None,
need_email=None, need_shipping_address=None, send_phone_number_to_provider=None,
send_email_to_provider=None, is_flexible=None):
method_url = r'createInvoiceLink'
payload = {'title': title, 'description': description, 'payload': payload, 'provider_token': provider_token,
'currency': currency, 'prices': await _convert_list_json_serializable(prices)}
if max_tip_amount:
payload['max_tip_amount'] = max_tip_amount
if suggested_tip_amounts:
payload['suggested_tip_amounts'] = json.dumps(suggested_tip_amounts)
if provider_data:
payload['provider_data'] = provider_data
if photo_url:
payload['photo_url'] = photo_url
if photo_size:
payload['photo_size'] = photo_size
if photo_width:
payload['photo_width'] = photo_width
if photo_height:
payload['photo_height'] = photo_height
if need_name is not None:
payload['need_name'] = need_name
if need_phone_number is not None:
payload['need_phone_number'] = need_phone_number
if need_email is not None:
payload['need_email'] = need_email
if need_shipping_address is not None:
payload['need_shipping_address'] = need_shipping_address
if send_phone_number_to_provider is not None:
payload['send_phone_number_to_provider'] = send_phone_number_to_provider
if send_email_to_provider is not None:
payload['send_email_to_provider'] = send_email_to_provider
if is_flexible is not None:
payload['is_flexible'] = is_flexible
return await _process_request(token, method_url, params=payload, method='post')
# noinspection PyShadowingBuiltins # noinspection PyShadowingBuiltins
async def send_poll( async def send_poll(
token, chat_id, token, chat_id,

View File

@ -215,7 +215,8 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable):
return cls(**obj) return cls(**obj)
def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None, def __init__(self, id, is_bot, first_name, last_name=None, username=None, language_code=None,
can_join_groups=None, can_read_all_group_messages=None, supports_inline_queries=None, **kwargs): can_join_groups=None, can_read_all_group_messages=None, supports_inline_queries=None,
is_premium=None, added_to_attachment_menu=None, **kwargs):
self.id: int = id self.id: int = id
self.is_bot: bool = is_bot self.is_bot: bool = is_bot
self.first_name: str = first_name self.first_name: str = first_name
@ -225,6 +226,9 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable):
self.can_join_groups: bool = can_join_groups self.can_join_groups: bool = can_join_groups
self.can_read_all_group_messages: bool = can_read_all_group_messages self.can_read_all_group_messages: bool = can_read_all_group_messages
self.supports_inline_queries: bool = supports_inline_queries self.supports_inline_queries: bool = supports_inline_queries
self.is_premium: bool = is_premium
self.added_to_attachment_menu: bool = added_to_attachment_menu
@property @property
def full_name(self): def full_name(self):
@ -280,7 +284,8 @@ class Chat(JsonDeserializable):
description=None, invite_link=None, pinned_message=None, description=None, invite_link=None, pinned_message=None,
permissions=None, slow_mode_delay=None, permissions=None, slow_mode_delay=None,
message_auto_delete_time=None, has_protected_content=None, sticker_set_name=None, message_auto_delete_time=None, has_protected_content=None, sticker_set_name=None,
can_set_sticker_set=None, linked_chat_id=None, location=None, **kwargs): can_set_sticker_set=None, linked_chat_id=None, location=None,
join_to_send_messages=None, join_by_request=None, **kwargs):
self.id: int = id self.id: int = id
self.type: str = type self.type: str = type
self.title: str = title self.title: str = title
@ -289,6 +294,8 @@ class Chat(JsonDeserializable):
self.last_name: str = last_name self.last_name: str = last_name
self.photo: ChatPhoto = photo self.photo: ChatPhoto = photo
self.bio: str = bio self.bio: str = bio
self.join_to_send_messages: bool = join_to_send_messages
self.join_by_request: bool = join_by_request
self.has_private_forwards: bool = has_private_forwards self.has_private_forwards: bool = has_private_forwards
self.description: str = description self.description: str = description
self.invite_link: str = invite_link self.invite_link: str = invite_link
@ -2576,10 +2583,13 @@ class Sticker(JsonDeserializable):
obj['thumb'] = None obj['thumb'] = None
if 'mask_position' in obj: if 'mask_position' in obj:
obj['mask_position'] = MaskPosition.de_json(obj['mask_position']) obj['mask_position'] = MaskPosition.de_json(obj['mask_position'])
if 'premium_animation' in obj:
obj['premium_animation'] = File.de_json(obj['premium_animation'])
return cls(**obj) return cls(**obj)
def __init__(self, file_id, file_unique_id, width, height, is_animated, def __init__(self, file_id, file_unique_id, width, height, is_animated,
is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None, **kwargs): is_video, thumb=None, emoji=None, set_name=None, mask_position=None, file_size=None,
premium_animation=None, **kwargs):
self.file_id: str = file_id self.file_id: str = file_id
self.file_unique_id: str = file_unique_id self.file_unique_id: str = file_unique_id
self.width: int = width self.width: int = width
@ -2591,6 +2601,7 @@ class Sticker(JsonDeserializable):
self.set_name: str = set_name self.set_name: str = set_name
self.mask_position: MaskPosition = mask_position self.mask_position: MaskPosition = mask_position
self.file_size: int = file_size self.file_size: int = file_size
self.premium_animation: File = premium_animation

View File

@ -1,3 +1,3 @@
# Versions should comply with PEP440. # Versions should comply with PEP440.
# This line is parsed in setup.py: # This line is parsed in setup.py:
__version__ = '4.5.1' __version__ = '4.6.0'

View File

@ -6,10 +6,13 @@ from telebot import types
def test_json_user(): def test_json_user():
jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","last_name":")))","username":"rdss_bot","is_bot":true}' jsonstring = r'{"id":101176298,"first_name":"RDSSBOT","last_name":")))","username":"rdss_bot","is_bot":true, "is_premium":true, "added_to_attachment_menu": true}'
u = types.User.de_json(jsonstring) u = types.User.de_json(jsonstring)
assert u.id == 101176298 assert u.id == 101176298
assert u.full_name == 'RDSSBOT )))' assert u.full_name == 'RDSSBOT )))'
assert u.is_premium is True
assert u.added_to_attachment_menu is True
def test_json_message(): def test_json_message():
@ -155,12 +158,14 @@ def test_json_update():
def test_json_chat(): def test_json_chat():
json_string = r'{"id": -111111,"title": "Test Title","type": "group"}' json_string = r'{"id": -111111,"title": "Test Title","type": "group", "join_to_send_messages": true, "join_by_request": true}'
chat = types.Chat.de_json(json_string) chat = types.Chat.de_json(json_string)
assert chat.id == -111111 assert chat.id == -111111
assert chat.type == 'group' assert chat.type == 'group'
assert chat.title == 'Test Title' assert chat.title == 'Test Title'
assert chat.join_to_send_messages is True
assert chat.join_by_request is True
def test_InlineQueryResultCachedPhoto(): def test_InlineQueryResultCachedPhoto():
iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid') iq = types.InlineQueryResultCachedPhoto('aaa', 'Fileid')