File: updates.rst

package info (click to toggle)
python-telethon 1.40.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,508 kB
  • sloc: python: 16,125; javascript: 200; makefile: 16; sh: 11
file content (228 lines) | stat: -rw-r--r-- 7,423 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
================
Updates in Depth
================

Properties vs. Methods
======================

The event shown above acts just like a `custom.Message
<telethon.tl.custom.message.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 <telethon.client.messages.MessageMethods.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
<telethon.tl.custom.message.Message.sender>`) don't need an ``await``, but
methods (`message.get_sender
<telethon.tl.custom.message.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
<telethon.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
<telethon.client.updates.UpdateMethods.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
<telethon.client.updates.UpdateMethods.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
<telethon.client.updates.UpdateMethods.remove_event_handler>`
and `client.list_event_handlers
<telethon.client.updates.UpdateMethods.list_event_handlers>`.

The ``event`` argument is optional in all three methods and defaults to
`events.Raw <telethon.events.raw.Raw>` for adding, and `None` when
removing (so all callbacks would be removed).

.. note::

    The ``event`` type is ignored in `client.add_event_handler
    <telethon.client.updates.UpdateMethods.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
<telethon.client.updates.UpdateMethods.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
<telethon.client.telegrambaseclient.TelegramBaseClient.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

    asyncio.run(main())


You could also run `client.disconnected
<telethon.client.telegrambaseclient.TelegramBaseClient.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
<telethon.client.telegrambaseclient.TelegramBaseClient.disconnected>`,
`client.run_until_disconnected
<telethon.client.updates.UpdateMethods.run_until_disconnected>` will
handle ``KeyboardInterrupt`` for 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:
        ...