Skip to content
Merged

3.9.0 #176

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
7b68981
[docs] add version warning and update documentation links for consist…
david-lev Jan 24, 2026
7e2e70d
Revert "[docs] add version warning and update documentation links for…
david-lev Jan 24, 2026
9c7a2c1
Reapply "[docs] add version warning and update documentation links fo…
david-lev Jan 26, 2026
860ddd0
[docs] update index.rst structure and add overview redirect functiona…
david-lev Jan 26, 2026
b0c54ca
Revert "[docs] update index.rst structure and add overview redirect f…
david-lev Jan 26, 2026
b180294
Revert "Reapply "[docs] add version warning and update documentation …
david-lev Jan 26, 2026
8645f24
Reapply "[docs] add version warning and update documentation links fo…
david-lev Jan 26, 2026
f341828
Revert "[docs] add version warning and update documentation links for…
david-lev Jan 26, 2026
19946a2
[docs] add version warning to documentation for non-latest versions
david-lev Jan 26, 2026
4e2187e
[templates] add additional language variants for Arabic, English, Fre…
oscargdi Feb 6, 2026
519356b
Merge pull request #172 from oscargdi/add-support-to-more-template-la…
yehuda-lev Feb 7, 2026
8e0a808
[templates] fix #171 by import `_validate_params` in async
yehuda-lev Feb 7, 2026
273105b
[base_update] edit `reply_template` to accept `Template` obj
david-lev Feb 22, 2026
77c11fc
[templates] add `auto_promotion_tag` field to `CreativeFeaturesSpec`
david-lev Mar 9, 2026
ac50758
[templates] fetch `degrees_of_freedom_spec` field for `TemplateDetails`
david-lev Mar 9, 2026
be9816f
[templates] fix filters import
david-lev Mar 11, 2026
a1ccbb3
[templates] fix filters import
david-lev Mar 11, 2026
f8d4f67
[version] new version 3.9.0
david-lev Mar 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 52 additions & 82 deletions CHANGELOG.md

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions docs/source/_templates/version-warning.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{% if not is_latest %}
<div style="
background-color: #fff8dc;
border: 1px solid #f0e68c;
color: #555;
padding: 10px 15px;
text-align: center;
font-size: 0.95rem;
margin-bottom: 1rem;
border-radius: 4px;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.3);
">
⚠ You are viewing documentation for version <b>{{ current_version }}</b>.
The latest version is <a href="/en/latest/">here</a>.
</div>
{% endif %}
15 changes: 15 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.

html_context = {
"is_latest": os.environ.get("READTHEDOCS_VERSION_TYPE") == "latest",
"current_version": os.environ.get("READTHEDOCS_VERSION", "unknown"),
}
html_sidebars = {
"**": [
"navbar-logo.html",
"icon-links.html",
"version-warning.html",
"search-button-field.html",
"sbt-sidebar-nav.html",
]
}

extensions = [
"sphinx.ext.autodoc",
"sphinx_copybutton",
Expand Down
10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ keywords = [
"whatsapp",
"whatsapp cloud api",
"meta whatsapp api",
"whatsapp webhook",
"whatsapp templates",
"whatsapp flows",
"whatsapp calling",
"whatsapp automation",
"python framework",
"python",
"python library",
"python framework",
"asyncio",
"async",
"whatsapp bot",
Expand All @@ -46,9 +51,6 @@ keywords = [
"webhook",
"webhook framework",
"webhook handler",
"whatsapp webhook",
"whatsapp flows",
"whatsapp automation",
"pywa",
]
classifiers = [
Expand Down
2 changes: 1 addition & 1 deletion pywa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
from pywa.client import WhatsApp
from pywa.utils import Version

__version__ = "3.8.0"
__version__ = "3.9.0"
__author__ = "David Lev"
__license__ = "MIT"
15 changes: 9 additions & 6 deletions pywa/types/base_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
SentTemplate,
SentVoiceMessage,
)
from .templates import BaseParams, TemplateLanguage
from .templates import BaseParams, Template, TemplateLanguage


class StopHandling(Exception):
Expand Down Expand Up @@ -1052,10 +1052,11 @@ def reply_products(

def reply_template(
self,
name: str,
language: TemplateLanguage,
params: list[BaseParams],
name: str | None = None,
language: TemplateLanguage | None = None,
params: list[BaseParams | dict] | None = None,
*,
template: Template | None = None,
use_mm_lite_api: bool = False,
message_activity_sharing: bool | None = None,
quote: bool = False,
Expand All @@ -1070,8 +1071,9 @@ def reply_template(
- Read more about `Template Messages <https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates>`_.

Args:
name: The name of the template to send.
language: The language of the template to send.
name: The name of the template to send (optional when ``template`` is provided).
language: The language of the template to send (optional when ``template`` is provided).
template: The template object to validate the parameters against (optional, if not provided, ``name`` and ``language`` must be provided).
params: The parameters to fill in the template.
use_mm_lite_api: Whether to use `Marketing Messages Lite API <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api>`_ (optional, default: False).
message_activity_sharing: Whether to share message activities (e.g. message read) for that specific marketing message to Meta to help optimize marketing messages (optional, only if ``use_mm_lite_api`` is True).
Expand All @@ -1088,6 +1090,7 @@ def reply_template(
to=self._internal_sender,
name=name,
language=language,
template=template,
params=params,
use_mm_lite_api=use_mm_lite_api,
message_activity_sharing=message_activity_sharing,
Expand Down
61 changes: 50 additions & 11 deletions pywa/types/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@
from .others import ProductsSection, Result, SuccessResult, _ItemFactory

if TYPE_CHECKING:
from pywa import filters as pywa_filters

from .. import filters as pywa_filters
from ..client import WhatsApp
from .sent_update import SentTemplate

Expand Down Expand Up @@ -740,6 +739,12 @@ class TemplateLanguage(utils.StrEnum):
AFRIKAANS = "af"
ALBANIAN = "sq"
ARABIC = "ar"
ARABIC_EG = "ar_EG"
ARABIC_AE = "ar_AE"
ARABIC_LB = "ar_LB"
ARABIC_MA = "ar_MA"
ARABIC_QA = "ar_QA"
ARABIC_SA = "ar_SA"
AZERBAIJANI = "az"
BENGALI = "bn"
BULGARIAN = "bg"
Expand All @@ -754,10 +759,15 @@ class TemplateLanguage(utils.StrEnum):
ENGLISH = "en"
ENGLISH_UK = "en_GB"
ENGLISH_US = "en_US"
ENGLISH_AU = "en_AU"
ENGLISH_CA = "en_CA"
ENGLISH_IN = "en_IN"
ESTONIAN = "et"
FILIPINO = "fil"
FINNISH = "fi"
FRENCH = "fr"
FRENCH_CA = "fr_CA"
FRENCH_BE = "fr_BE"
GEORGIAN = "ka"
GERMAN = "de"
GREEK = "el"
Expand Down Expand Up @@ -797,6 +807,10 @@ class TemplateLanguage(utils.StrEnum):
SPANISH_ARG = "es_AR"
SPANISH_SPA = "es_ES"
SPANISH_MEX = "es_MX"
SPANISH_CL = "es_CL"
SPANISH_CO = "es_CO"
SPANISH_PE = "es_PE"
SPANISH_VE = "es_VE"
SWAHILI = "sw"
SWEDISH = "sv"
TAMIL = "ta"
Expand Down Expand Up @@ -847,6 +861,7 @@ class CreativeFeaturesSpec:
image_background_gen: Whether to generate backgrounds for images. Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api/sending-messages#image-background-generation>`_.
text_extraction_for_headline: Whether to extract text from images for headlines. Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api/sending-messages#headline-extraction>`_.
text_extraction_for_tap_target: Whether to extract text from images for tap targets. Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api/sending-messages#tap-target-title-extraction>`_.
auto_promotion_tag: Whether to automatically extract the promotion tag, like “30% off”, “50% discount”, “Free shipping” from messages to create a promotion tag and put it into the image to highlight promotion information. Read more at `developers.facebook.com <https://developers.facebook.com/documentation/business-messaging/whatsapp/marketing-messages/send-marketing-messages#auto-promotion-tag>`_.
"""

image_brightness_and_contrast: bool
Expand All @@ -858,8 +873,9 @@ class CreativeFeaturesSpec:
text_extraction_for_tap_target: bool
product_extensions: bool
text_formatting_optimization: bool
auto_promotion_tag: bool

_fields = {
__slots__ = (
"image_brightness_and_contrast",
"image_touchups",
"add_text_overlay",
Expand All @@ -869,7 +885,8 @@ class CreativeFeaturesSpec:
"text_extraction_for_tap_target",
"product_extensions",
"text_formatting_optimization",
}
"auto_promotion_tag",
)

def __init__(
self,
Expand All @@ -883,6 +900,7 @@ def __init__(
text_extraction_for_tap_target: bool | None = None,
product_extensions: bool | None = None,
text_formatting_optimization: bool | None = None,
auto_promotion_tag: bool | None = None,
):
"""
Initializes a CreativeFeaturesSpec instance.
Expand All @@ -897,25 +915,36 @@ def __init__(
text_extraction_for_tap_target: Whether to extract keywords or phrases from your message to create a title for the tap-target area to highlight key information. Read more at `developers.facebook.com <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api/sending-messages#tap-target-title-extraction>`_.
product_extensions: Whether to encourage users to explore more products by appending additional catalog products to single-image creatives. Read more at `developers.facebook.com <https://developers.facebook.com/documentation/business-messaging/whatsapp/marketing-messages/send-marketing-messages#product-extensions>`_.
text_formatting_optimization: Whether to update the formatting of text (e.g. remove unnecessary spaces, bold phrases) to increase performance. No text content is changed - format only. Read more at `developers.facebook.com <https://developers.facebook.com/documentation/business-messaging/whatsapp/marketing-messages/send-marketing-messages#text-formatting>`_.
auto_promotion_tag: Whether to automatically extract the promotion tag, like “30% off”, “50% discount”, “Free shipping” from messages to create a promotion tag and put it into the image to highlight promotion information. Read more at `developers.facebook.com <https://developers.facebook.com/documentation/business-messaging/whatsapp/marketing-messages/send-marketing-messages#auto-promotion-tag>`_.
"""
for field_name, value in locals().items():
if field_name == "self":
continue
setattr(self, field_name, value)
locals_map = locals()
for field in self.__slots__:
setattr(self, field, locals_map[field])

def to_dict(self) -> dict:
return {
k: "OPT_IN" if v else "OPT_OUT"
k: {"enroll_status": "OPT_IN" if v else "OPT_OUT"}
for k, v in {
field_name: getattr(self, field_name) for field_name in self._fields
field_name: getattr(self, field_name) for field_name in self.__slots__
}.items()
if v is not None
}

@classmethod
def from_dict(cls, data: list[dict]) -> CreativeFeaturesSpec:
mapping = {"OPT_IN": True, "OPT_OUT": False}
return cls(
**{
item["key"].lower(): mapping.get(item["value"]["enroll_status"])
for item in data
if item["key"].lower() in cls.__slots__
}
)

def __repr__(self) -> str:
field_reprs = ", ".join(
f"{field_name}={getattr(self, field_name)!r}"
for field_name in self._fields
for field_name in self.__slots__
if getattr(self, field_name) is not None
)
return f"{self.__class__.__name__}({field_reprs})"
Expand Down Expand Up @@ -3945,6 +3974,7 @@ class TemplateDetails(utils.APIObject):
quality_score: QualityScore | None
cta_url_link_tracking_opted_out: bool | None
sub_category: TemplateSubCategory | None
degrees_of_freedom_spec: DegreesOfFreedomSpec | None

@classmethod
def from_dict(cls, data: dict, client: WhatsApp) -> TemplateDetails:
Expand Down Expand Up @@ -3979,6 +4009,13 @@ def from_dict(cls, data: dict, client: WhatsApp) -> TemplateDetails:
sub_category=TemplateSubCategory(data["sub_category"])
if "sub_category" in data
else None,
degrees_of_freedom_spec=DegreesOfFreedomSpec(
creative_features_spec=CreativeFeaturesSpec.from_dict(
data["degrees_of_freedom_spec"]["creative_features_spec"]
)
)
if "degrees_of_freedom_spec" in data
else None,
)

def to_json(self) -> str:
Expand Down Expand Up @@ -4368,6 +4405,8 @@ def wait_until_approved(
Returns:
TemplateStatusUpdate: An update containing the status of the template once it is approved.
"""
from pywa import filters as pywa_filters

if cancel_on_rejection:
cancelers = (
cancelers or pywa_filters.false | pywa_filters.template_status_rejected
Expand Down
16 changes: 9 additions & 7 deletions pywa_async/types/base_update.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from typing import TYPE_CHECKING, AsyncIterator, BinaryIO, Iterable, Iterator

from pywa.types.base_update import * # noqa MUST BE IMPORTED FIRST
from pywa.types.templates import BaseParams

from .others import Contact, ProductsSection, SuccessResult, User

Expand All @@ -34,7 +33,7 @@
SentTemplate,
SentVoiceMessage,
)
from .templates import TemplateLanguage
from .templates import BaseParams, Template, TemplateLanguage


class _ClientShortcutsAsync:
Expand Down Expand Up @@ -884,10 +883,11 @@ async def reply_products(

async def reply_template(
self,
name: str,
language: TemplateLanguage,
params: list[BaseParams],
name: str | None = None,
language: TemplateLanguage | None = None,
params: list[BaseParams | dict] | None = None,
*,
template: Template | None = None,
use_mm_lite_api: bool = False,
message_activity_sharing: bool | None = None,
quote: bool = False,
Expand All @@ -902,8 +902,9 @@ async def reply_template(
- Read more about `Template Messages <https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates>`_.

Args:
name: The name of the template to send.
language: The language of the template to send.
name: The name of the template to send (optional when ``template`` is provided).
language: The language of the template to send (optional when ``template`` is provided).
template: The template object to validate the parameters against (optional, if not provided, ``name`` and ``language`` must be provided).
params: The parameters to fill in the template.
use_mm_lite_api: Whether to use `Marketing Messages Lite API <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api>`_ (optional, default: False).
message_activity_sharing: Whether to share message activities (e.g. message read) for that specific marketing message to Meta to help optimize marketing messages (optional, only if ``use_mm_lite_api`` is True).
Expand All @@ -920,6 +921,7 @@ async def reply_template(
to=self._internal_sender,
name=name,
language=language,
template=template,
params=params,
use_mm_lite_api=use_mm_lite_api,
message_activity_sharing=message_activity_sharing,
Expand Down
1 change: 1 addition & 0 deletions pywa_async/types/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pywa.types.templates import * # noqa MUST BE IMPORTED FIRST
from pywa.types.templates import (
BaseParams,
_validate_params,
)
from pywa.types.templates import (
CreatedTemplate as _CreatedTemplate,
Expand Down
Loading