From ff6db8b947619ee67c62798e7c0e5e968c3ff4df Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:08:42 +0400 Subject: [PATCH 01/10] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97d55930d..119545779 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

A simple, but extensible Python implementation for the Telegram Bot API.

Both synchronous and asynchronous.

-##

Supported Bot API version: Supported Bot API version +##

Supported Bot API version: Supported Bot API version

Official documentation

Official ru documentation

From c8d8ec7c2ca8f519c0a60084773d7df344bf8688 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:26:25 +0400 Subject: [PATCH 02/10] Added business connection id and subscription period for createinvoicelink --- telebot/__init__.py | 15 +++++++++++++-- telebot/apihelper.py | 6 +++++- telebot/async_telebot.py | 14 ++++++++++++-- telebot/asyncio_helper.py | 6 +++++- 4 files changed, 35 insertions(+), 6 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 62cae3f96..04c4d3ced 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5418,7 +5418,9 @@ def create_invoice_link(self, need_shipping_address: Optional[bool]=None, send_phone_number_to_provider: Optional[bool]=None, send_email_to_provider: Optional[bool]=None, - is_flexible: Optional[bool]=None) -> str: + is_flexible: Optional[bool]=None, + subscription_period: Optional[int]=None, + business_connection_id: Optional[str]=None) -> str: """ Use this method to create a link for an invoice. @@ -5427,6 +5429,9 @@ def create_invoice_link(self, Telegram documentation: https://core.telegram.org/bots/api#createinvoicelink + :param business_connection_id: Unique identifier of the business connection on behalf of which the link will be created + :type business_connection_id: :obj:`str` + :param title: Product name, 1-32 characters :type title: :obj:`str` @@ -5449,6 +5454,11 @@ def create_invoice_link(self, (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :obj:`types.LabeledPrice` + :subscription_period: The number of seconds the subscription will be active for before the next payment. + The currency must be set to “XTR” (Telegram Stars) if the parameter is used. Currently, it must always + be 2592000 (30 days) if specified. + :type subscription_period: :obj:`int` + :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency :type max_tip_amount: :obj:`int` @@ -5505,7 +5515,8 @@ def create_invoice_link(self, photo_width=photo_width, photo_height=photo_height, need_name=need_name, need_phone_number=need_phone_number, need_email=need_email, need_shipping_address=need_shipping_address, send_phone_number_to_provider=send_phone_number_to_provider, - send_email_to_provider=send_email_to_provider, is_flexible=is_flexible) + send_email_to_provider=send_email_to_provider, is_flexible=is_flexible ,subscription_period=subscription_period, + business_connection_id=business_connection_id) # noinspection PyShadowingBuiltins diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 92d419c45..43e536e6f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1970,7 +1970,7 @@ def create_invoice_link(token, title, description, payload, provider_token, currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, - send_email_to_provider=None, is_flexible=None): + send_email_to_provider=None, is_flexible=None, subscription_period=None, business_connection_id=None): method_url = r'createInvoiceLink' payload = {'title': title, 'description': description, 'payload': payload, 'currency': currency, 'prices': _convert_list_json_serializable(prices)} @@ -2004,6 +2004,10 @@ def create_invoice_link(token, title, description, payload, provider_token, payload['is_flexible'] = is_flexible if provider_token is not None: payload['provider_token'] = provider_token + if subscription_period: + payload['subscription_period'] = subscription_period + if business_connection_id: + payload['business_connection_id'] = business_connection_id return _make_request(token, method_url, params=payload, method='post') diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 75ce41346..61b2dee21 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -6836,7 +6836,9 @@ async def create_invoice_link(self, need_shipping_address: Optional[bool]=None, send_phone_number_to_provider: Optional[bool]=None, send_email_to_provider: Optional[bool]=None, - is_flexible: Optional[bool]=None) -> str: + is_flexible: Optional[bool]=None, + subscription_period: Optional[int]=None, + business_connection_id: Optional[str]=None) -> str: """ Use this method to create a link for an invoice. @@ -6844,6 +6846,9 @@ async def create_invoice_link(self, Telegram documentation: https://core.telegram.org/bots/api#createinvoicelink + + :param business_connection_id: Unique identifier of the business connection on behalf of which the link will be created + :type business_connection_id: :obj:`str` :param title: Product name, 1-32 characters :type title: :obj:`str` @@ -6867,6 +6872,11 @@ async def create_invoice_link(self, (e.g. product price, tax, discount, delivery cost, delivery tax, bonus, etc.) :type prices: :obj:`list` of :obj:`types.LabeledPrice` + :subscription_period: The number of seconds the subscription will be active for before the next payment. + The currency must be set to “XTR” (Telegram Stars) if the parameter is used. Currently, it must always + be 2592000 (30 days) if specified. + :type subscription_period: :obj:`int` + :param max_tip_amount: The maximum accepted amount for tips in the smallest units of the currency :type max_tip_amount: :obj:`int` @@ -6921,7 +6931,7 @@ async def create_invoice_link(self, currency, prices, max_tip_amount, suggested_tip_amounts, provider_data, photo_url, photo_size, photo_width, photo_height, need_name, need_phone_number, need_email, need_shipping_address, send_phone_number_to_provider, - send_email_to_provider, is_flexible) + send_email_to_provider, is_flexible, subscription_period=subscription_period, business_connection_id=business_connection_id) return result # noinspection PyShadowingBuiltins diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index b21b7f9f0..46b70fe85 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1952,7 +1952,7 @@ async def create_invoice_link(token, title, description, payload, provider_token currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, need_email=None, need_shipping_address=None, send_phone_number_to_provider=None, - send_email_to_provider=None, is_flexible=None): + send_email_to_provider=None, is_flexible=None, subscription_period=None, business_connection_id=None): method_url = r'createInvoiceLink' payload = {'title': title, 'description': description, 'payload': payload, 'currency': currency, 'prices': await _convert_list_json_serializable(prices)} @@ -1986,6 +1986,10 @@ async def create_invoice_link(token, title, description, payload, provider_token payload['is_flexible'] = is_flexible if provider_token is not None: payload['provider_token'] = provider_token + if subscription_period: + payload['subscription_period'] = subscription_period + if business_connection_id: + payload['business_connection_id'] = business_connection_id return await _process_request(token, method_url, params=payload, method='post') From 90b3c976f542d2a8bd350839834e6f9260966b69 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:35:01 +0400 Subject: [PATCH 03/10] Added the fields subscription_expiration_date, is_recurring and is_first_recurring to the class SuccessfulPayment. --- telebot/types.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 66ad984bc..e11e74ecf 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -6318,6 +6318,15 @@ class SuccessfulPayment(JsonDeserializable): :param invoice_payload: Bot specified invoice payload :type invoice_payload: :obj:`str` + :param subscription_expiration_date: Optional. Expiration date of the subscription, in Unix time; for recurring payments only + :type subscription_expiration_date: :obj:`int` + + :param is_recurring: Optional. True, if the payment is a recurring payment, false otherwise + :type is_recurring: :obj:`bool` + + :param is_first_recurring: Optional. True, if the payment is the first payment for a subscription + :type is_first_recurring: :obj:`bool` + :param shipping_option_id: Optional. Identifier of the shipping option chosen by the user :type shipping_option_id: :obj:`str` @@ -6341,7 +6350,8 @@ def de_json(cls, json_string): return cls(**obj) def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=None, order_info=None, - telegram_payment_charge_id=None, provider_payment_charge_id=None, **kwargs): + telegram_payment_charge_id=None, provider_payment_charge_id=None, + subscription_expiration_date=None, is_recurring=None, is_first_recurring=None, **kwargs): self.currency: str = currency self.total_amount: int = total_amount self.invoice_payload: str = invoice_payload @@ -6349,6 +6359,9 @@ def __init__(self, currency, total_amount, invoice_payload, shipping_option_id=N self.order_info: OrderInfo = order_info self.telegram_payment_charge_id: str = telegram_payment_charge_id self.provider_payment_charge_id: str = provider_payment_charge_id + self.subscription_expiration_date: Optional[int] = subscription_expiration_date + self.is_recurring: Optional[bool] = is_recurring + self.is_first_recurring: Optional[bool] = is_first_recurring # noinspection PyShadowingBuiltins From a12d8c2844c2ac5fecc85e6ae9832ee0aabdce79 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:38:42 +0400 Subject: [PATCH 04/10] Added the method editUserStarSubscription. --- telebot/__init__.py | 20 ++++++++++++++++++++ telebot/apihelper.py | 5 +++++ telebot/async_telebot.py | 20 ++++++++++++++++++++ telebot/asyncio_helper.py | 6 ++++++ 4 files changed, 51 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 04c4d3ced..142d71193 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -5814,6 +5814,26 @@ def refund_star_payment(self, user_id: int, telegram_payment_charge_id: str) -> """ return apihelper.refund_star_payment(self.token, user_id, telegram_payment_charge_id) + def edit_user_star_subscription(self, user_id: int, telegram_payment_charge_id: str, is_canceled: bool) -> bool: + """ + Allows the bot to cancel or re-enable extension of a subscription paid in Telegram Stars. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#edituserstarsubscription + + :param user_id: Identifier of the user whose subscription will be edited + :type user_id: :obj:`int` + + :param telegram_payment_charge_id: Telegram payment identifier for the subscription + :type telegram_payment_charge_id: :obj:`str` + + :param is_canceled: Pass True to cancel extension of the user subscription; the subscription must be active up to the end of the current subscription period. Pass False to allow the user to re-enable a subscription that was previously canceled by the bot. + :type is_canceled: :obj:`bool` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return apihelper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) + def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 43e536e6f..69fa5bf4f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1805,6 +1805,11 @@ def refund_star_payment(token, user_id, telegram_payment_charge_id): payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id} return _make_request(token, method_url, params=payload) +def edit_user_star_subscription(token, user_id, telegram_payment_charge_id, is_canceled): + method_url = 'editUserStarSubscription' + payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id, 'is_canceled': is_canceled} + return _make_request(token, method_url, params=payload) + def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 61b2dee21..f82c7238e 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7220,6 +7220,26 @@ async def refund_star_payment(self, user_id: int, telegram_payment_charge_id: st """ return await asyncio_helper.refund_star_payment(self.token, user_id, telegram_payment_charge_id) + async def edit_user_star_subscription(self, user_id: int, telegram_payment_charge_id: str, is_canceled: bool) -> bool: + """ + Allows the bot to cancel or re-enable extension of a subscription paid in Telegram Stars. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#edituserstarsubscription + + :param user_id: Identifier of the user whose subscription will be edited + :type user_id: :obj:`int` + + :param telegram_payment_charge_id: Telegram payment identifier for the subscription + :type telegram_payment_charge_id: :obj:`str` + + :param is_canceled: Pass True to cancel extension of the user subscription; the subscription must be active up to the end of the current subscription period. Pass False to allow the user to re-enable a subscription that was previously canceled by the bot. + :type is_canceled: :obj:`bool` + + :return: On success, True is returned. + :rtype: :obj:`bool` + """ + return await asyncio_helper.edit_user_star_subscription(self.token, user_id, telegram_payment_charge_id, is_canceled) + async def edit_message_caption( self, caption: str, chat_id: Optional[Union[int, str]]=None, message_id: Optional[int]=None, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 46b70fe85..3e1a68a1d 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1793,6 +1793,12 @@ async def refund_star_payment(token, user_id, telegram_payment_charge_id): return await _process_request(token, method_url, params=payload) +async def edit_user_star_subscription(token, user_id, telegram_payment_charge_id, is_canceled): + method_url = 'editUserStarSubscription' + payload = {'user_id': user_id, 'telegram_payment_charge_id': telegram_payment_charge_id, 'is_canceled': is_canceled} + return await _process_request(token, method_url, params=payload) + + async def unpin_all_general_forum_topic_messages(token, chat_id): method_url = 'unpinAllGeneralForumTopicMessages' payload = {'chat_id': chat_id} From 8eb5d1065b2cd2712430bb7928806919edf6e961 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 17:40:04 +0400 Subject: [PATCH 05/10] Added the field subscription_period to the class TransactionPartnerUser. --- telebot/types.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index e11e74ecf..76cf20b61 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10489,6 +10489,9 @@ class TransactionPartnerUser(TransactionPartner): :param invoice_payload: Optional, Bot-specified invoice payload :type invoice_payload: :obj:`str` + :param subscription_period: Optional. The duration of the paid subscription + :type subscription_period: :obj:`int` + :param paid_media: Optional. Information about the paid media bought by the user :type paid_media: :obj:`list` of :class:`PaidMedia` @@ -10496,11 +10499,13 @@ class TransactionPartnerUser(TransactionPartner): :rtype: :class:`TransactionPartnerUser` """ - def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, **kwargs): + def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, + subscription_period=None, **kwargs): self.type: str = type self.user: User = user self.invoice_payload: Optional[str] = invoice_payload self.paid_media: Optional[List[PaidMedia]] = paid_media + self.subscription_period: Optional[int] = subscription_period @classmethod def de_json(cls, json_string): From e55b0971f65f74738c17d264291398da178101a5 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:01:48 +0400 Subject: [PATCH 06/10] Added the method setUserEmojiStatus. The user must allow the bot to manage their emoji status. --- telebot/__init__.py | 20 ++++++++++++++++++++ telebot/apihelper.py | 10 ++++++++++ telebot/async_telebot.py | 20 ++++++++++++++++++++ telebot/asyncio_helper.py | 10 ++++++++++ 4 files changed, 60 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 142d71193..494c0ec5d 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1512,6 +1512,26 @@ def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None, apihelper.get_user_profile_photos(self.token, user_id, offset=offset, limit=limit) ) + def set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Optional[str]=None, emoji_status_expiration_date: Optional[int]=None) -> bool: + """ + Changes the emoji status for a given user that previously allowed the bot to manage their emoji status via the Mini App method requestEmojiStatusAccess. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setuseremojistatus + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param emoji_status_custom_emoji_id: Custom emoji identifier of the emoji status to set. Pass an empty string to remove the status. + :type emoji_status_custom_emoji_id: :obj:`str` + + :param emoji_status_expiration_date: Expiration date of the emoji status, if any + :type emoji_status_expiration_date: :obj:`int` + + :return: :obj:`bool` + """ + return apihelper.set_user_emoji_status( + self.token, user_id, emoji_status_custom_emoji_id=emoji_status_custom_emoji_id, emoji_status_expiration_date=emoji_status_expiration_date) + def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 69fa5bf4f..68623922f 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -341,6 +341,16 @@ def get_user_profile_photos(token, user_id, offset=None, limit=None): payload['limit'] = limit return _make_request(token, method_url, params=payload) + +def set_user_emoji_status(token, user_id, emoji_status_custom_emoji_id=None, emoji_status_expiration_date=None): + method_url = r'setUserEmojiStatus' + payload = {'user_id': user_id} + if emoji_status_custom_emoji_id: + payload['emoji_status_custom_emoji_id'] = emoji_status_custom_emoji_id + if emoji_status_expiration_date: + payload['emoji_status_expiration_date'] = emoji_status_expiration_date + return _make_request(token, method_url, params=payload) + def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None): method_url = r'setMessageReaction' payload = {'chat_id': chat_id, 'message_id': message_id} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index f82c7238e..6ab489756 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2987,6 +2987,26 @@ async def get_user_profile_photos(self, user_id: int, offset: Optional[int]=None """ result = await asyncio_helper.get_user_profile_photos(self.token, user_id, offset, limit) return types.UserProfilePhotos.de_json(result) + + async def set_user_emoji_status(self, user_id: int, emoji_status_custom_emoji_id: Optional[str]=None, emoji_status_expiration_date: Optional[int]=None) -> bool: + """ + Use this method to change the emoji status for a given user that previously allowed the bot to manage their emoji status via the Mini App method requestEmojiStatusAccess. + + Telegram documentation: https://core.telegram.org/bots/api#setuseremojistatus + + :param user_id: Unique identifier of the target user + :type user_id: :obj:`int` + + :param emoji_status_custom_emoji_id: Custom emoji identifier of the emoji status to set. Pass an empty string to remove the status. + :type emoji_status_custom_emoji_id: :obj:`str`, optional + + :param emoji_status_expiration_date: Expiration date of the emoji status, if any + :type emoji_status_expiration_date: :obj:`int`, optional + + :return: :obj:`bool` + """ + result = await asyncio_helper.set_user_emoji_status(self.token, user_id, emoji_status_custom_emoji_id, emoji_status_expiration_date) + return result async def get_chat(self, chat_id: Union[int, str]) -> types.ChatFullInfo: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 3e1a68a1d..41d94d0bd 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -325,6 +325,16 @@ async def get_user_profile_photos(token, user_id, offset=None, limit=None): payload['limit'] = limit return await _process_request(token, method_url, params=payload) + +async def set_user_emoji_status(token, user_id, emoji_status_custom_emoji_id=None, emoji_status_expiration_date=None): + method_url = r'setUserEmojiStatus' + payload = {'user_id': user_id} + if emoji_status_custom_emoji_id: + payload['emoji_status_custom_emoji_id'] = emoji_status_custom_emoji_id + if emoji_status_expiration_date: + payload['emoji_status_expiration_date'] = emoji_status_expiration_date + return await _process_request(token, method_url, params=payload) + async def set_message_reaction(token, chat_id, message_id, reaction=None, is_big=None): method_url = r'setMessageReaction' payload = {'chat_id': chat_id, 'message_id': message_id} From bee6bab8213e1d22731146b9e92245fd1a42cdb5 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:10:08 +0400 Subject: [PATCH 07/10] Added the class PreparedInlineMessage and the method savePreparedInlineMessage, allowing bots to suggest users to send a specific message from a Mini App via the method shareMessage. --- telebot/__init__.py | 36 ++++++++++++++++++++++++++++++++++++ telebot/apihelper.py | 15 +++++++++++++++ telebot/async_telebot.py | 30 ++++++++++++++++++++++++++++++ telebot/asyncio_helper.py | 14 ++++++++++++++ telebot/types.py | 28 ++++++++++++++++++++++++++++ 5 files changed, 123 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 494c0ec5d..586f44c9a 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6769,6 +6769,42 @@ def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryR """ return apihelper.answer_web_app_query(self.token, web_app_query_id, result) + def save_prepared_inline_message( + self, user_id: int, result: types.InlineQueryResultBase, allow_user_chats: Optional[bool]=None, + allow_bot_chats: Optional[bool]=None, allow_group_chats: Optional[bool]=None, + allow_channel_chats: Optional[bool]=None) -> types.PreparedInlineMessage: + """ + Use this method to store a message that can be sent by a user of a Mini App. + Returns a PreparedInlineMessage object. + + Telegram Documentation: https://core.telegram.org/bots/api#savepreparedinlinemessage + + :param user_id: Unique identifier of the target user that can use the prepared message + :type user_id: :obj:`int` + + :param result: A JSON-serialized object describing the message to be sent + :type result: :class:`telebot.types.InlineQueryResultBase` + + :param allow_user_chats: Pass True if the message can be sent to private chats with users + :type allow_user_chats: :obj:`bool` + + :param allow_bot_chats: Pass True if the message can be sent to private chats with bots + :type allow_bot_chats: :obj:`bool` + + :param allow_group_chats: Pass True if the message can be sent to group and supergroup chats + :type allow_group_chats: :obj:`bool` + + :param allow_channel_chats: Pass True if the message can be sent to channel chats + :type allow_channel_chats: :obj:`bool` + + :return: On success, a PreparedInlineMessage object is returned. + :rtype: :class:`telebot.types.PreparedInlineMessage` + """ + return types.PreparedInlineMessage.de_json( + apihelper.save_prepared_inline_message( + self.token, user_id, result, allow_user_chats=allow_user_chats, allow_bot_chats=allow_bot_chats, + allow_group_chats=allow_group_chats, allow_channel_chats=allow_channel_chats) + ) def register_for_reply(self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 68623922f..01a1a77ba 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1981,6 +1981,21 @@ def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResul return _make_request(token, method_url, params=payload, method='post') +def save_prepared_inline_message(token, user_id, result: types.InlineQueryResultBase, allow_user_chats=None, + allow_bot_chats=None, allow_group_chats=None, allow_channel_chats=None): + method_url = 'savePreparedInlineMessage' + payload = {'user_id': user_id, 'result': result.to_json()} + if allow_user_chats is not None: + payload['allow_user_chats'] = allow_user_chats + if allow_bot_chats is not None: + payload['allow_bot_chats'] = allow_bot_chats + if allow_group_chats is not None: + payload['allow_group_chats'] = allow_group_chats + if allow_channel_chats is not None: + payload['allow_channel_chats'] = allow_channel_chats + return _make_request(token, method_url, params=payload, method='post') + + def create_invoice_link(token, title, description, payload, provider_token, currency, prices, max_tip_amount=None, suggested_tip_amounts=None, provider_data=None, photo_url=None, photo_size=None, photo_width=None, photo_height=None, need_name=None, need_phone_number=None, diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 6ab489756..659596f59 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -3148,6 +3148,36 @@ async def answer_web_app_query(self, web_app_query_id: str, result: types.Inline return await asyncio_helper.answer_web_app_query(self.token, web_app_query_id, result) + async def save_prepared_inline_message(self, user_id: int, result: types.InlineQueryResultBase, allow_user_chats: Optional[bool]=None, + allow_bot_chats: Optional[bool]=None, allow_group_chats: Optional[bool]=None, allow_channel_chats: Optional[bool]=None) -> types.PreparedInlineMessage: + """ + Stores a message that can be sent by a user of a Mini App. Returns a PreparedInlineMessage object. + + Telegram Documentation: https://core.telegram.org/bots/api#savepreparedinlinemessage + + :param user_id: Unique identifier of the target user that can use the prepared message + :type user_id: :obj:`int` + + :param result: A JSON-serialized object describing the message to be sent + :type result: :class:`telebot.types.InlineQueryResultBase` + + :param allow_user_chats: Pass True if the message can be sent to private chats with users + :type allow_user_chats: :obj:`bool`, optional + + :param allow_bot_chats: Pass True if the message can be sent to private chats with bots + :type allow_bot_chats: :obj:`bool`, optional + + :param allow_group_chats: Pass True if the message can be sent to group and supergroup chats + :type allow_group_chats: :obj:`bool`, optional + + :param allow_channel_chats: Pass True if the message can be sent to channel chats + :type allow_channel_chats: :obj:`bool`, optional + + :return: :class:`telebot.types.PreparedInlineMessage` + """ + result = await asyncio_helper.save_prepared_inline_message(self.token, user_id, result, allow_user_chats, allow_bot_chats, allow_group_chats, allow_channel_chats) + return types.PreparedInlineMessage.de_json(result) + async def get_chat_member(self, chat_id: Union[int, str], user_id: int) -> types.ChatMember: """ Use this method to get information about a member of a chat. Returns a ChatMember object on success. diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 41d94d0bd..4523e7173 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -406,6 +406,20 @@ async def answer_web_app_query(token, web_app_query_id, result: types.InlineQuer return await _process_request(token, method_url, params=payload, method='post') +async def save_prepared_inline_message(token, user_id, result: types.InlineQueryResultBase, allow_user_chats=None, allow_bot_chats=None, allow_group_chats=None, allow_channel_chats=None): + method_url = r'savePreparedInlineMessage' + payload = {'user_id': user_id, 'result': result.to_json()} + if allow_user_chats is not None: + payload['allow_user_chats'] = allow_user_chats + if allow_bot_chats is not None: + payload['allow_bot_chats'] = allow_bot_chats + if allow_group_chats is not None: + payload['allow_group_chats'] = allow_group_chats + if allow_channel_chats is not None: + payload['allow_channel_chats'] = allow_channel_chats + return await _process_request(token, method_url, params=payload) + + async def get_chat_member(token, chat_id, user_id): method_url = r'getChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} diff --git a/telebot/types.py b/telebot/types.py index 76cf20b61..904379dbc 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10997,3 +10997,31 @@ def de_json(cls, json_string): if json_string is None: return None obj = cls.check_json(json_string) return cls(**obj) + + +class PreparedInlineMessage(JsonDeserializable): + """ + Describes an inline message to be sent by a user of a Mini App. + + Telegram documentation: https://core.telegram.org/bots/api#preparedinlinemessage + + :param id: Unique identifier of the prepared message + :type id: :obj:`str` + + :param expiration_date: Expiration date of the prepared message, in Unix time. Expired prepared messages can no longer be used + :type expiration_date: :obj:`int` + + :return: Instance of the class + :rtype: :class:`PreparedInlineMessage` + """ + + def __init__(self, id, expiration_date, **kwargs): + self.id: str = id + self.expiration_date: int = expiration_date + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + From 3d740ffd97364ec5fd81c3eb3bba47d356500f60 Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:18:37 +0400 Subject: [PATCH 08/10] Added the classes Gift and Gifts and the method getAvailableGifts, allowing bots to get all gifts available for sending. --- telebot/__init__.py | 12 ++++++++ telebot/apihelper.py | 5 ++++ telebot/async_telebot.py | 12 ++++++++ telebot/asyncio_helper.py | 4 +++ telebot/types.py | 63 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index 586f44c9a..a274d2272 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6249,7 +6249,19 @@ def delete_sticker_set(self, name:str) -> bool: :rtype: :obj:`bool` """ return apihelper.delete_sticker_set(self.token, name) + + def get_available_gifts(self) -> types.Gifts: + """ + Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. + + Telegram documentation: https://core.telegram.org/bots/api#getavailablegifts + :return: On success, a Gifts object is returned. + :rtype: :class:`telebot.types.Gifts` + """ + return types.Gifts.de_json( + apihelper.get_available_gifts(self.token) + ) def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 01a1a77ba..83606f746 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1924,6 +1924,11 @@ def delete_sticker_set(token, name): return _make_request(token, method_url, params=payload, method='post') +def get_available_gifts(token): + method_url = 'getAvailableGifts' + return _make_request(token, method_url) + + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 659596f59..60719477d 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7707,6 +7707,18 @@ async def delete_sticker_set(self, name:str) -> bool: return await asyncio_helper.delete_sticker_set(self.token, name) + async def get_available_gifts(self) -> types.Gifts: + """ + Returns the list of gifts that can be sent by the bot to users. Requires no parameters. Returns a Gifts object. + + Telegram documentation: https://core.telegram.org/bots/api#getavailablegifts + + :return: On success, a Gifts object is returned. + :rtype: :class:`telebot.types.Gifts` + """ + + return types.Gifts.de_json(await asyncio_helper.get_available_gifts(self.token)) + async def replace_sticker_in_set(self, user_id: int, name: str, old_sticker: str, sticker: types.InputSticker) -> bool: """ Use this method to replace an existing sticker in a sticker set with a new one. The method is equivalent to calling deleteStickerFromSet, then addStickerToSet, diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 4523e7173..47a1da1dc 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1916,6 +1916,10 @@ async def delete_sticker_set(token, name): payload = {'name': name} return await _process_request(token, method_url, params=payload, method='post') +async def get_available_gifts(token): + method_url = 'getAvailableGifts' + return await _process_request(token, method_url) + async def set_custom_emoji_sticker_set_thumbnail(token, name, custom_emoji_id=None): method_url = 'setCustomEmojiStickerSetThumbnail' payload = {'name': name} diff --git a/telebot/types.py b/telebot/types.py index 904379dbc..c6386a61b 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -11025,3 +11025,66 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) + +class Gift(JsonDeserializable): + """ + This object represents a gift that can be sent by the bot. + + Telegram documentation: https://core.telegram.org/bots/api#gift + + :param id: Unique identifier of the gift + :type id: :obj:`str` + + :param sticker: The sticker that represents the gift + :type sticker: :class:`Sticker` + + :param star_count: The number of Telegram Stars that must be paid to send the sticker + :type star_count: :obj:`int` + + :param total_count: Optional. The total number of the gifts of this type that can be sent; for limited gifts only + :type total_count: :obj:`int` + + :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` + + :return: Instance of the class + :rtype: :class:`Gift` + """ + + def __init__(self, id, sticker, star_count, total_count=None, 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 + + @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']) + return cls(**obj) + +class Gifts(JsonDeserializable): + """ + This object represent a list of gifts. + + Telegram documentation: https://core.telegram.org/bots/api#gifts + + :param gifts: The list of gifts + :type gifts: :obj:`list` of :class:`Gift` + + :return: Instance of the class + :rtype: :class:`Gifts` + """ + + def __init__(self, gifts, **kwargs): + self.gifts: List[Gift] = gifts + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + obj['gifts'] = [Gift.de_json(gift) for gift in obj['gifts']] + return cls(**obj) + \ No newline at end of file From a2d2a86d4f496babff9353bf672222c773bbe5be Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:22:31 +0400 Subject: [PATCH 09/10] Added the method sendGift, allowing bots to send gifts to users. --- telebot/__init__.py | 26 ++++++++++++++++++++++++++ telebot/apihelper.py | 11 +++++++++++ telebot/async_telebot.py | 26 ++++++++++++++++++++++++++ telebot/asyncio_helper.py | 11 +++++++++++ 4 files changed, 74 insertions(+) diff --git a/telebot/__init__.py b/telebot/__init__.py index a274d2272..bb33f55b5 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -6249,6 +6249,32 @@ def delete_sticker_set(self, name:str) -> bool: :rtype: :obj:`bool` """ return apihelper.delete_sticker_set(self.token, name) + + def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + """ + Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#sendgift + + :param user_id: Unique identifier of the target user that will receive the gift + :type user_id: :obj:`int` + + :param gift_id: Identifier of the gift + :type gift_id: :obj:`str` + + :param text: Text that will be shown along with the gift; 0-255 characters + :type text: :obj:`str` + + :param text_parse_mode: Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_parse_mode: :obj:`str` + + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_entities: :obj:`list` of :obj:`types.MessageEntity` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return apihelper.send_gift(self.token, user_id, gift_id, text=text, text_parse_mode=text_parse_mode, text_entities=text_entities) def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 83606f746..5ea37c4fd 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -1929,6 +1929,17 @@ def get_available_gifts(token): return _make_request(token, method_url) +def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None): + method_url = 'sendGift' + payload = {'user_id': user_id, 'gift_id': gift_id} + if text: + payload['text'] = text + if text_parse_mode: + payload['text_parse_mode'] = text_parse_mode + if text_entities: + payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + return _make_request(token, method_url, params=payload, method='post') + def set_sticker_emoji_list(token, sticker, emoji_list): method_url = 'setStickerEmojiList' payload = {'sticker': sticker, 'emoji_list': json.dumps(emoji_list)} diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 60719477d..7f50b1c86 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -7706,6 +7706,32 @@ async def delete_sticker_set(self, name:str) -> bool: """ return await asyncio_helper.delete_sticker_set(self.token, name) + + async def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool: + """ + Sends a gift to the given user. The gift can't be converted to Telegram Stars by the user. Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#sendgift + + :param user_id: Unique identifier of the target user that will receive the gift + :type user_id: :obj:`int` + + :param gift_id: Identifier of the gift + :type gift_id: :obj:`str` + + :param text: Text that will be shown along with the gift; 0-255 characters + :type text: :obj:`str` + + :param text_parse_mode: Mode for parsing entities in the text. See formatting options for more details. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_parse_mode: :obj:`str` + + :param text_entities: A JSON-serialized list of special entities that appear in the gift text. It can be specified instead of text_parse_mode. Entities other than “bold”, “italic”, “underline”, “strikethrough”, “spoiler”, and “custom_emoji” are ignored. + :type text_entities: :obj:`list` of :obj:`types.MessageEntity` + + :return: Returns True on success. + :rtype: :obj:`bool` + """ + return await asyncio_helper.send_gift(self.token, user_id, gift_id, text, text_parse_mode, text_entities) async def get_available_gifts(self) -> types.Gifts: """ diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index 47a1da1dc..56604c750 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -1916,6 +1916,17 @@ async def delete_sticker_set(token, name): payload = {'name': name} return await _process_request(token, method_url, params=payload, method='post') +async def send_gift(token, user_id, gift_id, text=None, text_parse_mode=None, text_entities=None): + method_url = 'sendGift' + payload = {'user_id': user_id, 'gift_id': gift_id} + if text: + payload['text'] = text + if text_parse_mode: + payload['text_parse_mode'] = text_parse_mode + if text_entities: + payload['text_entities'] = json.dumps(types.MessageEntity.to_list_of_dicts(text_entities)) + return await _process_request(token, method_url, params=payload, method='post') + async def get_available_gifts(token): method_url = 'getAvailableGifts' return await _process_request(token, method_url) From 9c7c47f3628f5997949f456b52aa18cb624832ff Mon Sep 17 00:00:00 2001 From: _run Date: Sun, 17 Nov 2024 18:23:46 +0400 Subject: [PATCH 10/10] Added the field gift to the class TransactionPartnerUser. --- telebot/types.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/telebot/types.py b/telebot/types.py index c6386a61b..3a71887de 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -10495,17 +10495,21 @@ class TransactionPartnerUser(TransactionPartner): :param paid_media: Optional. Information about the paid media bought by the user :type paid_media: :obj:`list` of :class:`PaidMedia` + :param gift: Optional. The gift sent to the user by the bot + :type gift: :class:`Gift` + :return: Instance of the class :rtype: :class:`TransactionPartnerUser` """ def __init__(self, type, user, invoice_payload=None, paid_media: Optional[List[PaidMedia]] = None, - subscription_period=None, **kwargs): + subscription_period=None, gift: Optional[Gift] = None, **kwargs): self.type: str = type self.user: User = user self.invoice_payload: Optional[str] = invoice_payload self.paid_media: Optional[List[PaidMedia]] = paid_media self.subscription_period: Optional[int] = subscription_period + self.gift: Optional[Gift] = gift @classmethod def de_json(cls, json_string): @@ -10514,6 +10518,8 @@ def de_json(cls, json_string): obj['user'] = User.de_json(obj['user']) if 'paid_media' in obj: obj['paid_media'] = [PaidMedia.de_json(media) for media in obj['paid_media']] + if 'gift' in obj: + obj['gift'] = Gift.de_json(obj['gift']) return cls(**obj) @@ -11025,7 +11031,7 @@ def de_json(cls, json_string): obj = cls.check_json(json_string) return cls(**obj) - + class Gift(JsonDeserializable): """ This object represents a gift that can be sent by the bot.