mirror of
				https://github.com/LonamiWebs/Telethon.git
				synced 2025-10-31 07:57:38 +03:00 
			
		
		
		
	Add new action method to easily send chat actions like typing
This commit is contained in:
		
							parent
							
								
									fadc343821
								
							
						
					
					
						commit
						badefcec48
					
				|  | @ -1,3 +1,4 @@ | |||
| import asyncio | ||||
| import itertools | ||||
| import string | ||||
| 
 | ||||
|  | @ -10,6 +11,93 @@ _MAX_PARTICIPANTS_CHUNK_SIZE = 200 | |||
| _MAX_ADMIN_LOG_CHUNK_SIZE = 100 | ||||
| 
 | ||||
| 
 | ||||
| class _ChatAction: | ||||
|     _str_mapping = { | ||||
|         'typing': types.SendMessageTypingAction(), | ||||
|         'contact': types.SendMessageChooseContactAction(), | ||||
|         'game': types.SendMessageGamePlayAction(), | ||||
|         'location': types.SendMessageGeoLocationAction(), | ||||
| 
 | ||||
|         'record-audio': types.SendMessageRecordAudioAction(), | ||||
|         'record-voice': types.SendMessageRecordAudioAction(),  # alias | ||||
|         'record-round': types.SendMessageRecordRoundAction(), | ||||
|         'record-video': types.SendMessageRecordVideoAction(), | ||||
| 
 | ||||
|         'audio': types.SendMessageUploadAudioAction(1), | ||||
|         'voice': types.SendMessageUploadAudioAction(1),  # alias | ||||
|         'round': types.SendMessageUploadRoundAction(1), | ||||
|         'video': types.SendMessageUploadVideoAction(1), | ||||
| 
 | ||||
|         'photo': types.SendMessageUploadPhotoAction(1), | ||||
|         'document': types.SendMessageUploadDocumentAction(1), | ||||
|         'file': types.SendMessageUploadDocumentAction(1),  # alias | ||||
|         'song': types.SendMessageUploadDocumentAction(1),  # alias | ||||
| 
 | ||||
|         'cancel': types.SendMessageCancelAction() | ||||
|     } | ||||
| 
 | ||||
|     def __init__(self, client, chat, action, *, delay, auto_cancel): | ||||
|         self._client = client | ||||
|         self._chat = chat | ||||
|         self._action = action | ||||
|         self._delay = delay | ||||
|         self._auto_cancel = auto_cancel | ||||
|         self._request = None | ||||
|         self._task = None | ||||
|         self._running = False | ||||
| 
 | ||||
|     async def __aenter__(self): | ||||
|         self._chat = await self._client.get_input_entity(self._chat) | ||||
| 
 | ||||
|         # Since `self._action` is passed by reference we can avoid | ||||
|         # recreating the request all the time and still modify | ||||
|         # `self._action.progress` directly in `progress`. | ||||
|         self._request = functions.messages.SetTypingRequest( | ||||
|             self._chat, self._action) | ||||
| 
 | ||||
|         self._running = True | ||||
|         self._task = self._client.loop.create_task(self._update()) | ||||
| 
 | ||||
|     async def __aexit__(self, *args): | ||||
|         self._running = False | ||||
|         if self._task: | ||||
|             self._task.cancel() | ||||
|             try: | ||||
|                 await self._task | ||||
|             except asyncio.CancelledError: | ||||
|                 pass | ||||
| 
 | ||||
|             self._task = None | ||||
| 
 | ||||
|     def __enter__(self): | ||||
|         if self._client.loop.is_running(): | ||||
|             raise RuntimeError( | ||||
|                 'You must use "async with" if the event loop ' | ||||
|                 'is running (i.e. you are inside an "async def")' | ||||
|             ) | ||||
| 
 | ||||
|         return self._client.loop.run_until_complete(self.__aenter__()) | ||||
| 
 | ||||
|     def __exit__(self, *args): | ||||
|         return self._client.loop.run_until_complete(self.__aexit__(*args)) | ||||
| 
 | ||||
|     async def _update(self): | ||||
|         try: | ||||
|             while self._running: | ||||
|                 await self._client(self._request) | ||||
|                 await asyncio.sleep(self._delay) | ||||
|         except ConnectionError: | ||||
|             pass | ||||
|         except asyncio.CancelledError: | ||||
|             if self._auto_cancel: | ||||
|                 await self._client(functions.messages.SetTypingRequest( | ||||
|                     self._chat, types.SendMessageCancelAction())) | ||||
| 
 | ||||
|     def progress(self, current, total): | ||||
|         if hasattr(self._action, 'progress'): | ||||
|             self._action.progress = 100 * round(current / total) | ||||
| 
 | ||||
| 
 | ||||
| class _ParticipantsIter(RequestIter): | ||||
|     async def _init(self, entity, filter, search, aggressive): | ||||
|         if isinstance(filter, type): | ||||
|  | @ -380,4 +468,88 @@ class ChatMethods(UserMethods): | |||
|         """ | ||||
|         return await self.iter_admin_log(*args, **kwargs).collect() | ||||
| 
 | ||||
|     def action(self, entity, action, *, delay=4, auto_cancel=True): | ||||
|         """ | ||||
|         Returns a context-manager object to represent a "chat action". | ||||
| 
 | ||||
|         Chat actions indicate things like "user is typing", "user is | ||||
|         uploading a photo", etc. Normal usage is as follows: | ||||
| 
 | ||||
|         .. code-block:: python | ||||
| 
 | ||||
|             async with client.action(chat, 'typing'): | ||||
|                 await asyncio.sleep(2)  # type for 2 seconds | ||||
|                 await client.send_message(chat, 'Hello world! I type slow ^^') | ||||
| 
 | ||||
|         If the action is ``'cancel'``, you should just ``await`` the result, | ||||
|         since it makes no sense to use a context-manager for it: | ||||
| 
 | ||||
|         .. code-block:: python | ||||
| 
 | ||||
|             await client.action(chat, 'cancel') | ||||
| 
 | ||||
|         Args: | ||||
|             entity (`entity`): | ||||
|                 The entity where the action should be showed in. | ||||
| 
 | ||||
|             action (`str` | :tl:`SendMessageAction`): | ||||
|                 The action to show. You can either pass a instance of | ||||
|                 :tl:`SendMessageAction` or better, a string used while: | ||||
| 
 | ||||
|                 * ``'typing'``: typing a text message. | ||||
|                 * ``'contact'``: choosing a contact. | ||||
|                 * ``'game'``: playing a game. | ||||
|                 * ``'location'``: choosing a geo location. | ||||
|                 * ``'record-audio'``: recording a voice note. | ||||
|                   You may use ``'record-voice'`` as alias. | ||||
|                 * ``'record-round'``: recording a round video. | ||||
|                 * ``'record-video'``: recording a normal video. | ||||
|                 * ``'audio'``: sending an audio file (voice note or song). | ||||
|                   You may use ``'voice'`` and ``'song'`` as aliases. | ||||
|                 * ``'round'``: uploading a round video. | ||||
|                 * ``'video'``: uploading a video file. | ||||
|                 * ``'photo'``: uploading a photo. | ||||
|                 * ``'document'``: uploading a document file. | ||||
|                   You may use ``'file'`` as alias. | ||||
|                 * ``'cancel'``: cancel any pending action in this chat. | ||||
| 
 | ||||
|                 Invalid strings will raise a ``ValueError``. | ||||
| 
 | ||||
|             delay (`int` | `float`): | ||||
|                 The delay, in seconds, to wait between sending actions. | ||||
|                 For example, if the delay is 5 and it takes 7 seconds to | ||||
|                 do something, three requests will be made at 0s, 5s, and | ||||
|                 7s to cancel the action. | ||||
| 
 | ||||
|             auto_cancel (`bool`): | ||||
|                 Whether the action should be cancelled once the context | ||||
|                 manager exists or not. The default is ``True``, since | ||||
|                 you don't want progress to be shown when it has already | ||||
|                 completed. | ||||
| 
 | ||||
|         If you are uploading a file, you may do | ||||
|         ``progress_callback=chat.progress`` to update the progress of | ||||
|         the action. Some clients don't care about this progress, though, | ||||
|         so it's mostly not needed, but still available. | ||||
|         """ | ||||
|         if isinstance(action, str): | ||||
|             try: | ||||
|                 action = _ChatAction._str_mapping[action.lower()] | ||||
|             except KeyError: | ||||
|                 raise ValueError('No such action "{}"'.format(action)) from None | ||||
|         elif not isinstance(action, types.TLObject) or action.SUBCLASS_OF_ID != 0x20b2cc21: | ||||
|             # 0x20b2cc21 = crc32(b'SendMessageAction') | ||||
|             if isinstance(action, type): | ||||
|                 raise ValueError('You must pass an instance, not the class') | ||||
|             else: | ||||
|                 raise ValueError('Cannot use {} as action'.format(action)) | ||||
| 
 | ||||
|         if isinstance(action, types.SendMessageCancelAction): | ||||
|             # ``SetTypingRequest.resolve`` will get input peer of ``entity``. | ||||
|             return self(functions.messages.SetTypingRequest( | ||||
|                 entity, types.SendMessageCancelAction())) | ||||
| 
 | ||||
|         return _ChatAction( | ||||
|             self, entity, action, delay=delay, auto_cancel=auto_cancel) | ||||
| 
 | ||||
|     # endregion | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user