mirror of
https://github.com/LonamiWebs/Telethon.git
synced 2026-09-08 09:51:57 +00:00
Continue implementation and documentation
This commit is contained in:
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
.underline {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.spoiler {
|
||||
border-radius: 5%;
|
||||
padding: 0.1em;
|
||||
background-color: gray;
|
||||
color: gray;
|
||||
transition: all 200ms;
|
||||
}
|
||||
|
||||
.spoiler:hover {
|
||||
background-color: unset;
|
||||
color: unset;
|
||||
}
|
||||
@@ -56,8 +56,8 @@ 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:
|
||||
You can write the code in a file (such as ``hello.py``) and then run it, or use the built-in :mod:`asyncio`-enabled REPL.
|
||||
For this tutorial, we'll be using the :mod:`asyncio` REPL:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
@@ -209,3 +209,6 @@ Otherwise, you might run into errors such as tasks being destroyed while pending
|
||||
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.
|
||||
|
||||
If you want to learn how :mod:`asyncio` works under the hood or need an introduction,
|
||||
you can read my own blog post `An Introduction to Asyncio <https://lonami.dev/blog/asyncio/>`_.
|
||||
|
||||
@@ -44,6 +44,11 @@ Glossary
|
||||
|
||||
.. seealso:: The :doc:`../concepts/botapi-vs-mtproto` concept.
|
||||
|
||||
HTTP Bot API
|
||||
Telegram's simplified HTTP API to control bot accounts only.
|
||||
|
||||
.. seealso:: The :doc:`../concepts/botapi-vs-mtproto` concept.
|
||||
|
||||
login
|
||||
Used to refer to the login process as a whole, as opposed to the action to :term:`sign in`.
|
||||
The "login code" or "login token" get their name because they belong to the login process.
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
Messages
|
||||
========
|
||||
|
||||
.. currentmodule:: telethon
|
||||
|
||||
.. role:: underline
|
||||
:class: underline
|
||||
|
||||
.. role:: strikethrough
|
||||
:class: strikethrough
|
||||
|
||||
.. role:: spoiler
|
||||
:class: spoiler
|
||||
|
||||
Messages are at the heart of a messaging platform.
|
||||
In Telethon, you will be using the :class:`~types.Message` class to interact with them.
|
||||
|
||||
Formatting messages
|
||||
-------------------
|
||||
|
||||
The library supports 3 formatting modes: no formatting, CommonMark, HTML.
|
||||
|
||||
Telegram does not natively support markdown or HTML.
|
||||
Clients such as Telethon parse the text into a list of formatting :tl:`MessageEntity` at different offsets.
|
||||
|
||||
Note that `CommonMark's markdown <https://commonmark.org/>`_ is not fully compatible with :term:`HTTP Bot API`'s
|
||||
`MarkdownV2 style <https://core.telegram.org/bots/api#markdownv2-style>`_, and does not support spoilers::
|
||||
|
||||
*italic* and _italic_
|
||||
**bold** and __bold__
|
||||
# headings are underlined
|
||||
~~strikethrough~~
|
||||
[inline URL](https://www.example.com/)
|
||||
[inline mention](tg://user?id=ab1234cd6789)
|
||||
custom emoji image with 
|
||||
`inline code`
|
||||
```python
|
||||
multiline pre-formatted
|
||||
block with optional language
|
||||
```
|
||||
|
||||
HTML is also not fully compatible with :term:`HTTP Bot API`'s
|
||||
`MarkdownV2 style <https://core.telegram.org/bots/api#markdownv2-style>`_,
|
||||
and instead favours more standard `HTML elements <https://developer.mozilla.org/en-US/docs/Web/HTML/Element>`_:
|
||||
|
||||
* ``strong`` and ``b`` for **bold**.
|
||||
* ``em`` and ``i`` for *italics*.
|
||||
* ``u`` for :underline:`underlined text`.
|
||||
* ``del`` and ``s`` for :strikethrough:`strikethrough`.
|
||||
* ``blockquote`` for quotes.
|
||||
* ``details`` for :spoiler:`hidden text` (spoiler).
|
||||
* ``code`` for ``inline code``
|
||||
* ``pre`` for multiple lines of code.
|
||||
* ``a`` for links.
|
||||
* ``img`` for inline images (only custom emoji).
|
||||
|
||||
Both markdown and HTML recognise the following special URLs using the ``tg:`` protocol:
|
||||
|
||||
* ``tg://user?id=ab1234cd6789`` for inline mentions.
|
||||
To make sure the mention works, use :attr:`types.PackedChat.hex`.
|
||||
You can also use :attr:`types.User.id`, but the mention will fail if the user is not in cache.
|
||||
* ``tg://emoji?id=1234567890`` for custom emoji.
|
||||
You must use the document identifier as the value.
|
||||
The alt-text of the image **must** be a emoji such as 👍.
|
||||
|
||||
|
||||
To obtain a message's text formatted, use :attr:`types.Message.text_markdown` or :attr:`types.Message.text_html`.
|
||||
|
||||
To send a message with formatted text, use the ``markdown`` or ``html`` parameters in :meth:`Client.send_message`.
|
||||
|
||||
When sending files, the format is appended to the name of the ``caption`` parameter, either ``caption_markdown`` or ``caption_html``.
|
||||
|
||||
|
||||
Message identifiers
|
||||
-------------------
|
||||
|
||||
This is an in-depth explanation for how the :attr:`types.Message.id` works.
|
||||
|
||||
.. note::
|
||||
|
||||
You can safely skip this section if you're not interested.
|
||||
|
||||
Every account, whether it's an user account or bot account, has its own message counter.
|
||||
This counter starts at 1, and is incremented by 1 every time a new message is received.
|
||||
In private conversations or small groups, each account will receive a copy each message.
|
||||
The message identifier will be based on the message counter of the receiving account.
|
||||
|
||||
In megagroups and broadcast channels, the message counter instead belongs to the channel itself.
|
||||
It also starts at 1 and is incremented by 1 for every message sent to the group or channel.
|
||||
This means every account will see the same message identifier for a given mesasge in a group or channel.
|
||||
|
||||
This design has the following implications:
|
||||
|
||||
* The message identifier alone is enough to uniquely identify a message only if it's not from a megagroup or channel.
|
||||
This is why :class:`events.MessageDeleted` does not need to (and doesn't always) include chat information.
|
||||
* Messages cannot be deleted for one-side only in megagroups or channels.
|
||||
Because every account shares the same identifier for the message, it cannot be deleted only for some.
|
||||
* Links to messages only work for everyone inside megagroups or channels.
|
||||
In private conversations and small groups, each account will have their own counter, and the identifiers won't match.
|
||||
|
||||
Let's look at a concrete example.
|
||||
|
||||
* You are logged in as User-A.
|
||||
* Both User-B and User-C are your mutual contacts.
|
||||
* You have share a small group called Group-S with User-B.
|
||||
* You also share a megagroup called Group-M with User-C.
|
||||
|
||||
.. graphviz::
|
||||
:caption: Demo scenario
|
||||
|
||||
digraph scenario {
|
||||
"User A" [shape=trapezium];
|
||||
"User B" [shape=box];
|
||||
"User C" [shape=box];
|
||||
|
||||
"User A" -> "User B";
|
||||
"User A" -> "User C";
|
||||
|
||||
"Group-S" -> "User A";
|
||||
"Group-S" -> "User B";
|
||||
|
||||
"Group-M" -> "User A";
|
||||
"Group-M" -> "User C";
|
||||
}
|
||||
|
||||
Every account and channel has just been created.
|
||||
This means everyone has a message counter of one.
|
||||
|
||||
First, User-A will sent a welcome message to both User-B and User-C::
|
||||
|
||||
User-A → User-B: Hey, welcome!
|
||||
User-A → User-C: ¡Bienvenido!
|
||||
|
||||
* For User-A, "Hey, welcome!" will have the message identifier 1. The message with "¡Bienvenido!" will have an ID of 2.
|
||||
* For User-B, "Hey, welcome" will have ID 1.
|
||||
* For User-B, "¡Bienvenido!" will have ID 1.
|
||||
|
||||
.. csv-table:: Message identifiers
|
||||
:header: "Message", "User-A", "User-B", "User-C", "Group-S", "Group-M"
|
||||
|
||||
"Hey, welcome!", 1, 1, "", "", ""
|
||||
"¡Bienvenido!", 2, "", 1, "", ""
|
||||
|
||||
Next, User-B and User-C will respond to User-A::
|
||||
|
||||
User-B → User-A: Thanks!
|
||||
User-C → User-A: Gracias :)
|
||||
|
||||
.. csv-table:: Message identifiers
|
||||
:header: "Message", "User-A", "User-B", "User-C", "Group-S", "Group-M"
|
||||
|
||||
"Hey, welcome!", 1, 1, "", "", ""
|
||||
"¡Bienvenido!", 2, "", 1, "", ""
|
||||
"Thanks!", 3, 2, "", "", ""
|
||||
"Gracias :)", 4, "", 2, "", ""
|
||||
|
||||
Notice how for each message, the counter goes up by one, and they are independent.
|
||||
|
||||
Let's see what happens when User-B sends a message to Group-S::
|
||||
|
||||
User-B → Group-S: Nice group
|
||||
|
||||
.. csv-table:: Message identifiers
|
||||
:header: "Message", "User-A", "User-B", "User-C", "Group-S", "Group-M"
|
||||
|
||||
"Hey, welcome!", 1, 1, "", "", ""
|
||||
"¡Bienvenido!", 2, "", 1, "", ""
|
||||
"Thanks!", 3, 2, "", "", ""
|
||||
"Gracias :)", 4, "", 2, "", ""
|
||||
"Nice group", 5, 3, "", "", ""
|
||||
|
||||
While the message was sent to a different chat, the group itself doesn't have a counter.
|
||||
The message identifiers are still unique for each account.
|
||||
The chat where the message was sent can be completely ignored.
|
||||
|
||||
Megagroups behave differently::
|
||||
|
||||
User-C → Group-M: Buen grupo
|
||||
|
||||
.. csv-table:: Message identifiers
|
||||
:header: "Message", "User-A", "User-B", "User-C", "Group-S", "Group-M"
|
||||
|
||||
"Hey, welcome!", 1, 1, "", "", ""
|
||||
"¡Bienvenido!", 2, "", 1, "", ""
|
||||
"Thanks!", 3, 2, "", "", ""
|
||||
"Gracias :)", 4, "", 2, "", ""
|
||||
"Nice group", 5, 3, "", "", ""
|
||||
"Buen grupo", "", "", "", "", 1
|
||||
|
||||
The group has its own message counter.
|
||||
Each user won't get a copy of the message with their own identifier, but rather everyone sees the same message.
|
||||
@@ -40,3 +40,4 @@ graphviz_output_format = "svg"
|
||||
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
html_static_path = ["_static"]
|
||||
html_css_files = ["custom.css"]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Frequently Asked Questions
|
||||
==========================
|
||||
@@ -37,6 +37,7 @@ I can guarantee you will into far less problems.
|
||||
|
||||
Without further ado, let's take a look at the biggest changes.
|
||||
This list may not be exhaustive, but it should give you an idea on what to expect.
|
||||
If you feel like a major change is missing, please `open an issue <https://github.com/LonamiWebs/Telethon/>`_.
|
||||
|
||||
|
||||
Complete project restructure
|
||||
@@ -53,6 +54,12 @@ The public modules under the ``telethon`` now make actual sense.
|
||||
It's actually a factory object returning new error types on demand.
|
||||
This means you don't need to wait for new library versions to be released to catch them.
|
||||
|
||||
.. note::
|
||||
|
||||
Be sure to check the documentation for :data:`telethon.errors` to learn about error changes.
|
||||
Notably, errors such as ``FloodWaitError`` no longer have a ``.seconds`` field.
|
||||
Instead, every value for every error type is always ``.value``.
|
||||
|
||||
This was also a good opportunity to remove a lot of modules that were not supposed to public in their entirety:
|
||||
``.crypto``, ``.extensions``, ``.network``, ``.custom``, ``.functions``, ``.helpers``, ``.hints``, ``.password``, ``.requestiter``, ``.sync``, ``.types``, ``.utils``.
|
||||
|
||||
@@ -86,14 +93,49 @@ Each of them can have an additional namespace (as seen above with ``account.``).
|
||||
|
||||
* ``tl.functions`` contains every :term:`TL` definition treated as a function.
|
||||
The naming convention now follows Python's, and are ``snake_case``.
|
||||
They're no longer a class with attributes.
|
||||
They serialize the request immediately.
|
||||
* ``tl.abcs`` contains every abstract class, the "boxed" types from Telegram.
|
||||
You can use these for your type-hinting needs.
|
||||
* ``tl.types`` contains concrete instances, the "bare" types Telegram actually returns.
|
||||
You'll probably use these with :func:`isinstance` a lot.
|
||||
All types use :term:`__slots__` to save space.
|
||||
This means you can't add extra fields to these at runtime unless you subclass.
|
||||
|
||||
|
||||
Raw API has a reduced feature-set
|
||||
---------------------------------
|
||||
|
||||
The string representation is now on :meth:`object.__repr__`, not :meth:`object.__str__`.
|
||||
|
||||
All types use :term:`__slots__` to save space.
|
||||
This means you can't add extra fields to these at runtime unless you subclass.
|
||||
|
||||
The ``.stringify()`` methods on all TL types no longer exists.
|
||||
Instead, you can use a library like `beauty-print <https://pypi.org/project/beauty-print/>`_.
|
||||
|
||||
The ``.to_dict()`` method on all TL types no longer exists.
|
||||
The same is true for ``.to_json()``.
|
||||
Instead, you can use a library like `json-pickle <https://pypi.org/project/jsonpickle/>`_ or write your own:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def to_dict(obj):
|
||||
if obj is None or isinstance(obj, (bool, int, bytes, str)): return obj
|
||||
if isinstance(obj, list): return [to_dict(x) for x in obj]
|
||||
if isinstance(obj, dict): return {k: to_dict(v) for k, v in obj.items()}
|
||||
return {slot: to_dict(getattr(obj, slot)) for slot in obj.__slots__}
|
||||
|
||||
Lesser-known methods such as ``TLObject.pretty_format``, ``serialize_bytes``, ``serialize_datetime`` and ``from_reader`` are also gone.
|
||||
The remaining methods are:
|
||||
|
||||
* ``Serializable.constructor_id()`` class-method, to get the integer identifier of the corresponding type constructor.
|
||||
* ``Serializable.from_bytes()`` class-method, to convert serialized :class:`bytes` back into the class.
|
||||
* :meth:`object.__bytes__` instance-method, to serialize the instance into :class:`bytes` the way Telegram expects.
|
||||
|
||||
Functions are no longer a class with attributes.
|
||||
They serialize the request immediately.
|
||||
This means you cannot create request instance and change it later.
|
||||
Consider using :func:`functools.partial` if you want to reuse parts of a request instead.
|
||||
|
||||
Functions no longer have an asynchronous ``.resolve()``.
|
||||
This used to let you pass usernames and have them be resolved to :tl:`InputPeer` automatically (unless it was nested).
|
||||
|
||||
|
||||
Unified client iter and get methods
|
||||
@@ -159,6 +201,66 @@ The simplest approach could be using a global ``states`` dictionary storing the
|
||||
del states[event.sender.id]
|
||||
|
||||
|
||||
.. rubric:: No ``client.kick_participant()`` method.
|
||||
|
||||
This is not a thing in Telegram.
|
||||
It was implemented by restricting and then removing the restriction.
|
||||
|
||||
The old ``client.edit_permissions()`` was renamed to :meth:`Client.set_banned_rights`.
|
||||
This defines the rights a restricted participant has (bans them from doing other things).
|
||||
Revoking the right to view messages will kick them.
|
||||
This rename should avoid confusion, as it is now clear this is not to promote users to admin status.
|
||||
|
||||
For administrators, ``client.edit_admin`` was renamed to :meth:`Client.set_admin_rights` for consistency.
|
||||
|
||||
Note that a new method, :meth:`Client.set_default_rights`, must now be used to set a chat's default rights.
|
||||
|
||||
.. rubric:: No ``client.download_profile_photo()`` method.
|
||||
|
||||
You can simply use :meth:`Client.download` now.
|
||||
Note that :meth:`~Client.download` no longer supports downloading contacts as ``.vcard``.
|
||||
|
||||
.. rubric:: No ``client.set_proxy()`` method.
|
||||
|
||||
Proxy support is no longer built-in.
|
||||
They were never officially maintained.
|
||||
This doesn't mean you can't use them.
|
||||
You're now free to choose your own proxy library and pass a different connector to the :class:`Client` constructor.
|
||||
|
||||
This should hopefully make it clear that most connection issues when using proxies do *not* come from Telethon.
|
||||
|
||||
.. rubric:: No ``client.set_receive_updates`` method.
|
||||
|
||||
It was not working as expected.
|
||||
|
||||
.. rubric:: No ``client.catch_up()`` method.
|
||||
|
||||
You can still configure it when creating the :class:`Client`, which was the only way to make it work anyway.
|
||||
|
||||
.. rubric:: No ``client.action()`` method.
|
||||
|
||||
.. rubric:: No ``client.takeout()`` method.
|
||||
|
||||
.. rubric:: No ``client.qr_login()`` method.
|
||||
|
||||
.. rubric:: No ``client.edit_2fa()`` method.
|
||||
|
||||
.. rubric:: No ``client.get_stats()`` method.
|
||||
|
||||
.. rubric:: No ``client.edit_folder()`` method.
|
||||
|
||||
.. rubric:: No ``client.build_reply_markup()`` method.
|
||||
|
||||
.. rubric:: No ``client.list_event_handlers()`` method.
|
||||
|
||||
These are out of scope for the time being.
|
||||
They might be re-introduced in the future if there is a burning need for them and are not difficult to maintain.
|
||||
This doesn't mean you can't do these things anymore though, since the :term:`Raw API` is still available.
|
||||
|
||||
Telethon v2 is committed to not exposing the raw API under any public API of the ``telethon`` package.
|
||||
This means any method returning data from Telegram must have a custom wrapper object and be maintained too.
|
||||
Because the standards are higher, the barrier of entry for new additions and features is higher too.
|
||||
|
||||
|
||||
No message.raw_text or message.message
|
||||
--------------------------------------
|
||||
@@ -201,6 +303,57 @@ This means getting those identifiers is up to you, and you can handle it in a wa
|
||||
In-depth explanation for :doc:`/concepts/updates`.
|
||||
|
||||
|
||||
Behaviour changes in events
|
||||
---------------------------
|
||||
|
||||
:class:`events.CallbackQuery` no longer also handles "inline bot callback queries".
|
||||
This was a hacky workaround.
|
||||
|
||||
:class:`events.MessageRead` no longer triggers when the *contents* of a message are read, such as voice notes being played.
|
||||
|
||||
Albums in Telegram are an illusion.
|
||||
There is no "album media".
|
||||
There is only separate messages pretending to be a single message.
|
||||
|
||||
``events.Album`` was a hack that waited for a small amount of time to group messages sharing the same grouped identifier.
|
||||
If you want to wait for a full album, you will need to wait yourself:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
pending_albums = {} # global for simplicity
|
||||
async def gather_album(event, handler):
|
||||
if pending := pending_albums.get(event.grouped_id):
|
||||
pending.append(event)
|
||||
else:
|
||||
pending_albums[event.grouped_id] = [event]
|
||||
# Wait for other events to come in. Adjust delay to your needs.
|
||||
# This will NOT work if sequential updates are enabled (spawn a task to do the rest instead).
|
||||
await asyncio.sleep(1)
|
||||
events = pending_albums.pop(grouped_id, [])
|
||||
await handler(events)
|
||||
|
||||
@client.on(events.NewMessage)
|
||||
async def handler(event):
|
||||
if event.grouped_id:
|
||||
await gather_album(event, handle_album)
|
||||
else:
|
||||
await handle_message(event)
|
||||
|
||||
async def handle_album(events):
|
||||
... # do stuff with events
|
||||
|
||||
async def handle_message(event):
|
||||
... # do stuff with event
|
||||
|
||||
Note that the above code is not foolproof and will not handle more than one client.
|
||||
It might be possible for album events to be delayed for more than a second.
|
||||
|
||||
Note that messages that do **not** belong to an album can be received in-between an album.
|
||||
|
||||
Overall, it's probably better if you treat albums for what they really are:
|
||||
separate messages sharing a :attr:`~types.Message.grouped_id`.
|
||||
|
||||
|
||||
Streamlined chat, input_chat and chat_id
|
||||
----------------------------------------
|
||||
|
||||
@@ -232,6 +385,13 @@ Overall, dealing with users, groups and channels should feel a lot more natural.
|
||||
In-depth explanation for :doc:`/concepts/chats`.
|
||||
|
||||
|
||||
Other methods like ``client.get_peer_id``, ``client.get_input_entity`` and ``client.get_entity`` are gone too.
|
||||
While not directly related, ``client.is_bot`` is gone as well.
|
||||
You can use :meth:`Client.get_me` or read it from the session instead.
|
||||
|
||||
The ``telethon.utils`` package is gone entirely, so methods like ``utils.resolve_id`` no longer exist either.
|
||||
|
||||
|
||||
Session cache no longer exists
|
||||
------------------------------
|
||||
|
||||
@@ -264,7 +424,7 @@ TelegramClient renamed to Client
|
||||
You can rename it with :keyword:`as` during import if you want to use the old name.
|
||||
|
||||
Python allows using namespaces via packages and modules.
|
||||
Therefore, the full name :class:`telethon.Client` already indicates it's from ``telethon``, so the old name was redundant.
|
||||
Therefore, the full name :class:`telethon.Client` already indicates it's from ``telethon``, so the old ``Telegram`` prefix was redundant.
|
||||
|
||||
|
||||
Changes to start and client context-manager
|
||||
@@ -283,8 +443,9 @@ It also means you can now use the context manager even with custom login flows.
|
||||
The old ``sign_in()`` method also sent the code, which was rather confusing.
|
||||
Instead, you must now :meth:`~Client.request_login_code` as a separate operation.
|
||||
|
||||
The old ``log_out`` was also renamed to :meth:`~Client.sign_out` for consistency with :meth:`~Client.sign_in`.
|
||||
The old ``log_out()`` was also renamed to :meth:`~Client.sign_out` for consistency with :meth:`~Client.sign_in`.
|
||||
|
||||
The old ``is_user_authorized()`` was renamed to :meth:`~Client.is_authorized` since it works for bot accounts too.
|
||||
|
||||
No telethon.sync hack
|
||||
---------------------
|
||||
|
||||
@@ -41,6 +41,30 @@ The implementation consists of a parser and a code generator.
|
||||
The parser is able to read parse ``.tl`` files (:term:`Type Language` definition files).
|
||||
It doesn't do anything with the files other than to represent the content as Python objects.
|
||||
|
||||
.. admonition:: Type Language brief
|
||||
|
||||
TL-definitions are statements terminated with a semicolon ``;`` and often defined in a single line:
|
||||
|
||||
.. code-block::
|
||||
|
||||
geoPointEmpty#1117dd5f = GeoPoint;
|
||||
geoPoint#b2a2f663 flags:# long:double lat:double access_hash:long accuracy_radius:flags.0?int = GeoPoint;
|
||||
|
||||
The first word is the name, optionally followed by the hash sign ``#`` and an hexadecimal number.
|
||||
Every definition can have a constructor identifier inferred based on its own text representation.
|
||||
The hexadecimal number will override the constructor identifier used for the definition.
|
||||
|
||||
What follows up to the equals-sign ``=`` are the fields of the definition.
|
||||
They have a name and a type, separated by the colon ``:``.
|
||||
|
||||
The type ``#`` represents a bitflag.
|
||||
Other fields can be conditionally serialized by prefixing the type with ``flag_name.bit_index?``.
|
||||
|
||||
After the equal-sign comes the name for the "base class".
|
||||
This representation is known as "boxed", and it contains the constructor identifier to discriminate a definition.
|
||||
If the definition name appears on its own, it will be "bare" and will not have the constructor identifier prefix.
|
||||
|
||||
|
||||
The code generator uses the parsed definitions to generate Python code.
|
||||
Most of the code to serialize and deserialize objects lives under ``serde/``.
|
||||
|
||||
|
||||
@@ -102,14 +102,15 @@ A more in-depth explanation of some of the concepts and words used in Telethon.
|
||||
|
||||
concepts/chats
|
||||
concepts/updates
|
||||
concepts/messages
|
||||
concepts/sessions
|
||||
concepts/errors
|
||||
concepts/botapi-vs-mtproto
|
||||
concepts/full-api
|
||||
concepts/glossary
|
||||
|
||||
Developing
|
||||
==========
|
||||
Development resources
|
||||
=====================
|
||||
|
||||
Tips and tricks to develop both with the library and for the library.
|
||||
|
||||
@@ -117,10 +118,11 @@ Tips and tricks to develop both with the library and for the library.
|
||||
|
||||
.. toctree::
|
||||
:hidden:
|
||||
:caption: Developing
|
||||
:caption: Development resources
|
||||
|
||||
developing/changelog
|
||||
developing/migration-guide
|
||||
developing/faq
|
||||
developing/philosophy.rst
|
||||
developing/coding-style.rst
|
||||
developing/project-structure.rst
|
||||
|
||||
@@ -31,6 +31,7 @@ Errors
|
||||
except errors.FloodWait as e:
|
||||
await asyncio.sleep(e.value)
|
||||
|
||||
Note how the :attr:`RpcError.value` field is still accessible, as it's a subclass of :class:`RpcError`.
|
||||
The code above is equivalent to the following:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
Reference in New Issue
Block a user