Skip to content

Commit cbd51fb

Browse files
authored
Merge pull request #176 from david-lev/dev
3.9.0
2 parents 4e33b51 + f8d4f67 commit cbd51fb

9 files changed

Lines changed: 159 additions & 111 deletions

File tree

CHANGELOG.md

Lines changed: 52 additions & 82 deletions
Large diffs are not rendered by default.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{% if not is_latest %}
2+
<div style="
3+
background-color: #fff8dc;
4+
border: 1px solid #f0e68c;
5+
color: #555;
6+
padding: 10px 15px;
7+
text-align: center;
8+
font-size: 0.95rem;
9+
margin-bottom: 1rem;
10+
border-radius: 4px;
11+
box-shadow: inset 0 1px 0 rgba(255,255,255,0.3);
12+
">
13+
⚠ You are viewing documentation for version <b>{{ current_version }}</b>.
14+
The latest version is <a href="/en/latest/">here</a>.
15+
</div>
16+
{% endif %}

docs/source/conf.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@
3333
# Add any Sphinx extension module names here, as strings. They can be
3434
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
3535
# ones.
36+
37+
html_context = {
38+
"is_latest": os.environ.get("READTHEDOCS_VERSION_TYPE") == "latest",
39+
"current_version": os.environ.get("READTHEDOCS_VERSION", "unknown"),
40+
}
41+
html_sidebars = {
42+
"**": [
43+
"navbar-logo.html",
44+
"icon-links.html",
45+
"version-warning.html",
46+
"search-button-field.html",
47+
"sbt-sidebar-nav.html",
48+
]
49+
}
50+
3651
extensions = [
3752
"sphinx.ext.autodoc",
3853
"sphinx_copybutton",

pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@ keywords = [
3333
"whatsapp",
3434
"whatsapp cloud api",
3535
"meta whatsapp api",
36+
"whatsapp webhook",
37+
"whatsapp templates",
38+
"whatsapp flows",
39+
"whatsapp calling",
40+
"whatsapp automation",
41+
"python framework",
3642
"python",
3743
"python library",
38-
"python framework",
3944
"asyncio",
4045
"async",
4146
"whatsapp bot",
@@ -46,9 +51,6 @@ keywords = [
4651
"webhook",
4752
"webhook framework",
4853
"webhook handler",
49-
"whatsapp webhook",
50-
"whatsapp flows",
51-
"whatsapp automation",
5254
"pywa",
5355
]
5456
classifiers = [

pywa/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,6 @@
99
from pywa.client import WhatsApp
1010
from pywa.utils import Version
1111

12-
__version__ = "3.8.0"
12+
__version__ = "3.9.0"
1313
__author__ = "David Lev"
1414
__license__ = "MIT"

pywa/types/base_update.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
SentTemplate,
4949
SentVoiceMessage,
5050
)
51-
from .templates import BaseParams, TemplateLanguage
51+
from .templates import BaseParams, Template, TemplateLanguage
5252

5353

5454
class StopHandling(Exception):
@@ -1052,10 +1052,11 @@ def reply_products(
10521052

10531053
def reply_template(
10541054
self,
1055-
name: str,
1056-
language: TemplateLanguage,
1057-
params: list[BaseParams],
1055+
name: str | None = None,
1056+
language: TemplateLanguage | None = None,
1057+
params: list[BaseParams | dict] | None = None,
10581058
*,
1059+
template: Template | None = None,
10591060
use_mm_lite_api: bool = False,
10601061
message_activity_sharing: bool | None = None,
10611062
quote: bool = False,
@@ -1070,8 +1071,9 @@ def reply_template(
10701071
- Read more about `Template Messages <https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates>`_.
10711072
10721073
Args:
1073-
name: The name of the template to send.
1074-
language: The language of the template to send.
1074+
name: The name of the template to send (optional when ``template`` is provided).
1075+
language: The language of the template to send (optional when ``template`` is provided).
1076+
template: The template object to validate the parameters against (optional, if not provided, ``name`` and ``language`` must be provided).
10751077
params: The parameters to fill in the template.
10761078
use_mm_lite_api: Whether to use `Marketing Messages Lite API <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api>`_ (optional, default: False).
10771079
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).
@@ -1088,6 +1090,7 @@ def reply_template(
10881090
to=self._internal_sender,
10891091
name=name,
10901092
language=language,
1093+
template=template,
10911094
params=params,
10921095
use_mm_lite_api=use_mm_lite_api,
10931096
message_activity_sharing=message_activity_sharing,

pywa/types/templates.py

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,7 @@
9494
from .others import ProductsSection, Result, SuccessResult, _ItemFactory
9595

9696
if TYPE_CHECKING:
97-
from pywa import filters as pywa_filters
98-
97+
from .. import filters as pywa_filters
9998
from ..client import WhatsApp
10099
from .sent_update import SentTemplate
101100

@@ -740,6 +739,12 @@ class TemplateLanguage(utils.StrEnum):
740739
AFRIKAANS = "af"
741740
ALBANIAN = "sq"
742741
ARABIC = "ar"
742+
ARABIC_EG = "ar_EG"
743+
ARABIC_AE = "ar_AE"
744+
ARABIC_LB = "ar_LB"
745+
ARABIC_MA = "ar_MA"
746+
ARABIC_QA = "ar_QA"
747+
ARABIC_SA = "ar_SA"
743748
AZERBAIJANI = "az"
744749
BENGALI = "bn"
745750
BULGARIAN = "bg"
@@ -754,10 +759,15 @@ class TemplateLanguage(utils.StrEnum):
754759
ENGLISH = "en"
755760
ENGLISH_UK = "en_GB"
756761
ENGLISH_US = "en_US"
762+
ENGLISH_AU = "en_AU"
763+
ENGLISH_CA = "en_CA"
764+
ENGLISH_IN = "en_IN"
757765
ESTONIAN = "et"
758766
FILIPINO = "fil"
759767
FINNISH = "fi"
760768
FRENCH = "fr"
769+
FRENCH_CA = "fr_CA"
770+
FRENCH_BE = "fr_BE"
761771
GEORGIAN = "ka"
762772
GERMAN = "de"
763773
GREEK = "el"
@@ -797,6 +807,10 @@ class TemplateLanguage(utils.StrEnum):
797807
SPANISH_ARG = "es_AR"
798808
SPANISH_SPA = "es_ES"
799809
SPANISH_MEX = "es_MX"
810+
SPANISH_CL = "es_CL"
811+
SPANISH_CO = "es_CO"
812+
SPANISH_PE = "es_PE"
813+
SPANISH_VE = "es_VE"
800814
SWAHILI = "sw"
801815
SWEDISH = "sv"
802816
TAMIL = "ta"
@@ -847,6 +861,7 @@ class CreativeFeaturesSpec:
847861
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>`_.
848862
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>`_.
849863
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>`_.
864+
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>`_.
850865
"""
851866

852867
image_brightness_and_contrast: bool
@@ -858,8 +873,9 @@ class CreativeFeaturesSpec:
858873
text_extraction_for_tap_target: bool
859874
product_extensions: bool
860875
text_formatting_optimization: bool
876+
auto_promotion_tag: bool
861877

862-
_fields = {
878+
__slots__ = (
863879
"image_brightness_and_contrast",
864880
"image_touchups",
865881
"add_text_overlay",
@@ -869,7 +885,8 @@ class CreativeFeaturesSpec:
869885
"text_extraction_for_tap_target",
870886
"product_extensions",
871887
"text_formatting_optimization",
872-
}
888+
"auto_promotion_tag",
889+
)
873890

874891
def __init__(
875892
self,
@@ -883,6 +900,7 @@ def __init__(
883900
text_extraction_for_tap_target: bool | None = None,
884901
product_extensions: bool | None = None,
885902
text_formatting_optimization: bool | None = None,
903+
auto_promotion_tag: bool | None = None,
886904
):
887905
"""
888906
Initializes a CreativeFeaturesSpec instance.
@@ -897,25 +915,36 @@ def __init__(
897915
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>`_.
898916
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>`_.
899917
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>`_.
918+
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>`_.
900919
"""
901-
for field_name, value in locals().items():
902-
if field_name == "self":
903-
continue
904-
setattr(self, field_name, value)
920+
locals_map = locals()
921+
for field in self.__slots__:
922+
setattr(self, field, locals_map[field])
905923

906924
def to_dict(self) -> dict:
907925
return {
908-
k: "OPT_IN" if v else "OPT_OUT"
926+
k: {"enroll_status": "OPT_IN" if v else "OPT_OUT"}
909927
for k, v in {
910-
field_name: getattr(self, field_name) for field_name in self._fields
928+
field_name: getattr(self, field_name) for field_name in self.__slots__
911929
}.items()
912930
if v is not None
913931
}
914932

933+
@classmethod
934+
def from_dict(cls, data: list[dict]) -> CreativeFeaturesSpec:
935+
mapping = {"OPT_IN": True, "OPT_OUT": False}
936+
return cls(
937+
**{
938+
item["key"].lower(): mapping.get(item["value"]["enroll_status"])
939+
for item in data
940+
if item["key"].lower() in cls.__slots__
941+
}
942+
)
943+
915944
def __repr__(self) -> str:
916945
field_reprs = ", ".join(
917946
f"{field_name}={getattr(self, field_name)!r}"
918-
for field_name in self._fields
947+
for field_name in self.__slots__
919948
if getattr(self, field_name) is not None
920949
)
921950
return f"{self.__class__.__name__}({field_reprs})"
@@ -3945,6 +3974,7 @@ class TemplateDetails(utils.APIObject):
39453974
quality_score: QualityScore | None
39463975
cta_url_link_tracking_opted_out: bool | None
39473976
sub_category: TemplateSubCategory | None
3977+
degrees_of_freedom_spec: DegreesOfFreedomSpec | None
39483978

39493979
@classmethod
39503980
def from_dict(cls, data: dict, client: WhatsApp) -> TemplateDetails:
@@ -3979,6 +4009,13 @@ def from_dict(cls, data: dict, client: WhatsApp) -> TemplateDetails:
39794009
sub_category=TemplateSubCategory(data["sub_category"])
39804010
if "sub_category" in data
39814011
else None,
4012+
degrees_of_freedom_spec=DegreesOfFreedomSpec(
4013+
creative_features_spec=CreativeFeaturesSpec.from_dict(
4014+
data["degrees_of_freedom_spec"]["creative_features_spec"]
4015+
)
4016+
)
4017+
if "degrees_of_freedom_spec" in data
4018+
else None,
39824019
)
39834020

39844021
def to_json(self) -> str:
@@ -4368,6 +4405,8 @@ def wait_until_approved(
43684405
Returns:
43694406
TemplateStatusUpdate: An update containing the status of the template once it is approved.
43704407
"""
4408+
from pywa import filters as pywa_filters
4409+
43714410
if cancel_on_rejection:
43724411
cancelers = (
43734412
cancelers or pywa_filters.false | pywa_filters.template_status_rejected

pywa_async/types/base_update.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from typing import TYPE_CHECKING, AsyncIterator, BinaryIO, Iterable, Iterator
88

99
from pywa.types.base_update import * # noqa MUST BE IMPORTED FIRST
10-
from pywa.types.templates import BaseParams
1110

1211
from .others import Contact, ProductsSection, SuccessResult, User
1312

@@ -34,7 +33,7 @@
3433
SentTemplate,
3534
SentVoiceMessage,
3635
)
37-
from .templates import TemplateLanguage
36+
from .templates import BaseParams, Template, TemplateLanguage
3837

3938

4039
class _ClientShortcutsAsync:
@@ -884,10 +883,11 @@ async def reply_products(
884883

885884
async def reply_template(
886885
self,
887-
name: str,
888-
language: TemplateLanguage,
889-
params: list[BaseParams],
886+
name: str | None = None,
887+
language: TemplateLanguage | None = None,
888+
params: list[BaseParams | dict] | None = None,
890889
*,
890+
template: Template | None = None,
891891
use_mm_lite_api: bool = False,
892892
message_activity_sharing: bool | None = None,
893893
quote: bool = False,
@@ -902,8 +902,9 @@ async def reply_template(
902902
- Read more about `Template Messages <https://developers.facebook.com/docs/whatsapp/cloud-api/guides/send-message-templates>`_.
903903
904904
Args:
905-
name: The name of the template to send.
906-
language: The language of the template to send.
905+
name: The name of the template to send (optional when ``template`` is provided).
906+
language: The language of the template to send (optional when ``template`` is provided).
907+
template: The template object to validate the parameters against (optional, if not provided, ``name`` and ``language`` must be provided).
907908
params: The parameters to fill in the template.
908909
use_mm_lite_api: Whether to use `Marketing Messages Lite API <https://developers.facebook.com/docs/whatsapp/marketing-messages-lite-api>`_ (optional, default: False).
909910
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).
@@ -920,6 +921,7 @@ async def reply_template(
920921
to=self._internal_sender,
921922
name=name,
922923
language=language,
924+
template=template,
923925
params=params,
924926
use_mm_lite_api=use_mm_lite_api,
925927
message_activity_sharing=message_activity_sharing,

pywa_async/types/templates.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from pywa.types.templates import * # noqa MUST BE IMPORTED FIRST
1212
from pywa.types.templates import (
1313
BaseParams,
14+
_validate_params,
1415
)
1516
from pywa.types.templates import (
1617
CreatedTemplate as _CreatedTemplate,

0 commit comments

Comments
 (0)