Releases: david-lev/pywa
Release list
3.5.0
What's Changed
Update with pip:
pip3 install -U pywa
- [client] adding
send_voiceandwait_until_playedlistener - [listeners] adding
wait_for_locationwhen sending location request - [sent] adding
SentMediaMessagewithuploaded_mediaattr for sent messages - [message] adding
unsupportedattr - [message] adding support for copy
voicemessage - [base_update] add
identity_key_hashparameter to message methods for identity verification - [templates] adding
text_extraction_for_headlineandtext_extraction_for_tap_targettoCreativeFeaturesSpec - [flows] fixing
indexinFlowCompletion.get_media
from pywa import WhatsApp, types
wa = WhatsApp(...)
@wa.on_message(filters=filters.command("start"))
def on_start_message(_: WhatsApp, m: types.Message):
# Send a welcome voice message
sent = m.reply_voice(
voice=text_to_speech("Welcome to our service! How can we assist you today?")
)
sent.wait_until_played() # Wait until the voice message is played
print(sent.uploaded_media) # Access uploaded media
location = m.reply_location_request(
"Please share your location to find nearby services."
).wait_for_location().location # Wait for user to share location
print(f"User shared location: {location.latitude}, {location.longitude}")
@wa.on_message(filters=filters.unsupported)
def on_unsupported_message(_: WhatsApp, m: types.Message):
m.reply_text(
# Now you can access the unsupported message type
f"Sorry, we cannot process messages of type '{m.unsupported.type}' yet."
)Full Changelog: 3.4.0...3.5.0
3.4.0
What's Changed
Update with pip:
pip3 install -U pywa
- [templates] adding support for
HeaderGIF - [templates] override mimetype when sending template with media
- [helpers] fix closing file
Full Changelog: 3.3.0...3.4.0
3.3.0
What's Changed
Update with pip:
pip3 install -U pywa
- [templates] allowing to override header example mime-type
- [helpers] stream template examples when possible (sync/async)
- [helpers] fix template example resolving for
Mediatype
Full Changelog: 3.2.0...3.3.0
3.2.0
What's Changed
Update with pip:
pip3 install -U pywa
- [media] adding
uploaded_by,uploaded_atand more helpers toMediatype - [client] implementing Identity Change Check
- [client] adding
get_media_bytesandstream_media - [client] adding support for voice messages
- [client] updating
update_business_phone_number_settingsmethod to take specific settings - [base_update] adding method to handle the update again
- [message] fix typing of
buttonsforcopyand remove'copy'system message -
- [templates] adding
image_background_genoption toCreativeFeaturesSpec
- [templates] adding
- [sent_update] moving the
inputfrom user to theSentMessagelevel - [helpers] stream downloads and uploads
- [utils] bump
GRAPH_APIversion to24.0
from pywa import WhatsApp, types, filters
wa = WhatsApp(...)
@wa.on_message(filters.image)
def on_image(_: WhatsApp, m: types.Message):
if not m.image.is_expired:
save_to_db(m.image.id, m.image.expires_at)
# Access image metadata
print(m.image.days_until_expiration)
print(m.image.uploaded_at, m.image.uploaded_by, m.image.uploaded_to)
# Reupload the image to extend its expiration
new_media = m.image.reupload()
# Stream the media to your storage
with httpx.Client() as client:
client.post(url="https://your-storage-service/upload", content=m.image.stream())
# We also stream uploads!
wa.upload_media("https://your-storage-service/download/12345")from pywa import WhatsApp, types, filters, errors
wa = WhatsApp(...)
# Enable Identity Key Check
wa.update_business_phone_number_settings(
user_identity_change=types.UserIdentityChangeSettings(
enable_identity_key_check=True
)
)
# Save identity_key_hash to database for future reference
@wa.on_message(my_filter_for_new_user)
def on_new_user(_: WhatsApp, m: types.Message):
identity_key_hash = m.from_user.identity_key_hash
# Use the saved identity_key_hash from database to ensure message deliver
wa.send_message(
to=...,
text=...,
identity_key_hash=...,
)
# Monitor for identity changes
@wa.on_identity_change
def on_identity_change(_: WhatsApp, identity_change: types.IdentityChange):
print(identity_change)
# Monitor for identity key mismatches
@wa.on_message_status(filters.failed_with(errors.RecipientIdentityKeyMismatch))
def on_identity_key_mismatch(_: WhatsApp, status: types.MessageStatus):
status.reply(
text="We noticed that your WhatsApp identity has changed. Please verify your identity to continue receiving messages from us."
)from pywa import WhatsApp
wa.send_audio(
to=...,
audio=...,
is_voice=True, # Send as voice note
)Full Changelog: 3.1.1...3.2.0
3.1.1
What's Changed
Update with pip:
pip3 install -U pywa
- [server] Fix
filter updates
Full Changelog: 3.1.0...3.1.1
3.1.0
What's Changed
Update with pip:
pip3 install -U pywa
- [types] adding
RawUpdatetype - [flows] update
enabledandrequiredfields to useConditiontype - [flows] added support for
DataExchangeActioninDataSource(on_select_actionandon_unselect_action) - [flows] update
FLOW_JSONversion to7.3 - [client] refactoring media resolving
- [templates] adding
filenamewhen sending document - [media] allow to re-upload media to another phone id
from pywa import WhatsApp, types
wa = WhatsApp(...)
@wa.on_raw_update
def on_raw(_: WhatsApp, raw: types.RawUpdate):
print(raw["entry"]) # the raw dict
print(raw.id, raw.field, raw.value) # shortcut properties
print(raw.raw) # the raw bytes of the updatflow = FlowJSON(
screens=[
Screen(
layout=Layout(
children=[
TextHeading(text="Welcome to Pywa!"),
TextSubheading(text="Hi there, I am using Pywa."),
display_txt := TextInput(
name="display_txt", label="Type 'something'"
),
TextBody(
text="You typed 'something'!",
visible=display_txt.ref == "something",
), # Example of conditional visibility
]
)
)
]
)Full Changelog: 3.0.0...3.1.0
3.0.0
PyWa v3.0.0 is here!
Update with pip:
pip3 install -U pywa
This is one of the biggest updates to PyWa ever, bringing calls, user preferences, a fully redesigned template system, new update types, and more.
Important
Migration to v3 introduces breaking changes. Please review carefully before upgrading.
✨ Highlights
- Templates: completely redesigned — more flexible, reusable, and powerful.
- Calls: full support for making, receiving, and managing calls.
- User Preferences: listen for marketing opt-in/out events.
- System Updates:
PhoneNumberChangeandIdentityChangenow first-class updates. - Listeners: can now handle all update types, not just user messages.
- Client Improvements:
- New methods:
delete_media,update_display_name,get_business_account, and more. upload_medianow returns aMediaobject.send_*methods enforce keyword-only context arguments.
- New methods:
- Types:
SuccessResultreplacesboolfor extensibility. - Security: XSS vulnerability fixed.
- Docs: new PyWa logo! 🚀 (by @nyatitilkesh)
🔥 Breaking changes
- Old template system removed → use the new API.
systemmessages removed fromMessage→ usePhoneNumberChange/IdentityChange.upload_mediareturnsMediainstead of a string.send_*methods require keyword-only context args.- Methods like
mark_as_read,indicate_typingnow returnSuccessResult. FlowRequestDecryptedMediareplaces tuple return.
See full details and migration steps in the migration guide.
❤️ Thanks
- A huge thanks to my wife for always providing endless support and patience.
- Special thanks to my brother @yehuda-lev for his help, code reviews, testing, and for supporting users in the community groups 🙌.
📚 Resources
👏 New Contributors
💡 Examples
- Create and send template messages
See Templates for more details and examples.
from pywa import WhatsApp, types
from pywa.types.templates import *
wa = WhatsApp(..., business_account_id=123456)
# Create a template
wa.create_template(
template=Template(
name="buy_new_iphone_x",
category=TemplateCategory.MARKETING,
language=TemplateLanguage.ENGLISH_US,
parameter_format=ParamFormat.NAMED,
components=[
ht := HeaderText("The New iPhone {{iphone_num}} is here!", iphone_num=15),
bt := BodyText("Buy now and use the code {{code}} to get {{per}}% off!", code="WA_IPHONE_15", per=15),
FooterText(text="Powered by PyWa"),
Buttons(
buttons=[
url := URLButton(text="Buy Now", url="https://example.com/shop/{{1}}", example="iphone15"),
PhoneNumberButton(text="Call Us", phone_number="1234567890"),
qrb1 := QuickReplyButton(text="Unsubscribe from marketing messages"),
qrb2 := QuickReplyButton(text="Unsubscribe from all messages"),
]
),
]
),
)
# Send the template message
wa.send_template(
to="9876543210",
name="buy_new_iphone_x",
language=TemplateLanguage.ENGLISH_US,
params=[
ht.params(iphone_num=30),
bt.params(code="WA_IPHONE_30", per=30),
url.params(url_variable="iphone30", index=0),
qrb1.params(callback_data="unsubscribe_from_marketing_messages", index=1),
qrb2.params(callback_data="unsubscribe_from_all_messages", index=2),
]
)
# Manage templates
templates = wa.get_templates(statuses=[TemplateStatus.APPROVED])
print(templates.total_count, templates.message_template_limit)
for template in templates:
print(template)
template.update(...)
template.delete()
template.duplicate(...)
template.compare(...)
template.send(to=...)
# Handle changes
@wa.on_template_status_update
def on_template_status_update(_: WhatsApp, status: types.TemplateStatusUpdate):
print("Template status update:", status.template_name, status.template_language, status.new_status)
@wa.on_template_category_update
def on_template_category_update(_: WhatsApp, category: types.TemplateCategoryUpdate):
print("Template category update:", category.template_name, category.previous_category, category.new_category)
@wa.on_template_quality_update
def on_template_quality_update(_: WhatsApp, quality: types.TemplateQualityUpdate):
print("Template quality update:", quality.template_name, quality.previous_quality_score, quality.new_quality_score)
@wa.on_template_components_update
def on_template_components_update(_: WhatsApp, components: types.TemplateComponentsUpdate):
print("Template components update:", components.template_name, components.template_title, components.template_element)
# Inline listeners
wa.create_template(template=Template(...)).wait_until_approved()- Listen to phone number change and identity change
from pywa import WhatsApp, types
wa = WhatsApp(...)
@wa.on_phone_number_change
def on_phone_number_change(_: WhatsApp, update: types.PhoneNumberChange):
repository.update_phone_number(old=update.old_wa_id, new=update.new_wa_id) # update user wa_id in your db
@wa.on_identity_change
def on_identity_change(_: WhatsApp, update: types.IdentityChange):
repository.log_out_user(wa_id=update.sender) # secure the user account- Calling
from pywa import WhatsApp, types, filters
@wa.on_call_connect(filters.incoming_call)
def on_incoming_call(_: WhatsApp, call: types.CallConnect):
print(f"Incoming call from {call.from_user.name}: {call.session}")
call.pre_accept(...)
call.accept(...)
call.terminate(...)
call.reject(...)
@wa.on_call_terminate
def on_call_terminate(_: WhatsApp, call: types.CallTerminate):
print("Call terminated:", call.duration, call.start_time, call.end_time, call.status)
@wa.on_call_status
def on_call_status(_: WhatsApp, call: types.CallStatus):
print("Call status update:", call.status)
@wa.on_call_permission_update
def on_call_permission_update(_: WhatsApp, perm: types.CallPermissionUpdate):
print("Call permission update:", perm.response, perm.response_source, perm.expiration_timestamp)
if not perm.is_expired:
perm.call(...)- User marketing preferences
from pywa import WhatsApp, types
@wa.on_user_marketing_preferences
def on_user_marketing_preferences(_: WhatsApp, prefs: types.UserMarketingPreferences):
print(prefs.detail)
if prefs:
repository.opt_in(prefs.sender)
else:
repository.opt_out(prefs.sender)Full Changelog: 2.11.0...3.0.0
3.0.0-rc.3
What's Changed
Update with pip:
pip3 install -U pywa==3.0.0-rc.3
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
- [templates]
paramsis now can be called on class level - [templates] adding support for
library_inputwhen creating library templates - [templates] adding support for
degrees_of_freedom_specwhen creating template - [listeners] handling old
toparameter inlistenand update migration guide
Full Changelog: 3.0.0-rc.2...3.0.0-rc.3
3.0.0-rc.2
What's Changed
Update with pip:
pip3 install -U pywa==3.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
- [client] allowing to use mm-lite-api when sending a template
- [templates] allowing to set app-depplinks in
URLButton's - [templates] adding
TopBlockReasonTypeenum - [client] adding
get_business_accountmethod - [client] adding
deregister_phone_numbermethod - [client] allowing to get and set
StorageConfiguration - [callback] adding
is_quick_replytoCallbackButton - [callback] validate not
kw_onlyin dataclasses - [client] fix creating
LibraryTemplate - [system] support old
customer_changed_numbersys type - [docs] new logo for pywa!
Full Changelog: 3.0.0-rc.1...3.0.0-rc.2
3.0.0-rc.1
What's Changed
Update with pip:
pip3 install -U pywa==3.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
- [templates] refactored and improved templates support
- [calls] added full support for calls
- [user_preferences] added full support for user preferences
- [server] continued handling if listener is not using the update
- [system] moved
systemmessages toPhoneNumberChangeandIdentityChangeupdates - [client] forced keyword-only for context args in
send_message,send_image, and othersend_...methods - [types] returned
SuccessResultinstead ofboolto allow future extension with other attributes - [client]
upload_mediareturnsMediaobject - [client] added
get_business_phone_number_settingsandupdate_business_phone_number_settingsto get and update calling settings - [client] added
update_display_namemethod to update the WhatsApp display name - [security] fixed XSS vulnerability
- [api] suggest to provide custom
httpx.Clientonhttpx.RequestError - [handlers] added
on_completiondecorator to flow request callback wrapper - [errors] show more descriptive error messages
- [base_update] added
waba_idfor all user updates - [message] added
referralfield - [types] support
is_on_biz_appinBusinessPhoneNumber - [client] added
delete_mediamethod - [listeners] check if
serverexists before starting to listen - [utils] handled enum values case-sensitively
- [utils] returned
FlowRequestDecryptedMediainstead of(media_id, filename, data)tuple - [utils] new
APIObjectto get fields from datacls - [deprecations] removed attrs and types marked as deprecated
New Contributors
Full Changelog: 2.11.0...3.0.0-rc.1