From 43c6896481fa8845d06adb174cd65c8f3b0e1bba Mon Sep 17 00:00:00 2001 From: Lonami Exo Date: Fri, 23 Mar 2018 21:40:24 +0100 Subject: [PATCH] Add a custom role for TL references and make use of it --- readthedocs/conf.py | 14 +-- readthedocs/custom_roles.py | 69 +++++++++++++++ .../advanced-usage/accessing-the-full-api.rst | 13 ++- readthedocs/extra/basic/entities.rst | 24 +++--- readthedocs/extra/changelog.rst | 2 +- telethon/events/__init__.py | 54 ++++++------ telethon/network/mtproto_sender.py | 14 +-- telethon/telegram_client.py | 86 +++++++++---------- telethon/tl/custom/dialog.py | 6 +- telethon/tl/custom/draft.py | 2 +- telethon/update_state.py | 5 +- telethon/utils.py | 57 ++++++------ 12 files changed, 212 insertions(+), 134 deletions(-) create mode 100644 readthedocs/custom_roles.py diff --git a/readthedocs/conf.py b/readthedocs/conf.py index efb14992..35dadb24 100644 --- a/readthedocs/conf.py +++ b/readthedocs/conf.py @@ -17,15 +17,16 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) -import os import re +import os +import sys +sys.path.insert(0, os.path.abspath('.')) root = os.path.abspath(os.path.join(__file__, os.path.pardir, os.path.pardir)) +tl_ref_url = 'https://lonamiwebs.github.io/Telethon' + # -- General configuration ------------------------------------------------ @@ -36,7 +37,10 @@ root = os.path.abspath(os.path.join(__file__, os.path.pardir, os.path.pardir)) # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.autodoc'] +extensions = [ + 'sphinx.ext.autodoc', + 'custom_roles' +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/readthedocs/custom_roles.py b/readthedocs/custom_roles.py new file mode 100644 index 00000000..89a5bd79 --- /dev/null +++ b/readthedocs/custom_roles.py @@ -0,0 +1,69 @@ +from docutils import nodes, utils +from docutils.parsers.rst.roles import set_classes + + +def make_link_node(rawtext, app, name, options): + """ + Create a link to the TL reference. + + :param rawtext: Text being replaced with link node. + :param app: Sphinx application context + :param name: Name of the object to link to + :param options: Options dictionary passed to role func. + """ + 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): + """ + Link to the TL reference. + + Returns 2 part tuple containing list of nodes to insert into the + document and a list of system messages. Both are allowed to be empty. + + :param name: The role name used in the document. + :param rawtext: The entire markup snippet, with role. + :param text: The text marked with the role. + :param lineno: The line number where rawtext appears in the input. + :param inliner: The inliner instance that called us. + :param options: Directive options for customization. + :param content: The directive content for customization. + """ + if options is None: + options = {} + if content is None: + content = [] + + # TODO Report error on type not found? + # Usage: + # msg = inliner.reporter.error(..., line=lineno) + # return [inliner.problematic(rawtext, rawtext, msg)], [msg] + app = inliner.document.settings.env.app + node = make_link_node(rawtext, app, text, options) + return [node], [] + + +def setup(app): + """ + Install the plugin. + + :param app: Sphinx application context. + """ + app.info('Initializing TL reference plugin') + app.add_role('tl', tl_role) + app.add_config_value('tl_ref_url', None, 'env') + return diff --git a/readthedocs/extra/advanced-usage/accessing-the-full-api.rst b/readthedocs/extra/advanced-usage/accessing-the-full-api.rst index edbe821d..b8d63eb6 100644 --- a/readthedocs/extra/advanced-usage/accessing-the-full-api.rst +++ b/readthedocs/extra/advanced-usage/accessing-the-full-api.rst @@ -25,7 +25,7 @@ You should also refer to the documentation to see what the objects from a common type, and that's the reason for this distinction. Say ``client.send_message()`` didn't exist, we could use the `search`__ -to look for "message". There we would find `SendMessageRequest`__, +to look for "message". There we would find :tl:`SendMessageRequest`, which we can work with. Every request is a Python class, and has the parameters needed for you @@ -45,11 +45,11 @@ If you're going to use a lot of these, you may do: # We now have access to 'functions.messages.SendMessageRequest' We see that this request must take at least two parameters, a ``peer`` -of type `InputPeer`__, and a ``message`` which is just a Python +of type :tl:`InputPeer`, and a ``message`` which is just a Python ``str``\ ing. -How can we retrieve this ``InputPeer``? We have two options. We manually -`construct one`__, for instance: +How can we retrieve this :tl:`InputPeer`? We have two options. We manually +construct one, for instance: .. code-block:: python @@ -64,7 +64,7 @@ Or we call ``.get_input_entity()``: peer = client.get_input_entity('someone') When you're going to invoke an API method, most require you to pass an -``InputUser``, ``InputChat``, or so on, this is why using +:tl:`InputUser`, :tl:`InputChat`, or so on, this is why using ``.get_input_entity()`` is more straightforward (and often immediate, if you've seen the user before, know their ID, etc.). If you also need to have information about the whole user, use @@ -138,6 +138,3 @@ This can further be simplified to: __ https://lonamiwebs.github.io/Telethon __ https://lonamiwebs.github.io/Telethon/methods/index.html __ https://lonamiwebs.github.io/Telethon/?q=message -__ https://lonamiwebs.github.io/Telethon/methods/messages/send_message.html -__ https://lonamiwebs.github.io/Telethon/types/input_peer.html -__ https://lonamiwebs.github.io/Telethon/constructors/input_peer_user.html diff --git a/readthedocs/extra/basic/entities.rst b/readthedocs/extra/basic/entities.rst index 598a4230..c7e55524 100644 --- a/readthedocs/extra/basic/entities.rst +++ b/readthedocs/extra/basic/entities.rst @@ -9,16 +9,16 @@ Introduction ************ The library widely uses the concept of "entities". An entity will refer -to any ``User``, ``Chat`` or ``Channel`` object that the API may return -in response to certain methods, such as ``GetUsersRequest``. +to any :tl:`User`, :tl:`Chat` or :tl:`Channel` object that the API may return +in response to certain methods, such as :tl:`GetUsersRequest`. .. note:: When something "entity-like" is required, it means that you need to provide something that can be turned into an entity. These things include, - but are not limited to, usernames, exact titles, IDs, ``Peer`` objects, - or even entire ``User``, ``Chat`` and ``Channel`` objects and even phone - numbers from people you have in your contacts. + but are not limited to, usernames, exact titles, IDs, :tl:`Peer` objects, + or even entire :tl:`User`, :tl:`Chat` and :tl:`Channel` objects and even + phone numbers from people you have in your contacts. Getting entities **************** @@ -73,7 +73,7 @@ become possible. Every entity the library encounters (in any response to any call) will by default be cached in the ``.session`` file (an SQLite database), to avoid performing unnecessary API calls. If the entity cannot be found, additonal -calls like ``ResolveUsernameRequest`` or ``GetContactsRequest`` may be +calls like :tl:`ResolveUsernameRequest` or :tl:`GetContactsRequest` may be made to obtain the required information. @@ -90,14 +90,14 @@ Entities vs. Input Entities On top of the normal types, the API also make use of what they call their ``Input*`` versions of objects. The input version of an entity (e.g. -``InputPeerUser``, ``InputChat``, etc.) only contains the minimum +:tl:`InputPeerUser`, :tl:`InputChat`, etc.) only contains the minimum information that's required from Telegram to be able to identify -who you're referring to: a ``Peer``'s **ID** and **hash**. +who you're referring to: a :tl:`Peer`'s **ID** and **hash**. This ID/hash pair is unique per user, so if you use the pair given by another user **or bot** it will **not** work. -To save *even more* bandwidth, the API also makes use of the ``Peer`` +To save *even more* bandwidth, the API also makes use of the :tl:`Peer` versions, which just have an ID. This serves to identify them, but peers alone are not enough to use them. You need to know their hash before you can "use them". @@ -106,8 +106,8 @@ As we just mentioned, API calls don't need to know the whole information about the entities, only their ID and hash. For this reason, another method, ``.get_input_entity()`` is available. This will always use the cache while possible, making zero API calls most of the time. When a request is made, -if you provided the full entity, e.g. an ``User``, the library will convert -it to the required ``InputPeer`` automatically for you. +if you provided the full entity, e.g. an :tl:`User`, the library will convert +it to the required :tl:`InputPeer` automatically for you. **You should always favour** ``.get_input_entity()`` **over** ``.get_entity()`` for this reason! Calling the latter will always make an API call to get @@ -125,5 +125,5 @@ library, the raw requests you make to the API are also able to call client(SendMessageRequest('username', 'hello')) The library will call the ``.resolve()`` method of the request, which will -resolve ``'username'`` with the appropriated ``InputPeer``. Don't worry if +resolve ``'username'`` with the appropriated :tl:`InputPeer`. Don't worry if you don't get this yet, but remember some of the details here are important. diff --git a/readthedocs/extra/changelog.rst b/readthedocs/extra/changelog.rst index a22ad725..612547af 100644 --- a/readthedocs/extra/changelog.rst +++ b/readthedocs/extra/changelog.rst @@ -315,7 +315,7 @@ library alone (when invoking a request), it means that you can now use ``Peer`` types or even usernames where a ``InputPeer`` is required. The object now has access to the ``client``, so that it can fetch the right type if needed, or access the session database. Furthermore, you can -reuse requests that need "autocast" (e.g. you put ``User`` but ``InputPeer`` +reuse requests that need "autocast" (e.g. you put :tl:`User` but ``InputPeer`` was needed), since ``.resolve()`` is called when invoking. Before, it was only done on object construction. diff --git a/telethon/events/__init__.py b/telethon/events/__init__.py index a91665fb..a3c4774e 100644 --- a/telethon/events/__init__.py +++ b/telethon/events/__init__.py @@ -91,13 +91,13 @@ class _EventCommon(abc.ABC): def _get_entity(self, msg_id, entity_id, chat=None): """ - Helper function to call GetMessages on the give msg_id and + Helper function to call :tl:`GetMessages` on the give msg_id and return the input entity whose ID is the given entity ID. - If ``chat`` is present it must be an InputPeer. + If ``chat`` is present it must be an :tl:`InputPeer`. - Returns a tuple of (entity, input_peer) if it was found, or - a tuple of (None, None) if it couldn't be. + Returns a tuple of ``(entity, input_peer)`` if it was found, or + a tuple of ``(None, None)`` if it couldn't be. """ try: if isinstance(chat, types.InputPeerChannel): @@ -124,7 +124,7 @@ class _EventCommon(abc.ABC): @property def input_chat(self): """ - The (:obj:`InputPeer`) (group, megagroup or channel) on which + The (:tl:`InputPeer`) (group, megagroup or channel) on which the event occurred. This doesn't have the title or anything, but is useful if you don't need those to avoid further requests. @@ -156,7 +156,7 @@ class _EventCommon(abc.ABC): @property def chat(self): """ - The (:obj:`User` | :obj:`Chat` | :obj:`Channel`, optional) on which + The (:tl:`User` | :tl:`Chat` | :tl:`Channel`, optional) on which the event occurred. This property may make an API call the first time to get the most up to date version of the chat (mostly when the event doesn't belong to a channel), so keep that in mind. @@ -312,8 +312,8 @@ class NewMessage(_EventBuilder): Represents the event of a new message. Members: - message (:obj:`Message`): - This is the original ``Message`` object. + message (:tl:`Message`): + This is the original :tl:`Message` object. is_private (:obj:`bool`): True if the message was sent as a private message. @@ -406,7 +406,7 @@ class NewMessage(_EventBuilder): @property def input_sender(self): """ - This (:obj:`InputPeer`) is the input version of the user who + This (:tl:`InputPeer`) is the input version of the user who sent the message. Similarly to ``input_chat``, this doesn't have things like username or similar, but still useful in some cases. @@ -434,7 +434,7 @@ class NewMessage(_EventBuilder): @property def sender(self): """ - This (:obj:`User`) may make an API call the first time to get + This (:tl:`User`) may make an API call the first time to get the most up to date version of the sender (mostly when the event doesn't belong to a channel), so keep that in mind. @@ -474,8 +474,8 @@ class NewMessage(_EventBuilder): @property def reply_message(self): """ - This (:obj:`Message`, optional) will make an API call the first - time to get the full ``Message`` object that one was replying to, + This optional :tl:`Message` will make an API call the first + time to get the full :tl:`Message` object that one was replying to, so use with care as there is no caching besides local caching yet. """ if not self.message.reply_to_msg_id: @@ -498,14 +498,14 @@ class NewMessage(_EventBuilder): @property def forward(self): """ - The unmodified (:obj:`MessageFwdHeader`, optional). + The unmodified :tl:`MessageFwdHeader`, if present.. """ return self.message.fwd_from @property def media(self): """ - The unmodified (:obj:`MessageMedia`, optional). + The unmodified :tl:`MessageMedia`, if present. """ return self.message.media @@ -513,7 +513,7 @@ class NewMessage(_EventBuilder): def photo(self): """ If the message media is a photo, - this returns the (:obj:`Photo`) object. + this returns the :tl:`Photo` object. """ if isinstance(self.message.media, types.MessageMediaPhoto): photo = self.message.media.photo @@ -524,7 +524,7 @@ class NewMessage(_EventBuilder): def document(self): """ If the message media is a document, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ if isinstance(self.message.media, types.MessageMediaDocument): doc = self.message.media.document @@ -547,7 +547,7 @@ class NewMessage(_EventBuilder): def audio(self): """ If the message media is a document with an Audio attribute, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ return self._document_by_attribute(types.DocumentAttributeAudio, lambda attr: not attr.voice) @@ -556,7 +556,7 @@ class NewMessage(_EventBuilder): def voice(self): """ If the message media is a document with a Voice attribute, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ return self._document_by_attribute(types.DocumentAttributeAudio, lambda attr: attr.voice) @@ -565,7 +565,7 @@ class NewMessage(_EventBuilder): def video(self): """ If the message media is a document with a Video attribute, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ return self._document_by_attribute(types.DocumentAttributeVideo) @@ -573,7 +573,7 @@ class NewMessage(_EventBuilder): def video_note(self): """ If the message media is a document with a Video attribute, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ return self._document_by_attribute(types.DocumentAttributeVideo, lambda attr: attr.round_message) @@ -582,7 +582,7 @@ class NewMessage(_EventBuilder): def gif(self): """ If the message media is a document with an Animated attribute, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ return self._document_by_attribute(types.DocumentAttributeAnimated) @@ -590,7 +590,7 @@ class NewMessage(_EventBuilder): def sticker(self): """ If the message media is a document with a Sticker attribute, - this returns the (:obj:`Document`) object. + this returns the :tl:`Document` object. """ return self._document_by_attribute(types.DocumentAttributeSticker) @@ -689,7 +689,7 @@ class ChatAction(_EventBuilder): new_photo (:obj:`bool`): ``True`` if there's a new chat photo (or it was removed). - photo (:obj:`Photo`, optional): + photo (:tl:`Photo`, optional): The new photo (or ``None`` if it was removed). @@ -793,7 +793,7 @@ class ChatAction(_EventBuilder): @property def pinned_message(self): """ - If ``new_pin`` is ``True``, this returns the (:obj:`Message`) + If ``new_pin`` is ``True``, this returns the (:tl:`Message`) object that was pinned. """ if self._pinned_message == 0: @@ -857,7 +857,7 @@ class ChatAction(_EventBuilder): @property def input_user(self): """ - Input version of the self.user property. + Input version of the ``self.user`` property. """ if self.input_users: return self._input_users[0] @@ -894,7 +894,7 @@ class ChatAction(_EventBuilder): @property def input_users(self): """ - Input version of the self.users property. + Input version of the ``self.users`` property. """ if self._input_users is None and self._user_peers: self._input_users = [] @@ -947,7 +947,7 @@ class UserUpdate(_EventBuilder): recently (:obj:`bool`): ``True`` if the user was seen within a day. - action (:obj:`SendMessageAction`, optional): + action (:tl:`SendMessageAction`, optional): The "typing" action if any the user is performing if any. cancel (:obj:`bool`): diff --git a/telethon/network/mtproto_sender.py b/telethon/network/mtproto_sender.py index 532a8da7..8206fcaa 100644 --- a/telethon/network/mtproto_sender.py +++ b/telethon/network/mtproto_sender.py @@ -24,13 +24,15 @@ __log__ = logging.getLogger(__name__) class MtProtoSender: - """MTProto Mobile Protocol sender - (https://core.telegram.org/mtproto/description). + """ + MTProto Mobile Protocol sender + (https://core.telegram.org/mtproto/description). - Note that this class is not thread-safe, and calling send/receive - from two or more threads at the same time is undefined behaviour. - Rationale: a new connection should be spawned to send/receive requests - in parallel, so thread-safety (hence locking) isn't needed. + Note that this class is not thread-safe, and calling send/receive + from two or more threads at the same time is undefined behaviour. + Rationale: + a new connection should be spawned to send/receive requests + in parallel, so thread-safety (hence locking) isn't needed. """ def __init__(self, session, connection): diff --git a/telethon/telegram_client.py b/telethon/telegram_client.py index 6a3822d5..046838bd 100644 --- a/telethon/telegram_client.py +++ b/telethon/telegram_client.py @@ -213,7 +213,7 @@ class TelegramClient(TelegramBareClient): Whether to force sending as SMS. Returns: - Information about the result of the request. + An instance of :tl:`SentCode`. """ phone = utils.parse_phone(phone) or self._phone phone_hash = self._phone_code_hash.get(phone) @@ -257,8 +257,9 @@ class TelegramClient(TelegramBareClient): This is only required if it is enabled in your account. bot_token (:obj:`str`): - Bot Token obtained by @BotFather to log in as a bot. - Cannot be specified with `phone` (only one of either allowed). + Bot Token obtained by `@BotFather `_ + to log in as a bot. Cannot be specified with ``phone`` (only + one of either allowed). force_sms (:obj:`bool`, optional): Whether to force sending the code request as SMS. @@ -276,8 +277,8 @@ class TelegramClient(TelegramBareClient): Similar to the first name, but for the last. Optional. Returns: - :obj:`TelegramClient`: - This client, so initialization can be chained with `.start()`. + This :obj:`TelegramClient`, so initialization + can be chained with ``.start()``. """ if code_callback is None: @@ -453,7 +454,7 @@ class TelegramClient(TelegramBareClient): Optional last name. Returns: - The new created user. + The new created :tl:`User`. """ if self.is_user_authorized(): self._check_events_pending_resolve() @@ -478,7 +479,7 @@ class TelegramClient(TelegramBareClient): Logs out Telegram and deletes the current ``*.session`` file. Returns: - True if the operation was successful. + ``True`` if the operation was successful. """ try: self(LogOutRequest()) @@ -496,12 +497,12 @@ class TelegramClient(TelegramBareClient): Args: input_peer (:obj:`bool`, optional): - Whether to return the ``InputPeerUser`` version or the normal - ``User``. This can be useful if you just need to know the ID + Whether to return the :tl:`InputPeerUser` version or the normal + :tl:`User`. This can be useful if you just need to know the ID of yourself. Returns: - :obj:`User`: Your own user. + Your own :tl:`User`. """ if input_peer and self._self_input_peer: return self._self_input_peer @@ -541,7 +542,7 @@ class TelegramClient(TelegramBareClient): offset_id (:obj:`int`, optional): The message ID to be used as an offset. - offset_peer (:obj:`InputPeer`, optional): + offset_peer (:tl:`InputPeer`, optional): The peer to be used as an offset. _total (:obj:`list`, optional): @@ -712,10 +713,10 @@ class TelegramClient(TelegramBareClient): entity (:obj:`entity`): To who will it be sent. - message (:obj:`str` | :obj:`Message`): + message (:obj:`str` | :tl:`Message`): The message to be sent, or another message object to resend. - reply_to (:obj:`int` | :obj:`Message`, optional): + reply_to (:obj:`int` | :tl:`Message`, optional): Whether to reply to a message or not. If an integer is provided, it should be the ID of the message that it should reply to. @@ -740,7 +741,7 @@ class TelegramClient(TelegramBareClient): Has no effect when sending a file. Returns: - the sent message. + The sent :tl:`Message`. """ if file is not None: return self.send_file( @@ -806,7 +807,7 @@ class TelegramClient(TelegramBareClient): entity (:obj:`entity`): To which entity the message(s) will be forwarded. - messages (:obj:`list` | :obj:`int` | :obj:`Message`): + messages (:obj:`list` | :obj:`int` | :tl:`Message`): The message(s) to forward, or their integer IDs. from_peer (:obj:`entity`): @@ -815,7 +816,7 @@ class TelegramClient(TelegramBareClient): order for the forward to work. Returns: - The forwarded messages. + The list of forwarded :tl:`Message`. """ if not utils.is_list_like(messages): messages = (messages,) @@ -882,7 +883,7 @@ class TelegramClient(TelegramBareClient): not modified at all. Returns: - the edited message + The edited :tl:`Message`. """ message, msg_entities = self._parse_message_text(message, parse_mode) request = EditMessageRequest( @@ -905,7 +906,7 @@ class TelegramClient(TelegramBareClient): be ``None`` for normal chats, but **must** be present for channels and megagroups. - message_ids (:obj:`list` | :obj:`int` | :obj:`Message`): + message_ids (:obj:`list` | :obj:`int` | :tl:`Message`): The IDs (or ID) or messages to be deleted. revoke (:obj:`bool`, optional): @@ -915,7 +916,7 @@ class TelegramClient(TelegramBareClient): This has no effect on channels or megagroups. Returns: - The affected messages. + The :tl:`AffectedMessages`. """ if not utils.is_list_like(message_ids): message_ids = (message_ids,) @@ -978,7 +979,7 @@ class TelegramClient(TelegramBareClient): you are still free to do so. wait_time (:obj:`int`): - Wait time between different ``GetHistoryRequest``. Use this + Wait time between different :tl:`GetHistoryRequest`. Use this parameter to avoid hitting the ``FloodWaitError`` as needed. If left to ``None``, it will default to 1 second only if the limit is higher than 3000. @@ -987,7 +988,7 @@ class TelegramClient(TelegramBareClient): A single-item list to pass the total parameter by reference. Yields: - Instances of ``telethon.tl.types.Message`` with extra attributes: + Instances of :tl:`Message` with extra attributes: * ``.sender`` = entity of the sender. * ``.fwd_from.sender`` = if fwd_from, who sent it originally. @@ -995,7 +996,7 @@ class TelegramClient(TelegramBareClient): * ``.to`` = entity to which the message was sent. Notes: - Telegram's flood wait limit for ``GetHistoryRequest`` seems to + Telegram's flood wait limit for :tl:`GetHistoryRequest` seems to be around 30 seconds per 3000 messages, therefore a sleep of 1 second is the default for this limit (or above). You may need an higher limit, so you're free to set the ``batch_size`` that @@ -1101,7 +1102,7 @@ class TelegramClient(TelegramBareClient): entity (:obj:`entity`): The chat where these messages are located. - message (:obj:`list` | :obj:`Message`): + message (:obj:`list` | :tl:`Message`): Either a list of messages or a single message. max_id (:obj:`int`): @@ -1172,9 +1173,8 @@ class TelegramClient(TelegramBareClient): search (:obj:`str`, optional): Look for participants with this string in name/username. - filter (:obj:`ChannelParticipantsFilter`, optional): - The filter to be used, if you want e.g. only admins. See - https://lonamiwebs.github.io/Telethon/types/channel_participants_filter.html. + filter (:tl:`ChannelParticipantsFilter`, optional): + The filter to be used, if you want e.g. only admins Note that you might not have permissions for some filter. This has no effect for normal chats or users. @@ -1192,10 +1192,10 @@ class TelegramClient(TelegramBareClient): A single-item list to pass the total parameter by reference. Yields: - The ``User`` objects returned by ``GetParticipantsRequest`` + The :tl:`User` objects returned by :tl:`GetParticipantsRequest` with an additional ``.participant`` attribute which is the - matched ``ChannelParticipant`` type for channels/megagroups - or ``ChatParticipants`` for normal chats. + matched :tl:`ChannelParticipant` type for channels/megagroups + or :tl:`ChatParticipants` for normal chats. """ if isinstance(filter, type): filter = filter() @@ -1362,12 +1362,12 @@ class TelegramClient(TelegramBareClient): A callback function accepting two parameters: ``(sent bytes, total)``. - reply_to (:obj:`int` | :obj:`Message`): + reply_to (:obj:`int` | :tl:`Message`): Same as reply_to from .send_message(). attributes (:obj:`list`, optional): Optional attributes that override the inferred ones, like - ``DocumentAttributeFilename`` and so on. + :tl:`DocumentAttributeFilename` and so on. thumb (:obj:`str` | :obj:`bytes` | :obj:`file`, optional): Optional thumbnail (for videos). @@ -1390,7 +1390,7 @@ class TelegramClient(TelegramBareClient): it will be used to determine metadata from audio and video files. Returns: - The message (or messages) containing the sent file. + The :tl:`Message` (or messages) containing the sent file. """ # First check if the user passed an iterable, in which case # we may want to send as an album if all are photo files. @@ -1551,7 +1551,7 @@ class TelegramClient(TelegramBareClient): return msg def send_voice_note(self, *args, **kwargs): - """Wrapper method around .send_file() with is_voice_note=True""" + """Wrapper method around :meth:`send_file` with is_voice_note=True.""" kwargs['is_voice_note'] = True return self.send_file(*args, **kwargs) @@ -1652,8 +1652,8 @@ class TelegramClient(TelegramBareClient): ``(sent bytes, total)``. Returns: - ``InputFileBig`` if the file size is larger than 10MB, - ``InputSizedFile`` (subclass of ``InputFile``) otherwise. + :tl:`InputFileBig` if the file size is larger than 10MB, + ``InputSizedFile`` (subclass of :tl:`InputFile`) otherwise. """ if isinstance(file, (InputFile, InputFileBig)): return file # Already uploaded @@ -1836,7 +1836,7 @@ class TelegramClient(TelegramBareClient): """ Downloads the given media, or the media from a specified Message. - message (:obj:`Message` | :obj:`Media`): + message (:tl:`Message` | :tl:`Media`): The media or message containing the media that will be downloaded. file (:obj:`str` | :obj:`file`, optional): @@ -1845,7 +1845,7 @@ class TelegramClient(TelegramBareClient): progress_callback (:obj:`callable`, optional): A callback function accepting two parameters: - ``(recv bytes, total)``. + ``(received bytes, total)``. Returns: ``None`` if no media was provided, or if it was Empty. On success @@ -2065,7 +2065,7 @@ class TelegramClient(TelegramBareClient): Downloads the given input location to a file. Args: - input_location (:obj:`InputFileLocation`): + input_location (:tl:`InputFileLocation`): The file location from which the file will be downloaded. file (:obj:`str` | :obj:`file`): @@ -2293,7 +2293,7 @@ class TelegramClient(TelegramBareClient): """ Turns the given entity into a valid Telegram user or chat. - entity (:obj:`str` | :obj:`int` | :obj:`Peer` | :obj:`InputPeer`): + entity (:obj:`str` | :obj:`int` | :tl:`Peer` | :tl:`InputPeer`): The entity (or iterable of entities) to be transformed. If it's a string which can be converted to an integer or starts with '+' it will be resolved as if it were a phone number. @@ -2309,7 +2309,7 @@ class TelegramClient(TelegramBareClient): error will be raised. Returns: - ``User``, ``Chat`` or ``Channel`` corresponding to the input + :tl:`User`, :tl:`Chat` or :tl:`Channel` corresponding to the input entity. """ if utils.is_list_like(entity): @@ -2410,9 +2410,9 @@ class TelegramClient(TelegramBareClient): use this kind of InputUser, InputChat and so on, so this is the most suitable call to make for those cases. - entity (:obj:`str` | :obj:`int` | :obj:`Peer` | :obj:`InputPeer`): + entity (:obj:`str` | :obj:`int` | :tl:`Peer` | :tl:`InputPeer`): The integer ID of an user or otherwise either of a - ``PeerUser``, ``PeerChat`` or ``PeerChannel``, for + :tl:`PeerUser`, :tl:`PeerChat` or :tl:`PeerChannel`, for which to get its ``Input*`` version. If this ``Peer`` hasn't been seen before by the library, the top @@ -2423,7 +2423,7 @@ class TelegramClient(TelegramBareClient): a ValueError will be raised. Returns: - ``InputPeerUser``, ``InputPeerChat`` or ``InputPeerChannel``. + :tl:`InputPeerUser`, :tl:`InputPeerChat` or :tl:`InputPeerChannel`. """ try: # First try to get the entity from cache, otherwise figure it out diff --git a/telethon/tl/custom/dialog.py b/telethon/tl/custom/dialog.py index a2b1a966..86265140 100644 --- a/telethon/tl/custom/dialog.py +++ b/telethon/tl/custom/dialog.py @@ -10,13 +10,13 @@ class Dialog: return instances of this class when calling :meth:`.get_dialogs()`. Args: - dialog (:obj:`Dialog`): + dialog (:tl:`Dialog`): The original ``Dialog`` instance. pinned (:obj:`bool`): Whether this dialog is pinned to the top or not. - message (:obj:`Message`): + message (:tl:`Message`): The last message sent on this dialog. Note that this member will not be updated when new messages arrive, it's only set on creation of the instance. @@ -27,7 +27,7 @@ class Dialog: entity (:obj:`entity`): The entity that belongs to this dialog (user, chat or channel). - input_entity (:obj:`InputPeer`): + input_entity (:tl:`InputPeer`): Input version of the entity. id (:obj:`int`): diff --git a/telethon/tl/custom/draft.py b/telethon/tl/custom/draft.py index fc40c1cf..f52ac6c9 100644 --- a/telethon/tl/custom/draft.py +++ b/telethon/tl/custom/draft.py @@ -128,7 +128,7 @@ class Draft: def send(self, clear=True, parse_mode='md'): """ Sends the contents of this draft to the dialog. This is just a - wrapper around send_message(dialog.input_entity, *args, **kwargs). + wrapper around ``send_message(dialog.input_entity, *args, **kwargs)``. """ self._client.send_message(self._peer, self.text, reply_to=self.reply_to_msg_id, diff --git a/telethon/update_state.py b/telethon/update_state.py index 9f26e3a4..509697a0 100644 --- a/telethon/update_state.py +++ b/telethon/update_state.py @@ -11,8 +11,9 @@ __log__ = logging.getLogger(__name__) class UpdateState: - """Used to hold the current state of processed updates. - To retrieve an update, .poll() should be called. + """ + Used to hold the current state of processed updates. + To retrieve an update, :meth:`poll` should be called. """ WORKER_POLL_TIMEOUT = 5.0 # Avoid waiting forever on the workers diff --git a/telethon/utils.py b/telethon/utils.py index 286853ad..faa1537a 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -38,8 +38,8 @@ VALID_USERNAME_RE = re.compile(r'^[a-zA-Z][\w\d]{3,30}[a-zA-Z\d]$') def get_display_name(entity): """ - Gets the display name for the given entity, if it's an ``User``, - ``Chat`` or ``Channel``. Returns an empty string otherwise. + Gets the display name for the given entity, if it's an :tl:`User`, + :tl:`Chat` or :tl:`Channel`. Returns an empty string otherwise. """ if isinstance(entity, User): if entity.last_name and entity.first_name: @@ -58,7 +58,7 @@ def get_display_name(entity): def get_extension(media): - """Gets the corresponding extension for any Telegram media""" + """Gets the corresponding extension for any Telegram media.""" # Photos are always compressed as .jpg by Telegram if isinstance(media, (UserProfilePhoto, ChatPhoto, MessageMediaPhoto)): @@ -83,8 +83,10 @@ def _raise_cast_fail(entity, target): def get_input_peer(entity, allow_self=True): - """Gets the input peer for the given "entity" (user, chat or channel). - A TypeError is raised if the given entity isn't a supported type.""" + """ + Gets the input peer for the given "entity" (user, chat or channel). + A ``TypeError`` is raised if the given entity isn't a supported type. + """ try: if entity.SUBCLASS_OF_ID == 0xc91c90b6: # crc32(b'InputPeer') return entity @@ -129,7 +131,7 @@ def get_input_peer(entity, allow_self=True): def get_input_channel(entity): - """Similar to get_input_peer, but for InputChannel's alone""" + """Similar to :meth:`get_input_peer`, but for :tl:`InputChannel`'s alone.""" try: if entity.SUBCLASS_OF_ID == 0x40f202fd: # crc32(b'InputChannel') return entity @@ -146,7 +148,7 @@ def get_input_channel(entity): def get_input_user(entity): - """Similar to get_input_peer, but for InputUser's alone""" + """Similar to :meth:`get_input_peer`, but for :tl:`InputUser`'s alone.""" try: if entity.SUBCLASS_OF_ID == 0xe669bf46: # crc32(b'InputUser'): return entity @@ -175,7 +177,7 @@ def get_input_user(entity): def get_input_document(document): - """Similar to get_input_peer, but for documents""" + """Similar to :meth:`get_input_peer`, but for documents""" try: if document.SUBCLASS_OF_ID == 0xf33fdb68: # crc32(b'InputDocument'): return document @@ -198,7 +200,7 @@ def get_input_document(document): def get_input_photo(photo): - """Similar to get_input_peer, but for documents""" + """Similar to :meth:`get_input_peer`, but for photos""" try: if photo.SUBCLASS_OF_ID == 0x846363e0: # crc32(b'InputPhoto'): return photo @@ -218,7 +220,7 @@ def get_input_photo(photo): def get_input_geo(geo): - """Similar to get_input_peer, but for geo points""" + """Similar to :meth:`get_input_peer`, but for geo points""" try: if geo.SUBCLASS_OF_ID == 0x430d225: # crc32(b'InputGeoPoint'): return geo @@ -241,10 +243,11 @@ def get_input_geo(geo): def get_input_media(media, is_photo=False): - """Similar to get_input_peer, but for media. + """ + Similar to :meth:`get_input_peer`, but for media. - If the media is a file location and is_photo is known to be True, - it will be treated as an InputMediaUploadedPhoto. + If the media is a file location and ``is_photo`` is known to be ``True``, + it will be treated as an :tl:`InputMediaUploadedPhoto`. """ try: if media.SUBCLASS_OF_ID == 0xfaf846f4: # crc32(b'InputMedia'): @@ -317,7 +320,7 @@ def get_input_media(media, is_photo=False): def is_image(file): """ - Returns True if the file extension looks like an image file to Telegram. + Returns ``True`` if the file extension looks like an image file to Telegram. """ if not isinstance(file, str): return False @@ -326,23 +329,23 @@ def is_image(file): def is_audio(file): - """Returns True if the file extension looks like an audio file""" + """Returns ``True`` if the file extension looks like an audio file.""" return (isinstance(file, str) and (mimetypes.guess_type(file)[0] or '').startswith('audio/')) def is_video(file): - """Returns True if the file extension looks like a video file""" + """Returns ``True`` if the file extension looks like a video file.""" return (isinstance(file, str) and (mimetypes.guess_type(file)[0] or '').startswith('video/')) def is_list_like(obj): """ - Returns True if the given object looks like a list. + Returns ``True`` if the given object looks like a list. - Checking if hasattr(obj, '__iter__') and ignoring str/bytes is not - enough. Things like open() are also iterable (and probably many + Checking ``if hasattr(obj, '__iter__')`` and ignoring ``str/bytes`` is not + enough. Things like ``open()`` are also iterable (and probably many other things), so just support the commonly known list-like objects. """ return isinstance(obj, (list, tuple, set, dict, @@ -350,7 +353,7 @@ def is_list_like(obj): def parse_phone(phone): - """Parses the given phone, or returns None if it's invalid""" + """Parses the given phone, or returns ``None`` if it's invalid.""" if isinstance(phone, int): return str(phone) else: @@ -365,7 +368,7 @@ def parse_username(username): both the stripped, lowercase username and whether it is a joinchat/ hash (in which case is not lowercase'd). - Returns None if the username is not valid. + Returns ``None`` if the ``username`` is not valid. """ username = username.strip() m = USERNAME_RE.match(username) @@ -386,7 +389,7 @@ def parse_username(username): def _fix_peer_id(peer_id): """ Fixes the peer ID for chats and channels, in case the users - mix marking the ID with the ``Peer()`` constructors. + mix marking the ID with the :tl:`Peer` constructors. """ peer_id = abs(peer_id) if str(peer_id).startswith('100'): @@ -401,7 +404,7 @@ def get_peer_id(peer): chat ID is negated, and channel ID is prefixed with -100. The original ID and the peer type class can be returned with - a call to utils.resolve_id(marked_id). + a call to :meth:`resolve_id(marked_id)`. """ # First we assert it's a Peer TLObject, or early return for integers if isinstance(peer, int): @@ -450,7 +453,7 @@ def get_peer_id(peer): def resolve_id(marked_id): - """Given a marked ID, returns the original ID and its Peer type""" + """Given a marked ID, returns the original ID and its :tl:`Peer` type.""" if marked_id >= 0: return marked_id, PeerUser @@ -461,8 +464,10 @@ def resolve_id(marked_id): def get_appropriated_part_size(file_size): - """Gets the appropriated part size when uploading or downloading files, - given an initial file size""" + """ + Gets the appropriated part size when uploading or downloading files, + given an initial file size. + """ if file_size <= 104857600: # 100MB return 128 if file_size <= 786432000: # 750MB