π₯ Listeners#
When handling updates, most of the time you ask the user for input (e.g. a reply, text, button press, etc.). This is where listeners come in. With listeners, you can create an inline handler that waits for a specific user input and returns the result.
Letβs see an example of how to use a listener:
from pywa import WhatsApp, types, filters
wa = WhatsApp(...)
@wa.on_message(filters.command("start"))
def start(client: WhatsApp, msg: types.Message):
msg.reply("Hello! How old are you?")
# Now we want to wait for the user to send their age
age = client.listen(
to=msg.sender,
filters=filters.text
)
msg.reply(f"Your age is {age.text}.")
In the example above, we are waiting for the user to send their age. The client.listen method will wait for the user to send a message that matches the filter filters.text.
Now, listeners have a few options that you can use to customize the behavior. for example, you can set a timeout for the listener, or cancel the listener if the user sends a specific message. letβs see an example:
from pywa import WhatsApp, types, filters
wa = WhatsApp(...)
@wa.on_message(filters.command("start"))
def start(client: WhatsApp, msg: types.Message):
msg.reply(
text="Hello! How old are you?",
buttons=[types.Button("Cancel", callback_data="cancel")]
)
age = client.listen(
to=msg.sender,
filters=filters.text,
timeout=20, # 20 seconds
# If the user presses "cancel" the listener will be canceled
cancelers=filters.callback_button & filters.matches("cancel")
)
msg.reply(f"Your age is {age.text}.")
In the example above, we added a button to the message that the user can press to cancel the listener. The listener will be canceled if the user sends a message that matches the filter filters.callback_button & filters.matches(βcancelβ).
Now - we actually need to know what happend with the listener. The client.listen uses exceptions to notify you about the listener status. Letβs see an example:
from pywa import WhatsApp, types, filters, listeners
wa = WhatsApp(...)
@wa.on_message(filters.command("start"))
def start(client: WhatsApp, msg: types.Message):
msg.reply("Hello! Please send me your age.", buttons=[types.Button("Cancel", callback_data="cancel")])
try:
age = client.listen(
to=msg.sender,
filters=filters.text,
cancelers=filters.callback_button & filters.matches("cancel"),
timeout=20
)
msg.reply(f"Your age is {age.text}.")
except ListenerTimeout:
msg.reply("You took too long to send your age.")
except ListenerCanceled:
msg.reply("You canceled the operation.")
In the example above, we added a try-except block to handle the listener exceptions. If the listener times out, the ListenerTimeout exception will be raised. If the listener is canceled, the ListenerCanceled exception will be raised.
PyWa also provides a few shortcuts to create listeners when sending messages. Letβs see an example:
from pywa import WhatsApp, types, filters
wa = WhatsApp(...)
@wa.on_message(filters.command("start"))
def start(client: WhatsApp, msg: types.Message):
age = m.reply("Hello! How old are you?").wait_for_reply(filters.text)
m.reply(f"You are {age.text} years old")
In the example above, we used the .wait_for_reply method to create a listener that waits for a reply from the user.
Other shortcuts are available, such as wait_for_click, wait_for_selection, wait_to_read, wait_to_delivered and more.
- SentMessage.wait_for_reply(force_quote: bool = False, filters: Filter = None, cancelers: Filter = None, timeout: int | None = None) Message#
Wait for a message reply to the sent message.
Example
@wa.on_message(filters.text.command("start")) def start(w: WhatsApp, m: Message): user_id: str = m.reply( text=f"Hi {m.from_user.name}! Please enter your ID", buttons=[Button(title="Cancel", callback_data="cancel")], ).wait_for_reply( filters=filters.text, cancelers=filters.callback_button & filters.matches("cancel"), ).text ...
- Parameters:
force_quote β Whether to force the reply to quote the sent message.
filters β The filters to apply to the reply.
cancelers β The filters to cancel the listening.
timeout β The time to wait for a reply.
- Returns:
The reply message.
- Raises:
ListenerTimeout β If the listener timed out.
ListenerCanceled β If the listener was canceled by a filter.
ListenerStopped β If the listener was stopped manually.
- SentMessage.wait_for_click(force_options: bool = True, filters: Filter = None, cancelers: Filter = None, timeout: int | None = None) CallbackButton#
Wait for a button click.
Example
@wa.on_message(filters.text.command("start")) def start(w: WhatsApp, m: Message): r = m.reply( text="Click a button", buttons=[ Button(title="Option 1", callback_data="option1"), Button(title="Option 2", callback_data="option2"), ], ) option = r.wait_for_click() r.reply(f"You clicked: {option.title}", quote=True)
- Parameters:
force_options β Whether to force the button to be one of the sent buttons.
filters β The filters to apply to the button click.
cancelers β The filters to cancel the listening.
timeout β The time to wait for the button click.
- Returns:
The clicked button.
- Raises:
ListenerTimeout β If the listener timed out.
ListenerCanceled β If the listener was canceled by a filter.
ListenerStopped β If the listener was stopped manually.
- SentMessage.wait_for_selection(force_options: bool = True, filters: Filter = None, cancelers: Filter = None, timeout: int | None = None) CallbackSelection#
Wait for a callback selection.
- Parameters:
force_options β Whether to force the selection to be one of the sent rows.
filters β The filters to apply to the selection.
cancelers β The filters to cancel the listening.
timeout β The time to wait for the selection.
- Returns:
The callback selection.
- Raises:
ListenerTimeout β If the listener timed out.
ListenerCanceled β If the listener was canceled by a filter.
ListenerStopped β If the listener was stopped manually.
- SentMessage.wait_to_read(cancel_on_new_update: bool = False, cancelers: Filter = None, timeout: int | None = None) MessageStatus#
Wait for the message to be read by the recipient.
Note: This method will not work if the recipient has disabled read receipts. make sure to use
cancel_on_update=Trueto cancel the listening if the message is probably read, or use a timeout / cancelers.Example
@wa.on_message(filters.text.command("start")) def start(w: WhatsApp, m: Message): r = m.reply("This message waits for you to read it") try: r.wait_to_read(cancel_on_new_update=True) except ListenerCanceled: r.reply("You turned off read receipts") r.reply("You read this message", quote=True)
- Parameters:
cancel_on_new_update β Whether to cancel when another user update arrives (which may indicate the previous message was read).
cancelers β The filters to cancel the listening.
timeout β The time to wait for the message to be read.
- Returns:
The message status.
- Raises:
ListenerTimeout β If the listener timed out.
ListenerCanceled β If the listener was canceled by a filter.
ListenerStopped β If the listener was stopped manually.
- SentMessage.wait_to_delivered(cancelers: Filter = None, timeout: int | None = None) MessageStatus#
Wait for the message to be delivered to the recipient.
Example
@wa.on_message(filters.text.command("start")) def start(w: WhatsApp, m: Message): r = m.reply("This message waits for you to receive it") r.wait_to_delivered() r.reply("You received the message", quote=True)
- Parameters:
cancelers β The filters to cancel the listening.
timeout β The time to wait for the message to be delivered.
- Returns:
The message status.
- Raises:
ListenerTimeout β If the listener timed out.
ListenerCanceled β If the listener was canceled by a filter.
ListenerStopped β If the listener was stopped manually.