diff --git a/readthedocs/extra/basic/accessing-the-full-api.rst b/readthedocs/extra/advanced-usage/accessing-the-full-api.rst similarity index 58% rename from readthedocs/extra/basic/accessing-the-full-api.rst rename to readthedocs/extra/advanced-usage/accessing-the-full-api.rst index ab6682db..04659bdb 100644 --- a/readthedocs/extra/basic/accessing-the-full-api.rst +++ b/readthedocs/extra/advanced-usage/accessing-the-full-api.rst @@ -1,33 +1,41 @@ .. _accessing-the-full-api: -========================== +====================== Accessing the Full API -========================== +====================== -The ``TelegramClient`` doesn’t offer a method for every single request -the Telegram API supports. However, it’s very simple to ``.invoke()`` -any request. Whenever you need something, don’t forget to `check the + +The ``TelegramClient`` doesn't offer a method for every single request +the Telegram API supports. However, it's very simple to *call* or *invoke* +any request. Whenever you need something, don't forget to `check the documentation`__ and look for the `method you need`__. There you can go through a sorted list of everything you can do. + +.. note:: + + Removing the hand crafted documentation for methods is still + a work in progress! + + You should also refer to the documentation to see what the objects (constructors) Telegram returns look like. Every constructor inherits -from a common type, and that’s the reason for this distinction. +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`__, +Say ``client.send_message()`` didn't exist, we could use the `search`__ +to look for "message". There we would find `SendMessageRequest`__, which we can work with. Every request is a Python class, and has the parameters needed for you to invoke it. You can also call ``help(request)`` for information on -what input parameters it takes. Remember to “Copy import to the -clipboard”, or your script won’t be aware of this class! Now we have: +what input parameters it takes. Remember to "Copy import to the +clipboard", or your script won't be aware of this class! Now we have: .. code-block:: python from telethon.tl.functions.messages import SendMessageRequest -If you’re going to use a lot of these, you may do: +If you're going to use a lot of these, you may do: .. code-block:: python @@ -53,20 +61,20 @@ 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 +When you're going to invoke an API method, most require you to pass an ``InputUser``, ``InputChat``, or so on, this is why using -``.get_input_entity()`` is more straightforward (and sometimes -immediate, if you know the ID of the user for instance). If you also -need to have information about the whole user, use ``.get_entity()`` -instead: +``.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 +``.get_entity()`` instead: .. code-block:: python entity = client.get_entity('someone') In the later case, when you use the entity, the library will cast it to -its “input” version for you. If you already have the complete user and -want to cache its input version so the library doesn’t have to do this +its "input" version for you. If you already have the complete user and +want to cache its input version so the library doesn't have to do this every time its used, simply call ``.get_input_peer``: .. code-block:: python @@ -83,10 +91,9 @@ request we do: result = client(SendMessageRequest(peer, 'Hello there!')) # __call__ is an alias for client.invoke(request). Both will work -Message sent! Of course, this is only an example. -There are nearly 250 methods available as of layer 73, -and you can use every single of them as you wish. -Remember to use the right types! To sum up: +Message sent! Of course, this is only an example. There are nearly 250 +methods available as of layer 73, and you can use every single of them +as you wish. Remember to use the right types! To sum up: .. code-block:: python @@ -97,16 +104,16 @@ Remember to use the right types! To sum up: .. note:: - Note that some requests have a "hash" parameter. This is **not** your ``api_hash``! - It likely isn't your self-user ``.access_hash`` either. - It's a special hash used by Telegram to only send a difference of new data - that you don't already have with that request, - so you can leave it to 0, and it should work (which means no hash is known yet). + Note that some requests have a "hash" parameter. This is **not** + your ``api_hash``! It likely isn't your self-user ``.access_hash`` either. - For those requests having a "limit" parameter, - you can often set it to zero to signify "return as many items as possible". - This won't work for all of them though, - for instance, in "messages.search" it will actually return 0 items. + It's a special hash used by Telegram to only send a difference of new data + that you don't already have with that request, so you can leave it to 0, + and it should work (which means no hash is known yet). + + For those requests having a "limit" parameter, you can often set it to + zero to signify "return default amount". This won't work for all of them + though, for instance, in "messages.search" it will actually return 0 items. __ https://lonamiwebs.github.io/Telethon @@ -114,4 +121,4 @@ __ 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 \ No newline at end of file +__ https://lonamiwebs.github.io/Telethon/constructors/input_peer_user.html diff --git a/readthedocs/extra/advanced-usage/sessions.rst b/readthedocs/extra/advanced-usage/sessions.rst new file mode 100644 index 00000000..7f1ded9b --- /dev/null +++ b/readthedocs/extra/advanced-usage/sessions.rst @@ -0,0 +1,46 @@ +.. _sessions: + +============== +Session Files +============== + +The first parameter you pass the the constructor of the ``TelegramClient`` is +the ``session``, and defaults to be the session name (or full path). That is, +if you create a ``TelegramClient('anon')`` instance and connect, an +``anon.session`` file will be created on the working directory. + +These database files using ``sqlite3`` contain the required information to +talk to the Telegram servers, such as to which IP the client should connect, +port, authorization key so that messages can be encrypted, and so on. + +These files will by default also save all the input entities that you've seen, +so that you can get information about an user or channel by just their ID. +Telegram will **not** send their ``access_hash`` required to retrieve more +information about them, if it thinks you have already seem them. For this +reason, the library needs to store this information offline. + +The library will by default too save all the entities (chats and channels +with their name and username, and users with the phone too) in the session +file, so that you can quickly access them by username or phone number. + +If you're not going to work with updates, or don't need to cache the +``access_hash`` associated with the entities' ID, you can disable this +by setting ``client.session.save_entities = False``, or pass it as a +parameter to the ``TelegramClient``. + +If you don't want to save the files as a database, you can also create +your custom ``Session`` subclass and override the ``.save()`` and ``.load()`` +methods. For example, you could save it on a database: + + .. code-block:: python + + class DatabaseSession(Session): + def save(): + # serialize relevant data to the database + + def load(): + # load relevant data to the database + + +You should read the ````session.py```` source file to know what "relevant +data" you need to keep track of. diff --git a/readthedocs/extra/advanced-usage/signing-in.rst b/readthedocs/extra/advanced-usage/signing-in.rst deleted file mode 100644 index 08f4fe3d..00000000 --- a/readthedocs/extra/advanced-usage/signing-in.rst +++ /dev/null @@ -1,58 +0,0 @@ -========================= -Signing In -========================= - -.. note:: - Make sure you have gone through :ref:`prelude` already! - - -Two Factor Authorization (2FA) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -If you have Two Factor Authorization (from now on, 2FA) enabled on your account, calling -:meth:`telethon.TelegramClient.sign_in` will raise a `SessionPasswordNeededError`. -When this happens, just :meth:`telethon.TelegramClient.sign_in` again with a ``password=``: - - .. code-block:: python - - import getpass - from telethon.errors import SessionPasswordNeededError - - client.sign_in(phone) - try: - client.sign_in(code=input('Enter code: ')) - except SessionPasswordNeededError: - client.sign_in(password=getpass.getpass()) - -Enabling 2FA -************* - -If you don't have 2FA enabled, but you would like to do so through Telethon, take as example the following code snippet: - - .. code-block:: python - - import os - from hashlib import sha256 - from telethon.tl.functions import account - from telethon.tl.types.account import PasswordInputSettings - - new_salt = client(account.GetPasswordRequest()).new_salt - salt = new_salt + os.urandom(8) # new random salt - - pw = 'secret'.encode('utf-8') # type your new password here - hint = 'hint' - - pw_salted = salt + pw + salt - pw_hash = sha256(pw_salted).digest() - - result = client(account.UpdatePasswordSettingsRequest( - current_password_hash=salt, - new_settings=PasswordInputSettings( - new_salt=salt, - new_password_hash=pw_hash, - hint=hint - ) - )) - -Thanks to `Issue 259 `_ for the tip! - diff --git a/readthedocs/extra/advanced-usage/users-and-chats.rst b/readthedocs/extra/advanced-usage/users-and-chats.rst deleted file mode 100644 index a48a2857..00000000 --- a/readthedocs/extra/advanced-usage/users-and-chats.rst +++ /dev/null @@ -1,324 +0,0 @@ -========================= -Users and Chats -========================= - -.. note:: - Make sure you have gone through :ref:`prelude` already! - -.. contents:: - :depth: 2 - -.. _retrieving-an-entity: - -Retrieving an entity (user or group) -************************************** -An “entity” is used to refer to either an `User`__ or a `Chat`__ -(which includes a `Channel`__). The most straightforward way to get -an entity is to use ``TelegramClient.get_entity()``. This method accepts -either a string, which can be a username, phone number or `t.me`__-like -link, or an integer that will be the ID of an **user**. You can use it -like so: - - .. code-block:: python - - # all of these work - lonami = client.get_entity('lonami') - lonami = client.get_entity('t.me/lonami') - lonami = client.get_entity('https://telegram.dog/lonami') - - # other kind of entities - channel = client.get_entity('telegram.me/joinchat/AAAAAEkk2WdoDrB4-Q8-gg') - contact = client.get_entity('+34xxxxxxxxx') - friend = client.get_entity(friend_id) - -For the last one to work, the library must have “seen” the user at least -once. The library will “see” the user as long as any request contains -them, so if you’ve called ``.get_dialogs()`` for instance, and your -friend was there, the library will know about them. For more, read about -the :ref:`sessions`. - -If you want to get a channel or chat by ID, you need to specify that -they are a channel or a chat. The library can’t infer what they are by -just their ID (unless the ID is marked, but this is only done -internally), so you need to wrap the ID around a `Peer`__ object: - - .. code-block:: python - - from telethon.tl.types import PeerUser, PeerChat, PeerChannel - my_user = client.get_entity(PeerUser(some_id)) - my_chat = client.get_entity(PeerChat(some_id)) - my_channel = client.get_entity(PeerChannel(some_id)) - -**Note** that most requests don’t ask for an ``User``, or a ``Chat``, -but rather for ``InputUser``, ``InputChat``, and so on. If this is the -case, you should prefer ``.get_input_entity()`` over ``.get_entity()``, -as it will be immediate if you provide an ID (whereas ``.get_entity()`` -may need to find who the entity is first). - -Via your open “chats” (dialogs) -------------------------------- - -.. note:: - Please read here: :ref:`retrieving-all-dialogs`. - -Via ResolveUsernameRequest --------------------------- - -This is the request used by ``.get_entity`` internally, but you can also -use it by hand: - -.. code-block:: python - - from telethon.tl.functions.contacts import ResolveUsernameRequest - - result = client(ResolveUsernameRequest('username')) - found_chats = result.chats - found_users = result.users - # result.peer may be a PeerUser, PeerChat or PeerChannel - -See `Peer`__ for more information about this result. - -Via MessageFwdHeader --------------------- - -If all you have is a `MessageFwdHeader`__ after you retrieved a bunch -of messages, this gives you access to the ``from_id`` (if forwarded from -an user) and ``channel_id`` (if forwarded from a channel). Invoking -`GetMessagesRequest`__ also returns a list of ``chats`` and -``users``, and you can find the desired entity there: - - .. code-block:: python - - # Logic to retrieve messages with `GetMessagesRequest´ - messages = foo() - fwd_header = bar() - - user = next(u for u in messages.users if u.id == fwd_header.from_id) - channel = next(c for c in messages.chats if c.id == fwd_header.channel_id) - -Or you can just call ``.get_entity()`` with the ID, as you should have -seen that user or channel before. A call to ``GetMessagesRequest`` may -still be neeed. - -Via GetContactsRequest ----------------------- - -The library will call this for you if you pass a phone number to -``.get_entity``, but again, it can be done manually. If the user you -want to talk to is a contact, you can use `GetContactsRequest`__: - - .. code-block:: python - - from telethon.tl.functions.contacts import GetContactsRequest - from telethon.tl.types.contacts import Contacts - - contacts = client(GetContactsRequest(0)) - if isinstance(contacts, Contacts): - users = contacts.users - contacts = contacts.contacts - -__ https://lonamiwebs.github.io/Telethon/types/user.html -__ https://lonamiwebs.github.io/Telethon/types/chat.html -__ https://lonamiwebs.github.io/Telethon/constructors/channel.html -__ https://t.me -__ https://lonamiwebs.github.io/Telethon/types/peer.html -__ https://lonamiwebs.github.io/Telethon/types/peer.html -__ https://lonamiwebs.github.io/Telethon/constructors/message_fwd_header.html -__ https://lonamiwebs.github.io/Telethon/methods/messages/get_messages.html -__ https://lonamiwebs.github.io/Telethon/methods/contacts/get_contacts.html - - -.. _retrieving-all-dialogs: - -Retrieving all dialogs -*********************** - -There are several ``offset_xyz=`` parameters that have no effect at all, -but there's not much one can do since this is something the server should handle. -Currently, the only way to get all dialogs -(open chats, conversations, etc.) is by using the ``offset_date``: - - .. code-block:: python - - from telethon.tl.functions.messages import GetDialogsRequest - from telethon.tl.types import InputPeerEmpty - from time import sleep - - dialogs = [] - users = [] - chats = [] - - last_date = None - chunk_size = 20 - while True: - result = client(GetDialogsRequest( - offset_date=last_date, - offset_id=0, - offset_peer=InputPeerEmpty(), - limit=chunk_size - )) - dialogs.extend(result.dialogs) - users.extend(result.users) - chats.extend(result.chats) - if not result.messages: - break - last_date = min(msg.date for msg in result.messages) - sleep(2) - - -Joining a chat or channel -******************************* - -Note that `Chat`__\ s are normal groups, and `Channel`__\ s are a -special form of `Chat`__\ s, -which can also be super-groups if their ``megagroup`` member is -``True``. - -Joining a public channel ------------------------- - -Once you have the :ref:`entity ` -of the channel you want to join to, you can -make use of the `JoinChannelRequest`__ to join such channel: - - .. code-block:: python - - from telethon.tl.functions.channels import JoinChannelRequest - client(JoinChannelRequest(channel)) - - # In the same way, you can also leave such channel - from telethon.tl.functions.channels import LeaveChannelRequest - client(LeaveChannelRequest(input_channel)) - -For more on channels, check the `channels namespace`__. - -Joining a private chat or channel ---------------------------------- - -If all you have is a link like this one: -``https://t.me/joinchat/AAAAAFFszQPyPEZ7wgxLtd``, you already have -enough information to join! The part after the -``https://t.me/joinchat/``, this is, ``AAAAAFFszQPyPEZ7wgxLtd`` on this -example, is the ``hash`` of the chat or channel. Now you can use -`ImportChatInviteRequest`__ as follows: - - .. -block:: python - - from telethon.tl.functions.messages import ImportChatInviteRequest - updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg')) - -Adding someone else to such chat or channel -------------------------------------------- - -If you don’t want to add yourself, maybe because you’re already in, you -can always add someone else with the `AddChatUserRequest`__, which -use is very straightforward: - - .. code-block:: python - - from telethon.tl.functions.messages import AddChatUserRequest - - client(AddChatUserRequest( - chat_id, - user_to_add, - fwd_limit=10 # allow the user to see the 10 last messages - )) - -Checking a link without joining -------------------------------- - -If you don’t need to join but rather check whether it’s a group or a -channel, you can use the `CheckChatInviteRequest`__, which takes in -the `hash`__ of said channel or group. - -__ https://lonamiwebs.github.io/Telethon/constructors/chat.html -__ https://lonamiwebs.github.io/Telethon/constructors/channel.html -__ https://lonamiwebs.github.io/Telethon/types/chat.html -__ https://lonamiwebs.github.io/Telethon/methods/channels/join_channel.html -__ https://lonamiwebs.github.io/Telethon/methods/channels/index.html -__ https://lonamiwebs.github.io/Telethon/methods/messages/import_chat_invite.html -__ https://lonamiwebs.github.io/Telethon/methods/messages/add_chat_user.html -__ https://lonamiwebs.github.io/Telethon/methods/messages/check_chat_invite.html -__ https://github.com/LonamiWebs/Telethon/wiki/Joining-a-chat-or-channel#joining-a-private-chat-or-channel - - -Retrieving all chat members (channels too) -****************************************** - -In order to get all the members from a mega-group or channel, you need -to use `GetParticipantsRequest`__. As we can see it needs an -`InputChannel`__, (passing the mega-group or channel you’re going to -use will work), and a mandatory `ChannelParticipantsFilter`__. The -closest thing to “no filter” is to simply use -`ChannelParticipantsSearch`__ with an empty ``'q'`` string. - -If we want to get *all* the members, we need to use a moving offset and -a fixed limit: - - .. code-block:: python - - from telethon.tl.functions.channels import GetParticipantsRequest - from telethon.tl.types import ChannelParticipantsSearch - from time import sleep - - offset = 0 - limit = 100 - all_participants = [] - - while True: - participants = client.invoke(GetParticipantsRequest( - channel, ChannelParticipantsSearch(''), offset, limit - )) - if not participants.users: - break - all_participants.extend(participants.users) - offset += len(participants.users) - # sleep(1) # This line seems to be optional, no guarantees! - -Note that ``GetParticipantsRequest`` returns `ChannelParticipants`__, -which may have more information you need (like the role of the -participants, total count of members, etc.) - -__ https://lonamiwebs.github.io/Telethon/methods/channels/get_participants.html -__ https://lonamiwebs.github.io/Telethon/methods/channels/get_participants.html -__ https://lonamiwebs.github.io/Telethon/types/channel_participants_filter.html -__ https://lonamiwebs.github.io/Telethon/constructors/channel_participants_search.html -__ https://lonamiwebs.github.io/Telethon/constructors/channels/channel_participants.html - - -Recent Actions -******************** - -“Recent actions” is simply the name official applications have given to -the “admin log”. Simply use `GetAdminLogRequest`__ for that, and -you’ll get AdminLogResults.events in return which in turn has the final -`.action`__. - -__ https://lonamiwebs.github.io/Telethon/methods/channels/get_admin_log.html -__ https://lonamiwebs.github.io/Telethon/types/channel_admin_log_event_action.html - - -Increasing View Count in a Channel -**************************************** - -It has been asked `quite`__ `a few`__ `times`__ (really, `many`__), and -while I don’t understand why so many people ask this, the solution is to -use `GetMessagesViewsRequest`__, setting ``increment=True``: - - .. code-block:: python - - - # Obtain `channel' through dialogs or through client.get_entity() or anyhow. - # Obtain `msg_ids' through `.get_message_history()` or anyhow. Must be a list. - - client(GetMessagesViewsRequest( - peer=channel, - id=msg_ids, - increment=True - )) - -__ https://github.com/LonamiWebs/Telethon/issues/233 -__ https://github.com/LonamiWebs/Telethon/issues/305 -__ https://github.com/LonamiWebs/Telethon/issues/409 -__ https://github.com/LonamiWebs/Telethon/issues/447 -__ https://lonamiwebs.github.io/Telethon/methods/messages/get_messages_views.html \ No newline at end of file diff --git a/readthedocs/extra/advanced.rst b/readthedocs/extra/advanced.rst deleted file mode 100644 index 4433116d..00000000 --- a/readthedocs/extra/advanced.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. _prelude: - -Prelude ---------- - -Before reading any specific example, make sure to read the following common steps: - -All the examples assume that you have successfully created a client and you're authorized as follows: - - .. code-block:: python - - from telethon import TelegramClient - - # Use your own values here - api_id = 12345 - api_hash = '0123456789abcdef0123456789abcdef' - phone_number = '+34600000000' - - client = TelegramClient('some_name', api_id, api_hash) - client.connect() # Must return True, otherwise, try again - - if not client.is_user_authorized(): - client.send_code_request(phone_number) - # .sign_in() may raise PhoneNumberUnoccupiedError - # In that case, you need to call .sign_up() to get a new account - client.sign_in(phone_number, input('Enter code: ')) - - # The `client´ is now ready - -Although Python will probably clean up the resources used by the ``TelegramClient``, -you should always ``.disconnect()`` it once you're done: - - .. code-block:: python - - try: - # Code using the client goes here - except: - # No matter what happens, always disconnect in the end - client.disconnect() - -If the examples aren't enough, you're strongly advised to read the source code -for the InteractiveTelegramClient_ for an overview on how you could build your next script. -This example shows a basic usage more than enough in most cases. Even reading the source -for the TelegramClient_ may help a lot! - - -.. _InteractiveTelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/interactive_telegram_client.py -.. _TelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon/telegram_client.py diff --git a/readthedocs/extra/basic/creating-a-client.rst b/readthedocs/extra/basic/creating-a-client.rst index 997386db..58f36125 100644 --- a/readthedocs/extra/basic/creating-a-client.rst +++ b/readthedocs/extra/basic/creating-a-client.rst @@ -1,24 +1,28 @@ .. _creating-a-client: -=================== +================= Creating a Client -=================== +================= + Before working with Telegram's API, you need to get your own API ID and hash: -1. Follow `this link `_ and login with your phone number. +1. Follow `this link `_ and login with your + phone number. 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 be changed later as far as I'm aware. +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! +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! Once that's ready, the next step is to create a ``TelegramClient``. -This class will be your main interface with Telegram's API, and creating one is very simple: +This class will be your main interface with Telegram's API, and creating +one is very simple: .. code-block:: python @@ -31,14 +35,18 @@ This class will be your main interface with Telegram's API, and creating one is client = TelegramClient('some_name', api_id, api_hash) -Note that ``'some_name'`` will be used to save your session (persistent information such as access key and others) -as ``'some_name.session'`` in your disk. This is simply a JSON file which you can (but shouldn't) modify. -Before using the client, you must be connected to Telegram. Doing so is very easy: +Note that ``'some_name'`` will be used to save your session (persistent +information such as access key and others) as ``'some_name.session'`` in +your disk. This is by default a database file using Python's ``sqlite3``. + +Before using the client, you must be connected to Telegram. +Doing so is very easy: ``client.connect() # Must return True, otherwise, try again`` -You may or may not be authorized yet. You must be authorized before you're able to send any request: +You may or may not be authorized yet. You must be authorized +before you're able to send any request: ``client.is_user_authorized() # Returns True if you can send requests`` @@ -52,13 +60,25 @@ If you're not authorized, you need to ``.sign_in()``: # If .sign_in raises SessionPasswordNeeded error, call .sign_in(password=...) # You can import both exceptions from telethon.errors. -``myself`` is your Telegram user. -You can view all the information about yourself by doing ``print(myself.stringify())``. -You're now ready to use the client as you wish! +``myself`` is your Telegram user. You can view all the information about +yourself by doing ``print(myself.stringify())``. You're now ready to use +the client as you wish! Remember that any object returned by the API has +mentioned ``.stringify()`` method, and printing these might prove useful. + +As a full example: + + .. code-block:: python + + client = TelegramClient('anon', api_id, api_hash) + assert client.connect() + if not client.is_user_authorized(): + client.send_code_request(phone_number) + me = client.sign_in(phone_number, input('Enter code: ')) + .. note:: - If you want to use a **proxy**, you have to `install PySocks`__ (via pip or manual) - and then set the appropriated parameters: + If you want to use a **proxy**, you have to `install PySocks`__ + (via pip or manual) and then set the appropriated parameters: .. code-block:: python @@ -72,5 +92,58 @@ You're now ready to use the client as you wish! consisting of parameters described `here`__. + +Two Factor Authorization (2FA) +****************************** + +If you have Two Factor Authorization (from now on, 2FA) enabled on your +account, calling :meth:`telethon.TelegramClient.sign_in` will raise a +`SessionPasswordNeededError`. When this happens, just +:meth:`telethon.TelegramClient.sign_in` again with a ``password=``: + + .. code-block:: python + + import getpass + from telethon.errors import SessionPasswordNeededError + + client.sign_in(phone) + try: + client.sign_in(code=input('Enter code: ')) + except SessionPasswordNeededError: + client.sign_in(password=getpass.getpass()) + + +If you don't have 2FA enabled, but you would like to do so through Telethon, +take as example the following code snippet: + + .. code-block:: python + + import os + from hashlib import sha256 + from telethon.tl.functions import account + from telethon.tl.types.account import PasswordInputSettings + + new_salt = client(account.GetPasswordRequest()).new_salt + salt = new_salt + os.urandom(8) # new random salt + + pw = 'secret'.encode('utf-8') # type your new password here + hint = 'hint' + + pw_salted = salt + pw + salt + pw_hash = sha256(pw_salted).digest() + + result = client(account.UpdatePasswordSettingsRequest( + current_password_hash=salt, + new_settings=PasswordInputSettings( + new_salt=salt, + new_password_hash=pw_hash, + hint=hint + ) + )) + +Thanks to `Issue 259 `_ +for the tip! + + __ https://github.com/Anorov/PySocks#installation -__ https://github.com/Anorov/PySocks#usage-1%3E \ No newline at end of file +__ https://github.com/Anorov/PySocks#usage-1%3E diff --git a/readthedocs/extra/basic/entities.rst b/readthedocs/extra/basic/entities.rst new file mode 100644 index 00000000..c03ec6ce --- /dev/null +++ b/readthedocs/extra/basic/entities.rst @@ -0,0 +1,87 @@ +========================= +Users, Chats and Channels +========================= + + +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 save bandwidth, the API also makes use of their "input" versions. +The input version of an entity (e.g. ``InputPeerUser``, ``InputChat``, +etc.) only contains the minimum required information that's required +for Telegram to be able to identify who you're referring to: their 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`` +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". + +Luckily, the library tries to simplify this mess the best it can. + + +Getting entities +**************** + +Through the use of the :ref:`sessions`, the library will automatically +remember the ID and hash pair, along with some extra information, so +you're able to just do this: + + .. code-block:: python + + # dialogs are the "conversations you have open" + # this method returns a list of Dialog, which + # have the .entity attribute and other information. + dialogs = client.get_dialogs(limit=200) + + # all of these work and do the same + lonami = client.get_entity('lonami') + lonami = client.get_entity('t.me/lonami') + lonami = client.get_entity('https://telegram.dog/lonami') + + # other kind of entities + channel = client.get_entity('telegram.me/joinchat/AAAAAEkk2WdoDrB4-Q8-gg') + contact = client.get_entity('+34xxxxxxxxx') + friend = client.get_entity(friend_id) + + # using peers/input peers (note that the API may return these) + # users, chats and channels may all have the same ID, so it's + # necessary to wrap (at least) chat and channels inside Peer. + from telethon.tl.types import PeerUser, PeerChat, PeerChannel + my_user = client.get_entity(PeerUser(some_id)) + my_chat = client.get_entity(PeerChat(some_id)) + my_channel = client.get_entity(PeerChannel(some_id)) + + +All methods in the :ref:`telegram-client` call ``.get_entity()`` to further +save you from the hassle of doing so manually, so doing things like +``client.send_message('lonami', 'hi!')`` is possible. + +Every entity the library "sees" (in any response to any call) will by +default be cached in the ``.session`` file, to avoid performing +unnecessary API calls. If the entity cannot be found, some calls +like ``ResolveUsernameRequest`` or ``GetContactsRequest`` may be +made to obtain the required information. + + +Entities vs. Input Entities +*************************** + +As we mentioned before, 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. + +**You should always favour ``.get_input_entity()``** over ``.get_entity()`` +for this reason! Calling the latter will always make an API call to get +the most recent information about said entity, but invoking requests don't +need this information, just the ``InputPeer``. Only use ``.get_entity()`` +if you need to get actual information, like the username, name, title, etc. +of the entity. diff --git a/readthedocs/extra/basic/getting-started.rst b/readthedocs/extra/basic/getting-started.rst index bad3ea30..de0b3baf 100644 --- a/readthedocs/extra/basic/getting-started.rst +++ b/readthedocs/extra/basic/getting-started.rst @@ -3,13 +3,13 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +=============== +Getting Started +=============== -================= -Getting Started! -================= Simple Installation -********************* +******************* ``pip install telethon`` @@ -17,7 +17,7 @@ Simple Installation Creating a client -************** +***************** .. code-block:: python @@ -39,8 +39,9 @@ Creating a client **More details**: :ref:`creating-a-client` -Simple Stuff -************** +Basic Usage +*********** + .. code-block:: python print(me.stringify()) @@ -52,3 +53,5 @@ Simple Stuff total, messages, senders = client.get_message_history('username') client.download_media(messages[0]) + **More details**: :ref:`telegram-client` + diff --git a/readthedocs/extra/basic/installation.rst b/readthedocs/extra/basic/installation.rst index ecad699b..03aed393 100644 --- a/readthedocs/extra/basic/installation.rst +++ b/readthedocs/extra/basic/installation.rst @@ -1,18 +1,20 @@ .. _installation: -================= +============ Installation -================= +============ Automatic Installation -^^^^^^^^^^^^^^^^^^^^^^^ +********************** + To install Telethon, simply do: ``pip install telethon`` -If you get something like ``"SyntaxError: invalid syntax"`` or any other error while installing, -it's probably because ``pip`` defaults to Python 2, which is not supported. Use ``pip3`` instead. +If you get something like ``"SyntaxError: invalid syntax"`` or any other +error while installing/importing the library, it's probably because ``pip`` +defaults to Python 2, which is not supported. Use ``pip3`` instead. If you already have the library installed, upgrade with: @@ -20,7 +22,7 @@ If you already have the library installed, upgrade with: You can also install the library directly from GitHub or a fork: - .. code-block:: python + .. code-block:: sh # pip install git+https://github.com/LonamiWebs/Telethon.git or @@ -32,13 +34,15 @@ If you don't have root access, simply pass the ``--user`` flag to the pip comman Manual Installation -^^^^^^^^^^^^^^^^^^^^ +******************* -1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and ``rsa`` (`GitHub`__ | `PyPi`__) modules: +1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and + ``rsa`` (`GitHub`__ | `PyPi`__) modules: ``sudo -H pip install pyaes rsa`` -2. Clone Telethon's GitHub repository: ``git clone https://github.com/LonamiWebs/Telethon.git`` +2. Clone Telethon's GitHub repository: + ``git clone https://github.com/LonamiWebs/Telethon.git`` 3. Enter the cloned repository: ``cd Telethon`` @@ -50,22 +54,15 @@ To generate the documentation, ``cd docs`` and then ``python3 generate.py``. Optional dependencies -^^^^^^^^^^^^^^^^^^^^^^^^ - -If you're using the library under ARM (or even if you aren't), -you may want to install ``sympy`` through ``pip`` for a substantial speed-up -when generating the keys required to connect to Telegram -(you can of course do this on desktop too). See `issue #199`__ for more. - -If ``libssl`` is available on your system, it will also be used wherever encryption is needed. - -If neither of these are available, a pure Python callback will be used instead, -so you can still run the library wherever Python is available! +********************* +If ``libssl`` is available on your system, it will be used wherever encryption +is needed, but otherwise it will fall back to pure Python implementation so it +will also work without it. __ https://github.com/ricmoo/pyaes __ https://pypi.python.org/pypi/pyaes __ https://github.com/sybrenstuvel/python-rsa/ __ https://pypi.python.org/pypi/rsa/3.4.2 -__ https://github.com/LonamiWebs/Telethon/issues/199 \ No newline at end of file +__ https://github.com/LonamiWebs/Telethon/issues/199 diff --git a/readthedocs/extra/basic/sending-requests.rst b/readthedocs/extra/basic/sending-requests.rst deleted file mode 100644 index 160e2259..00000000 --- a/readthedocs/extra/basic/sending-requests.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. _sending-requests: - -================== -Sending Requests -================== - -Since we're working with Python, one must not forget that they can do ``help(client)`` or ``help(TelegramClient)`` -at any time for a more detailed description and a list of all the available methods. -Calling ``help()`` from an interactive Python session will always list all the methods for any object, even yours! - -Interacting with the Telegram API is done through sending **requests**, -this is, any "method" listed on the API. There are a few methods on the ``TelegramClient`` class -that abstract you from the need of manually importing the requests you need. - -For instance, retrieving your own user can be done in a single line: - - ``myself = client.get_me()`` - -Internally, this method has sent a request to Telegram, who replied with the information about your own user. - -If you want to retrieve any other user, chat or channel (channels are a special subset of chats), -you want to retrieve their "entity". This is how the library refers to either of these: - - .. code-block:: python - - # The method will infer that you've passed an username - # It also accepts phone numbers, and will get the user - # from your contact list. - lonami = client.get_entity('lonami') - -Note that saving and using these entities will be more important when Accessing the Full API. -For now, this is a good way to get information about an user or chat. - -Other common methods for quick scripts are also available: - - .. code-block:: python - - # Sending a message (use an entity/username/etc) - client.send_message('TheAyyBot', 'ayy') - - # Sending a photo, or a file - client.send_file(myself, '/path/to/the/file.jpg', force_document=True) - - # Downloading someone's profile photo. File is saved to 'where' - where = client.download_profile_photo(someone) - - # Retrieving the message history - total, messages, senders = client.get_message_history(someone) - - # Downloading the media from a specific message - # You can specify either a directory, a filename, or nothing at all - where = client.download_media(message, '/path/to/output') - -Remember that you can call ``.stringify()`` to any object Telegram returns to pretty print it. -Calling ``str(result)`` does the same operation, but on a single line. diff --git a/readthedocs/extra/basic/sessions.rst b/readthedocs/extra/basic/sessions.rst deleted file mode 100644 index f55d9703..00000000 --- a/readthedocs/extra/basic/sessions.rst +++ /dev/null @@ -1,48 +0,0 @@ -.. _sessions: - -============== -Session Files -============== - -The first parameter you pass the constructor of the -``TelegramClient`` is the ``session``, and defaults to be the session -name (or full path). That is, if you create a ``TelegramClient('anon')`` -instance and connect, an ``anon.session`` file will be created on the -working directory. - -These JSON session files contain the required information to talk to the -Telegram servers, such as to which IP the client should connect, port, -authorization key so that messages can be encrypted, and so on. - -These files will by default also save all the input entities that you’ve -seen, so that you can get information about an user or channel by just -their ID. Telegram will **not** send their ``access_hash`` required to -retrieve more information about them, if it thinks you have already seem -them. For this reason, the library needs to store this information -offline. - -The library will by default too save all the entities (users with their -name, username, chats and so on) **in memory**, not to disk, so that you -can quickly access them by username or phone number. This can be -disabled too. Run ``help(client.session.entities)`` to see the available -methods (or ``help(EntityDatabase)``). - -If you’re not going to work without updates, or don’t need to cache the -``access_hash`` associated with the entities’ ID, you can disable this -by setting ``client.session.save_entities = False``. - -If you don’t want to save the files as JSON, you can also create your -custom ``Session`` subclass and override the ``.save()`` and ``.load()`` -methods. For example, you could save it on a database: - - .. code-block:: python - - class DatabaseSession(Session): - def save(): - # serialize relevant data to the database - - def load(): - # load relevant data to the database - -You should read the ``session.py`` source file to know what “relevant -data” you need to keep track of. diff --git a/readthedocs/extra/basic/telegram-client.rst b/readthedocs/extra/basic/telegram-client.rst new file mode 100644 index 00000000..5663f533 --- /dev/null +++ b/readthedocs/extra/basic/telegram-client.rst @@ -0,0 +1,99 @@ +.. _telegram-client: + +============== +TelegramClient +============== + + +Introduction +************ + +The ``TelegramClient`` is the central class of the library, the one +you will be using most of the time. For this reason, it's important +to know what it offers. + +Since we're working with Python, one must not forget that we can do +``help(client)`` or ``help(TelegramClient)`` at any time for a more +detailed description and a list of all the available methods. Calling +``help()`` from an interactive Python session will always list all the +methods for any object, even yours! + +Interacting with the Telegram API is done through sending **requests**, +this is, any "method" listed on the API. There are a few methods (and +growing!) on the ``TelegramClient`` class that abstract you from the +need of manually importing the requests you need. + +For instance, retrieving your own user can be done in a single line: + + ``myself = client.get_me()`` + +Internally, this method has sent a request to Telegram, who replied with +the information about your own user, and then the desired information +was extracted from their response. + +If you want to retrieve any other user, chat or channel (channels are a +special subset of chats), you want to retrieve their "entity". This is +how the library refers to either of these: + + .. code-block:: python + + # The method will infer that you've passed an username + # It also accepts phone numbers, and will get the user + # from your contact list. + lonami = client.get_entity('lonami') + +The so called "entities" are another important whole concept on its own, +and you should +Note that saving and using these entities will be more important when +Accessing the Full API. For now, this is a good way to get information +about an user or chat. + +Other common methods for quick scripts are also available: + + .. code-block:: python + + # Sending a message (use an entity/username/etc) + client.send_message('TheAyyBot', 'ayy') + + # Sending a photo, or a file + client.send_file(myself, '/path/to/the/file.jpg', force_document=True) + + # Downloading someone's profile photo. File is saved to 'where' + where = client.download_profile_photo(someone) + + # Retrieving the message history + messages = client.get_message_history(someone) + + # Downloading the media from a specific message + # You can specify either a directory, a filename, or nothing at all + where = client.download_media(message, '/path/to/output') + + # Call .disconnect() when you're done + client.disconnect() + +Remember that you can call ``.stringify()`` to any object Telegram returns +to pretty print it. Calling ``str(result)`` does the same operation, but on +a single line. + + +Available methods +***************** + +This page lists all the "handy" methods available for you to use in the +``TelegramClient`` class. These are simply wrappers around the "raw" +Telegram API, making it much more manageable and easier to work with. + +Please refer to :ref:`accessing-the-full-api` if these aren't enough, +and don't be afraid to read the source code of the InteractiveTelegramClient_ +or even the TelegramClient_ itself to learn how it works. + + +.. _InteractiveTelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/interactive_telegram_client.py +.. _TelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon/telegram_client.py + + + +.. automodule:: telethon.telegram_client + :members: + :undoc-members: + :show-inheritance: diff --git a/readthedocs/extra/basic/working-with-updates.rst b/readthedocs/extra/basic/working-with-updates.rst index c5d9e919..bb78eb97 100644 --- a/readthedocs/extra/basic/working-with-updates.rst +++ b/readthedocs/extra/basic/working-with-updates.rst @@ -14,23 +14,24 @@ The library can run in four distinguishable modes: - With several worker threads that run your update handlers. - A mix of the above. -Since this section is about updates, we'll describe the simplest way to work with them. - -.. warning:: - Remember that you should always call ``client.disconnect()`` once you're done. +Since this section is about updates, we'll describe the simplest way to +work with them. Using multiple workers -^^^^^^^^^^^^^^^^^^^^^^^ +********************** -When you create your client, simply pass a number to the ``update_workers`` parameter: +When you create your client, simply pass a number to the +``update_workers`` parameter: ``client = TelegramClient('session', api_id, api_hash, update_workers=4)`` -4 workers should suffice for most cases (this is also the default on `Python Telegram Bot`__). -You can set this value to more, or even less if you need. +4 workers should suffice for most cases (this is also the default on +`Python Telegram Bot`__). You can set this value to more, or even less +if you need. -The next thing you want to do is to add a method that will be called when an `Update`__ arrives: +The next thing you want to do is to add a method that will be called when +an `Update`__ arrives: .. code-block:: python @@ -41,7 +42,8 @@ The next thing you want to do is to add a method that will be called when an `Up # do more work here, or simply sleep! That's it! Now let's do something more interesting. -Every time an user talks to use, let's reply to them with the same text reversed: +Every time an user talks to use, let's reply to them with the same +text reversed: .. code-block:: python @@ -56,16 +58,18 @@ Every time an user talks to use, let's reply to them with the same text reversed input('Press enter to stop this!') client.disconnect() -We only ask you one thing: don't keep this running for too long, or your contacts will go mad. +We only ask you one thing: don't keep this running for too long, or your +contacts will go mad. Spawning no worker at all -^^^^^^^^^^^^^^^^^^^^^^^^^^ +************************* -All the workers do is loop forever and poll updates from a queue that is filled from the ``ReadThread``, -responsible for reading every item off the network. -If you only need a worker and the ``MainThread`` would be doing no other job, -this is the preferred way. You can easily do the same as the workers like so: +All the workers do is loop forever and poll updates from a queue that is +filled from the ``ReadThread``, responsible for reading every item off +the network. If you only need a worker and the ``MainThread`` would be +doing no other job, this is the preferred way. You can easily do the same +as the workers like so: .. code-block:: python @@ -81,24 +85,27 @@ this is the preferred way. You can easily do the same as the workers like so: client.disconnect() -Note that ``poll`` accepts a ``timeout=`` parameter, -and it will return ``None`` if other thread got the update before you could or if the timeout expired, -so it's important to check ``if not update``. +Note that ``poll`` accepts a ``timeout=`` parameter, and it will return +``None`` if other thread got the update before you could or if the timeout +expired, so it's important to check ``if not update``. -This can coexist with the rest of ``N`` workers, or you can set it to ``0`` additional workers: +This can coexist with the rest of ``N`` workers, or you can set it to ``0`` +additional workers: ``client = TelegramClient('session', api_id, api_hash, update_workers=0)`` -You **must** set it to ``0`` (or other number), as it defaults to ``None`` and there is a different. -``None`` workers means updates won't be processed *at all*, -so you must set it to some value (0 or greater) if you want ``client.updates.poll()`` to work. +You **must** set it to ``0`` (or other number), as it defaults to ``None`` +and there is a different. ``None`` workers means updates won't be processed +*at all*, so you must set it to some value (``0`` or greater) if you want +``client.updates.poll()`` to work. Using the main thread instead the ``ReadThread`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +************************************************ -If you have no work to do on the ``MainThread`` and you were planning to have a ``while True: sleep(1)``, -don't do that. Instead, don't spawn the secondary ``ReadThread`` at all like so: +If you have no work to do on the ``MainThread`` and you were planning to have +a ``while True: sleep(1)``, don't do that. Instead, don't spawn the secondary +``ReadThread`` at all like so: .. code-block:: python @@ -111,8 +118,8 @@ And then ``.idle()`` from the ``MainThread``: ``client.idle()`` -You can stop it with :kbd:`Control+C`, -and you can configure the signals to be used in a similar fashion to `Python Telegram Bot`__. +You can stop it with :kbd:`Control+C`, and you can configure the signals +to be used in a similar fashion to `Python Telegram Bot`__. As a complete example: @@ -132,4 +139,4 @@ As a complete example: __ https://python-telegram-bot.org/ __ https://lonamiwebs.github.io/Telethon/types/update.html -__ https://github.com/python-telegram-bot/python-telegram-bot/blob/4b3315db6feebafb94edcaa803df52bb49999ced/telegram/ext/updater.py#L460 \ No newline at end of file +__ https://github.com/python-telegram-bot/python-telegram-bot/blob/4b3315db6feebafb94edcaa803df52bb49999ced/telegram/ext/updater.py#L460 diff --git a/readthedocs/extra/developing/api-status.rst b/readthedocs/extra/developing/api-status.rst new file mode 100644 index 00000000..b5092dad --- /dev/null +++ b/readthedocs/extra/developing/api-status.rst @@ -0,0 +1,54 @@ +========== +API Status +========== + + +In an attempt to help everyone who works with the Telegram API, the +library will by default report all *Remote Procedure Call* errors to +`RPC PWRTelegram `__, a public database +anyone can query, made by `Daniil `__. All the +information sent is a ``GET`` request with the error code, error message +and method used. + +If you still would like to opt out, simply set +``client.session.report_errors = False`` to disable this feature, or +pass ``report_errors=False`` as a named parameter when creating a +``TelegramClient`` instance. However Daniil would really thank you if +you helped him (and everyone) by keeping it on! + +Querying the API status +*********************** + +The API is accessed through ``GET`` requests, which can be made for +instance through ``curl``. A JSON response will be returned. + +**All known errors and their description**: + +.. code:: bash + + curl https://rpc.pwrtelegram.xyz/?all + +**Error codes for a specific request**: + +.. code:: bash + + curl https://rpc.pwrtelegram.xyz/?for=messages.sendMessage + +**Number of ``RPC_CALL_FAIL``\ 's**: + +.. code:: bash + + curl https://rpc.pwrtelegram.xyz/?rip # last hour + curl https://rpc.pwrtelegram.xyz/?rip=$(time()-60) # last minute + +**Description of errors**: + +.. code:: bash + + curl https://rpc.pwrtelegram.xyz/?description_for=SESSION_REVOKED + +**Code of a specific error**: + +.. code:: bash + + curl https://rpc.pwrtelegram.xyz/?code_for=STICKERSET_INVALID diff --git a/readthedocs/extra/developing/coding-style.rst b/readthedocs/extra/developing/coding-style.rst new file mode 100644 index 00000000..c629034c --- /dev/null +++ b/readthedocs/extra/developing/coding-style.rst @@ -0,0 +1,22 @@ +============ +Coding Style +============ + + +Basically, make it **readable**, while keeping the style similar to the +code of whatever file you're working on. + +Also note that not everyone has 4K screens for their primary monitors, +so please try to stick to the 80-columns limit. This makes it easy to +``git diff`` changes from a terminal before committing changes. If the +line has to be long, please don't exceed 120 characters. + +For the commit messages, please make them *explanatory*. Not only +they're helpful to troubleshoot when certain issues could have been +introduced, but they're also used to construct the change log once a new +version is ready. + +If you don't know enough Python, I strongly recommend reading `Dive Into +Python 3 `__, available online for +free. For instance, remember to do ``if x is None`` or +``if x is not None`` instead ``if x == None``! diff --git a/readthedocs/extra/developing/philosophy.rst b/readthedocs/extra/developing/philosophy.rst new file mode 100644 index 00000000..f779be2b --- /dev/null +++ b/readthedocs/extra/developing/philosophy.rst @@ -0,0 +1,25 @@ +========== +Philosophy +========== + + +The intention of the library is to have an existing MTProto library +existing with hardly any dependencies (indeed, wherever Python is +available, you can run this library). + +Being written in Python means that performance will be nowhere close to +other implementations written in, for instance, Java, C++, Rust, or +pretty much any other compiled language. However, the library turns out +to actually be pretty decent for common operations such as sending +messages, receiving updates, or other scripting. Uploading files may be +notably slower, but if you would like to contribute, pull requests are +appreciated! + +If ``libssl`` is available on your system, the library will make use of +it to speed up some critical parts such as encrypting and decrypting the +messages. Files will notably be sent and downloaded faster. + +The main focus is to keep everything clean and simple, for everyone to +understand how working with MTProto and Telegram works. Don't be afraid +to read the source, the code won't bite you! It may prove useful when +using the library on your own use cases. diff --git a/readthedocs/extra/developing/project-structure.rst b/readthedocs/extra/developing/project-structure.rst new file mode 100644 index 00000000..d40c6031 --- /dev/null +++ b/readthedocs/extra/developing/project-structure.rst @@ -0,0 +1,43 @@ +================= +Project Structure +================= + + +Main interface +************** + +The library itself is under the ``telethon/`` directory. The +``__init__.py`` file there exposes the main ``TelegramClient``, a class +that servers as a nice interface with the most commonly used methods on +Telegram such as sending messages, retrieving the message history, +handling updates, etc. + +The ``TelegramClient`` inherits the ``TelegramBareClient``. The later is +basically a pruned version of the ``TelegramClient``, which knows basic +stuff like ``.invoke()``\ 'ing requests, downloading files, or switching +between data centers. This is primary to keep the method count per class +and file low and manageable. + +Both clients make use of the ``network/mtproto_sender.py``. The +``MtProtoSender`` class handles packing requests with the ``salt``, +``id``, ``sequence``, etc., and also handles how to process responses +(i.e. pong, RPC errors). This class communicates through Telegram via +its ``.connection`` member. + +The ``Connection`` class uses a ``extensions/tcp_client``, a C#-like +``TcpClient`` to ease working with sockets in Python. All the +``TcpClient`` know is how to connect through TCP and writing/reading +from the socket with optional cancel. + +The ``Connection`` class bundles up all the connections modes and sends +and receives the messages accordingly (TCP full, obfuscated, +intermediate…). + +Auto-generated code +******************* + +The files under ``telethon_generator/`` are used to generate the code +that gets placed under ``telethon/tl/``. The ``TLGenerator`` takes in a +``.tl`` file, and spits out the generated classes which represent, as +Python classes, the request and types defined in the ``.tl`` file. It +also constructs an index so that they can be imported easily. diff --git a/readthedocs/extra/developing/telegram-api-in-other-languages.rst b/readthedocs/extra/developing/telegram-api-in-other-languages.rst new file mode 100644 index 00000000..0adeb988 --- /dev/null +++ b/readthedocs/extra/developing/telegram-api-in-other-languages.rst @@ -0,0 +1,64 @@ +=============================== +Telegram API in Other Languages +=============================== + + +Telethon was made for **Python**, and as far as I know, there is no +*exact* port to other languages. However, there *are* other +implementations made by awesome people (one needs to be awesome to +understand the official Telegram documentation) on several languages +(even more Python too), listed below: + +C +* + +Possibly the most well-known unofficial open source implementation out +there by `**@vysheng** `__, +```tgl`` `__, and its console client +```telegram-cli`` `__. Latest development +has been moved to `BitBucket `__. + +JavaScript +********** + +`**@zerobias** `__ is working on +```telegram-mtproto`` `__, +a work-in-progress JavaScript library installable via +```npm`` `__. + +Kotlin +****** + +`Kotlogram `__ is a Telegram +implementation written in Kotlin (the now +`official `__ +language for +`Android `__) by +`**@badoualy** `__, currently as a beta– +yet working. + +PHP +*** + +A PHP implementation is also available thanks to +`**@danog** `__ and his +`MadelineProto `__ project, with +a very nice `online +documentation `__ too. + +Python +****** + +A fairly new (as of the end of 2017) Telegram library written from the +ground up in Python by +`**@delivrance** `__ and his +`Pyrogram `__ library! No hard +feelings Dan and good luck dealing with some of your users ;) + +Rust +**** + +Yet another work-in-progress implementation, this time for Rust thanks +to `**@JuanPotato** `__ under the fancy +name of `Vail `__. This one is very +early still, but progress is being made at a steady rate. diff --git a/readthedocs/extra/developing/test-servers.rst b/readthedocs/extra/developing/test-servers.rst new file mode 100644 index 00000000..2ba66897 --- /dev/null +++ b/readthedocs/extra/developing/test-servers.rst @@ -0,0 +1,32 @@ +============ +Test Servers +============ + + +To run Telethon on a test server, use the following code: + + .. code-block:: python + + client = TelegramClient(None, api_id, api_hash) + client.session.server_address = '149.154.167.40' + client.connect() + +You can check your ``'test ip'`` on https://my.telegram.org. + +You should set ``None`` session so to ensure you're generating a new +authorization key for it (it would fail if you used a session where you +had previously connected to another data center). + +Once you're connected, you'll likely need to ``.sign_up()``. Remember +`anyone can access the phone you +choose `__, +so don't store sensitive data here: + + .. code-block:: python + + from random import randint + + dc_id = '2' # Change this to the DC id of the test server you chose + phone = '99966' + dc_id + str(randint(9999)).zfill(4) + client.send_code_request(phone) + client.sign_up(dc_id * 5, 'Some', 'Name') diff --git a/readthedocs/extra/developing/tips-for-porting-the-project.rst b/readthedocs/extra/developing/tips-for-porting-the-project.rst new file mode 100644 index 00000000..c7135096 --- /dev/null +++ b/readthedocs/extra/developing/tips-for-porting-the-project.rst @@ -0,0 +1,17 @@ +============================ +Tips for Porting the Project +============================ + + +If you're going to use the code on this repository to guide you, please +be kind and don't forget to mention it helped you! + +You should start by reading the source code on the `first +release `__ of +the project, and start creating a ``MtProtoSender``. Once this is made, +you should write by hand the code to authenticate on the Telegram's +server, which are some steps required to get the key required to talk to +them. Save it somewhere! Then, simply mimic, or reinvent other parts of +the code, and it will be ready to go within a few days. + +Good luck! diff --git a/readthedocs/extra/developing/understanding-the-type-language.rst b/readthedocs/extra/developing/understanding-the-type-language.rst new file mode 100644 index 00000000..c82063ef --- /dev/null +++ b/readthedocs/extra/developing/understanding-the-type-language.rst @@ -0,0 +1,35 @@ +=============================== +Understanding the Type Language +=============================== + + +`Telegram's Type Language `__ +(also known as TL, found on ``.tl`` files) is a concise way to define +what other programming languages commonly call classes or structs. + +Every definition is written as follows for a Telegram object is defined +as follows: + +.. code:: tl + + name#id argument_name:argument_type = CommonType + +This means that in a single line you know what the ``TLObject`` name is. +You know it's unique ID, and you know what arguments it has. It really +isn't that hard to write a generator for generating code to any +platform! + +The generated code should also be able to *encode* the ``TLObject`` (let +this be a request or a type) into bytes, so they can be sent over the +network. This isn't a big deal either, because you know how the +``TLObject``\ 's are made, and how the types should be serialized. + +You can either write your own code generator, or use the one this +library provides, but please be kind and keep some special mention to +this project for helping you out. + +This is only a introduction. The ``TL`` language is not *that* easy. But +it's not that hard either. You're free to sniff the +``telethon_generator/`` files and learn how to parse other more complex +lines, such as ``flags`` (to indicate things that may or may not be +written at all) and ``vector``\ 's. diff --git a/readthedocs/extra/advanced-usage/bots.rst b/readthedocs/extra/examples/bots.rst similarity index 77% rename from readthedocs/extra/advanced-usage/bots.rst rename to readthedocs/extra/examples/bots.rst index 091eada1..b231e200 100644 --- a/readthedocs/extra/advanced-usage/bots.rst +++ b/readthedocs/extra/examples/bots.rst @@ -1,13 +1,14 @@ -====== +==== Bots -====== +==== + Talking to Inline Bots -^^^^^^^^^^^^^^^^^^^^^^ +********************** -You can query an inline bot, such as `@VoteBot`__ -(note, *query*, not *interact* with a voting message), by making use of -the `GetInlineBotResultsRequest`__ request: +You can query an inline bot, such as `@VoteBot`__ (note, *query*, +not *interact* with a voting message), by making use of the +`GetInlineBotResultsRequest`__ request: .. code-block:: python @@ -32,11 +33,10 @@ And you can select any of their results by using Talking to Bots with special reply markup -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +***************************************** To interact with a message that has a special reply markup, such as -`@VoteBot`__ polls, you would use -`GetBotCallbackAnswerRequest`__: +`@VoteBot`__ polls, you would use `GetBotCallbackAnswerRequest`__: .. code-block:: python @@ -48,7 +48,7 @@ To interact with a message that has a special reply markup, such as data=msg.reply_markup.rows[wanted_row].buttons[wanted_button].data )) -It’s a bit verbose, but it has all the information you would need to +It's a bit verbose, but it has all the information you would need to show it visually (button rows, and buttons within each row, each with its own data). @@ -56,4 +56,4 @@ __ https://t.me/vote __ https://lonamiwebs.github.io/Telethon/methods/messages/get_inline_bot_results.html __ https://lonamiwebs.github.io/Telethon/methods/messages/send_inline_bot_result.html __ https://lonamiwebs.github.io/Telethon/methods/messages/get_bot_callback_answer.html -__ https://t.me/vote \ No newline at end of file +__ https://t.me/vote diff --git a/readthedocs/extra/examples/chats-and-channels.rst b/readthedocs/extra/examples/chats-and-channels.rst new file mode 100644 index 00000000..1bafec80 --- /dev/null +++ b/readthedocs/extra/examples/chats-and-channels.rst @@ -0,0 +1,205 @@ +=============================== +Working with Chats and Channels +=============================== + + +Joining a chat or channel +************************* + +Note that `Chat`__\ s are normal groups, and `Channel`__\ s are a +special form of `Chat`__\ s, +which can also be super-groups if their ``megagroup`` member is +``True``. + + +Joining a public channel +************************ + +Once you have the :ref:`entity ` of the channel you want to join +to, you can make use of the `JoinChannelRequest`__ to join such channel: + + .. code-block:: python + + from telethon.tl.functions.channels import JoinChannelRequest + client(JoinChannelRequest(channel)) + + # In the same way, you can also leave such channel + from telethon.tl.functions.channels import LeaveChannelRequest + client(LeaveChannelRequest(input_channel)) + + +For more on channels, check the `channels namespace`__. + + +Joining a private chat or channel +********************************* + +If all you have is a link like this one: +``https://t.me/joinchat/AAAAAFFszQPyPEZ7wgxLtd``, you already have +enough information to join! The part after the +``https://t.me/joinchat/``, this is, ``AAAAAFFszQPyPEZ7wgxLtd`` on this +example, is the ``hash`` of the chat or channel. Now you can use +`ImportChatInviteRequest`__ as follows: + + .. -block:: python + + from telethon.tl.functions.messages import ImportChatInviteRequest + updates = client(ImportChatInviteRequest('AAAAAEHbEkejzxUjAUCfYg')) + + +Adding someone else to such chat or channel +******************************************* + +If you don't want to add yourself, maybe because you're already in, +you can always add someone else with the `AddChatUserRequest`__, +which use is very straightforward: + + .. code-block:: python + + from telethon.tl.functions.messages import AddChatUserRequest + + client(AddChatUserRequest( + chat_id, + user_to_add, + fwd_limit=10 # allow the user to see the 10 last messages + )) + + +Checking a link without joining +******************************* + +If you don't need to join but rather check whether it's a group or a +channel, you can use the `CheckChatInviteRequest`__, which takes in +the `hash`__ of said channel or group. + +__ https://lonamiwebs.github.io/Telethon/constructors/chat.html +__ https://lonamiwebs.github.io/Telethon/constructors/channel.html +__ https://lonamiwebs.github.io/Telethon/types/chat.html +__ https://lonamiwebs.github.io/Telethon/methods/channels/join_channel.html +__ https://lonamiwebs.github.io/Telethon/methods/channels/index.html +__ https://lonamiwebs.github.io/Telethon/methods/messages/import_chat_invite.html +__ https://lonamiwebs.github.io/Telethon/methods/messages/add_chat_user.html +__ https://lonamiwebs.github.io/Telethon/methods/messages/check_chat_invite.html +__ https://github.com/LonamiWebs/Telethon/wiki/Joining-a-chat-or-channel#joining-a-private-chat-or-channel + + +Retrieving all chat members (channels too) +****************************************** + +In order to get all the members from a mega-group or channel, you need +to use `GetParticipantsRequest`__. As we can see it needs an +`InputChannel`__, (passing the mega-group or channel you're going to +use will work), and a mandatory `ChannelParticipantsFilter`__. The +closest thing to "no filter" is to simply use +`ChannelParticipantsSearch`__ with an empty ``'q'`` string. + +If we want to get *all* the members, we need to use a moving offset and +a fixed limit: + + .. code-block:: python + + from telethon.tl.functions.channels import GetParticipantsRequest + from telethon.tl.types import ChannelParticipantsSearch + from time import sleep + + offset = 0 + limit = 100 + all_participants = [] + + while True: + participants = client.invoke(GetParticipantsRequest( + channel, ChannelParticipantsSearch(''), offset, limit + )) + if not participants.users: + break + all_participants.extend(participants.users) + offset += len(participants.users) + + +Note that ``GetParticipantsRequest`` returns `ChannelParticipants`__, +which may have more information you need (like the role of the +participants, total count of members, etc.) + +__ https://lonamiwebs.github.io/Telethon/methods/channels/get_participants.html +__ https://lonamiwebs.github.io/Telethon/methods/channels/get_participants.html +__ https://lonamiwebs.github.io/Telethon/types/channel_participants_filter.html +__ https://lonamiwebs.github.io/Telethon/constructors/channel_participants_search.html +__ https://lonamiwebs.github.io/Telethon/constructors/channels/channel_participants.html + + +Recent Actions +************** + +"Recent actions" is simply the name official applications have given to +the "admin log". Simply use `GetAdminLogRequest`__ for that, and +you'll get AdminLogResults.events in return which in turn has the final +`.action`__. + +__ https://lonamiwebs.github.io/Telethon/methods/channels/get_admin_log.html +__ https://lonamiwebs.github.io/Telethon/types/channel_admin_log_event_action.html + + +Admin Permissions +***************** + +Giving or revoking admin permissions can be done with the `EditAdminRequest`__: + + .. code-block:: python + + from telethon.tl.functions.channels import EditAdminRequest + from telethon.tl.types import ChannelAdminRights + + # You need both the channel and who to grant permissions + # They can either be channel/user or input channel/input user. + # + # ChannelAdminRights is a list of granted permissions. + # Set to True those you want to give. + rights = ChannelAdminRights( + post_messages=None, + add_admins=None, + invite_users=None, + change_info=True, + ban_users=None, + delete_messages=True, + pin_messages=True, + invite_link=None, + edit_messages=None + ) + + client(EditAdminRequest(channel, who, rights)) + + +Thanks to `@Kyle2142`__ for `pointing out`__ that you **cannot** set +to ``True`` the ``post_messages`` and ``edit_messages`` fields. Those that +are ``None`` can be omitted (left here so you know `which are available`__. + +__ https://lonamiwebs.github.io/Telethon/methods/channels/edit_admin.html +__ https://github.com/Kyle2142 +__ https://github.com/LonamiWebs/Telethon/issues/490 +__ https://lonamiwebs.github.io/Telethon/constructors/channel_admin_rights.html + + +Increasing View Count in a Channel +********************************** + +It has been asked `quite`__ `a few`__ `times`__ (really, `many`__), and +while I don't understand why so many people ask this, the solution is to +use `GetMessagesViewsRequest`__, setting ``increment=True``: + + .. code-block:: python + + + # Obtain `channel' through dialogs or through client.get_entity() or anyhow. + # Obtain `msg_ids' through `.get_message_history()` or anyhow. Must be a list. + + client(GetMessagesViewsRequest( + peer=channel, + id=msg_ids, + increment=True + )) + +__ https://github.com/LonamiWebs/Telethon/issues/233 +__ https://github.com/LonamiWebs/Telethon/issues/305 +__ https://github.com/LonamiWebs/Telethon/issues/409 +__ https://github.com/LonamiWebs/Telethon/issues/447 +__ https://lonamiwebs.github.io/Telethon/methods/messages/get_messages_views.html diff --git a/readthedocs/extra/advanced-usage/working-with-messages.rst b/readthedocs/extra/examples/working-with-messages.rst similarity index 65% rename from readthedocs/extra/advanced-usage/working-with-messages.rst rename to readthedocs/extra/examples/working-with-messages.rst index 2c141406..880bac6f 100644 --- a/readthedocs/extra/advanced-usage/working-with-messages.rst +++ b/readthedocs/extra/examples/working-with-messages.rst @@ -1,20 +1,18 @@ -========================= +===================== Working with messages -========================= - -.. note:: - Make sure you have gone through :ref:`prelude` already! +===================== Forwarding messages ******************* -Note that ForwardMessageRequest_ (note it's Message, singular) will *not* work if channels are involved. -This is because channel (and megagroups) IDs are not unique, so you also need to know who the sender is -(a parameter this request doesn't have). +Note that ForwardMessageRequest_ (note it's Message, singular) will *not* +work if channels are involved. This is because channel (and megagroups) IDs +are not unique, so you also need to know who the sender is (a parameter this +request doesn't have). -Either way, you are encouraged to use ForwardMessagesRequest_ (note it's Message*s*, plural) *always*, -since it is more powerful, as follows: +Either way, you are encouraged to use ForwardMessagesRequest_ (note it's +Message*s*, plural) *always*, since it is more powerful, as follows: .. code-block:: python @@ -31,14 +29,16 @@ since it is more powerful, as follows: to_peer=to_entity # who are we forwarding them to? )) -The named arguments are there for clarity, although they're not needed because they appear in order. -You can obviously just wrap a single message on the list too, if that's all you have. +The named arguments are there for clarity, although they're not needed because +they appear in order. You can obviously just wrap a single message on the list +too, if that's all you have. Searching Messages ******************* -Messages are searched through the obvious SearchRequest_, but you may run into issues_. A valid example would be: +Messages are searched through the obvious SearchRequest_, but you may run +into issues_. A valid example would be: .. code-block:: python @@ -46,27 +46,32 @@ Messages are searched through the obvious SearchRequest_, but you may run into i entity, 'query', InputMessagesFilterEmpty(), None, None, 0, 0, 100 )) -It's important to note that the optional parameter ``from_id`` has been left omitted and thus defaults to ``None``. -Changing it to InputUserEmpty_, as one could think to specify "no user", won't work because this parameter is a flag, +It's important to note that the optional parameter ``from_id`` has been left +omitted and thus defaults to ``None``. Changing it to InputUserEmpty_, as one +could think to specify "no user", won't work because this parameter is a flag, and it being unspecified has a different meaning. -If one were to set ``from_id=InputUserEmpty()``, it would filter messages from "empty" senders, -which would likely match no users. +If one were to set ``from_id=InputUserEmpty()``, it would filter messages +from "empty" senders, which would likely match no users. -If you get a ``ChatAdminRequiredError`` on a channel, it's probably because you tried setting the ``from_id`` filter, -and as the error says, you can't do that. Leave it set to ``None`` and it should work. +If you get a ``ChatAdminRequiredError`` on a channel, it's probably because +you tried setting the ``from_id`` filter, and as the error says, you can't +do that. Leave it set to ``None`` and it should work. -As with every method, make sure you use the right ID/hash combination for your ``InputUser`` or ``InputChat``, -or you'll likely run into errors like ``UserIdInvalidError``. +As with every method, make sure you use the right ID/hash combination for +your ``InputUser`` or ``InputChat``, or you'll likely run into errors like +``UserIdInvalidError``. Sending stickers -***************** +**************** -Stickers are nothing else than ``files``, and when you successfully retrieve the stickers for a certain sticker set, -all you will have are ``handles`` to these files. Remember, the files Telegram holds on their servers can be referenced -through this pair of ID/hash (unique per user), and you need to use this handle when sending a "document" message. -This working example will send yourself the very first sticker you have: +Stickers are nothing else than ``files``, and when you successfully retrieve +the stickers for a certain sticker set, all you will have are ``handles`` to +these files. Remember, the files Telegram holds on their servers can be +referenced through this pair of ID/hash (unique per user), and you need to +use this handle when sending a "document" message. This working example will +send yourself the very first sticker you have: .. code-block:: python diff --git a/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst b/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst index 1ad3da19..6426ada9 100644 --- a/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst +++ b/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst @@ -1,6 +1,6 @@ -========================================= +======================================== Deleted, Limited or Deactivated Accounts -========================================= +======================================== If you're from Iran or Russian, we have bad news for you. Telegram is much more likely to ban these numbers, @@ -23,4 +23,4 @@ For more discussion, please see `issue 297`__. __ https://t.me/SpamBot -__ https://github.com/LonamiWebs/Telethon/issues/297 \ No newline at end of file +__ https://github.com/LonamiWebs/Telethon/issues/297 diff --git a/readthedocs/extra/troubleshooting/enable-logging.rst b/readthedocs/extra/troubleshooting/enable-logging.rst index a6d45d00..897052e2 100644 --- a/readthedocs/extra/troubleshooting/enable-logging.rst +++ b/readthedocs/extra/troubleshooting/enable-logging.rst @@ -1,15 +1,18 @@ ================ -Enable Logging +Enabling Logging ================ Telethon makes use of the `logging`__ module, and you can enable it as follows: - .. code-block:: python +.. code:: python - import logging - logging.basicConfig(level=logging.DEBUG) + import logging + logging.basicConfig(level=logging.DEBUG) -You can also use it in your own project very easily: +The library has the `NullHandler`__ added by default so that no log calls +will be printed unless you explicitly enable it. + +You can also `use the module`__ on your own project very easily: .. code-block:: python @@ -21,4 +24,17 @@ You can also use it in your own project very easily: logger.warning('This is a warning!') -__ https://docs.python.org/3/library/logging.html \ No newline at end of file +If you want to enable ``logging`` for your project *but* use a different +log level for the library: + + .. code-block:: python + + import logging + logging.basicConfig(level=logging.DEBUG) + # For instance, show only warnings and above + logging.getLogger('telethon').setLevel(level=logging.WARNING) + + +__ https://docs.python.org/3/library/logging.html +__ https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library +__ https://docs.python.org/3/howto/logging.html diff --git a/readthedocs/extra/troubleshooting/rpc-errors.rst b/readthedocs/extra/troubleshooting/rpc-errors.rst index 6e8a59f0..3618fb9a 100644 --- a/readthedocs/extra/troubleshooting/rpc-errors.rst +++ b/readthedocs/extra/troubleshooting/rpc-errors.rst @@ -3,9 +3,9 @@ RPC Errors ========== RPC stands for Remote Procedure Call, and when Telethon raises an -``RPCError``, it’s most likely because you have invoked some of the API +``RPCError``, it's most likely because you have invoked some of the API methods incorrectly (wrong parameters, wrong permissions, or even -something went wrong on Telegram’s server). The most common are: +something went wrong on Telegram's server). The most common are: - ``FloodError`` (420), the same request was repeated many times. Must wait ``.seconds``. @@ -13,7 +13,7 @@ something went wrong on Telegram’s server). The most common are: verification on Telegram. - ``CdnFileTamperedError``, if the media you were trying to download from a CDN has been altered. -- ``ChatAdminRequiredError``, you don’t have permissions to perform +- ``ChatAdminRequiredError``, you don't have permissions to perform said operation on a chat or channel. Try avoiding filters, i.e. when searching messages. @@ -22,6 +22,6 @@ The generic classes for different error codes are: \* ``InvalidDCError`` ``BadRequestError`` (400), the request contained errors. \* ``UnauthorizedError`` (401), the user is not authorized yet. \* ``ForbiddenError`` (403), privacy violation error. \* ``NotFoundError`` -(404), make sure you’re invoking ``Request``\ ’s! +(404), make sure you're invoking ``Request``\ 's! -If the error is not recognised, it will only be an ``RPCError``. \ No newline at end of file +If the error is not recognised, it will only be an ``RPCError``. diff --git a/readthedocs/extra/wall-of-shame.rst b/readthedocs/extra/wall-of-shame.rst new file mode 100644 index 00000000..b3c9a028 --- /dev/null +++ b/readthedocs/extra/wall-of-shame.rst @@ -0,0 +1,57 @@ +This project has an +`issues `__ section for +you to file **issues** whenever you encounter any when working with the +library. Said section is **not** for issues on *your* program but rather +issues with Telethon itself. + +If you have not made the effort to 1. `read through the +wiki `__ and 2. `look for +the method you need `__, you +will end up on the `Wall of +Shame `__, +i.e. all issues labeled +`"RTFM" `__: + +> > **rtfm** +> > Literally "Read The F\ **king Manual"; a term showing the +frustration of being bothered with questions so trivial that the asker +could have quickly figured out the answer on their own with minimal +effort, usually by reading readily-available documents. People who +say"RTFM!" might be considered rude, but the true rude ones are the +annoying people who take absolutely no self-responibility and expect to +have all the answers handed to them personally. +> > *"Damn, that's the twelveth time that somebody posted this question +to the messageboard today! RTFM, already!"* +> > **\ by Bill M. July 27, 2004*\* + +If you have indeed read the wiki, and have tried looking for the method, +and yet you didn't find what you need, **that's fine**. Telegram's API +can have some obscure names at times, and for this reason, there is a +`"question" +label `__ +with questions that are okay to ask. Just state what you've tried so +that we know you've made an effort, or you'll go to the Wall of Shame. + +Of course, if the issue you're going to open is not even a question but +a real issue with the library (thankfully, most of the issues have been +that!), you won't end up here. Don't worry. + +Current winner +-------------- + +The current winner is `issue +213 `__: + +**Issue:** + + .. figure:: https://user-images.githubusercontent.com/6297805/29822978-9a9a6ef0-8ccd-11e7-9ec5-934ea0f57681.jpg + :alt: Winner issue + + Winner issue + +**Answer:** + + .. figure:: https://user-images.githubusercontent.com/6297805/29822983-9d523402-8ccd-11e7-9fb1-5783740ee366.jpg + :alt: Winner issue answer + + Winner issue answer diff --git a/readthedocs/index.rst b/readthedocs/index.rst index b5c77e6b..8e5c6053 100644 --- a/readthedocs/index.rst +++ b/readthedocs/index.rst @@ -3,11 +3,14 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. +==================================== Welcome to Telethon's documentation! ==================================== -Pure Python 3 Telegram client library. Official Site `here `_. +Pure Python 3 Telegram client library. +Official Site `here `_. +Please follow the links below to get you started. .. _installation-and-usage: @@ -19,10 +22,9 @@ Pure Python 3 Telegram client library. Official Site `here