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
- π Documentation
- π¬ Community Telegram Chat
π 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