Releases: david-lev/pywa
2.4.0
What's Changed
Update with pip:
pip3 install -U pywa
- [sent_message] adding
SentTemplate
withSentTemplateStatus
- [flows] adding
pattern
forTextInput
- [flows] adding support for
NavigationList
- [flows] defaulting action's payload to empty dict
- [flows] deprecating
ActionNext
andActionNextType
from pywa import WhatsApp
wa = WhatsApp(...)
sent_template_status = wa.send_template(...).status
from pywa.types.flows import *
FlowJSON(
version="6.2",
screens=[
Screen(
id="MENU",
title="Main Menu",
layout=Layout(
children=[
NavigationList(
name="menu",
list_items=[
NavigationItem(
id="1",
main_content=NavigationItemMainContent(
title="Contact Us",
description="Contact us now",
),
on_click_action=NavigateAction(next=Next(name="CONTACT")),
),
NavigationItem(
id="2",
main_content=NavigationItemMainContent(
title="About Us",
description="Learn more about us",
),
on_click_action=NavigateAction(next=Next(name="ABOUT")),
),
],
),
],
),
),
Screen(id="CONTACT", ...),
Screen(id="ABOUT", ...),
],
)
Full Changelog: 2.3.0...2.4.0
2.3.0
What's Changed
Update with pip:
pip3 install -U pywa
- [client] allowing to specify the callback url scope
- [client] expose methods to override callback url in waba and phone scopes
- [flows] typing
DataSource
to acceptRef
s
from pywa import WhatsApp, utils
import fastapi
fastapi_app = fastapi.FastAPI()
wa = WhatsApp(
phone_id=1234567890,
token="EAAEZC6hUxkTI...",
server=fastapi_app,
callback_url="https://example.com",
callback_url_scope=utils.CallbackURLScope.PHONE, # override phone callback url
verify_token="xyzxyz"
)
Full Changelog: 2.2.0...2.3.0
2.2.0
What's Changed
Update with pip:
pip3 install -U pywa
- [flows] adding
ScreenDataUpdate
to use inUpdateDataAction
- [flows] using math operators between math objs
- [flows] renaming
ActionNext
toNext
andActionNextType
toNextType
from pywa.types.flows import *
FlowJSON(
version="6.0",
screens=[
Screen(
id="DEMO_SCREEN",
data=[
is_txt_visible := ScreenData( # Declaring a data object
key="is_txt_visible",
example=False,
),
],
layout=Layout(
children=[
TextBody(
text="You checked the box!",
visible=is_txt_visible.ref, # Using the data
),
OptIn(
label="Show the text",
name="show_txt",
on_select_action=UpdateDataAction(
payload=[is_txt_visible.update(True)] # Updating the data
),
on_unselect_action=UpdateDataAction(
payload=[is_txt_visible.update(False)]
),
),
]
),
)
],
)
Full Changelog: 2.1.0...2.2.0
2.1.0
What's Changed
Update with pip:
pip3 install -U pywa
- [flows] adding
CalendarPicker
component - [flows] allow string concatenation with refs
- [flows] adding support for math expressions
- [flows] allowing to use condition in
visible
- [flows] new action: open url
- [flows] new action: update data
- [flows]
init_value
available outside form - [flows] allow to use
screen/ref
as shortcut for.ref_in(screen)
- [flows] separating
Action
and addingon_unselect_action
from pywa.types.flows import *
FlowJSON(
version="6.1",
screens=[
start := Screen(
id="START",
title="Start",
layout=Layout(
children=[
date_range := CalendarPicker( # new component
name="date_range",
label={
"start-date": "Select start date",
"end-date": "Select end date",
},
mode=CalendarPickerMode.RANGE,
),
..., # Footer with NavigateAction to `RESULT`
]
),
),
Screen(
id="RESULT",
title="Result",
layout=Layout(
children=[
name := TextInput(
name="name",
label="Enter your name",
input_type=InputType.TEXT,
),
birth_year := TextInput(
name="birth_year",
label="Enter birth year",
input_type=InputType.NUMBER,
init_value="1990", # `init_value` outside Form
),
TextBody(
# string concatenation with refs and math expressions
text=f"`'Hello, ' {name.ref} '. You are ' {2024 - birth_year.ref} ' years old.'`",
visible=birth_year.ref > 0, # condition in `visible`
),
OptIn(
name="rtd",
label="I read the docs",
on_click_action=OpenUrlAction( # Open URL action
url="https://pywa.readthedocs.io",
),
),
Footer(
label="Done",
on_click_action=CompleteAction( # `Action` is deprecated
payload={
"date_range": start/date_range.ref, # using screen/ref as shortcut
},
),
),
],
),
),
],
)
Full Changelog: 2.0.5...2.1.0
2.0.5
What's Changed
Update with pip:
pip3 install -U pywa
Note
This release is not compatible with 1.x versions. Please see the Migration Guide for instructions on updating from earlier versions.
- [client] fix
send_template
return typeSentMessage
Full Changelog: 2.0.4...2.0.5
2.0.4
What's Changed
Update with pip:
pip3 install -U pywa
Note
This release is not compatible with 1.x versions. Please see the Migration Guide for instructions on updating from earlier versions.
- [client] fix
reply_to_message
Full Changelog: 2.0.3...2.0.4
2.0.3
What's Changed
Update with pip:
pip3 install -U pywa
Note
This release is not compatible with 1.x versions. Please see the Migration Guide for instructions on updating from earlier versions.
- [client] override
_flow_req_cls
- [handlers] descriptive repr for
Handler
Full Changelog: 2.0.2...2.0.3
2.0.2
What's Changed
Update with pip:
pip3 install -U pywa
Important
Breaking Changes! Please see the Migration Guide for instructions on updating from earlier versions.
- [listeners]: Listeners are a new way to handle incoming user updates (messages, callbacks, etc.). They are more flexible, faster, and easier to use than handlers.
- [sent_message]: The
SentMessage
object returned bysend_message
,send_image
, etc., contains the message ID and allows to act on the sent message with methods likereply_x
,wait_for_x
etc. - [filters]: Filters are now objects that can be combined using logical operators. They are more powerful and flexible than the previous filter system.
- [handlers] allow to register callbacks without WhatsApp instance
- [flows] allow pythonic conditionals in
If
component - [flows]: A new method
FlowCompletion.get_media(types.Image, key="img")
allows you to construct a media object and perform actions like.download()
on it. - [flows]: Decrypt media directly from FlowRequest using
.decrypt_media(key, index)
. - [flows] rename
.data_key
and.from_ref
to.ref
- [client]: The client can run without a token but won’t allow API operations (only webhook listening).
- [server] rely on update hash instead of update id to avid duplicate updates
- [callback] allow to override callback data id
- [utils] bump graph-api version to
21.0
# my_handlers.py
from pywa import WhatsApp, filters, types, listeners
# You can register callback without th instance!
@WhatsApp.on_message(filters.text & filters.matches("store")) # combine filters with & operator
def store(_: WhatsApp, m: types.Message):
try:
age = m.reply("How old are you?", buttons=[types.Button("Cancel", "cancel")]).wait_for_reply( # wait for a reply
filters=filters.text & filters.new(lambda _, m: m.text.isdigit()), # create a new filter
cancelers=filters.callback_button & filters.matches("cancel"), # cancel the listener if the user clicks the cancel button
timeout=10, # set a timeout
)
m.reply(f"Your age is {age.text}")
except listeners.ListenerCanceled:
m.reply("You cancelled the store")
except listeners.ListenerTimeout:
m.reply("You took too long to reply")
# main.py
from pywa import WhatsApp
from . import my_handlers
wa = WhatsApp(..., handlers_modules=[my_handlers])
# run the server
from pywa.types.flows import *
FlowJSON(
version=pywa.Version.FLOW_JSON,
screens=[
Screen(
id="WELCOME",
title="Welcome",
data=[is_admin := ScreenData(key="is_admin", example=True)],
layout=Layout(
children=[
animal := TextInput(
name="animal",
label="Animal",
helper_text="Type: cat",
),
If(
condition=is_admin.ref & (animal.ref == "cat"), # we can use python operators
then=[TextHeading(text="It is a cat")],
else_=[TextHeading(text="It is not a cat")],
),
]
),
)
],
)
Full Changelog: 1.26.0...2.0.2
2.0.0-rc.2
What's Changed
Update with pip:
pip3 install -U pywa==2.0.0-rc.2
Caution
Make sure to read the migration guide before updating to this version!
Important
Please let us know in the Telegram group or the GitHub discussion if you encountered any problems during the migration
Note
Updated documentation to the new version is available here
[listeners]: Listeners are a new way to handle incoming user updates (messages, callbacks, etc.). They are more flexible, faster, and easier to use than handlers.
[filters]: Filters are now objects that can be combined using logical operators. They are more powerful and flexible than the previous filter system.
[handlers]: Now you can register handlers with decorators without the need to use the add_handlers
method.
[flows] allow pythonic conditionals in If
component.
[flows]: A new method FlowCompletion.get_media(types.Image, key="img")
allows you to construct a media object and perform actions like .download()
on it.
[flows]: Decrypt media directly from FlowRequest using .decrypt_media(key, index)
.
[client]: The client can run without a token but won’t allow API operations (only webhook listening).
[sent_message]: The SentMessage
object returned by send_message
, send_image
, etc., contains the message ID and allows to act on the sent message with methods like reply_x
, wait_for_x
etc.
[callback] allow to override callback data id
[docs] docs updates
# my_handlers.py
from pywa import WhatsApp, filters, types, listeners
# You can register callback without th instance!
@WhatsApp.on_message(filters.text & filters.matches("store")) # combine filters with & operator
def store(_: WhatsApp, m: types.Message):
try:
age = m.reply("How old are you?", buttons=[types.Button("Cancel", "cancel")]).wait_for_reply( # wait for a reply
filters=filters.text & filters.new(lambda _, m: m.text.isdigit()), # create a new filter
cancelers=filters.callback_button & filters.matches("cancel"), # cancel the listener if the user clicks the cancel button
timeout=10, # set a timeout
)
m.reply(f"Your age is {age.text}")
except listeners.ListenerCanceled:
m.reply("You cancelled the store")
except listeners.ListenerTimeout:
m.reply("You took too long to reply")
# main.py
from pywa import WhatsApp
from . import my_handlers
wa = WhatsApp(..., handlers_modules=[my_handlers])
# run the server
from pywa.types.flows import *
FlowJSON(
version=pywa.Version.FLOW_JSON,
screens=[
Screen(
id="WELCOME",
title="Welcome",
data=[is_admin := ScreenData(key="is_admin", example=True)],
layout=Layout(
children=[
animal := TextInput(
name="animal",
label="Animal",
helper_text="Type: cat",
),
If(
condition=is_admin.ref & (animal.ref == "cat"), # we can use python operators
then=[TextHeading(text="It is a cat")],
else_=[TextHeading(text="It is not a cat")],
),
]
),
)
],
)
Full Changelog: 1.26.0...2.0.0-rc.2
2.0.0-rc.1
What's Changed
Update with pip:
pip3 install -U pywa==2.0.0-rc.1
Caution
Make sure to read the migration guide before updating to this version!
Important
Please let us know in the Telegram group or the GitHub discussion if you encountered any problems during the migration
Note
Updated documentation to the new version is available here
[listeners]: Listeners are a new way to handle incoming user updates (messages, callbacks, etc.). They are more flexible, faster, and easier to use than handlers.
[filters]: Filters are now objects that can be combined using logical operators. They are more powerful and flexible than the previous filter system.
[handlers]: Now you can register handlers with decorators without the need to use the add_handlers
method.
[flows]: A new method FlowCompletion.get_media(types.Image, key="img")
allows you to construct a media object and perform actions like .download()
on it.
[flows]: Decrypt media directly from FlowRequest using .decrypt_media(key, index)
.
[client]: The client can run without a token but won’t allow API operations (only webhook listening).
[sent_message]: The SentMessage
object returned by send_message
, send_image
, etc., contains the message ID and allows to act on the sent message with methods like reply_x
, wait_for_x
etc.
[callback] allow to override callback data id
[docs] docs updates
# my_handlers.py
from pywa import WhatsApp, filters, types, listeners
# You can register callback without th instance!
@WhatsApp.on_message(filters.text & filters.matches("store")) # combine filters with & operator
def store(_: WhatsApp, m: types.Message):
try:
age = m.reply("How old are you?", buttons=[types.Button("Cancel", "cancel")]).wait_for_reply( # wait for a reply
filters=filters.text & filters.new(lambda _, m: m.text.isdigit()), # create a new filter
cancelers=filters.callback_button & filters.matches("cancel"), # cancel the listener if the user clicks the cancel button
timeout=10, # set a timeout
)
m.reply(f"Your age is {age.text}")
except listeners.ListenerCanceled:
m.reply("You cancelled the store")
except listeners.ListenerTimeout:
m.reply("You took too long to reply")
# main.py
from pywa import WhatsApp
from . import my_handlers
wa = WhatsApp(..., handlers_modules=[my_handlers])
# run the server
Full Changelog: 1.26.0...2.0.0-rc.1