mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-10-30 15:37:30 +03:00 
			
		
		
		
	Create events.register and siblings for "handler templates"
This can be thought of as a different approach to Flask's blueprints.
This commit is contained in:
		
							parent
							
								
									cb6f980277
								
							
						
					
					
						commit
						d5d3733fd4
					
				|  | @ -216,7 +216,7 @@ Will show a much better: | ||||||
| Now it's easy to see how we could get, for example, | Now it's easy to see how we could get, for example, | ||||||
| the ``was_online`` time. It's inside ``status``: | the ``was_online`` time. It's inside ``status``: | ||||||
| 
 | 
 | ||||||
| .. code-block:: | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|     online_at = user.status.was_online |     online_at = user.status.was_online | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -181,7 +181,7 @@ random number, while if you say ``'eval 4+4'``, you will reply with the | ||||||
| solution. Try it! | solution. Try it! | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Properties vs. methods | Properties vs. Methods | ||||||
| ********************** | ********************** | ||||||
| 
 | 
 | ||||||
| The event shown above acts just like a `custom.Message | The event shown above acts just like a `custom.Message | ||||||
|  | @ -220,26 +220,91 @@ methods (`message.get_sender | ||||||
| and you should use methods in events for these properties that may need network. | and you should use methods in events for these properties that may need network. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Events without decorators | Events Without the client | ||||||
| ************************* | ************************* | ||||||
| 
 | 
 | ||||||
| If for any reason you can't use the `@client.on | The code of your application starts getting big, so you decide to | ||||||
| <telethon.client.updates.UpdateMethods.on>` syntax, don't worry. | separate the handlers into different files. But how can you access | ||||||
| You can call `client.add_event_handler(callback, event) | the client from these files? You don't need to! Just `events.register | ||||||
| <telethon.client.updates.UpdateMethods.add_event_handler>` to achieve | <telethon.events.register>` them: | ||||||
| the same effect. | 
 | ||||||
|  | .. 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 | Similarly, you also have `client.remove_event_handler | ||||||
| <telethon.client.updates.UpdateMethods.remove_event_handler>` | <telethon.client.updates.UpdateMethods.remove_event_handler>` | ||||||
| and `client.list_event_handlers | and `client.list_event_handlers | ||||||
| <telethon.client.updates.UpdateMethods.list_event_handlers>`. | <telethon.client.updates.UpdateMethods.list_event_handlers>`. | ||||||
| 
 | 
 | ||||||
| The ``event`` type is optional in all methods and defaults to | The ``event`` argument is optional in all three methods and defaults to | ||||||
| `events.Raw <telethon.events.raw.Raw>` for adding, and ``None`` when | `events.Raw <telethon.events.raw.Raw>` for adding, and ``None`` when | ||||||
| removing (so all callbacks would be removed). | removing (so all callbacks would be removed). | ||||||
| 
 | 
 | ||||||
|  | .. note:: | ||||||
| 
 | 
 | ||||||
| Stopping propagation of Updates |     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 | There might be cases when an event handler is supposed to be used solitary and | ||||||
|  |  | ||||||
|  | @ -51,6 +51,7 @@ The current winner is `issue | ||||||
| **Issue:** | **Issue:** | ||||||
| 
 | 
 | ||||||
| .. figure:: https://user-images.githubusercontent.com/6297805/29822978-9a9a6ef0-8ccd-11e7-9ec5-934ea0f57681.jpg | .. figure:: https://user-images.githubusercontent.com/6297805/29822978-9a9a6ef0-8ccd-11e7-9ec5-934ea0f57681.jpg | ||||||
|  | 
 | ||||||
| :alt: Winner issue | :alt: Winner issue | ||||||
| 
 | 
 | ||||||
| Winner issue | Winner issue | ||||||
|  | @ -58,6 +59,7 @@ Winner issue | ||||||
| **Answer:** | **Answer:** | ||||||
| 
 | 
 | ||||||
| .. figure:: https://user-images.githubusercontent.com/6297805/29822983-9d523402-8ccd-11e7-9fb1-5783740ee366.jpg | .. figure:: https://user-images.githubusercontent.com/6297805/29822983-9d523402-8ccd-11e7-9fb1-5783740ee366.jpg | ||||||
|  | 
 | ||||||
| :alt: Winner issue answer | :alt: Winner issue answer | ||||||
| 
 | 
 | ||||||
| Winner issue answer | Winner issue answer | ||||||
|  |  | ||||||
|  | @ -76,6 +76,10 @@ class UpdateMethods(UserMethods): | ||||||
|             callback (`callable`): |             callback (`callable`): | ||||||
|                 The callable function accepting one parameter to be used. |                 The callable function accepting one parameter to be used. | ||||||
| 
 | 
 | ||||||
|  |                 Note that if you have used `telethon.events.register` in | ||||||
|  |                 the callback, ``event`` will be ignored, and instead the | ||||||
|  |                 events you previously registered will be used. | ||||||
|  | 
 | ||||||
|             event (`_EventBuilder` | `type`, optional): |             event (`_EventBuilder` | `type`, optional): | ||||||
|                 The event builder class or instance to be used, |                 The event builder class or instance to be used, | ||||||
|                 for instance ``events.NewMessage``. |                 for instance ``events.NewMessage``. | ||||||
|  | @ -84,6 +88,12 @@ class UpdateMethods(UserMethods): | ||||||
|                 :tl:`Update` objects with no further processing) will |                 :tl:`Update` objects with no further processing) will | ||||||
|                 be passed instead. |                 be passed instead. | ||||||
|         """ |         """ | ||||||
|  |         builders = events._get_handlers(callback) | ||||||
|  |         if builders is not None: | ||||||
|  |             for event in builders: | ||||||
|  |                 self._event_builders.append((event, callback)) | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|         if isinstance(event, type): |         if isinstance(event, type): | ||||||
|             event = event() |             event = event() | ||||||
|         elif not event: |         elif not event: | ||||||
|  |  | ||||||
|  | @ -9,6 +9,9 @@ from .callbackquery import CallbackQuery | ||||||
| from .inlinequery import InlineQuery | from .inlinequery import InlineQuery | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | _HANDLERS_ATTRIBUTE = '__tl.handlers' | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class StopPropagation(Exception): | class StopPropagation(Exception): | ||||||
|     """ |     """ | ||||||
|     If this exception is raised in any of the handlers for a given event, |     If this exception is raised in any of the handlers for a given event, | ||||||
|  | @ -16,6 +19,7 @@ class StopPropagation(Exception): | ||||||
|     It can be seen as the ``StopIteration`` in a for loop but for events. |     It can be seen as the ``StopIteration`` in a for loop but for events. | ||||||
| 
 | 
 | ||||||
|     Example usage: |     Example usage: | ||||||
|  | 
 | ||||||
|         >>> from telethon import TelegramClient, events |         >>> from telethon import TelegramClient, events | ||||||
|         >>> client = TelegramClient(...) |         >>> client = TelegramClient(...) | ||||||
|         >>> |         >>> | ||||||
|  | @ -33,3 +37,103 @@ class StopPropagation(Exception): | ||||||
|     # For some reason Sphinx wants the silly >>> or |     # For some reason Sphinx wants the silly >>> or | ||||||
|     # it will show warnings and look bad when generated. |     # it will show warnings and look bad when generated. | ||||||
|     pass |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def register(event=None): | ||||||
|  |     """ | ||||||
|  |     Decorator method to *register* event handlers. This is the client-less | ||||||
|  |     `add_event_handler | ||||||
|  |     <telethon.client.updates.UpdateMethods.add_event_handler>` variant. | ||||||
|  | 
 | ||||||
|  |     Note that this method only registers callbacks as handlers, | ||||||
|  |     and does not attach them to any client. This is useful for | ||||||
|  |     external modules that don't have access to the client, but | ||||||
|  |     still want to define themselves as a handler. Example: | ||||||
|  | 
 | ||||||
|  |     >>> from telethon import events | ||||||
|  |     >>> @events.register(events.NewMessage) | ||||||
|  |     ... async def handler(event): | ||||||
|  |     ...     ... | ||||||
|  |     ... | ||||||
|  |     >>> # (somewhere else) | ||||||
|  |     ... | ||||||
|  |     >>> from telethon import TelegramClient | ||||||
|  |     >>> client = TelegramClient(...) | ||||||
|  |     >>> client.add_event_handler(handler) | ||||||
|  | 
 | ||||||
|  |     Remember that you can use this as a non-decorator | ||||||
|  |     through ``register(event)(callback)``. | ||||||
|  | 
 | ||||||
|  |     Args: | ||||||
|  |         event (`_EventBuilder` | `type`): | ||||||
|  |             The event builder class or instance to be used, | ||||||
|  |             for instance ``events.NewMessage``. | ||||||
|  |     """ | ||||||
|  |     if isinstance(event, type): | ||||||
|  |         event = event() | ||||||
|  |     elif not event: | ||||||
|  |         event = Raw() | ||||||
|  | 
 | ||||||
|  |     def decorator(callback): | ||||||
|  |         handlers = getattr(callback, _HANDLERS_ATTRIBUTE, []) | ||||||
|  |         handlers.append(event) | ||||||
|  |         setattr(callback, _HANDLERS_ATTRIBUTE, handlers) | ||||||
|  |         return callback | ||||||
|  | 
 | ||||||
|  |     return decorator | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def unregister(callback, event=None): | ||||||
|  |     """ | ||||||
|  |     Inverse operation of `register` (though not a decorator). Client-less | ||||||
|  |     `remove_event_handler | ||||||
|  |     <telethon.client.updates.UpdateMethods.remove_event_handler>` | ||||||
|  |     variant. **Note that this won't remove handlers from the client**, | ||||||
|  |     because it simply can't, so you would generally use this before | ||||||
|  |     adding the handlers to the client. | ||||||
|  | 
 | ||||||
|  |     This method is here for symmetry. You will rarely need to | ||||||
|  |     unregister events, since you can simply just not add them | ||||||
|  |     to any client. | ||||||
|  | 
 | ||||||
|  |     If no event is given, all events for this callback are removed. | ||||||
|  |     Returns how many callbacks were removed. | ||||||
|  |     """ | ||||||
|  |     found = 0 | ||||||
|  |     if event and not isinstance(event, type): | ||||||
|  |         event = type(event) | ||||||
|  | 
 | ||||||
|  |     handlers = getattr(callback, _HANDLERS_ATTRIBUTE, []) | ||||||
|  |     handlers.append((event, callback)) | ||||||
|  |     i = len(handlers) | ||||||
|  |     while i: | ||||||
|  |         i -= 1 | ||||||
|  |         ev = handlers[i] | ||||||
|  |         if not event or isinstance(ev, event): | ||||||
|  |             del handlers[i] | ||||||
|  |             found += 1 | ||||||
|  | 
 | ||||||
|  |     return found | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def is_handler(callback): | ||||||
|  |     """ | ||||||
|  |     Returns ``True`` if the given callback is an | ||||||
|  |     event handler (i.e. you used `register` on it). | ||||||
|  |     """ | ||||||
|  |     return hasattr(callback, _HANDLERS_ATTRIBUTE) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def list(callback): | ||||||
|  |     """ | ||||||
|  |     Returns a list containing the registered event | ||||||
|  |     builders inside the specified callback handler. | ||||||
|  |     """ | ||||||
|  |     return getattr(callback, _HANDLERS_ATTRIBUTE, [])[:] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def _get_handlers(callback): | ||||||
|  |     """ | ||||||
|  |     Like ``list`` but returns ``None`` if the callback was never registered. | ||||||
|  |     """ | ||||||
|  |     return getattr(callback, _HANDLERS_ATTRIBUTE, None) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user