Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Bot API 8.0 - Subscription plans, Emoji Statuses, Gifts and many more #2418

Merged
merged 10 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<p align="center">A simple, but extensible Python implementation for the <a href="https://core.telegram.org/bots/api">Telegram Bot API</a>.</p>
<p align="center">Both synchronous and asynchronous.</p>

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

<h2><a href='https://pytba.readthedocs.io/en/latest/index.html'>Official documentation</a></h2>
<h2><a href='https://pytba.readthedocs.io/ru/latest/index.html'>Official ru documentation</a></h2>
Expand Down
129 changes: 127 additions & 2 deletions telebot/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -5418,7 +5438,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.
Expand All @@ -5427,6 +5449,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`

Expand All @@ -5449,6 +5474,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`

Expand Down Expand Up @@ -5505,7 +5535,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
Expand Down Expand Up @@ -5803,6 +5834,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,
Expand Down Expand Up @@ -6199,6 +6250,44 @@ def delete_sticker_set(self, name:str) -> bool:
"""
return apihelper.delete_sticker_set(self.token, name)

def send_gift(self, user_id: int, gift_id: str, text: Optional[str]=None, text_parse_mode: Optional[str]=None, text_entities: Optional[List[types.MessageEntity]]=None) -> bool:
"""
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:
"""
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:
"""
Expand Down Expand Up @@ -6718,6 +6807,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:
"""
Expand Down
52 changes: 51 additions & 1 deletion telebot/apihelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -1805,6 +1815,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'
Expand Down Expand Up @@ -1909,6 +1924,22 @@ 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 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)}
Expand Down Expand Up @@ -1966,11 +1997,26 @@ 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,
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)}
Expand Down Expand Up @@ -2004,6 +2050,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')


Expand Down
Loading
Loading