From 09308bfd9b983a51e2c6ba21b8047023c195f595 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 16:46:42 +0500 Subject: [PATCH 01/27] Added the field has_topics_enabled to the class User, which can be used to determine whether forum topic mode is enabled for the bot in private chats. --- telebot/types.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index a707b72ba..7175ea2cc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -512,6 +512,9 @@ class User(JsonDeserializable, Dictionaryable, JsonSerializable): :param has_main_web_app: Optional. True, if the bot has a main Web App. Returned only in getMe. :type has_main_web_app: :obj:`bool` + :param has_topics_enabled: Optional. True, if the bot has forum topic mode enabled in private chats. Returned only in getMe. + :type has_topics_enabled: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.User` """ @@ -525,7 +528,7 @@ def de_json(cls, json_string): 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, is_premium=None, added_to_attachment_menu=None, can_connect_to_business=None, - has_main_web_app=None, **kwargs): + has_main_web_app=None, has_topics_enabled=None, **kwargs): self.id: int = id self.is_bot: bool = is_bot self.first_name: str = first_name @@ -539,6 +542,7 @@ def __init__(self, id, is_bot, first_name, last_name=None, username=None, langua self.added_to_attachment_menu: Optional[bool] = added_to_attachment_menu self.can_connect_to_business: Optional[bool] = can_connect_to_business self.has_main_web_app: Optional[bool] = has_main_web_app + self.has_topics_enabled: Optional[bool] = has_topics_enabled @property def full_name(self) -> str: From ec90c742edafe4c762cfcb18d882834c12306778 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 16:50:22 +0500 Subject: [PATCH 02/27] Added the method sendMessageDraft, allowing partial messages to be streamed to a user while being generated. --- telebot/__init__.py | 39 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 14 ++++++++++++++ telebot/async_telebot.py | 36 ++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 12 ++++++++++++ 4 files changed, 101 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 59a8d7db8..cc391489d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3933,6 +3933,45 @@ def send_contact( protect_content=protect_content, message_thread_id=message_thread_id, reply_parameters=reply_parameters, business_connection_id=business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast) ) + + + def send_message_draft( + self, chat_id: int, + draft_id: int, + text: str, + message_thread_id: Optional[int]=None, + parse_mode: Optional[str]=None, + entities: Optional[List[types.MessageEntity]]=None): + """ + Use this method to stream a partial message to a user while the message is being generated; + supported only for bots with forum topic mode enabled. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#sendmessagedraft + + :param chat_id: Unique identifier for the target private chat + :type chat_id: :obj:`int` + + :param message_thread_id: Unique identifier for the target message thread + :type message_thread_id: :obj:`int` + + :param draft_id: Unique identifier of the message draft; must be non-zero. Changes of drafts with the same identifier are animated + :type draft_id: :obj:`int` + + :param text: Text of the message to be sent, 1-4096 characters after entities parsing + :type text: :obj:`str` + + :param parse_mode: Mode for parsing entities in the message text. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param entities: A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode + :type entities: :obj:`list` of :class:`telebot.types.MessageEntity + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.send_message_draft( + self.token, chat_id, draft_id, text, parse_mode=parse_mode, entities=entities, message_thread_id=message_thread_id) + def send_chat_action( diff --git a/telebot/apihelper.py b/telebot/apihelper.py index cba38bbdd..c3cee6b23 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -805,6 +805,20 @@ def send_contact( return _make_request(token, method_url, params=payload) +def send_message_draft( + token, chat_id, draft_id, text, + message_thread_id=None, parse_mode=None, entities=None): + method_url = r'sendMessageDraft' + payload = {'chat_id': chat_id, 'draft_id': draft_id, 'text': text} + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id + if parse_mode: + payload['parse_mode'] = parse_mode + if entities: + payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) + return _make_request(token, method_url, params=payload) + + def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None, business_connection_id=None): method_url = r'sendChatAction' payload = {'chat_id': chat_id, 'action': action} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fe8013135..4b2397399 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5449,6 +5449,42 @@ async def send_contact( disable_notification, reply_markup, timeout, protect_content, message_thread_id, reply_parameters, business_connection_id, message_effect_id=message_effect_id, allow_paid_broadcast=allow_paid_broadcast)) + async def send_message_draft( + self, chat_id: int, + draft_id: int, + text: str, + message_thread_id: Optional[int]=None, + parse_mode: Optional[str]=None, + entities: Optional[List[types.MessageEntity]]=None): + """ + Use this method to stream a partial message to a user while the message is being generated; + supported only for bots with forum topic mode enabled. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#sendmessagedraft + + :param chat_id: Unique identifier for the target private chat + :type chat_id: :obj:`int` + + :param message_thread_id: Unique identifier for the target message thread + :type message_thread_id: :obj:`int` + + :param draft_id: Unique identifier of the message draft; must be non-zero. Changes of drafts with the same identifier are animated + :type draft_id: :obj:`int` + + :param text: Text of the message to be sent, 1-4096 characters after entities parsing + :type text: :obj:`str` + + :param parse_mode: Mode for parsing entities in the message text. See formatting options for more details. + :type parse_mode: :obj:`str` + + :param entities: A JSON-serialized list of special entities that appear in message text, which can be specified instead of parse_mode + :type entities: :obj:`list` of :class:`telebot.types.MessageEntity + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.send_message_draft( + self.token, chat_id, draft_id, text, parse_mode=parse_mode, entities=entities, message_thread_id=message_thread_id) async def send_chat_action( self, chat_id: Union[int, str], action: str, timeout: Optional[int]=None, message_thread_id: Optional[int]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index bc50068f6..0c0196aa2 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -805,6 +805,18 @@ async def send_contact( payload['allow_paid_broadcast'] = allow_paid_broadcast return await _process_request(token, method_url, params=payload) +async def send_message_draft( + token, chat_id, draft_id, text, + message_thread_id=None, parse_mode=None, entities=None): + method_url = r'sendMessageDraft' + payload = {'chat_id': chat_id, 'draft_id': draft_id, 'text': text} + if message_thread_id is not None: + payload['message_thread_id'] = message_thread_id + if parse_mode: + payload['parse_mode'] = parse_mode + if entities: + payload['entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(entities)) + return await _process_request(token, method_url, params=payload) async def send_chat_action(token, chat_id, action, timeout=None, message_thread_id=None, business_connection_id=None): method_url = r'sendChatAction' From 345a9b1b681acba11e73cb36be5bc63bc06979cb Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 16:51:37 +0500 Subject: [PATCH 03/27] Supported the fields message_thread_id and is_topic_message in the class Message for messages in private chats with forum topic mode enabled. --- telebot/types.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 7175ea2cc..9b4569e21 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -920,7 +920,7 @@ class Message(JsonDeserializable): :param message_id: Unique message identifier inside this chat :type message_id: :obj:`int` - :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups only + :param message_thread_id: Optional. Unique identifier of a message thread to which the message belongs; for supergroups and private chats only :type message_thread_id: :obj:`int` :param from_user: Optional. Sender of the message; empty for messages sent to channels. For backward compatibility, the @@ -952,7 +952,7 @@ class Message(JsonDeserializable): :forward_origin: Optional. For forwarded messages, information about the original message; :type forward_origin: :class:`telebot.types.MessageOrigin` - :param is_topic_message: Optional. True, if the message is sent to a forum topic + :param is_topic_message: Optional. True, if the message is sent to a topic in a forum supergroup or a private chat with the bot :type is_topic_message: :obj:`bool` :param is_automatic_forward: Optional. :obj:`bool`, if the message is a channel post that was automatically From 0a1ab2f47c4db50c1b44ccc9205a29211a8e2149 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 16:55:22 +0500 Subject: [PATCH 04/27] Supported the parameter message_thread_id in private chats with topics in some methods --- telebot/__init__.py | 2 +- telebot/async_telebot.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index cc391489d..cd755422d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -3999,7 +3999,7 @@ def send_chat_action( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param message_thread_id: The thread identifier of a message from which the reply will be sent(supergroups only) + :param message_thread_id: The thread identifier of a message from which the reply will be sent(for supergroups and private chats) :type message_thread_id: :obj:`int` :param business_connection_id: Identifier of a business connection diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4b2397399..ce80bb444 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -5511,7 +5511,7 @@ async def send_chat_action( :param timeout: Timeout in seconds for the request. :type timeout: :obj:`int` - :param message_thread_id: The thread to which the message will be sent(supergroups only) + :param message_thread_id: The thread to which the message will be sent(supergroups and private chats only) :type message_thread_id: :obj:`int` :param business_connection_id: Identifier of a business connection, in which the message will be sent From 184a67233d45329db8b32c817cd85ba6b7121f5a Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 16:58:00 +0500 Subject: [PATCH 05/27] Added the field is_name_implicit to the classes ForumTopic and ForumTopicCreated. --- telebot/types.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 9b4569e21..6ecced0bd 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -8039,6 +8039,9 @@ class ForumTopicCreated(JsonDeserializable): :param icon_custom_emoji_id: Optional. Unique identifier of the custom emoji shown as the topic icon :type icon_custom_emoji_id: :obj:`str` + :param is_name_implicit: Optional. True, if the name of the topic wasn't specified explicitly by its creator and likely needs to be changed by the bot + :type is_name_implicit: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ForumTopicCreated` """ @@ -8048,11 +8051,12 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None, **kwargs) -> None: + def __init__(self, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None, + is_name_implicit: Optional[bool]=None, **kwargs) -> None: self.name: str = name self.icon_color: int = icon_color self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id - + self.is_name_implicit: Optional[bool] = is_name_implicit class ForumTopicClosed(JsonDeserializable): """ @@ -8157,6 +8161,9 @@ class ForumTopic(JsonDeserializable): :param icon_custom_emoji_id: Optional. Unique identifier of the custom emoji shown as the topic icon :type icon_custom_emoji_id: :obj:`str` + :param is_name_implicit: Optional. True, if the name of the topic wasn't specified explicitly by its creator and likely needs to be changed by the bot + :type is_name_implicit: :obj:`bool` + :return: Instance of the class :rtype: :class:`telebot.types.ForumTopic` """ @@ -8168,11 +8175,12 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, message_thread_id: int, name: str, icon_color: int, icon_custom_emoji_id: Optional[str]=None, - **kwargs) -> None: + is_name_implicit: Optional[bool]=None, **kwargs) -> None: self.message_thread_id: int = message_thread_id self.name: str = name self.icon_color: int = icon_color self.icon_custom_emoji_id: Optional[str] = icon_custom_emoji_id + self.is_name_implicit: Optional[bool] = is_name_implicit class WriteAccessAllowed(JsonDeserializable): From 00032db7ee8ba58c6da7c48c6e216fee8d7fde5d Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:04:25 +0500 Subject: [PATCH 06/27] Added the methods getUserGifts and getChatGifts. --- telebot/__init__.py | 129 ++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 52 +++++++++++++++ telebot/async_telebot.py | 129 ++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 51 +++++++++++++++ 4 files changed, 361 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index cd755422d..47121dea4 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6718,6 +6718,135 @@ def get_business_account_gifts( ) ) + def get_user_gifts( + self, user_id: int, + exclude_unlimited: Optional[bool]=None, + exclude_limited_upgradable: Optional[bool]=None, + exclude_limited_non_upgradable: Optional[bool]=None, + exclude_from_blockchain: Optional[bool]=None, + exclude_unique: Optional[bool]=None, + sort_by_price: Optional[bool]=None, + offset: Optional[str]=None, + limit: Optional[int]=None) -> types.OwnedGifts: + """ + Returns the gifts owned and hosted by a user. Returns OwnedGifts on success. + + Telegram documentation: https://core.telegram.org/bots/api#getusergifts + + :param user_id: Unique identifier of the user + :type user_id: :obj:`int` + + :param exclude_unlimited: Pass True to exclude gifts that can be purchased an unlimited number of times + :type exclude_unlimited: :obj:`bool` + + :param exclude_limited_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can be upgraded to unique + :type exclude_limited_upgradable: :obj:`bool` + + :param exclude_limited_non_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can't be upgraded to unique + :type exclude_limited_non_upgradable: :obj:`bool` + + :param exclude_from_blockchain: Pass True to exclude gifts that were assigned from the TON blockchain and can't be resold or transferred in Telegram + :type exclude_from_blockchain: :obj:`bool` + + :param exclude_unique: Pass True to exclude unique gifts + :type exclude_unique: :obj:`bool` + + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. + :type sort_by_price: :obj:`bool` + + :param offset: Offset of the first entry to return as received from the previous request; use an empty string to get the first chunk of results + :type offset: :obj:`str` + + :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 + :type limit: :obj:`int` + + :return: On success, a OwnedGifts object is returned. + :rtype: :class:`telebot.types.OwnedGifts` + """ + return types.OwnedGifts.de_json( + apihelper.get_user_gifts( + self.token, user_id, + exclude_unlimited=exclude_unlimited, + exclude_limited_upgradable=exclude_limited_upgradable, + exclude_limited_non_upgradable=exclude_limited_non_upgradable, + exclude_from_blockchain=exclude_from_blockchain, + exclude_unique=exclude_unique, + sort_by_price=sort_by_price, + offset=offset, + limit=limit + ) + ) + + def get_chat_gifts( + self, chat_id: Union[int, str], + exclude_unsaved: Optional[bool]=None, + exclude_saved: Optional[bool]=None, + exclude_unlimited: Optional[bool]=None, + exclude_limited_upgradable: Optional[bool]=None, + exclude_limited_non_upgradable: Optional[bool]=None, + exclude_from_blockchain: Optional[bool]=None, + exclude_unique: Optional[bool]=None, + sort_by_price: Optional[bool]=None, + offset: Optional[str]=None, + limit: Optional[int]=None) -> types.OwnedGifts: + """ + Returns the gifts owned by a chat. Returns OwnedGifts on success. + + Telegram documentation: https://core.telegram.org/bots/api#getchatgifts + + :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 exclude_unsaved: Pass True to exclude gifts that aren't saved to the chat's profile page. Always True, unless the bot has the can_post_messages administrator right in the channel. + :type exclude_unsaved: :obj:`bool` + + :param exclude_saved: Pass True to exclude gifts that are saved to the chat's profile page. Always False, unless the bot has the can_post_messages administrator right in the channel. + :type exclude_saved: :obj:`bool` + + :param exclude_unlimited: Pass True to exclude gifts that can be purchased an unlimited number of times + :type exclude_unlimited: :obj:`bool` + + :param exclude_limited_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can be upgraded to unique + :type exclude_limited_upgradable: :obj:`bool` + + :param exclude_limited_non_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can't be upgraded to unique + :type exclude_limited_non_upgradable: :obj:`bool` + + :param exclude_from_blockchain: Pass True to exclude gifts that were assigned from the TON blockchain and can't be resold or transferred in Telegram + :type exclude_from_blockchain: :obj:`bool` + + :param exclude_unique: Pass True to exclude unique gifts + :type exclude_unique: :obj:`bool` + + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. + :type sort_by_price: :obj:`bool` + + :param offset: Offset of the first entry to return as received from the previous request; use an empty string to get the first chunk of results + :type offset: :obj:`str` + + :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 + :type limit: :obj:`int` + + :return: On success, a OwnedGifts object is returned. + :rtype: :class:`telebot.types.OwnedGifts` + """ + return types.OwnedGifts.de_json( + apihelper.get_chat_gifts( + self.token, chat_id, + exclude_unsaved=exclude_unsaved, + exclude_saved=exclude_saved, + exclude_unlimited=exclude_unlimited, + exclude_limited_upgradable=exclude_limited_upgradable, + exclude_limited_non_upgradable=exclude_limited_non_upgradable, + exclude_from_blockchain=exclude_from_blockchain, + exclude_unique=exclude_unique, + sort_by_price=sort_by_price, + offset=offset, + limit=limit + ) + ) + + def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) -> bool: """ Converts a given regular gift to Telegram Stars. Requires the can_convert_gifts_to_stars business bot right. Returns True on success. diff --git a/telebot/apihelper.py b/telebot/apihelper.py index c3cee6b23..0f85e18b6 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2126,6 +2126,58 @@ def get_business_account_gifts(token, business_connection_id, exclude_unsaved=No return _make_request(token, method_url, params=payload) +def get_user_gifts(token, user_id, exclude_unlimited=None, exclude_limited_upgradable=None, + exclude_limited_non_upgradable=None, exclude_from_blockchain=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None): + method_url = 'getUserGifts' + payload = {'user_id': user_id} + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return _make_request(token, method_url, params=payload) + +def get_chat_gifts(token, chat_id, exclude_unsaved=None, exclude_saved=None, + exclude_unlimited=None, exclude_limited_upgradable=None, + exclude_limited_non_upgradable=None, exclude_from_blockchain=None, + exclude_unique=None, sort_by_price=None, offset=None, limit=None): + method_url = 'getChatGifts' + payload = {'chat_id': chat_id} + if exclude_unsaved is not None: + payload['exclude_unsaved'] = exclude_unsaved + if exclude_saved is not None: + payload['exclude_saved'] = exclude_saved + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return _make_request(token, method_url, params=payload) + + def convert_gift_to_stars(token, business_connection_id, owned_gift_id): method_url = 'convertGiftToStars' payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index ce80bb444..bb7ee2955 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8169,6 +8169,135 @@ async def get_business_account_gifts( ) ) + async def get_user_gifts( + self, user_id: int, + exclude_unlimited: Optional[bool]=None, + exclude_limited_upgradable: Optional[bool]=None, + exclude_limited_non_upgradable: Optional[bool]=None, + exclude_from_blockchain: Optional[bool]=None, + exclude_unique: Optional[bool]=None, + sort_by_price: Optional[bool]=None, + offset: Optional[str]=None, + limit: Optional[int]=None) -> types.OwnedGifts: + """ + Returns the gifts owned and hosted by a user. Returns OwnedGifts on success. + + Telegram documentation: https://core.telegram.org/bots/api#getusergifts + + :param user_id: Unique identifier of the user + :type user_id: :obj:`int` + + :param exclude_unlimited: Pass True to exclude gifts that can be purchased an unlimited number of times + :type exclude_unlimited: :obj:`bool` + + :param exclude_limited_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can be upgraded to unique + :type exclude_limited_upgradable: :obj:`bool` + + :param exclude_limited_non_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can't be upgraded to unique + :type exclude_limited_non_upgradable: :obj:`bool` + + :param exclude_from_blockchain: Pass True to exclude gifts that were assigned from the TON blockchain and can't be resold or transferred in Telegram + :type exclude_from_blockchain: :obj:`bool` + + :param exclude_unique: Pass True to exclude unique gifts + :type exclude_unique: :obj:`bool` + + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. + :type sort_by_price: :obj:`bool` + + :param offset: Offset of the first entry to return as received from the previous request; use an empty string to get the first chunk of results + :type offset: :obj:`str` + + :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 + :type limit: :obj:`int` + + :return: On success, a OwnedGifts object is returned. + :rtype: :class:`telebot.types.OwnedGifts` + """ + return types.OwnedGifts.de_json( + await asyncio_helper.get_user_gifts( + self.token, user_id, + exclude_unlimited=exclude_unlimited, + exclude_limited_upgradable=exclude_limited_upgradable, + exclude_limited_non_upgradable=exclude_limited_non_upgradable, + exclude_from_blockchain=exclude_from_blockchain, + exclude_unique=exclude_unique, + sort_by_price=sort_by_price, + offset=offset, + limit=limit + ) + ) + + + async def get_chat_gifts( + self, chat_id: Union[int, str], + exclude_unsaved: Optional[bool]=None, + exclude_saved: Optional[bool]=None, + exclude_unlimited: Optional[bool]=None, + exclude_limited_upgradable: Optional[bool]=None, + exclude_limited_non_upgradable: Optional[bool]=None, + exclude_from_blockchain: Optional[bool]=None, + exclude_unique: Optional[bool]=None, + sort_by_price: Optional[bool]=None, + offset: Optional[str]=None, + limit: Optional[int]=None) -> types.OwnedGifts: + """ + Returns the gifts owned by a chat. Returns OwnedGifts on success. + + Telegram documentation: https://core.telegram.org/bots/api#getchatgifts + + :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 exclude_unsaved: Pass True to exclude gifts that aren't saved to the chat's profile page. Always True, unless the bot has the can_post_messages administrator right in the channel. + :type exclude_unsaved: :obj:`bool` + + :param exclude_saved: Pass True to exclude gifts that are saved to the chat's profile page. Always False, unless the bot has the can_post_messages administrator right in the channel. + :type exclude_saved: :obj:`bool` + + :param exclude_unlimited: Pass True to exclude gifts that can be purchased an unlimited number of times + :type exclude_unlimited: :obj:`bool` + + :param exclude_limited_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can be upgraded to unique + :type exclude_limited_upgradable: :obj:`bool` + + :param exclude_limited_non_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can't be upgraded to unique + :type exclude_limited_non_upgradable: :obj:`bool` + + :param exclude_from_blockchain: Pass True to exclude gifts that were assigned from the TON blockchain and can't be resold or transferred in Telegram + :type exclude_from_blockchain: :obj:`bool` + + :param exclude_unique: Pass True to exclude unique gifts + :type exclude_unique: :obj:`bool` + + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. + :type sort_by_price: :obj:`bool` + + :param offset: Offset of the first entry to return as received from the previous request; use an empty string to get the first chunk of results + :type offset: :obj:`str` + + :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 + :type limit: :obj:`int` + + :return: On success, a OwnedGifts object is returned. + :rtype: :class:`telebot.types.OwnedGifts` + """ + return types.OwnedGifts.de_json( + await asyncio_helper.get_chat_gifts( + self.token, chat_id, + exclude_unsaved=exclude_unsaved, + exclude_saved=exclude_saved, + exclude_unlimited=exclude_unlimited, + exclude_limited_upgradable=exclude_limited_upgradable, + exclude_limited_non_upgradable=exclude_limited_non_upgradable, + exclude_from_blockchain=exclude_from_blockchain, + exclude_unique=exclude_unique, + sort_by_price=sort_by_price, + offset=offset, + limit=limit + ) + ) + async def convert_gift_to_stars(self, business_connection_id: str, owned_gift_id: str) -> bool: """ Converts a given regular gift to Telegram Stars. Requires the can_convert_gifts_to_stars business bot right. Returns True on success. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 0c0196aa2..231e60d32 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2092,6 +2092,57 @@ async def get_business_account_gifts(token, business_connection_id, exclude_unsa payload['limit'] = limit return await _process_request(token, method_url, params=payload) +async def get_user_gifts(token, user_id, exclude_unlimited=None, exclude_limited_upgradable=None, + exclude_limited_non_upgradable=None, exclude_from_blockchain=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None): + method_url = 'getUserGifts' + payload = {'user_id': user_id} + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return await _process_request(token, method_url, params=payload) + +async def get_chat_gifts(token, chat_id, exclude_unsaved=None, exclude_saved=None, + exclude_unlimited=None, exclude_limited_upgradable=None, + exclude_limited_non_upgradable=None, exclude_from_blockchain=None, + exclude_unique=None, sort_by_price=None, offset=None, limit=None): + method_url = 'getChatGifts' + payload = {'chat_id': chat_id} + if exclude_unsaved is not None: + payload['exclude_unsaved'] = exclude_unsaved + if exclude_saved is not None: + payload['exclude_saved'] = exclude_saved + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return await _process_request(token, method_url, params=payload) + async def convert_gift_to_stars(token, business_connection_id, owned_gift_id): method_url = 'convertGiftToStars' payload = {'business_connection_id': business_connection_id, 'owned_gift_id': owned_gift_id} From d8a107a169db6eb3c9317ae0713076b5187b4c77 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:07:34 +0500 Subject: [PATCH 07/27] Replaced the field last_resale_star_count with the fields last_resale_currency and last_resale_amount in the class UniqueGiftInfo. --- telebot/types.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 6ecced0bd..e9da94514 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12445,6 +12445,12 @@ class UniqueGiftInfo(JsonDeserializable): :param last_resale_star_count: Optional. For gifts bought from other users, the price paid for the gift :type last_resale_star_count: :obj:`int` + :param last_resale_currency: Optional. For gifts bought from other users, the currency in which the payment for the gift was done. Currently, one of “XTR” for Telegram Stars or “TON” for toncoins. + :type last_resale_currency: :obj:`str` + + :param last_resale_amount: Optional. For gifts bought from other users, the price paid for the gift in either Telegram Stars or nanotoncoins + :type last_resale_amount: :obj:`int` + :param owned_gift_id: Optional. Unique identifier of the received gift for the bot; only present for gifts received on behalf of business accounts :type owned_gift_id: :obj:`str` @@ -12459,14 +12465,18 @@ class UniqueGiftInfo(JsonDeserializable): """ def __init__(self, gift: UniqueGift, origin: str, owned_gift_id: Optional[str] = None, transfer_star_count: Optional[int] = None, next_transfer_date: Optional[int] = None, - last_resale_star_count: Optional[int] = None, **kwargs): + last_resale_star_count: Optional[int] = None, last_resale_currency: Optional[str] = None, + last_resale_amount: Optional[int] = None, **kwargs): self.gift: UniqueGift = gift self.origin: str = origin self.last_resale_star_count: Optional[int] = last_resale_star_count + self.last_resale_currency: Optional[str] = last_resale_currency + self.last_resale_amount: Optional[int] = last_resale_amount self.owned_gift_id: Optional[str] = owned_gift_id self.transfer_star_count: Optional[int] = transfer_star_count self.next_transfer_date: Optional[int] = next_transfer_date + @classmethod def de_json(cls, json_string): if json_string is None: return None From 3bfc160ce09373085409dfaba9c12aa5739d0bf7 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:14:25 +0500 Subject: [PATCH 08/27] Replaced the parameter exclude_limited with the parameters exclude_limited_upgradable and exclude_limited_non_upgradable in the method getBusinessAccountGifts. --- telebot/__init__.py | 20 +++++++-- telebot/apihelper.py | 88 ++++++++++++++++++++------------------- telebot/async_telebot.py | 20 +++++++-- telebot/asyncio_helper.py | 45 ++++++++++---------- 4 files changed, 104 insertions(+), 69 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 47121dea4..87f5439df 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6668,7 +6668,9 @@ def get_business_account_gifts( exclude_unique: Optional[bool]=None, sort_by_price: Optional[bool]=None, offset: Optional[str]=None, - limit: Optional[int]=None) -> types.OwnedGifts: + limit: Optional[int]=None, + exclude_limited_upgradable: Optional[bool]=None, + exclude_limited_non_upgradable: Optional[bool]=None) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. @@ -6701,20 +6703,32 @@ def get_business_account_gifts( :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 :type limit: :obj:`int` + :param exclude_limited_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can be upgraded to unique + :type exclude_limited_upgradable: :obj:`bool` + + :param exclude_limited_non_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can't be upgraded to unique + :type exclude_limited_non_upgradable: :obj:`bool` + :return: On success, a OwnedGifts object is returned. :rtype: :class:`telebot.types.OwnedGifts` """ + if exclude_limited is not None and exclude_limited_upgradable is None and exclude_limited_non_upgradable is None: + exclude_limited_upgradable = exclude_limited + exclude_limited_non_upgradable = exclude_limited + + return types.OwnedGifts.de_json( apihelper.get_business_account_gifts( self.token, business_connection_id, exclude_unsaved=exclude_unsaved, exclude_saved=exclude_saved, exclude_unlimited=exclude_unlimited, - exclude_limited=exclude_limited, exclude_unique=exclude_unique, sort_by_price=sort_by_price, offset=offset, - limit=limit + limit=limit, + exclude_limited_upgradable=exclude_limited_upgradable, + exclude_limited_non_upgradable=exclude_limited_non_upgradable ) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 0f85e18b6..d951c84fc 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2103,51 +2103,55 @@ def transfer_business_account_stars(token, business_connection_id, star_count): return _make_request(token, method_url, params=payload, method='post') def get_business_account_gifts(token, business_connection_id, exclude_unsaved=None, exclude_saved=None, - exclude_unlimited=None, exclude_limited=None, exclude_unique=None, - sort_by_price=None, offset=None, limit=None): - method_url = 'getBusinessAccountGifts' - payload = {'business_connection_id': business_connection_id} - if exclude_unsaved is not None: - payload['exclude_unsaved'] = exclude_unsaved - if exclude_saved is not None: - payload['exclude_saved'] = exclude_saved - if exclude_unlimited is not None: - payload['exclude_unlimited'] = exclude_unlimited - if exclude_limited is not None: - payload['exclude_limited'] = exclude_limited - if exclude_unique is not None: - payload['exclude_unique'] = exclude_unique - if sort_by_price is not None: - payload['sort_by_price'] = sort_by_price - if offset is not None: - payload['offset'] = offset - if limit is not None: - payload['limit'] = limit - return _make_request(token, method_url, params=payload) + exclude_unlimited=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None, exclude_limited_upgradable=None, + exclude_limited_non_upgradable=None): + method_url = 'getBusinessAccountGifts' + payload = {'business_connection_id': business_connection_id} + if exclude_unsaved is not None: + payload['exclude_unsaved'] = exclude_unsaved + if exclude_saved is not None: + payload['exclude_saved'] = exclude_saved + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + + return _make_request(token, method_url, params=payload) def get_user_gifts(token, user_id, exclude_unlimited=None, exclude_limited_upgradable=None, - exclude_limited_non_upgradable=None, exclude_from_blockchain=None, exclude_unique=None, - sort_by_price=None, offset=None, limit=None): - method_url = 'getUserGifts' - payload = {'user_id': user_id} - if exclude_unlimited is not None: - payload['exclude_unlimited'] = exclude_unlimited - if exclude_limited_upgradable is not None: - payload['exclude_limited_upgradable'] = exclude_limited_upgradable - if exclude_limited_non_upgradable is not None: - payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable - if exclude_from_blockchain is not None: - payload['exclude_from_blockchain'] = exclude_from_blockchain - if exclude_unique is not None: - payload['exclude_unique'] = exclude_unique - if sort_by_price is not None: - payload['sort_by_price'] = sort_by_price - if offset is not None: - payload['offset'] = offset - if limit is not None: - payload['limit'] = limit - return _make_request(token, method_url, params=payload) + exclude_limited_non_upgradable=None, exclude_from_blockchain=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None): + method_url = 'getUserGifts' + payload = {'user_id': user_id} + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + return _make_request(token, method_url, params=payload) def get_chat_gifts(token, chat_id, exclude_unsaved=None, exclude_saved=None, exclude_unlimited=None, exclude_limited_upgradable=None, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index bb7ee2955..d802aa57e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8119,7 +8119,10 @@ async def get_business_account_gifts( exclude_unique: Optional[bool]=None, sort_by_price: Optional[bool]=None, offset: Optional[str]=None, - limit: Optional[int]=None) -> types.OwnedGifts: + limit: Optional[int]=None, + exclude_limited_upgradable: Optional[bool]=None, + exclude_limited_non_upgradable: Optional[bool]=None, + ) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. @@ -8152,20 +8155,31 @@ async def get_business_account_gifts( :param limit: The maximum number of gifts to be returned; 1-100. Defaults to 100 :type limit: :obj:`int` + :param exclude_limited_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can be upgraded to unique + :type exclude_limited_upgradable: :obj:`bool` + + :param exclude_limited_non_upgradable: Pass True to exclude gifts that can be purchased a limited number of times and can't be upgraded to unique + :type exclude_limited_non_upgradable: :obj:`bool` + :return: On success, a OwnedGifts object is returned. :rtype: :class:`telebot.types.OwnedGifts` """ + if exclude_limited is not None and exclude_limited_upgradable is None and exclude_limited_non_upgradable is None: + exclude_limited_upgradable = exclude_limited + exclude_limited_non_upgradable = exclude_limited + return types.OwnedGifts.de_json( await asyncio_helper.get_business_account_gifts( self.token, business_connection_id, exclude_unsaved=exclude_unsaved, exclude_saved=exclude_saved, exclude_unlimited=exclude_unlimited, - exclude_limited=exclude_limited, exclude_unique=exclude_unique, sort_by_price=sort_by_price, offset=offset, - limit=limit + limit=limit, + exclude_limited_upgradable=exclude_limited_upgradable, + exclude_limited_non_upgradable=exclude_limited_non_upgradable ) ) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 231e60d32..045bfd85f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2070,27 +2070,30 @@ async def transfer_business_account_stars(token, business_connection_id, star_co return await _process_request(token, method_url, params=payload, method='post') async def get_business_account_gifts(token, business_connection_id, exclude_unsaved=None, exclude_saved=None, - exclude_unlimited=None, exclude_limited=None, exclude_unique=None, - sort_by_price=None, offset=None, limit=None): - method_url = 'getBusinessAccountGifts' - payload = {'business_connection_id': business_connection_id} - if exclude_unsaved is not None: - payload['exclude_unsaved'] = exclude_unsaved - if exclude_saved is not None: - payload['exclude_saved'] = exclude_saved - if exclude_unlimited is not None: - payload['exclude_unlimited'] = exclude_unlimited - if exclude_limited is not None: - payload['exclude_limited'] = exclude_limited - if exclude_unique is not None: - payload['exclude_unique'] = exclude_unique - if sort_by_price is not None: - payload['sort_by_price'] = sort_by_price - if offset is not None: - payload['offset'] = offset - if limit is not None: - payload['limit'] = limit - return await _process_request(token, method_url, params=payload) + exclude_unlimited=None, exclude_unique=None, + sort_by_price=None, offset=None, limit=None, exclude_limited_upgradable=None, + exclude_limited_non_upgradable=None): + method_url = 'getBusinessAccountGifts' + payload = {'business_connection_id': business_connection_id} + if exclude_unsaved is not None: + payload['exclude_unsaved'] = exclude_unsaved + if exclude_saved is not None: + payload['exclude_saved'] = exclude_saved + if exclude_unlimited is not None: + payload['exclude_unlimited'] = exclude_unlimited + if exclude_unique is not None: + payload['exclude_unique'] = exclude_unique + if sort_by_price is not None: + payload['sort_by_price'] = sort_by_price + if offset is not None: + payload['offset'] = offset + if limit is not None: + payload['limit'] = limit + if exclude_limited_upgradable is not None: + payload['exclude_limited_upgradable'] = exclude_limited_upgradable + if exclude_limited_non_upgradable is not None: + payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + return await _process_request(token, method_url, params=payload) async def get_user_gifts(token, user_id, exclude_unlimited=None, exclude_limited_upgradable=None, exclude_limited_non_upgradable=None, exclude_from_blockchain=None, exclude_unique=None, From b53bb5949f2fcd34d5ce79a5086213a0401a6ec8 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:20:16 +0500 Subject: [PATCH 09/27] =?UTF-8?q?Added=20the=20value=20=E2=80=9Cgifted=5Fu?= =?UTF-8?q?pgrade=E2=80=9D=20as=20a=20possible=20value=20of=20UniqueGiftIn?= =?UTF-8?q?fo.origin=20for=20messages=20about=20the=20upgrade=20of=20a=20g?= =?UTF-8?q?ift=20that=20was=20purchased=20after=20it=20was=20sent.=20Added?= =?UTF-8?q?=20the=20value=20=E2=80=9Coffer=E2=80=9D=20as=20a=20possible=20?= =?UTF-8?q?value=20of=20UniqueGiftInfo.origin=20for=20messages=20about=20t?= =?UTF-8?q?he=20purchase=20of=20a=20gift=20through=20a=20purchase=20offer.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- telebot/types.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index e9da94514..8fe676368 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12438,8 +12438,9 @@ class UniqueGiftInfo(JsonDeserializable): :param gift: Information about the gift :type gift: :class:`UniqueGift` - :param origin: Origin of the gift. Currently, either “upgrade” for gifts upgraded from regular gifts, “transfer” for gifts transferred from other users or channels, - or “resale” for gifts bought from other users + :param origin: Origin of the gift. Currently, either “upgrade” for gifts upgraded from regular gifts, “transfer” for gifts + transferred from other users or channels, “resale” for gifts bought from other users, “gifted_upgrade” for upgrades purchased + after the gift was sent, or “offer” for gifts bought or sold through gift purchase offers :type origin: :obj:`str` :param last_resale_star_count: Optional. For gifts bought from other users, the price paid for the gift From 5ca90c87ffe2d090b6f9336f89e01660eed3ff05 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:21:17 +0500 Subject: [PATCH 10/27] Added the field gift_upgrade_sent to the class Message. --- telebot/types.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index 8fe676368..15d7d7ac0 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1147,6 +1147,9 @@ class Message(JsonDeserializable): :param unique_gift: Optional. Service message: a unique gift was sent or received :type unique_gift: :class:`telebot.types.UniqueGiftInfo` + :param gift_upgrade_sent: Optional. Service message: upgrade of a gift was purchased after the gift was sent + :type gift_upgrade_sent: :class:`telebot.types.GiftInfo` + :param connected_website: Optional. The domain name of the website on which the user has logged in. More about Telegram Login » :type connected_website: :obj:`str` @@ -1488,6 +1491,9 @@ def de_json(cls, json_string): if 'direct_message_price_changed' in obj: opts['direct_message_price_changed'] = DirectMessagePriceChanged.de_json(obj['direct_message_price_changed']) content_type = 'direct_message_price_changed' + if 'gift_upgrade_sent' in obj: + opts['gift_upgrade_sent'] = GiftInfo.de_json(obj['gift_upgrade_sent']) + content_type = 'gift_upgrade_sent' return cls(message_id, from_user, date, chat, content_type, opts, json_string) @@ -1614,6 +1620,7 @@ def __init__(self, message_id, from_user, date, chat, content_type, options, jso self.checklist_tasks_done: Optional[ChecklistTasksDone] = None self.checklist_tasks_added: Optional[List[ChecklistTasksAdded]] = None self.direct_message_price_changed: Optional[DirectMessagePriceChanged] = None + self.gift_upgrade_sent: Optional[GiftInfo] = None for key in options: setattr(self, key, options[key]) From 2f062da6394f69f0bb68f3f44a2d346d83de4d07 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:22:34 +0500 Subject: [PATCH 11/27] Added the field gift_id to the class UniqueGift. --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 15d7d7ac0..783cc2941 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11787,6 +11787,9 @@ class UniqueGift(JsonDeserializable): Telegram documentation: https://core.telegram.org/bots/api#uniquegift + :param gift_id: Identifier of the regular gift from which the gift was upgraded + :type gift_id: :obj:`str` + :param base_name: Human-readable name of the regular gift from which this unique gift was upgraded :type base_name: :obj:`str` @@ -11808,13 +11811,14 @@ class UniqueGift(JsonDeserializable): :return: Instance of the class :rtype: :class:`UniqueGift` """ - def __init__(self, base_name, name, number, model, symbol, backdrop, **kwargs): + def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, **kwargs): self.base_name: str = base_name self.name: str = name self.number: int = number self.model: UniqueGiftModel = model self.symbol: UniqueGiftSymbol = symbol self.backdrop: UniqueGiftBackdrop = backdrop + self.gift_id: str = gift_id @classmethod def de_json(cls, json_string): @@ -11823,6 +11827,7 @@ def de_json(cls, json_string): obj['model'] = UniqueGiftModel.de_json(obj['model']) obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) + obj['gift_id'] = obj['gift_id'] return cls(**obj) From 98245c3bfd4f762ceab9226f1ad19e80e441ebf8 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:24:13 +0500 Subject: [PATCH 12/27] Added the field is_from_blockchain to the class UniqueGift. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 783cc2941..44e6dd725 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11808,10 +11808,13 @@ class UniqueGift(JsonDeserializable): :param backdrop: Backdrop of the gift :type backdrop: :class:`UniqueGiftBackdrop` + :param is_from_blockchain: Optional. True, if the gift is assigned from the TON blockchain and can't be resold or transferred in Telegram + :type is_from_blockchain: :obj:`bool` + :return: Instance of the class :rtype: :class:`UniqueGift` """ - def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, **kwargs): + def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is_from_blockchain=None, **kwargs): self.base_name: str = base_name self.name: str = name self.number: int = number @@ -11819,6 +11822,7 @@ def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, ** self.symbol: UniqueGiftSymbol = symbol self.backdrop: UniqueGiftBackdrop = backdrop self.gift_id: str = gift_id + self.is_from_blockchain: Optional[bool] = is_from_blockchain @classmethod def de_json(cls, json_string): @@ -11828,6 +11832,8 @@ def de_json(cls, json_string): obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) obj['gift_id'] = obj['gift_id'] + if 'is_from_blockchain' in obj: + obj['is_from_blockchain'] = obj['is_from_blockchain'] return cls(**obj) From 1702b7ca837a70f27b26fb4be0aa66027e4d21ba Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:27:44 +0500 Subject: [PATCH 13/27] Added the parameter exclude_from_blockchain in the method getBusinessAccountGifts, to filter out gifts that were assigned from the TON blockchain. --- telebot/__init__.py | 9 +++++++-- telebot/apihelper.py | 4 +++- telebot/async_telebot.py | 7 ++++++- telebot/asyncio_helper.py | 4 +++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 87f5439df..a38235b23 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6670,7 +6670,8 @@ def get_business_account_gifts( offset: Optional[str]=None, limit: Optional[int]=None, exclude_limited_upgradable: Optional[bool]=None, - exclude_limited_non_upgradable: Optional[bool]=None) -> types.OwnedGifts: + exclude_limited_non_upgradable: Optional[bool]=None, + exclude_from_blockchain: Optional[bool]=None,) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. @@ -6694,6 +6695,9 @@ def get_business_account_gifts( :param exclude_unique: Pass True to exclude unique gifts :type exclude_unique: :obj:`bool` + :param exclude_from_blockchain: Pass True to exclude gifts that were assigned from the TON blockchain and can't be resold or transferred in Telegram + :type exclude_from_blockchain: :obj:`bool` + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. :type sort_by_price: :obj:`bool` @@ -6728,7 +6732,8 @@ def get_business_account_gifts( offset=offset, limit=limit, exclude_limited_upgradable=exclude_limited_upgradable, - exclude_limited_non_upgradable=exclude_limited_non_upgradable + exclude_limited_non_upgradable=exclude_limited_non_upgradable, + exclude_from_blockchain=exclude_from_blockchain ) ) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index d951c84fc..aacb3ea05 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2105,7 +2105,7 @@ def transfer_business_account_stars(token, business_connection_id, star_count): def get_business_account_gifts(token, business_connection_id, exclude_unsaved=None, exclude_saved=None, exclude_unlimited=None, exclude_unique=None, sort_by_price=None, offset=None, limit=None, exclude_limited_upgradable=None, - exclude_limited_non_upgradable=None): + exclude_limited_non_upgradable=None, exclude_from_blockchain=None): method_url = 'getBusinessAccountGifts' payload = {'business_connection_id': business_connection_id} if exclude_unsaved is not None: @@ -2126,6 +2126,8 @@ def get_business_account_gifts(token, business_connection_id, exclude_unsaved=No payload['exclude_limited_upgradable'] = exclude_limited_upgradable if exclude_limited_non_upgradable is not None: payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain return _make_request(token, method_url, params=payload) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index d802aa57e..a2879fc5a 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8122,6 +8122,7 @@ async def get_business_account_gifts( limit: Optional[int]=None, exclude_limited_upgradable: Optional[bool]=None, exclude_limited_non_upgradable: Optional[bool]=None, + exclude_from_blockchain: Optional[bool]=None ) -> types.OwnedGifts: """ Returns the gifts received and owned by a managed business account. Requires the can_view_gifts_and_stars business bot right. Returns OwnedGifts on success. @@ -8146,6 +8147,9 @@ async def get_business_account_gifts( :param exclude_unique: Pass True to exclude unique gifts :type exclude_unique: :obj:`bool` + :param exclude_from_blockchain: Pass True to exclude gifts that were assigned from the TON blockchain and can't be resold or transferred in Telegram + :type exclude_from_blockchain: :obj:`bool` + :param sort_by_price: Pass True to sort results by gift price instead of send date. Sorting is applied before pagination. :type sort_by_price: :obj:`bool` @@ -8179,7 +8183,8 @@ async def get_business_account_gifts( offset=offset, limit=limit, exclude_limited_upgradable=exclude_limited_upgradable, - exclude_limited_non_upgradable=exclude_limited_non_upgradable + exclude_limited_non_upgradable=exclude_limited_non_upgradable, + exclude_from_blockchain=exclude_from_blockchain ) ) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 045bfd85f..c306fdd3f 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2072,7 +2072,7 @@ async def transfer_business_account_stars(token, business_connection_id, star_co async def get_business_account_gifts(token, business_connection_id, exclude_unsaved=None, exclude_saved=None, exclude_unlimited=None, exclude_unique=None, sort_by_price=None, offset=None, limit=None, exclude_limited_upgradable=None, - exclude_limited_non_upgradable=None): + exclude_limited_non_upgradable=None, exclude_from_blockchain=None): method_url = 'getBusinessAccountGifts' payload = {'business_connection_id': business_connection_id} if exclude_unsaved is not None: @@ -2093,6 +2093,8 @@ async def get_business_account_gifts(token, business_connection_id, exclude_unsa payload['exclude_limited_upgradable'] = exclude_limited_upgradable if exclude_limited_non_upgradable is not None: payload['exclude_limited_non_upgradable'] = exclude_limited_non_upgradable + if exclude_from_blockchain is not None: + payload['exclude_from_blockchain'] = exclude_from_blockchain return await _process_request(token, method_url, params=payload) async def get_user_gifts(token, user_id, exclude_unlimited=None, exclude_limited_upgradable=None, From e87e7cc211712f2d783cc972ed1a9d4830a27874 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:29:14 +0500 Subject: [PATCH 14/27] Added the fields personal_total_count and personal_remaining_count to the class Gift. --- telebot/types.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 44e6dd725..162839544 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11279,23 +11279,37 @@ class Gift(JsonDeserializable): :param remaining_count: Optional. The number of remaining gifts of this type that can be sent; for limited gifts only :type remaining_count: :obj:`int` + :param personal_total_count: Optional. The total number of gifts of this type that can be sent by the bot; for limited gifts only + :type personal_total_count: :obj:`int` + + :param personal_remaining_count: Optional. The number of remaining gifts of this type that can be sent by the bot; for limited gifts only + :type personal_remaining_count: :obj:`int` + :return: Instance of the class :rtype: :class:`Gift` """ - def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, **kwargs): + def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, + personal_total_count=None, personal_remaining_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 + self.personal_total_count: Optional[int] = personal_total_count + self.personal_remaining_count: Optional[int] = personal_remaining_count @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) + if 'personal_total_count' in obj: + obj['personal_total_count'] = obj['personal_total_count'] + if 'personal_remaining_count' in obj: + obj['personal_remaining_count'] = obj['personal_remaining_count'] + return cls(**obj) From 19cea9dc3c8db5ebcb8b7f8d2781a53d990f9069 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:33:27 +0500 Subject: [PATCH 15/27] Added the field is_premium to the classes Gift and UniqueGift. --- telebot/types.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 162839544..f7b5bab8d 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11273,6 +11273,9 @@ class Gift(JsonDeserializable): :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 is_premium: Optional. True, if the gift can only be purchased by Telegram Premium subscribers + :type is_premium: :obj:`bool` + :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` @@ -11290,7 +11293,7 @@ class Gift(JsonDeserializable): """ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, - personal_total_count=None, personal_remaining_count=None, **kwargs): + personal_total_count=None, personal_remaining_count=None, is_premium=None, **kwargs): self.id: str = id self.sticker: Sticker = sticker self.star_count: int = star_count @@ -11299,6 +11302,7 @@ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=No self.upgrade_star_count: Optional[int] = upgrade_star_count self.personal_total_count: Optional[int] = personal_total_count self.personal_remaining_count: Optional[int] = personal_remaining_count + self.is_premium: Optional[bool] = is_premium @classmethod def de_json(cls, json_string): @@ -11309,7 +11313,9 @@ def de_json(cls, json_string): obj['personal_total_count'] = obj['personal_total_count'] if 'personal_remaining_count' in obj: obj['personal_remaining_count'] = obj['personal_remaining_count'] - + if 'is_premium' in obj: + obj['is_premium'] = obj['is_premium'] + return cls(**obj) @@ -11825,10 +11831,13 @@ class UniqueGift(JsonDeserializable): :param is_from_blockchain: Optional. True, if the gift is assigned from the TON blockchain and can't be resold or transferred in Telegram :type is_from_blockchain: :obj:`bool` + :param is_premium: Optional. True, if the gift can only be purchased by Telegram Premium subscribers + :type is_premium: :obj:`bool` + :return: Instance of the class :rtype: :class:`UniqueGift` """ - def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is_from_blockchain=None, **kwargs): + def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is_from_blockchain=None, is_premium=None, **kwargs): self.base_name: str = base_name self.name: str = name self.number: int = number @@ -11837,6 +11846,7 @@ def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is self.backdrop: UniqueGiftBackdrop = backdrop self.gift_id: str = gift_id self.is_from_blockchain: Optional[bool] = is_from_blockchain + self.is_premium: Optional[bool] = is_premium @classmethod def de_json(cls, json_string): @@ -11848,6 +11858,8 @@ def de_json(cls, json_string): obj['gift_id'] = obj['gift_id'] if 'is_from_blockchain' in obj: obj['is_from_blockchain'] = obj['is_from_blockchain'] + if 'is_premium' in obj: + obj['is_premium'] = obj['is_premium'] return cls(**obj) From d1f28620084228c213d6db7ee6b3ea57d8ff390d Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:37:49 +0500 Subject: [PATCH 16/27] Fix --- telebot/types.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index f7b5bab8d..ad8e4ccec 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11855,11 +11855,6 @@ def de_json(cls, json_string): obj['model'] = UniqueGiftModel.de_json(obj['model']) obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) - obj['gift_id'] = obj['gift_id'] - if 'is_from_blockchain' in obj: - obj['is_from_blockchain'] = obj['is_from_blockchain'] - if 'is_premium' in obj: - obj['is_premium'] = obj['is_premium'] return cls(**obj) From 4ea5ddc0f7a12c7fbf297985ae23e0337339a4c0 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:41:33 +0500 Subject: [PATCH 17/27] Added the field is_upgrade_separate to the classes GiftInfo and OwnedGiftRegular. --- telebot/types.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index ad8e4ccec..b9d817896 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11678,12 +11678,18 @@ class OwnedGiftRegular(OwnedGift): :param prepaid_upgrade_star_count: Optional. Number of Telegram Stars that were paid by the sender for the ability to upgrade the gift :type prepaid_upgrade_star_count: :obj:`int` + :param is_upgrade_separate: Optional. True, if the gift's upgrade was purchased after the gift was sent; for gifts received on behalf of business accounts only + :type is_upgrade_separate: :obj:`bool` + + :param unique_gift_number: Optional. Unique number reserved for this gift when upgraded. See the number field in UniqueGift + :type unique_gift_number: :obj:`int` + :return: Instance of the class :rtype: :class:`OwnedGiftRegular` """ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=None, text=None, entities=None, is_private=None, is_saved=None, can_be_upgraded=None, was_refunded=None, convert_star_count=None, - prepaid_upgrade_star_count=None, **kwargs): + prepaid_upgrade_star_count=None, is_upgrade_separate=None, unique_gift_number=None, **kwargs): super().__init__(type=type) self.gift: Gift = gift self.owned_gift_id: Optional[str] = owned_gift_id @@ -11697,6 +11703,8 @@ def __init__(self, type, gift, owned_gift_id=None, sender_user=None, send_date=N self.was_refunded: Optional[bool] = was_refunded self.convert_star_count: Optional[int] = convert_star_count self.prepaid_upgrade_star_count: Optional[int] = prepaid_upgrade_star_count + self.is_upgrade_separate: Optional[bool] = is_upgrade_separate + self.unique_gift_number: Optional[int] = unique_gift_number @classmethod def de_json(cls, json_string): @@ -12430,6 +12438,9 @@ class GiftInfo(JsonDeserializable): :param prepaid_upgrade_star_count: Optional. Number of Telegram Stars that were prepaid by the sender for the ability to upgrade the gift :type prepaid_upgrade_star_count: :obj:`int` + :param is_upgrade_separate: Optional. True, if the gift's upgrade was purchased after the gift was sent + :type is_upgrade_separate: :obj:`bool` + :param can_be_upgraded: Optional. True, if the gift can be upgraded to a unique gift :type can_be_upgraded: :obj:`bool` @@ -12448,7 +12459,7 @@ class GiftInfo(JsonDeserializable): def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star_count: Optional[int] = None, prepaid_upgrade_star_count: Optional[int] = None, can_be_upgraded: Optional[bool] = None, text: Optional[str] = None, entities: Optional[List[MessageEntity]] = None, - is_private: Optional[bool] = None, **kwargs): + is_private: Optional[bool] = None, is_upgrade_separate: Optional[bool] = None, **kwargs): self.gift: Gift = gift self.owned_gift_id: Optional[str] = owned_gift_id self.convert_star_count: Optional[int] = convert_star_count @@ -12457,6 +12468,7 @@ def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star self.text: Optional[str] = text self.entities: Optional[List[MessageEntity]] = entities self.is_private: Optional[bool] = is_private + self.is_upgrade_separate: Optional[bool] = is_upgrade_separate @classmethod def de_json(cls, json_string): From ba12d90e905f1dd4d2389a3b8d47e69ebae03eee Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:43:03 +0500 Subject: [PATCH 18/27] Added the class UniqueGiftColors that describes the color scheme for a user's name, replies to messages and link previews based on a unique gift. --- telebot/types.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/telebot/types.py b/telebot/types.py index b9d817896..43c68d842 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -12927,3 +12927,49 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) + + +class UniqueGiftColors(JsonDeserializable): + """ + This object contains information about the color scheme for a user's name, message replies and link previews + based on a unique gift. + + Telegram documentation: https://core.telegram.org/bots/api#uniquegiftcolors + + :param model_custom_emoji_id: Custom emoji identifier of the unique gift's model + :type model_custom_emoji_id: :obj:`str` + + :param symbol_custom_emoji_id: Custom emoji identifier of the unique gift's symbol + :type symbol_custom_emoji_id: :obj:`str` + + :param light_theme_main_color: Main color used in light themes; RGB format + :type light_theme_main_color: :obj:`int` + + :param light_theme_other_colors: List of 1-3 additional colors used in light themes; RGB format + :type light_theme_other_colors: :obj:`list` of :obj:`int` + + :param dark_theme_main_color: Main color used in dark themes; RGB format + :type dark_theme_main_color: :obj:`int` + + :param dark_theme_other_colors: List of 1-3 additional colors used in dark themes; RGB format + :type dark_theme_other_colors: :obj:`list` of :obj:`int` + + :return: Instance of the class + :rtype: :class:`UniqueGiftColors` + """ + def __init__(self, model_custom_emoji_id: str, symbol_custom_emoji_id: str, + light_theme_main_color: int, light_theme_other_colors: List[int], + dark_theme_main_color: int, dark_theme_other_colors: List[int], **kwargs): + self.model_custom_emoji_id: str = model_custom_emoji_id + self.symbol_custom_emoji_id: str = symbol_custom_emoji_id + self.light_theme_main_color: int = light_theme_main_color + self.light_theme_other_colors: List[int] = light_theme_other_colors + self.dark_theme_main_color: int = dark_theme_main_color + self.dark_theme_other_colors: List[int] = dark_theme_other_colors + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + From 951f24c7f0aaaea309306cb30317162441c3f41e Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:44:19 +0500 Subject: [PATCH 19/27] Added the field has_colors to the class Gift. --- telebot/types.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 43c68d842..4184b2e35 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11276,6 +11276,9 @@ class Gift(JsonDeserializable): :param is_premium: Optional. True, if the gift can only be purchased by Telegram Premium subscribers :type is_premium: :obj:`bool` + :param has_colors: Optional. True, if the gift can be used (after being upgraded) to customize a user's appearance + :type has_colors: :obj:`bool` + :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` @@ -11293,7 +11296,7 @@ class Gift(JsonDeserializable): """ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, - personal_total_count=None, personal_remaining_count=None, is_premium=None, **kwargs): + personal_total_count=None, personal_remaining_count=None, is_premium=None, has_colors=None, **kwargs): self.id: str = id self.sticker: Sticker = sticker self.star_count: int = star_count @@ -11303,18 +11306,13 @@ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=No self.personal_total_count: Optional[int] = personal_total_count self.personal_remaining_count: Optional[int] = personal_remaining_count self.is_premium: Optional[bool] = is_premium + self.has_colors: Optional[bool] = has_colors @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) - if 'personal_total_count' in obj: - obj['personal_total_count'] = obj['personal_total_count'] - if 'personal_remaining_count' in obj: - obj['personal_remaining_count'] = obj['personal_remaining_count'] - if 'is_premium' in obj: - obj['is_premium'] = obj['is_premium'] return cls(**obj) From b6df64cebec7824283c7f56ef15aa7e8e232a68a Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:45:29 +0500 Subject: [PATCH 20/27] Added the field colors to the class UniqueGift. --- telebot/types.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 4184b2e35..320c9a5aa 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11840,10 +11840,13 @@ class UniqueGift(JsonDeserializable): :param is_premium: Optional. True, if the gift can only be purchased by Telegram Premium subscribers :type is_premium: :obj:`bool` + :param colors: Optional. The color scheme that can be used by the gift's owner for the chat's name, replies to messages and link previews; for business account gifts and gifts that are currently on sale only + :type colors: :class:`UniqueGiftColors` + :return: Instance of the class :rtype: :class:`UniqueGift` """ - def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is_from_blockchain=None, is_premium=None, **kwargs): + def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is_from_blockchain=None, is_premium=None, colors=None, **kwargs): self.base_name: str = base_name self.name: str = name self.number: int = number @@ -11853,6 +11856,7 @@ def __init__(self, base_name, name, number, model, symbol, backdrop, gift_id, is self.gift_id: str = gift_id self.is_from_blockchain: Optional[bool] = is_from_blockchain self.is_premium: Optional[bool] = is_premium + self.colors: Optional[UniqueGiftColors] = colors @classmethod def de_json(cls, json_string): @@ -11861,6 +11865,8 @@ def de_json(cls, json_string): obj['model'] = UniqueGiftModel.de_json(obj['model']) obj['symbol'] = UniqueGiftSymbol.de_json(obj['symbol']) obj['backdrop'] = UniqueGiftBackdrop.de_json(obj['backdrop']) + if 'colors' in obj: + obj['colors'] = UniqueGiftColors.de_json(obj['colors']) return cls(**obj) From a845b7655be2436f6059a1c9992e0f55e9153633 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 17:47:34 +0500 Subject: [PATCH 21/27] Added the class GiftBackground and the field background to the class Gift. --- telebot/types.py | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 320c9a5aa..811b7a96e 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11291,12 +11291,16 @@ class Gift(JsonDeserializable): :param personal_remaining_count: Optional. The number of remaining gifts of this type that can be sent by the bot; for limited gifts only :type personal_remaining_count: :obj:`int` + :param background: Optional. Background of the gift + :type background: :class:`GiftBackground` + :return: Instance of the class :rtype: :class:`Gift` """ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, - personal_total_count=None, personal_remaining_count=None, is_premium=None, has_colors=None, **kwargs): + personal_total_count=None, personal_remaining_count=None, is_premium=None, has_colors=None, + background=None, **kwargs): self.id: str = id self.sticker: Sticker = sticker self.star_count: int = star_count @@ -11307,12 +11311,15 @@ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=No self.personal_remaining_count: Optional[int] = personal_remaining_count self.is_premium: Optional[bool] = is_premium self.has_colors: Optional[bool] = has_colors + self.background: Optional[GiftBackground] = background @classmethod def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) obj['sticker'] = Sticker.de_json(obj['sticker']) + if 'background' in obj: + obj['background'] = GiftBackground.de_json(obj['background']) return cls(**obj) @@ -12977,3 +12984,33 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) + +class GiftBackground(JsonDeserializable): + """ + This object describes the background of a gift. + + Telegram documentation: https://core.telegram.org/bots/api#giftbackground + + :param center_color: Center color of the background in RGB format + :type center_color: :obj:`int` + + :param edge_color: Edge color of the background in RGB format + :type edge_color: :obj:`int` + + :param text_color: Text color of the background in RGB format + :type text_color: :obj:`int` + + :return: Instance of the class + :rtype: :class:`GiftBackground` + """ + def __init__(self, center_color: int, edge_color: int, text_color: int, **kwargs): + self.center_color: int = center_color + self.edge_color: int = edge_color + self.text_color: int = text_color + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + \ No newline at end of file From 8d614486e1c5d4cd5f0d376459a614c26ff59a42 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 19:24:47 +0500 Subject: [PATCH 22/27] Added the field unique_gift_variant_count to the class Gift. Added the field unique_gift_number to the classes GiftInfo and OwnedGiftRegular. --- telebot/types.py | 47 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 901165abd..015922838 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11382,6 +11382,10 @@ class Gift(JsonDeserializable): :param background: Optional. Background of the gift :type background: :class:`GiftBackground` + + :param unique_gift_variant_count: Optional. The total number of different unique gifts that can be obtained by upgrading the gift + :type unique_gift_variant_count: :obj:`int` + :param publisher_chat: Optional. Information about the chat that published the gift :type publisher_chat: :class:`Chat` @@ -11391,7 +11395,7 @@ class Gift(JsonDeserializable): def __init__(self, id, sticker, star_count, total_count=None, remaining_count=None, upgrade_star_count=None, personal_total_count=None, personal_remaining_count=None, is_premium=None, has_colors=None, - background=None, publisher_chat=None, **kwargs): + background=None, publisher_chat=None, unique_gift_variant_count=None, **kwargs): self.id: str = id self.sticker: Sticker = sticker self.star_count: int = star_count @@ -11404,6 +11408,7 @@ def __init__(self, id, sticker, star_count, total_count=None, remaining_count=No self.has_colors: Optional[bool] = has_colors self.background: Optional[GiftBackground] = background self.publisher_chat: Optional[Chat] = publisher_chat + self.unique_gift_variant_count: Optional[int] = unique_gift_variant_count @classmethod def de_json(cls, json_string): @@ -11649,26 +11654,35 @@ class AcceptedGiftTypes(JsonDeserializable, JsonSerializable): :param premium_subscription: True, if a Telegram Premium subscription is accepted :type premium_subscription: :obj:`bool` + :param gifts_from_channels: True, if transfers of unique gifts from channels are accepted + :type gifts_from_channels: :obj:`bool` + :return: Instance of the class :rtype: :class:`AcceptedGiftTypes` """ - def __init__(self, unlimited_gifts: bool, limited_gifts: bool, - unique_gifts: bool, premium_subscription: bool, **kwargs): - self.unlimited_gifts: bool = unlimited_gifts - self.limited_gifts: bool = limited_gifts - self.unique_gifts: bool = unique_gifts - self.premium_subscription: bool = premium_subscription + def __init__(self, unlimited_gifts: Optional[bool]=None, limited_gifts: Optional[bool]=None, + unique_gifts: Optional[bool]=None, premium_subscription: Optional[bool]=None, gifts_from_channels: Optional[bool]=None, **kwargs): + self.unlimited_gifts: Optional[bool] = unlimited_gifts + self.limited_gifts: Optional[bool] = limited_gifts + self.unique_gifts: Optional[bool] = unique_gifts + self.premium_subscription: Optional[bool] = premium_subscription + self.gifts_from_channels: Optional[bool] = gifts_from_channels def to_json(self): return json.dumps(self.to_dict()) def to_dict(self): - data = { - 'unlimited_gifts': self.unlimited_gifts, - 'limited_gifts': self.limited_gifts, - 'unique_gifts': self.unique_gifts, - 'premium_subscription': self.premium_subscription - } + data = {} + if self.unlimited_gifts is not None: + data['unlimited_gifts'] = self.unlimited_gifts + if self.limited_gifts is not None: + data['limited_gifts'] = self.limited_gifts + if self.unique_gifts is not None: + data['unique_gifts'] = self.unique_gifts + if self.premium_subscription is not None: + data['premium_subscription'] = self.premium_subscription + if self.gifts_from_channels is not None: + data['gifts_from_channels'] = self.gifts_from_channels return data @classmethod @@ -12562,13 +12576,17 @@ class GiftInfo(JsonDeserializable): :param is_private: Optional. True, if the sender and gift text are shown only to the gift receiver; otherwise, everyone will be able to see them :type is_private: :obj:`bool` + :param unique_gift_number: Optional. Unique number reserved for this gift when upgraded. See the number field in UniqueGift + :type unique_gift_number: :obj:`int` + :return: Instance of the class :rtype: :class:`GiftInfo` """ def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star_count: Optional[int] = None, prepaid_upgrade_star_count: Optional[int] = None, can_be_upgraded: Optional[bool] = None, text: Optional[str] = None, entities: Optional[List[MessageEntity]] = None, - is_private: Optional[bool] = None, is_upgrade_separate: Optional[bool] = None, **kwargs): + is_private: Optional[bool] = None, is_upgrade_separate: Optional[bool] = None, + unique_gift_number: Optional[int] = None, **kwargs): self.gift: Gift = gift self.owned_gift_id: Optional[str] = owned_gift_id self.convert_star_count: Optional[int] = convert_star_count @@ -12578,6 +12596,7 @@ def __init__(self, gift: Gift, owned_gift_id: Optional[str] = None, convert_star self.entities: Optional[List[MessageEntity]] = entities self.is_private: Optional[bool] = is_private self.is_upgrade_separate: Optional[bool] = is_upgrade_separate + self.unique_gift_number: Optional[int] = unique_gift_number @classmethod def de_json(cls, json_string): From 913ff9573c309cb955abfdf4e679c73ba613a8d8 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 19:28:27 +0500 Subject: [PATCH 23/27] Added the method repostStory, allowing bots to repost stories across different business accounts they manage. --- telebot/__init__.py | 44 +++++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 14 +++++++++++++ telebot/async_telebot.py | 44 +++++++++++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 12 +++++++++++ 4 files changed, 114 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index ec29bc915..6478fada8 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -7283,6 +7283,50 @@ def post_story( ) ) + def repost_story( + self, business_connection_id: str, + from_chat_id: int, from_story_id: int, + active_period: int, + post_to_chat_page: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Story: + """ + Reposts a story on behalf of a business account from another business account. Both business accounts + must be managed by the same bot, and the story on the source account must have been posted (or reposted) + by the bot. Requires the can_manage_stories business bot right for both business accounts. Returns Story on success. + + Telegram documentation: https://core.telegram.org/bots/api#repoststory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param from_chat_id: Unique identifier of the chat which posted the story that should be reposted + :type from_chat_id: :obj:`int` + + :param from_story_id: Unique identifier of the story that should be reposted + :type from_story_id: :obj:`int` + + :param active_period: Period after which the story is moved to the archive, in seconds; must be one of 6 * 3600, 12 * 3600, 86400, or 2 * 86400 + :type active_period: :obj:`int` + + :param post_to_chat_page: Pass True to keep the story accessible after it expires + :type post_to_chat_page: :obj:`bool` + + :param protect_content: Pass True if the content of the story must be protected from forwarding and screenshotting + :type protect_content: :obj:`bool` + + :return: On success, a Story object is returned. + :rtype: :class:`telebot.types.Story` + """ + return types.Story.de_json( + apihelper.repost_story( + self.token, business_connection_id, + from_chat_id, from_story_id, + active_period, + post_to_chat_page=post_to_chat_page, + protect_content=protect_content + ) + ) + def edit_story( self, business_connection_id: str, story_id: int, content: types.InputStoryContent, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index b4f05aedc..bed001c49 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -2314,6 +2314,20 @@ def post_story(token, business_connection_id, content, active_period, caption=No payload['protect_content'] = protect_content return _make_request(token, method_url, params=payload, files=files, method='post') + +def repost_story(token, business_connection_id, from_chat_id, from_story_id, active_period, + post_to_chat_page=None, protect_content=None): + method_url = 'repostStory' + payload = {'business_connection_id': business_connection_id, 'from_chat_id': from_chat_id, + 'from_story_id': from_story_id, 'active_period': active_period} + + if post_to_chat_page is not None: + payload['post_to_chat_page'] = post_to_chat_page + if protect_content is not None: + payload['protect_content'] = protect_content + return _make_request(token, method_url, params=payload, method='post') + + def edit_story(token, business_connection_id, story_id, content, caption=None, parse_mode=None, caption_entities=None, areas=None): method_url = 'editStory' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index fee4e0a13..273188310 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -8742,6 +8742,50 @@ async def post_story( ) ) + async def repost_story( + self, business_connection_id: str, + from_chat_id: int, from_story_id: int, + active_period: int, + post_to_chat_page: Optional[bool]=None, + protect_content: Optional[bool]=None) -> types.Story: + """ + Reposts a story on behalf of a business account from another business account. Both business accounts + must be managed by the same bot, and the story on the source account must have been posted (or reposted) + by the bot. Requires the can_manage_stories business bot right for both business accounts. Returns Story on success. + + Telegram documentation: https://core.telegram.org/bots/api#repoststory + + :param business_connection_id: Unique identifier of the business connection + :type business_connection_id: :obj:`str` + + :param from_chat_id: Unique identifier of the chat which posted the story that should be reposted + :type from_chat_id: :obj:`int` + + :param from_story_id: Unique identifier of the story that should be reposted + :type from_story_id: :obj:`int` + + :param active_period: Period after which the story is moved to the archive, in seconds; must be one of 6 * 3600, 12 * 3600, 86400, or 2 * 86400 + :type active_period: :obj:`int` + + :param post_to_chat_page: Pass True to keep the story accessible after it expires + :type post_to_chat_page: :obj:`bool` + + :param protect_content: Pass True if the content of the story must be protected from forwarding and screenshotting + :type protect_content: :obj:`bool` + + :return: On success, a Story object is returned. + :rtype: :class:`telebot.types.Story` + """ + return types.Story.de_json( + await asyncio_helper.repost_story( + self.token, business_connection_id, + from_chat_id, from_story_id, + active_period, + post_to_chat_page=post_to_chat_page, + protect_content=protect_content + ) + ) + async def edit_story( self, business_connection_id: str, story_id: int, content: types.InputStoryContent, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 9e99fc3ff..dbd5351a5 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -2282,6 +2282,18 @@ async def post_story(token, business_connection_id, content, active_period, capt payload['protect_content'] = protect_content return await _process_request(token, method_url, params=payload, files=files, method='post') +async def repost_story(token, business_connection_id, from_chat_id, from_story_id, active_period, + post_to_chat_page=None, protect_content=None): + method_url = 'repostStory' + payload = {'business_connection_id': business_connection_id, 'from_chat_id': from_chat_id, + 'from_story_id': from_story_id, 'active_period': active_period} + + if post_to_chat_page is not None: + payload['post_to_chat_page'] = post_to_chat_page + if protect_content is not None: + payload['protect_content'] = protect_content + return await _process_request(token, method_url, params=payload, method='post') + async def edit_story(token, business_connection_id, story_id, content, caption=None, parse_mode=None, caption_entities=None, areas=None): method_url = 'editStory' From 9688d335f41833c6fa4b06cfed0f7c1611ab7a90 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 19:32:59 +0500 Subject: [PATCH 24/27] Added the class UserRating and the field rating to the class ChatFullInfo. --- telebot/types.py | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 015922838..937f90d17 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -748,6 +748,9 @@ class ChatFullInfo(JsonDeserializable): :param location: Optional. For supergroups, the location to which the supergroup is connected. Returned only in getChat. :type location: :class:`telebot.types.ChatLocation` + :param rating: Optional. For private chats, the rating of the user if any + :type rating: :class:`telebot.types.UserRating` + :return: Instance of the class :rtype: :class:`telebot.types.ChatFullInfo` """ @@ -779,6 +782,8 @@ def de_json(cls, json_string): obj['accepted_gift_types'] = AcceptedGiftTypes.de_json(obj['accepted_gift_types']) if 'parent_chat' in obj: obj['parent_chat'] = Chat.de_json(obj['parent_chat']) + if 'rating' in obj: + obj['rating'] = UserRating.de_json(obj['rating']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -795,7 +800,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, business_opening_hours=None, personal_chat=None, birthdate=None, can_send_paid_media=None, - accepted_gift_types=None, is_direct_messages=None, parent_chat=None, **kwargs): + accepted_gift_types=None, is_direct_messages=None, parent_chat=None, rating=None, **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -843,6 +848,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.accepted_gift_types: AcceptedGiftTypes = accepted_gift_types self.is_direct_messages: Optional[bool] = is_direct_messages self.parent_chat: Optional[Chat] = parent_chat + self.rating: Optional[UserRating] = rating @property def can_send_gift(self) -> bool: """ @@ -13405,4 +13411,38 @@ def de_json(cls, json_string): return cls(**obj) +class UserRating(JsonDeserializable): + """ + This object describes the rating of a user based on their Telegram Star spendings. + + Telegram documentation: https://core.telegram.org/bots/api#userrating + + :param level: Current level of the user, indicating their reliability when purchasing digital goods and services. A higher level suggests a more trustworthy customer; a negative level is likely reason for concern. + :type level: :obj:`int` + + :param rating: Numerical value of the user's rating; the higher the rating, the better + :type rating: :obj:`int` + + :param current_level_rating: The rating value required to get the current level + :type current_level_rating: :obj:`int` + + :param next_level_rating: Optional. The rating value required to get to the next level; omitted if the maximum level was reached + :type next_level_rating: :obj:`int` + + :return: Instance of the class + :rtype: :class:`UserRating` + """ + def __init__(self, level: int, rating: int, current_level_rating: int, + next_level_rating: Optional[int] = None, **kwargs): + self.level: int = level + self.rating: int = rating + self.current_level_rating: int = current_level_rating + self.next_level_rating: Optional[int] = next_level_rating + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + \ No newline at end of file From 40d9c3b2ba71b0bda9feed77c4b4608f9a07791c Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 19:34:58 +0500 Subject: [PATCH 25/27] Added the field paid_message_star_count to the class ChatFullInfo. --- telebot/types.py | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index 937f90d17..e1b0d81dc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -751,6 +751,9 @@ class ChatFullInfo(JsonDeserializable): :param rating: Optional. For private chats, the rating of the user if any :type rating: :class:`telebot.types.UserRating` + :param paid_message_star_count: Optional. The number of Telegram Stars a general user have to pay to send a message to the chat + :type paid_message_star_count: :obj:`int` + :return: Instance of the class :rtype: :class:`telebot.types.ChatFullInfo` """ @@ -787,20 +790,21 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, - last_name=None, photo=None, bio=None, has_private_forwards=None, - description=None, invite_link=None, pinned_message=None, - permissions=None, slow_mode_delay=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, - join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None, - is_forum=None, max_reaction_count=None, active_usernames=None, emoji_status_custom_emoji_id=None, - has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, - available_reactions=None, accent_color_id=None, background_custom_emoji_id=None, profile_accent_color_id=None, - profile_background_custom_emoji_id=None, has_visible_history=None, - unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, - business_opening_hours=None, personal_chat=None, birthdate=None, - can_send_paid_media=None, - accepted_gift_types=None, is_direct_messages=None, parent_chat=None, rating=None, **kwargs): + last_name=None, photo=None, bio=None, has_private_forwards=None, + description=None, invite_link=None, pinned_message=None, + permissions=None, slow_mode_delay=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, + join_to_send_messages=None, join_by_request=None, has_restricted_voice_and_video_messages=None, + is_forum=None, max_reaction_count=None, active_usernames=None, emoji_status_custom_emoji_id=None, + has_hidden_members=None, has_aggressive_anti_spam_enabled=None, emoji_status_expiration_date=None, + available_reactions=None, accent_color_id=None, background_custom_emoji_id=None, profile_accent_color_id=None, + profile_background_custom_emoji_id=None, has_visible_history=None, + unrestrict_boost_count=None, custom_emoji_sticker_set_name=None, business_intro=None, business_location=None, + business_opening_hours=None, personal_chat=None, birthdate=None, + can_send_paid_media=None, + accepted_gift_types=None, is_direct_messages=None, parent_chat=None, rating=None, paid_message_star_count=None, + **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -849,6 +853,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.is_direct_messages: Optional[bool] = is_direct_messages self.parent_chat: Optional[Chat] = parent_chat self.rating: Optional[UserRating] = rating + self.paid_message_star_count: Optional[int] = paid_message_star_count @property def can_send_gift(self) -> bool: """ From 6e6190bce45a8365cf532480d1e6374a33532dc7 Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 19:36:47 +0500 Subject: [PATCH 26/27] completed_by_chat & unique_gift_colors --- telebot/types.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e1b0d81dc..f2a845b25 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -754,6 +754,9 @@ class ChatFullInfo(JsonDeserializable): :param paid_message_star_count: Optional. The number of Telegram Stars a general user have to pay to send a message to the chat :type paid_message_star_count: :obj:`int` + :param unique_gift_colors: Optional. The color scheme based on a unique gift that must be used for the chat's name, message replies and link previews + :type unique_gift_colors: :class:`telebot.types.UniqueGiftColors` + :return: Instance of the class :rtype: :class:`telebot.types.ChatFullInfo` """ @@ -787,6 +790,8 @@ def de_json(cls, json_string): obj['parent_chat'] = Chat.de_json(obj['parent_chat']) if 'rating' in obj: obj['rating'] = UserRating.de_json(obj['rating']) + if 'unique_gift_colors' in obj: + obj['unique_gift_colors'] = UniqueGiftColors.de_json(obj['unique_gift_colors']) return cls(**obj) def __init__(self, id, type, title=None, username=None, first_name=None, @@ -804,7 +809,7 @@ def __init__(self, id, type, title=None, username=None, first_name=None, business_opening_hours=None, personal_chat=None, birthdate=None, can_send_paid_media=None, accepted_gift_types=None, is_direct_messages=None, parent_chat=None, rating=None, paid_message_star_count=None, - **kwargs): + unique_gift_colors=None, **kwargs): self.id: int = id self.type: str = type self.title: Optional[str] = title @@ -854,6 +859,9 @@ def __init__(self, id, type, title=None, username=None, first_name=None, self.parent_chat: Optional[Chat] = parent_chat self.rating: Optional[UserRating] = rating self.paid_message_star_count: Optional[int] = paid_message_star_count + self.unique_gift_colors: Optional[UniqueGiftColors] = unique_gift_colors + + @property def can_send_gift(self) -> bool: """ @@ -12808,6 +12816,9 @@ class ChecklistTask(JsonDeserializable): :param completed_by_user: Optional. User that completed the task; omitted if the task wasn't completed :type completed_by_user: :class:`User` + :param completed_by_chat: Optional. Chat that completed the task; omitted if the task wasn't completed by a chat + :type completed_by_chat: :class:`Chat` + :param completion_date: Optional. Point in time (Unix timestamp) when the task was completed; 0 if the task wasn't completed :type completion_date: :obj:`int` @@ -12816,11 +12827,13 @@ class ChecklistTask(JsonDeserializable): """ def __init__(self, id: int, text: str, text_entities: Optional[List[MessageEntity]] = None, completed_by_user: Optional[User] = None, + completed_by_chat: Optional[Chat] = None, completion_date: Optional[int] = None, **kwargs): self.id: int = id self.text: str = text self.text_entities: Optional[List[MessageEntity]] = text_entities self.completed_by_user: Optional[User] = completed_by_user + self.completed_by_chat: Optional[Chat] = completed_by_chat self.completion_date: Optional[int] = completion_date @classmethod @@ -12831,6 +12844,8 @@ def de_json(cls, json_string): obj['text_entities'] = Message.parse_entities(obj['text_entities']) if 'completed_by_user' in obj: obj['completed_by_user'] = User.de_json(obj['completed_by_user']) + if 'completed_by_chat' in obj: + obj['completed_by_chat'] = Chat.de_json(obj['completed_by_chat']) return cls(**obj) From 9c057fbb09a5e3a0415f9160e932d9bfedcb5b3c Mon Sep 17 00:00:00 2001 From: _run Date: Wed, 31 Dec 2025 19:41:09 +0500 Subject: [PATCH 27/27] Added the parameter message_effect_id to the methods forwardMessage and copyMessage. --- telebot/__init__.py | 16 ++++++++++++---- telebot/apihelper.py | 10 ++++++++-- telebot/async_telebot.py | 18 ++++++++++++++---- telebot/asyncio_helper.py | 9 +++++++-- 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 6478fada8..fb12ea686 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1827,7 +1827,8 @@ def forward_message( message_thread_id: Optional[int]=None, video_start_timestamp: Optional[int]=None, direct_messages_topic_id: Optional[int]=None, - suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -1866,6 +1867,9 @@ def forward_message( is automatically declined. :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :param message_effect_id: Unique identifier of the message effect to be added to the message; only available when forwarding to private chats + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -1877,7 +1881,7 @@ def forward_message( self.token, chat_id, from_chat_id, message_id, disable_notification=disable_notification, timeout=timeout, protect_content=protect_content, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id, - suggested_post_parameters=suggested_post_parameters + suggested_post_parameters=suggested_post_parameters, message_effect_id=message_effect_id ) ) @@ -1901,7 +1905,8 @@ def copy_message( allow_paid_broadcast: Optional[bool]=None, video_start_timestamp: Optional[int]=None, direct_messages_topic_id: Optional[int]=None, - suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.MessageID: + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None, + message_effect_id: Optional[str]=None) -> types.MessageID: """ Use this method to copy messages of any kind. Service messages, paid media messages, giveaway messages, giveaway winners messages, and invoice messages can't be copied. @@ -1973,6 +1978,9 @@ def copy_message( is automatically declined. :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :param message_effect_id: Unique identifier of the message effect to be added to the message; only available when copying to private chats + :type message_effect_id: :obj:`str` + :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` """ @@ -2006,7 +2014,7 @@ def copy_message( message_thread_id=message_thread_id, reply_parameters=reply_parameters, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp, direct_messages_topic_id=direct_messages_topic_id, - suggested_post_parameters=suggested_post_parameters + suggested_post_parameters=suggested_post_parameters, message_effect_id=message_effect_id )) diff --git a/telebot/apihelper.py b/telebot/apihelper.py index bed001c49..01137b7ca 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -432,7 +432,8 @@ def get_chat_member(token, chat_id, user_id): def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, - video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): + video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None, + message_effect_id=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -449,13 +450,16 @@ def forward_message( payload['direct_messages_topic_id'] = direct_messages_topic_id if suggested_post_parameters is not None: payload['suggested_post_parameters'] = suggested_post_parameters.to_json() + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, allow_paid_broadcast=None, - video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): + video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None, + message_effect_id=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -486,6 +490,8 @@ def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_m payload['direct_messages_topic_id'] = direct_messages_topic_id if suggested_post_parameters is not None: payload['suggested_post_parameters'] = suggested_post_parameters.to_json() + if message_effect_id: + payload['message_effect_id'] = message_effect_id return _make_request(token, method_url, params=payload) def send_checklist( diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 273188310..d8a70742b 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3355,7 +3355,8 @@ async def forward_message( message_thread_id: Optional[int]=None, video_start_timestamp: Optional[int]=None, direct_messages_topic_id: Optional[int]=None, - suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.Message: + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None, + message_effect_id: Optional[str]=None) -> types.Message: """ Use this method to forward messages of any kind. @@ -3394,6 +3395,9 @@ async def forward_message( is automatically declined. :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :param message_effect_id: Unique identifier of the message effect to be added to the message; only available when forwarding to private chats + :type message_effect_id: :obj:`str` + :return: On success, the sent Message is returned. :rtype: :class:`telebot.types.Message` """ @@ -3404,7 +3408,8 @@ async def forward_message( await asyncio_helper.forward_message(self.token, chat_id=chat_id, from_chat_id=from_chat_id, message_id=message_id, disable_notification=disable_notification, protect_content=protect_content, timeout=timeout, message_thread_id=message_thread_id, video_start_timestamp=video_start_timestamp, - direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters) + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters, + message_effect_id=message_effect_id) ) async def copy_message( @@ -3426,7 +3431,8 @@ async def copy_message( allow_paid_broadcast: Optional[bool]=None, video_start_timestamp: Optional[bool]=None, direct_messages_topic_id: Optional[int]=None, - suggested_post_parameters: Optional[types.SuggestedPostParameters]=None) -> types.MessageID: + suggested_post_parameters: Optional[types.SuggestedPostParameters]=None, + message_effect_id: Optional[str]=None) -> types.MessageID: """ Use this method to copy messages of any kind. If some of the specified messages can't be found or copied, they are skipped. Service messages, paid media messages, giveaway messages, giveaway winners messages, @@ -3499,6 +3505,9 @@ async def copy_message( is automatically declined. :type suggested_post_parameters: :class:`telebot.types.SuggestedPostParameters` + :param message_effect_id: Unique identifier of the message effect to be added to the message; only available when forwarding to private chats + :type message_effect_id: :obj:`str` + :return: On success, the MessageId of the sent message is returned. :rtype: :class:`telebot.types.MessageID` """ @@ -3533,7 +3542,8 @@ async def copy_message( reply_parameters=reply_parameters, reply_markup=reply_markup, timeout=timeout, message_thread_id=message_thread_id, show_caption_above_media=show_caption_above_media, allow_paid_broadcast=allow_paid_broadcast, video_start_timestamp=video_start_timestamp, - direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters + direct_messages_topic_id=direct_messages_topic_id, suggested_post_parameters=suggested_post_parameters, + message_effect_id=message_effect_id ) ) diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index dbd5351a5..5e95e365c 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -434,7 +434,7 @@ async def forward_message( token, chat_id, from_chat_id, message_id, disable_notification=None, timeout=None, protect_content=None, message_thread_id=None, video_start_timestamp=None, direct_messages_topic_id=None, - suggested_post_parameters=None): + suggested_post_parameters=None, message_effect_id=None): method_url = r'forwardMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if disable_notification is not None: @@ -451,13 +451,16 @@ async def forward_message( payload['direct_messages_topic_id'] = direct_messages_topic_id if suggested_post_parameters is not None: payload['suggested_post_parameters'] = suggested_post_parameters.to_json() + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, parse_mode=None, caption_entities=None, disable_notification=None, reply_markup=None, timeout=None, protect_content=None, message_thread_id=None, reply_parameters=None, show_caption_above_media=None, - allow_paid_broadcast=None, video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None): + allow_paid_broadcast=None, video_start_timestamp=None, direct_messages_topic_id=None, suggested_post_parameters=None, + message_effect_id=None): method_url = r'copyMessage' payload = {'chat_id': chat_id, 'from_chat_id': from_chat_id, 'message_id': message_id} if caption is not None: @@ -488,6 +491,8 @@ async def copy_message(token, chat_id, from_chat_id, message_id, caption=None, p payload['direct_messages_topic_id'] = direct_messages_topic_id if suggested_post_parameters is not None: payload['suggested_post_parameters'] = suggested_post_parameters.to_json() + if message_effect_id: + payload['message_effect_id'] = message_effect_id return await _process_request(token, method_url, params=payload) async def send_checklist(