gosms-python 1.0.3__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.
- gosms_python-1.0.3/.github/workflows/release.yml +87 -0
- gosms_python-1.0.3/.gitignore +37 -0
- gosms_python-1.0.3/.releaserc.json +12 -0
- gosms_python-1.0.3/PKG-INFO +276 -0
- gosms_python-1.0.3/README.md +240 -0
- gosms_python-1.0.3/pyproject.toml +62 -0
- gosms_python-1.0.3/src/gosms/__init__.py +13 -0
- gosms_python-1.0.3/src/gosms/_base.py +69 -0
- gosms_python-1.0.3/src/gosms/async_client.py +167 -0
- gosms_python-1.0.3/src/gosms/client.py +144 -0
- gosms_python-1.0.3/src/gosms/django.py +80 -0
- gosms_python-1.0.3/src/gosms/exceptions.py +33 -0
- gosms_python-1.0.3/src/gosms/py.typed +0 -0
- gosms_python-1.0.3/src/gosms/types.py +173 -0
- gosms_python-1.0.3/tests/conftest.py +96 -0
- gosms_python-1.0.3/tests/test_async_client.py +167 -0
- gosms_python-1.0.3/tests/test_client.py +345 -0
- gosms_python-1.0.3/tests/test_django.py +58 -0
- gosms_python-1.0.3/tests/test_types.py +112 -0
- gosms_python-1.0.3/tests/test_validation.py +80 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
22
|
+
uses: actions/setup-python@v5
|
|
23
|
+
with:
|
|
24
|
+
python-version: ${{ matrix.python-version }}
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: pip install -e ".[dev,async]"
|
|
28
|
+
|
|
29
|
+
- name: Lint
|
|
30
|
+
run: ruff check src/ tests/
|
|
31
|
+
|
|
32
|
+
- name: Test
|
|
33
|
+
run: pytest --tb=short -q
|
|
34
|
+
|
|
35
|
+
release:
|
|
36
|
+
needs: test
|
|
37
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
outputs:
|
|
40
|
+
released: ${{ steps.semantic.outputs.new_release_published }}
|
|
41
|
+
version: ${{ steps.semantic.outputs.new_release_version }}
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
with:
|
|
45
|
+
fetch-depth: 0
|
|
46
|
+
|
|
47
|
+
- name: Semantic Release
|
|
48
|
+
id: semantic
|
|
49
|
+
uses: cycjimmy/semantic-release-action@v4
|
|
50
|
+
with:
|
|
51
|
+
extra_plugins: |
|
|
52
|
+
conventional-changelog-conventionalcommits@8
|
|
53
|
+
semantic_version: 24
|
|
54
|
+
env:
|
|
55
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
56
|
+
|
|
57
|
+
publish:
|
|
58
|
+
needs: release
|
|
59
|
+
if: needs.release.outputs.released == 'true'
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v4
|
|
63
|
+
with:
|
|
64
|
+
ref: main
|
|
65
|
+
|
|
66
|
+
- name: Set up Python
|
|
67
|
+
uses: actions/setup-python@v5
|
|
68
|
+
with:
|
|
69
|
+
python-version: "3.13"
|
|
70
|
+
|
|
71
|
+
- name: Set version from release
|
|
72
|
+
run: |
|
|
73
|
+
VERSION=${{ needs.release.outputs.version }}
|
|
74
|
+
sed -i "s/version = \".*\"/version = \"$VERSION\"/" pyproject.toml
|
|
75
|
+
sed -i "s/__version__ = \".*\"/__version__ = \"$VERSION\"/" src/gosms/__init__.py
|
|
76
|
+
|
|
77
|
+
- name: Install build tools
|
|
78
|
+
run: pip install build twine
|
|
79
|
+
|
|
80
|
+
- name: Build package
|
|
81
|
+
run: python -m build
|
|
82
|
+
|
|
83
|
+
- name: Publish to PyPI
|
|
84
|
+
run: twine upload dist/*
|
|
85
|
+
env:
|
|
86
|
+
TWINE_USERNAME: __token__
|
|
87
|
+
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
*.egg-info/
|
|
11
|
+
*.egg
|
|
12
|
+
pip-wheel-metadata/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
venv/
|
|
17
|
+
env/
|
|
18
|
+
|
|
19
|
+
# IDE
|
|
20
|
+
.idea/
|
|
21
|
+
.vscode/
|
|
22
|
+
*.swp
|
|
23
|
+
*.swo
|
|
24
|
+
|
|
25
|
+
# Testing
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
.tox/
|
|
30
|
+
|
|
31
|
+
# Linting
|
|
32
|
+
.ruff_cache/
|
|
33
|
+
.mypy_cache/
|
|
34
|
+
|
|
35
|
+
# OS
|
|
36
|
+
.DS_Store
|
|
37
|
+
Thumbs.db
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gosms-python
|
|
3
|
+
Version: 1.0.3
|
|
4
|
+
Summary: Official Python SDK for GoSMS.GE SMS Gateway
|
|
5
|
+
Project-URL: Homepage, https://gosms.ge
|
|
6
|
+
Project-URL: Repository, https://github.com/gosms-ge/gosmsge-python
|
|
7
|
+
Project-URL: Issues, https://github.com/gosms-ge/gosmsge-python/issues
|
|
8
|
+
Author-email: "GoSMS.GE" <info@gosms.ge>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: gosms,otp,sms,sms-api,sms-gateway,sms-sdk
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
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: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Communications
|
|
21
|
+
Classifier: Typing :: Typed
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Requires-Dist: requests>=2.28.0
|
|
24
|
+
Provides-Extra: async
|
|
25
|
+
Requires-Dist: httpx>=0.24.0; extra == 'async'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: build; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-httpx>=0.21; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: responses>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.1; extra == 'dev'
|
|
33
|
+
Provides-Extra: django
|
|
34
|
+
Requires-Dist: django>=3.2; extra == 'django'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# GoSMS.GE Python SDK
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/gosms-python/)
|
|
40
|
+
[](https://pypi.org/project/gosms-python/)
|
|
41
|
+
[](https://github.com/gosms-python/gosms-python-python/actions)
|
|
42
|
+
[](https://opensource.org/licenses/MIT)
|
|
43
|
+
|
|
44
|
+
Official Python SDK for the [GoSMS.GE](https://gosms.ge) SMS gateway. Send SMS messages, manage OTP verification, and check balances with both sync and async clients.
|
|
45
|
+
|
|
46
|
+
## Installation
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
pip install gosms-python
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For async support:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pip install gosms-python[async]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from gosms import SMS
|
|
62
|
+
|
|
63
|
+
sms = SMS("your_api_key")
|
|
64
|
+
|
|
65
|
+
# Send a message
|
|
66
|
+
result = sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
67
|
+
print(result.message_id) # 12345
|
|
68
|
+
print(result.balance) # 99
|
|
69
|
+
|
|
70
|
+
# Check balance
|
|
71
|
+
balance = sms.balance()
|
|
72
|
+
print(balance.balance) # 500
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## All Endpoints
|
|
76
|
+
|
|
77
|
+
### Send SMS
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
result = sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
81
|
+
result = sms.send("995555123456", "Urgent!", "GOSMS.GE", urgent=True)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Send Bulk SMS
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
result = sms.send_bulk(
|
|
88
|
+
"GOSMS.GE",
|
|
89
|
+
["995555111111", "995555222222"],
|
|
90
|
+
"Hello everyone!",
|
|
91
|
+
)
|
|
92
|
+
print(result.total_count) # 2
|
|
93
|
+
print(result.success_count) # 2
|
|
94
|
+
|
|
95
|
+
for msg in result.messages:
|
|
96
|
+
print(f"{msg.to}: {msg.message_id}")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Send OTP
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
result = sms.send_otp("995555123456")
|
|
103
|
+
print(result.hash) # "abc123hash" — save this for verification
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Verify OTP
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
result = sms.verify_otp("995555123456", "abc123hash", "1234")
|
|
110
|
+
print(result.verify) # True
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Check Message Status
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
result = sms.status(12345)
|
|
117
|
+
print(result.status) # "delivered"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Check Balance
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
result = sms.balance()
|
|
124
|
+
print(result.balance) # 500
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Create Sender Name
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
result = sms.create_sender("MyBrand")
|
|
131
|
+
print(result.success) # True
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Async Usage
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
import asyncio
|
|
138
|
+
from gosms import AsyncSMS
|
|
139
|
+
|
|
140
|
+
async def main():
|
|
141
|
+
async with AsyncSMS("your_api_key") as sms:
|
|
142
|
+
result = await sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
143
|
+
print(result.message_id)
|
|
144
|
+
|
|
145
|
+
balance = await sms.balance()
|
|
146
|
+
print(balance.balance)
|
|
147
|
+
|
|
148
|
+
asyncio.run(main())
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
All methods from the sync client are available as async equivalents with the same signatures.
|
|
152
|
+
|
|
153
|
+
## Django Integration
|
|
154
|
+
|
|
155
|
+
Add to your `settings.py`:
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
GOSMS_SETTINGS = {
|
|
159
|
+
"api_key": "your_api_key",
|
|
160
|
+
"timeout": 30, # optional
|
|
161
|
+
"retries": 1, # optional
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Use anywhere in your project:
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from gosms.django import get_sms_client
|
|
169
|
+
|
|
170
|
+
sms = get_sms_client()
|
|
171
|
+
sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The client is created lazily on first call and reused as a singleton.
|
|
175
|
+
|
|
176
|
+
## Configuration
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
sms = SMS(
|
|
180
|
+
"your_api_key",
|
|
181
|
+
timeout=30, # request timeout in seconds (default: 30)
|
|
182
|
+
retries=3, # retry attempts on failure (default: 1)
|
|
183
|
+
debug=True, # enable debug logging (default: False)
|
|
184
|
+
)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Error Handling
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
from gosms import SMS, GoSmsApiError, GoSmsErrorCode
|
|
191
|
+
|
|
192
|
+
sms = SMS("your_api_key")
|
|
193
|
+
|
|
194
|
+
try:
|
|
195
|
+
result = sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
196
|
+
except GoSmsApiError as e:
|
|
197
|
+
print(e.error_code) # 100
|
|
198
|
+
print(e.message) # "Invalid API key"
|
|
199
|
+
|
|
200
|
+
if e.error_code == GoSmsErrorCode.INVALID_API_KEY:
|
|
201
|
+
print("Check your API key")
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Error Codes
|
|
205
|
+
|
|
206
|
+
| Code | Constant | Description |
|
|
207
|
+
|------|----------|-------------|
|
|
208
|
+
| 100 | `INVALID_API_KEY` | Invalid API key |
|
|
209
|
+
| 101 | `INVALID_PHONE_NUMBER` | Invalid phone number |
|
|
210
|
+
| 102 | `INSUFFICIENT_BALANCE` | Insufficient balance |
|
|
211
|
+
| 103 | `SENDER_NOT_FOUND` | Sender name not found |
|
|
212
|
+
| 104 | `INVALID_TEXT` | Invalid message text |
|
|
213
|
+
| 105 | `TOO_MANY_RECIPIENTS` | Too many recipients (max 1000) |
|
|
214
|
+
| 106 | `INVALID_MESSAGE_ID` | Invalid message ID |
|
|
215
|
+
| 107 | `SENDER_EXISTS` | Sender name already exists |
|
|
216
|
+
| 108 | `INVALID_SENDER_NAME` | Invalid sender name |
|
|
217
|
+
| 109 | `INVALID_OTP_HASH` | Invalid OTP hash |
|
|
218
|
+
| 110 | `INVALID_OTP_CODE` | Invalid OTP code |
|
|
219
|
+
| 111 | `OTP_EXPIRED` | OTP expired |
|
|
220
|
+
| 112 | `OTP_ALREADY_VERIFIED` | OTP already verified |
|
|
221
|
+
| 113 | `RATE_LIMIT_EXCEEDED` | Rate limit exceeded |
|
|
222
|
+
|
|
223
|
+
## Response Types
|
|
224
|
+
|
|
225
|
+
All methods return typed frozen dataclasses:
|
|
226
|
+
|
|
227
|
+
| Method | Return Type | Key Fields |
|
|
228
|
+
|--------|-------------|------------|
|
|
229
|
+
| `send()` | `SmsSendResponse` | `success`, `message_id`, `balance` |
|
|
230
|
+
| `send_bulk()` | `SendBulkSmsResponse` | `success`, `total_count`, `messages` |
|
|
231
|
+
| `send_otp()` | `OtpSendResponse` | `success`, `hash`, `balance` |
|
|
232
|
+
| `verify_otp()` | `OtpVerifyResponse` | `success`, `verify` |
|
|
233
|
+
| `status()` | `CheckStatusResponse` | `success`, `status`, `message_id` |
|
|
234
|
+
| `balance()` | `BalanceResponse` | `success`, `balance` |
|
|
235
|
+
| `create_sender()` | `SenderCreateResponse` | `success` |
|
|
236
|
+
|
|
237
|
+
## Migration from v1.x
|
|
238
|
+
|
|
239
|
+
v2.0 is a complete rewrite. Key changes:
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
# v1.x (old)
|
|
243
|
+
from gosms import sms # module-level singleton
|
|
244
|
+
sms.send('995...', 'text', 'SENDER')
|
|
245
|
+
|
|
246
|
+
# v2.0 (new)
|
|
247
|
+
from gosms import SMS # explicit instantiation
|
|
248
|
+
sms = SMS('your_api_key')
|
|
249
|
+
sms.send('995...', 'text', 'SENDER')
|
|
250
|
+
|
|
251
|
+
# v1.x Django (old)
|
|
252
|
+
from gosms import sms # import-time side effect
|
|
253
|
+
|
|
254
|
+
# v2.0 Django (new)
|
|
255
|
+
from gosms.django import get_sms_client # lazy factory
|
|
256
|
+
sms = get_sms_client()
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Other changes:
|
|
260
|
+
- `GoSmsApiError` now extends `Exception` (was `BaseException`)
|
|
261
|
+
- Added `GoSmsErrorCode` constants for typed error handling
|
|
262
|
+
- Added `send_bulk()` and `create_sender()` endpoints
|
|
263
|
+
- Added async client (`AsyncSMS`) via `pip install gosms-python[async]`
|
|
264
|
+
- All responses are typed frozen dataclasses
|
|
265
|
+
- Removed `dev_mode` / `RequestMock` in favor of standard test mocking
|
|
266
|
+
|
|
267
|
+
## License
|
|
268
|
+
|
|
269
|
+
MIT
|
|
270
|
+
|
|
271
|
+
## Links
|
|
272
|
+
|
|
273
|
+
- Website: https://gosms.ge
|
|
274
|
+
- PyPI: https://pypi.org/project/gosms-python/
|
|
275
|
+
- GitHub: https://github.com/gosms-python/gosms-python-python
|
|
276
|
+
- Support: info@gosms.ge
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# GoSMS.GE Python SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/gosms-python/)
|
|
4
|
+
[](https://pypi.org/project/gosms-python/)
|
|
5
|
+
[](https://github.com/gosms-python/gosms-python-python/actions)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Official Python SDK for the [GoSMS.GE](https://gosms.ge) SMS gateway. Send SMS messages, manage OTP verification, and check balances with both sync and async clients.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install gosms-python
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
For async support:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install gosms-python[async]
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from gosms import SMS
|
|
26
|
+
|
|
27
|
+
sms = SMS("your_api_key")
|
|
28
|
+
|
|
29
|
+
# Send a message
|
|
30
|
+
result = sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
31
|
+
print(result.message_id) # 12345
|
|
32
|
+
print(result.balance) # 99
|
|
33
|
+
|
|
34
|
+
# Check balance
|
|
35
|
+
balance = sms.balance()
|
|
36
|
+
print(balance.balance) # 500
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## All Endpoints
|
|
40
|
+
|
|
41
|
+
### Send SMS
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
result = sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
45
|
+
result = sms.send("995555123456", "Urgent!", "GOSMS.GE", urgent=True)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Send Bulk SMS
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
result = sms.send_bulk(
|
|
52
|
+
"GOSMS.GE",
|
|
53
|
+
["995555111111", "995555222222"],
|
|
54
|
+
"Hello everyone!",
|
|
55
|
+
)
|
|
56
|
+
print(result.total_count) # 2
|
|
57
|
+
print(result.success_count) # 2
|
|
58
|
+
|
|
59
|
+
for msg in result.messages:
|
|
60
|
+
print(f"{msg.to}: {msg.message_id}")
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Send OTP
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
result = sms.send_otp("995555123456")
|
|
67
|
+
print(result.hash) # "abc123hash" — save this for verification
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Verify OTP
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
result = sms.verify_otp("995555123456", "abc123hash", "1234")
|
|
74
|
+
print(result.verify) # True
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Check Message Status
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
result = sms.status(12345)
|
|
81
|
+
print(result.status) # "delivered"
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Check Balance
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
result = sms.balance()
|
|
88
|
+
print(result.balance) # 500
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Create Sender Name
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
result = sms.create_sender("MyBrand")
|
|
95
|
+
print(result.success) # True
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Async Usage
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
import asyncio
|
|
102
|
+
from gosms import AsyncSMS
|
|
103
|
+
|
|
104
|
+
async def main():
|
|
105
|
+
async with AsyncSMS("your_api_key") as sms:
|
|
106
|
+
result = await sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
107
|
+
print(result.message_id)
|
|
108
|
+
|
|
109
|
+
balance = await sms.balance()
|
|
110
|
+
print(balance.balance)
|
|
111
|
+
|
|
112
|
+
asyncio.run(main())
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
All methods from the sync client are available as async equivalents with the same signatures.
|
|
116
|
+
|
|
117
|
+
## Django Integration
|
|
118
|
+
|
|
119
|
+
Add to your `settings.py`:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
GOSMS_SETTINGS = {
|
|
123
|
+
"api_key": "your_api_key",
|
|
124
|
+
"timeout": 30, # optional
|
|
125
|
+
"retries": 1, # optional
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use anywhere in your project:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from gosms.django import get_sms_client
|
|
133
|
+
|
|
134
|
+
sms = get_sms_client()
|
|
135
|
+
sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The client is created lazily on first call and reused as a singleton.
|
|
139
|
+
|
|
140
|
+
## Configuration
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
sms = SMS(
|
|
144
|
+
"your_api_key",
|
|
145
|
+
timeout=30, # request timeout in seconds (default: 30)
|
|
146
|
+
retries=3, # retry attempts on failure (default: 1)
|
|
147
|
+
debug=True, # enable debug logging (default: False)
|
|
148
|
+
)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Error Handling
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from gosms import SMS, GoSmsApiError, GoSmsErrorCode
|
|
155
|
+
|
|
156
|
+
sms = SMS("your_api_key")
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
result = sms.send("995555123456", "Hello!", "GOSMS.GE")
|
|
160
|
+
except GoSmsApiError as e:
|
|
161
|
+
print(e.error_code) # 100
|
|
162
|
+
print(e.message) # "Invalid API key"
|
|
163
|
+
|
|
164
|
+
if e.error_code == GoSmsErrorCode.INVALID_API_KEY:
|
|
165
|
+
print("Check your API key")
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Error Codes
|
|
169
|
+
|
|
170
|
+
| Code | Constant | Description |
|
|
171
|
+
|------|----------|-------------|
|
|
172
|
+
| 100 | `INVALID_API_KEY` | Invalid API key |
|
|
173
|
+
| 101 | `INVALID_PHONE_NUMBER` | Invalid phone number |
|
|
174
|
+
| 102 | `INSUFFICIENT_BALANCE` | Insufficient balance |
|
|
175
|
+
| 103 | `SENDER_NOT_FOUND` | Sender name not found |
|
|
176
|
+
| 104 | `INVALID_TEXT` | Invalid message text |
|
|
177
|
+
| 105 | `TOO_MANY_RECIPIENTS` | Too many recipients (max 1000) |
|
|
178
|
+
| 106 | `INVALID_MESSAGE_ID` | Invalid message ID |
|
|
179
|
+
| 107 | `SENDER_EXISTS` | Sender name already exists |
|
|
180
|
+
| 108 | `INVALID_SENDER_NAME` | Invalid sender name |
|
|
181
|
+
| 109 | `INVALID_OTP_HASH` | Invalid OTP hash |
|
|
182
|
+
| 110 | `INVALID_OTP_CODE` | Invalid OTP code |
|
|
183
|
+
| 111 | `OTP_EXPIRED` | OTP expired |
|
|
184
|
+
| 112 | `OTP_ALREADY_VERIFIED` | OTP already verified |
|
|
185
|
+
| 113 | `RATE_LIMIT_EXCEEDED` | Rate limit exceeded |
|
|
186
|
+
|
|
187
|
+
## Response Types
|
|
188
|
+
|
|
189
|
+
All methods return typed frozen dataclasses:
|
|
190
|
+
|
|
191
|
+
| Method | Return Type | Key Fields |
|
|
192
|
+
|--------|-------------|------------|
|
|
193
|
+
| `send()` | `SmsSendResponse` | `success`, `message_id`, `balance` |
|
|
194
|
+
| `send_bulk()` | `SendBulkSmsResponse` | `success`, `total_count`, `messages` |
|
|
195
|
+
| `send_otp()` | `OtpSendResponse` | `success`, `hash`, `balance` |
|
|
196
|
+
| `verify_otp()` | `OtpVerifyResponse` | `success`, `verify` |
|
|
197
|
+
| `status()` | `CheckStatusResponse` | `success`, `status`, `message_id` |
|
|
198
|
+
| `balance()` | `BalanceResponse` | `success`, `balance` |
|
|
199
|
+
| `create_sender()` | `SenderCreateResponse` | `success` |
|
|
200
|
+
|
|
201
|
+
## Migration from v1.x
|
|
202
|
+
|
|
203
|
+
v2.0 is a complete rewrite. Key changes:
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
# v1.x (old)
|
|
207
|
+
from gosms import sms # module-level singleton
|
|
208
|
+
sms.send('995...', 'text', 'SENDER')
|
|
209
|
+
|
|
210
|
+
# v2.0 (new)
|
|
211
|
+
from gosms import SMS # explicit instantiation
|
|
212
|
+
sms = SMS('your_api_key')
|
|
213
|
+
sms.send('995...', 'text', 'SENDER')
|
|
214
|
+
|
|
215
|
+
# v1.x Django (old)
|
|
216
|
+
from gosms import sms # import-time side effect
|
|
217
|
+
|
|
218
|
+
# v2.0 Django (new)
|
|
219
|
+
from gosms.django import get_sms_client # lazy factory
|
|
220
|
+
sms = get_sms_client()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Other changes:
|
|
224
|
+
- `GoSmsApiError` now extends `Exception` (was `BaseException`)
|
|
225
|
+
- Added `GoSmsErrorCode` constants for typed error handling
|
|
226
|
+
- Added `send_bulk()` and `create_sender()` endpoints
|
|
227
|
+
- Added async client (`AsyncSMS`) via `pip install gosms-python[async]`
|
|
228
|
+
- All responses are typed frozen dataclasses
|
|
229
|
+
- Removed `dev_mode` / `RequestMock` in favor of standard test mocking
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT
|
|
234
|
+
|
|
235
|
+
## Links
|
|
236
|
+
|
|
237
|
+
- Website: https://gosms.ge
|
|
238
|
+
- PyPI: https://pypi.org/project/gosms-python/
|
|
239
|
+
- GitHub: https://github.com/gosms-python/gosms-python-python
|
|
240
|
+
- Support: info@gosms.ge
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "gosms-python"
|
|
7
|
+
version = "1.0.3"
|
|
8
|
+
description = "Official Python SDK for GoSMS.GE SMS Gateway"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "GoSMS.GE", email = "info@gosms.ge" },
|
|
14
|
+
]
|
|
15
|
+
keywords = ["sms", "sms-api", "gosms", "sms-sdk", "sms-gateway", "otp"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 5 - Production/Stable",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"License :: OSI Approved :: MIT License",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Communications",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"requests>=2.28.0",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
[project.optional-dependencies]
|
|
34
|
+
async = ["httpx>=0.24.0"]
|
|
35
|
+
django = ["django>=3.2"]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=7.0",
|
|
38
|
+
"pytest-asyncio>=0.21",
|
|
39
|
+
"responses>=0.23",
|
|
40
|
+
"pytest-httpx>=0.21",
|
|
41
|
+
"ruff>=0.1",
|
|
42
|
+
"build",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.urls]
|
|
46
|
+
Homepage = "https://gosms.ge"
|
|
47
|
+
Repository = "https://github.com/gosms-ge/gosmsge-python"
|
|
48
|
+
Issues = "https://github.com/gosms-ge/gosmsge-python/issues"
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.wheel]
|
|
51
|
+
packages = ["src/gosms"]
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
testpaths = ["tests"]
|
|
55
|
+
asyncio_mode = "auto"
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
target-version = "1.0.3"
|
|
59
|
+
line-length = 120
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|