mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-10-30 23:47:33 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			344 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			344 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. _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
 | |
| <https://t.me/LonamiWebs>`_ 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
 | |
|    <telethon.tl.custom.chatgetter.ChatGetter.input_chat>`,
 | |
|    `message.input_sender
 | |
|    <telethon.tl.custom.sendergetter.SenderGetter.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()
 | |
|    <telethon.client.users.UserMethods.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 <telethon.tl.custom.message.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(...)
 | |
| <telethon.client.users.UserMethods.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 <telethon.tl.custom.chatgetter.ChatGetter.chat>`
 | |
|     or `sender <telethon.tl.custom.sendergetter.SenderGetter.sender>`
 | |
|     may not work, but `input_chat
 | |
|     <telethon.tl.custom.chatgetter.ChatGetter.input_chat>`
 | |
|     and `input_sender
 | |
|     <telethon.tl.custom.sendergetter.SenderGetter.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
 | |
| <https://docs.python.org/3/library/functions.html#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 <telethon.tl.custom.chatgetter.ChatGetter>`
 | |
| and `SenderGetter <telethon.tl.custom.sendergetter.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 <telethon.tl.custom.chatgetter.ChatGetter>`
 | |
| knows how to get the *chat* where a thing belongs to.
 | |
| 
 | |
| So, a `Message <telethon.tl.custom.message.Message>` is a
 | |
| `ChatGetter <telethon.tl.custom.chatgetter.ChatGetter>`.
 | |
| That means you can do this:
 | |
| 
 | |
| .. code-block:: python
 | |
| 
 | |
|     message.is_private
 | |
|     message.chat_id
 | |
|     message.get_chat()
 | |
|     # ...etc
 | |
| 
 | |
| `SenderGetter <telethon.tl.custom.sendergetter.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
 | |
| <telethon.tl.custom.chatgetter.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!
 |