diff --git a/DEVELOPING.md b/DEVELOPING.md
index 5e7d7799..43778762 100644
--- a/DEVELOPING.md
+++ b/DEVELOPING.md
@@ -8,13 +8,15 @@ python tools/codegen.py
Formatting, type-checking and testing:
```sh
-pip install isort black mypy pytest pytest-asyncio
+pip install -e client/[dev]
python tools/check.py
```
-Documentation:
+Documentation (requires [sphinx](https://www.sphinx-doc.org) and [graphviz](https://www.graphviz.org)'s `dot`):
```sh
-pip install sphinx_rtd_theme
+pip install -e client/[doc]
python tools/docgen.py
```
+
+Note that multiple optional dependency sets can be specified by separating them with a comma (`[dev,doc]`).
diff --git a/client/doc/basic/installation.rst b/client/doc/basic/installation.rst
new file mode 100644
index 00000000..0abde3ac
--- /dev/null
+++ b/client/doc/basic/installation.rst
@@ -0,0 +1,59 @@
+Installation
+============
+
+Telethon is a Python 3 library, which means you need to download and install Python to use it.
+Installing Python, using virtual environments, and the basics of the language, are outside of the scope of this guide.
+
+You can find the official resources to `download Python `_,
+learn about the `Python Setup and Usage `_ for different platforms,
+or follow the `The Python Tutorial `_ to learn the basics.
+These are not necessarily the best resources to learn, but they are official.
+Be sure to search online if you prefer learning in video form or otherwise.
+
+You can confirm that you have Python installed with:
+
+.. code-block:: shell
+
+ python --version
+
+Which should print something similar to ``Python 3.11.5`` (or newer).
+
+
+Installing the latest stable version
+------------------------------------
+
+Once you have a working Python 3 installation, you can install or upgrade the ``telethon`` package with ``pip``:
+
+.. code-block:: shell
+
+ python -m pip install --upgrade telethon
+
+Be sure to use lock-files if your project depends on a specific, older version of the library!
+
+
+Installing development versions
+-------------------------------
+
+If you want the *latest* unreleased changes, you can run the following command instead:
+
+.. code-block:: shell
+
+ python -m pip install --upgrade https://github.com/LonamiWebs/Telethon/archive/v2.zip
+
+.. note::
+
+ The development version may have bugs and is not recommended for production use.
+ However, when you are `reporting a library bug `,
+ you must reproduce the issue in this version before reporting the problem.
+
+
+Verifying the installation
+--------------------------
+
+To verify that the library is installed correctly, run the following command:
+
+.. code-block:: shell
+
+ python -c "import telethon; print(telethon.__version__)"
+
+The version number of the library should show in the output.
diff --git a/client/doc/basic/next-steps.rst b/client/doc/basic/next-steps.rst
new file mode 100644
index 00000000..4c1e7abf
--- /dev/null
+++ b/client/doc/basic/next-steps.rst
@@ -0,0 +1,32 @@
+Next steps
+==========
+
+.. currentmodule:: telethon
+
+By now, you should have successfully gone through both the :doc:`installation` and :doc:`signing-in` processes.
+
+With a :class:`Client` instance connected and authorized, you can send any request to Telegram.
+Some requests are bot-specific, and some are user-specific, but most can be used by any account.
+You will need to have the correct permissions and pass valid parameters, but after that, your imagination is the limit.
+
+Telethon features extensive documentation for every public item offered by the library.
+All methods within the :class:`Client` also contain one or more examples on how to use them.
+
+Whatever you build, remember to comply with both `Telegram's Terms of Service `_
+and `Telegram's API ToS `_.
+There are `several requests that applications must make `_:
+
+.. epigraph::
+
+ […] when logging in as an existing user, apps are supposed to call :tl:`help.getTermsOfServiceUpdate`
+ to check for any updates to the Terms of Service;
+ this call should be repeated after ``expires`` seconds have elapsed.
+ If an update to the Terms Of Service is available, clients are supposed to show a consent popup;
+ if accepted, clients should call :tl:`help.acceptTermsOfService`,
+ providing the ``termsOfService id`` JSON object;
+ in case of denial, clients are to delete the account using :tl:`account.deleteAccount`,
+ providing Decline ToS update as deletion reason.
+
+The library will not make these calls for you, as it cannot know how users interact with the application being developed.
+If you use an official client alongside the application you are developing,
+it should be safe to rely on that client making the requests instead.
diff --git a/client/doc/basic/signing-in.rst b/client/doc/basic/signing-in.rst
new file mode 100644
index 00000000..9adb91e4
--- /dev/null
+++ b/client/doc/basic/signing-in.rst
@@ -0,0 +1,211 @@
+Signing in
+==========
+
+.. currentmodule:: telethon
+
+Most of Telegram's API methods are gated behind an account login.
+But before you can interact with the API at all, you will need to obtain an API ID and hash pair for your application.
+
+
+Registering your Telegram application
+-------------------------------------
+
+Before working with Telegram's API, you (as the application developer) need to get an API ID and hash:
+
+1. `Login to your Telegram account `_ with the phone number of the developer account to use.
+
+2. Click under *API Development tools*.
+
+3. A *Create new application* window will appear. Fill in your application details.
+ There is no need to enter any *URL*, and only the first two fields (*App title* and *Short name*) can currently be changed later.
+
+4. Click on *Create application* at the end.
+ Remember that your **API hash is secret** and Telegram won't let you revoke it.
+ Don't post it anywhere!
+
+This API ID and hash can now be used to develop an application using Telegram's API.
+Telethon consumes this API ID and hash in order to make the requests to Telegram.
+
+It is important to note that this API ID and hash is attached to a developer account,
+and can be used to develop applications or otherwise using libraries such as Telethon.
+
+The *users* of the application you develop do *not* need to provide their own API ID and hash.
+The API ID and hash values are meant to be hardcoded in the application.
+Any user is then able to login with just their phone number or bot token, even if they have not registered an application themselves.
+
+.. important::
+
+ The API ID and hash are meant to be *secret*, but Python is often distributed in source-code form.
+ These two things conflict with eachother!
+ You can opt to obfuscate the values somehow, or perhaps distribute an executable binary file instead.
+ Depending on what you are developing, it might be reasonable to expect users to provide their own API ID and hash instead.
+
+ Official applications *also* must embed the API ID and hash, but these are often distributed as binary files.
+ Whatever you do, **do not use other people's API ID and hash!**
+ Telegram may detect this as suspicious and ban the accounts.
+
+If you receive an error, Telegram is likely blocking the registration of a new applications.
+The best you can do is wait and try again later.
+If the issue persists, you may try contacting them, using a proxy or using a VPN.
+Be aware that some phone numbers are not eligible to register applications with.
+
+
+Interactive login
+-----------------
+
+The library offers a method for "quick and dirty" scripts known as :meth:`~Client.interactive_login`.
+This method will first check whether the account was previously logged-in, and if not, ask for a phone number to be input.
+
+You can write the code in a file (such as ``hello.py``) and then run it, or use the built-in ``asyncio``-enabled REPL.
+For this tutorial, we'll be using the ``asyncio`` REPL:
+
+.. code-block:: shell
+
+ python -m asyncio
+
+.. important::
+
+ If you opt to write your code in a file, do **not** call your script ``telethon.py``!
+ Python will try to import from there and it will fail with an error such as "ImportError: cannot import name ...".
+
+The first thing we need to do is import the :class:`Client` class and create an instance of it:
+
+.. code-block:: python
+
+ from telethon import Client
+
+ client = Client('name', 12345, '0123456789abcdef0123456789abcdef')
+
+The second and third parameters must be the API ID and hash, respectively.
+We have a client instance now, but we can't send requests to Telegram until we connect!
+So the next step is to :meth:`~Client.connect`:
+
+.. code-block:: python
+
+ await client.connect()
+
+If all went well, you will have connected to one of Telegram's servers.
+If you run into issues, you might need to try a different hosting provider or use some sort of proxy.
+
+Once you're connected, we can begin the :meth:`~Client.interactive_login`:
+
+.. code-block:: python
+
+ await client.interactive_login()
+
+Do as the prompts say on the terminal, and you will have successfully logged-in!
+
+Once you're done, make sure to :meth:`~Client.disconnect` for a graceful shutdown.
+
+
+Manual login
+------------
+
+We've talked about the second and third parameters of the :class:`Client` constructor, but not the first:
+
+.. code-block:: python
+
+ client = Client('name', 12345, '0123456789abcdef0123456789abcdef')
+
+The first parameter is the "session".
+When using a string or a :class:`~pathlib.Path`, the library will create a SQLite database in that path.
+The session path can contain directory separators and live anywhere in the file system.
+Telethon will automatically append the ``.session`` extension if you don't provide any.
+
+Briefly, the session contains some of the information needed to connect to Telegram.
+This includes the datacenter belonging to the account logged-in, and the authorization key used for encryption, among other things.
+
+.. important::
+
+ **Do not leak the session file!**
+ Anyone with that file can login to the account stored in it.
+ If you believe someone else has obtained this file, immediately revoke all active sessions from an official client.
+
+Let's take a look at what :meth:`~Client.interactive_login` does under the hood.
+
+1. First, it's using an equivalent of :meth:`~Client.is_authorized` to check whether the session was logged-in previously.
+2. Then, it will either :meth:`~Client.bot_sign_in` with a bot token or :meth:`~Client.request_login_code` with a phone number.
+
+ * If it logged-in as a bot account, a :class:`~types.User` is returned and we're done.
+ * Otherwise, a login code was sent. Go to step 3.
+
+3. Attempt to complete the user sign-in with :meth:`~Client.sign_in`, by entering the login code.
+
+ * If a :class:`~types.User` is returned, we're done.
+ * Otherwise, a 2FA password is required. Go to step 4.
+
+4. Use :meth:`Client.check_password` to check that the password is correct.
+
+ * If the password is correct, :class:`~types.User` is returned and we're done.
+
+Put into code, a user can thus login as follows:
+
+.. code-block:: python
+
+ from telethon import Client
+ from telethon.types import User
+
+ # SESSION, API_ID, API_HASH should be previously defined in your code
+ async with Client(SESSION, API_ID, API_HASH) as client:
+ if not await client.is_authorized():
+ phone = input('phone: ')
+ login_token = await client.request_login_code(phone_or_token)
+
+ code = input('code: ')
+ user_or_token = await client.sign_in(login_token, code)
+
+ if isinstance(user_or_token, User):
+ return user_or_token
+
+ # user_or_token is PasswordToken
+ password_token = user_or_token
+
+ import getpass
+ password = getpass.getpass("password: ")
+ user = await client.check_password(password_token, password)
+ return user
+
+A bot account does not need to request login code and cannot have passwords, so the login flow is much simpler:
+
+.. code-block:: python
+
+ from telethon import Client
+
+ # SESSION, API_ID, API_HASH should be previously defined in your code
+ async with Client(SESSION, API_ID, API_HASH) as client:
+ bot_token = input('token: ')
+ bot_user = await client.bot_sign_in(bot_token)
+ return bot_user
+
+To get a bot account, you need to talk with `@BotFather `_.
+
+You may have noticed the ``async with`` keywords.
+The :class:`Client` can be used in a context-manager.
+This will automatically call :meth:`Client.connect` and :meth:`Client.disconnect` for you.
+
+A good way to structure your code is as follows:
+
+.. code-block:: python
+
+ import asyncio
+ from telethon import Client
+
+ SESSION = ...
+ API_ID = ...
+ API_HASH = ...
+
+ async def main():
+ async with Client(SESSION, API_ID, API_HASH) as client:
+ ... # use client to your heart's content
+
+ if __name__ == '__main__':
+ asyncio.run(main())
+
+This way, both the :mod:`asyncio` event loop and the :class:`Client` will exit cleanly.
+Otherwise, you might run into errors such as tasks being destroyed while pending.
+
+.. note::
+
+ Once a :class:`Client` instance has been connected, you cannot change the :mod:`asyncio` event loop.
+ Methods like :func:`asyncio.run` setup and tear-down a new event loop every time.
+ If the loop changes, the client is likely to be "stuck" because its loop cannot advance.
diff --git a/client/doc/concepts/botapi-vs-mtproto.rst b/client/doc/concepts/botapi-vs-mtproto.rst
new file mode 100644
index 00000000..da8b12dd
--- /dev/null
+++ b/client/doc/concepts/botapi-vs-mtproto.rst
@@ -0,0 +1,117 @@
+HTTP Bot API vs MTProto
+=======================
+
+.. currentmodule:: telethon
+
+Telethon is more than capable to develop bots for Telegram.
+If you haven't decided which wrapper library for bots to use yet,
+using Telethon from the beginning may save you some headaches later.
+
+
+What is Bot API?
+----------------
+
+`Telegram's HTTP Bot API `_,
+from now on referred to as simply "Bot API", is Telegram's official way for developers to control their own Telegram bots.
+Quoting their main page:
+
+.. epigraph::
+
+ The Bot API is an HTTP-based interface created for developers keen on building bots for Telegram.
+
+ To learn how to create and set up a bot, please consult our
+ `Introduction to Bots `_
+ and `Bot FAQ `_.
+
+Bot API is simply an HTTP endpoint offering a custom HTTP API.
+Underneath, it uses `tdlib `_ to talk to Telegram's servers.
+
+You can configure your bot details via `@BotFather `_.
+This includes name, commands, and auto-completion.
+
+
+What is MTProto?
+----------------
+
+`MTProto `_ stands for "Mobile Transport Protocol".
+It is the language that the Telegram servers "speak".
+You can think of it as an alternative to HTTP.
+
+Telegram offers multiple APIs.
+All user accounts must use the API offered via MTProto.
+We will call this API the "MTProto API".
+This is the canonical Telegram API.
+
+The MTProto API is different from Bot API, but bot accounts can use either in the same way.
+In fact, the Bot API is implemented to use the MTProto API to map the requests and responses.
+
+Telethon implements the MTProto and offers classes and methods that can be called to send requests.
+In Telethon, all the methods and types generated from Telegram's API definitions are also known as :term:`Raw API`.
+This name was chosen because it gives you "raw" access to the MTProto API.
+Telethon's :class:`Client` and other custom types are implemented using the :term:`Raw API`.
+
+
+Advantages of MTProto over Bot API
+----------------------------------
+
+MTProto clients (like Telethon) connect directly to Telegram's servers via TCP or UDP.
+There is no HTTP connection, no "polling", and no "web hooks".
+We can compare the two visually:
+
+.. graphviz::
+ :caption: Communication between a Client and the Bot API
+
+ digraph botapi {
+ rankdir=LR;
+ "Client" -> "HTTP API";
+ "HTTP API" -> "MTProto API";
+ "MTProto API" -> "Telegram Servers";
+
+ "Telegram Servers" -> "MTProto API" [label="IPC"];
+ "MTProto API" -> "HTTP API" [label="MTProto"];
+ "HTTP API" -> "Client" [label="JSON"];
+ }
+
+.. graphviz::
+ :caption: Communication between a Client and the MTProto API
+
+ digraph botapi {
+ rankdir=LR;
+ "Client" -> "MTProto API";
+ "MTProto API" -> "Telegram Servers";
+
+ "Telegram Servers" -> "MTProto API" [label="IPC"];
+ "MTProto API" -> "Client" [label="MTProto"];
+ }
+
+When interacting with the MTProto API directly, we can cut down one intermediary (the HTTP API).
+This is less theoretical overhead and latency.
+It also means that, even if the Bot API endpoint is down, talking to the MTProto API could still work.
+
+The methods offered by the Bot API map to some of the methods in the MTProto API, but not all.
+The Bot API is its own abstraction, and chooses to expose less details.
+By talking to the MTProto API directly, you unlock the `full potential `_.
+
+The serialization format used by MTProto is more compact than JSON and can still be compressed.
+
+Another benefit of avoiding the Bot API is the ease to switch to user accounts instead of bots.
+The MTProto API is the same for users and bots, so by using Telethon, you don't need to learn to use a second library.
+
+
+Migrating from Bot API to Telethon
+----------------------------------
+
+If the above points convinced you to switch to Telethon, the following short guides should help you make the switch!
+
+It doesn't matter if you wrote your bot with `requests `_
+and you were making API requests manually, or if you used a wrapper library like
+`python-telegram-bot `_
+or `pyTelegramBotAPI `.
+You will surely be pleased with Telethon!
+
+If you were using an asynchronous library like `aiohttp `_
+or a wrapper like `aiogram `_, the switch will be even easier.
+
+
+Migrating from TODO
+^^^^^^^^^^^^^^^^^^^
diff --git a/client/doc/concepts/chats.rst b/client/doc/concepts/chats.rst
new file mode 100644
index 00000000..cabd1204
--- /dev/null
+++ b/client/doc/concepts/chats.rst
@@ -0,0 +1,108 @@
+Chats
+=====
+
+.. currentmodule:: telethon
+
+The term :term:`chat` is extremely overloaded, so it's no surprise many are confused by what it means.
+This section should hopefully clear that up.
+
+
+Telethon Chat
+-------------
+
+The word :term:`chat` in Telethon is used to refer a place where messages are sent to.
+Therefore, a Telethon :term:`chat` can be another user, a bot, a group, or a broadcast channel.
+All of those are places where messages can be sent.
+
+Of course, chats do more things than contain messages.
+They often have a name, username, photo, description, and other information.
+
+When a :term:`chat` appears in a parameter or as a property,
+it means that it will be either a :class:`~types.User`, :class:`~types.Group` or :class:`~types.Channel`.
+
+When a parameter must be "chat-like", it means Telethon will accept anything that can be "converted" to a :term:`chat`.
+The following types are chat-like:
+
+* The ``'me'`` literal string. This represents the account that is logged in ("yourself").
+* An ``'@username'``. The at-sign ``@`` is optional. Note that links are not supported.
+* An ``'+1 23'`` phone number string. It must be an ``str`` and start with the plus-sign ``+`` character.
+* An ``123`` integer identifier. It must be an ``int`` and cannot be negative.
+* An existing :class:`~types.User`, :class:`~types.Group` or :class:`~types.Channel`.
+* A :class:`~types.PackedChat`.
+
+Previous versions of Telethon referred to this term as "entity" or "entities" instead.
+
+
+Telegram Chat
+-------------
+
+The Telegram API is very confusing when it comes to the word "chat".
+You only need to know about this if you plan to use the :term:`Raw API`.
+
+In the schema definitions, there are two boxed types, :tl:`User` and :tl:`Chat`.
+A boxed :tl:`User` can only be the bare :tl:`user`, but the boxed :tl:`Chat` can be either a bare :tl:`chat` or a bare :tl:`channel`.
+
+A bare :tl:`chat` always refers to small groups.
+A bare :tl:`channel` can have either the ``broadcast`` or the ``megagroup`` flag set to ``True``.
+
+A bare :tl:`channel` with the ``broadcast`` flag set to ``True`` is known as a broadcast channel.
+A bare :tl:`channel` with the ``megagroup`` flag set to ``True`` is known as a supergroup.
+
+A bare :tl:`chat` with has less features than a bare :tl:`channel` ``megagroup``.
+Official clients are very good at hiding this difference.
+They will implicitly convert bare :tl:`chat` to bare :tl:`channel` ``megagroup`` when doing certain operations.
+Doing things like setting a username is actually a two-step process (migration followed by updating the username).
+Official clients transparently merge the history of migrated :tl:`channel` with their old :tl:`chat`.
+
+In Telethon:
+
+* A :class:`~types.User` always corresponds to :tl:`user`.
+* A :class:`~types.Group` represents either a :tl:`chat` or a :tl:`channel` ``megagroup``.
+* A :class:`~types.Channel` represents a :tl:`channel` ``broadcast``.
+
+Telethon classes aim to map to similar concepts in official applications.
+
+
+Bot API chat
+------------
+
+The Bot API follows a certain convention when it comes to identifiers:
+
+* User IDs are positive.
+* Chat IDs are negative.
+* Channel IDs are prefixed with ``-100``.
+
+Telethon encourages the use of :class:`~types.PackedChat` instead of naked identifiers.
+As a reminder, negative identifiers are not supported in Telethon's chat-like parameters.
+
+
+Encountering chats
+------------------
+
+The way you encounter chats in Telethon is no different from official clients.
+If you:
+
+* …have joined a group or channel, or have sent private messages to some user, you can :meth:`~Client.get_dialogs`.
+* …know the user is in your contact list, you can :meth:`~Client.get_contacts`.
+* …know the user has a common chat with you, you can :meth:`~Client.get_participants` of the chat in common.
+* …know the username of the user, group, or channel, you can :meth:`~Client.resolve_username`.
+* …are a bot responding to users, you will be able to access the :attr:`types.Message.sender`.
+
+Chats access hash
+-----------------
+
+Users, supergroups and channels all need an :term:`access hash`.
+
+In Telethon, the :class:`~types.PackedChat` is the recommended way to deal with the identifier-hash pairs.
+This compact type can be used anywhere a chat is expected.
+It's designed to be easy to store and cache in any way your application chooses.
+
+Bot accounts can get away with an invalid :term:`access hash` for certain operations under certain conditions.
+The same is true for user accounts, although to a lesser extent.
+
+When using just the identifier to refer to a chat, Telethon will attempt to retrieve its hash from its in-memory cache.
+If this fails, an invalid hash will be used. This may or may not make the API call succeed.
+For this reason, it is recommended that you always use :class:`~types.PackedChat` instead.
+
+Remember that an :term:`access hash` is account-bound.
+You cannot obtain an :term:`access hash` in Account-A and use it in Account-B.
diff --git a/client/doc/concepts/errors.rst b/client/doc/concepts/errors.rst
new file mode 100644
index 00000000..10a2487a
--- /dev/null
+++ b/client/doc/concepts/errors.rst
@@ -0,0 +1,40 @@
+RPC Errors
+==========
+
+.. currentmodule:: telethon
+
+:term:`RPC` stands for Remote Procedure Call.
+By extension, RPC Errors occur when a RPC fails to execute in the server.
+In Telethon, a :term:`RPC error` corresponds to the :class:`RpcError` class.
+
+Telethon will only ever raise :class:`RpcError` when the result to a :term:`RPC` is an error.
+If the error is raised, you know it comes from Telegram.
+Consequently, when using :term:`Raw API`, if a :class:`RpcError` occurs, it is never a bug in the library.
+
+:term:`RPC error` consist of an integer :attr:`~RpcError.code` and a string :attr:`~RpcError.name`.
+The :attr:`RpcError.code` is roughly the same as `HTTP status codes `_.
+The :attr:`RpcError.name` is often a string in ``SCREAMING_CASE`` and refers to what went wrong.
+
+Certain error names also contain an integer value.
+This value is removed from the :attr:`~RpcError.name` and put into :attr:`RpcError.value`.
+If Telegram responds with ``FLOOD_WAIT_60``, the name would be ``'FLOOD_WAIT'`` and the value ``60``.
+
+A very common error is ``FLOOD_WAIT``.
+It occurs when you have attempted to use a request too many times during a certain window of time:
+
+.. code-block:: python
+
+ import asyncio
+ from telethon import RpcError
+
+ try:
+ await client.send_message('me', 'Spam')
+ except RpcError as e:
+ # If we get a flood error, sleep. Else, propagate the error.
+ if e.name == 'FLOOD_WAIT':
+ await asyncio.sleep(e.value)
+ else:
+ raise
+
+Note that the library can automatically handle and retry on ``FLOOD_WAIT`` for you.
+Refer to the ``flood_sleep_threshold`` of the :class:`Client` to learn how.
diff --git a/client/doc/concepts/full-api.rst b/client/doc/concepts/full-api.rst
new file mode 100644
index 00000000..0bfd75c8
--- /dev/null
+++ b/client/doc/concepts/full-api.rst
@@ -0,0 +1,66 @@
+The Full API
+============
+
+.. currentmodule:: telethon
+
+The API surface offered by Telethon is not exhaustive.
+Telegram is constantly adding new features, and both implementing and documenting custom methods would an exhausting, never-ending job.
+
+Telethon concedes to this fact and implements only commonly-used features to keep a lean API.
+Access to the entirity of Telegram's API via Telethon's :term:`Raw API` is a necessary evil.
+
+The ``telethon._tl`` module has a leading underscore to signal that it is private.
+It is not covered by the semver guarantees of the library, but you may need to use it regardless.
+If the :class:`Client` doesn't offer a method for what you need, using the :term:`Raw API` is inevitable.
+
+
+Invoking Raw API methods
+------------------------
+
+The :term:`Raw API` can be *invoked* in a very similar way to other client methods:
+
+.. code-block:: python
+
+ from telethon import _tl as tl
+
+ was_reset = await client(tl.functions.account.reset_wall_papers())
+
+Inside ``telethon._tl.functions`` you will find a function for every single :term:`RPC` supported by Telegram.
+The parameters are keyword-only and do not have defaults.
+Whatever arguments you pass is exactly what Telegram will receive.
+Whatever is returned is exactly what Telegram responded with.
+
+All functions inside ``telethon._tl.functions`` will return the serialized request.
+When calling a :class:`Client` instance with this request as input, it will be sent to Telegram and wait for a response.
+
+Multiple requests may be in-flight at the same time, specially when using :mod:`asyncio`.
+Telethon will attempt to combine these into a single "container" when possible as an optimization.
+
+
+Exploring the Raw API
+---------------------
+
+Everything under ``telethon._tl.types`` implements :func:`repr`.
+This means you can print any response and get the Python representation of that object.
+
+All types are proper classes with attributes.
+You do not need to use a regular expression on the string representation to access the field you want.
+
+Most :term:`RPC` return an abstract class from ``telethon._tl.abcs``.
+To check for a concrete type, you can use :func:`isinstance`:
+
+.. code-block:: python
+
+ invite = await client(tl.functions.messages.check_chat_invite(hash='aBcDeF'))
+ if isinstance(invite, tl.types.ChatInviteAlready):
+ print(invite.chat)
+
+The ``telethon._tl`` module is not documented here because it would result in tens of megabytes.
+Instead, there are multiple alternatives:
+
+* Use Telethon's separate site to search in the `Telethon Raw API `_.
+ This is the recommended way. It also features auto-generated examples.
+* Use Python's built-in :func:`help` and :func:`dir` to help you navigate the module.
+* Use an editor with autocompletion support.
+* Choose the right layer from `Telegram's official API Layers `_.
+ Note that the `TL Schema `_ might not be up-to-date.
diff --git a/client/doc/concepts/glossary.rst b/client/doc/concepts/glossary.rst
new file mode 100644
index 00000000..11ca8d74
--- /dev/null
+++ b/client/doc/concepts/glossary.rst
@@ -0,0 +1,42 @@
+Glossary
+========
+
+.. currentmodule:: telethon
+
+.. glossary::
+ :sorted:
+
+ chat
+ A :class:`~types.User`, :class:`~types.Group` or :class:`~types.Channel`.
+
+ .. seealso:: The :doc:`../concepts/chats` concept.
+
+ Raw API
+ Functions and types under ``telethon._tl`` that enable access to all of Telegram's API.
+
+ .. seealso:: The :doc:`../concepts/full-api` concept.
+
+ access hash
+ Account-bound integer tied to a specific resource.
+ Users, channels, photos and documents are all resources with an access hash.
+ The access hash doesn't change, but every account will see a different value for the same resource.
+
+ RPC
+ Remote Procedure Call.
+ Invoked when calling a :class:`Client` with a function from ``telethon._tl.functions``.
+
+ RPC Error
+ Error type returned by Telegram.
+ :class:`RpcError` contains an integer code similar to HTTP status codes and a name.
+
+ .. seealso:: The :doc:`../concepts/errors` concept.
+
+ session
+ Data used to securely connect to Telegram and other state related to the logged-in account.
+
+ .. seealso:: The :doc:`../concepts/sessions` concept.
+
+ MTProto
+ Mobile Transport Protocol used to interact with Telegram's API.
+
+ .. seealso:: The :doc:`../concepts/botapi-vs-mtproto` concept.
diff --git a/client/doc/concepts/sessions.rst b/client/doc/concepts/sessions.rst
new file mode 100644
index 00000000..20af0bf3
--- /dev/null
+++ b/client/doc/concepts/sessions.rst
@@ -0,0 +1,55 @@
+Sessions
+========
+
+.. currentmodule:: telethon
+
+In Telethon, the word :term:`session` is used to refer to the set of data needed to connect to Telegram.
+This includes the server address of your home datacenter, as well as the authorization key bound to an account.
+When you first connect to Telegram, an authorization key is generated to encrypt all communication.
+After login, Telegram remembers this authorization key as logged-in, so you don't need to login again.
+
+.. important::
+
+ **Do not leak the session file!**
+ Anyone with that file can login to the account stored in it.
+ If you believe someone else has obtained this file, immediately revoke all active sessions from an official client.
+
+Some auxiliary information such as the user ID of the logged-in user is also kept.
+
+The update state, which can change every time an update is received from Telegram, is also stored in the session.
+Telethon needs this information to catch up on all missed updates while your code was not running.
+This is why it's important to call :meth:`Client.disconnect`.
+Doing so flushes all the update state to the session and saves it.
+
+
+Session files
+-------------
+
+Telethon defaults to using SQLite to store the session state.
+The session state is written to ``.session`` files, so make sure your VCS ignores them!
+To make sure the ``.session`` file is saved, you should call :meth:`Client.disconnect` before exiting the program.
+
+The first parameter in the :class:`Client` constructor is the session to use.
+You can use a `str`, a :class:`pathlib.Path` or a :class:`session.Storage`.
+The string or path are relative to the Current Working Directory.
+You can use absolute paths or relative paths to folders elsewhere.
+The ``.session`` extension is automatically added if the path has no extension.
+
+
+Session storages
+----------------
+
+The :class:`session.Storage` abstract base class defines the required methods to create custom storages.
+Telethon comes with two built-in storages:
+
+* :class:`~session.SqliteSession`. This is used by default when a string or path is used.
+* :class:`~session.MemorySession`. This is used by default when the path is ``None``.
+ You can also use it directly when you have a :class:`~session.Session` instance.
+ It's useful when you don't have file-system access.
+
+If you would like to store the session state in a different way, you can subclass :class:`session.Storage`.
+
+Some Python installations do not have the ``sqlite3`` module.
+In this case, attempting to use the default :class:`~session.SqliteSession` will fail.
+If this happens, you can try reinstalling Python.
+If you still don't have the ``sqlite3`` module, you should use a different storage.
diff --git a/client/doc/concepts/updates.rst b/client/doc/concepts/updates.rst
new file mode 100644
index 00000000..9fe6af01
--- /dev/null
+++ b/client/doc/concepts/updates.rst
@@ -0,0 +1,75 @@
+Updates
+=======
+
+.. currentmodule:: telethon
+
+Updates are an important topic in a messaging platform like Telegram.
+After all, you want to be notified as soon as certain events happen, such as new message arrives.
+
+Telethon abstracts away Telegram updates with :mod:`~telethon.events`.
+
+.. important::
+
+ It is strongly advised to configure logging when working with events:
+
+ .. code-block:: python
+
+ import logging
+ logging.basicConfig(
+ format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s',
+ level=logging.WARNING
+ )
+
+ With the above, you will see all warnings and errors and when they happened.
+
+
+Filtering events
+----------------
+
+There is no way to tell Telegram to only send certain updates.
+Telethon must be received and process all updates to ensure correct ordering.
+
+Filters are not magic.
+They work all the same as ``if`` conditions inside your event handlers.
+However, they offer a more convenient and consistent way to check for certain conditions.
+
+All built-in filters can be found in :mod:`telethon.events.filters`.
+
+When registering an event handler, you can optionally define the filter to use.
+You can retrieve a handler's filter with :meth:`~Client.get_handler_filter`.
+You can set (and overwrite) a handler's filter with :meth:`~Client.set_handler_filter`.
+
+Filters are meant to be fast and never raise exceptions.
+For this reason, filters cannot be asynchronous.
+This reduces the chance a filter will do slow IO and potentially fail.
+
+A filter is simply a callable function that takes an event as input and returns a boolean.
+If the filter returns ``True``, the handler will be called.
+Using this knowledge, you can create custom filters too.
+If you need state, you can use a class with a ``__call__`` method defined:
+
+.. code-block:: python
+
+ def only_odd_messages(event):
+ "A filter that only handles messages when their ID is divisible by 2"
+ return event.id % 2 == 0
+
+ client.add_event_handler(handler, events.NewMessage, only_odd_messages)
+
+ # ...
+
+ class OnlyDivisibleMessages:
+ "A filter that only handles messages when their ID is divisible by some amount"
+ def __init__(self, divisible_by):
+ self.divisible_by = divisible_by
+
+ def __call__(self, event):
+ return event.id % self.divisible_by == 0
+
+ client.add_event_handler(handler, events.NewMessage, OnlyDivisibleMessages(7))
+
+Custom filters should accept any :class:`~events.Event`.
+You can use :func:`isinstance` if your filter can only deal with certain types of events.
+
+If you need to perform asynchronous operations, you can't use a filter.
+Instead, manually check for those conditions inside your handler.
diff --git a/client/doc/conf.py b/client/doc/conf.py
index b577c7df..c4864dce 100644
--- a/client/doc/conf.py
+++ b/client/doc/conf.py
@@ -3,6 +3,11 @@
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(os.curdir)) # for custom extensions
+
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
@@ -14,11 +19,21 @@ release = "2.0.0a0"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
-# extensions = []
-
-templates_path = ["_templates"]
-# exclude_patterns = []
+extensions = [
+ "sphinx.ext.autodoc",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.graphviz",
+ "roles.tl",
+]
+intersphinx_mapping = {"python": ("https://docs.python.org/3/", None)}
+tl_ref_url = "https://tl.telethon.dev"
+autodoc_default_options = {
+ "members": True,
+ "undoc-members": True,
+}
+modindex_common_prefix = ["telethon."]
+graphviz_output_format = "svg"
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
diff --git a/client/doc/developing/changelog.rst b/client/doc/developing/changelog.rst
new file mode 100644
index 00000000..52869da3
--- /dev/null
+++ b/client/doc/developing/changelog.rst
@@ -0,0 +1,8 @@
+Changelog (Version History)
+===========================
+
+
+v2 alpha
+--------
+
+WIP!
diff --git a/client/doc/developing/coding-style.rst b/client/doc/developing/coding-style.rst
new file mode 100644
index 00000000..fd9fce04
--- /dev/null
+++ b/client/doc/developing/coding-style.rst
@@ -0,0 +1,17 @@
+Coding style
+============
+
+Knowledge of Python is a obviously a must to develop a Python library.
+A good online resource is `Dive Into Python 3 `_.
+
+Telethon uses multiple tools to automatically format the code and check for linting rules.
+This means you can simply ignore formatting and let the tools handle it for you.
+You can find these tools under the ``tools/`` folder.
+
+The documentation is written with mostly a newline after every period.
+This is not a hard rule.
+Lines can be cut earlier if they become too long to be comfortable.
+
+Commit messages should be short and descriptive.
+They should start with an action in the present ("Fix" and not "Fixed").
+This saves a few characters and represents what the commit will "do" after applied.
diff --git a/client/doc/developing/migration-guide.rst b/client/doc/developing/migration-guide.rst
new file mode 100644
index 00000000..6a1ec3b8
--- /dev/null
+++ b/client/doc/developing/migration-guide.rst
@@ -0,0 +1,4 @@
+Migrating from v1 to v2
+=======================
+
+WIP!
diff --git a/client/doc/developing/philosophy.rst b/client/doc/developing/philosophy.rst
new file mode 100644
index 00000000..435d6150
--- /dev/null
+++ b/client/doc/developing/philosophy.rst
@@ -0,0 +1,10 @@
+Philosophy
+==========
+
+* Dependencies should only be added when absolutely necessary.
+* Dependencies written in anything other than Python cannot be mandatory.
+* The library must work correctly with no system dependencies other than Python 3.
+* Strict type-checking is required to pass everywhere in the library to make upgrades easier.
+* The code structure must make use of hard and clear boundaries to keep the different parts decoupled.
+* The API should cover only the most commonly used features to avoid bloat and reduce maintenance costs.
+* Documentation must be a pleasure to use and contain plenty of code examples.
diff --git a/client/doc/developing/project-structure.rst b/client/doc/developing/project-structure.rst
new file mode 100644
index 00000000..978524ec
--- /dev/null
+++ b/client/doc/developing/project-structure.rst
@@ -0,0 +1,95 @@
+Project Structure
+=================
+
+.. currentmodule:: telethon
+
+The repository contains several folders, each with their own "package".
+
+
+benches/
+--------
+
+This folder contains different benchmarks.
+Pretty straightforward.
+
+
+stubs/
+------
+
+If a dependency doesn't support typing, files here must work around that.
+
+
+tools/
+------
+
+Various utility scripts.
+Each script should have a "comment" at the top explaining what they are for.
+
+See ``DEVELOPING.md`` in the repository root to learn how to use some of the tools.
+
+
+generator/
+----------
+
+A package that should not be published and is only used when developing the library.
+The implementation is private and exists under the ``src/*/_impl/`` folder.
+Only select parts are exported under public modules.
+Tests live under ``tests/``.
+
+The implementation consists of a parser and a code generator.
+
+The parser is able to read parse ``.tl`` files (Type-Language definition files).
+It doesn't do anything with the files other than to represent the content as Python objects.
+
+The code generator uses the parsed definitions to generate Python code.
+Most of the code to serialize and deserialize objects lives under ``serde/``.
+
+An in-memory "filesystem" structure is kept before writing all files to disk.
+This makes it possible to execute most of the process in a sans-io manner.
+Once the code generation finishes, all files are written to disk at once.
+
+See ``DEVELOPING.md`` in the repository root to learn how to generate code.
+
+
+client/
+-------
+
+The Telethon client library and documentation lives here.
+This is the package that gets published.
+The implementation is private and exists under the ``src/*/_impl/`` folder.
+Only select parts are exported under public modules.
+Tests live under ``tests/``.
+
+The client implementation consists of several subpackages.
+
+The ``tl`` package sits at the bottom.
+It is where the generated code is placed.
+It also contains some of the definitions needed for the generated code to work.
+Even though all the :term:`RPC` live here, this package can't do anything by itself.
+
+The ``crypto`` package implements all the encryption and decryption rules used by Telegram.
+Details concerning the :term:`MTProto` are mostly avoided, so the package can be generally useful.
+
+The ``mtproto`` package implements the logic required to talk to Telegram.
+It is implemented in a sans-io manner.
+This package is responsible for generating an authorization key and serializing packets.
+It also contains some optimizations which are not strictly necessary when implementing the library.
+
+The ``mtsender`` package simply adds IO to ``mtproto``.
+It is responsible for driving the network, enqueuing requests, and waiting for results.
+
+The ``session`` crate implements what's needed to manage the :term:`session` state.
+The logic to handle and correctly order updates also lives here, in a sans-io manner.
+
+The ``client`` ties everything together.
+This is what defines the Pythonic API to interact with Telegram.
+Custom object and event types also live here.
+
+Even though only common methods are implemented, the code is still huge.
+For this reason, the :class:`Client` implementation is separated from the class definition.
+The class definition only contains documentation and calls functions defined in other files.
+A tool under ``tools/`` exists to make it easy to keep these two in sync.
+
+If you plan to port the library to a different language, good luck!
+You will need a code generator, the ``crypto``, ``mtproto`` and ``mtsender`` packages to have an initial working version.
+The tests are your friend, write them too!
diff --git a/client/doc/index.rst b/client/doc/index.rst
index b8b239cd..7ffdbe55 100644
--- a/client/doc/index.rst
+++ b/client/doc/index.rst
@@ -1,15 +1,126 @@
-Telethon's documentation
-========================
+.. |svglogo| image:: ../../logo.svg
+ :width: 24pt
+ :height: 24pt
+
+.. only:: html
+
+ .. highlights:: |svglogo| **Welcome to Telethon's documentation!**
+
+.. only:: not html
+
+ .. highlights:: **Welcome to Telethon's documentation!**
+
+.. code-block:: python
+
+ import asyncio
+ from telethon import Client, events
+ from telethon.events import filters
+
+ async def main():
+ async with Client('name', api_id, api_hash) as client:
+ me = await client.interactive_login()
+ await client.send_message(me, f'Hello, {me.full_name}!')
+
+ @client.on(events.NewMessage, filters.Text(r'(?i)hello'))
+ async def handler(event):
+ await event.reply('Hey!')
+
+ await client.run_until_disconnected()
+
+ asyncio.run(main())
+
+* Are you new here? Jump straight into :doc:`basic/installation`!
+* Looking for the Client API reference? See :doc:`modules/client`.
+* Did you upgrade the library? Please read :doc:`developing/changelog`.
+* Coming from Bot API or want to create new bots? See :doc:`concepts/botapi-vs-mtproto`.
+* Used Telethon before v2.0? See :doc:`developing/migration-guide`.
+* Want to hack away with the raw API? Search in `Telethon Raw API `_.
+
+
+Preface
+=======
+
+.. rubric:: What is this?
+
+Telegram is a popular messaging application.
+This library is meant to make it easy for you to write Python programs that can interact with Telegram.
+Think of it as a wrapper that has already done the hard work for you, so you can focus on developing an application.
+
+
+.. rubric:: How should I use the documentation?
+
+This documentation is divided in multiple sections. The first few sections are a guide, while others contain the API reference or a glossary of terms.
+The documentation assumes some familiarity with Python.
+
+If you are getting started with the library, you should follow the documentation in order by pressing the "Next" button at the bottom-right of every page.
+
+You can also use the menu on the left to quickly skip over sections if you're looking for something in particular or want to continue where you left off.
+
+
+First steps
+===========
+
+In this section you will learn how to install the library and login to your Telegram account.
+
+:doc:`‣ Start reading Installation `
.. toctree::
- :maxdepth: 2
- :caption: Contents:
+ :hidden:
+ :caption: First steps
+
+ basic/installation
+ basic/signing-in
+ basic/next-steps
+API reference
+=============
-Indices and tables
-==================
+This section contains all the functions and types offered by the library.
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
+:doc:`‣ Start reading Client API `
+
+.. toctree::
+ :hidden:
+ :caption: API reference
+
+ modules/client
+ modules/events
+ modules/types
+ modules/sessions
+
+Concepts
+========
+
+A more in-depth explanation of some of the concepts and words used in Telethon.
+
+:doc:`‣ Start reading Chat concept `
+
+.. toctree::
+ :hidden:
+ :caption: Concepts
+
+ concepts/chats
+ concepts/updates
+ concepts/sessions
+ concepts/errors
+ concepts/botapi-vs-mtproto
+ concepts/full-api
+ concepts/glossary
+
+Developing
+==========
+
+Tips and tricks to develop both with the library and for the library.
+
+:doc:`‣ Start reading Changelog `
+
+.. toctree::
+ :hidden:
+ :caption: Developing
+
+ developing/changelog
+ developing/migration-guide
+ developing/philosophy.rst
+ developing/coding-style.rst
+ developing/project-structure.rst
diff --git a/client/doc/modules/client.rst b/client/doc/modules/client.rst
new file mode 100644
index 00000000..ac028519
--- /dev/null
+++ b/client/doc/modules/client.rst
@@ -0,0 +1,4 @@
+Client
+======
+
+.. autoclass:: telethon.Client
diff --git a/client/doc/modules/events.rst b/client/doc/modules/events.rst
new file mode 100644
index 00000000..aee1d44f
--- /dev/null
+++ b/client/doc/modules/events.rst
@@ -0,0 +1,12 @@
+Events and filters
+==================
+
+Events
+------
+
+.. automodule:: telethon.events
+
+Filters
+-------
+
+.. automodule:: telethon.events.filters
diff --git a/client/doc/modules/sessions.rst b/client/doc/modules/sessions.rst
new file mode 100644
index 00000000..6426e19a
--- /dev/null
+++ b/client/doc/modules/sessions.rst
@@ -0,0 +1,4 @@
+Session storages
+================
+
+.. automodule:: telethon.session
diff --git a/client/doc/modules/types.rst b/client/doc/modules/types.rst
new file mode 100644
index 00000000..56d1227e
--- /dev/null
+++ b/client/doc/modules/types.rst
@@ -0,0 +1,6 @@
+Types
+=====
+
+.. automodule:: telethon.types
+
+.. autoclass:: telethon.RpcError
diff --git a/client/doc/roles/tl.py b/client/doc/roles/tl.py
new file mode 100644
index 00000000..683441bd
--- /dev/null
+++ b/client/doc/roles/tl.py
@@ -0,0 +1,34 @@
+from docutils import nodes, utils
+from docutils.parsers.rst.roles import set_classes
+
+
+def make_link_node(rawtext, app, name, options):
+ try:
+ base = app.config.tl_ref_url
+ if not base:
+ raise AttributeError
+ except AttributeError as e:
+ raise ValueError("tl_ref_url config value is not set") from e
+
+ if base[-1] != "/":
+ base += "/"
+
+ set_classes(options)
+ node = nodes.reference(
+ rawtext, utils.unescape(name), refuri="{}?q={}".format(base, name), **options
+ )
+ return node
+
+
+def tl_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ if options is None:
+ options = {}
+
+ app = inliner.document.settings.env.app
+ node = make_link_node(rawtext, app, text, options)
+ return [node], []
+
+
+def setup(app):
+ app.add_role("tl", tl_role)
+ app.add_config_value("tl_ref_url", None, "env")
diff --git a/client/pyproject.toml b/client/pyproject.toml
index 4e26967a..a418124d 100644
--- a/client/pyproject.toml
+++ b/client/pyproject.toml
@@ -28,9 +28,16 @@ dynamic = ["version"]
[project.optional-dependencies]
cryptg = ["cryptg~=0.4"]
dev = [
+ "isort~=5.12",
+ "black~=23.3.0",
+ "mypy~=1.3",
"pytest~=7.3",
"pytest-asyncio~=0.21",
]
+doc = [
+ "sphinx_rtd_theme~=1.2",
+ "types-docutils~=0.20",
+]
[project.urls]
"Homepage" = "https://telethon.dev/"
@@ -43,4 +50,4 @@ requires = ["setuptools>=61.0"]
build-backend = "setuptools.build_meta"
[tool.setuptools.dynamic]
-version = {attr = "telethon.__version__"}
+version = {attr = "telethon.version.__version__"}