diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 00000000..46f739f0
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,4 @@
+constructors/
+functions/
+types/
+core/
diff --git a/docs/css/docs.css b/docs/css/docs.css
new file mode 100644
index 00000000..3ac8a3e0
--- /dev/null
+++ b/docs/css/docs.css
@@ -0,0 +1,103 @@
+body {
+ font-family: 'Nunito', sans-serif;
+ color: #333;
+ background-color:#fff;
+ font-size: 16px;
+}
+
+a {
+ color: #42aaed;
+ text-decoration: none;
+}
+
+pre {
+ font-family: 'Space Mono', monospace;
+ padding: 8px;
+ color: #567;
+ background: #f0f4f8;
+ border-radius: 0;
+ overflow-x: auto;
+}
+
+a:hover {
+ color: #64bbdd;
+ text-decoration: underline;
+}
+
+table {
+ width: 100%;
+ max-width: 100%;
+}
+
+table td {
+ border-top: 1px solid #eee;
+ padding: 8px;
+}
+
+.horizontal {
+ margin-bottom: 16px;
+ list-style: none;
+ background: #f0f4f8;
+ border-radius: 4px;
+ padding: 8px 16px;
+}
+
+.horizontal li {
+ display: inline-block;
+ margin: 0 8px 0 0;
+}
+
+.horizontal img {
+ display: inline-block;
+ margin: 0 8px -2px 0;
+}
+
+h1 {
+ font-size: 24px;
+}
+
+h3 {
+ font-size: 20px;
+}
+
+#main_div {
+ padding: 20px 0;
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+pre::-webkit-scrollbar {
+ visibility: visible;
+ display: block;
+ height: 12px;
+}
+
+pre::-webkit-scrollbar-track:horizontal {
+ background: #def;
+ border-radius: 0;
+ height: 12px;
+}
+
+pre::-webkit-scrollbar-thumb:horizontal {
+ background: #bdd;
+ border-radius: 0;
+ height: 12px;
+}
+
+@media (max-width: 640px) {
+ h1 {
+ font-size: 18px;
+ }
+ h3 {
+ font-size: 16px;
+ }
+
+ #dev_page_content_wrap {
+ padding-top: 12px;
+ }
+
+ #dev_page_title {
+ margin-top: 10px;
+ margin-bottom: 20px;
+ }
+}
diff --git a/docs/generate.py b/docs/generate.py
new file mode 100644
index 00000000..5f110dbf
--- /dev/null
+++ b/docs/generate.py
@@ -0,0 +1,291 @@
+import os
+import re
+import sys
+
+# Small trick so importing telethon_generator works
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
+
+from telethon_generator.parser import TLParser, TLObject
+
+
+# TLObject -> hypertext formatted code
+def write_code(file, tlobject, filename):
+ """Writes the code for the given 'tlobject' to the 'file' handle with hyperlinks,
+ using 'filename' as the file from which the relative links should work"""
+
+ # Write the function or type and its ID
+ if tlobject.namespace:
+ file.write(tlobject.namespace)
+ file.write('.')
+
+ file.write(tlobject.name)
+ file.write('#')
+ file.write(hex(tlobject.id)[2:].rjust(8, '0'))
+
+ # Write all the arguments (or do nothing if there's none)
+ for arg in tlobject.args:
+ file.write(' ')
+
+ # "Opening" modifiers
+ if arg.generic_definition:
+ file.write('{')
+
+ # Argument name
+ file.write(arg.name)
+ file.write(':')
+
+ # "Opening" modifiers
+ if arg.is_flag:
+ file.write('flags.%d?' % arg.flag_index)
+
+ if arg.is_generic:
+ file.write('!')
+
+ if arg.is_vector:
+ file.write('Vector <' % get_path_for_type('vector', relative_to=filename))
+
+ # Argument type
+ if arg.type:
+ file.write('%s ' % arg.type)
+ else:
+ file.write('#')
+
+ # "Closing" modifiers
+ if arg.is_vector:
+ file.write('>')
+
+ if arg.generic_definition:
+ file.write('}')
+
+ # Now write the resulting type (result from a function, or type for a constructor)
+ file.write(' = %s ' % tlobject.result)
+
+
+# TLObject -> Python class name
+def get_class_name(tlobject):
+ """Gets the class name following the Python style guidelines, in ThisClassFormat"""
+ # Courtesy of http://stackoverflow.com/a/31531797/4759433
+ result = re.sub(r'_([a-z])', lambda m: m.group(1).upper(), tlobject.name)
+
+ # Replace '_' with '' once again to make sure it doesn't appear on the name
+ result = result[:1].upper() + result[1:].replace('_', '')
+
+ # If it's a function, let it end with "Request" to identify them more easily
+ if tlobject.is_function:
+ result += 'Request'
+
+ return result
+
+
+# TLObject -> filename
+def get_file_name(tlobject, add_extension=False):
+ """Gets the file name in file_name_format.html for the given TLObject.
+ Only its name may also be given if the full TLObject is not available"""
+ if isinstance(tlobject, TLObject):
+ name = tlobject.name
+ else:
+ name = tlobject
+
+ # Courtesy of http://stackoverflow.com/a/1176023/4759433
+ s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
+ result = re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
+ if add_extension:
+ return result + '.html'
+ else:
+ return result
+
+
+def get_create_path_for(tlobject):
+ """Gets the file path (and creates the parent directories)
+ for the given 'tlobject', relative to nothing; only its local path"""
+
+ # Determine the output directory
+ out_dir = 'functions' if tlobject.is_function else 'constructors'
+
+ if tlobject.namespace:
+ out_dir = os.path.join(out_dir, tlobject.namespace)
+
+ # Ensure that it exists
+ os.makedirs(out_dir, exist_ok=True)
+
+ # Return the resulting filename
+ return os.path.join(out_dir, get_file_name(tlobject, add_extension=True))
+
+
+def get_path_for_type(type, relative_to):
+ """Similar to getting the path for a TLObject, it might not be possible
+ to have the TLObject itself but rather its name (the type);
+ this method works in the same way, returning a relative path"""
+ if type.lower() in {'int', 'long', 'int128', 'int256', 'double',
+ 'vector', 'string', 'bool', 'true', 'bytes', 'date'}:
+ path = 'core/index.html#%s' % type.lower()
+
+ elif '.' in type:
+ # If it's not a core type, then it has to be a custom Telegram type
+ namespace, name = type.split('.')
+ path = 'types/%s/%s' % (namespace, get_file_name(name, add_extension=True))
+ else:
+ path = 'types/%s' % get_file_name(type, add_extension=True)
+
+ return get_relative_path(path, relative_to)
+
+
+# Destination path from the current position -> relative to the given path
+def get_relative_path(destination, relative_to):
+ if os.path.isfile(relative_to):
+ relative_to = os.path.dirname(relative_to)
+
+ return os.path.relpath(destination, start=relative_to)
+
+
+def get_relative_paths(original, relative_to):
+ """Converts the dictionary of 'original' paths to relative paths
+ starting from the given 'relative_to' file"""
+ return {k: get_relative_path(v, relative_to) for k, v in original.items()}
+
+
+def generate_documentation(scheme_file):
+ """Generates the documentation HTML files from from scheme.tl to /functions and /types"""
+ original_paths = {
+ 'css': 'css/docs.css',
+ 'arrow': 'img/arrow.svg',
+ 'index_all': 'core/index.html',
+ 'index_types': 'types/index.html',
+ 'index_functions': 'functions/index.html',
+ 'index_constructors': 'constructors/index.html'
+ }
+
+ tlobjects = tuple(TLParser.parse_file(scheme_file))
+
+ # First write the functions and the available constructors
+ for tlobject in tlobjects:
+ filename = get_create_path_for(tlobject)
+
+ # Determine the relative paths for this file
+ paths = get_relative_paths(original_paths, relative_to=filename)
+
+ with open(filename, 'w', encoding='utf-8') as file:
+ file.write('''
+
+
+
+ ''')
+
+ # Let the page title be the same as the class name for this object
+ file.write(get_class_name(tlobject))
+
+ file.write('''
+
+
+
+
+
+
+
')
+
+ # Body title, again the class name
+ file.write(get_class_name(tlobject))
+
+ file.write(' ')
+
+ # Is it listed under functions or under types?
+ file.write('
---')
+ file.write('functions' if tlobject.is_function else 'types')
+ file.write('---\n')
+
+ write_code(file, tlobject, filename=filename)
+
+ file.write(' ')
+
+ file.write('
')
+ file.write('Parameters' if tlobject.is_function else 'Members')
+ file.write(' ')
+
+ # Sort the arguments in the same way they're sorted on the generated code (flags go last)
+ args = sorted([a for a in tlobject.args if
+ not a.flag_indicator and not a.generic_definition],
+ key=lambda a: a.is_flag)
+ if args:
+ # Writing parameters
+ file.write('
')
+
+ for arg in args:
+ file.write('')
+
+ # Name
+ file.write('')
+ file.write(arg.name)
+ file.write(' ')
+
+ # Type
+ file.write('%s ' % arg.type)
+
+ # Description
+ file.write('')
+ if arg.is_vector:
+ file.write('A list must be supplied for this argument. ')
+
+ if arg.is_generic:
+ file.write('A different MTProtoRequest must be supplied for this argument. ')
+
+ if arg.is_flag:
+ file.write('This argument can be omitted. ')
+
+ file.write(' ')
+ file.write(' ')
+
+ file.write('
')
+ else:
+ if tlobject.is_function:
+ file.write('
This request takes no input parameters.
')
+ else:
+ file.write('
This type has no members.
')
+
+ file.write('
')
+
+ # TODO Explain the difference between functions, types and constructors
+ # TODO Write the available types, listing the available constructors for each
+ # TODO Write index.html for every sub-folder (functions/, types/ and constructors/) as well as sub-namespaces
+ # TODO Write the core/index.html containing the core types
+
+
+if __name__ == '__main__':
+ print('Generating documentation...')
+ generate_documentation('../telethon_generator/scheme.tl')
+ print('Done.')
diff --git a/docs/img/arrow.svg b/docs/img/arrow.svg
new file mode 100644
index 00000000..1e131224
--- /dev/null
+++ b/docs/img/arrow.svg
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+