diff --git a/readthedocs/extra/advanced-usage/bots.rst b/readthedocs/extra/advanced-usage/bots.rst new file mode 100644 index 00000000..091eada1 --- /dev/null +++ b/readthedocs/extra/advanced-usage/bots.rst @@ -0,0 +1,59 @@ +====== +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: + + .. code-block:: python + + from telethon.tl.functions.messages import GetInlineBotResultsRequest + + bot_results = client(GetInlineBotResultsRequest( + bot, user_or_chat, 'query', '' + )) + +And you can select any of their results by using +`SendInlineBotResultRequest`__: + + .. code-block:: python + + from telethon.tl.functions.messages import SendInlineBotResultRequest + + client(SendInlineBotResultRequest( + get_input_peer(user_or_chat), + obtained_query_id, + obtained_str_id + )) + + +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`__: + + .. code-block:: python + + from telethon.tl.functions.messages import GetBotCallbackAnswerRequest + + client(GetBotCallbackAnswerRequest( + user_or_chat, + msg.id, + 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 +show it visually (button rows, and buttons within each row, each with +its own data). + +__ 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 diff --git a/readthedocs/extra/examples-signing-in.rst b/readthedocs/extra/advanced-usage/signing-in.rst similarity index 95% rename from readthedocs/extra/examples-signing-in.rst rename to readthedocs/extra/advanced-usage/signing-in.rst index cade3649..08f4fe3d 100644 --- a/readthedocs/extra/examples-signing-in.rst +++ b/readthedocs/extra/advanced-usage/signing-in.rst @@ -2,6 +2,10 @@ Signing In ========================= +.. note:: + Make sure you have gone through :ref:`prelude` already! + + Two Factor Authorization (2FA) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/readthedocs/extra/advanced-usage/users-and-chats.rst b/readthedocs/extra/advanced-usage/users-and-chats.rst new file mode 100644 index 00000000..3f3e0729 --- /dev/null +++ b/readthedocs/extra/advanced-usage/users-and-chats.rst @@ -0,0 +1,323 @@ +========================= +Users and Chats +========================= + +.. note:: + Make sure you have gone through :ref:`prelude` already! + +.. contents:: + +.. _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/examples-working-with-messages.rst b/readthedocs/extra/advanced-usage/working-with-messages.rst similarity index 98% rename from readthedocs/extra/examples-working-with-messages.rst rename to readthedocs/extra/advanced-usage/working-with-messages.rst index 1c47d328..2c141406 100644 --- a/readthedocs/extra/examples-working-with-messages.rst +++ b/readthedocs/extra/advanced-usage/working-with-messages.rst @@ -2,6 +2,10 @@ Working with messages ========================= +.. note:: + Make sure you have gone through :ref:`prelude` already! + + Forwarding messages ******************* diff --git a/readthedocs/extra/examples.rst b/readthedocs/extra/advanced.rst similarity index 89% rename from readthedocs/extra/examples.rst rename to readthedocs/extra/advanced.rst index c068f2fc..4433116d 100644 --- a/readthedocs/extra/examples.rst +++ b/readthedocs/extra/advanced.rst @@ -1,8 +1,4 @@ - - -***************** -Examples -***************** +.. _prelude: Prelude --------- @@ -48,19 +44,5 @@ This example shows a basic usage more than enough in most cases. Even reading th for the TelegramClient_ may help a lot! -Signing In --------------- - -.. toctree:: - examples-signing-in - - -Working with messages ------------------------ - -.. toctree:: - examples-working-with-messages - - .. _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/accessing-the-full-api.rst b/readthedocs/extra/basic/accessing-the-full-api.rst new file mode 100644 index 00000000..ceeea7d8 --- /dev/null +++ b/readthedocs/extra/basic/accessing-the-full-api.rst @@ -0,0 +1,115 @@ +========================== +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 +documentation`__ and look for the `method you need`__. There you can go +through a sorted list of everything you can do. + +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. + +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: + + .. code-block:: python + + from telethon.tl.functions.messages import SendMessageRequest + +If you’re going to use a lot of these, you may do: + + .. code-block:: python + + import telethon.tl.functions as tl + # We now have access to 'tl.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 +``str``\ ing. + +How can we retrieve this ``InputPeer``? We have two options. We manually +`construct one`__, for instance: + + .. code-block:: python + + from telethon.tl.types import InputPeerUser + + peer = InputPeerUser(user_id, user_hash) + +Or we call ``.get_input_entity()``: + + .. code-block:: python + + 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 +``.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: + + .. 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 +every time its used, simply call ``.get_input_peer``: + + .. code-block:: python + + from telethon import utils + peer = utils.get_input_user(entity) + +After this small parenthesis about ``.get_entity`` versus +``.get_input_entity``, we have everything we need. To ``.invoke()`` our +request we do: + + .. code-block:: python + + 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: + + .. code-block:: python + + result = client(SendMessageRequest( + client.get_input_entity('username'), 'Hello there!' + )) + + +.. 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). + + 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. + + +__ 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 \ No newline at end of file diff --git a/readthedocs/extra/basic/creating-a-client.rst b/readthedocs/extra/basic/creating-a-client.rst new file mode 100644 index 00000000..997386db --- /dev/null +++ b/readthedocs/extra/basic/creating-a-client.rst @@ -0,0 +1,76 @@ +.. _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. + +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. + +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: + + .. 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) + +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: + + ``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: + + ``client.is_user_authorized() # Returns True if you can send requests`` + +If you're not authorized, you need to ``.sign_in()``: + + .. code-block:: python + + client.send_code_request(phone_number) + myself = client.sign_in(phone_number, input('Enter code: ')) + # If .sign_in raises PhoneNumberUnoccupiedError, use .sign_up instead + # 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! + +.. note:: + 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 + + import socks + client = TelegramClient('session_id', + api_id=12345, api_hash='0123456789abcdef0123456789abcdef', + proxy=(socks.SOCKS5, 'localhost', 4444) + ) + + The ``proxy=`` argument should be a tuple, a list or a dict, + consisting of parameters described `here`__. + + +__ https://github.com/Anorov/PySocks#installation +__ https://github.com/Anorov/PySocks#usage-1%3E \ No newline at end of file diff --git a/readthedocs/extra/basic/getting-started.rst b/readthedocs/extra/basic/getting-started.rst new file mode 100644 index 00000000..ce4354fd --- /dev/null +++ b/readthedocs/extra/basic/getting-started.rst @@ -0,0 +1,54 @@ +.. Telethon documentation master file, created by + sphinx-quickstart on Fri Nov 17 15:36:11 2017. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + + +================= +Getting Started! +================= + +Simple Installation +********************* + + ``pip install telethon`` + +**More details**: :ref:`installation` + + +Creating a client +************** + + .. code-block:: python + + from telethon import TelegramClient + + # These example values won't work. You must get your own api_id and + # api_hash from https://my.telegram.org, under API Development. + api_id = 12345 + api_hash = '0123456789abcdef0123456789abcdef' + phone = '+34600000000' + + client = TelegramClient('session_name', api_id, api_hash) + client.connect() + + # If you already have a previous 'session_name.session' file, skip this. + client.sign_in(phone=phone) + me = client.sign_in(code=77777) # Put whatever code you received here. + +**More details**: `Click here `_ + + +Simple Stuff +************** + .. code-block:: python + + print(me.stringify()) + + client.send_message('username', 'Hello! Talking to you from Telethon') + client.send_file('username', '/home/myself/Pictures/holidays.jpg') + + client.download_profile_photo(me) + total, messages, senders = client.get_message_history('username') + client.download_media(messages[0]) + diff --git a/readthedocs/extra/basic/installation.rst b/readthedocs/extra/basic/installation.rst new file mode 100644 index 00000000..f302e8f7 --- /dev/null +++ b/readthedocs/extra/basic/installation.rst @@ -0,0 +1,82 @@ +.. _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 already have the library installed, upgrade with: + + ``pip install --upgrade telethon``. + +You can also install the library directly from GitHub or a fork: + + .. code-block:: python + + # pip install git+https://github.com/LonamiWebs/Telethon.git + or + $ git clone https://github.com/LonamiWebs/Telethon.git + $ cd Telethon/ + # pip install -Ue . + +If you don't have root access, simply pass the ``--user`` flag to the pip command. + + +Manual Installation +^^^^^^^^^^^^^^^^^^^^ + +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`` + +3. Enter the cloned repository: ``cd Telethon`` + +4. Run the code generator: ``python3 setup.py gen_tl`` + +5. Done! + +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! + + +Sending Requests +***************** + `Here `__ + +Working with updates +********************** + `Here `__ + +Accessing the full API +*********************** + `Here `__ + + +__ 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 diff --git a/readthedocs/extra/basic/sending-requests.rst b/readthedocs/extra/basic/sending-requests.rst new file mode 100644 index 00000000..160e2259 --- /dev/null +++ b/readthedocs/extra/basic/sending-requests.rst @@ -0,0 +1,55 @@ +.. _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 new file mode 100644 index 00000000..0f9d458a --- /dev/null +++ b/readthedocs/extra/basic/sessions.rst @@ -0,0 +1,48 @@ +.. _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 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. \ No newline at end of file diff --git a/readthedocs/extra/basic/working-with-updates.rst b/readthedocs/extra/basic/working-with-updates.rst new file mode 100644 index 00000000..4367bbc4 --- /dev/null +++ b/readthedocs/extra/basic/working-with-updates.rst @@ -0,0 +1,133 @@ +==================== +Working with Updates +==================== + +.. contents:: + + +The library can run in four distinguishable modes: + +- With no extra threads at all. +- With an extra thread that receives everything as soon as possible (default). +- 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. + + +Using multiple workers +^^^^^^^^^^^^^^^^^^^^^^^ + +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. + +The next thing you want to do is to add a method that will be called when an `Update`__ arrives: + + .. code-block:: python + + def callback(update): + print('I received', update) + + client.add_update_handler(callback) + # 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: + + .. code-block:: python + + from telethon.tl.types import UpdateShortMessage, PeerUser + + def replier(update): + if isinstance(update, UpdateShortMessage) and not update.out: + client.send_message(PeerUser(update.user_id), update.message[::-1]) + + + client.add_update_handler(replier) + 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. + + +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: + + .. code-block:: python + + while True: + try: + update = client.updates.poll() + if not update: + continue + + print('I received', update) + except KeyboardInterrupt: + break + + 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``. + +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. + + +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: + + .. code-block:: python + + client = TelegramClient( + ... + spawn_read_thread=False + ) + +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`__. + +As a complete example: + + .. code-block:: python + + def callback(update): + print('I received', update) + + client = TelegramClient('session', api_id, api_hash, + update_workers=1, spawn_read_thread=False) + + client.connect() + client.add_update_handler(callback) + client.idle() # ends with Ctrl+C + client.disconnect() + + +__ 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 diff --git a/readthedocs/extra/getting_started.rst b/readthedocs/extra/getting_started.rst deleted file mode 100644 index 3a6b36a8..00000000 --- a/readthedocs/extra/getting_started.rst +++ /dev/null @@ -1,103 +0,0 @@ -.. Telethon documentation master file, created by - sphinx-quickstart on Fri Nov 17 15:36:11 2017. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - - -================= -Getting Started! -================= - -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 already have the library installed, upgrade with: - - ``pip install --upgrade telethon``. - -You can also install the library directly from GitHub or a fork: - - .. code-block:: python - - # pip install git+https://github.com/LonamiWebs/Telethon.git - or - $ git clone https://github.com/LonamiWebs/Telethon.git - $ cd Telethon/ - # pip install -Ue . - -If you don't have root access, simply pass the ``--user`` flag to the pip command. - - - - -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. - -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. - -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: - - .. code-block:: python - - from telethon import TelegramClient - - # These example values won't work. You must get your own api_id and - # api_hash from https://my.telegram.org, under API Development. - api_id = 12345 - api_hash = '0123456789abcdef0123456789abcdef' - phone = '+34600000000' - - client = TelegramClient('session_name', api_id, api_hash) - client.connect() - - # If you already have a previous 'session_name.session' file, skip this. - client.sign_in(phone=phone) - me = client.sign_in(code=77777) # Put whatever code you received here. - -**More details**: `Click here `_ - - -Simple Stuff -************** - .. code-block:: python - - print(me.stringify()) - - client.send_message('username', 'Hello! Talking to you from Telethon') - client.send_file('username', '/home/myself/Pictures/holidays.jpg') - - client.download_profile_photo(me) - total, messages, senders = client.get_message_history('username') - client.download_media(messages[0]) - - -Diving In -************** - -.. note:: More info in our Wiki! - -Sending Requests -^^^^^^^^^^^^^^^^^^^^^ - `Here `__ - -Working with updates -^^^^^^^^^^^^^^^^^^^^^ - `Here `__ - -Accessing the full API -^^^^^^^^^^^^^^^^^^^^^^^ - `Here `__ - diff --git a/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst b/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst new file mode 100644 index 00000000..1ad3da19 --- /dev/null +++ b/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst @@ -0,0 +1,26 @@ +========================================= +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, +as they are often used to spam other accounts, +likely through the use of libraries like this one. +The best advice we can give you is to not abuse the API, +like calling many requests really quickly, +and to sign up with these phones through an official application. + +Telegram may also ban virtual (VoIP) phone numbers, +as again, they're likely to be used for spam. + +If you want to check if your account has been limited, +simply send a private message to `@SpamBot`__ through Telegram itself. +You should notice this by getting errors like ``PeerFloodError``, +which means you're limited, for instance, +when sending a message to some accounts but not others. + +For more discussion, please see `issue 297`__. + + +__ https://t.me/SpamBot +__ https://github.com/LonamiWebs/Telethon/issues/297 \ No newline at end of file diff --git a/readthedocs/extra/troubleshooting/enable-logging.rst b/readthedocs/extra/troubleshooting/enable-logging.rst new file mode 100644 index 00000000..a6d45d00 --- /dev/null +++ b/readthedocs/extra/troubleshooting/enable-logging.rst @@ -0,0 +1,24 @@ +================ +Enable Logging +================ + +Telethon makes use of the `logging`__ module, and you can enable it as follows: + + .. code-block:: python + + import logging + logging.basicConfig(level=logging.DEBUG) + +You can also use it in your own project very easily: + + .. code-block:: python + + import logging + logger = logging.getLogger(__name__) + + logger.debug('Debug messages') + logger.info('Useful information') + logger.warning('This is a warning!') + + +__ https://docs.python.org/3/library/logging.html \ No newline at end of file diff --git a/readthedocs/extra/troubleshooting/rpc-errors.rst b/readthedocs/extra/troubleshooting/rpc-errors.rst new file mode 100644 index 00000000..6e8a59f0 --- /dev/null +++ b/readthedocs/extra/troubleshooting/rpc-errors.rst @@ -0,0 +1,27 @@ +========== +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 +methods incorrectly (wrong parameters, wrong permissions, or even +something went wrong on Telegram’s server). The most common are: + +- ``FloodError`` (420), the same request was repeated many times. Must + wait ``.seconds``. +- ``SessionPasswordNeededError``, if you have setup two-steps + 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 + said operation on a chat or channel. Try avoiding filters, i.e. when + searching messages. + +The generic classes for different error codes are: \* ``InvalidDCError`` +(303), the request must be repeated on another DC. \* +``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! + +If the error is not recognised, it will only be an ``RPCError``. \ No newline at end of file diff --git a/readthedocs/index.rst b/readthedocs/index.rst index 8f036adc..b5c77e6b 100644 --- a/readthedocs/index.rst +++ b/readthedocs/index.rst @@ -10,24 +10,49 @@ Pure Python 3 Telegram client library. Official Site `here