From 9652fdbecb578a72d539f3b680d9496b26f7cf13 Mon Sep 17 00:00:00 2001 From: _run Date: Sat, 16 Apr 2022 23:50:45 +0500 Subject: [PATCH 1/4] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2e0183b..a0e283f 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

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

Both synchronous and asynchronous.

-##

Supported Bot API version: 5.7! +##

Supported Bot API version: 6.0!

Official documentation

@@ -699,7 +699,7 @@ Result will be: ## API conformance - +* ✔ [Bot API 6.0](https://core.telegram.org/bots/api#april-16-2022) * ✔ [Bot API 5.7](https://core.telegram.org/bots/api#january-31-2022) * ✔ [Bot API 5.6](https://core.telegram.org/bots/api#december-30-2021) * ✔ [Bot API 5.5](https://core.telegram.org/bots/api#december-7-2021) From a1bf961fd2edfb6d0d3e20bb04d6677023c251ee Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 17 Apr 2022 16:39:09 +0500 Subject: [PATCH 2/4] Bump Bot API 6.0(Beta) --- telebot/__init__.py | 91 ++++++++++++++++- telebot/apihelper.py | 49 ++++++++- telebot/async_telebot.py | 90 ++++++++++++++++- telebot/asyncio_helper.py | 48 ++++++++- telebot/types.py | 208 ++++++++++++++++++++++++++++++++++---- tests/test_types.py | 23 +++++ 6 files changed, 481 insertions(+), 28 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 55d322e..55be7af 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -1772,6 +1772,7 @@ class TeleBot: can_promote_members: Optional[bool]=None, is_anonymous: Optional[bool]=None, can_manage_chat: Optional[bool]=None, + can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator @@ -1798,15 +1799,20 @@ class TeleBot: message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege - :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + :param can_manage_video_chats: Bool: Pass True, if the administrator can manage voice chats For now, bots can use this privilege only for passing to other administrators. + :param can_manage_voice_chats: Deprecated, use can_manage_video_chats. + :return: True on success. """ + if can_manage_voice_chats and not can_manage_video_chats is None: + can_manage_video_chats = can_manage_voice_chats + return apihelper.promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_voice_chats) + is_anonymous, can_manage_chat, can_manage_video_chats) def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: @@ -2033,6 +2039,71 @@ class TeleBot: result = apihelper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + def set_chat_menu_button(self, chat_id: Union[int, str]=None, + menu_button: types.MenuButton=None) -> bool: + """ + Use this method to change the bot's menu button in a private chat, + or the default menu button. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be changed + :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault + + """ + return apihelper.set_chat_menu_button(self.token, chat_id, menu_button) + + + def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButton: + """ + Use this method to get the current value of the bot's menu button + in a private chat, or the default menu button. + Returns MenuButton on success. + + Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be returned + :return: types.MenuButton + + """ + return types.MenuButton.de_json(apihelper.get_chat_menu_button(self.token, chat_id)) + + + def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + for_channels: bool=None) -> bool: + """ + Use this method to change the default administrator rights requested by the bot + when it's added as an administrator to groups or channels. + These rights will be suggested to users, but they are are free to modify + the list before adding the bot. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights + + :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. + :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + """ + + return apihelper.set_my_default_administrator_rights(self.token, rights, for_channels) + + + def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: + """ + Use this method to get the current default administrator rights of the bot. + Returns ChatAdministratorRights on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmydefaultadministratorrights + + :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. + :return: types.ChatAdministratorRights + """ + + return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) + + def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: @@ -2684,6 +2755,22 @@ class TeleBot: """ return apihelper.delete_sticker_from_set(self.token, sticker) + def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryResultBase) -> types.SentWebAppMessage: + """ + Use this method to set the result of an interaction with a Web App and + send a corresponding message on behalf of the user to the chat from which + the query originated. + On success, a SentWebAppMessage object is returned. + + Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery + + :param web_app_query_id: Unique identifier for the query to be answered + :param result: A JSON-serialized object describing the message to be sent + :return: + """ + + return apihelper.answer_web_app_query(self.token, web_app_query_id, result) + def register_for_reply( self, message: types.Message, callback: Callable, *args, **kwargs) -> None: """ diff --git a/telebot/apihelper.py b/telebot/apihelper.py index 67a8e7c..118b496 100644 --- a/telebot/apihelper.py +++ b/telebot/apihelper.py @@ -971,7 +971,7 @@ def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -994,8 +994,8 @@ def promote_chat_member( payload['is_anonymous'] = is_anonymous if can_manage_chat is not None: payload['can_manage_chat'] = can_manage_chat - if can_manage_voice_chats is not None: - payload['can_manage_voice_chats'] = can_manage_voice_chats + if can_manage_video_chats is not None: + payload['can_manage_video_chats'] = can_manage_video_chats return _make_request(token, method_url, params=payload, method='post') @@ -1139,6 +1139,43 @@ def get_my_commands(token, scope=None, language_code=None): payload['language_code'] = language_code return _make_request(token, method_url, params=payload) +def set_chat_menu_button(token, chat_id=None, menu_button=None): + method_url = r'setChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if menu_button: + payload['menu_button'] = menu_button.to_json() + + return _make_request(token, method_url, params=payload, method='post') + +def get_chat_menu_button(token, chat_id=None): + method_url = r'getChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + + return _make_request(token, method_url, params=payload, method='post') + + +def set_my_default_administrator_rights(token, rights=None, for_channels=None): + method_url = r'setMyDefaultAdministratorRights' + payload = {} + if rights: + payload['rights'] = rights.to_json() + if for_channels: + payload['for_channels'] = for_channels + + return _make_request(token, method_url, params=payload, method='post') + +def get_my_default_administrator_rights(token, for_channels=None): + method_url = r'getMyDefaultAdministratorRights' + payload = {} + if for_channels: + payload['for_channels'] = for_channels + + return _make_request(token, method_url, params=payload, method='post') + def set_my_commands(token, commands, scope=None, language_code=None): method_url = r'setMyCommands' @@ -1590,6 +1627,11 @@ def delete_sticker_from_set(token, sticker): payload = {'sticker': sticker} return _make_request(token, method_url, params=payload, method='post') +def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResultBase): + method_url = 'answerWebAppQuery' + result = result.to_json() + payload = {'query_id': web_app_query_id, 'result': result} + return _make_request(token, method_url, params=payload, method='post') # noinspection PyShadowingBuiltins def send_poll( @@ -1663,6 +1705,7 @@ def _convert_list_json_serializable(results): return '[' + ret + ']' + def _convert_markup(markup): if isinstance(markup, types.JsonSerializable): return markup.to_json() diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 7b45ed9..4dd78a7 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -1550,6 +1550,22 @@ class AsyncTeleBot: result = await asyncio_helper.delete_chat_sticker_set(self.token, chat_id) return result + async def answer_web_app_query(self, web_app_query_id: str, result: types.InlineQueryResultBase) -> types.SentWebAppMessage: + """ + Use this method to set the result of an interaction with a Web App and + send a corresponding message on behalf of the user to the chat from which + the query originated. + On success, a SentWebAppMessage object is returned. + + Telegram Documentation: https://core.telegram.org/bots/api#answerwebappquery + + :param web_app_query_id: Unique identifier for the query to be answered + :param result: A JSON-serialized object describing the message to be sent + :return: + """ + + return await asyncio_helper.answer_web_app_query(self.token, web_app_query_id, 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. @@ -2374,6 +2390,7 @@ class AsyncTeleBot: can_promote_members: Optional[bool]=None, is_anonymous: Optional[bool]=None, can_manage_chat: Optional[bool]=None, + can_manage_video_chats: Optional[bool]=None, can_manage_voice_chats: Optional[bool]=None) -> bool: """ Use this method to promote or demote a user in a supergroup or a channel. The bot must be an administrator @@ -2400,15 +2417,20 @@ class AsyncTeleBot: message statistics in channels, see channel members, see anonymous administrators in supergroups and ignore slow mode. Implied by any other administrator privilege - :param can_manage_voice_chats: Bool: Pass True, if the administrator can manage voice chats + :param can_manage_video_chats: Bool: Pass True, if the administrator can manage voice chats For now, bots can use this privilege only for passing to other administrators. + :param can_manage_voice_chats: Deprecated, use can_manage_video_chats :return: True on success. """ + + if can_manage_voice_chats and not can_manage_video_chats is None: + can_manage_video_chats = can_manage_voice_chats + return await asyncio_helper.promote_chat_member( self.token, chat_id, user_id, can_change_info, can_post_messages, can_edit_messages, can_delete_messages, can_invite_users, can_restrict_members, can_pin_messages, can_promote_members, - is_anonymous, can_manage_chat, can_manage_voice_chats) + is_anonymous, can_manage_chat, can_manage_video_chats) async def set_chat_administrator_custom_title( self, chat_id: Union[int, str], user_id: int, custom_title: str) -> bool: @@ -2635,6 +2657,70 @@ class AsyncTeleBot: result = await asyncio_helper.get_my_commands(self.token, scope, language_code) return [types.BotCommand.de_json(cmd) for cmd in result] + async def set_chat_menu_button(self, chat_id: Union[int, str]=None, + menu_button: types.MenuButton=None) -> bool: + """ + Use this method to change the bot's menu button in a private chat, + or the default menu button. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be changed + :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault + + """ + return await asyncio_helper.set_chat_menu_button(self.token, chat_id, menu_button) + + + async def get_chat_menu_button(self, chat_id: Union[int, str]=None) -> types.MenuButton: + """ + Use this method to get the current value of the bot's menu button + in a private chat, or the default menu button. + Returns MenuButton on success. + + Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton + + :param chat_id: Unique identifier for the target private chat. + If not specified, default bot's menu button will be returned + :return: types.MenuButton + + """ + return types.MenuButton.de_json(await asyncio_helper.get_chat_menu_button(self.token, chat_id)) + + + async def set_my_default_administrator_rights(self, rights: types.ChatAdministratorRights=None, + for_channels: bool=None) -> bool: + """ + Use this method to change the default administrator rights requested by the bot + when it's added as an administrator to groups or channels. + These rights will be suggested to users, but they are are free to modify + the list before adding the bot. + Returns True on success. + + Telegram documentation: https://core.telegram.org/bots/api#setmydefaultadministratorrights + + :param rights: A JSON-serialized object describing new default administrator rights. If not specified, the default administrator rights will be cleared. + :param for_channels: Pass True to change the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be changed. + """ + + return await asyncio_helper.set_my_default_administrator_rights(self.token, rights, for_channels) + + + async def get_my_default_administrator_rights(self, for_channels: bool=None) -> types.ChatAdministratorRights: + """ + Use this method to get the current default administrator rights of the bot. + Returns ChatAdministratorRights on success. + + Telegram documentation: https://core.telegram.org/bots/api#getmydefaultadministratorrights + + :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. + :return: types.ChatAdministratorRights + """ + + return types.ChatAdministratorRights.de_json(await asyncio_helper.get_my_default_administrator_rights(self.token, for_channels)) + async def set_my_commands(self, commands: List[types.BotCommand], scope: Optional[types.BotCommandScope]=None, language_code: Optional[str]=None) -> bool: diff --git a/telebot/asyncio_helper.py b/telebot/asyncio_helper.py index aaac114..eae41a5 100644 --- a/telebot/asyncio_helper.py +++ b/telebot/asyncio_helper.py @@ -356,6 +356,12 @@ async def delete_chat_sticker_set(token, chat_id): payload = {'chat_id': chat_id} return await _process_request(token, method_url, params=payload) +async def answer_web_app_query(token, web_app_query_id, result: types.InlineQueryResultBase): + method_url = 'answerWebAppQuery' + result = result.to_json() + payload = {'query_id': web_app_query_id, 'result': result} + return await _process_request(token, method_url, params=payload, method='post') + async def get_chat_member(token, chat_id, user_id): method_url = r'getChatMember' @@ -941,7 +947,7 @@ async def promote_chat_member( token, chat_id, user_id, can_change_info=None, can_post_messages=None, can_edit_messages=None, can_delete_messages=None, can_invite_users=None, can_restrict_members=None, can_pin_messages=None, can_promote_members=None, - is_anonymous=None, can_manage_chat=None, can_manage_voice_chats=None): + is_anonymous=None, can_manage_chat=None, can_manage_video_chats=None): method_url = 'promoteChatMember' payload = {'chat_id': chat_id, 'user_id': user_id} if can_change_info is not None: @@ -964,8 +970,8 @@ async def promote_chat_member( payload['is_anonymous'] = is_anonymous if can_manage_chat is not None: payload['can_manage_chat'] = can_manage_chat - if can_manage_voice_chats is not None: - payload['can_manage_voice_chats'] = can_manage_voice_chats + if can_manage_video_chats is not None: + payload['can_manage_video_chats'] = can_manage_video_chats return await _process_request(token, method_url, params=payload, method='post') @@ -1106,6 +1112,42 @@ async def get_my_commands(token, scope=None, language_code=None): payload['language_code'] = language_code return await _process_request(token, method_url, params=payload) +async def set_chat_menu_button(token, chat_id=None, menu_button=None): + method_url = r'setChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + if menu_button: + payload['menu_button'] = menu_button.to_json() + + return await _process_request(token, method_url, params=payload, method='post') + +async def get_chat_menu_button(token, chat_id=None): + method_url = r'getChatMenuButton' + payload = {} + if chat_id: + payload['chat_id'] = chat_id + + return await _process_request(token, method_url, params=payload, method='post') + + +async def set_my_default_administrator_rights(token, rights=None, for_channels=None): + method_url = r'setMyDefaultAdministratorRights' + payload = {} + if rights: + payload['rights'] = rights.to_json() + if for_channels: + payload['for_channels'] = for_channels + + return await _process_request(token, method_url, params=payload, method='post') + +async def get_my_default_administrator_rights(token, for_channels=None): + method_url = r'getMyDefaultAdministratorRights' + payload = {} + if for_channels: + payload['for_channels'] = for_channels + + return await _process_request(token, method_url, params=payload, method='post') async def set_my_commands(token, commands, scope=None, language_code=None): method_url = r'setMyCommands' diff --git a/telebot/types.py b/telebot/types.py index 0c6c3bc..307d7c5 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -194,14 +194,15 @@ class WebhookInfo(JsonDeserializable): return cls(**obj) def __init__(self, url, has_custom_certificate, pending_update_count, ip_address=None, - last_error_date=None, last_error_message=None, max_connections=None, - allowed_updates=None, **kwargs): + last_error_date=None, last_error_message=None, last_synchronization_error_date=None, + max_connections=None, allowed_updates=None, **kwargs): self.url = url self.has_custom_certificate = has_custom_certificate self.pending_update_count = pending_update_count self.ip_address = ip_address self.last_error_date = last_error_date self.last_error_message = last_error_message + self.last_synchronization_error_date = last_synchronization_error_date self.max_connections = max_connections self.allowed_updates = allowed_updates @@ -313,6 +314,20 @@ class MessageID(JsonDeserializable): self.message_id = message_id +class WebAppData(JsonDeserializable): + def __init__(self, data, button_text): + self.data = data + self.button_text = button_text + def to_dict(self): + return {'data': self.data, 'button_text': self.button_text} + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + class Message(JsonDeserializable): @classmethod def de_json(cls, json_string): @@ -457,18 +472,25 @@ class Message(JsonDeserializable): opts['proximity_alert_triggered'] = ProximityAlertTriggered.de_json(obj[ 'proximity_alert_triggered']) content_type = 'proximity_alert_triggered' - if 'voice_chat_scheduled' in obj: - opts['voice_chat_scheduled'] = VoiceChatScheduled.de_json(obj['voice_chat_scheduled']) - content_type = 'voice_chat_scheduled' - if 'voice_chat_started' in obj: - opts['voice_chat_started'] = VoiceChatStarted.de_json(obj['voice_chat_started']) - content_type = 'voice_chat_started' - if 'voice_chat_ended' in obj: - opts['voice_chat_ended'] = VoiceChatEnded.de_json(obj['voice_chat_ended']) - content_type = 'voice_chat_ended' - if 'voice_chat_participants_invited' in obj: - opts['voice_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['voice_chat_participants_invited']) - content_type = 'voice_chat_participants_invited' + if 'video_chat_scheduled' in obj: + opts['video_chat_scheduled'] = VoiceChatScheduled.de_json(obj['video_chat_scheduled']) + opts['voice_chat_scheduled'] = opts['video_chat_scheduled'] + content_type = 'video_chat_scheduled' + if 'video_chat_started' in obj: + opts['video_chat_started'] = VoiceChatStarted.de_json(obj['video_chat_started']) + opts['voice_chat_started'] = opts['video_chat_started'] + content_type = 'video_chat_started' + if 'video_chat_ended' in obj: + opts['video_chat_ended'] = VoiceChatEnded.de_json(obj['video_chat_ended']) + opts['voice_chat_ended'] = opts['video_chat_ended'] + content_type = 'video_chat_ended' + if 'video_chat_participants_invited' in obj: + opts['video_chat_participants_invited'] = VoiceChatParticipantsInvited.de_json(obj['video_chat_participants_invited']) + opts['voice_chat_participants_invited'] = opts['video_chat_participants_invited'] + content_type = 'video_chat_participants_invited' + if 'web_app_data' in obj: + opts['web_app_data'] = WebAppData.de_json(obj['web_app_data']) + content_type = 'web_app_data' if 'message_auto_delete_timer_changed' in obj: opts['message_auto_delete_timer_changed'] = MessageAutoDeleteTimerChanged.de_json(obj['message_auto_delete_timer_changed']) content_type = 'message_auto_delete_timer_changed' @@ -919,6 +941,20 @@ class ReplyKeyboardRemove(JsonSerializable): return json.dumps(json_dict) +class WebAppInfo(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + def __init__(self, url, **kwargs): + self.url: str = url + + def to_dict(self): + return {'url': self.url} + + class ReplyKeyboardMarkup(JsonSerializable): max_row_keys = 12 @@ -1011,11 +1047,13 @@ class KeyboardButtonPollType(Dictionaryable): class KeyboardButton(Dictionaryable, JsonSerializable): def __init__(self, text: str, request_contact: Optional[bool]=None, - request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None): + request_location: Optional[bool]=None, request_poll: Optional[KeyboardButtonPollType]=None, + web_app: WebAppInfo=None): self.text: str = text self.request_contact: bool = request_contact self.request_location: bool = request_location self.request_poll: KeyboardButtonPollType = request_poll + self.web_app: WebAppInfo = web_app def to_json(self): return json.dumps(self.to_dict()) @@ -1028,6 +1066,8 @@ class KeyboardButton(Dictionaryable, JsonSerializable): json_dict['request_location'] = self.request_location if self.request_poll is not None: json_dict['request_poll'] = self.request_poll.to_dict() + if self.web_app is not None: + json_dict['web_app'] = self.web_app.to_dict() return json_dict @@ -1122,13 +1162,17 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) obj = cls.check_json(json_string) if 'login_url' in obj: obj['login_url'] = LoginUrl.de_json(obj.get('login_url')) + if 'web_app' in obj: + obj['web_app'] = WebAppInfo.de_json(obj.get('web_app')) + return cls(**obj) - def __init__(self, text, url=None, callback_data=None, switch_inline_query=None, + def __init__(self, text, url=None, callback_data=None, web_app=None, switch_inline_query=None, switch_inline_query_current_chat=None, callback_game=None, pay=None, login_url=None, **kwargs): self.text: str = text self.url: str = url self.callback_data: str = callback_data + self.web_app: WebAppInfo = web_app self.switch_inline_query: str = switch_inline_query self.switch_inline_query_current_chat: str = switch_inline_query_current_chat self.callback_game = callback_game # Not Implemented @@ -1144,6 +1188,8 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) json_dict['url'] = self.url if self.callback_data: json_dict['callback_data'] = self.callback_data + if self.web_app: + json_dict['web_url'] = self.web_app.to_dict() if self.switch_inline_query is not None: json_dict['switch_inline_query'] = self.switch_inline_query if self.switch_inline_query_current_chat is not None: @@ -1235,7 +1281,7 @@ class ChatMember(JsonDeserializable): can_invite_users=None, can_pin_messages=None, is_member=None, can_send_messages=None, can_send_media_messages=None, can_send_polls=None, can_send_other_messages=None, can_add_web_page_previews=None, - can_manage_chat=None, can_manage_voice_chats=None, + can_manage_chat=None, can_manage_video_chats=None, until_date=None, **kwargs): self.user: User = user self.status: str = status @@ -1257,7 +1303,8 @@ class ChatMember(JsonDeserializable): self.can_send_other_messages: bool = can_send_other_messages self.can_add_web_page_previews: bool = can_add_web_page_previews self.can_manage_chat: bool = can_manage_chat - self.can_manage_voice_chats: bool = can_manage_voice_chats + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_manage_voice_chats: bool = self.can_manage_video_chats self.until_date: int = until_date @@ -1689,6 +1736,23 @@ class InlineQueryResultBase(ABC, Dictionaryable, JsonSerializable): return json_dict +class SentWebAppMessage(JsonDeserializable): + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + def __init__(self, inline_message_id): + self.inline_message_id = inline_message_id + + def to_dict(self): + return {'inline_message_id': self.inline_message_id} + + + + + class InlineQueryResultArticle(InlineQueryResultBase): def __init__(self, id, title, input_message_content, reply_markup=None, url=None, hide_url=None, description=None, thumb_url=None, thumb_width=None, thumb_height=None): @@ -2890,3 +2954,111 @@ class MessageAutoDeleteTimerChanged(JsonDeserializable): def __init__(self, message_auto_delete_time, **kwargs): self.message_auto_delete_time = message_auto_delete_time + + +class MenuButton(JsonDeserializable, JsonSerializable): + """ + Base class for MenuButtons. + """ + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + map = { + 'commands': MenuButtonCommands, + 'web_app': MenuButtonWebApp, + 'default': MenuButtonDefault + } + return map[obj['type']](**obj) + + def to_json(self): + raise NotImplementedError + + +class MenuButtonCommands(MenuButton): + + def __init__(self, type): + self.type = type + + def to_dict(self): + return {'type': self.type} + + def to_json(self): + return json.dumps(self.to_dict()) + +class MenuButtonWebApp(MenuButton): + + def __init__(self, type, text, web_app): + self.type: str = type + self.text: str = text + self.web_app: WebAppInfo = web_app + + def to_dict(self): + return {'type': self.type, 'text': self.text, 'web_app': self.web_app.to_dict()} + + def to_json(self): + return json.dumps(self.to_dict()) + +class MenuButtonDefault(MenuButton): + + def __init__(self, type): + self.type: str = type + + def to_dict(self): + return {'type': self.type} + + def to_json(self): + return json.dumps(self.to_dict()) + + +class ChatAdministratorRights(JsonDeserializable, JsonSerializable): + """ + Class representation of: + https://core.telegram.org/bots/api#chatadministratorrights + """ + + @classmethod + def de_json(cls, json_string): + if json_string is None: return None + obj = cls.check_json(json_string) + return cls(**obj) + + def __init__(self, is_anonymous: bool, can_manage_chat: bool, + can_delete_messages: bool, can_manage_video_chats: bool, can_restrict_members: bool, + can_promote_members: bool, can_change_info: bool, can_invite_users: bool, + can_post_messages: bool=None, can_edit_messages: bool=None, + can_pin_messages: bool=None) -> None: + + self.is_anonymous: bool = is_anonymous + self.can_manage_chat: bool = can_manage_chat + self.can_delete_messages: bool = can_delete_messages + self.can_manage_video_chats: bool = can_manage_video_chats + self.can_restrict_members: bool = can_restrict_members + self.can_promote_members: bool = can_promote_members + self.can_change_info: bool = can_change_info + self.can_invite_users: bool = can_invite_users + self.can_post_messages: bool = can_post_messages + self.can_edit_messages: bool = can_edit_messages + self.can_pin_messages: bool = can_pin_messages + + def to_dict(self): + data = { + 'is_anonymous': self.is_anonymous, + 'can_manage_chat': self.can_manage_chat, + 'can_delete_messages': self.can_delete_messages, + 'can_manage_video_chats': self.can_manage_video_chats, + 'can_restrict_members': self.can_restrict_members, + 'can_promote_members': self.can_promote_members, + 'can_change_info': self.can_change_info, + 'can_invite_users': self.can_invite_users, + 'can_post_messages': self.can_post_messages, + 'can_edit_messages': self.can_edit_messages, + 'can_pin_messages': self.can_pin_messages + } + return data + + def to_json(self): + return json.dumps(self.to_dict()) + + + diff --git a/tests/test_types.py b/tests/test_types.py index 43200a8..dedee19 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -245,3 +245,26 @@ def test_chat_member_updated(): assert cm_updated.old_chat_member.status == "member" assert cm_updated.new_chat_member.status == "administrator" + +def test_webhook_info(): + json_string = r'{"url": "https://example.com/webhook", "has_custom_certificate": true, "pending_update_count": 1, "last_error_date": 0, "last_error_message": "", "last_synchronization_error_date": 489309, "max_connections": 40, "allowed_updates": ["message"]}' + webhook_info = types.WebhookInfo.de_json(json_string) + print(webhook_info) + assert webhook_info.url == 'https://example.com/webhook' + assert webhook_info.has_custom_certificate is True + assert webhook_info.pending_update_count == 1 + assert webhook_info.last_error_date == 0 + assert webhook_info.last_error_message == '' + assert webhook_info.max_connections == 40 + assert webhook_info.last_synchronization_error_date == 489309 + assert webhook_info.allowed_updates == ['message'] + + +def test_sent_web_app_message(): + json_string = r'{"inline_message_id": "29430"}' + sent_web_app_message = types.SentWebAppMessage.de_json(json_string) + assert sent_web_app_message.inline_message_id == '29430' + + + + From b146df346dab963f9c574e59681a4f05a47e0861 Mon Sep 17 00:00:00 2001 From: coder2020official Date: Sun, 17 Apr 2022 16:46:38 +0500 Subject: [PATCH 3/4] Indentation fix to fit documentation. --- telebot/__init__.py | 6 +++--- telebot/async_telebot.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/telebot/__init__.py b/telebot/__init__.py index 55be7af..a33b265 100644 --- a/telebot/__init__.py +++ b/telebot/__init__.py @@ -2049,7 +2049,7 @@ class TeleBot: Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be changed + If not specified, default bot's menu button will be changed. :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault """ @@ -2065,7 +2065,7 @@ class TeleBot: Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be returned + If not specified, default bot's menu button will be returned. :return: types.MenuButton """ @@ -2100,7 +2100,7 @@ class TeleBot: :param for_channels: Pass True to get the default administrator rights of the bot in channels. Otherwise, the default administrator rights of the bot for groups and supergroups will be returned. :return: types.ChatAdministratorRights """ - + return types.ChatAdministratorRights.de_json(apihelper.get_my_default_administrator_rights(self.token, for_channels)) diff --git a/telebot/async_telebot.py b/telebot/async_telebot.py index 4dd78a7..a2fdd75 100644 --- a/telebot/async_telebot.py +++ b/telebot/async_telebot.py @@ -2667,7 +2667,7 @@ class AsyncTeleBot: Telegram documentation: https://core.telegram.org/bots/api#setchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be changed + If not specified, default bot's menu button will be changed. :param menu_button: A JSON-serialized object for the new bot's menu button. Defaults to MenuButtonDefault """ @@ -2683,7 +2683,7 @@ class AsyncTeleBot: Telegram Documentation: https://core.telegram.org/bots/api#getchatmenubutton :param chat_id: Unique identifier for the target private chat. - If not specified, default bot's menu button will be returned + If not specified, default bot's menu button will be returned. :return: types.MenuButton """ From 4812dcb02b69949e4fd003d95ad842d28a1e3e6f Mon Sep 17 00:00:00 2001 From: coder2020official Date: Fri, 22 Apr 2022 23:06:11 +0500 Subject: [PATCH 4/4] Fix typo in types.py --- telebot/types.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/telebot/types.py b/telebot/types.py index 307d7c5..c659fd4 100644 --- a/telebot/types.py +++ b/telebot/types.py @@ -1189,7 +1189,7 @@ class InlineKeyboardButton(Dictionaryable, JsonSerializable, JsonDeserializable) if self.callback_data: json_dict['callback_data'] = self.callback_data if self.web_app: - json_dict['web_url'] = self.web_app.to_dict() + json_dict['web_app'] = self.web_app.to_dict() if self.switch_inline_query is not None: json_dict['switch_inline_query'] = self.switch_inline_query if self.switch_inline_query_current_chat is not None: