whatsapp-cloud-api-py 0.1.1__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/PKG-INFO +19 -11
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/README.md +18 -10
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/pyproject.toml +1 -1
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/client.py +3 -3
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_client.py +5 -5
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_messages_resource.py +1 -1
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_resources_flows.py +1 -1
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_resources_media.py +2 -2
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_resources_phone_numbers.py +1 -1
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_resources_templates.py +1 -1
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/.github/workflows/publish.yml +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/.gitignore +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/LICENSE +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/errors/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/errors/categorize.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/errors/graph_api_error.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/errors/retry.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/events/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/events/dispatcher.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/events/events.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/flows.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/media.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/messages/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/messages/models.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/messages/resource.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/phone_numbers.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/templates/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/templates/models.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/resources/templates/resource.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/types.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/utils/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/utils/case.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/webhooks/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/webhooks/normalize.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/webhooks/verify.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/__init__.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/conftest.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_errors_categorize.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_errors_graph_api_error.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_errors_retry.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_events.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_events_dispatcher.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_messages_models.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_types.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_utils_case.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_webhooks_normalize.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_webhooks_verify.py +0 -0
- {whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: whatsapp-cloud-api-py
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Async Python SDK for WhatsApp Business Cloud API with Pydantic V2
|
|
5
5
|
Project-URL: Homepage, https://github.com/HeiCg/whatsapp-cloud-api-py
|
|
6
6
|
Project-URL: Repository, https://github.com/HeiCg/whatsapp-cloud-api-py
|
|
@@ -26,12 +26,22 @@ Description-Content-Type: text/markdown
|
|
|
26
26
|
|
|
27
27
|
# whatsapp-cloud-api-py
|
|
28
28
|
|
|
29
|
-
Community-built async Python SDK for the WhatsApp Business Cloud API.
|
|
29
|
+
Community-built async Python SDK for the WhatsApp Business Cloud API, powered by [Kapso](https://kapso.ai).
|
|
30
30
|
|
|
31
31
|
> **Note:** This is an **independent Python implementation** — not a port or fork. It was inspired by the excellent [`@kapso/whatsapp-cloud-api`](https://github.com/gokapso/whatsapp-cloud-api-js) (TypeScript), but written from scratch in Python with its own architecture, design choices, and API surface.
|
|
32
32
|
|
|
33
33
|
Built with **httpx** (HTTP/2 + connection pooling), **Pydantic V2** (Rust-powered validation), and optional **pyventus** event-driven webhooks.
|
|
34
34
|
|
|
35
|
+
## Prerequisites
|
|
36
|
+
|
|
37
|
+
This SDK connects to Meta's WhatsApp Cloud API through [Kapso's](https://kapso.ai) managed proxy. You'll need a **Kapso API key** before getting started:
|
|
38
|
+
|
|
39
|
+
1. Create an account at [kapso.ai](https://kapso.ai)
|
|
40
|
+
2. Connect your WhatsApp Business account
|
|
41
|
+
3. Generate an API key from the dashboard
|
|
42
|
+
|
|
43
|
+
See the [Kapso docs](https://docs.kapso.ai/docs/introduction) for detailed setup instructions.
|
|
44
|
+
|
|
35
45
|
## Features
|
|
36
46
|
|
|
37
47
|
- **Fully async** — all I/O uses `async`/`await` with httpx
|
|
@@ -69,7 +79,7 @@ import asyncio
|
|
|
69
79
|
from whatsapp_cloud_api import WhatsAppClient, TextMessage
|
|
70
80
|
|
|
71
81
|
async def main():
|
|
72
|
-
async with WhatsAppClient(access_token="
|
|
82
|
+
async with WhatsAppClient(access_token="YOUR_KAPSO_API_KEY") as client:
|
|
73
83
|
response = await client.messages.send_text(TextMessage(
|
|
74
84
|
phone_number_id="PHONE_NUMBER_ID",
|
|
75
85
|
to="5511999999999",
|
|
@@ -562,24 +572,22 @@ except GraphApiError as e:
|
|
|
562
572
|
```python
|
|
563
573
|
from whatsapp_cloud_api import WhatsAppClient
|
|
564
574
|
|
|
565
|
-
# Default:
|
|
566
|
-
client = WhatsAppClient(access_token="
|
|
575
|
+
# Default: api.kapso.ai, v23.0, HTTP/2, 30s timeout
|
|
576
|
+
client = WhatsAppClient(access_token="YOUR_KAPSO_API_KEY")
|
|
567
577
|
|
|
568
|
-
# Custom
|
|
578
|
+
# Custom timeout
|
|
569
579
|
client = WhatsAppClient(
|
|
570
|
-
access_token="
|
|
571
|
-
base_url="https://graph.facebook.com",
|
|
572
|
-
graph_version="v23.0",
|
|
580
|
+
access_token="YOUR_KAPSO_API_KEY",
|
|
573
581
|
timeout=60.0,
|
|
574
582
|
)
|
|
575
583
|
|
|
576
584
|
# Bring your own httpx client
|
|
577
585
|
import httpx
|
|
578
586
|
custom_http = httpx.AsyncClient(http2=True, timeout=60.0)
|
|
579
|
-
client = WhatsAppClient(access_token="
|
|
587
|
+
client = WhatsAppClient(access_token="YOUR_KAPSO_API_KEY", http_client=custom_http)
|
|
580
588
|
|
|
581
589
|
# Always use as async context manager
|
|
582
|
-
async with WhatsAppClient(access_token="
|
|
590
|
+
async with WhatsAppClient(access_token="YOUR_KAPSO_API_KEY") as client:
|
|
583
591
|
await client.messages.send_text(...)
|
|
584
592
|
```
|
|
585
593
|
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
# whatsapp-cloud-api-py
|
|
2
2
|
|
|
3
|
-
Community-built async Python SDK for the WhatsApp Business Cloud API.
|
|
3
|
+
Community-built async Python SDK for the WhatsApp Business Cloud API, powered by [Kapso](https://kapso.ai).
|
|
4
4
|
|
|
5
5
|
> **Note:** This is an **independent Python implementation** — not a port or fork. It was inspired by the excellent [`@kapso/whatsapp-cloud-api`](https://github.com/gokapso/whatsapp-cloud-api-js) (TypeScript), but written from scratch in Python with its own architecture, design choices, and API surface.
|
|
6
6
|
|
|
7
7
|
Built with **httpx** (HTTP/2 + connection pooling), **Pydantic V2** (Rust-powered validation), and optional **pyventus** event-driven webhooks.
|
|
8
8
|
|
|
9
|
+
## Prerequisites
|
|
10
|
+
|
|
11
|
+
This SDK connects to Meta's WhatsApp Cloud API through [Kapso's](https://kapso.ai) managed proxy. You'll need a **Kapso API key** before getting started:
|
|
12
|
+
|
|
13
|
+
1. Create an account at [kapso.ai](https://kapso.ai)
|
|
14
|
+
2. Connect your WhatsApp Business account
|
|
15
|
+
3. Generate an API key from the dashboard
|
|
16
|
+
|
|
17
|
+
See the [Kapso docs](https://docs.kapso.ai/docs/introduction) for detailed setup instructions.
|
|
18
|
+
|
|
9
19
|
## Features
|
|
10
20
|
|
|
11
21
|
- **Fully async** — all I/O uses `async`/`await` with httpx
|
|
@@ -43,7 +53,7 @@ import asyncio
|
|
|
43
53
|
from whatsapp_cloud_api import WhatsAppClient, TextMessage
|
|
44
54
|
|
|
45
55
|
async def main():
|
|
46
|
-
async with WhatsAppClient(access_token="
|
|
56
|
+
async with WhatsAppClient(access_token="YOUR_KAPSO_API_KEY") as client:
|
|
47
57
|
response = await client.messages.send_text(TextMessage(
|
|
48
58
|
phone_number_id="PHONE_NUMBER_ID",
|
|
49
59
|
to="5511999999999",
|
|
@@ -536,24 +546,22 @@ except GraphApiError as e:
|
|
|
536
546
|
```python
|
|
537
547
|
from whatsapp_cloud_api import WhatsAppClient
|
|
538
548
|
|
|
539
|
-
# Default:
|
|
540
|
-
client = WhatsAppClient(access_token="
|
|
549
|
+
# Default: api.kapso.ai, v23.0, HTTP/2, 30s timeout
|
|
550
|
+
client = WhatsAppClient(access_token="YOUR_KAPSO_API_KEY")
|
|
541
551
|
|
|
542
|
-
# Custom
|
|
552
|
+
# Custom timeout
|
|
543
553
|
client = WhatsAppClient(
|
|
544
|
-
access_token="
|
|
545
|
-
base_url="https://graph.facebook.com",
|
|
546
|
-
graph_version="v23.0",
|
|
554
|
+
access_token="YOUR_KAPSO_API_KEY",
|
|
547
555
|
timeout=60.0,
|
|
548
556
|
)
|
|
549
557
|
|
|
550
558
|
# Bring your own httpx client
|
|
551
559
|
import httpx
|
|
552
560
|
custom_http = httpx.AsyncClient(http2=True, timeout=60.0)
|
|
553
|
-
client = WhatsAppClient(access_token="
|
|
561
|
+
client = WhatsAppClient(access_token="YOUR_KAPSO_API_KEY", http_client=custom_http)
|
|
554
562
|
|
|
555
563
|
# Always use as async context manager
|
|
556
|
-
async with WhatsAppClient(access_token="
|
|
564
|
+
async with WhatsAppClient(access_token="YOUR_KAPSO_API_KEY") as client:
|
|
557
565
|
await client.messages.send_text(...)
|
|
558
566
|
```
|
|
559
567
|
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/client.py
RENAMED
|
@@ -15,8 +15,8 @@ if TYPE_CHECKING:
|
|
|
15
15
|
from .resources.phone_numbers import PhoneNumbersResource
|
|
16
16
|
from .resources.templates.resource import TemplatesResource
|
|
17
17
|
|
|
18
|
-
_DEFAULT_BASE_URL = "https://
|
|
19
|
-
_DEFAULT_VERSION = "
|
|
18
|
+
_DEFAULT_BASE_URL = "https://api.kapso.ai/meta/whatsapp"
|
|
19
|
+
_DEFAULT_VERSION = "v24.0"
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class WhatsAppClient:
|
|
@@ -71,7 +71,7 @@ class WhatsAppClient:
|
|
|
71
71
|
|
|
72
72
|
@property
|
|
73
73
|
def _auth_headers(self) -> dict[str, str]:
|
|
74
|
-
return {"
|
|
74
|
+
return {"X-API-Key": self._access_token}
|
|
75
75
|
|
|
76
76
|
# ── core request ─────────────────────────────────────────────
|
|
77
77
|
|
|
@@ -9,7 +9,7 @@ import respx
|
|
|
9
9
|
from whatsapp_cloud_api.client import WhatsAppClient
|
|
10
10
|
from whatsapp_cloud_api.errors import GraphApiError
|
|
11
11
|
|
|
12
|
-
BASE = "https://
|
|
12
|
+
BASE = "https://api.kapso.ai/meta/whatsapp/v24.0"
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class TestUrl:
|
|
@@ -28,11 +28,11 @@ class TestUrl:
|
|
|
28
28
|
|
|
29
29
|
def test_custom_base_url(self):
|
|
30
30
|
client = WhatsAppClient(access_token="tok", base_url="https://custom.api.com/")
|
|
31
|
-
assert client._url("path") == "https://custom.api.com/
|
|
31
|
+
assert client._url("path") == "https://custom.api.com/v24.0/path"
|
|
32
32
|
|
|
33
33
|
def test_custom_version(self):
|
|
34
34
|
client = WhatsAppClient(access_token="tok", graph_version="v22.0")
|
|
35
|
-
assert client._url("path") == "https://
|
|
35
|
+
assert client._url("path") == "https://api.kapso.ai/meta/whatsapp/v22.0/path"
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class TestRequest:
|
|
@@ -78,7 +78,7 @@ class TestRequest:
|
|
|
78
78
|
)
|
|
79
79
|
async with WhatsAppClient(access_token="my-token") as client:
|
|
80
80
|
await client.get("test")
|
|
81
|
-
assert route.calls[0].request.headers["
|
|
81
|
+
assert route.calls[0].request.headers["x-api-key"] == "my-token"
|
|
82
82
|
|
|
83
83
|
@respx.mock
|
|
84
84
|
async def test_post_with_json(self):
|
|
@@ -163,7 +163,7 @@ class TestFetchMethods:
|
|
|
163
163
|
async with WhatsAppClient(access_token="secret-tok") as client:
|
|
164
164
|
resp = await client.fetch_authenticated(url)
|
|
165
165
|
assert resp.content == b"bytes"
|
|
166
|
-
assert route.calls[0].request.headers["
|
|
166
|
+
assert route.calls[0].request.headers["x-api-key"] == "secret-tok"
|
|
167
167
|
|
|
168
168
|
|
|
169
169
|
class TestCachedProperties:
|
|
@@ -22,7 +22,7 @@ from whatsapp_cloud_api.resources.messages.models import (
|
|
|
22
22
|
)
|
|
23
23
|
from whatsapp_cloud_api.resources.messages.resource import MessagesResource
|
|
24
24
|
|
|
25
|
-
BASE = "https://
|
|
25
|
+
BASE = "https://api.kapso.ai/meta/whatsapp/v24.0"
|
|
26
26
|
PHONE = "1234567890"
|
|
27
27
|
MSG_URL = f"{BASE}/{PHONE}/messages"
|
|
28
28
|
|
|
@@ -8,7 +8,7 @@ import respx
|
|
|
8
8
|
from whatsapp_cloud_api.client import WhatsAppClient
|
|
9
9
|
from whatsapp_cloud_api.resources.media import MediaResource, MediaUploadInput
|
|
10
10
|
|
|
11
|
-
BASE = "https://
|
|
11
|
+
BASE = "https://api.kapso.ai/meta/whatsapp/v24.0"
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class TestUpload:
|
|
@@ -155,4 +155,4 @@ class TestDownload:
|
|
|
155
155
|
assert data == b"auth-bytes"
|
|
156
156
|
# With use_auth=True, the first call already has auth headers
|
|
157
157
|
req = cdn_route.calls[0].request
|
|
158
|
-
assert
|
|
158
|
+
assert req.headers.get("x-api-key") == "tok"
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_resources_templates.py
RENAMED
|
@@ -15,7 +15,7 @@ from whatsapp_cloud_api.resources.templates.models import (
|
|
|
15
15
|
)
|
|
16
16
|
from whatsapp_cloud_api.resources.templates.resource import TemplatesResource
|
|
17
17
|
|
|
18
|
-
BASE = "https://
|
|
18
|
+
BASE = "https://api.kapso.ai/meta/whatsapp/v24.0"
|
|
19
19
|
WABA = "waba123"
|
|
20
20
|
|
|
21
21
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/errors/retry.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/events/events.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/utils/__init__.py
RENAMED
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/src/whatsapp_cloud_api/utils/case.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_errors_graph_api_error.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{whatsapp_cloud_api_py-0.1.1 → whatsapp_cloud_api_py-0.2.0}/tests/test_webhooks_normalize.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|