Skip to content

Releases: david-lev/pywa

2.4.0

14 Dec 20:28
Compare
Choose a tag to compare

What's Changed

Update with pip: pip3 install -U pywa

  • [sent_message] adding SentTemplate with SentTemplateStatus
  • [flows] adding pattern for TextInput
  • [flows] adding support for NavigationList
  • [flows] defaulting action's payload to empty dict
  • [flows] deprecating ActionNext and ActionNextType
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

30 Nov 23:14
Compare
Choose a tag to compare

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 accept Refs
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

28 Nov 22:06
Compare
Choose a tag to compare

What's Changed

Update with pip: pip3 install -U pywa

  • [flows] adding ScreenDataUpdate to use in UpdateDataAction
  • [flows] using math operators between math objs
  • [flows] renaming ActionNext to Next and ActionNextType to NextType
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

25 Nov 21:28
92270c2
Compare
Choose a tag to compare

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 adding on_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

10 Nov 09:04
Compare
Choose a tag to compare

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 type SentMessage

Full Changelog: 2.0.4...2.0.5

2.0.4

07 Nov 22:43
Compare
Choose a tag to compare

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

02 Nov 16:27
Compare
Choose a tag to compare

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

30 Oct 15:13
Compare
Choose a tag to compare

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 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.
  • [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

29 Oct 18:45
Compare
Choose a tag to compare
2.0.0-rc.2 Pre-release
Pre-release

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

27 Oct 14:22
Compare
Choose a tag to compare
2.0.0-rc.1 Pre-release
Pre-release

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