diff --git a/davtelepot/api.py b/davtelepot/api.py index bd88fc7..ca2e690 100644 --- a/davtelepot/api.py +++ b/davtelepot/api.py @@ -1464,3 +1464,75 @@ class TelegramBot: 'setStickerSetThumb', parameters=locals() ) + + async def logOut(self): + """Log out from the cloud Bot API server. + + Use this method to log out from the cloud Bot API server + before launching the bot locally. + You must log out the bot before running it locally, otherwise there + is no guarantee that the bot will receive updates. + After a successful call, you can immediately log in on a local server, + but will not be able to log in back to the cloud Bot API server + for 10 minutes. + Returns True on success. Requires no parameters. + See https://core.telegram.org/bots/api#logout for details. + """ + return await self.api_request( + 'logOut', + parameters=locals() + ) + + async def close(self): + """Close bot instance in local server. + + Use this method to close the bot instance before moving it from one + local server to another. + You need to delete the webhook before calling this method to ensure + that the bot isn't launched again after server restart. + The method will return error 429 in the first 10 minutes after the + bot is launched. Returns True on success. + Requires no parameters. + See https://core.telegram.org/bots/api#close for details. + """ + return await self.api_request( + 'close', + parameters=locals() + ) + + async def copyMessage(self, chat_id: Union[int, str], + from_chat_id: Union[int, str], + message_id: int, + caption: str = None, + parse_mode: str = None, + caption_entities: list = None, + disable_notification: bool = None, + reply_to_message_id: int = None, + allow_sending_without_reply: bool = None, + reply_markup=None): + """Use this method to copy messages of any kind. + + The method is analogous to the method forwardMessages, but the copied + message doesn't have a link to the original message. + Returns the MessageId of the sent message on success. + See https://core.telegram.org/bots/api#copymessage for details. + """ + return await self.api_request( + 'copyMessage', + parameters=locals() + ) + + async def unpinAllChatMessages(self, chat_id: Union[int, str]): + """Use this method to clear the list of pinned messages in a chat. + + If the chat is not a private chat, the bot must be an administrator + in the chat for this to work and must have the 'can_pin_messages' + admin right in a supergroup or 'can_edit_messages' admin right in a + channel. + Returns True on success. + See https://core.telegram.org/bots/api#unpinallchatmessages for details. + """ + return await self.api_request( + 'unpinAllChatMessages', + parameters=locals() + ) diff --git a/davtelepot/api_helper.py b/davtelepot/api_helper.py index 4a7e5cc..bc7f840 100644 --- a/davtelepot/api_helper.py +++ b/davtelepot/api_helper.py @@ -3,9 +3,13 @@ # Standard library modules import argparse import asyncio +import inspect import logging # Third party modules +import os +from typing import List + import aiohttp from bs4 import BeautifulSoup @@ -52,7 +56,7 @@ class TelegramApiMethod(object): return self._parameters @property - def parameters_with_types(self): + def parameters_with_types(self) -> List[str]: return [ f"{parameter['name']}: {parameter['type']}" for parameter in self._parameters @@ -101,22 +105,30 @@ class TelegramApiMethod(object): async def print_api_methods(loop=None, filename=None, print_all=False, - output_file=None): + output_file=None, + input_file=None): """Get information from Telegram bot API web page.""" if loop is None: loop = asyncio.get_event_loop() implemented_methods = dir(TelegramBot) - async with aiohttp.ClientSession( - loop=loop, - timeout=aiohttp.ClientTimeout( - total=100 - ) - ) as session: - async with session.get( - api_url - ) as response: + if input_file is None or not os.path.isfile(input_file): + async with aiohttp.ClientSession( + loop=loop, + timeout=aiohttp.ClientTimeout( + total=100 + ) + ) as session: + async with session.get( + api_url + ) as response: + web_page = BeautifulSoup( + await response.text(), + "html.parser" + ) + else: + with open(input_file, 'r') as local_web_page: web_page = BeautifulSoup( - await response.text(), + ''.join(local_web_page.readlines()), "html.parser" ) if filename is not None: @@ -146,40 +158,84 @@ async def print_api_methods(loop=None, ) ) new_line = '\n' + new_methods = [] + edited_methods = [] + for method in methods: + if print_all or method.name not in implemented_methods: + new_methods.append(method) + else: + parameters = set(parameter['name'] for parameter in method.parameters) + implemented_parameters = set( + parameter.strip('_') # Parameter `type` becomes `type_` in python + for parameter in inspect.signature( + getattr(TelegramBot, + method.name) + ).parameters.keys() + if parameter != 'self' + ) + new_parameters = parameters - implemented_parameters + deprecated_parameters = implemented_parameters - parameters + if new_parameters or deprecated_parameters: + edited_methods.append( + dict( + name=method.name, + new_parameters=new_parameters, + deprecated_parameters=deprecated_parameters + ) + ) if output_file: with open(output_file, 'w') as file: - file.write( - "from typing import List, Union\n" - "from davtelepot.api import TelegramBot\n" - "self = TelegramBot('fake_token')\n\n\n" - ) + if new_methods: + file.write( + "from typing import List, Union\n" + "from davtelepot.api import TelegramBot\n\n\n" + "# noinspection PyPep8Naming\n" + "class Bot(TelegramBot):\n\n" + ) file.writelines( - f"async def {method.name}(" - f"{', '.join(method.parameters_with_types)}" - "):\n" - " \"\"\"" - f"{method.description.replace(new_line, new_line + ' ' * 4)}\n" - " See https://core.telegram.org/bots/api#" - f"{method.name.lower()} for details.\n" - " \"\"\"\n" - " return await self.api_request(\n" - f" '{method.name}',\n" - " parameters=locals()\n" - " )\n\n\n" - for method in methods - if print_all or method.name not in implemented_methods + f" async def {method.name}(" + f"{', '.join(['self'] + method.parameters_with_types)}" + f"):\n" + f" \"\"\"" + f" {method.description.replace(new_line, new_line + ' ' * 4)}\n" + f" See https://core.telegram.org/bots/api#" + f" {method.name.lower()} for details.\n" + f" \"\"\"\n" + f" return await self.api_request(\n" + f" '{method.name}',\n" + f" parameters=locals()\n" + f" )\n\n" + for method in new_methods ) + if edited_methods: + file.write('\n# === EDITED METHODS ===\n') + for method in edited_methods: + file.write(f'\n"""{method["name"]}\n') + if method['new_parameters']: + file.write(" New parameters: " + + ", ".join(method['new_parameters']) + + "\n") + if method['deprecated_parameters']: + file.write(" Deprecated parameters: " + + ", ".join(method['deprecated_parameters']) + + "\n") + file.write('"""\n') else: print( '\n'.join( f"NAME\n\t{method.name}\n" - f"PARAMETERS\n\t{', '.join(method.parameters_with_types)}\n" + f"PARAMETERS\n\t{', '.join(['self'] + method.parameters_with_types)}\n" f"DESCRIPTION\n\t{method.description}\n" f"TABLE\n\t{method.print_parameters_table()}\n\n" - for method in methods - if print_all or method.name not in implemented_methods + for method in new_methods ) ) + for method in edited_methods: + print(method['name']) + if method['new_parameters']: + print("\tNew parameters: " + ", ".join(method['new_parameters'])) + if method['deprecated_parameters']: + print("\tDeprecated parameters: " + ", ".join(method['deprecated_parameters'])) def main(): @@ -202,16 +258,22 @@ def main(): default=None, required=False, help='File path to store methods implementation') + cli_parser.add_argument('--in', '--input', '-i', type=str, + default=None, + required=False, + help='File path to read Telegram API web page') cli_arguments = vars(cli_parser.parse_args()) filename = cli_arguments['file'] print_all = cli_arguments['all'] output_file = cli_arguments['out'] + input_file = cli_arguments['in'] loop = asyncio.get_event_loop() loop.run_until_complete( print_api_methods(loop=loop, filename=filename, print_all=print_all, - output_file=output_file) + output_file=output_file, + input_file=input_file) ) logging.info("Done!")