diff --git a/readthedocs/basic/installation.rst b/readthedocs/basic/installation.rst new file mode 100644 index 00000000..96f529a9 --- /dev/null +++ b/readthedocs/basic/installation.rst @@ -0,0 +1,77 @@ +.. _installation: + +============ +Installation +============ + +Telethon is a Python library, which means you need to download and install +Python from https://www.python.org/downloads/ if you haven't already. Once +you have Python installed, run: + +.. code-block:: sh + + pip3 install -U telethon --user + +To install or upgrade the library to the latest version. + + +Installing Development Versions +=============================== + +If you want the *latest* unreleased changes, +you can run the following command instead: + +.. code-block:: sh + + pip3 install -U https://github.com/LonamiWebs/Telethon/archive/master.zip --user + +.. note:: + + The development version may have bugs and is not recommended for production + use. However, when you are `reporting a library bug`__, you should try if the + bug still occurs in this version. + +.. __: https://github.com/LonamiWebs/Telethon/issues/ + + +Verification +============ + +To verify that the library is installed correctly, run the following command: + +.. code-block:: sh + + python3 -c 'import telethon; print(telethon.__version__)' + +The version number of the library should show in the output. + + +Optional Dependencies +===================== + +If cryptg_ is installed, **the library will work a lot faster**, since +encryption and decryption will be made in C instead of Python. If your +code deals with a lot of updates or you are downloading/uploading a lot +of files, you will notice a considerable speed-up (from a hundred kilobytes +per second to several megabytes per second, if your connection allows it). +If it's not installed, pyaes_ will be used (which is pure Python, so it's +much slower). + +If pillow_ is installed, large images will be automatically resized when +sending photos to prevent Telegram from failing with "invalid image". +Official clients also do this. + +If aiohttp_ is installed, the library will be able to download +:tl:`WebDocument` media files (otherwise you will get an error). + +If hachoir_ is installed, it will be used to extract metadata from files +when sending documents. Telegram uses this information to show the song's +performer, artist, title, duration, and for videos too (including size). +Otherwise, they will default to empty values, and you can set the attributes +manually. + +.. _cryptg: https://github.com/Lonami/cryptg +.. _pyaes: https://github.com/ricmoo/pyaes +.. _pillow: https://python-pillow.org +.. _aiohttp: https://docs.aiohttp.org +.. _hachoir: https://hachoir.readthedocs.io diff --git a/readthedocs/basic/next-steps.rst b/readthedocs/basic/next-steps.rst new file mode 100644 index 00000000..7cecdc9a --- /dev/null +++ b/readthedocs/basic/next-steps.rst @@ -0,0 +1,22 @@ +========== +Next Steps +========== + +These basic first steps should have gotten you started with the library. + +By now, you should know how to call friendly methods and how to work with +the returned objects, how things work inside event handlers, etc. + +Next, we will see a quick reference summary of *all* the methods and +properties that you will need when using the library. If you follow +the links there, you will expand the documentation for the method +and property, with more examples on how to use them. + +Therefore, **you can find an example on every method** of the client +to learn how to use it, as well as a description of all the arguments. + +After that, we will go in-depth with some other important concepts +that are worth learning and understanding. + +From now on, you can keep pressing the "Next" button if you want, +or use the menu on the left, since some pages are quite lengthy. diff --git a/readthedocs/basic/quick-start.rst b/readthedocs/basic/quick-start.rst new file mode 100644 index 00000000..40e99f6b --- /dev/null +++ b/readthedocs/basic/quick-start.rst @@ -0,0 +1,79 @@ +=========== +Quick-Start +=========== + +Let's see a longer example to learn some of the methods that the library +has to offer. These are known as "friendly methods", and you should always +use these if possible. + +.. code-block:: python + + from telethon.sync import TelegramClient + + # Remember to use your own values from my.telegram.org! + api_id = 12345 + api_hash = '0123456789abcdef0123456789abcdef' + + with TelegramClient('anon', api_id, api_hash) as client: + # Getting information about yourself + me = client.get_me() + + # "me" is an User object. You can pretty-print + # any Telegram object with the "stringify" method: + print(me.stringify()) + + # When you print something, you see a representation of it. + # You can access all attributes of Telegram objects with + # the dot operator. For example, to get the username: + username = me.username + print(username) + print(me.phone) + + # You can print all the dialogs/conversations that you are part of: + for dialog in client.iter_dialogs(): + print(dialog.name, 'has ID', dialog.id) + + # You can send messages to yourself... + client.send_message('me', 'Hello, myself!') + # ...to some chat ID + client.send_message(-100123456, 'Hello, group!') + # ...to your contacts + client.send_message('+34600123123', 'Hello, friend!') + # ...or even to any username + client.send_message('TelethonChat', 'Hello, Telethon!') + + # You can, of course, use markdown in your messages: + message = client.send_message( + 'me', + 'This message has **bold**, `code`, __italics__ and ' + 'a [nice website](https://lonamiwebs.github.io)!', + link_preview=False + ) + + # Sending a message returns the sent message object, which you can use + print(message.raw_text) + + # You can reply to messages directly if you have a message object + message.reply('Cool!') + + # Or send files, songs, documents, albums... + client.send_file('me', '/home/me/Pictures/holidays.jpg') + + # You can print the message history of any chat: + for message in client.iter_messages('me'): + print(message.id, message.text) + + # You can download media from messages, too! + # The method will return the path where the file was saved. + if message.photo: + path = message.download_media() + print('File saved to', path) + + +Here, we show how to sign in, get information about yourself, send +messages, files, getting chats, printing messages, and downloading +files. + +You should make sure that you understand what the code shown here +does, take note on how methods are called and used and so on before +proceeding. We will see all the available methods later on. diff --git a/readthedocs/basic/signing-in.rst b/readthedocs/basic/signing-in.rst new file mode 100644 index 00000000..a0b3faa3 --- /dev/null +++ b/readthedocs/basic/signing-in.rst @@ -0,0 +1,128 @@ +.. _signing-in: + +========== +Signing In +========== + +Before working with Telegram's API, you need to get your own API ID and hash: + +1. `Login to your Telegram account `_ with the + phone number of the developer account to use. + +2. Click under API Development tools. + +3. A *Create new application* window will appear. Fill in your application + details. There is no need to enter any *URL*, and only the first two + fields (*App title* and *Short name*) can currently be changed later. + +4. Click on *Create application* at the end. Remember that your + **API hash is secret** and Telegram won't let you revoke it. + Don't post it anywhere! + +.. note:: + + This API ID and hash is the one used by *your application*, not your + phone number. You can use this API ID and hash with *any* phone number + or even for bot accounts. + + +Editing the Code +================ + +This is a little introduction for those new to Python programming in general. + +We will write our code inside ``hello.py``, so you can use any text +editor that you like. To run the code, use ``python3 hello.py`` from +the terminal. + +.. important:: + + Don't call your script ``telethon.py``! Python will try to import + the client from there and it will fail with an error such as + "ImportError: cannot import name 'TelegramClient' ...". + + +Signing In +========== + +We can finally write some code to log into our account! + +.. code-block:: python + + from telethon.sync import TelegramClient + + # Use your own values from my.telegram.org + api_id = 12345 + api_hash = '0123456789abcdef0123456789abcdef' + + # The first parameter is the .session file name (absolute paths allowed) + with TelegramClient('anon', api_id, api_hash) as client: + client.send_message('me', 'Hello, myself!') + + +In the first line, we import the class name so we can create an instance +of the client. Then, we define variables to store our API ID and hash +conveniently. + +At last, we create a new `TelegramClient ` +instance and call it ``client``. We can now use the client variable +for anything that we want, such as sending a message to ourselves. + +Using a ``with`` block is the preferred way to use the library. It will +automatically `start() ` the client, +logging or signing up if necessary. + +If the ``.session`` file already existed, it will not login +again, so be aware of this if you move or rename the file! + + +Signing In as a Bot Account +=========================== + +You can also use Telethon for your bots (normal bot accounts, not users). +You will still need an API ID and hash, but the process is very similar: + + +.. code-block:: python + + from telethon.sync import TelegramClient + + api_id = 12345 + api_hash = '0123456789abcdef0123456789abcdef' + bot_token = '12345:0123456789abcdef0123456789abcdef + + # We have to manually call "start" if we want a explicit bot token + bot = TelegramClient('bot', api_id, api_hash).start(bot_token=bot_token) + + # But then we can use the client instance as usual + with bot: + ... + + +To get a bot account, you need to talk +with `@BotFather `_. + + +Signing In behind a Proxy +========================= + +If you need to use a proxy to access Telegram, +you will need to `install PySocks`__ and then change: + +.. code-block:: python + + TelegramClient('anon', api_id, api_hash) + +with + +.. code-block:: python + + TelegramClient('anon', api_id, api_hash, proxy=(socks.SOCKS5, '127.0.0.1', 4444)) + +(of course, replacing the IP and port with the IP and port of the proxye). + +The ``proxy=`` argument should be a tuple, a list or a dict, +consisting of parameters described `in PySocks usage`__. + +.. __: https://github.com/Anorov/PySocks#installation +.. __: https://github.com/Anorov/PySocks#usage-1 diff --git a/readthedocs/basic/updates.rst b/readthedocs/basic/updates.rst new file mode 100644 index 00000000..bf9ca92b --- /dev/null +++ b/readthedocs/basic/updates.rst @@ -0,0 +1,161 @@ +======= +Updates +======= + +Updates are an important topic in a messaging platform like Telegram. +After all, you want to be notified when a new message arrives, when +a member joins, when someone starts typing, etc. +For that, you can use **events**. + +.. important:: + + It is strongly advised to enable logging when working with events, + since exceptions in event handlers are hidden by default. Please + add the following snippet to the very top of your file: + + .. code-block:: python + + import logging + logging.basicConfig(format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s', + level=logging.WARNING) + + +Getting Started +=============== + +Let's start things with an example to automate replies: + +.. code-block:: python + + from telethon import TelegramClient, events + + client = TelegramClient('anon', api_id, api_hash) + + @client.on(events.NewMessage) + async def my_event_handler(event): + if 'hello' in event.raw_text: + await event.reply('hi!') + + client.start() + client.run_until_disconnected() + + +This code isn't much, but there might be some things unclear. +Let's break it down: + +.. code-block:: python + + from telethon import TelegramClient, events + + client = TelegramClient('anon', api_id, api_hash) + + +This is normal creation (of course, pass session name, API ID and hash). +Nothing we don't know already. + +.. code-block:: python + + @client.on(events.NewMessage) + + +This Python decorator will attach itself to the ``my_event_handler`` +definition, and basically means that *on* a `NewMessage +` *event*, +the callback function you're about to define will be called: + +.. code-block:: python + + async def my_event_handler(event): + if 'hello' in event.raw_text: + await event.reply('hi!') + + +If a `NewMessage +` event occurs, +and ``'hello'`` is in the text of the message, we `reply() +` to the event +with a ``'hi!'`` message. + +.. note:: + + Event handlers **must** be ``async def``. After all, + Telethon is an asynchronous library based on asyncio_, + which is a safer and often faster approach to threads. + + You **must** ``await`` all method calls that use + network requests, which is most of them. + + +More Examples +============= + +Replying to messages with hello is fun, but, can we do more? + +.. code-block:: python + + @client.on(events.NewMessage(outgoing=True, pattern=r'\.save')) + async def handler(event): + if event.is_reply: + replied = await event.get_reply_message() + sender = replied.sender + await client.download_profile_photo(sender) + await event.respond('Saved your photo {}'.format(sender.username)) + +We could also get replies. This event filters outgoing messages +(only those that we send will trigger the method), then we filter +by the regex ``r'\.save'``, which will match messages starting +with ``".save"``. + +Inside the method, we check whether the event is replying to another message +or not. If it is, we get the reply message and the sender of that message, +and download their profile photo. + +Let's delete messages which contain "heck". We don't allow swearing here. + +.. code-block:: python + + @client.on(events.NewMessage(pattern=r'(?i).*heck')) + async def handler(event): + await event.delete() + + +With the ``r'(?i).*heck'`` regex, we match case-insensitive +"heck" anywhere in the message. Regex is very powerful and you +can learn more at https://regexone.com/. + +So far, we have only seen the `NewMessage +`, but there are many more +which will be covered later. This is only a small introduction to updates. + +Entities +======== + +When you need the user or chat where an event occurred, you **must** use +the following methods: + +.. code-block:: + + async def handler(event): + # Good + chat = await event.get_chat() + sender = await event.get_sender() + chat_id = event.chat_id + sender_id = event.sender_id + + # BAD. Don't do this + chat = event.chat + sender = event.sender + chat_id = event.chat.id + sender_id = event.sender.id + +Events are like messages, but don't have all the information a message has! +When you manually get a message, it will have all the information it needs. +When you receive an update about a message, it **won't** have all the +information, so you have to **use the methods**, not the properties. + +Make sure you understand the code seen here before continuing! +As a rule of thumb, remember that new message events behave just +like message objects, so you can do with them everything you can +do with a message object. + +.. _asyncio: https://docs.python.org/3/library/asyncio.html diff --git a/readthedocs/extra/advanced-usage/mastering-asyncio.rst b/readthedocs/concepts/asyncio.rst similarity index 97% rename from readthedocs/extra/advanced-usage/mastering-asyncio.rst rename to readthedocs/concepts/asyncio.rst index 96382951..182a5d6f 100644 --- a/readthedocs/extra/advanced-usage/mastering-asyncio.rst +++ b/readthedocs/concepts/asyncio.rst @@ -8,7 +8,7 @@ Mastering asyncio What's asyncio? -*************** +=============== asyncio_ is a Python 3's built-in library. This means it's already installed if you have Python 3. Since Python 3.5, it is convenient to work with asynchronous @@ -20,7 +20,7 @@ APIs such as Telegram's makes a lot of sense this way. Why asyncio? -************ +============ Asynchronous IO makes a lot of sense in a library like Telethon. You send a request to the server (such as "get some message"), and @@ -38,7 +38,7 @@ because tasks are smaller than threads, which are smaller than processes. What are asyncio basics? -************************ +======================== .. code-block:: python @@ -59,7 +59,7 @@ What are asyncio basics? What does telethon.sync do? -*************************** +=========================== The moment you import any of these: @@ -133,7 +133,7 @@ running, and if the loop is running, you must ``await`` things yourself: What are async, await and coroutines? -************************************* +===================================== The ``async`` keyword lets you define asynchronous functions, also known as coroutines, and also iterate over asynchronous @@ -212,7 +212,7 @@ The same example, but without the comment noise: Can I use threads? -****************** +================== Yes, you can, but you must understand that the loops themselves are not thread safe. and you must be sure to know what is happening. You @@ -250,7 +250,7 @@ which only works in the main thread. client.run_until_disconnected() blocks! -*************************************** +======================================= All of what `client.run_until_disconnected() ` does is @@ -282,7 +282,7 @@ Of course, there are better tools to run code hourly or daily, see below. What else can asyncio do? -************************* +========================= Asynchronous IO is a really powerful tool, as we've seen. There are plenty of other useful libraries that also use asyncio_ and that you can integrate @@ -339,7 +339,7 @@ combine all the libraries you want. People seem to forget this simple fact! Why does client.start() work outside async? -******************************************* +=========================================== Because it's so common that it's really convenient to offer said functionality by default. This means you can set up all your event @@ -352,7 +352,7 @@ Using the client in a ``with`` block, `start all support this. Where can I read more? -********************** +====================== `Check out my blog post `_ about asyncio_, which diff --git a/readthedocs/extra/basic/entities.rst b/readthedocs/concepts/entities.rst similarity index 59% rename from readthedocs/extra/basic/entities.rst rename to readthedocs/concepts/entities.rst index 18fda69c..a92d36ba 100644 --- a/readthedocs/extra/basic/entities.rst +++ b/readthedocs/concepts/entities.rst @@ -1,46 +1,8 @@ .. _entities: -========================= -Users, Chats and Channels -========================= - - -.. important:: - - TL;DR; If you're here because of *"Could not find the input entity for"*, - you must ask yourself "how did I find this entity through official - applications"? Now do the same with the library. Use what applies: - - .. code-block:: python - - with client: - # Does it have an username? Use it! - entity = client.get_entity(username) - - # Do you have a conversation open with them? Get dialogs. - client.get_dialogs() - - # Are they participant of some group? Get them. - client.get_participants('TelethonChat') - - # Is the entity the original sender of a forwarded message? Get it. - client.get_messages('TelethonChat', 100) - - # NOW you can use the ID, anywhere! - entity = client.get_entity(123456) - client.send_message(123456, 'Hi!') - - Once the library has "seen" the entity, you can use their **integer** ID. - You can't use entities from IDs the library hasn't seen. You must make the - library see them *at least once* and disconnect properly. You know where - the entities are and you must tell the library. It won't guess for you. - - -.. contents:: - - -Introduction -************ +======== +Entities +======== The library widely uses the concept of "entities". An entity will refer to any :tl:`User`, :tl:`Chat` or :tl:`Channel` object that the API may return @@ -67,8 +29,72 @@ in response to certain methods, such as :tl:`GetUsersRequest`. should work to find the entity. -Getting entities -**************** +.. contents:: + + +What is an Entity? +================== + +A lot of methods and requests require *entities* to work. For example, +you send a message to an *entity*, get the username of an *entity*, and +so on. + +There are a lot of things that work as entities: usernames, phone numbers, +chat links, invite links, IDs, and the types themselves. That is, you can +use any of those when you see an "entity" is needed. + +.. note:: + + Remember that the phone number must be in your contact list before you + can use it. + +You should use, **from better to worse**: + +1. Input entities. For example, `event.input_chat + `, + `message.input_sender + `, + or caching an entity you will use a lot with + ``entity = await client.get_input_entity(...)``. + +2. Entities. For example, if you had to get someone's + username, you can just use ``user`` or ``channel``. + It will work. Only use this option if you already have the entity! + +3. IDs. This will always look the entity up from the + cache (the ``*.session`` file caches seen entities). + +4. Usernames, phone numbers and links. The cache will be + used too (unless you force a `client.get_entity() + `), + but may make a request if the username, phone or link + has not been found yet. + +In recent versions of the library, the following two are equivalent: + +.. code-block:: python + + async def handler(event): + await client.send_message(event.sender_id, 'Hi') + await client.send_message(event.input_sender, 'Hi') + + +If you need to be 99% sure that the code will work (sometimes it's +simply impossible for the library to find the input entity), or if +you will reuse the chat a lot, consider using the following instead: + +.. code-block:: python + + async def handler(event): + # This method may make a network request to find the input sender. + # Properties can't make network requests, so we need a method. + sender = await event.get_input_sender() + await client.send_message(sender, 'Hi') + await client.send_message(sender, 'Hi') + + +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 @@ -79,6 +105,8 @@ you're able to just do this: # Dialogs are the "conversations you have open". # This method returns a list of Dialog, which # has the .entity attribute and other information. + # + # This part is IMPORTANT, because it feels the entity cache. dialogs = client.get_dialogs() # All of these work and do the same. @@ -109,7 +137,7 @@ you're able to just do this: library do its job. Use a phone from your contacts, username, ID or input entity (preferred but not necessary), whatever you already have. -All methods in the :ref:`telegram-client` call `.get_input_entity() +All methods in the :ref:`telethon-client` call `.get_input_entity() ` prior to sending the request to save you from the hassle of doing so manually. That way, convenience calls such as `client.send_message('lonami', 'hi!') @@ -124,16 +152,12 @@ made to obtain the required information. Entities vs. Input Entities -*************************** +=========================== .. note:: - Don't worry if you don't understand this section, just remember some - of the details listed here are important. When you're calling a method, - don't call `client.get_entity() ` - beforehand, just use the username, a phone from your contacts, or the entity - retrieved by other means like `client.get_dialogs() - `. + This section is informative, but worth reading. The library + will transparently handle all of these details for you. On top of the normal types, the API also make use of what they call their ``Input*`` versions of objects. The input version of an entity (e.g. @@ -195,8 +219,8 @@ resolve ``'username'`` with the appropriated :tl:`InputPeer`. Don't worry if you don't get this yet, but remember some of the details here are important. -Full entities -************* +Full Entities +============= In addition to :tl:`PeerUser`, :tl:`InputPeerUser`, :tl:`User` (and its variants for chats and channels), there is also the concept of :tl:`UserFull`. @@ -211,3 +235,74 @@ suggest commands to use). You can get both of these by invoking :tl:`GetFullUser`, :tl:`GetFullChat` and :tl:`GetFullChannel` respectively. + + +Accessing Entities +================== + +Although it's explicitly noted in the documentation that messages +*subclass* `ChatGetter ` +and `SenderGetter `, +some people still don't get inheritance. + +When the documentation says "Bases: `telethon.tl.custom.chatgetter.ChatGetter`" +it means that the class you're looking at, *also* can act as the class it +bases. In this case, `ChatGetter ` +knows how to get the *chat* where a thing belongs to. + +So, a `Message ` is a +`ChatGetter `. +That means you can do this: + +.. code-block:: python + + message.is_private + message.chat_id + message.get_chat() + # ...etc + +`SenderGetter ` is similar: + +.. code-block:: python + + message.user_id + message.get_input_user() + message.user + # ...etc + +Quite a few things implement them, so it makes sense to reuse the code. +For example, all events (except raw updates) implement `ChatGetter +` since all events occur +in some chat. + + +Summary +======= + +TL;DR; If you're here because of *"Could not find the input entity for"*, +you must ask yourself "how did I find this entity through official +applications"? Now do the same with the library. Use what applies: + +.. code-block:: python + + with client: + # Does it have an username? Use it! + entity = client.get_entity(username) + + # Do you have a conversation open with them? Get dialogs. + client.get_dialogs() + + # Are they participant of some group? Get them. + client.get_participants('TelethonChat') + + # Is the entity the original sender of a forwarded message? Get it. + client.get_messages('TelethonChat', 100) + + # NOW you can use the ID, anywhere! + entity = client.get_entity(123456) + client.send_message(123456, 'Hi!') + +Once the library has "seen" the entity, you can use their **integer** ID. +You can't use entities from IDs the library hasn't seen. You must make the +library see them *at least once* and disconnect properly. You know where +the entities are and you must tell the library. It won't guess for you. diff --git a/readthedocs/extra/troubleshooting/rpc-errors.rst b/readthedocs/concepts/errors.rst similarity index 61% rename from readthedocs/extra/troubleshooting/rpc-errors.rst rename to readthedocs/concepts/errors.rst index f675a26b..429c5697 100644 --- a/readthedocs/extra/troubleshooting/rpc-errors.rst +++ b/readthedocs/concepts/errors.rst @@ -1,3 +1,5 @@ +.. _rpc-errors: + ========== RPC Errors ========== @@ -6,7 +8,7 @@ RPC stands for Remote Procedure Call, and when the library raises a ``RPCError``, it's because you have invoked some of the API methods incorrectly (wrong parameters, wrong permissions, or even something went wrong on Telegram's server). All the errors are -available in :ref:`telethon-errors-package`, but some examples are: +available in :ref:`telethon-errors`, but some examples are: - ``FloodWaitError`` (420), the same request was repeated many times. Must wait ``.seconds`` (you can access this attribute). For example: @@ -43,3 +45,33 @@ If the error is not recognised, it will only be an ``RPCError``. You can refer to all errors from Python through the ``telethon.errors`` module. If you don't know what attributes they have, try printing their dir (like ``print(dir(e))``). + +Avoiding Limits +=============== + +Don't spam. You won't get ``FloodWaitError`` or your account banned or +deleted if you use the library *for legit use cases*. Make cool tools. +Don't spam! Nobody knows the exact limits for all requests since they +depend on a lot of factors, so don't bother asking. + +Still, if you do have a legit use case and still get those errors, the +library will automatically sleep when they are smaller than 60 seconds +by default. You can set different "auto-sleep" thresholds: + +.. code-block:: python + + client.flood_sleep_threshold = 0 # Don't auto-sleep + client.flood_sleep_threshold = 24 * 60 * 60 # Sleep always + +You can also except it and act as you prefer: + +.. code-block:: python + + from telethon.errors import FloodWaitError + try: + ... + except FloodWaitError as e: + print('Flood waited for', e.seconds) + quit(1) + +VoIP numbers are very limited, and some countries are more limited too. diff --git a/readthedocs/extra/advanced-usage/accessing-the-full-api.rst b/readthedocs/concepts/full-api.rst similarity index 85% rename from readthedocs/extra/advanced-usage/accessing-the-full-api.rst rename to readthedocs/concepts/full-api.rst index 2d61c253..e7c86b56 100644 --- a/readthedocs/extra/advanced-usage/accessing-the-full-api.rst +++ b/readthedocs/concepts/full-api.rst @@ -1,21 +1,21 @@ -.. _accessing-the-full-api: +.. _full-api: -====================== -Accessing the Full API -====================== +============ +The Full API +============ .. important:: While you have access to this, you should always use the friendly - methods listed on :ref:`telethon-client` unless you have a better - reason not to, like a method not existing or you wanting more control. + methods listed on :ref:`client-ref` unless you have a better reason + not to, like a method not existing or you wanting more control. -The :ref:`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. +The :ref:`telethon-client` 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:: @@ -39,9 +39,9 @@ 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 +Say `client.send_message() ` didn't exist, -we could use the `search`__ to look for "message". There we would find +we could `use the search`_ to look for "message". There we would find :tl:`SendMessageRequest`, which we can work with. Every request is a Python class, and has the parameters needed for you @@ -73,7 +73,7 @@ construct one, for instance: peer = InputPeerUser(user_id, user_hash) -Or we call `client.get_input_entity +Or we call `client.get_input_entity() `: .. code-block:: python @@ -84,10 +84,10 @@ Or we call `client.get_input_entity When you're going to invoke an API method, most require you to pass an :tl:`InputUser`, :tl:`InputChat`, or so on, this is why using -`client.get_input_entity ` +`client.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 `client.get_entity ` +user, use `client.get_entity() ` instead: .. code-block:: python @@ -114,7 +114,7 @@ every time its used, simply call `telethon.utils.get_input_peer`: After this small parenthesis about `client.get_entity ` versus -`client.get_input_entity `, +`client.get_input_entity() `, we have everything we need. To invoke our request we do: @@ -156,7 +156,7 @@ This can further be simplified to: Requests in Parallel -******************** +==================== The library will automatically merge outgoing requests into a single *container*. Telegram's API supports sending multiple requests in a @@ -224,6 +224,6 @@ and still access the successful results: # The second request failed. second = e.exceptions[1] -__ https://lonamiwebs.github.io/Telethon -__ https://lonamiwebs.github.io/Telethon/methods/index.html -__ https://lonamiwebs.github.io/Telethon/?q=message&redirect=no +.. _check the documentation: https://lonamiwebs.github.io/Telethon +.. _method you need: https://lonamiwebs.github.io/Telethon/methods/index.html +.. _use the search: https://lonamiwebs.github.io/Telethon/?q=message&redirect=no diff --git a/readthedocs/extra/advanced-usage/sessions.rst b/readthedocs/concepts/sessions.rst similarity index 81% rename from readthedocs/extra/advanced-usage/sessions.rst rename to readthedocs/concepts/sessions.rst index eeae7940..ee528b48 100644 --- a/readthedocs/extra/advanced-usage/sessions.rst +++ b/readthedocs/concepts/sessions.rst @@ -6,8 +6,11 @@ Session Files .. contents:: -What are sessions? -****************** +They are an important part for the library to be efficient, such as caching +and handling your authorization key (or you would have to login every time!). + +What are Sessions? +================== The first parameter you pass to the constructor of the :ref:`TelegramClient ` is @@ -43,7 +46,7 @@ by setting ``client.session.save_entities = False``. Different Session Storage -************************* +========================= If you don't want to use the default SQLite session storage, you can also use one of the other implementations or implement your own storage. @@ -54,13 +57,15 @@ the session name. Telethon contains three implementations of the abstract ``Session`` class: -* ``MemorySession``: stores session data within memory. -* ``SQLiteSession``: stores sessions within on-disk SQLite databases. Default. -* ``StringSession``: stores session data within memory, +.. currentmodule:: telethon.sessions + +* `MemorySession `: stores session data within memory. +* `SQLiteSession `: stores sessions within on-disk SQLite databases. Default. +* `StringSession `: stores session data within memory, but can be saved as a string. You can import these ``from telethon.sessions``. For example, using the -``StringSession`` is done as follows: +`StringSession ` is done as follows: .. code-block:: python @@ -91,25 +96,27 @@ There are other community-maintained implementations available: * `Redis `_: stores all sessions in a single Redis data store. + Creating your Own Storage -************************* +========================= The easiest way to create your own storage implementation is to use -``MemorySession`` as the base and check out how ``SQLiteSession`` or -one of the community-maintained implementations work. You can find the -relevant Python files under the ``sessions`` directory in Telethon. +`MemorySession ` as the base and check out how +`SQLiteSession ` or one of the community-maintained +implementations work. You can find the relevant Python files under the +``sessions/`` directory in the Telethon's repository. After you have made your own implementation, you can add it to the community-maintained session implementation list above with a pull request. String Sessions -*************** +=============== -``StringSession`` are a convenient way to embed your login credentials -directly into your code for extremely easy portability, since all they -take is a string to be able to login without asking for your phone and -code (or faster start if you're using a bot token). +`StringSession ` are a convenient way to embed your +login credentials directly into your code for extremely easy portability, +since all they take is a string to be able to login without asking for your +phone and code (or faster start if you're using a bot token). The easiest way to generate a string session is as follows: diff --git a/readthedocs/concepts/strings.rst b/readthedocs/concepts/strings.rst new file mode 100644 index 00000000..f6f3812e --- /dev/null +++ b/readthedocs/concepts/strings.rst @@ -0,0 +1,100 @@ +====================== +String-based Debugging +====================== + +Debugging is *really* important. Telegram's API is really big and there +is a lot of things that you should know. Such as, what attributes or fields +does a result have? Well, the easiest thing to do is printing it: + +.. code-block:: python + + user = client.get_entity('Lonami') + print(user) + +That will show a huge **string** similar to the following: + +.. code-block:: python + + User(id=10885151, is_self=False, contact=False, mutual_contact=False, deleted=False, bot=False, bot_chat_history=False, bot_nochats=False, verified=False, restricted=False, min=False, bot_inline_geo=False, access_hash=123456789012345678, first_name='Lonami', last_name=None, username='Lonami', phone=None, photo=UserProfilePhoto(photo_id=123456789012345678, photo_small=FileLocation(dc_id=4, volume_id=1234567890, local_id=1234567890, secret=123456789012345678), photo_big=FileLocation(dc_id=4, volume_id=1234567890, local_id=1234567890, secret=123456789012345678)), status=UserStatusOffline(was_online=datetime.datetime(2018, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc)), bot_info_version=None, restriction_reason=None, bot_inline_placeholder=None, lang_code=None) + +That's a lot of text. But as you can see, all the properties are there. +So if you want the username you **don't use regex** or anything like +splitting ``str(user)`` to get what you want. You just access the +attribute you need: + +.. code-block:: python + + username = user.username + +Can we get better than the shown string, though? Yes! + +.. code-block:: python + + print(user.stringify()) + +Will show a much better: + +.. code-block:: python + + User( + id=10885151, + is_self=False, + contact=False, + mutual_contact=False, + deleted=False, + bot=False, + bot_chat_history=False, + bot_nochats=False, + verified=False, + restricted=False, + min=False, + bot_inline_geo=False, + access_hash=123456789012345678, + first_name='Lonami', + last_name=None, + username='Lonami', + phone=None, + photo=UserProfilePhoto( + photo_id=123456789012345678, + photo_small=FileLocation( + dc_id=4, + volume_id=123456789, + local_id=123456789, + secret=-123456789012345678 + ), + photo_big=FileLocation( + dc_id=4, + volume_id=123456789, + local_id=123456789, + secret=123456789012345678 + ) + ), + status=UserStatusOffline( + was_online=datetime.datetime(2018, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc) + ), + bot_info_version=None, + restriction_reason=None, + bot_inline_placeholder=None, + lang_code=None + ) + +Now it's easy to see how we could get, for example, +the ``was_online`` time. It's inside ``status``: + +.. code-block:: python + + online_at = user.status.was_online + +You don't need to print everything to see what all the possible values +can be. You can just search in http://lonamiwebs.github.io/Telethon/. + +Remember that you can use Python's `isinstance +`_ +to check the type of something. For example: + +.. code-block:: python + + from telethon import types + + if isinstance(user.status, types.UserStatusOffline): + print(user.status.was_online) diff --git a/readthedocs/concepts/updates.rst b/readthedocs/concepts/updates.rst new file mode 100644 index 00000000..aec75094 --- /dev/null +++ b/readthedocs/concepts/updates.rst @@ -0,0 +1,229 @@ +================ +Updates in Depth +================ + +Properties vs. Methods +====================== + +The event shown above acts just like a `custom.Message +`, which means you +can access all the properties it has, like ``.sender``. + +**However** events are different to other methods in the client, like +`client.get_messages `. +Events *may not* send information about the sender or chat, which means it +can be ``None``, but all the methods defined in the client always have this +information so it doesn't need to be re-fetched. For this reason, you have +``get_`` methods, which will make a network call if necessary. + +In short, you should do this: + +.. code-block:: python + + @client.on(events.NewMessage) + async def handler(event): + # event.input_chat may be None, use event.get_input_chat() + chat = await event.get_input_chat() + sender = await event.get_sender() + buttons = await event.get_buttons() + + async def main(): + async for message in client.iter_messages('me', 10): + # Methods from the client always have these properties ready + chat = message.input_chat + sender = message.sender + buttons = message.buttons + +Notice, properties (`message.sender +`) don't need an ``await``, but +methods (`message.get_sender +`) **do** need an ``await``, +and you should use methods in events for these properties that may need network. + +Events Without the client +========================= + +The code of your application starts getting big, so you decide to +separate the handlers into different files. But how can you access +the client from these files? You don't need to! Just `events.register +` them: + +.. code-block:: python + + # handlers/welcome.py + from telethon import events + + @events.register(events.NewMessage('(?i)hello')) + async def handler(event): + client = event.client + await event.respond('Hey!') + await client.send_message('me', 'I said hello to someone') + + +Registering events is a way of saying "this method is an event handler". +You can use `telethon.events.is_handler` to check if any method is a handler. +You can think of them as a different approach to Flask's blueprints. + +It's important to note that this does **not** add the handler to any client! +You never specified the client on which the handler should be used. You only +declared that it is a handler, and its type. + +To actually use the handler, you need to `client.add_event_handler +` to the +client (or clients) where they should be added to: + +.. code-block:: python + + # main.py + from telethon import TelegramClient + import handlers.welcome + + with TelegramClient(...) as client: + client.add_event_handler(handlers.welcome.handler) + client.run_until_disconnected() + + +This also means that you can register an event handler once and +then add it to many clients without re-declaring the event. + + +Events Without Decorators +========================= + +If for any reason you don't want to use `telethon.events.register`, +you can explicitly pass the event handler to use to the mentioned +`client.add_event_handler +`: + +.. code-block:: python + + from telethon import TelegramClient, events + + async def handler(event): + ... + + with TelegramClient(...) as client: + client.add_event_handler(handler, events.NewMessage) + client.run_until_disconnected() + + +Similarly, you also have `client.remove_event_handler +` +and `client.list_event_handlers +`. + +The ``event`` argument is optional in all three methods and defaults to +`events.Raw ` for adding, and ``None`` when +removing (so all callbacks would be removed). + +.. note:: + + The ``event`` type is ignored in `client.add_event_handler + ` + if you have used `telethon.events.register` on the ``callback`` + before, since that's the point of using such method at all. + + +Stopping Propagation of Updates +=============================== + +There might be cases when an event handler is supposed to be used solitary and +it makes no sense to process any other handlers in the chain. For this case, +it is possible to raise a `telethon.events.StopPropagation` exception which +will cause the propagation of the update through your handlers to stop: + +.. code-block:: python + + from telethon.events import StopPropagation + + @client.on(events.NewMessage) + async def _(event): + # ... some conditions + await event.delete() + + # Other handlers won't have an event to work with + raise StopPropagation + + @client.on(events.NewMessage) + async def _(event): + # Will never be reached, because it is the second handler + # in the chain. + pass + + +Remember to check :ref:`telethon-events` if you're looking for +the methods reference. + +Understanding asyncio +===================== + + +With ``asyncio``, the library has several tasks running in the background. +One task is used for sending requests, another task is used to receive them, +and a third one is used to handle updates. + +To handle updates, you must keep your script running. You can do this in +several ways. For instance, if you are *not* running ``asyncio``'s event +loop, you should use `client.run_until_disconnected +`: + +.. code-block:: python + + import asyncio + from telethon import TelegramClient + + client = TelegramClient(...) + ... + client.run_until_disconnected() + + +Behind the scenes, this method is ``await``'ing on the `client.disconnected +` property, +so the code above and the following are equivalent: + +.. code-block:: python + + import asyncio + from telethon import TelegramClient + + client = TelegramClient(...) + + async def main(): + await client.disconnected + + loop = asyncio.get_event_loop() + loop.run_until_complete(main()) + + +You could also run `client.disconnected +` +until it completed. + +But if you don't want to ``await``, then you should know what you want +to be doing instead! What matters is that you shouldn't let your script +die. If you don't care about updates, you don't need any of this. + +Notice that unlike `client.disconnected +`, +`client.run_until_disconnected +` will +handle ``KeyboardInterrupt`` with you. This method is special and can +also be ran while the loop is running, so you can do this: + +.. code-block:: python + + async def main(): + await client.run_until_disconnected() + + loop.run_until_complete(main()) + +Sequential Updates +================== + +If you need to process updates sequentially (i.e. not in parallel), +you should set ``sequential_updates=True`` when creating the client: + +.. code-block:: python + + with TelegramClient(..., sequential_updates=True) as client: + ... diff --git a/readthedocs/conf.py b/readthedocs/conf.py index 9c04cc47..ed57f537 100644 --- a/readthedocs/conf.py +++ b/readthedocs/conf.py @@ -61,7 +61,7 @@ master_doc = 'index' # General information about the project. project = 'Telethon' -copyright = '2017, Lonami' +copyright = '2017 - 2019, Lonami' author = 'Lonami' # The version info for the project you're documenting, acts as replacement for diff --git a/readthedocs/extra/developing/coding-style.rst b/readthedocs/developing/coding-style.rst similarity index 100% rename from readthedocs/extra/developing/coding-style.rst rename to readthedocs/developing/coding-style.rst diff --git a/readthedocs/extra/developing/philosophy.rst b/readthedocs/developing/philosophy.rst similarity index 100% rename from readthedocs/extra/developing/philosophy.rst rename to readthedocs/developing/philosophy.rst diff --git a/readthedocs/extra/developing/project-structure.rst b/readthedocs/developing/project-structure.rst similarity index 98% rename from readthedocs/extra/developing/project-structure.rst rename to readthedocs/developing/project-structure.rst index c96a243b..960234ac 100644 --- a/readthedocs/extra/developing/project-structure.rst +++ b/readthedocs/developing/project-structure.rst @@ -4,7 +4,7 @@ Project Structure Main interface -************** +============== The library itself is under the ``telethon/`` directory. The ``__init__.py`` file there exposes the main ``TelegramClient``, a class @@ -34,7 +34,7 @@ which outgoing messages should be sent (how to encode their length and their body, if they're further encrypted). Auto-generated code -******************* +=================== The files under ``telethon_generator/`` are used to generate the code that gets placed under ``telethon/tl/``. The parsers take in files in diff --git a/readthedocs/extra/developing/telegram-api-in-other-languages.rst b/readthedocs/developing/telegram-api-in-other-languages.rst similarity index 98% rename from readthedocs/extra/developing/telegram-api-in-other-languages.rst rename to readthedocs/developing/telegram-api-in-other-languages.rst index 22bb416a..d77ebaa9 100644 --- a/readthedocs/extra/developing/telegram-api-in-other-languages.rst +++ b/readthedocs/developing/telegram-api-in-other-languages.rst @@ -19,7 +19,7 @@ there by `@vysheng `__, has been moved to `BitBucket `__. C++ -*** +=== The newest (and official) library, written from scratch, is called `tdlib `__ and is what the Telegram X @@ -27,7 +27,7 @@ uses. You can find more information in the official documentation, published `here `__. JavaScript -********** +========== `@zerobias `__ is working on `telegram-mtproto `__, @@ -35,7 +35,7 @@ a work-in-progress JavaScript library installable via `npm `__. Kotlin -****** +====== `Kotlogram `__ is a Telegram implementation written in Kotlin (one of the @@ -46,7 +46,7 @@ languages for yet working. PHP -*** +=== A PHP implementation is also available thanks to `@danog `__ and his @@ -55,7 +55,7 @@ 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 @@ -66,7 +66,7 @@ sad to see you go, but it would be nice to know what you miss from each other library in either one so both can improve. Rust -**** +==== Yet another work-in-progress implementation, this time for Rust thanks to `@JuanPotato `__ under the fancy diff --git a/readthedocs/extra/developing/test-servers.rst b/readthedocs/developing/test-servers.rst similarity index 100% rename from readthedocs/extra/developing/test-servers.rst rename to readthedocs/developing/test-servers.rst diff --git a/readthedocs/extra/developing/tips-for-porting-the-project.rst b/readthedocs/developing/tips-for-porting-the-project.rst similarity index 100% rename from readthedocs/extra/developing/tips-for-porting-the-project.rst rename to readthedocs/developing/tips-for-porting-the-project.rst diff --git a/readthedocs/extra/developing/understanding-the-type-language.rst b/readthedocs/developing/understanding-the-type-language.rst similarity index 100% rename from readthedocs/extra/developing/understanding-the-type-language.rst rename to readthedocs/developing/understanding-the-type-language.rst diff --git a/readthedocs/extra/examples/chats-and-channels.rst b/readthedocs/examples/chats-and-channels.rst similarity index 95% rename from readthedocs/extra/examples/chats-and-channels.rst rename to readthedocs/examples/chats-and-channels.rst index 33d06d85..c764d2b7 100644 --- a/readthedocs/extra/examples/chats-and-channels.rst +++ b/readthedocs/examples/chats-and-channels.rst @@ -5,13 +5,13 @@ Working with Chats and Channels .. note:: - These examples assume you have read :ref:`accessing-the-full-api`. + These examples assume you have read :ref:`full-api`. .. contents:: Joining a chat or channel -************************* +========================= Note that :tl:`Chat` are normal groups, and :tl:`Channel` are a special form of :tl:`Chat`, which can also be super-groups if @@ -19,7 +19,7 @@ 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 :tl:`JoinChannelRequest` to join such channel: @@ -41,7 +41,7 @@ __ https://lonamiwebs.github.io/Telethon/methods/channels/index.html Joining a private chat or channel -********************************* +================================= If all you have is a link like this one: ``https://t.me/joinchat/AAAAAFFszQPyPEZ7wgxLtd``, you already have @@ -57,7 +57,7 @@ example, is the ``hash`` of the chat or channel. Now you can use 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 :tl:`AddChatUserRequest`, which @@ -86,7 +86,7 @@ use is very straightforward, or :tl:`InviteToChannelRequest` for channels: 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 :tl:`CheckChatInviteRequest`, which takes in @@ -94,7 +94,7 @@ the hash of said channel or group. Admin Permissions -***************** +================= Giving or revoking admin permissions can be done with the :tl:`EditAdminRequest`: @@ -152,7 +152,7 @@ Giving or revoking admin permissions can be done with the :tl:`EditAdminRequest` Restricting Users -***************** +================= Similar to how you give or revoke admin permissions, you can edit the banned rights of a user through :tl:`EditBannedRequest` and its parameter @@ -203,7 +203,7 @@ https://core.telegram.org/bots/api#restrictchatmember. Kicking a member -**************** +================ Telegram doesn't actually have a request to kick a user from a group. Instead, you need to restrict them so they can't see messages. Any date @@ -228,7 +228,7 @@ __ 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 diff --git a/readthedocs/extra/examples/projects-using-telethon.rst b/readthedocs/examples/projects-using-telethon.rst similarity index 62% rename from readthedocs/extra/examples/projects-using-telethon.rst rename to readthedocs/examples/projects-using-telethon.rst index 122e02a7..7b7be2c1 100644 --- a/readthedocs/extra/examples/projects-using-telethon.rst +++ b/readthedocs/examples/projects-using-telethon.rst @@ -1,3 +1,5 @@ +.. _telethon_projects: + ======================= Projects using Telethon ======================= @@ -15,10 +17,10 @@ the library. .. _projects-telegram-export: telethon_examples/ -****************** +================== -`Link `_ / -`Author's website `_ +`telethon_examples `_ / +`LonamiWebs' site `_ This documentation is not the only place where you can find useful code snippets using the library. The main repository also has a folder with @@ -26,10 +28,10 @@ some cool examples (even a Tkinter GUI!) which you can download, edit and run to learn and play with them. telegram-export -*************** +=============== -`Link `_ / -`Author's website `_ +`telegram-export `_ / +`expectocode's GitHub `_ A tool to download Telegram data (users, chats, messages, and media) into a database (and display the saved data). @@ -37,28 +39,28 @@ into a database (and display the saved data). .. _projects-mautrix-telegram: mautrix-telegram -**************** +================ -`Link `_ / -`Author's website `_ +`mautrix-telegram `_ / +`maunium's site `_ A Matrix-Telegram hybrid puppeting/relaybot bridge. .. _projects-telegramtui: TelegramTUI -*********** +=========== -`Link `_ / -`Author's website `_ +`TelegramTUI `_ / +`bad-day's GitHub `_ A Telegram client on your terminal. spotify_telegram_bio_updater -**************************** +============================ -`Link `_ / -`Author's website `_ +`spotify_telegram_bio_updater `_ / +`pooltalks' Telegram `_ Small project that updates the biography of a telegram user according to their current Spotify playback, or revert it if no playback is active. diff --git a/readthedocs/extra/examples/users.rst b/readthedocs/examples/users.rst similarity index 88% rename from readthedocs/extra/examples/users.rst rename to readthedocs/examples/users.rst index 54cae92c..130bd7ae 100644 --- a/readthedocs/extra/examples/users.rst +++ b/readthedocs/examples/users.rst @@ -5,13 +5,13 @@ Users .. note:: - These examples assume you have read :ref:`accessing-the-full-api`. + These examples assume you have read :ref:`full-api`. .. contents:: Retrieving full information -*************************** +=========================== If you need to retrieve the bio, biography or about information for a user you should use :tl:`GetFullUser`: @@ -32,7 +32,7 @@ See :tl:`UserFull` to know what other fields you can access. Updating your name and/or bio -***************************** +============================= The first name, last name and bio (about) can all be changed with the same request. Omitted fields won't change after invoking :tl:`UpdateProfile`: @@ -47,7 +47,7 @@ request. Omitted fields won't change after invoking :tl:`UpdateProfile`: Updating your username -********************** +====================== You need to use :tl:`account.UpdateUsername`: @@ -59,7 +59,7 @@ You need to use :tl:`account.UpdateUsername`: Updating your profile photo -*************************** +=========================== The easiest way is to upload a new file and use that as the profile photo through :tl:`UploadProfilePhoto`: diff --git a/readthedocs/examples/word-of-warning.rst b/readthedocs/examples/word-of-warning.rst new file mode 100644 index 00000000..5501325f --- /dev/null +++ b/readthedocs/examples/word-of-warning.rst @@ -0,0 +1,16 @@ +================= +A Word of Warning +================= + +Full API is **not** how you are intended to use the library. You **should** +always prefer the :ref:`client-ref`. However, not everything is implemented +as a friendly method, so full API is your last resort. + +If you select a method in :ref:`client-ref`, you will most likely find an +example for that method. This is how you are intended to use the library. + +Full API **will** break between different minor versions of the library, +since Telegram changes very often. The friendly methods will be kept +compatible between major versions. + +If you need to see real-world examples, please refer to :ref:`telethon_projects`. diff --git a/readthedocs/extra/examples/working-with-messages.rst b/readthedocs/examples/working-with-messages.rst similarity index 93% rename from readthedocs/extra/examples/working-with-messages.rst rename to readthedocs/examples/working-with-messages.rst index 08b47c8e..e08e4cf0 100644 --- a/readthedocs/extra/examples/working-with-messages.rst +++ b/readthedocs/examples/working-with-messages.rst @@ -5,13 +5,13 @@ Working with messages .. note:: - These examples assume you have read :ref:`accessing-the-full-api`. + These examples assume you have read :ref:`full-api`. .. contents:: 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 diff --git a/readthedocs/extra/advanced-usage/mastering-telethon.rst b/readthedocs/extra/advanced-usage/mastering-telethon.rst deleted file mode 100644 index 60803db4..00000000 --- a/readthedocs/extra/advanced-usage/mastering-telethon.rst +++ /dev/null @@ -1,343 +0,0 @@ -.. _mastering-telethon: - -================== -Mastering Telethon -================== - -You've come far! In this section you will learn best practices, as well -as how to fix some silly (yet common) errors you may have found. Let's -start with a simple one. - -Asyncio madness -*************** - -We promise ``asyncio`` is worth learning. Take your time to learn it. -It's a powerful tool that enables you to use this powerful library. -You need to be comfortable with it if you want to master Telethon. - -.. code-block:: text - - AttributeError: 'coroutine' object has no attribute 'id' - -You probably had a previous version, upgraded, and expected everything -to work. Remember, just add this line: - -.. code-block:: python - - import telethon.sync - -If you're inside an event handler you need to ``await`` **everything** that -*makes a network request*. Getting users, sending messages, and nearly -everything in the library needs access to the network, so they need to -be awaited: - -.. code-block:: python - - @client.on(events.NewMessage) - async def handler(event): - print((await event.get_sender()).username) - - -You may want to read https://lonamiwebs.github.io/blog/asyncio/ to help -you understand ``asyncio`` better. I'm open for `feedback -`_ regarding that blog post - -Entities -******** - -A lot of methods and requests require *entities* to work. For example, -you send a message to an *entity*, get the username of an *entity*, and -so on. There is an entire section on this at :ref:`entities` due to their -importance. - -There are a lot of things that work as entities: usernames, phone numbers, -chat links, invite links, IDs, and the types themselves. That is, you can -use any of those when you see an "entity" is needed. - -.. note:: - - Remember that the phone number must be in your contact list before you - can use it. - -You should use, **from better to worse**: - -1. Input entities. For example, `event.input_chat - `, - `message.input_sender - `, - or caching an entity you will use a lot with - ``entity = await client.get_input_entity(...)``. - -2. Entities. For example, if you had to get someone's - username, you can just use ``user`` or ``channel``. - It will work. Only use this option if you already have the entity! - -3. IDs. This will always look the entity up from the - cache (the ``*.session`` file caches seen entities). - -4. Usernames, phone numbers and links. The cache will be - used too (unless you force a `client.get_entity() - `), - but may make a request if the username, phone or link - has not been found yet. - -In short, unlike in most bot API libraries where you use the ID, you -**should not** use the ID *if* you have the input entity. This is OK: - -.. code-block:: python - - async def handler(event): - await client.send_message(event.sender_id, 'Hi') - -However, **this is better**: - -.. code-block:: python - - async def handler(event): - await client.send_message(event.input_sender, 'Hi') - -Note that this also works for `message ` -instead of ``event``. Telegram may not send the sender information, so if you -want to be 99% confident that the above will work you should do this: - -.. code-block:: python - - async def handler(event): - await client.send_message(await event.get_input_sender(), 'Hi') - -Methods are able to make network requests to get information that -could be missing. Properties will never make a network request. - -Of course, it is convenient to IDs or usernames for most purposes. It will -be fast enough and caching with `client.get_input_entity(...) -` will -be a micro-optimization. However it's worth knowing, and it -will also let you know if the entity cannot be found beforehand. - -.. note:: - - Sometimes Telegram doesn't send the access hash inside entities, - so using `chat ` - or `sender ` - may not work, but `input_chat - ` - and `input_sender - ` - while making requests definitely will since that's what they exist - for. If Telegram did not send information about the access hash, - you will get something like "Invalid channel object" or - "Invalid user object". - - -Debugging -********* - -**Please enable logging**: - -.. code-block:: python - - import logging - logging.basicConfig(level=logging.WARNING) - -Change it for ``logging.DEBUG`` if you are asked for logs. It will save you -a lot of headaches and time when you work with events. This is for errors. - -Debugging is *really* important. Telegram's API is really big and there -is a lot of things that you should know. Such as, what attributes or fields -does a result have? Well, the easiest thing to do is printing it: - -.. code-block:: python - - user = client.get_entity('Lonami') - print(user) - -That will show a huge line similar to the following: - -.. code-block:: python - - User(id=10885151, is_self=False, contact=False, mutual_contact=False, deleted=False, bot=False, bot_chat_history=False, bot_nochats=False, verified=False, restricted=False, min=False, bot_inline_geo=False, access_hash=123456789012345678, first_name='Lonami', last_name=None, username='Lonami', phone=None, photo=UserProfilePhoto(photo_id=123456789012345678, photo_small=FileLocation(dc_id=4, volume_id=1234567890, local_id=1234567890, secret=123456789012345678), photo_big=FileLocation(dc_id=4, volume_id=1234567890, local_id=1234567890, secret=123456789012345678)), status=UserStatusOffline(was_online=datetime.datetime(2018, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc)), bot_info_version=None, restriction_reason=None, bot_inline_placeholder=None, lang_code=None) - -That's a lot of text. But as you can see, all the properties are there. -So if you want the username you **don't use regex** or anything like -splitting ``str(user)`` to get what you want. You just access the -attribute you need: - -.. code-block:: python - - username = user.username - -Can we get better than the shown string, though? Yes! - -.. code-block:: python - - print(user.stringify()) - -Will show a much better: - -.. code-block:: python - - User( - id=10885151, - is_self=False, - contact=False, - mutual_contact=False, - deleted=False, - bot=False, - bot_chat_history=False, - bot_nochats=False, - verified=False, - restricted=False, - min=False, - bot_inline_geo=False, - access_hash=123456789012345678, - first_name='Lonami', - last_name=None, - username='Lonami', - phone=None, - photo=UserProfilePhoto( - photo_id=123456789012345678, - photo_small=FileLocation( - dc_id=4, - volume_id=123456789, - local_id=123456789, - secret=-123456789012345678 - ), - photo_big=FileLocation( - dc_id=4, - volume_id=123456789, - local_id=123456789, - secret=123456789012345678 - ) - ), - status=UserStatusOffline( - was_online=datetime.datetime(2018, 1, 2, 3, 4, 5, tzinfo=datetime.timezone.utc) - ), - bot_info_version=None, - restriction_reason=None, - bot_inline_placeholder=None, - lang_code=None - ) - -Now it's easy to see how we could get, for example, -the ``was_online`` time. It's inside ``status``: - -.. code-block:: python - - online_at = user.status.was_online - -You don't need to print everything to see what all the possible values -can be. You can just search in http://lonamiwebs.github.io/Telethon/. - -Remember that you can use Python's `isinstance -`_ -to check the type of something. For example: - -.. code-block:: python - - from telethon import types - - if isinstance(user.status, types.UserStatusOffline): - print(user.status.was_online) - -Avoiding Limits -*************** - -Don't spam. You won't get ``FloodWaitError`` or your account banned or -deleted if you use the library *for legit use cases*. Make cool tools. -Don't spam! Nobody knows the exact limits for all requests since they -depend on a lot of factors, so don't bother asking. - -Still, if you do have a legit use case and still get those errors, the -library will automatically sleep when they are smaller than 60 seconds -by default. You can set different "auto-sleep" thresholds: - -.. code-block:: python - - client.flood_sleep_threshold = 0 # Don't auto-sleep - client.flood_sleep_threshold = 24 * 60 * 60 # Sleep always - -You can also except it and act as you prefer: - -.. code-block:: python - - from telethon.errors import FloodWaitError - try: - ... - except FloodWaitError as e: - print('Flood waited for', e.seconds) - quit(1) - -VoIP numbers are very limited, and some countries are more limited too. - -Chat or User From Messages -************************** - -Although it's explicitly noted in the documentation that messages -*subclass* `ChatGetter ` -and `SenderGetter `, -some people still don't get inheritance. - -When the documentation says "Bases: `telethon.tl.custom.chatgetter.ChatGetter`" -it means that the class you're looking at, *also* can act as the class it -bases. In this case, `ChatGetter ` -knows how to get the *chat* where a thing belongs to. - -So, a `Message ` is a -`ChatGetter `. -That means you can do this: - -.. code-block:: python - - message.is_private - message.chat_id - message.get_chat() - # ...etc - -`SenderGetter ` is similar: - -.. code-block:: python - - message.user_id - message.get_input_user() - message.user - # ...etc - -Quite a few things implement them, so it makes sense to reuse the code. -For example, all events (except raw updates) implement `ChatGetter -` since all events occur -in some chat. - -Session Files -************* - -They are an important part for the library to be efficient, such as caching -and handling your authorization key (or you would have to login every time!). - -However, some people have a lot of trouble with SQLite, especially in Windows: - -.. code-block:: text - - ...some lines of traceback - 'insert or replace into entities values (?,?,?,?,?)', rows) - sqlite3.OperationalError: database is locked - -This error occurs when **two or more clients use the same session**, -that is, when you write the same session name to be used in the client: - -* You have two scripts running (interactive sessions count too). -* You have two clients in the same script running at the same time. - -The solution is, if you need two clients, use two sessions. If the -problem persists and you're on Linux, you can use ``fuser my.session`` -to find out the process locking the file. As a last resort, you can -reboot your system. - -If you really dislike SQLite, use a different session storage. There -is an entire section covering that at :ref:`sessions`. - -Final Words -*********** - -Now you are aware of some common errors and use cases, this should help -you master your Telethon skills to get the most out of the library. Have -fun developing awesome things! diff --git a/readthedocs/extra/advanced-usage/update-modes.rst b/readthedocs/extra/advanced-usage/update-modes.rst deleted file mode 100644 index d8950834..00000000 --- a/readthedocs/extra/advanced-usage/update-modes.rst +++ /dev/null @@ -1,73 +0,0 @@ -.. _update-modes: - -============ -Update Modes -============ - -With ``asyncio``, the library has several tasks running in the background. -One task is used for sending requests, another task is used to receive them, -and a third one is used to handle updates. - -To handle updates, you must keep your script running. You can do this in -several ways. For instance, if you are *not* running ``asyncio``'s event -loop, you should use `client.run_until_disconnected -`: - -.. code-block:: python - - import asyncio - from telethon import TelegramClient - - client = TelegramClient(...) - ... - client.run_until_disconnected() - - -Behind the scenes, this method is ``await``'ing on the `client.disconnected -` property, -so the code above and the following are equivalent: - -.. code-block:: python - - import asyncio - from telethon import TelegramClient - - client = TelegramClient(...) - - async def main(): - await client.disconnected - - loop = asyncio.get_event_loop() - loop.run_until_complete(main()) - - -You could also run `client.disconnected -` -until it completed. - -But if you don't want to ``await``, then you should know what you want -to be doing instead! What matters is that you shouldn't let your script -die. If you don't care about updates, you don't need any of this. - -Notice that unlike `client.disconnected -`, -`client.run_until_disconnected -` will -handle ``KeyboardInterrupt`` with you. This method is special and can -also be ran while the loop is running, so you can do this: - -.. code-block:: python - - async def main(): - await client.run_until_disconnected() - - loop.run_until_complete(main()) - - -If you need to process updates sequentially (i.e. not in parallel), -you should set ``sequential_updates=True`` when creating the client: - -.. code-block:: python - - with TelegramClient(..., sequential_updates=True) as client: - ... diff --git a/readthedocs/extra/basic/creating-a-client.rst b/readthedocs/extra/basic/creating-a-client.rst deleted file mode 100644 index 8416e3b5..00000000 --- a/readthedocs/extra/basic/creating-a-client.rst +++ /dev/null @@ -1,263 +0,0 @@ -.. _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 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! - -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, sync - - # Use your own values here - api_id = 12345 - api_hash = '0123456789abcdef0123456789abcdef' - - 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 by default a database file using Python's ``sqlite3``. - -.. note:: - - It's important that the library always accesses the same session file so - that you don't need to re-send the code over and over again. By default it - creates the file in your working directory, but absolute paths work too. - - -Once you have a client ready, simply `.start() -` it: - -.. code-block:: python - - client.start() - -This line connects to Telegram, checks whether the current user is -authorized or not, and if it's not, it begins the login or sign up process. - -When you're done with your code, you should always disconnect: - -.. code-block:: python - - client = TelegramClient(...) - try: - client.start() - ... # your code here - finally: - client.disconnect() - - -You can also use a ``with`` block to achieve the same effect: - -.. code-block:: python - - client = TelegramClient(...) - with client: - ... # your code here - - # or - with TelegramClient(...) as client: - ... # your code here - - -Wrapping it all together: - -.. code-block:: python - - from telethon import TelegramClient, sync - with TelegramClient('session_name', api_id, api_hash) as client: - ... # your code - -Just two setup lines. - -.. warning:: - Please note that if you fail to login around 5 times (or change the first - parameter of the :ref:`TelegramClient `, which is the session - name) you will receive a ``FloodWaitError`` of around 22 hours, so be - careful not to mess this up! This shouldn't happen if you're doing things - as explained, though. - -.. 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`__. - - -Manually Signing In -******************* - -.. note:: - - Skip this unless you need more control when connecting. - -If you need more control, you can replicate what `client.start() -` is doing behind the scenes -for your convenience. The first step is to connect to the servers: - -.. code-block:: python - - client.connect() - -You may or may not be authorized yet. You must be authorized -before you're able to send any request: - -.. code-block:: python - - client.is_user_authorized() # Returns True if you can send requests - -If you're not authorized, you need to `.sign_in -`: - -.. code-block:: python - - phone_number = '+34600000000' - 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. - -.. note:: - - If you send the code that Telegram sent you over the app through the - app itself, it will expire immediately. You can still send the code - through the app by "obfuscating" it (maybe add a magic constant, like - ``12345``, and then subtract it to get the real code back) or any other - technique. - -``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 - - from telethon import TelegramClient, sync - client = TelegramClient('session_name', api_id, api_hash) - - client.connect() - if not client.is_user_authorized(): - client.send_code_request(phone_number) - me = client.sign_in(phone_number, input('Enter code: ')) - - -Remember that this is the manual process and it's so much easier -to use the code snippets shown at the beginning of the page. - -The code shown is just what `.start() -` will be doing behind the scenes -(with a few extra checks), so that you know how to sign in case you want -to avoid using ``input()`` (the default) for whatever reason. If no phone -or bot token is provided, you will be asked one through ``input()``. The -method also accepts a ``phone=`` and ``bot_token`` parameters. - -You can use either, as both will work. Determining which -is just a matter of taste, and how much control you need. - -Remember that you can get yourself at any time with `client.get_me() -`. - - -Two Factor Authorization (2FA) ------------------------------- - -If you have Two Factor Authorization (from now on, 2FA) enabled on your -account, calling `.sign_in() -` will raise a -``SessionPasswordNeededError``. When this happens, just use the method -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()) - - -The mentioned `.start() -` method will handle this for you as -well, but you must set the ``password=`` parameter beforehand (it won't be -asked). - -If you don't have 2FA enabled, but you would like to do so through the -library, use `client.edit_2fa() -`. - -Be sure to know what you're doing when using this function and -you won't run into any problems. Take note that if you want to -set only the email/hint and leave the current password unchanged, -you need to "redo" the 2fa. - -See the examples below: - -.. code-block:: python - - from telethon.errors import EmailUnconfirmedError - - # Sets 2FA password for first time: - client.edit_2fa(new_password='supersecurepassword') - - # Changes password: - client.edit_2fa(current_password='supersecurepassword', - new_password='changedmymind') - - # Clears current password (i.e. removes 2FA): - client.edit_2fa(current_password='changedmymind', new_password=None) - - # Sets new password with recovery email: - try: - client.edit_2fa(new_password='memes and dreams', - email='JohnSmith@example.com') - # Raises error (you need to check your email to complete 2FA setup.) - except EmailUnconfirmedError: - # You can put email checking code here if desired. - pass - - # Also take note that unless you remove 2FA or explicitly - # give email parameter again it will keep the last used setting - - # Set hint after already setting password: - client.edit_2fa(current_password='memes and dreams', - new_password='memes and dreams', - hint='It keeps you alive') - -__ https://github.com/Anorov/PySocks#installation -__ https://github.com/Anorov/PySocks#usage-1 diff --git a/readthedocs/extra/basic/getting-started.rst b/readthedocs/extra/basic/getting-started.rst deleted file mode 100644 index b54a9e46..00000000 --- a/readthedocs/extra/basic/getting-started.rst +++ /dev/null @@ -1,95 +0,0 @@ -.. _getting-started: - - -=============== -Getting Started -=============== - -.. contents:: - - -Simple Installation -******************* - -.. code-block:: sh - - pip3 install telethon - -**More details**: :ref:`installation` - - -Creating a client -***************** - -.. code-block:: python - - from telethon import TelegramClient, sync - - # 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' - - client = TelegramClient('session_name', api_id, api_hash).start() - -**More details**: :ref:`creating-a-client` - - -Basic Usage -*********** - -.. code-block:: python - - # Getting information about yourself - me = client.get_me() - print(me.stringify()) - - # Sending a message (you can use 'me' or 'self' to message yourself) - client.send_message('username', 'Hello World from Telethon!') - - # Sending a file - client.send_file('username', '/home/myself/Pictures/holidays.jpg') - - # Retrieving messages from a chat - from telethon import utils - for message in client.iter_messages('username', limit=10): - print(utils.get_display_name(message.sender), message.message) - - # Listing all the dialogs (conversations you have open) - for dialog in client.get_dialogs(limit=10): - print(dialog.name, dialog.draft.text) - - # Downloading profile photos (default path is the working directory) - client.download_profile_photo('username') - - # Once you have a message with .media (if message.media) - # you can download it using client.download_media(), - # or even using message.download_media(): - messages = client.get_messages('username') - messages[0].download_media() - -**More details**: :ref:`telegram-client` - -See :ref:`telethon-client` for all available friendly methods. - - -Handling Updates -**************** - -.. code-block:: python - - from telethon import events - - @client.on(events.NewMessage(incoming=True, pattern='(?i)hi')) - async def handler(event): - await event.reply('Hello!') - - client.run_until_disconnected() - -**More details**: :ref:`working-with-updates` - - ----------- - -You can continue by clicking on the "More details" link below each -snippet of code or the "Next" button at the bottom of the page. diff --git a/readthedocs/extra/basic/installation.rst b/readthedocs/extra/basic/installation.rst deleted file mode 100644 index 003e142d..00000000 --- a/readthedocs/extra/basic/installation.rst +++ /dev/null @@ -1,119 +0,0 @@ -.. _installation: - -============ -Installation -============ - -.. contents:: - - -Automatic Installation -********************** - -To install Telethon, simply do: - -.. code-block:: sh - - pip3 install telethon - -Needless to say, you must have Python 3 and PyPi installed in your system. -See https://python.org and https://pypi.python.org/pypi/pip for more. - -If you already have the library installed, upgrade with: - -.. code-block:: sh - - pip3 install --upgrade telethon - -You can also install the library directly from GitHub or a fork: - -.. code-block:: sh - - # pip3 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. If you want to install a specific branch, append ``@branch`` to -the end of the first install command. - -By default the library will use a pure Python implementation for encryption, -which can be really slow when uploading or downloading files. If you don't -mind using a C extension, install `cryptg `__ -via ``pip`` or as an extra: - -.. code-block:: sh - - pip3 install telethon[cryptg] - - -Manual Installation -******************* - -1. Install the required ``pyaes`` (`GitHub`__ | `PyPi`__) and - ``rsa`` (`GitHub`__ | `PyPi`__) modules: - - .. code-block:: sh - - pip3 install pyaes rsa - -2. Clone Telethon's GitHub repository: - - .. code-block:: sh - - git clone https://github.com/LonamiWebs/Telethon.git - -3. Enter the cloned repository: - - .. code-block:: sh - - cd Telethon - -4. Run the code generator: - - .. code-block:: sh - - python3 setup.py gen - -5. Done! - -To generate the `method documentation`__, ``python3 setup.py gen docs``. - - -Optional dependencies -********************* - -If pillow_ is installed, large images will be automatically resized when -sending photos to prevent Telegram from failing with "invalid image". -Official clients also do this. - -If aiohttp_ is installed, the library will be able to download -:tl:`WebDocument` media files (otherwise you will get an error). - -If hachoir_ is installed, it will be used to extract metadata from files -when sending documents. Telegram uses this information to show the song's -performer, artist, title, duration, and for videos too (including size). -Otherwise, they will default to empty values, and you can set the attributes -manually. - -If cryptg_ is installed, encryption and decryption will be made in C instead -of Python which will be a lot faster. If your code deals with a lot of -updates or you are downloading/uploading a lot of files, you will notice -a considerable speed-up (from a hundred kilobytes per second to several -megabytes per second, if your connection allows it). If it's not installed, -pyaes_ will be used (which is pure Python, so it's much slower). - - -__ 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://lonamiwebs.github.io/Telethon - -.. _pillow: https://python-pillow.org -.. _aiohttp: https://docs.aiohttp.org -.. _hachoir: https://hachoir.readthedocs.io -.. _cryptg: https://github.com/Lonami/cryptg -.. _pyaes: https://github.com/ricmoo/pyaes diff --git a/readthedocs/extra/basic/telegram-client.rst b/readthedocs/extra/basic/telegram-client.rst deleted file mode 100644 index 9d2f5011..00000000 --- a/readthedocs/extra/basic/telegram-client.rst +++ /dev/null @@ -1,104 +0,0 @@ -.. _telegram-client: - -============== -TelegramClient -============== - -.. note:: - - Make sure to use the friendly methods described in :ref:`telethon-client`! - This section is just an introduction to using the client, but all the - available methods are in the :ref:`telethon-client` reference, including - detailed descriptions to what they do. - -The :ref:`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 :ref:`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 -(assuming you have ``from telethon import sync`` or ``import telethon.sync``): - -.. code-block:: python - - 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 a 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, -but for now you don't need to worry about it. Simply know that they are -a good way to get information about a user, chat or channel. - -Many other common methods for quick scripts are also available: - -.. code-block:: python - - # Note that you can use 'me' or 'self' to message yourself - client.send_message('username', 'Hello World from Telethon!') - - # .send_message's parse mode defaults to markdown, so you - # can use **bold**, __italics__, [links](https://example.com), `code`, - # and even [mentions](@username)/[mentions](tg://user?id=123456789) - client.send_message('username', '**Using** __markdown__ `too`!') - - client.send_file('username', '/home/myself/Pictures/holidays.jpg') - - # The utils package has some goodies, like .get_display_name() - from telethon import utils - for message in client.iter_messages('username', limit=10): - print(utils.get_display_name(message.sender), message.message) - - # Dialogs are the conversations you have open - for dialog in client.get_dialogs(limit=10): - print(dialog.name, dialog.draft.text) - - # Default path is the working directory - client.download_profile_photo('username') - - # 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 -***************** - -The :ref:`reference ` lists all the "handy" methods -available for you to use in the :ref:`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. - -See the mentioned :ref:`telethon-client` to find the available methods. - -.. _InteractiveTelegramClient: https://github.com/LonamiWebs/Telethon/blob/master/telethon_examples/interactive_telegram_client.py -.. _TelegramClient: https://github.com/LonamiWebs/Telethon/tree/master/telethon/client diff --git a/readthedocs/extra/basic/working-with-updates.rst b/readthedocs/extra/basic/working-with-updates.rst deleted file mode 100644 index 944317d3..00000000 --- a/readthedocs/extra/basic/working-with-updates.rst +++ /dev/null @@ -1,333 +0,0 @@ -.. _working-with-updates: - -==================== -Working with Updates -==================== - -.. important:: - - Coming from Telethon before it reached its version 1.0? - Make sure to read :ref:`compatibility-and-convenience`! - Otherwise, you can ignore this note and just follow along. - -The library comes with the `telethon.events` module. *Events* are an abstraction -over what Telegram calls `updates`__, and are meant to ease simple and common -usage when dealing with them, since there are many updates. If you're looking -for the method reference, check :ref:`telethon-events-package`, otherwise, -let's dive in! - - -.. important:: - - The library logs by default no output, and any exception that occurs - inside your handlers will be "hidden" from you to prevent the thread - from terminating (so it can still deliver events). You should enable - logging when working with events, at least the error level, to see if - this is happening so you can debug the error. - - **When using updates, please enable logging:** - - .. code-block:: python - - import logging - logging.basicConfig(level=logging.ERROR) - - -.. contents:: - - -Getting Started -*************** - -.. code-block:: python - - from telethon import TelegramClient, events - - client = TelegramClient('name', api_id, api_hash) - - @client.on(events.NewMessage) - async def my_event_handler(event): - if 'hello' in event.raw_text: - await event.reply('hi!') - - client.start() - client.run_until_disconnected() - - -Not much, but there might be some things unclear. What does this code do? - -.. code-block:: python - - from telethon import TelegramClient, events - - client = TelegramClient('name', api_id, api_hash) - - -This is normal creation (of course, pass session name, API ID and hash). -Nothing we don't know already. - -.. code-block:: python - - @client.on(events.NewMessage) - - -This Python decorator will attach itself to the ``my_event_handler`` -definition, and basically means that *on* a `NewMessage -` *event*, -the callback function you're about to define will be called: - -.. code-block:: python - - async def my_event_handler(event): - if 'hello' in event.raw_text: - await event.reply('hi!') - - -If a `NewMessage -` event occurs, -and ``'hello'`` is in the text of the message, we `.reply() -` to the event -with a ``'hi!'`` message. - -Do you notice anything different? Yes! Event handlers **must** be ``async`` -for them to work, and **every method using the network** needs to have an -``await``, otherwise, Python's ``asyncio`` will tell you that you forgot -to do so, so you can easily add it. - -.. code-block:: python - - client.start() - client.run_until_disconnected() - - -Finally, this tells the client that we're done with our code. We run the -``asyncio`` loop until the client starts (this is done behind the scenes, -since the method is so common), and then we run it again until we are -disconnected. Of course, you can do other things instead of running -until disconnected. For this refer to :ref:`update-modes`. - - -More on events -************** - -The `NewMessage ` event has much -more than what was shown. You can access the `.sender -` of the message -through that member, or even see if the message had `.media -`, a `.photo -` or a `.document -` (which you -could download with for example `client.download_media(event.photo) -`. - -If you don't want to `.reply() -` as a reply, -you can use the `.respond() ` -method instead. Of course, there are more events such as `ChatAction -` or `UserUpdate -`, and they're all -used in the same way. Simply add the `@client.on(events.XYZ) -` decorator on the top -of your handler and you're done! The event that will be passed always -is of type ``XYZ.Event`` (for instance, `NewMessage.Event -`), except for the `Raw -` event which just passes the :tl:`Update` object. - -Note that `.reply() -` and `.respond() -` are just wrappers around the -`client.send_message() ` -method which supports the ``file=`` parameter. -This means you can reply with a photo if you do `event.reply(file=photo) -`. - -You can put the same event on many handlers, and even different events on -the same handler. You can also have a handler work on only specific chats, -for example: - - -.. code-block:: python - - import ast - import random - - - # Either a single item or a list of them will work for the chats. - # You can also use the IDs, Peers, or even User/Chat/Channel objects. - @client.on(events.NewMessage(chats=('TelethonChat', 'TelethonOffTopic'))) - async def normal_handler(event): - if 'roll' in event.raw_text: - await event.reply(str(random.randint(1, 6))) - - - # Similarly, you can use incoming=True for messages that you receive - @client.on(events.NewMessage(chats='TelethonOffTopic', outgoing=True, - pattern='eval (.+)')) - async def admin_handler(event): - expression = event.pattern_match.group(1) - await event.reply(str(ast.literal_eval(expression))) - - -You can pass one or more chats to the ``chats`` parameter (as a list or tuple), -and only events from there will be processed. You can also specify whether you -want to handle incoming or outgoing messages (those you receive or those you -send). In this example, people can say ``'roll'`` and you will reply with a -random number, while if you say ``'eval 4+4'``, you will reply with the -solution. Try it! - - -Properties vs. Methods -********************** - -The event shown above acts just like a `custom.Message -`, which means you -can access all the properties it has, like ``.sender``. - -**However** events are different to other methods in the client, like -`client.get_messages `. -Events *may not* send information about the sender or chat, which means it -can be ``None``, but all the methods defined in the client always have this -information so it doesn't need to be re-fetched. For this reason, you have -``get_`` methods, which will make a network call if necessary. - -In short, you should do this: - -.. code-block:: python - - @client.on(events.NewMessage) - async def handler(event): - # event.input_chat may be None, use event.get_input_chat() - chat = await event.get_input_chat() - sender = await event.get_sender() - buttons = await event.get_buttons() - - async def main(): - async for message in client.iter_messages('me', 10): - # Methods from the client always have these properties ready - chat = message.input_chat - sender = message.sender - buttons = message.buttons - -Notice, properties (`message.sender -`) don't need an ``await``, but -methods (`message.get_sender -`) **do** need an ``await``, -and you should use methods in events for these properties that may need network. - - -Events Without the client -************************* - -The code of your application starts getting big, so you decide to -separate the handlers into different files. But how can you access -the client from these files? You don't need to! Just `events.register -` them: - -.. code-block:: python - - # handlers/welcome.py - from telethon import events - - @events.register(events.NewMessage('(?i)hello')) - async def handler(event): - client = event.client - await event.respond('Hey!') - await client.send_message('me', 'I said hello to someone') - - -Registering events is a way of saying "this method is an event handler". -You can use `telethon.events.is_handler` to check if any method is a handler. -You can think of them as a different approach to Flask's blueprints. - -It's important to note that this does **not** add the handler to any client! -You never specified the client on which the handler should be used. You only -declared that it is a handler, and its type. - -To actually use the handler, you need to `client.add_event_handler -` to the -client (or clients) where they should be added to: - -.. code-block:: python - - # main.py - from telethon import TelegramClient - import handlers.welcome - - with TelegramClient(...) as client: - client.add_event_handler(handlers.welcome.handler) - client.run_until_disconnected() - - -This also means that you can register an event handler once and -then add it to many clients without re-declaring the event. - - -Events Without Decorators -************************* - -If for any reason you don't want to use `telethon.events.register`, -you can explicitly pass the event handler to use to the mentioned -`client.add_event_handler -`: - -.. code-block:: python - - from telethon import TelegramClient, events - - async def handler(event): - ... - - with TelegramClient(...) as client: - client.add_event_handler(handler, events.NewMessage) - client.run_until_disconnected() - - -Similarly, you also have `client.remove_event_handler -` -and `client.list_event_handlers -`. - -The ``event`` argument is optional in all three methods and defaults to -`events.Raw ` for adding, and ``None`` when -removing (so all callbacks would be removed). - -.. note:: - - The ``event`` type is ignored in `client.add_event_handler - ` - if you have used `telethon.events.register` on the ``callback`` - before, since that's the point of using such method at all. - - -Stopping Propagation of Updates -******************************* - -There might be cases when an event handler is supposed to be used solitary and -it makes no sense to process any other handlers in the chain. For this case, -it is possible to raise a `telethon.events.StopPropagation` exception which -will cause the propagation of the update through your handlers to stop: - -.. code-block:: python - - from telethon.events import StopPropagation - - @client.on(events.NewMessage) - async def _(event): - # ... some conditions - await event.delete() - - # Other handlers won't have an event to work with - raise StopPropagation - - @client.on(events.NewMessage) - async def _(event): - # Will never be reached, because it is the second handler - # in the chain. - pass - - -Remember to check :ref:`telethon-events-package` if you're looking for -the methods reference. - - -__ https://lonamiwebs.github.io/Telethon/types/update.html diff --git a/readthedocs/extra/examples/telegram-client.rst b/readthedocs/extra/examples/telegram-client.rst deleted file mode 100644 index afc62ae2..00000000 --- a/readthedocs/extra/examples/telegram-client.rst +++ /dev/null @@ -1,751 +0,0 @@ -.. _telegram-client-example: - - -======================== -Examples with the Client -======================== - -This section explores the methods defined in the :ref:`telegram-client` -with some practical examples. The section assumes that you have imported -the ``telethon.sync`` package and that you have a client ready to use. - - -.. note:: - - There are some very common errors (such as forgetting to add - ``import telethon.sync``) for newcomers to ``asyncio``: - - .. code-block:: python - - # AttributeError: 'coroutine' object has no attribute 'first_name' - print(client.get_me().first_name) - - # TypeError: 'AsyncGenerator' object is not iterable - for message in client.iter_messages('me'): - ... - - # RuntimeError: This event loop is already running - with client.conversation('me') as conv: - ... - - That error means you're probably inside an ``async def`` so you - need to use: - - .. code-block:: python - - print((await client.get_me()).first_name) - async for message in client.iter_messages('me'): - ... - - async with client.conversation('me') as conv: - ... - - You can of course call other ``def`` functions from your ``async def`` - event handlers, but if they need making API calls, make your own - functions ``async def`` so you can ``await`` things: - - .. code-block:: python - - async def helper(client): - await client.send_message('me', 'Hi') - - If you're not inside an ``async def`` you can enter one like so: - - .. code-block:: python - - import asyncio - loop = asyncio.get_event_loop() - loop.run_until_complete(my_async_def()) - - -.. contents:: - - -Authorization -************* - -Starting the client is as easy as calling `client.start() -`: - -.. code-block:: python - - client.start() - ... # code using the client - client.disconnect() - -And you can even use a ``with`` block: - -.. code-block:: python - - with client: - ... # code using the client - - -.. note:: - - Remember we assume you have ``import telethon.sync``. You can of course - use the library without importing it. The code would be rewritten as: - - .. code-block:: python - - import asyncio - loop = asyncio.get_event_loop() - - async def main(): - await client.start() - ... - await client.disconnect() - - # or - async with client: - ... - - loop.run_until_complete(main()) - - All methods that need access to the network (e.g. to make an API call) - **must** be awaited (or their equivalent such as ``async for`` and - ``async with``). You can do this yourself or you can let the library - do it for you by using ``import telethon.sync``. With event handlers, - you must do this yourself. - -The cleanest way to delete your ``*.session`` file is `client.log_out -`. Note that you will obviously -need to login again if you use this: - -.. code-block:: python - - # Logs out and deletes the session file; you will need to sign in again - client.log_out() - - # You often simply want to disconnect. You will not need to sign in again - client.disconnect() - - -Group Chats -*********** - -You can easily iterate over all the :tl:`User` in a chat and -do anything you want with them by using `client.iter_participants -`: - -.. code-block:: python - - for user in client.iter_participants(chat): - ... # do something with the user - -You can also search by their name: - -.. code-block:: python - - for user in client.iter_participants(chat, search='name'): - ... - -Or by their type (e.g. if they are admin) with :tl:`ChannelParticipantsFilter`: - -.. code-block:: python - - from telethon.tl.types import ChannelParticipantsAdmins - - for user in client.iter_participants(chat, filter=ChannelParticipantsAdmins): - ... - - -Open Conversations and Joined Channels -************************************** - -The conversations you have open and the channels you have joined -are in your "dialogs", so to get them you need to `client.get_dialogs -`: - -.. code-block:: python - - dialogs = client.get_dialogs() - first = dialogs[0] - print(first.title) - -You can then use the dialog as if it were a peer: - -.. code-block:: python - - client.send_message(first, 'hi') - - -You can access `dialog.draft ` or you can -get them all at once without getting the dialogs: - -.. code-block:: python - - drafts = client.get_drafts() - - -Downloading Media -***************** - -It's easy to `download_profile_photo -`: - -.. code-block:: python - - client.download_profile_photo(user) - -Or `download_media ` -from a message: - -.. code-block:: python - - client.download_media(message) - client.download_media(message, filename) - # or - message.download_media() - message.download_media(filename) - -Remember that these methods return the final filename where the -media was downloaded (e.g. it may add the extension automatically). - -Getting Messages -**************** - -You can easily iterate over all the `messages -` of a chat with `iter_messages -`: - -.. code-block:: python - - for message in client.iter_messages(chat): - ... # do something with the message from recent to older - - for message in client.iter_messages(chat, reverse=True): - ... # going from the oldest to the most recent - -You can also use it to search for messages from a specific person: - -.. code-block:: python - - for message in client.iter_messages(chat, from_user='me'): - ... - -Or you can search by text: - -.. code-block:: python - - for message in client.iter_messages(chat, search='hello'): - ... - -Or you can search by media with a :tl:`MessagesFilter`: - -.. code-block:: python - - from telethon.tl.types import InputMessagesFilterPhotos - - for message in client.iter_messages(chat, filter=InputMessagesFilterPhotos): - ... - -If you want a list instead, use the get variant. The second -argument is the limit, and ``None`` means "get them all": - -.. code-block:: python - - - from telethon.tl.types import InputMessagesFilterPhotos - - # Get 0 photos and print the total - photos = client.get_messages(chat, 0, filter=InputMessagesFilterPhotos) - print(photos.total) - - # Get all the photos - photos = client.get_messages(chat, None, filter=InputMessagesFilterPhotos) - -Or just some IDs: - -.. code-block:: python - - message_1337 = client.get_messages(chats, ids=1337) - - -Exporting Messages -****************** - -If you plan on exporting data from your Telegram account, such as the entire -message history from your private conversations, chats or channels, or if you -plan to download a lot of media, you may prefer to do this within a *takeout* -session. Takeout sessions let you export data from your account with lower -flood wait limits. - -To start a takeout session, simply call `client.takeout() -`: - -.. code-block:: python - - from telethon import errors - - try: - with client.takeout() as takeout: - for message in takeout.iter_messages(chat, wait_time=0): - ... # Do something with the message - - except errors.TakeoutInitDelayError as e: - print('Must wait', e.seconds, 'before takeout') - - -Depending on the condition of the session (for example, when it's very -young and the method has not been called before), you may or not need -to ``except errors.TakeoutInitDelayError``. However, it is good practice. - - -Sending Messages -**************** - -Just use `send_message `: - -.. code-block:: python - - client.send_message('lonami', 'Thanks for the Telethon library!') - -The function returns the `custom.Message ` -that was sent so you can do more things with it if you want. - -You can also `reply ` or -`respond ` to messages: - -.. code-block:: python - - message.reply('Hello') - message.respond('World') - -Sending Markdown or HTML messages -********************************* - -Markdown (``'md'`` or ``'markdown'``) is the default `parse_mode -` -for the client. You can change the default parse mode like so: - -.. code-block:: python - - client.parse_mode = 'html' - - -Now all messages will be formatted as HTML by default: - -.. code-block:: python - - client.send_message('me', 'Some bold and italic text') - client.send_message('me', 'An URL') - client.send_message('me', 'code and
pre\nblocks
') - client.send_message('me', 'Mentions') - - -You can override the default parse mode to use for special cases: - -.. code-block:: python - - # No parse mode by default - client.parse_mode = None - - # ...but here I want markdown - client.send_message('me', 'Hello, **world**!', parse_mode='md') - - # ...and here I need HTML - client.send_message('me', 'Hello, world!', parse_mode='html') - -The rules are the same as for Bot API, so please refer to -https://core.telegram.org/bots/api#formatting-options. - -Sending Messages with Media -*************************** - -Sending media can be done with `send_file -`: - -.. code-block:: python - - client.send_file(chat, '/my/photos/me.jpg', caption="It's me!") - # or - client.send_message(chat, "It's me!", file='/my/photos/me.jpg') - -You can send voice notes or round videos by setting the right arguments: - -.. code-block:: python - - client.send_file(chat, '/my/songs/song.mp3', voice_note=True) - client.send_file(chat, '/my/videos/video.mp4', video_note=True) - -You can set a JPG thumbnail for any document: - -.. code-block:: python - - client.send_file(chat, '/my/documents/doc.txt', thumb='photo.jpg') - -You can force sending images as documents: - -.. code-block:: python - - client.send_file(chat, '/my/photos/photo.png', force_document=True) - -You can send albums if you pass more than one file: - -.. code-block:: python - - client.send_file(chat, [ - '/my/photos/holiday1.jpg', - '/my/photos/holiday2.jpg', - '/my/drawings/portrait.png' - ]) - -The caption can also be a list to match the different photos. - -Reusing Uploaded Files -********************** - -All files you send are automatically cached, so if you do: - -.. code-block:: python - - client.send_file(first_chat, 'document.txt') - client.send_file(second_chat, 'document.txt') - -The ``'document.txt'`` file will only be uploaded once. You -can disable this behaviour by settings ``allow_cache=False``: - -.. code-block:: python - - client.send_file(first_chat, 'document.txt', allow_cache=False) - client.send_file(second_chat, 'document.txt', allow_cache=False) - -Disabling cache is the only way to send the same document with different -attributes (for example, you send an ``.ogg`` as a song but now you want -it to show as a voice note; you probably need to disable the cache). - -However, you can *upload* the file once (not sending it yet!), and *then* -you can send it with different attributes. This means you can send an image -as a photo and a document: - -.. code-block:: python - - file = client.upload_file('photo.jpg') - client.send_file(chat, file) # sends as photo - client.send_file(chat, file, force_document=True) # sends as document - - file.name = 'not a photo.jpg' - client.send_file(chat, file, force_document=True) # document, new name - -Or, the example described before: - -.. code-block:: python - - file = client.upload_file('song.ogg') - client.send_file(chat, file) # sends as song - client.send_file(chat, file, voice_note=True) # sends as voice note - -The ``file`` returned by `client.upload_file -` represents the uploaded -file, not an immutable document (that's why the attributes can change, because -they are set later). This handle can be used only for a limited amount of time -(somewhere within a day). Telegram decides this limit and it is not public. -However, a day is often more than enough. - - -Sending Messages with Buttons -***************************** - -**You must sign in as a bot** in order to add inline buttons (or normal -keyboards) to your messages. Once you have signed in as a bot specify -the `Button ` or buttons to use: - -.. code-block:: python - - from telethon import events - from telethon.tl.custom import Button - - @client.on(events.CallbackQuery) - async def callback(event): - await event.edit('Thank you for clicking {}!'.format(event.data)) - - client.send_message(chat, 'A single button, with "clk1" as data', - buttons=Button.inline('Click me', b'clk1')) - - client.send_message(chat, 'Pick one from this grid', buttons=[ - [Button.inline('Left'), Button.inline('Right')], - [Button.url('Check this site!', 'https://lonamiwebs.github.io')] - ]) - -You can also use normal buttons (not inline) to request the user's -location, phone number, or simply for them to easily send a message: - -.. code-block:: python - - client.send_message(chat, 'Welcome', buttons=[ - Button.text('Thanks!', resize=True, single_use=True), - Button.request_phone('Send phone'), - Button.request_location('Send location') - ]) - -Forcing a reply or removing the keyboard can also be done: - -.. code-block:: python - - client.send_message(chat, 'Reply to me', buttons=Button.force_reply()) - client.send_message(chat, 'Bye Keyboard!', buttons=Button.clear()) - -Remember to check `Button ` for more. - -Making Inline Queries -********************* - -You can send messages ``via @bot`` by first making an inline query: - -.. code-block:: python - - results = client.inline_query('like', 'Do you like Telethon?') - -Then access the result you want and `click -` it in the chat -where you want to send it to: - -.. code-block:: python - - message = results[0].click('TelethonOffTopic') - -Sending messages through inline bots lets you use buttons as a normal user. - -It can look a bit strange at first, but you can make inline queries in no -chat in particular, and then click a *result* to send it to some chat. - -Clicking Buttons -**************** - -Let's `click ` -the message we sent in the example above! - -.. code-block:: python - - message.click(0) - -This will click the first button in the message. You could also -``click(row, column)``, using some text such as ``click(text='👍')`` -or even the data directly ``click(data=b'payload')``. - -Answering Inline Queries -************************ - -As a bot, you can answer to inline queries with `events.InlineQuery -`. You should make use of the -`builder ` property -to conveniently build the list of results to show to the user. Remember -to check the properties of the `InlineQuery.Event -`: - -.. code-block:: python - - @bot.on(events.InlineQuery) - async def handler(event): - builder = event.builder - - rev_text = event.text[::-1] - await event.answer([ - builder.article('Reverse text', text=rev_text), - builder.photo('/path/to/photo.jpg') - ]) - -Conversations: Waiting for Messages or Replies -********************************************** - -This one is really useful for unit testing your bots, which you can -even write within Telethon itself! You can open a `Conversation -` in any chat as: - -.. code-block:: python - - with client.conversation(chat) as conv: - ... - -Conversations let you program a finite state machine with the -higher-level constructs we are all used to, such as ``while`` -and ``if`` conditionals instead setting the state and jumping -from one place to another which is less clean. - -For instance, let's imagine ``you`` are the bot talking to ``usr``: - -.. code-block:: text - - Hi! - Hello! - Please tell me your name - ? - Your name didn't have any letters! Try again - Lonami - Thanks Lonami! - -This can be programmed as follows: - -.. code-block:: python - - with bot.conversation(chat) as conv: - conv.send_message('Hi!') - hello = conv.get_response() - - conv.send_message('Please tell me your name') - name = conv.get_response().raw_text - while not any(x.isalpha() for x in name): - conv.send_message("Your name didn't have any letters! Try again") - name = conv.get_response().raw_text - - conv.send_message('Thanks {}!'.format(name)) - -Note how we sent a message **with the conversation**, not with the client. -This is important so the conversation remembers what messages you sent. - -The method reference for getting a response, getting a reply or marking -the conversation as read can be found by clicking here: `Conversation -`. - -Sending a message or getting a response returns a `Message -`. Reading its documentation -will also be really useful! - -If a reply never arrives or too many messages come in, getting -responses will raise ``asyncio.TimeoutError`` or ``ValueError`` -respectively. You may want to ``except`` these and tell the user -they were too slow, or simply drop the conversation. - - -Forwarding Messages -******************* - -You can forward up to 100 messages with `forward_messages -`, -or a single one if you have the message with `forward_to -`: - -.. code-block:: python - - # a single one - client.forward_messages(chat, message) - # or - client.forward_messages(chat, message_id, from_chat) - # or - message.forward_to(chat) - - # multiple - client.forward_messages(chat, messages) - # or - client.forward_messages(chat, message_ids, from_chat) - -You can also "forward" messages without showing "Forwarded from" by -re-sending the message: - -.. code-block:: python - - client.send_message(chat, message) - - -Editing Messages -**************** - -With `edit_message ` -or `message.edit `: - -.. code-block:: python - - client.edit_message(message, 'New text') - # or - message.edit('New text') - # or - client.edit_message(chat, message_id, 'New text') - -Deleting Messages -***************** - -With `delete_messages ` -or `message.delete `. Note that the -first one supports deleting entire chats at once!: - -.. code-block:: python - - client.delete_messages(chat, messages) - # or - message.delete() - - -Marking Messages as Read -************************ - -Marking messages up to a certain point as read with `send_read_acknowledge -`: - -.. code-block:: python - - client.send_read_acknowledge(last_message) - # or - client.send_read_acknowledge(last_message_id) - # or - client.send_read_acknowledge(messages) - - -Getting Entities -**************** - -Entities are users, chats, or channels. You can get them by their ID if -you have seen them before (e.g. you probably need to get all dialogs or -all the members from a chat first): - -.. code-block:: python - - from telethon import utils - - me = client.get_entity('me') - print(utils.get_display_name(me)) - - chat = client.get_input_entity('username') - for message in client.iter_messages(chat): - ... - - # Note that you could have used the username directly, but it's - # good to use get_input_entity if you will reuse it a lot. - for message in client.iter_messages('username'): - ... - - # Note that for this to work the phone number must be in your contacts - some_id = client.get_peer_id('+34123456789') - -The documentation for shown methods are `get_entity -`, `get_input_entity -` and `get_peer_id -`. - -Note that the utils package also has a `get_peer_id -` but it won't work with things -that need access to the network such as usernames or phones, -which need to be in your contact list. - -Getting the Admin Log -********************* - -If you're an administrator in a channel or megagroup, then you have access -to the admin log. This is a list of events within the last 48 hours of -different actions, such as joining or leaving members, edited or deleted -messages, new promotions, bans or restrictions. - -You can iterate over all the available actions like so: - -.. code-block:: python - - for event in client.iter_admin_log(channel): - if event.changed_title: - print('The title changed from', event.old, 'to', event.new) - -You can also filter to only show some text or actions. -Let's find people who swear to ban them: - -.. code-block:: python - - # Get a list of deleted message events which said "heck" - events = client.get_admin_log(channel, search='heck', delete=True) - - # Print the old message before it was deleted - print(events[0].old) - -You can find here the documentation for `client.iter_admin_log -`, and be sure -to also check the properties of the returned `AdminLogEvent -` to know what -you can access. diff --git a/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst b/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst deleted file mode 100644 index 671306c4..00000000 --- a/readthedocs/extra/troubleshooting/deleted-limited-or-deactivated-accounts.rst +++ /dev/null @@ -1,27 +0,0 @@ -======================================== -Deleted, Limited or Deactivated Accounts -======================================== - -If you're from Iran or Russia, 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. - -We have also had reports from Kazakhstan and China, where connecting -would fail. To solve these connection problems, you should use a proxy. - -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 diff --git a/readthedocs/extra/troubleshooting/enable-logging.rst b/readthedocs/extra/troubleshooting/enable-logging.rst deleted file mode 100644 index 8d8747f4..00000000 --- a/readthedocs/extra/troubleshooting/enable-logging.rst +++ /dev/null @@ -1,40 +0,0 @@ -================ -Enabling Logging -================ - -Telethon makes use of the `logging`__ module, and you can enable it as follows: - -.. code:: python - - import logging - logging.basicConfig(level=logging.DEBUG) - -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 - - import logging - logger = logging.getLogger(__name__) - - logger.debug('Debug messages') - logger.info('Useful information') - logger.warning('This is a warning!') - - -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/index.rst b/readthedocs/index.rst index 097ce208..1d2847c4 100644 --- a/readthedocs/index.rst +++ b/readthedocs/index.rst @@ -1,29 +1,31 @@ -.. 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. +======================== +Telethon's Documentation +======================== -==================================== -Welcome to Telethon's documentation! -==================================== +.. code-block:: python + + from telethon.sync import TelegramClient, events + + with TelegramClient('name', api_id, api_hash) as client: + client.send_message('me', 'Hello, myself!') + print(client.download_profile_photo('me')) + + @client.on(events.NewMessage(pattern='(?i).*Hello')) + async def handler(event): + await event.reply('Hey!') + + client.run_until_disconnected() -Pure Python 3 Telegram client library. -Official Site `here `_. -Please follow the links on the index below to navigate from here, -or use the menu on the left. Remember to read the :ref:`changelog` -when you upgrade! - -.. important:: - - * Are you new here? Jump straight into :ref:`getting-started`! - * Looking for available friendly methods? See :ref:`ref-summary`. - * Used Telethon before v1.0? See :ref:`compatibility-and-convenience`. - * Need the full API reference? https://lonamiwebs.github.io/Telethon/. +* Are you new here? Jump straight into :ref:`installation`! +* Looking for the method reference? See :ref:`client-ref`. +* Did you upgrade the library? Please read :ref:`changelog`. +* Used Telethon before v1.0? See :ref:`compatibility-and-convenience`. +* Need the full API reference? https://lonamiwebs.github.io/Telethon/. What is this? -************* +------------- Telegram is a popular messaging application. This library is meant to make it easy for you to write Python programs that can interact @@ -31,93 +33,84 @@ with Telegram. Think of it as a wrapper that has already done the heavy job for you, so you can focus on developing an application. -.. _installation-and-usage: +How should I use the documentation? +----------------------------------- + +If you are getting started with the library, you should follow the +documentation in order by pressing the "Next" button at the bottom-right +of every page. + +You can also use the menu on the left to quickly skip over sections. .. toctree:: - :maxdepth: 2 - :caption: Installation and Simple Usage + :hidden: + :caption: First Steps - extra/basic/getting-started - extra/basic/installation - extra/basic/creating-a-client - extra/basic/telegram-client - extra/reference - extra/basic/entities - extra/basic/working-with-updates - extra/basic/compatibility-and-convenience - - -.. _Advanced-usage: + basic/installation + basic/signing-in + basic/quick-start + basic/updates + basic/next-steps .. toctree:: - :maxdepth: 2 - :caption: Advanced Usage + :hidden: + :caption: Quick References - extra/advanced-usage/accessing-the-full-api - extra/advanced-usage/sessions - extra/advanced-usage/update-modes - extra/advanced-usage/mastering-telethon - extra/advanced-usage/mastering-asyncio - - -.. _Examples: + quick-references/faq + quick-references/client-reference + quick-references/events-reference + quick-references/objects-reference .. toctree:: - :maxdepth: 2 - :caption: Examples + :hidden: + :caption: Concepts - extra/examples/telegram-client - extra/examples/working-with-messages - extra/examples/chats-and-channels - extra/examples/users - extra/examples/projects-using-telethon - - -.. _Troubleshooting: + concepts/strings + concepts/entities + concepts/updates + concepts/sessions + concepts/full-api + concepts/errors + concepts/asyncio .. toctree:: - :maxdepth: 2 - :caption: Troubleshooting + :hidden: + :caption: Full API Examples - extra/troubleshooting/enable-logging - extra/troubleshooting/deleted-limited-or-deactivated-accounts - extra/troubleshooting/rpc-errors - - -.. _Developing: + examples/word-of-warning + examples/chats-and-channels + examples/users + examples/working-with-messages + examples/projects-using-telethon .. toctree:: - :maxdepth: 2 - :caption: Developing + :hidden: + :caption: Developing - extra/developing/philosophy.rst - extra/developing/test-servers.rst - extra/developing/project-structure.rst - extra/developing/coding-style.rst - extra/developing/understanding-the-type-language.rst - extra/developing/tips-for-porting-the-project.rst - extra/developing/telegram-api-in-other-languages.rst - - -.. _More: + developing/philosophy.rst + developing/test-servers.rst + developing/project-structure.rst + developing/coding-style.rst + developing/understanding-the-type-language.rst + developing/tips-for-porting-the-project.rst + developing/telegram-api-in-other-languages.rst .. toctree:: - :maxdepth: 2 - :caption: More - - extra/changelog - extra/wall-of-shame.rst + :hidden: + :caption: Miscellaneous + misc/changelog + misc/wall-of-shame.rst + misc/compatibility-and-convenience .. toctree:: - :caption: Telethon modules + :hidden: + :caption: Telethon Modules - modules - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` + modules/client + modules/events + modules/custom + modules/utils + modules/errors + modules/sessions + modules/network diff --git a/readthedocs/extra/changelog.rst b/readthedocs/misc/changelog.rst similarity index 99% rename from readthedocs/extra/changelog.rst rename to readthedocs/misc/changelog.rst index 08f0395f..a2a649fb 100644 --- a/readthedocs/extra/changelog.rst +++ b/readthedocs/misc/changelog.rst @@ -867,7 +867,7 @@ Additions `. - New ``silent`` parameter when sending messages, usable in broadcast channels. - Documentation now has an entire section dedicate to how to use - the client's friendly methods at :ref:`telegram-client-example`. + the client's friendly methods at *(removed broken link)*. Bug fixes ~~~~~~~~~ @@ -924,7 +924,7 @@ Additions ~~~~~~~~~ - ``add_mark=`` is now back on ``utils.get_input_peer`` and also on - `client.get_input_entity `. + `client.get_input_entity() `. - New `client.get_peer_id ` convenience for ``utils.get_peer_id(await client.get_input_entity(peer))``. @@ -1776,7 +1776,7 @@ Of course there was more work to be done regarding updates, and it's here! The library comes with a new ``events`` module (which you will often import as ``from telethon import TelegramClient, events``). This are pretty much all the additions that come with this version change, but they are a nice -addition. Refer to :ref:`working-with-updates` to get started with events. +addition. Refer to *(removed broken link)* to get started with events. Trust the Server with Updates (v0.17) diff --git a/readthedocs/extra/basic/compatibility-and-convenience.rst b/readthedocs/misc/compatibility-and-convenience.rst similarity index 99% rename from readthedocs/extra/basic/compatibility-and-convenience.rst rename to readthedocs/misc/compatibility-and-convenience.rst index 86be8c1c..9d6e8257 100644 --- a/readthedocs/extra/basic/compatibility-and-convenience.rst +++ b/readthedocs/misc/compatibility-and-convenience.rst @@ -12,7 +12,7 @@ is there to tell you when these important changes happen. Compatibility -************* +============= Some decisions when developing will inevitable be proven wrong in the future. One of these decisions was using threads. Now that Python 3.4 is reaching EOL @@ -76,7 +76,7 @@ the chat or sender. If you don't use updates, you're done! Convenience -*********** +=========== .. note:: @@ -134,7 +134,7 @@ This keeps the best of both worlds as a sane default. them too. Otherwise, there is no need to do so with this mode. Speed -***** +===== When you're ready to micro-optimize your application, or if you simply don't need to call any non-basic methods from a synchronous context, @@ -176,7 +176,7 @@ So that you don't have to write it yourself every time. That's the overhead you pay if you import it, and what you save if you don't. Learning -******** +======== You know the library uses ``asyncio`` everywhere, and you want to learn how to do things right. Even though ``asyncio`` is its own topic, the diff --git a/readthedocs/extra/wall-of-shame.rst b/readthedocs/misc/wall-of-shame.rst similarity index 100% rename from readthedocs/extra/wall-of-shame.rst rename to readthedocs/misc/wall-of-shame.rst diff --git a/readthedocs/modules.rst b/readthedocs/modules.rst deleted file mode 100644 index f710574a..00000000 --- a/readthedocs/modules.rst +++ /dev/null @@ -1,7 +0,0 @@ -telethon -======== - -.. toctree:: - :maxdepth: 3 - - telethon diff --git a/readthedocs/telethon.client.rst b/readthedocs/modules/client.rst similarity index 76% rename from readthedocs/telethon.client.rst rename to readthedocs/modules/client.rst index c5d80d0b..6639200f 100644 --- a/readthedocs/telethon.client.rst +++ b/readthedocs/modules/client.rst @@ -1,12 +1,14 @@ .. _telethon-client: +============== +TelegramClient +============== -telethon\.client package -======================== +.. currentmodule:: telethon.client -The `telethon.TelegramClient` aggregates several mixin classes to provide -all the common functionality in a nice, Pythonic interface. Each mixin has -its own methods, which you all can use. +The `TelegramClient ` aggregates several mixin +classes to provide all the common functionality in a nice, Pythonic interface. +Each mixin has its own methods, which you all can use. **In short, to create a client you must run:** @@ -24,8 +26,15 @@ its own methods, which you all can use. You **don't** need to import these `AuthMethods`, `MessageMethods`, etc. -Together they are the `telethon.TelegramClient` and you can access all of -their methods. +Together they are the `TelegramClient ` and +you can access all of their methods. + +See :ref:`client-ref` for a short summary. + +.. automodule:: telethon.client.telegramclient + :members: + :undoc-members: + :show-inheritance: .. automodule:: telethon.client.telegrambaseclient :members: diff --git a/readthedocs/telethon.tl.custom.rst b/readthedocs/modules/custom.rst similarity index 55% rename from readthedocs/telethon.tl.custom.rst rename to readthedocs/modules/custom.rst index f005f209..1f1329bf 100644 --- a/readthedocs/telethon.tl.custom.rst +++ b/readthedocs/modules/custom.rst @@ -1,115 +1,145 @@ -telethon\.tl\.custom package -============================ +============== +Custom package +============== +The `telethon.tl.custom` package contains custom classes that the library +uses in order to make working with Telegram easier. Only those that you +are supposed to use will be documented here. You can use undocumented ones +at your own risk. -telethon\.tl\.custom\.draft module ----------------------------------- +More often than not, you don't need to import these (unless you want +type hinting), nor do you need to manually create instances of these +classes. They are returned by client methods. -.. automodule:: telethon.tl.custom.draft +.. contents:: + +.. automodule:: telethon.tl.custom :members: :undoc-members: :show-inheritance: -telethon\.tl\.custom\.dialog module ------------------------------------ -.. automodule:: telethon.tl.custom.dialog +AdminLogEvent +============= + +.. automodule:: telethon.tl.custom.adminlogevent :members: :undoc-members: :show-inheritance: -telethon\.tl\.custom\.file module ---------------------------------- -.. automodule:: telethon.tl.custom.file - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.message module ------------------------------------- - -.. automodule:: telethon.tl.custom.message - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.messagebutton module ------------------------------------------- - -.. automodule:: telethon.tl.custom.messagebutton - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.forward module ------------------------------------- - -.. automodule:: telethon.tl.custom.forward - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.button module ------------------------------------ +Button +====== .. automodule:: telethon.tl.custom.button :members: :undoc-members: :show-inheritance: -telethon\.tl\.custom\.inlinebuilder module ------------------------------------------- -.. automodule:: telethon.tl.custom.inlinebuilder - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.inlineresult module ------------------------------------------ - -.. automodule:: telethon.tl.custom.inlineresult - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.inlineresults module ------------------------------------------- - -.. automodule:: telethon.tl.custom.inlineresults - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.chatgetter module ---------------------------------------- +ChatGetter +========== .. automodule:: telethon.tl.custom.chatgetter :members: :undoc-members: :show-inheritance: -telethon\.tl\.custom\.sendergetter module ------------------------------------------ -.. automodule:: telethon.tl.custom.sendergetter - :members: - :undoc-members: - :show-inheritance: - -telethon\.tl\.custom\.conversation module ------------------------------------------ +Conversation +============ .. automodule:: telethon.tl.custom.conversation :members: :undoc-members: :show-inheritance: -telethon\.tl\.custom\.adminlogevent module ------------------------------------------- -.. automodule:: telethon.tl.custom.adminlogevent +Dialog +====== + +.. automodule:: telethon.tl.custom.dialog + :members: + :undoc-members: + :show-inheritance: + + +Draft +===== + +.. automodule:: telethon.tl.custom.draft + :members: + :undoc-members: + :show-inheritance: + + +File +==== + +.. automodule:: telethon.tl.custom.file + :members: + :undoc-members: + :show-inheritance: + + +Forward +======= + +.. automodule:: telethon.tl.custom.forward + :members: + :undoc-members: + :show-inheritance: + + +InlineBuilder +============= + +.. automodule:: telethon.tl.custom.inlinebuilder + :members: + :undoc-members: + :show-inheritance: + + +InlineResult +============ + +.. automodule:: telethon.tl.custom.inlineresult + :members: + :undoc-members: + :show-inheritance: + + +InlineResults +============= + +.. automodule:: telethon.tl.custom.inlineresults + :members: + :undoc-members: + :show-inheritance: + + +Message +======= + +.. automodule:: telethon.tl.custom.message + :members: + :undoc-members: + :show-inheritance: + + +MessageButton +============= + +.. automodule:: telethon.tl.custom.messagebutton + :members: + :undoc-members: + :show-inheritance: + + +SenderGetter +============ + +.. automodule:: telethon.tl.custom.sendergetter :members: :undoc-members: :show-inheritance: diff --git a/readthedocs/modules/errors.rst b/readthedocs/modules/errors.rst new file mode 100644 index 00000000..0df239c9 --- /dev/null +++ b/readthedocs/modules/errors.rst @@ -0,0 +1,19 @@ +.. _telethon-errors: + +========== +API Errors +========== + +These are the base errors that Telegram's API may raise. + +See :ref:`rpc-errors` for a more friendly explanation. + +.. automodule:: telethon.errors.common + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.errors.rpcbaseerrors + :members: + :undoc-members: + :show-inheritance: diff --git a/readthedocs/telethon.events.rst b/readthedocs/modules/events.rst similarity index 87% rename from readthedocs/telethon.events.rst rename to readthedocs/modules/events.rst index deb83e96..961885a0 100644 --- a/readthedocs/telethon.events.rst +++ b/readthedocs/modules/events.rst @@ -1,9 +1,12 @@ -.. _telethon-events-package: +.. _telethon-events: -telethon\.events package -======================== +============= +Update Events +============= -Every event (builder) subclasses `telethon.events.common.EventBuilder`, +.. currentmodule:: telethon.events + +Every event (builder) subclasses `common.EventBuilder`, so all the methods in it can be used from any event builder/event instance. .. automodule:: telethon.events.common @@ -11,62 +14,51 @@ so all the methods in it can be used from any event builder/event instance. :undoc-members: :show-inheritance: - .. automodule:: telethon.events.newmessage :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.chataction :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.userupdate :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.messageedited :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.messagedeleted :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.messageread :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.callbackquery :members: :undoc-members: :show-inheritance: - - .. automodule:: telethon.events.inlinequery :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events.raw :members: :undoc-members: :show-inheritance: - .. automodule:: telethon.events :members: :undoc-members: diff --git a/readthedocs/modules/helpers.rst b/readthedocs/modules/helpers.rst new file mode 100644 index 00000000..cffe53e5 --- /dev/null +++ b/readthedocs/modules/helpers.rst @@ -0,0 +1,8 @@ +======= +Helpers +======= + +.. automodule:: telethon.helpers + :members: + :undoc-members: + :show-inheritance: diff --git a/readthedocs/modules/network.rst b/readthedocs/modules/network.rst new file mode 100644 index 00000000..3395fa51 --- /dev/null +++ b/readthedocs/modules/network.rst @@ -0,0 +1,33 @@ +.. _telethon-network: + +================ +Connection Modes +================ + +The only part about network that you should worry about are +the different connection modes, which are the following: + +.. automodule:: telethon.network.connection.tcpfull + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.network.connection.tcpabridged + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.network.connection.tcpintermediate + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.network.connection.tcpobfuscated + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.network.connection.http + :members: + :undoc-members: + :show-inheritance: diff --git a/readthedocs/modules/sessions.rst b/readthedocs/modules/sessions.rst new file mode 100644 index 00000000..86ae22a4 --- /dev/null +++ b/readthedocs/modules/sessions.rst @@ -0,0 +1,27 @@ +.. _telethon-sessions: + +======== +Sessions +======== + +These are the different built-in session storage that you may subclass. + +.. automodule:: telethon.sessions.abstract + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.sessions.memory + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.sessions.sqlite + :members: + :undoc-members: + :show-inheritance: + +.. automodule:: telethon.sessions.string + :members: + :undoc-members: + :show-inheritance: diff --git a/readthedocs/modules/utils.rst b/readthedocs/modules/utils.rst new file mode 100644 index 00000000..2fab89a2 --- /dev/null +++ b/readthedocs/modules/utils.rst @@ -0,0 +1,12 @@ +.. _telethon-utils: + +========= +Utilities +========= + +These are the utilities that the library has to offer. + +.. automodule:: telethon.utils + :members: + :undoc-members: + :show-inheritance: diff --git a/readthedocs/quick-references/client-reference.rst b/readthedocs/quick-references/client-reference.rst new file mode 100644 index 00000000..8ecc70dc --- /dev/null +++ b/readthedocs/quick-references/client-reference.rst @@ -0,0 +1,188 @@ +.. _client-ref: + +================ +Client Reference +================ + +This page contains a summary of all the important methods and properties that +you may need when using Telethon. They are sorted by relevance and are not in +alphabetical order. + +You should use this page to learn about which methods are available, and +if you need an usage example or further description of the arguments, be +sure to follow the links. + +.. contents:: + +TelegramClient +============== + +This is a summary of the methods and +properties you will find at :ref:`telethon-client`. + +Auth +---- + +.. currentmodule:: telethon.client.auth.AuthMethods + +.. autosummary:: + :nosignatures: + + start + send_code_request + sign_in + sign_up + log_out + edit_2fa + +Base +---- + +.. py:currentmodule:: telethon.client.telegrambaseclient.TelegramBaseClient + +.. autosummary:: + :nosignatures: + + connect + disconnect + is_connected + disconnected + loop + +Messages +-------- + +.. py:currentmodule:: telethon.client.messages.MessageMethods + +.. autosummary:: + :nosignatures: + + send_message + edit_message + delete_messages + forward_messages + iter_messages + get_messages + send_read_acknowledge + +Uploads +------- + +.. py:currentmodule:: telethon.client.uploads.UploadMethods + +.. autosummary:: + :nosignatures: + + send_file + upload_file + +Downloads +--------- + +.. currentmodule:: telethon.client.downloads.DownloadMethods + +.. autosummary:: + :nosignatures: + + download_media + download_profile_photo + download_file + +Dialogs +------- + +.. py:currentmodule:: telethon.client.dialogs.DialogMethods + +.. autosummary:: + :nosignatures: + + iter_dialogs + get_dialogs + iter_drafts + get_drafts + conversation + +Users +----- + +.. py:currentmodule:: telethon.client.users.UserMethods + +.. autosummary:: + :nosignatures: + + get_me + is_bot + is_user_authorized + get_entity + get_input_entity + get_peer_id + +Chats +----- + +.. currentmodule:: telethon.client.chats.ChatMethods + +.. autosummary:: + :nosignatures: + + iter_participants + get_participants + iter_admin_log + get_admin_log + action + +Parse Mode +---------- + +.. py:currentmodule:: telethon.client.messageparse.MessageParseMethods + +.. autosummary:: + :nosignatures: + + parse_mode + +Updates +------- + +.. py:currentmodule:: telethon.client.updates.UpdateMethods + +.. autosummary:: + :nosignatures: + + on + run_until_disconnected + add_event_handler + remove_event_handler + list_event_handlers + catch_up + +Bots +---- + +.. currentmodule:: telethon.client.bots.BotMethods + +.. autosummary:: + :nosignatures: + + inline_query + +Buttons +------- + +.. currentmodule:: telethon.client.buttons.ButtonMethods + +.. autosummary:: + :nosignatures: + + build_reply_markup + +Account +------- + +.. currentmodule:: telethon.client.account.AccountMethods + +.. autosummary:: + :nosignatures: + + takeout + end_takeout diff --git a/readthedocs/quick-references/events-reference.rst b/readthedocs/quick-references/events-reference.rst new file mode 100644 index 00000000..89c4cee2 --- /dev/null +++ b/readthedocs/quick-references/events-reference.rst @@ -0,0 +1,203 @@ +================ +Events Reference +================ + +Here you will find a quick summary of all the methods +and properties that you can access when working with events. + +You can access the client that creates this event by doing +``event.client``, and you should view the description of the +events to find out what arguments it allows on creation and +its **attributes** (the properties will be shown here). + +It is important to remember that **all events subclass** +`ChatGetter `! + +.. contents:: + + +ChatGetter +========== + +All events subclass `ChatGetter `, +which means all events have (and you can access to): + +.. currentmodule:: telethon.tl.custom.chatgetter.ChatGetter + +.. autosummary:: + :nosignatures: + + chat + input_chat + chat_id + is_private + is_group + is_channel + + get_chat + get_input_chat + + +CallbackQuery +============= + +Full documentation for the `CallbackQuery +`. + +.. currentmodule:: telethon.events.callbackquery.CallbackQuery.Event + +.. autosummary:: + :nosignatures: + + id + message_id + data + chat_instance + via_inline + + respond + reply + edit + delete + answer + get_message + + +ChatAction +========== + +Full documentation for the `ChatAction +`. + +.. currentmodule:: telethon.events.chataction.ChatAction.Event + +.. autosummary:: + :nosignatures: + + added_by + kicked_by + user + input_user + user_id + users + input_users + user_ids + + respond + reply + delete + get_pinned_message + get_added_by + get_kicked_by + get_user + get_input_user + get_users + get_input_users + + +InlineQuery +=========== + +Full documentation for the `InlineQuery +`. + +.. currentmodule:: telethon.events.inlinequery.InlineQuery.Event + +.. autosummary:: + :nosignatures: + + id + text + offset + geo + builder + + answer + + +MessageDeleted +============== + +Full documentation for the `MessageDeleted +`. + +It only has the ``deleted_id`` and ``deleted_ids`` attributes +(in addition to the chat if the deletion happened in a channel). + + +MessageEdited +============= + +Full documentation for the `MessageEdited +`. + +This event is the same as `NewMessage +`, +but occurs only when an edit happens. + + +MessageRead +=========== + +Full documentation for the `MessageRead +`. + +.. currentmodule:: telethon.events.messageread.MessageRead.Event + +.. autosummary:: + :nosignatures: + + inbox + message_ids + + get_messages + is_read + + +NewMessage +========== + +Full documentation for the `NewMessage +`. + +Note that the new message event **should be treated as** a +normal `Message `, with +the following exceptions: + +* ``pattern_match`` is the match object returned by ``pattern=``. +* ``message`` is **not** the message string. It's the `Message + ` object. + +Remember, this event is just a proxy over the message, so while +you won't see its attributes and properties, you can still access +them. + + +Raw +=== + +Raw events are not actual events. Instead, they are the raw +:tl:`Update` object that Telegram sends. You normally shouldn't +need these. + + +UserUpdate +========== + +Full documentation for the `UserUpdate +`. + +A lot of fields are attributes and not properties, so they +are not shown here. + +.. currentmodule:: telethon.events.userupdate.UserUpdate.Event + +.. autosummary:: + :nosignatures: + + user + input_user + user_id + + get_user + get_input_user diff --git a/readthedocs/quick-references/faq.rst b/readthedocs/quick-references/faq.rst new file mode 100644 index 00000000..0363a873 --- /dev/null +++ b/readthedocs/quick-references/faq.rst @@ -0,0 +1,190 @@ +=== +FAQ +=== + +Let's start the quick references section with some useful tips to keep in +mind, with the hope that you will understand why certain things work the +way that they do. + +.. contents:: + + +Code without errors doesn't work +================================ + +Then it probably has errors, but you haven't enabled logging yet. +To enable logging, at the following code to the top of your main file: + +.. code-block:: python + + import logging + logging.basicConfig(format='[%(levelname) 5s/%(asctime)s] %(name)s: %(message)s', + level=logging.WARNING) + +You can change the logging level to be something different, from less to more information: + +.. code-block:: python + + level=logging.CRITICAL # won't show errors (same as disabled) + level=logging.ERROR # will only show errors that you didn't handle + level=logging.WARNING # will also show messages with medium severity, such as internal Telegram issues + level=logging.INFO # will also show informational messages, such as connection or disconnections + level=logging.DEBUG # will show a lot of output to help debugging issues in the library + +See the official Python documentation for more information on logging_. + + +How can I except FloodWaitError? +================================ + +You can use all errors from the API by importing: + +.. code-block:: python + + from telethon import errors + +And except them as such: + +.. code-block:: python + + import time + try: + client.send_message(chat, 'Hi') + except FloodWaitError as e: + print('Flood for', e.seconds) + time.sleep(e.seconds) + + +My account was deleted/limited when using the library +===================================================== + +The library will only do things that you tell it to do. If you use +the library with bad intentions, Telegram will hopefully ban you. + +However, you may also be part of a limited country, such as Iran or Russia. +In that case, 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. + +We have also had reports from Kazakhstan and China, where connecting +would fail. To solve these connection problems, you should use a proxy. + +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`_. + + +How can I use a proxy? +====================== + +This was one of the first things described in :ref:`signing-in`. + + +How do I access a field? +======================== + +This is basic Python knowledge. You should use the dot operator: + +.. code-block:: python + + me = client.get_me() + print(me.username) + # ^ we used the dot operator to access the username attribute + + result = client(functions.photos.GetUserPhotosRequest( + user_id='me', + offset=0, + max_id=0, + limit=100 + )) + + # Working with list is also pretty basic + print(result.photos[0].sizes[-1].type) + # ^ ^ ^ ^ ^ + # | | | | \ type + # | | | \ last size + # | | \ list of sizes + # access | \ first photo from the list + # the... \ list of photos + # + # To print all, you could do (or mix-and-match): + for photo in result.photos: + for size in photo.sizes: + print(size.type) + + +AttributeError: 'coroutine' object has no attribute 'id' +======================================================== + +You either forgot to: + +.. code-block:: python + + import telethon.sync + # ^^^^^ import sync + +Or: + +.. code-block:: python + + async def handler(event): + me = await client.get_me() + # ^^^^^ note the await + print(me.username) + + +sqlite3.OperationalError: database is locked +============================================ + +An older process is still running and is using the same ``'session'`` file. + +This error occurs when **two or more clients use the same session**, +that is, when you write the same session name to be used in the client: + +* You have an older process using the same session file. +* You have two different scripts running (interactive sessions count too). +* You have two clients in the same script running at the same time. + +The solution is, if you need two clients, use two sessions. If the +problem persists and you're on Linux, you can use ``fuser my.session`` +to find out the process locking the file. As a last resort, you can +reboot your system. + +If you really dislike SQLite, use a different session storage. There +is an entire section covering that at :ref:`sessions`. + + +event.chat or event.sender is None +================================== + +Telegram doesn't always send this information in order to save bandwidth. +If you need the information, you should fetch it yourself, since the library +won't do unnecessary work unless you need to: + +.. code-block:: python + + async def handler(event): + chat = await event.get_chat() + sender = await event.get_sender() + + +Can I use Flask with the library? +================================= + +Yes, if you know what you are doing. However, you will probably have a +lot of headaches to get threads and asyncio to work together. Instead, +consider using `Quart `_, an asyncio-based +alternative to `Flask `_. + +.. _logging: https://docs.python.org/3/library/logging.html +.. _@SpamBot: https://t.me/SpamBot +.. _issue 297: https://github.com/LonamiWebs/Telethon/issues/297 diff --git a/readthedocs/extra/reference.rst b/readthedocs/quick-references/objects-reference.rst similarity index 62% rename from readthedocs/extra/reference.rst rename to readthedocs/quick-references/objects-reference.rst index 34e093f8..2fed2253 100644 --- a/readthedocs/extra/reference.rst +++ b/readthedocs/quick-references/objects-reference.rst @@ -1,191 +1,16 @@ -.. _ref-summary: - ================= -Reference Summary +Objects Reference ================= -This page contains a summary of all the important methods and properties that -you may need when using Telethon. They are sorted by relevance and are not in -alphabetical order. +This is the quick reference for those objects returned by client methods +or other useful modules that the library has to offer. They are kept in +a separate page to help finding and discovering them. -The way you should use this page is by looking up the type you need in the -table of contents (method index below) and searching for the method or -property that you are interested in. +Remember that this page only shows properties and methods, +**not attributes**. Make sure to open the full documentation +to find out about the attributes. -.. contents:: Method Index - -TelegramClient -============== - -This is a summary of the methods and -properties you will find at :ref:`telethon-client`. - -Auth ----- - -.. currentmodule:: telethon.client.auth.AuthMethods - -.. autosummary:: - :nosignatures: - - start - send_code_request - sign_in - sign_up - log_out - edit_2fa - -Base ----- - -.. py:currentmodule:: telethon.client.telegrambaseclient.TelegramBaseClient - -.. autosummary:: - :nosignatures: - - connect - disconnect - is_connected - disconnected - loop - -Messages --------- - -.. py:currentmodule:: telethon.client.messages.MessageMethods - -.. autosummary:: - :nosignatures: - - send_message - edit_message - delete_messages - forward_messages - iter_messages - get_messages - send_read_acknowledge - -Uploads -------- - -.. py:currentmodule:: telethon.client.uploads.UploadMethods - -.. autosummary:: - :nosignatures: - - send_file - upload_file - -Downloads ---------- - -.. currentmodule:: telethon.client.downloads.DownloadMethods - -.. autosummary:: - :nosignatures: - - download_media - download_profile_photo - download_file - -Dialogs -------- - -.. py:currentmodule:: telethon.client.dialogs.DialogMethods - -.. autosummary:: - :nosignatures: - - iter_dialogs - get_dialogs - iter_drafts - get_drafts - conversation - -Users ------ - -.. py:currentmodule:: telethon.client.users.UserMethods - -.. autosummary:: - :nosignatures: - - get_me - is_bot - is_user_authorized - get_entity - get_input_entity - get_peer_id - -Chats ------ - -.. currentmodule:: telethon.client.chats.ChatMethods - -.. autosummary:: - :nosignatures: - - iter_participants - get_participants - iter_admin_log - get_admin_log - action - -Parse Mode ----------- - -.. py:currentmodule:: telethon.client.messageparse.MessageParseMethods - -.. autosummary:: - :nosignatures: - - parse_mode - -Updates -------- - -.. py:currentmodule:: telethon.client.updates.UpdateMethods - -.. autosummary:: - :nosignatures: - - on - run_until_disconnected - add_event_handler - remove_event_handler - list_event_handlers - catch_up - -Bots ----- - -.. currentmodule:: telethon.client.bots.BotMethods - -.. autosummary:: - :nosignatures: - - inline_query - -Buttons -------- - -.. currentmodule:: telethon.client.buttons.ButtonMethods - -.. autosummary:: - :nosignatures: - - build_reply_markup - -Account -------- - -.. currentmodule:: telethon.client.account.AccountMethods - -.. autosummary:: - :nosignatures: - - takeout - end_takeout +.. contents:: Message diff --git a/readthedocs/telethon.errors.rst b/readthedocs/telethon.errors.rst deleted file mode 100644 index f69e2967..00000000 --- a/readthedocs/telethon.errors.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. _telethon-errors-package: - - -telethon\.errors package -======================== - - -telethon\.errors\.common module -------------------------------- - -.. automodule:: telethon.errors.common - :members: - :undoc-members: - :show-inheritance: - -telethon\.errors\.rpcbaseerrors module --------------------------------------- - -.. automodule:: telethon.errors.rpcbaseerrors - :members: - :undoc-members: - :show-inheritance: diff --git a/readthedocs/telethon.extensions.rst b/readthedocs/telethon.extensions.rst deleted file mode 100644 index 83bb4d93..00000000 --- a/readthedocs/telethon.extensions.rst +++ /dev/null @@ -1,27 +0,0 @@ -telethon\.extensions package -============================ - - -telethon\.extensions\.binaryreader module ------------------------------------------ - -.. automodule:: telethon.extensions.binaryreader - :members: - :undoc-members: - :show-inheritance: - -telethon\.extensions\.markdown module -------------------------------------- - -.. automodule:: telethon.extensions.markdown - :members: - :undoc-members: - :show-inheritance: - -telethon\.extensions\.html module ---------------------------------- - -.. automodule:: telethon.extensions.html - :members: - :undoc-members: - :show-inheritance: diff --git a/readthedocs/telethon.network.rst b/readthedocs/telethon.network.rst deleted file mode 100644 index 79da891b..00000000 --- a/readthedocs/telethon.network.rst +++ /dev/null @@ -1,35 +0,0 @@ -telethon\.network package -========================= - - -telethon\.network\.connection module ------------------------------------- - -.. automodule:: telethon.network.connection - :members: - :undoc-members: - :show-inheritance: - -telethon\.network\.mtprotoplainsender module ------------------------------------------------- - -.. automodule:: telethon.network.mtprotoplainsender - :members: - :undoc-members: - :show-inheritance: - -telethon\.network\.mtprotosender module ------------------------------------------ - -.. automodule:: telethon.network.mtprotosender - :members: - :undoc-members: - :show-inheritance: - -telethon\.network\.authenticator module ---------------------------------------- - -.. automodule:: telethon.network.authenticator - :members: - :undoc-members: - :show-inheritance: diff --git a/readthedocs/telethon.rst b/readthedocs/telethon.rst deleted file mode 100644 index e91a3ee8..00000000 --- a/readthedocs/telethon.rst +++ /dev/null @@ -1,90 +0,0 @@ -.. _telethon-package: - - -telethon package -================ - - -telethon\.client module ------------------------ - -.. toctree:: - - telethon.client - -.. automodule:: telethon.client - :members: - :undoc-members: - :show-inheritance: - - -telethon\.utils module ----------------------- - -.. automodule:: telethon.utils - :members: - :undoc-members: - :show-inheritance: - - -telethon\.helpers module ------------------------- - -.. automodule:: telethon.helpers - :members: - :undoc-members: - :show-inheritance: - - -telethon\.events package ------------------------- - -.. toctree:: - - telethon.events - - -telethon\.sessions module -------------------------- - -.. automodule:: telethon.sessions - :members: - :undoc-members: - :show-inheritance: - -telethon\.errors package ------------------------- - -.. toctree:: - - telethon.errors - -telethon\.extensions package ----------------------------- - -.. toctree:: - - telethon.extensions - -telethon\.network package -------------------------- - -.. toctree:: - - telethon.network - -telethon\.tl package --------------------- - -.. toctree:: - - telethon.tl - - -Module contents ---------------- - -.. automodule:: telethon - :members: - :undoc-members: - :show-inheritance: diff --git a/readthedocs/telethon.tl.rst b/readthedocs/telethon.tl.rst deleted file mode 100644 index 7e8c1da1..00000000 --- a/readthedocs/telethon.tl.rst +++ /dev/null @@ -1,16 +0,0 @@ -telethon\.tl\.custom package -============================ - - -.. toctree:: - - telethon.tl.custom - - -telethon\.tl\.tlobject module ------------------------------ - -.. automodule:: telethon.tl.tlobject - :members: - :undoc-members: - :show-inheritance: diff --git a/telethon/client/account.py b/telethon/client/account.py index 3faa366c..884bfae3 100644 --- a/telethon/client/account.py +++ b/telethon/client/account.py @@ -120,7 +120,7 @@ class AccountMethods(UserMethods): files: bool = None, max_file_size: bool = None) -> 'TelegramClient': """ - Returns a :ref:`TelegramClient` which calls methods behind a takeout session. + Returns a :ref:`telethon-client` which calls methods behind a takeout session. It does so by creating a proxy object over the current client through which making requests will use :tl:`InvokeWithTakeoutRequest` to wrap @@ -190,6 +190,20 @@ class AccountMethods(UserMethods): max_file_size (`int`): The maximum file size, in bytes, that you plan to download for each message with media. + + Example: + + .. code-block:: python + + from telethon import errors + + try: + with client.takeout() as takeout: + for message in takeout.iter_messages(chat, wait_time=0): + ... # Do something with the message + + except errors.TakeoutInitDelayError as e: + print('Must wait', e.seconds, 'before takeout') """ request_kwargs = dict( contacts=contacts, diff --git a/telethon/client/auth.py b/telethon/client/auth.py index 6d70b059..9010c301 100644 --- a/telethon/client/auth.py +++ b/telethon/client/auth.py @@ -40,13 +40,6 @@ class AuthMethods(MessageParseMethods, UserMethods): will be banned otherwise.** See https://telegram.org/tos and https://core.telegram.org/api/terms. - Example usage: - >>> client = ... - >>> client.start(phone) - Please enter the code you received: 12345 - Please enter your password: ******* - (You are now logged in) - If the event loop is already running, this method returns a coroutine that you should await on your own code; otherwise the loop is ran until said coroutine completes. @@ -91,6 +84,24 @@ class AuthMethods(MessageParseMethods, UserMethods): Returns: This `TelegramClient`, so initialization can be chained with ``.start()``. + + Example: + .. code-block:: python + + client = TelegramClient('anon', api_id, api_hash) + + # Starting as a bot account + client.start(bot_token=bot_token) + + # Starting as an user account + client.start(phone) + # Please enter the code you received: 12345 + # Please enter your password: ******* + # (You are now logged in) + + # Starting using a context manager (this calls start()): + with client: + pass """ if code_callback is None: def code_callback(): @@ -452,6 +463,13 @@ class AuthMethods(MessageParseMethods, UserMethods): Returns: ``True`` if the operation was successful. + + Example: + + .. code-block:: python + + # Note: you will need to login again! + client.log_out() """ try: await self(functions.auth.LogOutRequest()) diff --git a/telethon/client/bots.py b/telethon/client/bots.py index 33af4c84..1fefecf0 100644 --- a/telethon/client/bots.py +++ b/telethon/client/bots.py @@ -39,6 +39,16 @@ class BotMethods(UserMethods): Returns: A list of `custom.InlineResult `. + + Example: + + .. code-block:: python + + # Make an inline query to @like + results = client.inline_query('like', 'Do you like Telethon?') + + # Send the first result to some chat + message = results[0].click('TelethonOffTopic') """ bot = await self.get_input_entity(bot) result = await self(functions.messages.GetInlineBotResultsRequest( diff --git a/telethon/client/chats.py b/telethon/client/chats.py index 23292525..273979f6 100644 --- a/telethon/client/chats.py +++ b/telethon/client/chats.py @@ -328,6 +328,23 @@ class ChatMethods(UserMethods): with an additional ``.participant`` attribute which is the matched :tl:`ChannelParticipant` type for channels/megagroups or :tl:`ChatParticipants` for normal chats. + + Example: + + .. code-block:: python + + # Show all user IDs in a chat + for user in client.iter_participants(chat): + print(user.id) + + # Search by name + for user in client.iter_participants(chat, search='name'): + print(user.username) + + # Filter by admins + from telethon.tl.types import ChannelParticipantsAdmins + for user in client.iter_participants(chat, filter=ChannelParticipantsAdmins): + print(user.first_name) """ return _ParticipantsIter( self, @@ -343,7 +360,7 @@ class ChatMethods(UserMethods): *args, **kwargs) -> 'hints.TotalList': """ - Same as `iter_participants`, but returns a + Same as `iter_participants()`, but returns a `TotalList ` instead. """ return await self.iter_participants(*args, **kwargs).collect() @@ -457,6 +474,20 @@ class ChatMethods(UserMethods): Yields: Instances of `telethon.tl.custom.adminlogevent.AdminLogEvent`. + + Example: + + .. code-block:: python + + for event in client.iter_admin_log(channel): + if event.changed_title: + print('The title changed from', event.old, 'to', event.new) + + # Get a list of deleted message events which said "heck" + events = client.get_admin_log(channel, search='heck', delete=True) + + # Print the old message before it was deleted + print(events[0].old) """ return _AdminLogIter( self, @@ -487,7 +518,7 @@ class ChatMethods(UserMethods): *args, **kwargs) -> 'hints.TotalList': """ - Same as `iter_admin_log`, but returns a ``list`` instead. + Same as `iter_admin_log()`, but returns a ``list`` instead. """ return await self.iter_admin_log(*args, **kwargs).collect() diff --git a/telethon/client/dialogs.py b/telethon/client/dialogs.py index a18aaddf..90c54bbc 100644 --- a/telethon/client/dialogs.py +++ b/telethon/client/dialogs.py @@ -138,6 +138,21 @@ class DialogMethods(UserMethods): Yields: Instances of `telethon.tl.custom.dialog.Dialog`. + + Example: + + .. code-block:: python + + # Get all open conversation, print the title of the first + dialogs = client.get_dialogs() + first = dialogs[0] + print(first.title) + + # Use the dialog somewhere else + client.send_message(first, 'hi') + + # Get drafts + drafts = client.get_drafts() """ return _DialogsIter( self, @@ -150,7 +165,7 @@ class DialogMethods(UserMethods): async def get_dialogs(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList': """ - Same as `iter_dialogs`, but returns a + Same as `iter_dialogs()`, but returns a `TotalList ` instead. """ return await self.iter_dialogs(*args, **kwargs).collect() @@ -169,7 +184,7 @@ class DialogMethods(UserMethods): async def get_drafts(self: 'TelegramClient') -> 'hints.TotalList': """ - Same as :meth:`iter_drafts`, but returns a list instead. + Same as `iter_drafts()`, but returns a list instead. """ return await self.iter_drafts().collect() @@ -257,6 +272,35 @@ class DialogMethods(UserMethods): Returns: A `Conversation `. + + Example: + + .. code-block:: python + + # denotes outgoing messages you sent + # denotes incoming response messages + with bot.conversation(chat) as conv: + # Hi! + conv.send_message('Hi!') + + # Hello! + hello = conv.get_response() + + # Please tell me your name + conv.send_message('Please tell me your name') + + # ? + name = conv.get_response().raw_text + + while not any(x.isalpha() for x in name): + # Your name didn't have any letters! Try again + conv.send_message("Your name didn't have any letters! Try again") + + # Lonami + name = conv.get_response().raw_text + + # Thanks Lonami! + conv.send_message('Thanks {}!'.format(name)) """ return custom.Conversation( self, diff --git a/telethon/client/downloads.py b/telethon/client/downloads.py index 39a3eed3..6b404bf1 100644 --- a/telethon/client/downloads.py +++ b/telethon/client/downloads.py @@ -56,6 +56,13 @@ class DownloadMethods(UserMethods): Returns: ``None`` if no photo was provided, or if it was Empty. On success the file path is returned since it may differ from the one given. + + Example: + + .. code-block:: python + + path = client.download_profile_photo('me') + print(path) """ # hex(crc32(x.encode('ascii'))) for x in # ('User', 'Chat', 'UserFull', 'ChatFull') @@ -173,6 +180,17 @@ class DownloadMethods(UserMethods): Returns: ``None`` if no media was provided, or if it was Empty. On success the file path is returned since it may differ from the one given. + + Example: + + .. code-block:: python + + path = client.download_media(message) + client.download_media(message, filename) + # or + path = message.download_media() + message.download_media(filename) + """ # TODO This won't work for messageService if isinstance(message, types.Message): diff --git a/telethon/client/messages.py b/telethon/client/messages.py index deb4db41..473d6ddb 100644 --- a/telethon/client/messages.py +++ b/telethon/client/messages.py @@ -413,6 +413,31 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): Telegram's flood wait limit for :tl:`GetHistoryRequest` seems to be around 30 seconds per 10 requests, therefore a sleep of 1 second is the default for this limit (or above). + + Example: + + .. code-block:: python + + # From most-recent to oldest + for message in client.iter_messages(chat): + print(message.id, message.text) + + # From oldest to most-recent + for message in client.iter_messages(chat, reverse=True): + print(message.id, message.text) + + # Filter by sender + for message in client.iter_messages(chat, from_user='me'): + print(message.text) + + # Server-side search with fuzzy text + for message in client.iter_messages(chat, search='hello'): + print(message.id) + + # Filter by message type: + from telethon.tl.types import InputMessagesFilterPhotos + for message in client.iter_messages(chat, filter=InputMessagesFilterPhotos): + print(message.photo) """ if ids is not None: @@ -436,7 +461,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): async def get_messages(self: 'TelegramClient', *args, **kwargs) -> 'hints.TotalList': """ - Same as `iter_messages`, but returns a + Same as `iter_messages()`, but returns a `TotalList ` instead. If the `limit` is not set, it will be 1 by default unless both @@ -450,6 +475,21 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): If `ids` is present in the *named* arguments and is not a list, a single `Message ` will be returned for convenience instead of a list. + + Example: + + .. code-block:: python + + # Get 0 photos and print the total to show how many photos there are + from telethon.tl.types import InputMessagesFilterPhotos + photos = client.get_messages(chat, 0, filter=InputMessagesFilterPhotos) + print(photos.total) + + # Get all the photos + photos = client.get_messages(chat, None, filter=InputMessagesFilterPhotos) + + # Get messages by ID: + message_1337 = client.get_messages(chats, ids=1337) """ if len(args) == 1 and 'limit' not in kwargs: if 'min_id' in kwargs and 'max_id' in kwargs: @@ -501,6 +541,7 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): the bot. Args: + entity (`entity`): To who will it be sent. @@ -556,7 +597,65 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): notify them. Set it to ``True`` to alter this behaviour. Returns: + The sent `custom.Message `. + + Example: + + .. code-block:: python + + client.send_message('lonami', 'Thanks for the Telethon library!') + + # Replies and responses + message = client.send_message('me', 'Trying out **markdown**') + message.reply('Trying replies') + message.respond('Trying responses') + + # Default to another parse mode + client.parse_mode = 'html' + + client.send_message('me', 'Some bold and italic text') + client.send_message('me', 'An URL') + client.send_message('me', 'code and
pre\nblocks
') + client.send_message('me', 'Mentions') + + # Explicit parse mode + # No parse mode by default + client.parse_mode = None + + # ...but here I want markdown + client.send_message('me', 'Hello, **world**!', parse_mode='md') + + # ...and here I need HTML + client.send_message('me', 'Hello, world!', parse_mode='html') + + # If you logged in as a bot account, you can send buttons + from telethon import events, Button + + @client.on(events.CallbackQuery) + async def callback(event): + await event.edit('Thank you for clicking {}!'.format(event.data)) + + # Single inline button + client.send_message(chat, 'A single button, with "clk1" as data', + buttons=Button.inline('Click me', b'clk1')) + + # Matrix of inline buttons + client.send_message(chat, 'Pick one from this grid', buttons=[ + [Button.inline('Left'), Button.inline('Right')], + [Button.url('Check this site!', 'https://lonamiwebs.github.io')] + ]) + + # Reply keyboard + client.send_message(chat, 'Welcome', buttons=[ + Button.text('Thanks!', resize=True, single_use=True), + Button.request_phone('Send phone'), + Button.request_location('Send location') + ]) + + # Forcing replies or clearing buttons. + client.send_message(chat, 'Reply to me', buttons=Button.force_reply()) + client.send_message(chat, 'Bye Keyboard!', buttons=Button.clear()) """ if file is not None: return await self.send_file( @@ -685,6 +784,25 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): Note that if all messages are invalid (i.e. deleted) the call will fail with ``MessageIdInvalidError``. If only some are invalid, the list will have ``None`` instead of those messages. + + Example: + + .. code-block:: python + + # a single one + client.forward_messages(chat, message) + # or + client.forward_messages(chat, message_id, from_chat) + # or + message.forward_to(chat) + + # multiple + client.forward_messages(chat, messages) + # or + client.forward_messages(chat, message_ids, from_chat) + + # Forwarding as a copy + client.send_message(chat, message) """ single = not utils.is_list_like(messages) if single: @@ -829,6 +947,16 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): The edited `telethon.tl.custom.message.Message`, unless `entity` was a :tl:`InputBotInlineMessageID` in which case this method returns a boolean. + + Example: + + .. code-block:: python + + client.edit_message(message, 'New text') + # or + message.edit('New text') + # or + client.edit_message(chat, message_id, 'New text') """ if isinstance(entity, types.InputBotInlineMessageID): text = message @@ -900,6 +1028,14 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): Returns: A list of :tl:`AffectedMessages`, each item being the result for the delete calls of the messages in chunks of 100 each. + + Example: + + .. code-block:: python + + client.delete_messages(chat, messages) + # or + message.delete() """ if not utils.is_list_like(message_ids): message_ids = (message_ids,) @@ -955,6 +1091,16 @@ class MessageMethods(UploadMethods, ButtonMethods, MessageParseMethods): If no message is provided, this will be the only action taken. + + Example: + + .. code-block:: python + + client.send_read_acknowledge(last_message) + # or + client.send_read_acknowledge(last_message_id) + # or + client.send_read_acknowledge(messages) """ if max_id is None: if not message: diff --git a/telethon/client/telegrambaseclient.py b/telethon/client/telegrambaseclient.py index 3f6d1422..85d6fd23 100644 --- a/telethon/client/telegrambaseclient.py +++ b/telethon/client/telegrambaseclient.py @@ -396,6 +396,13 @@ class TelegramBaseClient(abc.ABC): If the event loop is already running, this method returns a coroutine that you should await on your own code; otherwise the loop is ran until said coroutine completes. + + Example: + + .. code-block:: python + + # You don't need to use this if you used "with client" + client.disconnect() """ if self._loop.is_running(): return self._disconnect_coro() diff --git a/telethon/client/updates.py b/telethon/client/updates.py index 927fb411..01e1aca0 100644 --- a/telethon/client/updates.py +++ b/telethon/client/updates.py @@ -117,7 +117,7 @@ class UpdateMethods(UserMethods): callback: callable, event: EventBuilder = None) -> int: """ - Inverse operation of `add_event_handler`. + Inverse operation of `add_event_handler()`. If no event is given, all events for this callback are removed. Returns how many callbacks were removed. diff --git a/telethon/client/uploads.py b/telethon/client/uploads.py index b141767c..888cbfd6 100644 --- a/telethon/client/uploads.py +++ b/telethon/client/uploads.py @@ -232,6 +232,32 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods): Returns: The `telethon.tl.custom.message.Message` (or messages) containing the sent file, or messages if a list of them was passed. + + Example: + + .. code-block:: python + + # Normal files like photos + client.send_file(chat, '/my/photos/me.jpg', caption="It's me!") + # or + client.send_message(chat, "It's me!", file='/my/photos/me.jpg') + + # Voice notes or round videos + client.send_file(chat, '/my/songs/song.mp3', voice_note=True) + client.send_file(chat, '/my/videos/video.mp4', video_note=True) + + # Custom thumbnails + client.send_file(chat, '/my/documents/doc.txt', thumb='photo.jpg') + + # Only documents + client.send_file(chat, '/my/photos/photo.png', force_document=True) + + # Albums + client.send_file(chat, [ + '/my/photos/holiday1.jpg', + '/my/photos/holiday2.jpg', + '/my/drawings/portrait.png' + ]) """ # i.e. ``None`` was used if not file: @@ -427,6 +453,23 @@ class UploadMethods(ButtonMethods, MessageParseMethods, UserMethods): :tl:`InputFileBig` if the file size is larger than 10MB, `telethon.tl.custom.inputsizedfile.InputSizedFile` (subclass of :tl:`InputFile`) otherwise. + + Example: + + .. code-block:: python + + # Photos as photo and document + file = client.upload_file('photo.jpg') + client.send_file(chat, file) # sends as photo + client.send_file(chat, file, force_document=True) # sends as document + + file.name = 'not a photo.jpg' + client.send_file(chat, file, force_document=True) # document, new name + + # As song or as voice note + file = client.upload_file('song.ogg') + client.send_file(chat, file) # sends as song + client.send_file(chat, file, voice_note=True) # sends as voice note """ if isinstance(file, (types.InputFile, types.InputFileBig)): return file # Already uploaded diff --git a/telethon/client/users.py b/telethon/client/users.py index beba42b1..3ad3564c 100644 --- a/telethon/client/users.py +++ b/telethon/client/users.py @@ -191,6 +191,27 @@ class UserMethods(TelegramBaseClient): Returns: :tl:`User`, :tl:`Chat` or :tl:`Channel` corresponding to the input entity. A list will be returned if more than one was given. + + Example: + + .. code-block:: python + + from telethon import utils + + me = client.get_entity('me') + print(utils.get_display_name(me)) + + chat = client.get_input_entity('username') + for message in client.iter_messages(chat): + ... + + # Note that you could have used the username directly, but it's + # good to use get_input_entity if you will reuse it a lot. + for message in client.iter_messages('username'): + ... + + # Note that for this to work the phone number must be in your contacts + some_id = client.get_peer_id('+34123456789') """ single = not utils.is_list_like(entity) if single: diff --git a/telethon/events/__init__.py b/telethon/events/__init__.py index 25b2d4e9..0b1c16c6 100644 --- a/telethon/events/__init__.py +++ b/telethon/events/__init__.py @@ -42,7 +42,7 @@ class StopPropagation(Exception): def register(event=None): """ Decorator method to *register* event handlers. This is the client-less - `add_event_handler + `add_event_handler() ` variant. Note that this method only registers callbacks as handlers, diff --git a/telethon/events/inlinequery.py b/telethon/events/inlinequery.py index 4845295f..7946ecb5 100644 --- a/telethon/events/inlinequery.py +++ b/telethon/events/inlinequery.py @@ -178,6 +178,20 @@ class InlineQuery(EventBuilder): switch_pm_param (`str`, optional): Optional parameter to start the bot with if `switch_pm` was used. + + Example: + + .. code-block:: python + + @bot.on(events.InlineQuery) + async def handler(event): + builder = event.builder + + rev_text = event.text[::-1] + await event.answer([ + builder.article('Reverse text', text=rev_text), + builder.photo('/path/to/photo.jpg') + ]) """ if self._answered: return diff --git a/telethon/events/messageread.py b/telethon/events/messageread.py index fd8d73db..1b782db1 100644 --- a/telethon/events/messageread.py +++ b/telethon/events/messageread.py @@ -95,7 +95,7 @@ class MessageRead(EventBuilder): async def get_messages(self): """ - Returns the list of `telethon.tl.custom.message.Message` + Returns the list of `Message ` **which contents'** were read. Use :meth:`is_read` if you need to check whether a message diff --git a/telethon/events/userupdate.py b/telethon/events/userupdate.py index 63ebb4cf..0357f870 100644 --- a/telethon/events/userupdate.py +++ b/telethon/events/userupdate.py @@ -190,23 +190,23 @@ class UserUpdate(EventBuilder): @property def user(self): - """Alias for `sender`.""" + """Alias for `sender `.""" return self.sender async def get_user(self): - """Alias for `get_sender`.""" + """Alias for `get_sender `.""" return await self.get_sender() @property def input_user(self): - """Alias for `input_sender`.""" + """Alias for `input_sender `.""" return self.input_sender async def get_input_user(self): - """Alias for `get_input_sender`.""" + """Alias for `get_input_sender `.""" return await self.get_input_sender() @property def user_id(self): - """Alias for `sender_id`.""" + """Alias for `sender_id `.""" return self.sender_id diff --git a/telethon/tl/custom/inlinebuilder.py b/telethon/tl/custom/inlinebuilder.py index 5edb1438..2f4cc7d4 100644 --- a/telethon/tl/custom/inlinebuilder.py +++ b/telethon/tl/custom/inlinebuilder.py @@ -33,11 +33,11 @@ class InlineBuilder: May be ``True`` to indicate that the game will be sent. buttons (`list`, `custom.Button `, :tl:`KeyboardButton`, optional): - Same as ``buttons`` for `client.send_message + Same as ``buttons`` for `client.send_message() `. parse_mode (`str`, optional): - Same as ``parse_mode`` for `client.send_message + Same as ``parse_mode`` for `client.send_message() `. id (`str`, optional): @@ -119,7 +119,7 @@ class InlineBuilder: Args: file (`obj`, optional): - Same as ``file`` for `client.send_file + Same as ``file`` for `client.send_file() `. """ try: @@ -173,7 +173,7 @@ class InlineBuilder: Args: file (`obj`): - Same as ``file`` for `client.send_file + Same as ``file`` for `client.send_file() `. title (`str`, optional): diff --git a/telethon/tl/custom/message.py b/telethon/tl/custom/message.py index d32129d6..f5ce9ece 100644 --- a/telethon/tl/custom/message.py +++ b/telethon/tl/custom/message.py @@ -784,6 +784,22 @@ class Message(ChatGetter, SenderGetter, TLObject, abc.ABC): behave as if it clicked a button with said data. Note that if the message does not have this data, it will ``raise DataInvalidError``. + + Example: + + .. code-block:: python + + # Click the first button + message.click(0) + + # Click some row/column + message.click(row, column) + + # Click by text + message.click(text='👍') + + # Click by data + message.click(data=b'payload') """ if not self._client: return diff --git a/telethon/utils.py b/telethon/utils.py index 0a4f09bb..e8e71af5 100644 --- a/telethon/utils.py +++ b/telethon/utils.py @@ -937,11 +937,12 @@ def _encode_telegram_base64(string): def resolve_bot_file_id(file_id): """ - Given a Bot API-style `file_id`, returns the media it represents. - If the `file_id` is not valid, ``None`` is returned instead. + Given a Bot API-style `file_id `, + returns the media it represents. If the `file_id ` + is not valid, ``None`` is returned instead. - Note that the `file_id` does not have information such as image - dimensions or file size, so these will be zero if present. + Note that the `file_id ` does not have information + such as image dimensions or file size, so these will be zero if present. For thumbnails, the photo ID and hash will always be zero. """