Queer European MD passionate about IT
Przeglądaj źródła

Added a script to implement missing methods from Telegram website

Davte 4 lat temu
rodzic
commit
45ff81afbc
1 zmienionych plików z 135 dodań i 22 usunięć
  1. 135 22
      davtelepot/api_helper.py

+ 135 - 22
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 <table> 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
+            )
+        )
 
-if __name__ == '__main__':
+
+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(main(loop=loop, filename='prova.txt'))
+    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__':
+    main()