pynextsms 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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Your Name
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,531 @@
1
+ Metadata-Version: 2.4
2
+ Name: pynextsms
3
+ Version: 1.0.0
4
+ Summary: Python SDK for the messaging-service.co.tz SMS API v2
5
+ Author-email: Ronald Gosso <ronaldgosso@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/ronaldgosso/pynextsms
8
+ Project-URL: Documentation, https://github.com/ronaldgosso/pynextsms#readme
9
+ Project-URL: Repository, https://github.com/ronaldgosso/pynextsms
10
+ Project-URL: Bug Tracker, https://github.com/ronaldgosso/pynextsms/issues
11
+ Project-URL: Changelog, https://github.com/ronaldgosso/pynextsms/blob/main/CHANGELOG.md
12
+ Keywords: sms,tanzania,messaging,api,sdk,pynextsms
13
+ Classifier: Development Status :: 5 - Production/Stable
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Topic :: Communications :: Telephony
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: requests>=2.28
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=7.4; extra == "dev"
31
+ Requires-Dist: pytest-cov>=4.1; extra == "dev"
32
+ Requires-Dist: black>=23.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.1; extra == "dev"
34
+ Requires-Dist: mypy>=1.5; extra == "dev"
35
+ Requires-Dist: types-requests; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ <div align="center">
39
+
40
+ # pynextsms 🇹🇿
41
+
42
+ **Python SDK for the [NEXT SMS Tanzania](https://app.nextsms.co.tz) SMS API v2**
43
+
44
+ [![PyPI version](https://badge.fury.io/py/pynextsms.svg)](https://pypi.org/project/pynextsms/)
45
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/)
46
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
47
+ [![Tests](https://github.com/ronaldgosso/pynextsms/actions/workflows/test.yml/badge.svg)](https://github.com/ronaldgosso/pynextsms/actions)
48
+ [![Coverage](https://img.shields.io/badge/coverage-95%25-brightgreen)](https://github.com/ronaldgosso/pynextsms)
49
+ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
50
+
51
+ </div>
52
+
53
+ ---
54
+
55
+ ## Table of Contents
56
+
57
+ - [Documentation](#documentation)
58
+ - [Requirements](#requirements)
59
+ - [Features](#features)
60
+ - [Installation](#installation)
61
+ - [Quick Start](#quick-start)
62
+ - [Authentication](#authentication)
63
+ - [Usage Guide](#usage-guide)
64
+ - [Send a Single SMS](#1-send-a-single-sms)
65
+ - [Broadcast to Multiple Recipients](#2-broadcast-to-multiple-recipients)
66
+ - [Send Different Messages (Bulk)](#3-send-different-messages-to-different-people)
67
+ - [Schedule an SMS](#4-schedule-an-sms)
68
+ - [Scheduled + Recurring SMS](#5-scheduled--recurring-sms)
69
+ - [Flash SMS](#6-flash-sms)
70
+ - [Context Manager](#7-context-manager)
71
+ - [Environment Variables](#8-environment-variables-production)
72
+ - [Response Objects](#response-objects)
73
+ - [Error Handling](#error-handling)
74
+ - [Logging](#logging)
75
+ - [Running Locally](#running-locally)
76
+ - [Contributing](#contributing)
77
+ - [Changelog](#changelog)
78
+ - [License](#license)
79
+
80
+ ---
81
+
82
+ ## Documentation
83
+
84
+ - [Official Next SMS API Documentation](https://documenter.getpostman.com/view/1679195/2sAYkDP1XN#fe8eaa5c-5987-4a3c-bb6b-98d497c5b3b0)
85
+
86
+ ## Requirements
87
+
88
+ - [Next SMS Registration](https://app.nextsms.co.tz/register)
89
+ - Python 3.8+
90
+ - requests >= 2.28
91
+
92
+ ## Features
93
+
94
+ | Feature | Support |
95
+ |---|---|
96
+ | Send SMS to a single recipient | ✅ |
97
+ | Broadcast same message to multiple recipients | ✅ |
98
+ | Send different messages to different recipients | ✅ |
99
+ | Schedule SMS (one-time) | ✅ |
100
+ | Schedule SMS (recurring: hourly / daily / weekly / monthly) | ✅ |
101
+ | Flash (Class-0) SMS | ✅ |
102
+ | Auto-retry on transient 5xx errors | ✅ |
103
+ | Typed responses — no raw dict-wrangling | ✅ |
104
+ | Full type annotations + `py.typed` (mypy strict) | ✅ |
105
+ | Environment-variable credentials (12-factor ready) | ✅ |
106
+ | Zero dependencies beyond `requests` | ✅ |
107
+ | Python 3.8 – 3.12 | ✅ |
108
+
109
+ ---
110
+
111
+ ## Installation
112
+
113
+ ```bash
114
+ pip install pynextsms
115
+ ```
116
+ ---
117
+
118
+ ## Quick Start
119
+
120
+ ```python
121
+ from pynextsms import SMSClient
122
+
123
+ client = SMSClient(token="your_bearer_token", sender_id="YOURBRAND")
124
+
125
+ resp = client.sms.send("255763930052", "Hello from PyNextSMS!")
126
+ print(resp)
127
+ # SMSResponse(✅ sent, http=200, ref='a3f1c2d4')
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Authentication
133
+
134
+ Generate your **Bearer Token** from your [Next SMS API Documentation](https://documenter.getpostman.com/view/1679195/2sAYkDP1XN#authorization:~:text=header%3A%20application/json-,Authorization,-Bearer%20Authentication%20).
135
+
136
+ ### Option 1 — pass credentials directly *(quick scripts, notebooks)*
137
+
138
+ ```python
139
+ client = SMSClient(token="your_bearer_token", sender_id="YOURBRAND")
140
+ ```
141
+
142
+ ### Option 2 — environment variables *(recommended for production)*
143
+
144
+ ```bash
145
+ export PYNEXTSMS_TOKEN="your_bearer_token"
146
+ export PYNEXTSMS_SENDER_ID="YOURBRAND"
147
+ ```
148
+
149
+ ```python
150
+ client = SMSClient() # reads from environment automatically
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Usage Guide
156
+
157
+ ### 1. Send a Single SMS
158
+
159
+ ```python
160
+ resp = client.sms.send("255763930052", "Hello, Ronald!")
161
+
162
+ if resp.successful:
163
+ print(f"✅ Sent! message_id={resp.message_id}, ref={resp.reference}")
164
+ else:
165
+ print(f"❌ Failed: {resp.raw}")
166
+ ```
167
+
168
+ ### 2. Broadcast to Multiple Recipients
169
+
170
+ Send the **same message** to many people in a single API call:
171
+
172
+ ```python
173
+ resp = client.sms.send(
174
+ to=["255763930052", "255627350020", "255622999999"],
175
+ text="Hello everyone — you are now registered!",
176
+ reference="campaign_june", # optional tracking ref
177
+ )
178
+ print(f"✅ Broadcast successful: {resp.successful}")
179
+ ```
180
+
181
+ ### 3. Send Different Messages to Different People
182
+
183
+ ```python
184
+ from pynextsms import MessageRecipient
185
+
186
+ resp = client.sms.send_bulk(
187
+ messages=[
188
+ MessageRecipient(to="255763930052", text="Hello Daniel, welcome!"),
189
+ MessageRecipient(to="255627350020", text="Hello Patricia, welcome!"),
190
+ MessageRecipient(to="255622999999", text="Hello Precious, welcome!"),
191
+ ],
192
+ reference="onboarding_batch_001",
193
+ )
194
+ print(f"✅ Sent {resp.total} personalised messages")
195
+ ```
196
+
197
+ Each `MessageRecipient` can also override the sender ID:
198
+
199
+ ```python
200
+ MessageRecipient(to="255763930052", text="Hi!", sender_id="CUSTOM")
201
+ ```
202
+
203
+ ### 4. Schedule an SMS
204
+
205
+ ```python
206
+ from datetime import date, time
207
+ from pynextsms import ScheduleOptions
208
+
209
+ opts = ScheduleOptions(
210
+ send_date=date(2025, 6, 1),
211
+ send_time=time(9, 0), # 09:00, 24-hour clock
212
+ )
213
+
214
+ resp = client.sms.schedule(
215
+ to="255763930052",
216
+ text="Good morning! Your session starts in 1 hour.",
217
+ options=opts,
218
+ )
219
+ print(f"✅ Scheduled: {resp.successful}")
220
+ ```
221
+
222
+ ### 5. Scheduled + Recurring SMS
223
+
224
+ ```python
225
+ from pynextsms import ScheduleOptions, RepeatInterval
226
+
227
+ opts = ScheduleOptions(
228
+ send_date = date(2025, 6, 1),
229
+ send_time = time(8, 0),
230
+ repeat = RepeatInterval.DAILY,
231
+ start_date = date(2025, 6, 1),
232
+ end_date = date(2025, 6, 30),
233
+ )
234
+
235
+ resp = client.sms.schedule(
236
+ to="255763930052",
237
+ text="Daily reminder: drink water 💧",
238
+ options=opts,
239
+ )
240
+ ```
241
+
242
+ Available repeat intervals:
243
+
244
+ | Value | Constant |
245
+ |---|---|
246
+ | `"hourly"` | `RepeatInterval.HOURLY` |
247
+ | `"daily"` | `RepeatInterval.DAILY` |
248
+ | `"weekly"` | `RepeatInterval.WEEKLY` |
249
+ | `"monthly"` | `RepeatInterval.MONTHLY` |
250
+
251
+ ### 6. Flash SMS
252
+
253
+ Pass `flash=True` to any `send` or `send_bulk` call:
254
+
255
+ ```python
256
+ resp = client.sms.send("255712345678", "Urgent alert!", flash=True)
257
+ ```
258
+
259
+ ### 7. Context Manager
260
+
261
+ The client implements `__enter__` / `__exit__` so it can be used as a context
262
+ manager — the HTTP connection pool is automatically released on exit:
263
+
264
+ ```python
265
+ with SMSClient(token="...", sender_id="BRAND") as client:
266
+ client.sms.send("255712345678", "Hello!")
267
+ # connection pool closed here
268
+ ```
269
+
270
+ ### 8. Environment Variables *(Production)*
271
+
272
+ | Variable | Description |
273
+ |---|---|
274
+ | `PYNEXTSMS_TOKEN` | Bearer token (required if not passed to constructor) |
275
+ | `PYNEXTSMS_SENDER_ID` | Default sender ID |
276
+
277
+ ```bash
278
+ # .env file (use python-dotenv or similar)
279
+ PYNEXTSMS_TOKEN=your_bearer_token
280
+ PYNEXTSMS_SENDER_ID=YOURBRAND
281
+ ```
282
+
283
+ ```python
284
+ from dotenv import load_dotenv
285
+ load_dotenv()
286
+
287
+ from pynextsms import SMSClient
288
+ client = SMSClient()
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Response Objects
294
+
295
+ ### `SMSResponse`
296
+
297
+ Returned by `sms.send()` and `sms.schedule()`.
298
+
299
+ | Attribute | Type | Description |
300
+ |---|---|---|
301
+ | `successful` | `bool` | `True` when HTTP 2xx |
302
+ | `status_code` | `int` | Raw HTTP status |
303
+ | `message_id` | `str \| None` | ID assigned by API |
304
+ | `reference` | `str \| None` | Tracking reference |
305
+ | `raw` | `dict` | Full JSON response body |
306
+
307
+ ```python
308
+ resp = client.sms.send("255763930052", "Hello!")
309
+
310
+ print(resp.successful) # True
311
+ print(resp.message_id) # "msg_abc123"
312
+ print(resp.to_dict()) # plain dict
313
+ print(resp.to_json()) # JSON string
314
+ ```
315
+
316
+ ### `BulkSMSResponse`
317
+
318
+ Returned by `sms.send_bulk()`. Same as `SMSResponse` plus:
319
+
320
+ | Attribute | Type | Description |
321
+ |---|---|---|
322
+ | `total` | `int` | Number of messages in the batch |
323
+
324
+ ```python
325
+ resp = client.sms.send_bulk([...])
326
+ print(f"Accepted {resp.total} messages")
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Error Handling
332
+
333
+ All exceptions inherit from `PyNextSMSError`:
334
+
335
+ ```python
336
+ from pynextsms import (
337
+ PyNextSMSError,
338
+ AuthenticationError,
339
+ ValidationError,
340
+ RateLimitError,
341
+ APIError,
342
+ )
343
+
344
+ try:
345
+ resp = client.sms.send("255712345678", "Hello!")
346
+
347
+ except AuthenticationError:
348
+ # Bad or missing bearer token
349
+ print("Check your PYNEXTSMS_TOKEN.")
350
+
351
+ except ValidationError as e:
352
+ # Bad input caught *before* the HTTP call
353
+ print(f"Input error: {e}")
354
+
355
+ except RateLimitError as e:
356
+ # HTTP 429
357
+ import time
358
+ print(f"Rate limited — retrying in {e.retry_after}s")
359
+ time.sleep(e.retry_after or 5)
360
+
361
+ except APIError as e:
362
+ # Any other non-2xx response
363
+ print(f"API error (HTTP {e.status_code}): {e}")
364
+
365
+ except PyNextSMSError as e:
366
+ # Catch-all for any other SDK error
367
+ print(f"SDK error: {e}")
368
+ ```
369
+
370
+ ---
371
+
372
+ ## Logging
373
+
374
+ PyNextSMS uses Python's standard `logging` under the `pynextsms` logger.
375
+
376
+ ```python
377
+ import logging
378
+
379
+ # Show all SDK log messages (DEBUG and above)
380
+ logging.basicConfig(level=logging.DEBUG)
381
+
382
+ # Or configure just the pynextsms logger
383
+ logger = logging.getLogger("pynextsms")
384
+ logger.setLevel(logging.INFO)
385
+ handler = logging.StreamHandler()
386
+ handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
387
+ logger.addHandler(handler)
388
+ ```
389
+
390
+ ---
391
+
392
+ ## Running Locally
393
+
394
+ ### 1. Clone the repo
395
+
396
+ ```bash
397
+ git clone https://github.com/ronaldgosso/pynextsms.git
398
+ cd pynextsms
399
+ ```
400
+
401
+ ### 2. Create a virtual environment
402
+
403
+ ```bash
404
+ python -m venv .venv
405
+ source .venv/bin/activate # Linux / macOS
406
+ # .venv\Scripts\activate # Windows
407
+ ```
408
+
409
+ ### 3. Install in editable mode with dev extras
410
+
411
+ ```bash
412
+ pip install -e ".[dev]"
413
+ ```
414
+
415
+ ### 4. Set your credentials
416
+
417
+ ```bash
418
+ export PYNEXTSMS_TOKEN="your_bearer_token"
419
+ export PYNEXTSMS_SENDER_ID="YOURBRAND"
420
+ ```
421
+
422
+ ### 5. Try it out
423
+
424
+ ```bash
425
+ python - << 'EOF'
426
+ from pynextsms import SMSClient
427
+
428
+ with SMSClient() as client:
429
+ resp = client.sms.send("255763930052", "Hello from local dev!")
430
+ print(resp)
431
+ EOF
432
+ ```
433
+
434
+ ### 6. Run the test suite
435
+
436
+ ```bash
437
+ # All tests, verbose
438
+ pytest
439
+
440
+ # With coverage report
441
+ pytest --cov=pynextsms --cov-report=term-missing
442
+
443
+ # Specific test class
444
+ pytest tests/ -v -k "TestSend"
445
+ ```
446
+
447
+ ### 7. Lint & type-check
448
+
449
+ ```bash
450
+ ruff check pynextsms/ # linter
451
+ black --check pynextsms/ # formatter check
452
+ mypy pynextsms/ # strict type checking
453
+ ```
454
+
455
+ ---
456
+
457
+ ## Contributing
458
+
459
+ Contributions are welcome — bug reports, feature requests, documentation improvements, and pull requests all appreciated.
460
+
461
+ ### Workflow
462
+
463
+ 1. **Fork** the repository on GitHub.
464
+ 2. **Clone** your fork:
465
+ ```bash
466
+ git clone https://github.com/YOUR_USERNAME/pynextsms.git
467
+ cd pynextsms
468
+ ```
469
+ 3. **Create a branch** for your change:
470
+ ```bash
471
+ git checkout -b feat/my-new-feature
472
+ # or
473
+ git checkout -b fix/the-bug-description
474
+ ```
475
+ 4. **Install dev dependencies:**
476
+ ```bash
477
+ python -m venv .venv && source .venv/bin/activate
478
+ pip install -e ".[dev]"
479
+ ```
480
+ 5. **Make your changes**, then make sure all of these pass:
481
+ ```bash
482
+ pytest # all tests pass
483
+ ruff check pynextsms/ # no lint errors
484
+ black pynextsms/ tests/ # code is formatted
485
+ mypy pynextsms/ # no type errors
486
+ ```
487
+ 6. **Commit** with a clear message:
488
+ ```bash
489
+ git commit -m "feat: add support for sending MMS"
490
+ ```
491
+ 7. **Push** and open a **Pull Request** against `main`.
492
+
493
+ ### Commit message conventions
494
+
495
+ | Prefix | When to use |
496
+ |---|---|
497
+ | `feat:` | New feature |
498
+ | `fix:` | Bug fix |
499
+ | `docs:` | Documentation change |
500
+ | `test:` | Adding or updating tests |
501
+ | `refactor:` | Code change with no behaviour change |
502
+ | `chore:` | Tooling, CI, config |
503
+
504
+ ### Reporting bugs
505
+
506
+ Open an issue at [github.com/ronaldgosso/pynextsms/issues](https://github.com/ronaldgosso/pynextsms/issues) and include:
507
+ - Python version (`python --version`)
508
+ - pynextsms version (`pip show pynextsms`)
509
+ - Minimal code that reproduces the issue
510
+ - Full traceback
511
+
512
+ ---
513
+
514
+ ## Changelog
515
+
516
+ ### v1.0.0 — 2025-06-01
517
+
518
+ - Initial release
519
+ - `sms.send()` — single and broadcast sends
520
+ - `sms.send_bulk()` — personalised bulk sends
521
+ - `sms.schedule()` — one-time and recurring scheduled SMS
522
+ - Flash SMS support
523
+ - Automatic retries on 5xx errors
524
+ - Full type annotations, `py.typed` marker
525
+ - Comprehensive test suite (95% coverage)
526
+
527
+ ---
528
+
529
+ ## License
530
+
531
+ MIT © Ronald Gosso — see [LICENSE](LICENSE).