wotp 1.0.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.
- wotp-1.0.0/.gitignore +80 -0
- wotp-1.0.0/PKG-INFO +146 -0
- wotp-1.0.0/README.md +122 -0
- wotp-1.0.0/pyproject.toml +38 -0
wotp-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# ─── OS & IDEs ─────────────────────────────────────────────────
|
|
2
|
+
.DS_Store
|
|
3
|
+
Thumbs.db
|
|
4
|
+
.idea/
|
|
5
|
+
.vscode/
|
|
6
|
+
*.swp
|
|
7
|
+
*.swo
|
|
8
|
+
|
|
9
|
+
# ─── Go ────────────────────────────────────────────────────────
|
|
10
|
+
bin/
|
|
11
|
+
obj/
|
|
12
|
+
*.exe
|
|
13
|
+
*.exe~
|
|
14
|
+
*.dll
|
|
15
|
+
*.so
|
|
16
|
+
*.dylib
|
|
17
|
+
*.test
|
|
18
|
+
*.out
|
|
19
|
+
vendor/
|
|
20
|
+
# Goreleaser & local builds
|
|
21
|
+
dist/
|
|
22
|
+
/wotp-cli-*
|
|
23
|
+
/wotp-core
|
|
24
|
+
/wotp
|
|
25
|
+
|
|
26
|
+
# ─── Node.js / React (Dashboard & TS SDK) ──────────────────────
|
|
27
|
+
node_modules/
|
|
28
|
+
npm-debug.log*
|
|
29
|
+
yarn-debug.log*
|
|
30
|
+
yarn-error.log*
|
|
31
|
+
.pnpm-debug.log*
|
|
32
|
+
# Vite output
|
|
33
|
+
core/dashboard/dist/
|
|
34
|
+
# TS SDK output
|
|
35
|
+
sdks/typescript/dist/
|
|
36
|
+
|
|
37
|
+
# ─── Python (Python SDK) ───────────────────────────────────────
|
|
38
|
+
__pycache__/
|
|
39
|
+
*.py[cod]
|
|
40
|
+
*$py.class
|
|
41
|
+
*.so
|
|
42
|
+
.Python
|
|
43
|
+
env/
|
|
44
|
+
build/
|
|
45
|
+
develop-eggs/
|
|
46
|
+
dist/
|
|
47
|
+
downloads/
|
|
48
|
+
eggs/
|
|
49
|
+
.eggs/
|
|
50
|
+
lib/
|
|
51
|
+
lib64/
|
|
52
|
+
parts/
|
|
53
|
+
sdist/
|
|
54
|
+
var/
|
|
55
|
+
wheels/
|
|
56
|
+
*.egg-info/
|
|
57
|
+
.installed.cfg
|
|
58
|
+
*.egg
|
|
59
|
+
venv/
|
|
60
|
+
.venv/
|
|
61
|
+
.pytest_cache/
|
|
62
|
+
|
|
63
|
+
# ─── Environment & Secrets ─────────────────────────────────────
|
|
64
|
+
.env
|
|
65
|
+
.env.*
|
|
66
|
+
!.env.example
|
|
67
|
+
*.pem
|
|
68
|
+
|
|
69
|
+
# ─── Project Specific Data ─────────────────────────────────────
|
|
70
|
+
# SQLite databases
|
|
71
|
+
*.db
|
|
72
|
+
*.sqlite
|
|
73
|
+
*.sqlite3
|
|
74
|
+
*.db-shm
|
|
75
|
+
*.db-wal
|
|
76
|
+
# Local CLI test folders
|
|
77
|
+
.wotp/
|
|
78
|
+
data/
|
|
79
|
+
|
|
80
|
+
inspiration/
|
wotp-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wotp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for Wotp — WhatsApp OTP, self-hosted, one command.
|
|
5
|
+
Project-URL: Homepage, https://github.com/wotp/wotp
|
|
6
|
+
Project-URL: Documentation, https://github.com/wotp/wotp
|
|
7
|
+
Project-URL: Repository, https://github.com/wotp/wotp
|
|
8
|
+
Author: Wotp
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: 2fa,auth,otp,verification,whatsapp,wotp
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: httpx>=0.27.0
|
|
22
|
+
Requires-Dist: pydantic>=2.0.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# wotp
|
|
26
|
+
|
|
27
|
+
Official Python SDK for **Wotp** — WhatsApp OTP, self-hosted, one command.
|
|
28
|
+
|
|
29
|
+
[](https://pypi.org/project/wotp/)
|
|
30
|
+
[](https://opensource.org/licenses/MIT)
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install wotp
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from wotp import create_client
|
|
42
|
+
|
|
43
|
+
client = create_client("http://localhost:54321", "wotp_anon_xxx")
|
|
44
|
+
|
|
45
|
+
# Send an OTP
|
|
46
|
+
resp = client.send_otp("+212600000000")
|
|
47
|
+
print(f"Token: {resp.token}, expires at: {resp.expires_at}")
|
|
48
|
+
|
|
49
|
+
# Verify the code entered by the user
|
|
50
|
+
result = client.verify_otp(resp.token, "483920")
|
|
51
|
+
|
|
52
|
+
# Send a text message
|
|
53
|
+
text_res = client.send_text("+212600000000", "Hello world")
|
|
54
|
+
|
|
55
|
+
# Send a media message
|
|
56
|
+
media_res = client.send_media("+212600000000", url="https://example.com/image.png")
|
|
57
|
+
|
|
58
|
+
# List chats
|
|
59
|
+
chats = client.get_chats()
|
|
60
|
+
|
|
61
|
+
print(f"Verified: {result.verified}")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API Reference
|
|
65
|
+
|
|
66
|
+
### `create_client(url, api_key, **options)`
|
|
67
|
+
|
|
68
|
+
Creates a new Wotp client instance.
|
|
69
|
+
|
|
70
|
+
| Parameter | Type | Description |
|
|
71
|
+
|-----------|------|-------------|
|
|
72
|
+
| `url` | `str` | Base URL of your Wotp instance |
|
|
73
|
+
| `api_key` | `str` | Your anon or service API key |
|
|
74
|
+
| `max_retries` | `int` | Max retries on transient errors (default: `3`) |
|
|
75
|
+
| `retry_delay` | `float` | Base delay in seconds between retries (default: `0.5`) |
|
|
76
|
+
| `timeout` | `float` | Request timeout in seconds (default: `10.0`) |
|
|
77
|
+
|
|
78
|
+
### `client.send_otp(phone)`
|
|
79
|
+
|
|
80
|
+
Send an OTP to the given phone number.
|
|
81
|
+
|
|
82
|
+
- **Parameters:** `phone` — E.164 formatted phone number
|
|
83
|
+
- **Returns:** `SendOTPResponse` with `.token` and `.expires_at`
|
|
84
|
+
- **Raises:** `RateLimitError`
|
|
85
|
+
|
|
86
|
+
### `client.verify_otp(token, code)`
|
|
87
|
+
|
|
88
|
+
Verify an OTP code against a previously issued token.
|
|
89
|
+
|
|
90
|
+
- **Parameters:** `token` — from `send_otp`, `code` — the user-entered code
|
|
91
|
+
- **Returns:** `VerifyOTPResponse` with `.verified`, `.phone`, `.attempts_remaining`
|
|
92
|
+
- **Raises:** `ExpiredTokenError`, `InvalidCodeError`
|
|
93
|
+
|
|
94
|
+
### `client.health()`
|
|
95
|
+
|
|
96
|
+
Check the health of the Wotp instance.
|
|
97
|
+
|
|
98
|
+
- **Returns:** `HealthResponse` with `.status`, `.phone`, `.uptime_seconds`
|
|
99
|
+
|
|
100
|
+
## Error Handling
|
|
101
|
+
|
|
102
|
+
The SDK raises typed exceptions for business failures:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from wotp import create_client, RateLimitError, ExpiredTokenError, InvalidCodeError
|
|
106
|
+
|
|
107
|
+
client = create_client("http://localhost:54321", "wotp_anon_xxx")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
result = client.verify_otp(token, code)
|
|
111
|
+
except RateLimitError as e:
|
|
112
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
113
|
+
except ExpiredTokenError:
|
|
114
|
+
print("Token expired — request a new OTP")
|
|
115
|
+
except InvalidCodeError as e:
|
|
116
|
+
print(f"Wrong code. {e.attempts_remaining} attempts left")
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
| Exception | When |
|
|
120
|
+
|-----------|------|
|
|
121
|
+
| `RateLimitError` | Phone/IP exceeded rate limit (HTTP 429) |
|
|
122
|
+
| `ExpiredTokenError` | Token has expired (HTTP 410 or `expired_token`) |
|
|
123
|
+
| `InvalidCodeError` | Wrong OTP code (HTTP 400 + `invalid_code`) |
|
|
124
|
+
| `WotpError` | Base class for all SDK errors |
|
|
125
|
+
|
|
126
|
+
## Context Manager
|
|
127
|
+
|
|
128
|
+
The client can be used as a context manager to ensure proper cleanup:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
with create_client("http://localhost:54321", "wotp_anon_xxx") as client:
|
|
132
|
+
resp = client.send_otp("+212600000000")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Auto-Retry
|
|
136
|
+
|
|
137
|
+
Transient errors (502, 503, 504, network timeouts) are automatically retried with exponential backoff. Business errors are **never** retried.
|
|
138
|
+
|
|
139
|
+
## Requirements
|
|
140
|
+
|
|
141
|
+
- Python ≥ 3.10
|
|
142
|
+
- A running Wotp instance
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT — see [LICENSE](../../LICENSE) for details.
|
wotp-1.0.0/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# wotp
|
|
2
|
+
|
|
3
|
+
Official Python SDK for **Wotp** — WhatsApp OTP, self-hosted, one command.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/wotp/)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install wotp
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from wotp import create_client
|
|
18
|
+
|
|
19
|
+
client = create_client("http://localhost:54321", "wotp_anon_xxx")
|
|
20
|
+
|
|
21
|
+
# Send an OTP
|
|
22
|
+
resp = client.send_otp("+212600000000")
|
|
23
|
+
print(f"Token: {resp.token}, expires at: {resp.expires_at}")
|
|
24
|
+
|
|
25
|
+
# Verify the code entered by the user
|
|
26
|
+
result = client.verify_otp(resp.token, "483920")
|
|
27
|
+
|
|
28
|
+
# Send a text message
|
|
29
|
+
text_res = client.send_text("+212600000000", "Hello world")
|
|
30
|
+
|
|
31
|
+
# Send a media message
|
|
32
|
+
media_res = client.send_media("+212600000000", url="https://example.com/image.png")
|
|
33
|
+
|
|
34
|
+
# List chats
|
|
35
|
+
chats = client.get_chats()
|
|
36
|
+
|
|
37
|
+
print(f"Verified: {result.verified}")
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## API Reference
|
|
41
|
+
|
|
42
|
+
### `create_client(url, api_key, **options)`
|
|
43
|
+
|
|
44
|
+
Creates a new Wotp client instance.
|
|
45
|
+
|
|
46
|
+
| Parameter | Type | Description |
|
|
47
|
+
|-----------|------|-------------|
|
|
48
|
+
| `url` | `str` | Base URL of your Wotp instance |
|
|
49
|
+
| `api_key` | `str` | Your anon or service API key |
|
|
50
|
+
| `max_retries` | `int` | Max retries on transient errors (default: `3`) |
|
|
51
|
+
| `retry_delay` | `float` | Base delay in seconds between retries (default: `0.5`) |
|
|
52
|
+
| `timeout` | `float` | Request timeout in seconds (default: `10.0`) |
|
|
53
|
+
|
|
54
|
+
### `client.send_otp(phone)`
|
|
55
|
+
|
|
56
|
+
Send an OTP to the given phone number.
|
|
57
|
+
|
|
58
|
+
- **Parameters:** `phone` — E.164 formatted phone number
|
|
59
|
+
- **Returns:** `SendOTPResponse` with `.token` and `.expires_at`
|
|
60
|
+
- **Raises:** `RateLimitError`
|
|
61
|
+
|
|
62
|
+
### `client.verify_otp(token, code)`
|
|
63
|
+
|
|
64
|
+
Verify an OTP code against a previously issued token.
|
|
65
|
+
|
|
66
|
+
- **Parameters:** `token` — from `send_otp`, `code` — the user-entered code
|
|
67
|
+
- **Returns:** `VerifyOTPResponse` with `.verified`, `.phone`, `.attempts_remaining`
|
|
68
|
+
- **Raises:** `ExpiredTokenError`, `InvalidCodeError`
|
|
69
|
+
|
|
70
|
+
### `client.health()`
|
|
71
|
+
|
|
72
|
+
Check the health of the Wotp instance.
|
|
73
|
+
|
|
74
|
+
- **Returns:** `HealthResponse` with `.status`, `.phone`, `.uptime_seconds`
|
|
75
|
+
|
|
76
|
+
## Error Handling
|
|
77
|
+
|
|
78
|
+
The SDK raises typed exceptions for business failures:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from wotp import create_client, RateLimitError, ExpiredTokenError, InvalidCodeError
|
|
82
|
+
|
|
83
|
+
client = create_client("http://localhost:54321", "wotp_anon_xxx")
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
result = client.verify_otp(token, code)
|
|
87
|
+
except RateLimitError as e:
|
|
88
|
+
print(f"Rate limited. Retry after {e.retry_after}s")
|
|
89
|
+
except ExpiredTokenError:
|
|
90
|
+
print("Token expired — request a new OTP")
|
|
91
|
+
except InvalidCodeError as e:
|
|
92
|
+
print(f"Wrong code. {e.attempts_remaining} attempts left")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
| Exception | When |
|
|
96
|
+
|-----------|------|
|
|
97
|
+
| `RateLimitError` | Phone/IP exceeded rate limit (HTTP 429) |
|
|
98
|
+
| `ExpiredTokenError` | Token has expired (HTTP 410 or `expired_token`) |
|
|
99
|
+
| `InvalidCodeError` | Wrong OTP code (HTTP 400 + `invalid_code`) |
|
|
100
|
+
| `WotpError` | Base class for all SDK errors |
|
|
101
|
+
|
|
102
|
+
## Context Manager
|
|
103
|
+
|
|
104
|
+
The client can be used as a context manager to ensure proper cleanup:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
with create_client("http://localhost:54321", "wotp_anon_xxx") as client:
|
|
108
|
+
resp = client.send_otp("+212600000000")
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Auto-Retry
|
|
112
|
+
|
|
113
|
+
Transient errors (502, 503, 504, network timeouts) are automatically retried with exponential backoff. Business errors are **never** retried.
|
|
114
|
+
|
|
115
|
+
## Requirements
|
|
116
|
+
|
|
117
|
+
- Python ≥ 3.10
|
|
118
|
+
- A running Wotp instance
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT — see [LICENSE](../../LICENSE) for details.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "wotp"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Python SDK for Wotp — WhatsApp OTP, self-hosted, one command."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Wotp" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["wotp", "otp", "whatsapp", "verification", "2fa", "auth"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.27.0",
|
|
29
|
+
"pydantic>=2.0.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/wotp/wotp"
|
|
34
|
+
Documentation = "https://github.com/wotp/wotp"
|
|
35
|
+
Repository = "https://github.com/wotp/wotp"
|
|
36
|
+
|
|
37
|
+
[tool.hatch.build.targets.wheel]
|
|
38
|
+
packages = ["wotp"]
|