zapcore-sdk 0.1.1__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.
- zapcore_sdk-0.1.1/.gitignore +8 -0
- zapcore_sdk-0.1.1/LICENSE +21 -0
- zapcore_sdk-0.1.1/PKG-INFO +145 -0
- zapcore_sdk-0.1.1/README.md +128 -0
- zapcore_sdk-0.1.1/pyproject.toml +31 -0
- zapcore_sdk-0.1.1/src/zapcore/__init__.py +17 -0
- zapcore_sdk-0.1.1/src/zapcore/_client.py +423 -0
- zapcore_sdk-0.1.1/src/zapcore/_errors.py +37 -0
- zapcore_sdk-0.1.1/src/zapcore/_generated.py +4208 -0
- zapcore_sdk-0.1.1/src/zapcore/py.typed +0 -0
- zapcore_sdk-0.1.1/src/zapcore/webhook.py +123 -0
- zapcore_sdk-0.1.1/tests/test_client.py +293 -0
- zapcore_sdk-0.1.1/tests/test_webhook.py +60 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Nicolas Gudiño and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: zapcore-sdk
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: SDK oficial da API REST v2 do zapCore (WhatsApp): tipado, zero dependência, retry que respeita 429/Retry-After, idempotência automática e verificação de webhook HMAC
|
|
5
|
+
Project-URL: Homepage, https://zapcore.app
|
|
6
|
+
Project-URL: Source, https://github.com/adeiltonpessini/zapcore/tree/main/sdks/python
|
|
7
|
+
Author: Adeilton Pessini
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: hmac,sdk,webhook,whatsapp,whatsapp-api,zapcore
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Communications :: Chat
|
|
14
|
+
Classifier: Typing :: Typed
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# zapcore-sdk (Python)
|
|
19
|
+
|
|
20
|
+
SDK oficial da API REST v2 do [zapCore](https://zapcore.app) (WhatsApp).
|
|
21
|
+
Python ≥ 3.9, **zero dependência** (só biblioteca padrão), tipado (`TypedDict` + `py.typed`).
|
|
22
|
+
|
|
23
|
+
> **Ainda não publicado no PyPI.** Não rode `pip install zapcore-sdk`: o nome ainda não é nosso
|
|
24
|
+
> no registro. Por enquanto instale desta pasta, a partir de um clone do repositório:
|
|
25
|
+
>
|
|
26
|
+
> ```sh
|
|
27
|
+
> pip install ./sdks/python
|
|
28
|
+
> ```
|
|
29
|
+
|
|
30
|
+
## Início rápido
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from zapcore import Client, ZapcoreError
|
|
34
|
+
|
|
35
|
+
zap = Client("https://api.seudominio.com", token="TOKEN_DA_INSTANCIA") # host sem /api/v2
|
|
36
|
+
|
|
37
|
+
zap.send_text("+55 (27) 99999-9999", "Olá!") # pontuação é limpa
|
|
38
|
+
zap.send_image("5527999999999", open("foto.jpg", "rb").read(), "Seu pedido")
|
|
39
|
+
zap.send_image("5527999999999", "https://site/foto.jpg") # o SDK baixa
|
|
40
|
+
zap.send_buttons("5527999999999", "Confirma o horário?", ["Sim", "Remarcar"])
|
|
41
|
+
zap.send_list("5527999999999", "Escolha o serviço", "Ver opções",
|
|
42
|
+
[{"title": "Cabelo", "rows": [{"RowId": "corte", "title": "Corte", "desc": "R$ 50"}]}])
|
|
43
|
+
zap.send_poll("120363000000000000@g.us", "Melhor dia?", ["Sexta", "Sábado"])
|
|
44
|
+
|
|
45
|
+
zap.status()["connected"]
|
|
46
|
+
zap.get_qr()["QRCode"] # data URL
|
|
47
|
+
zap.pair_phone("5527999999999")["LinkingCode"]
|
|
48
|
+
zap.create_group("Equipe", ["5527999990000"])
|
|
49
|
+
zap.check_numbers(["5527999999999"])[0]["IsInWhatsapp"]
|
|
50
|
+
|
|
51
|
+
for msg in zap.iter_history("5527999999999@s.whatsapp.net"): # pagina sozinho
|
|
52
|
+
print(msg["timestamp"], msg.get("text_content"))
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Toda rota existe como método gerado: `zap.post_chat_send_carousel(...)`,
|
|
56
|
+
`zap.get_group_list(query={"limit": 50})`… (235 operações). Para `meta`, status e headers use
|
|
57
|
+
`zap.request("GET", "/api/v2/session/limits")`.
|
|
58
|
+
|
|
59
|
+
## Votar em enquete, GIF, link, fila assíncrona e busca de conversas
|
|
60
|
+
|
|
61
|
+
Métodos gerados (nome = `operationId` da spec, em snake_case):
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
# Votar numa enquete recebida (poll_msg_id do histórico local; options exatas)
|
|
65
|
+
zap.post_chat_send_pollvote({"poll_msg_id": "3EB0...", "options": ["Sexta"]})
|
|
66
|
+
# GIF (mp4 até 16 MB; URL ou data URL) e link com cartão de prévia
|
|
67
|
+
zap.post_chat_send_gif({"Phone": "5527999999999", "Gif": "https://site/anim.mp4", "Caption": "Oi"})
|
|
68
|
+
zap.post_chat_send_link({"Phone": "5527999999999", "Url": "https://site/oferta", "Body": "Veja:"})
|
|
69
|
+
|
|
70
|
+
# Fila assíncrona: enfileirar (async=true na query), listar, consultar, cancelar
|
|
71
|
+
zap.post_chat_send_text({"Phone": "5527999999999", "Body": "oi"},
|
|
72
|
+
query={"async": True, "track_source": "crm", "track_id": "pedido-42"})
|
|
73
|
+
zap.get_chat_send_async(query={"limit": 50})
|
|
74
|
+
zap.get_chat_send_async_id(id)
|
|
75
|
+
zap.delete_chat_send_async_id(id) # 409 se já saiu
|
|
76
|
+
zap.post_chat_send_async_cancel({"track_source": "crm", "track_id": "pedido-42"}) # em massa
|
|
77
|
+
|
|
78
|
+
# Busca de conversas com filtros ("counts" só vem na 1ª página: sem cursor e offset 0)
|
|
79
|
+
r = zap.get_chat_search(query={"type": "group", "unread": True, "limit": 50})
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Os filtros da listagem da fila (`status`, `track_source`, `track_id`, `since`) são suportados
|
|
83
|
+
([API.md](../../API.md)), na query de `get_chat_send_async`.
|
|
84
|
+
|
|
85
|
+
## Erros, retry e idempotência
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
try:
|
|
89
|
+
zap.send_text("5527999999999", "oi", idempotency_key=pedido_id)
|
|
90
|
+
except ZapcoreError as e:
|
|
91
|
+
if e.code == "rate_limited": # case por code, nunca pela mensagem
|
|
92
|
+
print("tente em", e.retry_after, "s")
|
|
93
|
+
print(e.status, e.field, e.request_id)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`429` espera o `Retry-After` (até `max_retry_wait`, padrão 60 s); `502/503/504` e falha de
|
|
97
|
+
rede re-tentam só GET e envios. Envios levam `Idempotency-Key` automática, a mesma em todos
|
|
98
|
+
os retries (`auto_idempotency=False` desliga). Opções: `Client(..., max_retries=3, timeout=15,
|
|
99
|
+
headers={...}, transport=meu_transporte)`.
|
|
100
|
+
|
|
101
|
+
**Redirect nunca é seguido**: o transporte padrão não tem `HTTPRedirectHandler`, e um 3xx vira
|
|
102
|
+
`ZapcoreError(code="redirect_refused")`. Seguir levaria o header `token` para o `Location`.
|
|
103
|
+
|
|
104
|
+
## Webhook (HMAC)
|
|
105
|
+
|
|
106
|
+
> **Verificação ESTRITA por padrão.** Só passa entrega com `x-hmac-signature-v2` +
|
|
107
|
+
> `x-webhook-timestamp` dentro de 5 min (anti-replay). Entrega só com a v1 (`x-hmac-signature`,
|
|
108
|
+
> sem carimbo) é **recusada**: ela não expira, então quem capturar uma pode reenviá-la para
|
|
109
|
+
> sempre. Motor antigo que ainda não manda a v2? Ligue ``allow_v1=True``, conscientemente.
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from zapcore import parse_webhook, WebhookVerificationError
|
|
113
|
+
|
|
114
|
+
# Flask: request.get_data() é o corpo CRU, não use request.json
|
|
115
|
+
@app.post("/zapcore")
|
|
116
|
+
def webhook():
|
|
117
|
+
try:
|
|
118
|
+
ev = parse_webhook(request.get_data(), request.headers, os.environ["ZAPCORE_HMAC_SECRET"])
|
|
119
|
+
except WebhookVerificationError:
|
|
120
|
+
return "", 401
|
|
121
|
+
print(ev["type"], ev.get("event"))
|
|
122
|
+
return "", 200
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`verify_webhook(...)` devolve `bool` e nunca levanta por header malicioso; `tolerance_seconds`
|
|
126
|
+
ajusta a janela. Corpo assinado que não é objeto JSON levanta `WebhookVerificationError`.
|
|
127
|
+
|
|
128
|
+
## Desenvolvimento
|
|
129
|
+
|
|
130
|
+
```sh
|
|
131
|
+
PYTHONPATH=src python -m unittest discover -s tests
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`src/zapcore/_generated.py` é gerado por `node ../gerar.mjs`, não edite.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## English
|
|
139
|
+
|
|
140
|
+
Official Python SDK (≥ 3.9, stdlib only) for the zapCore WhatsApp REST API v2. One generated
|
|
141
|
+
method per operation (snake_case of the `operationId`) plus helpers (`send_text`, `send_image`,
|
|
142
|
+
`send_buttons`, `send_list`, `send_poll`, `get_qr`, `pair_phone`, `status`, `create_group`,
|
|
143
|
+
`check_numbers`, `history`/`iter_history`). Webhook verification is strict by default (v1-only deliveries need `allow_v1=True`) and redirects are never followed. Retries honor `429 Retry-After`, send routes get an
|
|
144
|
+
automatic idempotency key reused across retries, errors raise `ZapcoreError` with a stable
|
|
145
|
+
`code`, and `verify_webhook`/`parse_webhook` check the HMAC-SHA256 signature.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# zapcore-sdk (Python)
|
|
2
|
+
|
|
3
|
+
SDK oficial da API REST v2 do [zapCore](https://zapcore.app) (WhatsApp).
|
|
4
|
+
Python ≥ 3.9, **zero dependência** (só biblioteca padrão), tipado (`TypedDict` + `py.typed`).
|
|
5
|
+
|
|
6
|
+
> **Ainda não publicado no PyPI.** Não rode `pip install zapcore-sdk`: o nome ainda não é nosso
|
|
7
|
+
> no registro. Por enquanto instale desta pasta, a partir de um clone do repositório:
|
|
8
|
+
>
|
|
9
|
+
> ```sh
|
|
10
|
+
> pip install ./sdks/python
|
|
11
|
+
> ```
|
|
12
|
+
|
|
13
|
+
## Início rápido
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from zapcore import Client, ZapcoreError
|
|
17
|
+
|
|
18
|
+
zap = Client("https://api.seudominio.com", token="TOKEN_DA_INSTANCIA") # host sem /api/v2
|
|
19
|
+
|
|
20
|
+
zap.send_text("+55 (27) 99999-9999", "Olá!") # pontuação é limpa
|
|
21
|
+
zap.send_image("5527999999999", open("foto.jpg", "rb").read(), "Seu pedido")
|
|
22
|
+
zap.send_image("5527999999999", "https://site/foto.jpg") # o SDK baixa
|
|
23
|
+
zap.send_buttons("5527999999999", "Confirma o horário?", ["Sim", "Remarcar"])
|
|
24
|
+
zap.send_list("5527999999999", "Escolha o serviço", "Ver opções",
|
|
25
|
+
[{"title": "Cabelo", "rows": [{"RowId": "corte", "title": "Corte", "desc": "R$ 50"}]}])
|
|
26
|
+
zap.send_poll("120363000000000000@g.us", "Melhor dia?", ["Sexta", "Sábado"])
|
|
27
|
+
|
|
28
|
+
zap.status()["connected"]
|
|
29
|
+
zap.get_qr()["QRCode"] # data URL
|
|
30
|
+
zap.pair_phone("5527999999999")["LinkingCode"]
|
|
31
|
+
zap.create_group("Equipe", ["5527999990000"])
|
|
32
|
+
zap.check_numbers(["5527999999999"])[0]["IsInWhatsapp"]
|
|
33
|
+
|
|
34
|
+
for msg in zap.iter_history("5527999999999@s.whatsapp.net"): # pagina sozinho
|
|
35
|
+
print(msg["timestamp"], msg.get("text_content"))
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Toda rota existe como método gerado: `zap.post_chat_send_carousel(...)`,
|
|
39
|
+
`zap.get_group_list(query={"limit": 50})`… (235 operações). Para `meta`, status e headers use
|
|
40
|
+
`zap.request("GET", "/api/v2/session/limits")`.
|
|
41
|
+
|
|
42
|
+
## Votar em enquete, GIF, link, fila assíncrona e busca de conversas
|
|
43
|
+
|
|
44
|
+
Métodos gerados (nome = `operationId` da spec, em snake_case):
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# Votar numa enquete recebida (poll_msg_id do histórico local; options exatas)
|
|
48
|
+
zap.post_chat_send_pollvote({"poll_msg_id": "3EB0...", "options": ["Sexta"]})
|
|
49
|
+
# GIF (mp4 até 16 MB; URL ou data URL) e link com cartão de prévia
|
|
50
|
+
zap.post_chat_send_gif({"Phone": "5527999999999", "Gif": "https://site/anim.mp4", "Caption": "Oi"})
|
|
51
|
+
zap.post_chat_send_link({"Phone": "5527999999999", "Url": "https://site/oferta", "Body": "Veja:"})
|
|
52
|
+
|
|
53
|
+
# Fila assíncrona: enfileirar (async=true na query), listar, consultar, cancelar
|
|
54
|
+
zap.post_chat_send_text({"Phone": "5527999999999", "Body": "oi"},
|
|
55
|
+
query={"async": True, "track_source": "crm", "track_id": "pedido-42"})
|
|
56
|
+
zap.get_chat_send_async(query={"limit": 50})
|
|
57
|
+
zap.get_chat_send_async_id(id)
|
|
58
|
+
zap.delete_chat_send_async_id(id) # 409 se já saiu
|
|
59
|
+
zap.post_chat_send_async_cancel({"track_source": "crm", "track_id": "pedido-42"}) # em massa
|
|
60
|
+
|
|
61
|
+
# Busca de conversas com filtros ("counts" só vem na 1ª página: sem cursor e offset 0)
|
|
62
|
+
r = zap.get_chat_search(query={"type": "group", "unread": True, "limit": 50})
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Os filtros da listagem da fila (`status`, `track_source`, `track_id`, `since`) são suportados
|
|
66
|
+
([API.md](../../API.md)), na query de `get_chat_send_async`.
|
|
67
|
+
|
|
68
|
+
## Erros, retry e idempotência
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
try:
|
|
72
|
+
zap.send_text("5527999999999", "oi", idempotency_key=pedido_id)
|
|
73
|
+
except ZapcoreError as e:
|
|
74
|
+
if e.code == "rate_limited": # case por code, nunca pela mensagem
|
|
75
|
+
print("tente em", e.retry_after, "s")
|
|
76
|
+
print(e.status, e.field, e.request_id)
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`429` espera o `Retry-After` (até `max_retry_wait`, padrão 60 s); `502/503/504` e falha de
|
|
80
|
+
rede re-tentam só GET e envios. Envios levam `Idempotency-Key` automática, a mesma em todos
|
|
81
|
+
os retries (`auto_idempotency=False` desliga). Opções: `Client(..., max_retries=3, timeout=15,
|
|
82
|
+
headers={...}, transport=meu_transporte)`.
|
|
83
|
+
|
|
84
|
+
**Redirect nunca é seguido**: o transporte padrão não tem `HTTPRedirectHandler`, e um 3xx vira
|
|
85
|
+
`ZapcoreError(code="redirect_refused")`. Seguir levaria o header `token` para o `Location`.
|
|
86
|
+
|
|
87
|
+
## Webhook (HMAC)
|
|
88
|
+
|
|
89
|
+
> **Verificação ESTRITA por padrão.** Só passa entrega com `x-hmac-signature-v2` +
|
|
90
|
+
> `x-webhook-timestamp` dentro de 5 min (anti-replay). Entrega só com a v1 (`x-hmac-signature`,
|
|
91
|
+
> sem carimbo) é **recusada**: ela não expira, então quem capturar uma pode reenviá-la para
|
|
92
|
+
> sempre. Motor antigo que ainda não manda a v2? Ligue ``allow_v1=True``, conscientemente.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
from zapcore import parse_webhook, WebhookVerificationError
|
|
96
|
+
|
|
97
|
+
# Flask: request.get_data() é o corpo CRU, não use request.json
|
|
98
|
+
@app.post("/zapcore")
|
|
99
|
+
def webhook():
|
|
100
|
+
try:
|
|
101
|
+
ev = parse_webhook(request.get_data(), request.headers, os.environ["ZAPCORE_HMAC_SECRET"])
|
|
102
|
+
except WebhookVerificationError:
|
|
103
|
+
return "", 401
|
|
104
|
+
print(ev["type"], ev.get("event"))
|
|
105
|
+
return "", 200
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`verify_webhook(...)` devolve `bool` e nunca levanta por header malicioso; `tolerance_seconds`
|
|
109
|
+
ajusta a janela. Corpo assinado que não é objeto JSON levanta `WebhookVerificationError`.
|
|
110
|
+
|
|
111
|
+
## Desenvolvimento
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
PYTHONPATH=src python -m unittest discover -s tests
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`src/zapcore/_generated.py` é gerado por `node ../gerar.mjs`, não edite.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## English
|
|
122
|
+
|
|
123
|
+
Official Python SDK (≥ 3.9, stdlib only) for the zapCore WhatsApp REST API v2. One generated
|
|
124
|
+
method per operation (snake_case of the `operationId`) plus helpers (`send_text`, `send_image`,
|
|
125
|
+
`send_buttons`, `send_list`, `send_poll`, `get_qr`, `pair_phone`, `status`, `create_group`,
|
|
126
|
+
`check_numbers`, `history`/`iter_history`). Webhook verification is strict by default (v1-only deliveries need `allow_v1=True`) and redirects are never followed. Retries honor `429 Retry-After`, send routes get an
|
|
127
|
+
automatic idempotency key reused across retries, errors raise `ZapcoreError` with a stable
|
|
128
|
+
`code`, and `verify_webhook`/`parse_webhook` check the HMAC-SHA256 signature.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "zapcore-sdk"
|
|
7
|
+
version = "0.1.1"
|
|
8
|
+
description = "SDK oficial da API REST v2 do zapCore (WhatsApp): tipado, zero dependência, retry que respeita 429/Retry-After, idempotência automática e verificação de webhook HMAC"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Adeilton Pessini" }]
|
|
14
|
+
keywords = ["zapcore", "whatsapp", "whatsapp-api", "sdk", "webhook", "hmac"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
"Typing :: Typed",
|
|
19
|
+
"Topic :: Communications :: Chat",
|
|
20
|
+
]
|
|
21
|
+
dependencies = []
|
|
22
|
+
|
|
23
|
+
[project.urls]
|
|
24
|
+
Homepage = "https://zapcore.app"
|
|
25
|
+
Source = "https://github.com/adeiltonpessini/zapcore/tree/main/sdks/python"
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.sdist]
|
|
28
|
+
include = ["src/zapcore", "tests", "README.md", "LICENSE"]
|
|
29
|
+
|
|
30
|
+
[tool.hatch.build.targets.wheel]
|
|
31
|
+
packages = ["src/zapcore"]
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""SDK oficial da API REST v2 do zapCore (WhatsApp).
|
|
2
|
+
|
|
3
|
+
from zapcore import Client
|
|
4
|
+
zap = Client("https://api.seudominio.com", token="TOKEN_DA_INSTANCIA")
|
|
5
|
+
zap.send_text("5527999999999", "Olá!")
|
|
6
|
+
"""
|
|
7
|
+
from ._client import ApiResponse, Client, Page, SDK_VERSION, Transport, destino, parse_retry_after, urllib_transport
|
|
8
|
+
from ._errors import WebhookVerificationError, ZapcoreError
|
|
9
|
+
from ._generated import API_VERSION, SPEC_SHA256
|
|
10
|
+
from .webhook import parse_webhook, verify_webhook
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"Client", "ApiResponse", "Page", "Transport", "ZapcoreError", "WebhookVerificationError",
|
|
14
|
+
"verify_webhook", "parse_webhook", "destino", "parse_retry_after", "urllib_transport",
|
|
15
|
+
"SDK_VERSION", "API_VERSION", "SPEC_SHA256",
|
|
16
|
+
]
|
|
17
|
+
__version__ = SDK_VERSION
|