Skip to content

Commit 616fec2

Browse files
authored
Merge pull request #209 from david-lev/dev
Enhance documentation, typing, error handling, and version update
2 parents 930acb7 + ed09785 commit 616fec2

30 files changed

Lines changed: 1721 additions & 1058 deletions

CHANGELOG.md

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,55 @@
33

44
> NOTE: pywa follows the [semver](https://semver.org/) versioning standard.
55
6-
#### 4.0.0 (2026-06-09) **Latest**
7-
8-
WORK IN PROGRESS
6+
#### 4.1.0 (2026-06-16) **Latest**
7+
8+
- [client] add `archive_templates` and `unarchive_templates` methods for template archival management
9+
- [client] add `force_transfer` option to `set_username` method for username management
10+
- [client] add `request_contact_info` method to request customer contact information
11+
- [listners] add `wait_for_contact_info` method to wait for contact info requests
12+
- [filters] add `webhook_fields` filter for filtering raw updates by fields
13+
- [cli] improve error handling during CLI command execution
14+
- [handlers] improve handler typing and inline documentation examples
15+
- [api] add internal utility methods for filtering `None` values and joining fields
16+
17+
#### 4.0.0 (2026-06-09)
18+
19+
- **User Identity & BSUID Readiness**:
20+
- Full support for BSUIDs (Business-Scoped User IDs), parent BSUIDs, usernames, and `country_code`.
21+
- `types.User.wa_id` is now optional (users who enable usernames may no longer expose a phone-number-based WhatsApp
22+
ID).
23+
- `types.User.preferred_id` resolves IDs using the new `WhatsApp(user_identifier_priority=...)` priority
24+
configuration.
25+
- `filters.from_users(...)` now accepts BSUIDs, parent BSUIDs, WA IDs, and formatted phone numbers.
26+
- Phone number change updates now expose BSUID-related fields (`new_user_id`, `new_parent_id`).
27+
- **Groups & Chat-Aware Updates**:
28+
- Full group management support: create, update, delete, fetch groups, manage invite links, handle join requests,
29+
and add/remove participants.
30+
- Incoming messages now expose `msg.chat` (a `Chat` object with `id` and `type`) to distinguish private chats from
31+
groups.
32+
- Added `filters.private`, `filters.group`, and `filters.from_groups(...)`.
33+
- Added `GroupMessageStatusesHandler` and `wa.on_group_message_statuses(...)` for group status updates.
34+
- Sent messages now expose `sent.chat` and support pinning/unpinning.
35+
- **Webhooks, CLI, & Local Development**:
36+
- Added the built-in server workflow: `pywa dev` (auto-reload), `pywa run` (production-style), and
37+
`WhatsApp.run()` (quick scripts).
38+
- Added `utils.start_ngrok_tunnel(...)` for easy local webhook testing.
39+
- Support for custom webhook subscription fields with `utils.WebhookFields` via `webhook_fields`.
40+
- Refactored webhook validation and endpoint registration to work consistently across built-in Starlette app,
41+
FastAPI, Flask, and manual integrations.
42+
- Listeners now warn when no timeout is provided and prevent usage with multiple Uvicorn workers.
43+
- **Messages, Media, Callbacks, & Account Updates**:
44+
- Added `EditedMessage`, `DeletedMessage`, `OutgoingEditedMessage`, and `OutgoingDeletedMessage` updates for
45+
coexistence support.
46+
- Added `AccountUpdate` and related enums for account updates.
47+
- Media objects now store their `caption`, and media upload internals support async pending uploads with
48+
`PendingMedia`.
49+
- Added `ContactInfoRequestButton`, `ContactList`, and carousel message support (`send_carousel`, `reply_carousel`).
50+
- **Business Management & Templates**:
51+
- Retrieve shared/owned WABAs, create/verify phone numbers, and manage usernames (`set_username`, etc.).
52+
- Added WABA settings updates, including `degrees_of_freedom_spec`.
53+
- Enhanced template validation, lookup, parameter introspection (`param_names`), and error reporting.
54+
- Support for `target_waba_id` in `Template.duplicate(...)` and helper states in `CreativeFeaturesSpec`.
955

1056
#### 4.0.0b7 (2026-04-30)
1157

docs/source/content/client/overview.rst

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Its **three main responsibilities** are:
1313
3. **Managing resources** — templates, flows, profiles, and other business-related settings.
1414

1515
.. tip::
16-
:class: note
16+
:class: tip
1717

1818
Pywa provides **two types of clients**:
1919

@@ -43,6 +43,9 @@ Its **three main responsibilities** are:
4343
await msg.reply("Hello!")
4444
4545
For optimal type checking, ensure that **all** your imports come from the same package—either ``pywa`` or ``pywa_async``.
46+
47+
To help you navigate the API, the client's methods are grouped below by functionality. Most of these methods return type-safe objects representing API responses, which you can then manipulate or reply to directly.
48+
4649
.. autoclass:: WhatsApp()
4750
:members: __init__
4851

@@ -227,7 +230,7 @@ Create, update, and manage message templates:
227230
* - :meth:`~WhatsApp.create_template`
228231
- Create a new template
229232
* - :meth:`~WhatsApp.upsert_authentication_template`
230-
- Bulk create or update authentication templates
233+
- Create or update multiple authentication templates in a single request
231234
* - :meth:`~WhatsApp.get_templates`
232235
- Retrieve all templates
233236
* - :meth:`~WhatsApp.get_template`

docs/source/content/examples/demo-bots.rst

Lines changed: 19 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
🤖 Demo Bots
22
============
33

4-
This page contains some examples of bots you can create using pywa.
5-
Every example is a complete working bot that you can run on your own server.
4+
This page contains complete, working examples of bots you can create using pywa.
65

76
👋 Hello Bot
87
--------------
@@ -12,15 +11,11 @@ This is a simple bot that welcomes the user when they send a message.
1211
.. code-block:: python
1312
:linenos:
1413
15-
import flask # pip3 install flask
1614
from pywa import WhatsApp, types
1715
18-
flask_app = flask.Flask(__name__)
19-
2016
wa = WhatsApp(
2117
phone_id='your_phone_number',
2218
token='your_token',
23-
server=flask_app,
2419
verify_token='xyzxyz',
2520
)
2621
@@ -29,8 +24,7 @@ This is a simple bot that welcomes the user when they send a message.
2924
msg.react('👋')
3025
msg.reply(f'Hello {msg.from_user.name}!')
3126
32-
# Run the server
33-
flask_app.run()
27+
# Run the server with `pywa dev`
3428
3529
3630
📝 Echo Bot
@@ -42,15 +36,11 @@ This is a simple bot that echoes back the user's message.
4236
.. code-block:: python
4337
:linenos:
4438
45-
import flask # pip3 install flask
4639
from pywa import WhatsApp, types
4740
48-
flask_app = flask.Flask(__name__)
49-
5041
wa = WhatsApp(
5142
phone_id='your_phone_number',
5243
token='your_token',
53-
server=flask_app,
5444
verify_token='xyzxyz',
5545
)
5646
@@ -59,11 +49,9 @@ This is a simple bot that echoes back the user's message.
5949
try:
6050
msg.copy(to=msg.sender, reply_to_message_id=msg.message_id_to_reply)
6151
except ValueError:
62-
msg.reply_text("I can't echo this message")
63-
64-
# Run the server
65-
flask_app.run()
52+
msg.reply("I can't echo this message")
6653
54+
# Run the server with `pywa dev`
6755
6856
6957
⬆️ Url Uploader Bot
@@ -74,16 +62,11 @@ This is a simple bot that uploads files from URLs.
7462
.. code-block:: python
7563
:linenos:
7664
77-
import flask # pip3 install flask
7865
from pywa import WhatsApp, types, filters, errors
79-
from pywa.types import Message, MessageStatus
80-
81-
flask_app = flask.Flask(__name__)
8266
8367
wa = WhatsApp(
8468
phone_id='your_phone_number',
8569
token='your_token',
86-
server=flask_app,
8770
verify_token='xyzxyz',
8871
)
8972
@@ -94,10 +77,9 @@ This is a simple bot that uploads files from URLs.
9477
# When a file fails to download/upload, the bot will reply with an error message.
9578
@wa.on_message_status(filters.failed_with(errors.MediaDownloadError, errors.MediaUploadError))
9679
def on_media_download_error(_: WhatsApp, status: types.MessageStatus):
97-
status.reply_text(f"I can't download/upload this file: {status.error.details}")
80+
status.reply(f"I can't download/upload this file: {status.error.details}")
9881
99-
# Run the server
100-
flask_app.run()
82+
# Run the server with `pywa dev`
10183
10284
10385
🔢 Calculator WhatsApp Bot
@@ -115,15 +97,11 @@ Usage:
11597
.. code-block:: python
11698
11799
import re
118-
import flask # pip3 install flask
119100
from pywa import WhatsApp, types, filters
120101
121-
flask_app = flask.Flask(__name__)
122-
123102
wa = WhatsApp(
124103
phone_id='your_phone_number',
125104
token='your_token',
126-
server=flask_app,
127105
verify_token='xyzxyz',
128106
)
129107
@@ -153,9 +131,7 @@ Usage:
153131
return
154132
msg.reply(f'{a} {op} {b} = *{result}*')
155133
156-
# Run the server
157-
flask_app.run()
158-
134+
# Run the server with `pywa dev`
159135
160136
🌐 Translator Bot
161137
-----------------
@@ -166,17 +142,14 @@ A simple WhatsApp bot that translates text messages to other languages.
166142
:linenos:
167143
168144
import logging
169-
import flask # pip3 install flask
170145
import googletrans # pip3 install googletrans==4.0.0-rc1
171146
from pywa import WhatsApp, types, filters
172147
173-
flask_app = flask.Flask(__name__)
174148
translator = googletrans.Translator()
175149
176150
wa = WhatsApp(
177151
phone_id='your_phone_number',
178152
token='your_token',
179-
server=flask_app,
180153
verify_token='xyzxyz',
181154
)
182155
@@ -199,7 +172,7 @@ A simple WhatsApp bot that translates text messages to other languages.
199172
200173
@wa.on_message(filters.text)
201174
def offer_translation(_: WhatsApp, msg: types.Message):
202-
msg_id = msg.reply_text(
175+
msg_id = msg.reply(
203176
text='Choose language to translate to:',
204177
buttons=types.SectionList(
205178
button_title='🌐 Choose Language',
@@ -238,27 +211,26 @@ A simple WhatsApp bot that translates text messages to other languages.
238211
original_text = MESSAGE_ID_TO_TEXT[sel.reply_to_message.message_id]
239212
except KeyError: # If the bot was restarted, the message ID is no longer valid.
240213
sel.react('')
241-
sel.reply_text(
214+
sel.reply(
242215
text='Original message not found. Please send a new message.'
243216
)
244217
return
245218
try:
246219
translated = translator.translate(original_text, dest=lang_code)
247220
except Exception as e:
248221
sel.react('')
249-
sel.reply_text(
222+
sel.reply(
250223
text='An error occurred. Please try again.'
251224
)
252225
logging.exception(e)
253226
return
254227
255-
sel.reply_text(
228+
sel.reply(
256229
text=f"Translated to {translated.dest}:\n{translated.text}"
257230
)
258231
259232
260-
# Run the server
261-
flask_app.run()
233+
# Run the server with `pywa dev`
262234
263235
264236
🖼 Random image bot
@@ -270,16 +242,11 @@ This example shows how to create a simple bot that replies with a random image f
270242
.. code-block:: python
271243
:linenos:
272244
273-
import requests
274-
import flask
275245
from pywa import WhatsApp, types
276246
277-
flask_app = flask.Flask(__name__)
278-
279247
wa = WhatsApp(
280248
phone_id='your_phone_number',
281249
token='your_token',
282-
server=flask_app,
283250
verify_token='xyzxyz',
284251
)
285252
@@ -291,8 +258,7 @@ This example shows how to create a simple bot that replies with a random image f
291258
buttons=types.ButtonUrl(title='Unsplash', url='https://unsplash.com')
292259
)
293260
294-
# Run the server
295-
flask_app.run()
261+
# Run the server with `pywa dev`
296262
297263
298264
📸 Remove background from image
@@ -303,16 +269,13 @@ This example shows how to create a bot that removes the background from an image
303269
.. code-block:: python
304270
:linenos:
305271
306-
import requests
307-
import flask
272+
import logging
273+
import httpx
308274
from pywa import WhatsApp, types
309275
310-
flask_app = flask.Flask(__name__)
311-
312276
wa = WhatsApp(
313277
phone_id='your_phone_number',
314278
token='your_token',
315-
server=flask_app,
316279
verify_token='xyzxyz',
317280
)
318281
@@ -324,7 +287,7 @@ This example shows how to create a bot that removes the background from an image
324287
files = {'image_file': original_img}
325288
data = {'size': 'auto'}
326289
headers = {'X-Api-Key': REMOVEBG_API_KEY}
327-
response = requests.post(url, files=files, data=data, headers=headers)
290+
response = httpx.post(url, files=files, data=data, headers=headers)
328291
response.raise_for_status()
329292
return response.content
330293
@@ -334,8 +297,8 @@ This example shows how to create a bot that removes the background from an image
334297
try:
335298
original_img = msg.image.download(in_memory=True)
336299
image = get_removed_bg_image(original_img)
337-
except requests.HTTPError as e:
338-
msg.reply_text(f"A error occurred")
300+
except httpx.HTTPError as e:
301+
msg.reply("An error occurred")
339302
logging.exception(e)
340303
return
341304
msg.reply_image(
@@ -344,5 +307,4 @@ This example shows how to create a bot that removes the background from an image
344307
mime_type='image/png', # when sending bytes, you must specify the mime type
345308
)
346309
347-
# Run the server
348-
flask_app.run()
310+
# Run the server with `pywa dev`

0 commit comments

Comments
 (0)