Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bot API 8.2 full support #2440

Merged
merged 2 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,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">Both synchronous and asynchronous.</p>

## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#november-17-2024"><img src="https://img.shields.io/badge/Bot%20API-8.1-blue?logo=telegram" alt="Supported Bot API version"></a>
## <p align="center">Supported Bot API version: <a href="https://core.telegram.org/bots/api#january-1-2025"><img src="https://img.shields.io/badge/Bot%20API-8.2-blue?logo=telegram" alt="Supported Bot API version"></a>

<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
<h2><a href='https://pytba.readthedocs.io/ru/latest/index.html'>Official ru documentation</a></h2>
Expand Down
72 changes: 70 additions & 2 deletions telebot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6250,7 +6250,8 @@ def delete_sticker_set(self, name:str) -> bool:
"""
return apihelper.delete_sticker_set(self.token, name)

def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool:
def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None,
text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool:
"""
Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success.

Expand All @@ -6262,6 +6263,9 @@ def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_p
:param gift_id: Identifier of the gift
:type gift_id: :obj:`str`

:param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver
:type pay_for_upgrade: :obj:`bool`

:param text: Text that will be shown along with the gift; 0-255 characters
:type text: :obj:`str`

Expand All @@ -6274,7 +6278,71 @@ def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_p
:return: Returns True on success.
:rtype: :obj:`bool`
"""
return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities)
return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities,
pay_for_upgrade=pay_for_upgrade)

def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool:
"""
Verifies a user on behalf of the organization which is represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#verifyuser

:param user_id: Unique identifier of the target user
:type user_id: :obj:`int`

:param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description.
:type custom_description: :obj:`str`

:return: Returns True on success.
:rtype: :obj:`bool`
"""
return apihelper.verify_user(self.token, user_id, custom_description=custom_description)

def verify_chat(self, chat_id: Union[int, str], custom_description: Optional[str]=None) -> bool:
"""
Verifies a chat on behalf of the organization which is represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#verifychat

:param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
:type chat_id: :obj:`int` | :obj:`str`

:param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description.
:type custom_description: :obj:`str`

:return: Returns True on success.
:rtype: :obj:`bool`
"""
return apihelper.verify_chat(self.token, chat_id, custom_description=custom_description)

def remove_user_verification(self, user_id: int) -> bool:
"""
Removes verification from a user who is currently verified on behalf of the organization represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#removeuserverification

:param user_id: Unique identifier of the target user
:type user_id: :obj:`int`

:return: Returns True on success.
:rtype: :obj:`bool`
"""

return apihelper.remove_user_verification(self.token, user_id)

def remove_chat_verification(self, chat_id: Union[int, str]) -> bool:
"""
Removes verification from a chat that is currently verified on behalf of the organization represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#removechatverification

:param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
:type chat_id: :obj:`int` | :obj:`str`

:return: Returns True on success.
:rtype: :obj:`bool`
"""
return apihelper.remove_chat_verification(self.token, chat_id)

def get_available_gifts(self) -> types.Gifts:
"""
Expand Down
34 changes: 32 additions & 2 deletions telebot/apihelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1929,7 +1929,7 @@ def get_available_gifts(token):
return _make_request(token, method_url)


def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None):
def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None):
method_url = 'sendGift'
payload = {'user_id': user_id, 'gift_id': gift_id}
if text:
Expand All @@ -1938,6 +1938,36 @@ def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_ent
payload['text_parse_mode'] = text_parse_mode
if text_entities:
payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities))
if pay_for_upgrade is not None:
payload['pay_for_upgrade'] = pay_for_upgrade
return _make_request(token, method_url, params=payload, method='post')


def verify_user(token, user_id, custom_description=None):
method_url = 'verifyUser'
payload = {'user_id': user_id}
if custom_description:
payload['custom_description'] = custom_description
return _make_request(token, method_url, params=payload, method='post')


def verify_chat(token, chat_id, custom_description=None):
method_url = 'verifyChat'
payload = {'chat_id': chat_id}
if custom_description:
payload['custom_description'] = custom_description
return _make_request(token, method_url, params=payload, method='post')


def remove_user_verification(token, user_id):
method_url = 'removeUserVerification'
payload = {'user_id': user_id}
return _make_request(token, method_url, params=payload, method='post')


def remove_chat_verification(token, chat_id):
method_url = 'removeChatVerification'
payload = {'chat_id': chat_id}
return _make_request(token, method_url, params=payload, method='post')

def set_sticker_emoji_list(token, sticker, emoji_list):
Expand Down Expand Up @@ -2276,7 +2306,7 @@ def convert_input_media_array(array):
if isinstance(thumbnail, types.InputFile):
thumbnail_key = 'thumbnail_' + key
files[thumbnail_key] = thumbnail
media_dict['thumbnail'] = 'attach://' + thumbnail_key
media_dict['thumbnail'] = 'attach://' + thumbnail_key
media.append(media_dict)
return json.dumps(media), files

Expand Down
72 changes: 70 additions & 2 deletions telebot/async_telebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7707,7 +7707,8 @@ async def delete_sticker_set(self, name:str) -> bool:

return await asyncio_helper.delete_sticker_set(self.token, name)

async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool:
async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None,
text_entities: Optional[List[types.MessageEntity]]=None, pay_for_upgrade: Optional[bool]=None) -> bool:
"""
Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success.

Expand All @@ -7719,6 +7720,9 @@ async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None,
:param gift_id: Identifier of the gift
:type gift_id: :obj:`str`

:param pay_for_upgrade: Pass True to pay for the gift upgrade from the bot's balance, thereby making the upgrade free for the receiver
:type pay_for_upgrade: :obj:`bool`

:param text: Text that will be shown along with the gift; 0-255 characters
:type text: :obj:`str`

Expand All @@ -7731,7 +7735,71 @@ async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None,
:return: Returns True on success.
:rtype: :obj:`bool`
"""
return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities)
return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities, pay_for_upgrade=pay_for_upgrade)

async def verify_user(self, user_id: int, custom_description: Optional[str]=None) -> bool:
"""
Verifies a user on behalf of the organization which is represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#verifyuser

:param user_id: Unique identifier of the target user
:type user_id: :obj:`int`

:param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description.
:type custom_description: :obj:`str`

:return: Returns True on success.
:rtype: :obj:`bool`
"""
return await asyncio_helper.verify_user(self.token, user_id, custom_description=custom_description)

async def verify_chat(self, chat_id: Union[int, str], custom_description: Optional[str]=None) -> bool:
"""
Verifies a chat on behalf of the organization which is represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#verifychat

:param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
:type chat_id: :obj:`int` | :obj:`str`

:param custom_description: Custom description for the verification; 0-70 characters. Must be empty if the organization isn't allowed to provide a custom verification description.
:type custom_description: :obj:`str`

:return: Returns True on success.
:rtype: :obj:`bool`
"""

return await asyncio_helper.verify_chat(self.token, chat_id, custom_description=custom_description)

async def remove_user_verification(self, user_id: int) -> bool:
"""
Removes verification from a user who is currently verified on behalf of the organization represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#removeuserverification

:param user_id: Unique identifier of the target user
:type user_id: :obj:`int`

:return: Returns True on success.
:rtype: :obj:`bool`

"""
return await asyncio_helper.remove_user_verification(self.token, user_id)

async def remove_chat_verification(self, chat_id: Union[int, str]) -> bool:
"""
Removes verification from a chat that is currently verified on behalf of the organization represented by the bot. Returns True on success.

Telegram documentation: https://core.telegram.org/bots/api#removechatverification

:param chat_id: Unique identifier for the target chat or username of the target channel (in the format @channelusername)
:type chat_id: :obj:`int` | :obj:`str`

:return: Returns True on success.
:rtype: :obj:`bool`
"""
return await asyncio_helper.remove_chat_verification(self.token, chat_id)

async def get_available_gifts(self) -> types.Gifts:
"""
Expand Down
30 changes: 28 additions & 2 deletions telebot/asyncio_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -1916,7 +1916,7 @@ async def delete_sticker_set(token, name):
payload = {'name': name}
return await _process_request(token, method_url, params=payload, method='post')

async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None):
async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None, pay_for_upgrade=None):
method_url = 'sendGift'
payload = {'user_id': user_id, 'gift_id': gift_id}
if text:
Expand All @@ -1925,6 +1925,32 @@ async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, te
payload['text_parse_mode'] = text_parse_mode
if text_entities:
payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities))
if pay_for_upgrade is not None:
payload['pay_for_upgrade'] = pay_for_upgrade
return await _process_request(token, method_url, params=payload, method='post')

async def verify_user(token, user_id, custom_description=None):
method_url = 'verifyUser'
payload = {'user_id': user_id}
if custom_description:
payload['custom_description'] = custom_description
return await _process_request(token, method_url, params=payload, method='post')

async def verify_chat(token, chat_id, custom_description=None):
method_url = 'verifyChat'
payload = {'chat_id': chat_id}
if custom_description:
payload['custom_description'] = custom_description
return await _process_request(token, method_url, params=payload, method='post')

async def remove_user_verification(token, user_id):
method_url = 'removeUserVerification'
payload = {'user_id': user_id}
return await _process_request(token, method_url, params=payload, method='post')

async def remove_chat_verification(token, chat_id):
method_url = 'removeChatVerification'
payload = {'chat_id': chat_id}
return await _process_request(token, method_url, params=payload, method='post')

async def get_available_gifts(token):
Expand Down Expand Up @@ -2248,7 +2274,7 @@ async def convert_input_media_array(array):
if isinstance(thumbnail, types.InputFile):
thumbnail_key = 'thumbnail_' + key
files[thumbnail_key] = thumbnail
media_dict['thumbnail'] = 'attach://' + thumbnail_key
media_dict['thumbnail'] = 'attach://' + thumbnail_key
media.append(media_dict)
return json.dumps(media), files

Expand Down
10 changes: 9 additions & 1 deletion telebot/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4449,6 +4449,10 @@ def __init__(self, id: str, title: str, input_message_content: InputMessageConte
self.thumbnail_width: Optional[int] = thumbnail_width
self.thumbnail_height: Optional[int] = thumbnail_height

if hide_url:
log_deprecation_warning('The parameter "hide_url" is deprecated. Pass an empty string as url instead.')
self.url = ''


@property
def thumb_url(self) -> str:
Expand Down Expand Up @@ -11059,6 +11063,9 @@ class Gift(JsonDeserializable):
:param star_count: The number of Telegram Stars that must be paid to send the sticker
:type star_count: :obj:`int`

:param upgrade_star_count: Optional. The number of Telegram Stars that must be paid to upgrade the gift to a unique one
:type upgrade_star_count: :obj:`int`

:param total_count: Optional. The total number of the gifts of this type that can be sent; for limited gifts only
:type total_count: :obj:`int`

Expand All @@ -11069,12 +11076,13 @@ class Gift(JsonDeserializable):
:rtype: :class:`Gift`
"""

def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, **kwargs):
def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, **kwargs):
self.id: str = id
self.sticker: Sticker = sticker
self.star_count: int = star_count
self.total_count: Optional[int] = total_count
self.remaining_count: Optional[int] = remaining_count
self.upgrade_star_count: Optional[int] = upgrade_star_count

@classmethod
def de_json(cls, json_string):
Expand Down
Loading