<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 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)