kibonet-python-sdk 0.1.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.
- kibonet_python_sdk-0.1.0/LICENSE +21 -0
- kibonet_python_sdk-0.1.0/PKG-INFO +269 -0
- kibonet_python_sdk-0.1.0/README.md +232 -0
- kibonet_python_sdk-0.1.0/pyproject.toml +66 -0
- kibonet_python_sdk-0.1.0/setup.cfg +4 -0
- kibonet_python_sdk-0.1.0/src/kibonet_python_sdk.egg-info/PKG-INFO +269 -0
- kibonet_python_sdk-0.1.0/src/kibonet_python_sdk.egg-info/SOURCES.txt +19 -0
- kibonet_python_sdk-0.1.0/src/kibonet_python_sdk.egg-info/dependency_links.txt +1 -0
- kibonet_python_sdk-0.1.0/src/kibonet_python_sdk.egg-info/requires.txt +12 -0
- kibonet_python_sdk-0.1.0/src/kibonet_python_sdk.egg-info/top_level.txt +1 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/__init__.py +61 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/async_client.py +238 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/client.py +279 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/exceptions.py +47 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/utils/__init__.py +0 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/utils/async_http_client.py +108 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/utils/http_client.py +108 -0
- kibonet_python_sdk-0.1.0/src/kibonet_sdk/utils/validators.py +92 -0
- kibonet_python_sdk-0.1.0/tests/test_async_client.py +194 -0
- kibonet_python_sdk-0.1.0/tests/test_client.py +198 -0
- kibonet_python_sdk-0.1.0/tests/test_validators.py +83 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Jackson Linus
|
|
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,269 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kibonet-python-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simple, secure Python SDK for the KiboNet SMS Gateway
|
|
5
|
+
Author-email: Jackson Linus <jacksonlinus95@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/JAXPARROW/kibonet-python-sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/JAXPARROW/kibonet-python-sdk.git
|
|
9
|
+
Project-URL: Issues, https://github.com/JAXPARROW/kibonet-python-sdk/issues
|
|
10
|
+
Keywords: kibonet,sms,tanzania,africa,messaging,httpx,async
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Communications :: Telephony
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Framework :: AsyncIO
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: httpx>=0.24.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
|
|
29
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
30
|
+
Requires-Dist: respx>=0.20.0; extra == "dev"
|
|
31
|
+
Requires-Dist: mypy>=1.0.0; extra == "dev"
|
|
32
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
33
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
34
|
+
Requires-Dist: build>=1.0.0; extra == "dev"
|
|
35
|
+
Requires-Dist: twine>=4.0.0; extra == "dev"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# KiboNet Python SDK
|
|
39
|
+
|
|
40
|
+
Simple, secure Python SDK for the [KiboNet SMS Gateway](https://sms.kibonet.co.tz).
|
|
41
|
+
Supports both **synchronous** and **async** usage via [httpx](https://www.python-httpx.org/).
|
|
42
|
+
|
|
43
|
+
[](https://pypi.org/project/kibonet-python-sdk/)
|
|
44
|
+
[](https://pypi.org/project/kibonet-python-sdk/)
|
|
45
|
+
[](https://pypi.org/project/kibonet-python-sdk/)
|
|
46
|
+
|
|
47
|
+
## Features
|
|
48
|
+
|
|
49
|
+
- **Send SMS** to one or multiple recipients (comma-separated contacts)
|
|
50
|
+
- **Delivery reports** retrieved by `shootId`
|
|
51
|
+
- **Balance check** for your SMS credits
|
|
52
|
+
- **Health check** — `is_healthy()` never raises, perfect for monitoring probes
|
|
53
|
+
- **Async support** — full `async`/`await` via `AsyncKiboNetClient`
|
|
54
|
+
- **httpx** under the hood — connection pooling, automatic retries, TLS verification
|
|
55
|
+
- **Phone number validation** — auto-converts `0XXXXXXXXX` → `255XXXXXXXXX`
|
|
56
|
+
- **Typed exceptions** — catch exactly what you need
|
|
57
|
+
- **Context manager** support (both sync and async)
|
|
58
|
+
- **No credentials in code** — load from environment variables
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install kibonet-python-sdk
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
### Synchronous
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
from kibonet_sdk import KiboNetClient
|
|
72
|
+
|
|
73
|
+
with KiboNetClient(api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET") as client:
|
|
74
|
+
# Health check
|
|
75
|
+
if client.is_healthy():
|
|
76
|
+
print("API is up!")
|
|
77
|
+
|
|
78
|
+
# Send SMS
|
|
79
|
+
resp = client.send_sms(
|
|
80
|
+
sender_id="MyBrand",
|
|
81
|
+
message="Hello from KiboNet!",
|
|
82
|
+
contacts=["255712345678", "255798765432"],
|
|
83
|
+
delivery_report_url="https://myserver.com/delivery-callback", # optional
|
|
84
|
+
)
|
|
85
|
+
print(f"Shoot ID: {resp['data']['shootId']}")
|
|
86
|
+
print(f"Valid: {resp['data']['validContacts']}")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Asynchronous
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import asyncio
|
|
93
|
+
from kibonet_sdk import AsyncKiboNetClient
|
|
94
|
+
|
|
95
|
+
async def main():
|
|
96
|
+
async with AsyncKiboNetClient(api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET") as client:
|
|
97
|
+
if await client.is_healthy():
|
|
98
|
+
resp = await client.send_sms(
|
|
99
|
+
sender_id="MyBrand",
|
|
100
|
+
message="Hello async!",
|
|
101
|
+
contacts="255712345678",
|
|
102
|
+
)
|
|
103
|
+
print(resp["data"]["shootId"])
|
|
104
|
+
|
|
105
|
+
asyncio.run(main())
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## All Methods
|
|
109
|
+
|
|
110
|
+
Both `KiboNetClient` and `AsyncKiboNetClient` expose the same interface:
|
|
111
|
+
|
|
112
|
+
### `send_sms`
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
resp = client.send_sms(
|
|
116
|
+
sender_id="MyBrand", # Sender name shown to recipient
|
|
117
|
+
message="Your OTP is 1234", # SMS text
|
|
118
|
+
contacts=[ # Single string, list, or CSV string
|
|
119
|
+
"255712345678",
|
|
120
|
+
"255798765432",
|
|
121
|
+
],
|
|
122
|
+
delivery_report_url="https://myserver.com/callback", # optional
|
|
123
|
+
message_type="text", # currently only "text" is supported
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Response
|
|
127
|
+
print(resp["data"]["shootId"]) # Use this for delivery reports
|
|
128
|
+
print(resp["data"]["validContacts"]) # int
|
|
129
|
+
print(resp["data"]["invalidContacts"]) # int
|
|
130
|
+
print(resp["data"]["duplicatedContacts"])
|
|
131
|
+
print(resp["data"]["messageSize"]) # character count
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### `get_delivery_report`
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
report = client.get_delivery_report("abcd1234-5678-efgh-9012-ijklmnopqrst")
|
|
138
|
+
|
|
139
|
+
for record in report["data"]:
|
|
140
|
+
print(record["mobile"]) # 255712345678
|
|
141
|
+
print(record["status"]) # Delivered / Failed / Buffered
|
|
142
|
+
print(record["statusCode"]) # 000
|
|
143
|
+
print(record["sentAt"]) # "07-02-2024 07:35:08"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `get_balance`
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
result = client.get_balance()
|
|
150
|
+
print(result["data"]["totalSms"]) # e.g. 30
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `is_healthy`
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
if client.is_healthy():
|
|
157
|
+
print("API is reachable and credentials are valid")
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Phone Number Formats
|
|
161
|
+
|
|
162
|
+
The SDK automatically normalises phone numbers:
|
|
163
|
+
|
|
164
|
+
| Input format | Stored as |
|
|
165
|
+
|--------------------|----------------|
|
|
166
|
+
| `0712345678` | `255712345678` |
|
|
167
|
+
| `+255712345678` | `255712345678` |
|
|
168
|
+
| `255712345678` | `255712345678` |
|
|
169
|
+
|
|
170
|
+
## Error Handling
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from kibonet_sdk import KiboNetClient
|
|
174
|
+
from kibonet_sdk.exceptions import (
|
|
175
|
+
KiboNetAuthenticationError,
|
|
176
|
+
KiboNetValidationError,
|
|
177
|
+
KiboNetRateLimitError,
|
|
178
|
+
KiboNetInsufficientBalanceError,
|
|
179
|
+
KiboNetConnectionError,
|
|
180
|
+
KiboNetServerError,
|
|
181
|
+
KiboNetAPIError,
|
|
182
|
+
KiboNetError, # base — catch everything SDK-related
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
client = KiboNetClient(api_key="...", api_secret="...")
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
client.send_sms(sender_id="X", message="Hi", contacts="255712345678")
|
|
189
|
+
except KiboNetAuthenticationError:
|
|
190
|
+
print("Bad credentials")
|
|
191
|
+
except KiboNetValidationError as e:
|
|
192
|
+
print(f"Input error: {e}")
|
|
193
|
+
except KiboNetRateLimitError:
|
|
194
|
+
print("Slow down — rate limited")
|
|
195
|
+
except KiboNetConnectionError as e:
|
|
196
|
+
print(f"Network error: {e}")
|
|
197
|
+
except KiboNetServerError as e:
|
|
198
|
+
print(f"KiboNet server problem: {e}")
|
|
199
|
+
except KiboNetAPIError as e:
|
|
200
|
+
print(f"API error (code={e.code}): {e}")
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Environment Variables
|
|
204
|
+
|
|
205
|
+
Keep credentials out of source code:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
export KIBONET_API_KEY="your_api_key"
|
|
209
|
+
export KIBONET_API_SECRET="your_api_secret"
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
import os
|
|
214
|
+
from kibonet_sdk import KiboNetClient
|
|
215
|
+
|
|
216
|
+
client = KiboNetClient(
|
|
217
|
+
api_key=os.environ["KIBONET_API_KEY"],
|
|
218
|
+
api_secret=os.environ["KIBONET_API_SECRET"],
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Constructor Options
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
KiboNetClient(
|
|
226
|
+
api_key: str, # Required — from KiboNet portal
|
|
227
|
+
api_secret: str, # Required — from KiboNet portal
|
|
228
|
+
timeout: float = 30.0, # Per-request timeout in seconds
|
|
229
|
+
max_retries: int = 3, # Automatic retries on transient failures
|
|
230
|
+
logger=None, # Optional logging.Logger
|
|
231
|
+
)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
`AsyncKiboNetClient` takes the same parameters.
|
|
235
|
+
|
|
236
|
+
## Logging
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
import logging
|
|
240
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
241
|
+
|
|
242
|
+
client = KiboNetClient(api_key="...", api_secret="...")
|
|
243
|
+
# All HTTP calls are logged at DEBUG level
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Development
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
git clone https://github.com/JAXPARROW/kibonet-python-sdk.git
|
|
250
|
+
cd kibonet-python-sdk
|
|
251
|
+
pip install -e ".[dev]"
|
|
252
|
+
|
|
253
|
+
# Run tests
|
|
254
|
+
pytest
|
|
255
|
+
|
|
256
|
+
# Run with coverage
|
|
257
|
+
pytest --cov=kibonet_sdk --cov-report=term-missing
|
|
258
|
+
|
|
259
|
+
# Build
|
|
260
|
+
python -m build
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## License
|
|
264
|
+
|
|
265
|
+
MIT — see [LICENSE](LICENSE).
|
|
266
|
+
|
|
267
|
+
## Contributors
|
|
268
|
+
|
|
269
|
+
- **Jackson Linus** — Creator and maintainer
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# KiboNet Python SDK
|
|
2
|
+
|
|
3
|
+
Simple, secure Python SDK for the [KiboNet SMS Gateway](https://sms.kibonet.co.tz).
|
|
4
|
+
Supports both **synchronous** and **async** usage via [httpx](https://www.python-httpx.org/).
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/kibonet-python-sdk/)
|
|
7
|
+
[](https://pypi.org/project/kibonet-python-sdk/)
|
|
8
|
+
[](https://pypi.org/project/kibonet-python-sdk/)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- **Send SMS** to one or multiple recipients (comma-separated contacts)
|
|
13
|
+
- **Delivery reports** retrieved by `shootId`
|
|
14
|
+
- **Balance check** for your SMS credits
|
|
15
|
+
- **Health check** — `is_healthy()` never raises, perfect for monitoring probes
|
|
16
|
+
- **Async support** — full `async`/`await` via `AsyncKiboNetClient`
|
|
17
|
+
- **httpx** under the hood — connection pooling, automatic retries, TLS verification
|
|
18
|
+
- **Phone number validation** — auto-converts `0XXXXXXXXX` → `255XXXXXXXXX`
|
|
19
|
+
- **Typed exceptions** — catch exactly what you need
|
|
20
|
+
- **Context manager** support (both sync and async)
|
|
21
|
+
- **No credentials in code** — load from environment variables
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install kibonet-python-sdk
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
### Synchronous
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from kibonet_sdk import KiboNetClient
|
|
35
|
+
|
|
36
|
+
with KiboNetClient(api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET") as client:
|
|
37
|
+
# Health check
|
|
38
|
+
if client.is_healthy():
|
|
39
|
+
print("API is up!")
|
|
40
|
+
|
|
41
|
+
# Send SMS
|
|
42
|
+
resp = client.send_sms(
|
|
43
|
+
sender_id="MyBrand",
|
|
44
|
+
message="Hello from KiboNet!",
|
|
45
|
+
contacts=["255712345678", "255798765432"],
|
|
46
|
+
delivery_report_url="https://myserver.com/delivery-callback", # optional
|
|
47
|
+
)
|
|
48
|
+
print(f"Shoot ID: {resp['data']['shootId']}")
|
|
49
|
+
print(f"Valid: {resp['data']['validContacts']}")
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Asynchronous
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
import asyncio
|
|
56
|
+
from kibonet_sdk import AsyncKiboNetClient
|
|
57
|
+
|
|
58
|
+
async def main():
|
|
59
|
+
async with AsyncKiboNetClient(api_key="YOUR_API_KEY", api_secret="YOUR_API_SECRET") as client:
|
|
60
|
+
if await client.is_healthy():
|
|
61
|
+
resp = await client.send_sms(
|
|
62
|
+
sender_id="MyBrand",
|
|
63
|
+
message="Hello async!",
|
|
64
|
+
contacts="255712345678",
|
|
65
|
+
)
|
|
66
|
+
print(resp["data"]["shootId"])
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## All Methods
|
|
72
|
+
|
|
73
|
+
Both `KiboNetClient` and `AsyncKiboNetClient` expose the same interface:
|
|
74
|
+
|
|
75
|
+
### `send_sms`
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
resp = client.send_sms(
|
|
79
|
+
sender_id="MyBrand", # Sender name shown to recipient
|
|
80
|
+
message="Your OTP is 1234", # SMS text
|
|
81
|
+
contacts=[ # Single string, list, or CSV string
|
|
82
|
+
"255712345678",
|
|
83
|
+
"255798765432",
|
|
84
|
+
],
|
|
85
|
+
delivery_report_url="https://myserver.com/callback", # optional
|
|
86
|
+
message_type="text", # currently only "text" is supported
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Response
|
|
90
|
+
print(resp["data"]["shootId"]) # Use this for delivery reports
|
|
91
|
+
print(resp["data"]["validContacts"]) # int
|
|
92
|
+
print(resp["data"]["invalidContacts"]) # int
|
|
93
|
+
print(resp["data"]["duplicatedContacts"])
|
|
94
|
+
print(resp["data"]["messageSize"]) # character count
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `get_delivery_report`
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
report = client.get_delivery_report("abcd1234-5678-efgh-9012-ijklmnopqrst")
|
|
101
|
+
|
|
102
|
+
for record in report["data"]:
|
|
103
|
+
print(record["mobile"]) # 255712345678
|
|
104
|
+
print(record["status"]) # Delivered / Failed / Buffered
|
|
105
|
+
print(record["statusCode"]) # 000
|
|
106
|
+
print(record["sentAt"]) # "07-02-2024 07:35:08"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### `get_balance`
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
result = client.get_balance()
|
|
113
|
+
print(result["data"]["totalSms"]) # e.g. 30
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `is_healthy`
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
if client.is_healthy():
|
|
120
|
+
print("API is reachable and credentials are valid")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Phone Number Formats
|
|
124
|
+
|
|
125
|
+
The SDK automatically normalises phone numbers:
|
|
126
|
+
|
|
127
|
+
| Input format | Stored as |
|
|
128
|
+
|--------------------|----------------|
|
|
129
|
+
| `0712345678` | `255712345678` |
|
|
130
|
+
| `+255712345678` | `255712345678` |
|
|
131
|
+
| `255712345678` | `255712345678` |
|
|
132
|
+
|
|
133
|
+
## Error Handling
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from kibonet_sdk import KiboNetClient
|
|
137
|
+
from kibonet_sdk.exceptions import (
|
|
138
|
+
KiboNetAuthenticationError,
|
|
139
|
+
KiboNetValidationError,
|
|
140
|
+
KiboNetRateLimitError,
|
|
141
|
+
KiboNetInsufficientBalanceError,
|
|
142
|
+
KiboNetConnectionError,
|
|
143
|
+
KiboNetServerError,
|
|
144
|
+
KiboNetAPIError,
|
|
145
|
+
KiboNetError, # base — catch everything SDK-related
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
client = KiboNetClient(api_key="...", api_secret="...")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
client.send_sms(sender_id="X", message="Hi", contacts="255712345678")
|
|
152
|
+
except KiboNetAuthenticationError:
|
|
153
|
+
print("Bad credentials")
|
|
154
|
+
except KiboNetValidationError as e:
|
|
155
|
+
print(f"Input error: {e}")
|
|
156
|
+
except KiboNetRateLimitError:
|
|
157
|
+
print("Slow down — rate limited")
|
|
158
|
+
except KiboNetConnectionError as e:
|
|
159
|
+
print(f"Network error: {e}")
|
|
160
|
+
except KiboNetServerError as e:
|
|
161
|
+
print(f"KiboNet server problem: {e}")
|
|
162
|
+
except KiboNetAPIError as e:
|
|
163
|
+
print(f"API error (code={e.code}): {e}")
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Environment Variables
|
|
167
|
+
|
|
168
|
+
Keep credentials out of source code:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
export KIBONET_API_KEY="your_api_key"
|
|
172
|
+
export KIBONET_API_SECRET="your_api_secret"
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```python
|
|
176
|
+
import os
|
|
177
|
+
from kibonet_sdk import KiboNetClient
|
|
178
|
+
|
|
179
|
+
client = KiboNetClient(
|
|
180
|
+
api_key=os.environ["KIBONET_API_KEY"],
|
|
181
|
+
api_secret=os.environ["KIBONET_API_SECRET"],
|
|
182
|
+
)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Constructor Options
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
KiboNetClient(
|
|
189
|
+
api_key: str, # Required — from KiboNet portal
|
|
190
|
+
api_secret: str, # Required — from KiboNet portal
|
|
191
|
+
timeout: float = 30.0, # Per-request timeout in seconds
|
|
192
|
+
max_retries: int = 3, # Automatic retries on transient failures
|
|
193
|
+
logger=None, # Optional logging.Logger
|
|
194
|
+
)
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
`AsyncKiboNetClient` takes the same parameters.
|
|
198
|
+
|
|
199
|
+
## Logging
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
import logging
|
|
203
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
204
|
+
|
|
205
|
+
client = KiboNetClient(api_key="...", api_secret="...")
|
|
206
|
+
# All HTTP calls are logged at DEBUG level
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Development
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
git clone https://github.com/JAXPARROW/kibonet-python-sdk.git
|
|
213
|
+
cd kibonet-python-sdk
|
|
214
|
+
pip install -e ".[dev]"
|
|
215
|
+
|
|
216
|
+
# Run tests
|
|
217
|
+
pytest
|
|
218
|
+
|
|
219
|
+
# Run with coverage
|
|
220
|
+
pytest --cov=kibonet_sdk --cov-report=term-missing
|
|
221
|
+
|
|
222
|
+
# Build
|
|
223
|
+
python -m build
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT — see [LICENSE](LICENSE).
|
|
229
|
+
|
|
230
|
+
## Contributors
|
|
231
|
+
|
|
232
|
+
- **Jackson Linus** — Creator and maintainer
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kibonet-python-sdk"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Simple, secure Python SDK for the KiboNet SMS Gateway"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "Jackson Linus", email = "jacksonlinus95@gmail.com"}
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 3 - Alpha",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.8",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Communications :: Telephony",
|
|
24
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
25
|
+
"Framework :: AsyncIO",
|
|
26
|
+
]
|
|
27
|
+
keywords = ["kibonet", "sms", "tanzania", "africa", "messaging", "httpx", "async"]
|
|
28
|
+
dependencies = [
|
|
29
|
+
"httpx>=0.24.0",
|
|
30
|
+
]
|
|
31
|
+
requires-python = ">=3.8"
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/JAXPARROW/kibonet-python-sdk"
|
|
35
|
+
Repository = "https://github.com/JAXPARROW/kibonet-python-sdk.git"
|
|
36
|
+
Issues = "https://github.com/JAXPARROW/kibonet-python-sdk/issues"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=7.0.0",
|
|
41
|
+
"pytest-asyncio>=0.21.0",
|
|
42
|
+
"pytest-cov>=4.0.0",
|
|
43
|
+
"respx>=0.20.0",
|
|
44
|
+
"mypy>=1.0.0",
|
|
45
|
+
"black>=23.0.0",
|
|
46
|
+
"ruff>=0.1.0",
|
|
47
|
+
"build>=1.0.0",
|
|
48
|
+
"twine>=4.0.0",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[tool.setuptools.packages.find]
|
|
52
|
+
where = ["src"]
|
|
53
|
+
include = ["kibonet_sdk*"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
asyncio_mode = "auto"
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
|
|
59
|
+
[tool.mypy]
|
|
60
|
+
python_version = "3.8"
|
|
61
|
+
strict = true
|
|
62
|
+
ignore_missing_imports = true
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 100
|
|
66
|
+
target-version = "py38"
|