From 45ff81afbcf643522e6af59279ff50aa3c0ffc1f Mon Sep 17 00:00:00 2001 From: Davte Date: Sun, 26 Apr 2020 19:14:24 +0200 Subject: [PATCH] Added a script to implement missing methods from Telegram website --- davtelepot/api_helper.py | 159 +++++++++++++++++++++++++++++++++------ 1 file changed, 136 insertions(+), 23 deletions(-) diff --git a/davtelepot/api_helper.py b/davtelepot/api_helper.py index 341954c..c2f98f7 100644 --- a/davtelepot/api_helper.py +++ b/davtelepot/api_helper.py @@ -1,6 +1,7 @@ -"""Get and parse Telegram API webpage.""" +"""Get and parse Telegram API web page.""" # Standard library modules +import argparse import asyncio import logging @@ -8,10 +9,20 @@ import logging import aiohttp from bs4 import BeautifulSoup +# Project modules +from . import api + api_url = "https://core.telegram.org/bots/api" class TelegramApiMethod(object): + types = { + 'Array of String': "List[str]", + 'Boolean': "bool", + 'Integer': "int", + 'Integer or String': "Union[int, str]", + 'String': "str", + } """Telegram bot API method.""" def __init__(self, name, description, table): @@ -19,6 +30,7 @@ class TelegramApiMethod(object): self._name = name self._description = description self._table = table + self._parameters = self.get_parameters_from_table() @property def name(self): @@ -35,7 +47,18 @@ class TelegramApiMethod(object): """Return method parameters table.""" return self._table - def get_parameters_from_table(self): + @property + def parameters(self): + return self._parameters + + @property + def parameters_with_types(self): + return [ + f"{parameter['name']}: {parameter['type']}" + for parameter in self._parameters + ] + + def print_parameters_table(self): """Extract parameters from API table.""" result = '' if self.table is None: @@ -54,28 +77,53 @@ class TelegramApiMethod(object): result += '\n' return result + def get_parameters_from_table(self): + if self.table is None: + return [] + parameters = [] + rows = self.table.tbody.find_all('tr') or [] + for row in rows: + columns = row.find_all('td') or [] + name, type_, *_ = map(lambda column: column.text.strip(), columns) + if type_ in self.types: + type_ = self.types[type_] + else: + type_ = f"'{type_}'" + parameters.append( + dict( + name=name, + type=type_ + ) + ) + return parameters -async def main(loop=None, filename=None): - """Get information from Telegram bot API webpage.""" + +async def print_api_methods(loop=None, + filename=None, + print_all=False, + output_file=None): + """Get information from Telegram bot API web page.""" if loop is None: loop = asyncio.get_event_loop() + implemented_methods = dir(api.TelegramBot) async with aiohttp.ClientSession( loop=loop, timeout=aiohttp.ClientTimeout( total=100 ) ) as session: - async with session.get( + async with session.get( api_url - ) as response: - webpage = BeautifulSoup( - await response.text(), - "html.parser" - ) + ) as response: + web_page = BeautifulSoup( + await response.text(), + "html.parser" + ) if filename is not None: with open(filename, 'w') as _file: - _file.write(webpage.decode()) - for method in webpage.find_all("h4"): + _file.write(web_page.decode()) + methods = [] + for method in web_page.find_all("h4"): method_name = method.text description = '' parameters_table = None @@ -89,18 +137,83 @@ async def main(loop=None, filename=None): break # Stop searching in siblings if is found description += tag.get_text() if method_name and method_name[0] == method_name[0].lower(): - method = TelegramApiMethod( - method_name, description, parameters_table - ) - print( - "NAME\n\t{m.name}\n" - "DESCRIPTION\n\t{m.description}\n" - f"TABLE\n\t{method.get_parameters_from_table()}\n\n".format( - m=method + methods.append( + TelegramApiMethod( + method_name, + description, + parameters_table ) ) + new_line = '\n' + 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" + ) + 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 + ) + else: + print( + '\n'.join( + f"NAME\n\t{method.name}\n" + f"PARAMETERS\n\t{', '.join(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 + ) + ) + + +def main(): + cli_parser = argparse.ArgumentParser( + description='Get Telegram API methods information from telegram ' + 'website.\n' + 'Implement missing (or --all) methods in --out file, ' + 'or print methods information.', + allow_abbrev=False, + ) + cli_parser.add_argument('--file', '-f', '--filename', type=str, + default=None, + required=False, + help='File path to store Telegram API web page') + cli_parser.add_argument('--all', '-a', + action='store_true', + help='Print all methods (default: print missing ' + 'methods only)') + cli_parser.add_argument('--out', '--output', '-o', type=str, + default=None, + required=False, + help='File path to store methods implementation') + cli_arguments = vars(cli_parser.parse_args()) + filename = cli_arguments['file'] + print_all = cli_arguments['all'] + output_file = cli_arguments['out'] + loop = asyncio.get_event_loop() + loop.run_until_complete( + print_api_methods(loop=loop, + filename=filename, + print_all=print_all, + output_file=output_file) + ) + logging.info("Done!") + if __name__ == '__main__': - loop = asyncio.get_event_loop() - loop.run_until_complete(main(loop=loop, filename='prova.txt')) - logging.info("Done!") + main()