django-mimsms 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.
@@ -0,0 +1,156 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-mimsms
3
+ Version: 0.1.0
4
+ Summary: Production-ready Django & Python package for MiMSMS.com bulk SMS API. Features async support, type safety, and seamless Bangladesh SMS gateway integration.
5
+ Author: Mimsms Integration Team
6
+ Project-URL: Homepage, https://github.com/sharf-shawon/django-mimsms
7
+ Project-URL: Bug Tracker, https://github.com/sharf-shawon/django-mimsms/issues
8
+ Project-URL: Documentation, https://github.com/sharf-shawon/django-mimsms#readme
9
+ Project-URL: Repository, https://github.com/sharf-shawon/django-mimsms
10
+ Keywords: django-mimsms,mimsms sms api,django sms integration,bulk sms bangladesh,mimsms.com python,dynamic sms api,sms gateway django
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Framework :: Django
15
+ Classifier: Framework :: Django :: 5.0
16
+ Requires-Python: >=3.12
17
+ Description-Content-Type: text/markdown
18
+ Requires-Dist: django>=5.0
19
+ Requires-Dist: httpx>=0.27.0
20
+ Requires-Dist: pydantic>=2.6.0
21
+ Requires-Dist: rich>=15.0.0
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=8.0.0; extra == "test"
24
+ Requires-Dist: pytest-django>=4.8.0; extra == "test"
25
+ Requires-Dist: pytest-cov>=4.1.0; extra == "test"
26
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "test"
27
+ Requires-Dist: respx>=0.20.0; extra == "test"
28
+ Provides-Extra: dev
29
+ Requires-Dist: ruff>=0.3.0; extra == "dev"
30
+ Requires-Dist: mypy>=1.9.0; extra == "dev"
31
+ Requires-Dist: build; extra == "dev"
32
+ Requires-Dist: rich>=13.0.0; extra == "dev"
33
+ Requires-Dist: django-mimsms[test]; extra == "dev"
34
+ Requires-Dist: python-semantic-release>=9.0.0; extra == "dev"
35
+
36
+ # django-mimsms: MiMSMS SMS API Integration for Django & Python
37
+
38
+ [![PyPI version](https://img.shields.io/pypi/v/django-mimsms.svg)](https://pypi.org/project/django-mimsms/)
39
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
40
+ [![CI Status](https://github.com/mimsms/django-mimsms/actions/workflows/ci.yml/badge.svg)](https://github.com/mimsms/django-mimsms/actions)
41
+
42
+ Seamlessly integrate **MiMSMS.com SMS API** into your Django and Python applications. This package provides a robust, type-safe, and asynchronous client for sending SMS, bulk messages, and tracking delivery reports in Bangladesh.
43
+
44
+ ## Key Features
45
+
46
+ - **Strict Type Safety**: Fully typed with `mypy` strict checks and Pydantic v2 validation.
47
+ - **Async Support**: Efficient non-blocking I/O using `httpx`.
48
+ - **Django Integration**: Configuration via standard Django settings.
49
+ - **100% Test Coverage**: Comprehensive test suite with deterministic network isolation (`respx`).
50
+ - **Multiple API Support**: Single SMS, Bulk SMS (One-to-Many), and Dynamic SMS (DSMS).
51
+
52
+ ## Installation
53
+
54
+ Install the package via `pip` or `uv`:
55
+
56
+ ```bash
57
+ # Using pip
58
+ pip install django-mimsms
59
+
60
+ # Using uv
61
+ uv add django-mimsms
62
+ ```
63
+
64
+ ## Django Integration
65
+
66
+ ### 1. Add to `INSTALLED_APPS`
67
+
68
+ ```python
69
+ # settings.py
70
+ INSTALLED_APPS = [
71
+ ...,
72
+ "django_mimsms",
73
+ ]
74
+ ```
75
+
76
+ ### 2. Configure Settings
77
+
78
+ Add your MiMSMS credentials to your `settings.py`:
79
+
80
+ ```python
81
+ # settings.py
82
+ MIMSMS_API_KEY = "your_api_key"
83
+ MIMSMS_SENDER_ID = "your_sender_id"
84
+ MIMSMS_USERNAME = "your_username"
85
+ ```
86
+
87
+ ### 3. Usage
88
+
89
+ ```python
90
+ from django_mimsms import send_sms
91
+
92
+ # Send a simple SMS
93
+ response = send_sms(
94
+ to="88017XXXXXXXX",
95
+ message="Hello from Django!",
96
+ )
97
+
98
+ print(response.trxnId)
99
+ ```
100
+
101
+ ## Plain Python Usage
102
+
103
+ If you're not using Django, you can use the `MiMSMSClient` directly:
104
+
105
+ ```python
106
+ import asyncio
107
+ from django_mimsms import MiMSMSClient
108
+
109
+ async def main():
110
+ client = MiMSMSClient(
111
+ api_key="your_api_key",
112
+ username="your_username",
113
+ sender_id="your_sender_id"
114
+ )
115
+
116
+ response = await client.send_sms(
117
+ to="88017XXXXXXXX",
118
+ message="Hello from Python!",
119
+ )
120
+ print(response.trxnId)
121
+
122
+ if __name__ == "__main__":
123
+ asyncio.run(main())
124
+ ```
125
+
126
+ ## Advanced Features
127
+
128
+ ### Bulk SMS
129
+ Send the same message to multiple recipients:
130
+
131
+ ```python
132
+ from django_mimsms import send_bulk_sms
133
+
134
+ send_bulk_sms(
135
+ numbers=["88017XXXXXXXX", "88018XXXXXXXX"],
136
+ message="Bulk message testing",
137
+ )
138
+ ```
139
+
140
+ ### Dynamic SMS
141
+ Send different messages to different recipients in one call:
142
+
143
+ ```python
144
+ from django_mimsms import send_dynamic_sms
145
+
146
+ messages = [
147
+ {"to": "88017XXXXXXXX", "message": "Hi Alice!"},
148
+ {"to": "88018XXXXXXXX", "message": "Hi Bob!"},
149
+ ]
150
+
151
+ send_dynamic_sms(messages)
152
+ ```
153
+
154
+ ## License
155
+
156
+ MIT
@@ -0,0 +1,121 @@
1
+ # django-mimsms: MiMSMS SMS API Integration for Django & Python
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/django-mimsms.svg)](https://pypi.org/project/django-mimsms/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![CI Status](https://github.com/mimsms/django-mimsms/actions/workflows/ci.yml/badge.svg)](https://github.com/mimsms/django-mimsms/actions)
6
+
7
+ Seamlessly integrate **MiMSMS.com SMS API** into your Django and Python applications. This package provides a robust, type-safe, and asynchronous client for sending SMS, bulk messages, and tracking delivery reports in Bangladesh.
8
+
9
+ ## Key Features
10
+
11
+ - **Strict Type Safety**: Fully typed with `mypy` strict checks and Pydantic v2 validation.
12
+ - **Async Support**: Efficient non-blocking I/O using `httpx`.
13
+ - **Django Integration**: Configuration via standard Django settings.
14
+ - **100% Test Coverage**: Comprehensive test suite with deterministic network isolation (`respx`).
15
+ - **Multiple API Support**: Single SMS, Bulk SMS (One-to-Many), and Dynamic SMS (DSMS).
16
+
17
+ ## Installation
18
+
19
+ Install the package via `pip` or `uv`:
20
+
21
+ ```bash
22
+ # Using pip
23
+ pip install django-mimsms
24
+
25
+ # Using uv
26
+ uv add django-mimsms
27
+ ```
28
+
29
+ ## Django Integration
30
+
31
+ ### 1. Add to `INSTALLED_APPS`
32
+
33
+ ```python
34
+ # settings.py
35
+ INSTALLED_APPS = [
36
+ ...,
37
+ "django_mimsms",
38
+ ]
39
+ ```
40
+
41
+ ### 2. Configure Settings
42
+
43
+ Add your MiMSMS credentials to your `settings.py`:
44
+
45
+ ```python
46
+ # settings.py
47
+ MIMSMS_API_KEY = "your_api_key"
48
+ MIMSMS_SENDER_ID = "your_sender_id"
49
+ MIMSMS_USERNAME = "your_username"
50
+ ```
51
+
52
+ ### 3. Usage
53
+
54
+ ```python
55
+ from django_mimsms import send_sms
56
+
57
+ # Send a simple SMS
58
+ response = send_sms(
59
+ to="88017XXXXXXXX",
60
+ message="Hello from Django!",
61
+ )
62
+
63
+ print(response.trxnId)
64
+ ```
65
+
66
+ ## Plain Python Usage
67
+
68
+ If you're not using Django, you can use the `MiMSMSClient` directly:
69
+
70
+ ```python
71
+ import asyncio
72
+ from django_mimsms import MiMSMSClient
73
+
74
+ async def main():
75
+ client = MiMSMSClient(
76
+ api_key="your_api_key",
77
+ username="your_username",
78
+ sender_id="your_sender_id"
79
+ )
80
+
81
+ response = await client.send_sms(
82
+ to="88017XXXXXXXX",
83
+ message="Hello from Python!",
84
+ )
85
+ print(response.trxnId)
86
+
87
+ if __name__ == "__main__":
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## Advanced Features
92
+
93
+ ### Bulk SMS
94
+ Send the same message to multiple recipients:
95
+
96
+ ```python
97
+ from django_mimsms import send_bulk_sms
98
+
99
+ send_bulk_sms(
100
+ numbers=["88017XXXXXXXX", "88018XXXXXXXX"],
101
+ message="Bulk message testing",
102
+ )
103
+ ```
104
+
105
+ ### Dynamic SMS
106
+ Send different messages to different recipients in one call:
107
+
108
+ ```python
109
+ from django_mimsms import send_dynamic_sms
110
+
111
+ messages = [
112
+ {"to": "88017XXXXXXXX", "message": "Hi Alice!"},
113
+ {"to": "88018XXXXXXXX", "message": "Hi Bob!"},
114
+ ]
115
+
116
+ send_dynamic_sms(messages)
117
+ ```
118
+
119
+ ## License
120
+
121
+ MIT
@@ -0,0 +1,100 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "django-mimsms"
7
+ version = "0.1.0"
8
+ authors = [
9
+ { name="Mimsms Integration Team" },
10
+ ]
11
+ description = "Production-ready Django & Python package for MiMSMS.com bulk SMS API. Features async support, type safety, and seamless Bangladesh SMS gateway integration."
12
+ readme = "README.md"
13
+ requires-python = ">=3.12"
14
+ keywords = [
15
+ "django-mimsms",
16
+ "mimsms sms api",
17
+ "django sms integration",
18
+ "bulk sms bangladesh",
19
+ "mimsms.com python",
20
+ "dynamic sms api",
21
+ "sms gateway django",
22
+ ]
23
+ classifiers = [
24
+ "Programming Language :: Python :: 3",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Operating System :: OS Independent",
27
+ "Framework :: Django",
28
+ "Framework :: Django :: 5.0",
29
+ ]
30
+ dependencies = [
31
+ "django>=5.0",
32
+ "httpx>=0.27.0",
33
+ "pydantic>=2.6.0",
34
+ "rich>=15.0.0",
35
+ ]
36
+
37
+ [project.urls]
38
+ "Homepage" = "https://github.com/sharf-shawon/django-mimsms"
39
+ "Bug Tracker" = "https://github.com/sharf-shawon/django-mimsms/issues"
40
+ "Documentation" = "https://github.com/sharf-shawon/django-mimsms#readme"
41
+ "Repository" = "https://github.com/sharf-shawon/django-mimsms"
42
+
43
+ [project.optional-dependencies]
44
+ test = [
45
+ "pytest>=8.0.0",
46
+ "pytest-django>=4.8.0",
47
+ "pytest-cov>=4.1.0",
48
+ "pytest-asyncio>=0.23.0",
49
+ "respx>=0.20.0",
50
+ ]
51
+ dev = [
52
+ "ruff>=0.3.0",
53
+ "mypy>=1.9.0",
54
+ "build",
55
+ "rich>=13.0.0",
56
+ "django-mimsms[test]",
57
+ "python-semantic-release>=9.0.0",
58
+ ]
59
+
60
+
61
+
62
+
63
+
64
+ [tool.semantic_release]
65
+ vcs_provider = "github"
66
+ version_variable = [
67
+ "src/django_mimsms/version.py:__version__",
68
+ ]
69
+ version_toml = [
70
+ "pyproject.toml:project.version",
71
+ ]
72
+ branch = "main"
73
+ upload_to_pypi = false
74
+ upload_to_release = true
75
+ build_command = "python -m pip install build && python -m build"
76
+
77
+ [tool.ruff]
78
+ line-length = 120
79
+ target-version = "py312"
80
+
81
+ [tool.ruff.lint]
82
+ select = ["E", "F", "I", "N", "UP", "B", "C4", "DTZ", "SIM", "TID"]
83
+
84
+ [tool.mypy]
85
+ python_version = "3.12"
86
+ strict = true
87
+ ignore_missing_imports = true
88
+ plugins = ["pydantic.mypy"]
89
+
90
+ [tool.pytest.ini_options]
91
+ minversion = "8.0"
92
+ addopts = "-ra -q --cov=src/django_mimsms --cov-report=term-missing --cov-fail-under=100"
93
+ testpaths = [
94
+ "tests",
95
+ ]
96
+ pythonpath = [".", "src"]
97
+ DJANGO_SETTINGS_MODULE = "tests.settings"
98
+
99
+ [tool.setuptools.packages.find]
100
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from django_mimsms.version import __version__
2
+
3
+ __all__ = ["__version__"]
@@ -0,0 +1,280 @@
1
+ from typing import Any
2
+
3
+ from pydantic import ValidationError
4
+
5
+ from django_mimsms.config import MiMSMSConfig
6
+ from django_mimsms.exceptions import MiMSMSResponseParseError, MiMSMSValidationError
7
+ from django_mimsms.models import (
8
+ BulkSmsRequest,
9
+ DlrRequest,
10
+ DlrResponse,
11
+ DynamicSmsItem,
12
+ DynamicSmsRequest,
13
+ MiMSMSBaseRequest,
14
+ MiMSMSResponse,
15
+ SingleSmsRequest,
16
+ )
17
+ from django_mimsms.transport import Transport
18
+
19
+
20
+ class MiMSMSClient:
21
+ """Core client for the MiMSMS API."""
22
+
23
+ def __init__(
24
+ self,
25
+ username: str,
26
+ apikey: str,
27
+ sender_name: str,
28
+ **options: Any,
29
+ ) -> None:
30
+ self.config = MiMSMSConfig(username=username, apikey=apikey, sender_name=sender_name, **options)
31
+ self.transport = Transport(self.config)
32
+
33
+ def send_sms(
34
+ self,
35
+ number: str,
36
+ message: str,
37
+ transaction_type: str | None = None,
38
+ campaign_id: str | None = None,
39
+ ) -> MiMSMSResponse:
40
+ """Send a single SMS via JSON POST."""
41
+ try:
42
+ payload = SingleSmsRequest(
43
+ username=self.config.username,
44
+ apikey=self.config.apikey,
45
+ mobile_number=number,
46
+ sender_name=self.config.sender_name,
47
+ transaction_type=transaction_type or self.config.default_transaction_type,
48
+ message=message,
49
+ campaign_id=campaign_id or self.config.campaign_id,
50
+ )
51
+ except ValidationError as e:
52
+ raise MiMSMSValidationError(str(e)) from e
53
+
54
+ data = self.transport.request(
55
+ "POST",
56
+ "/api/SmsSending/SMS",
57
+ json=payload.model_dump(by_alias=True, exclude_none=True),
58
+ )
59
+
60
+ try:
61
+ return MiMSMSResponse.model_validate(data)
62
+ except ValidationError as e:
63
+ raise MiMSMSResponseParseError(str(e)) from e
64
+
65
+ def send_sms_get(
66
+ self,
67
+ number: str,
68
+ message: str,
69
+ transaction_type: str | None = None,
70
+ campaign_id: str | None = None,
71
+ ) -> MiMSMSResponse:
72
+ """Send a single SMS via query-string GET."""
73
+ try:
74
+ payload = SingleSmsRequest(
75
+ username=self.config.username,
76
+ apikey=self.config.apikey,
77
+ mobile_number=number,
78
+ sender_name=self.config.sender_name,
79
+ transaction_type=transaction_type or self.config.default_transaction_type,
80
+ message=message,
81
+ campaign_id=campaign_id or self.config.campaign_id,
82
+ )
83
+ except ValidationError as e:
84
+ raise MiMSMSValidationError(str(e)) from e
85
+
86
+ data = self.transport.request(
87
+ "GET",
88
+ "/api/SmsSending/Send",
89
+ params=payload.model_dump(by_alias=True, exclude_none=True),
90
+ )
91
+
92
+ try:
93
+ return MiMSMSResponse.model_validate(data)
94
+ except ValidationError as e:
95
+ raise MiMSMSResponseParseError(str(e)) from e
96
+
97
+ def send_one_to_many(
98
+ self,
99
+ numbers: list[str] | str,
100
+ message: str,
101
+ transaction_type: str | None = None,
102
+ campaign_id: str | None = None,
103
+ ) -> MiMSMSResponse:
104
+ """Send bulk SMS via JSON POST."""
105
+ numbers_str = ",".join(numbers) if isinstance(numbers, list) else numbers
106
+
107
+ try:
108
+ payload = BulkSmsRequest(
109
+ username=self.config.username,
110
+ apikey=self.config.apikey,
111
+ mobile_number=numbers_str,
112
+ sender_name=self.config.sender_name,
113
+ transaction_type=transaction_type or self.config.default_transaction_type,
114
+ message=message,
115
+ campaign_id=campaign_id or self.config.campaign_id,
116
+ )
117
+ except ValidationError as e:
118
+ raise MiMSMSValidationError(str(e)) from e
119
+
120
+ data = self.transport.request(
121
+ "POST",
122
+ "/api/SmsSending/OneToMany",
123
+ json=payload.model_dump(by_alias=True, exclude_none=True),
124
+ )
125
+
126
+ try:
127
+ return MiMSMSResponse.model_validate(data)
128
+ except ValidationError as e:
129
+ raise MiMSMSResponseParseError(str(e)) from e
130
+
131
+ def send_one_to_many_get(
132
+ self,
133
+ numbers: list[str] | str,
134
+ message: str,
135
+ transaction_type: str | None = None,
136
+ campaign_id: str | None = None,
137
+ ) -> MiMSMSResponse:
138
+ """Send bulk SMS via query-string GET."""
139
+ numbers_str = ",".join(numbers) if isinstance(numbers, list) else numbers
140
+
141
+ try:
142
+ payload = BulkSmsRequest(
143
+ username=self.config.username,
144
+ apikey=self.config.apikey,
145
+ mobile_number=numbers_str,
146
+ sender_name=self.config.sender_name,
147
+ transaction_type=transaction_type or self.config.default_transaction_type,
148
+ message=message,
149
+ campaign_id=campaign_id or self.config.campaign_id,
150
+ )
151
+ except ValidationError as e:
152
+ raise MiMSMSValidationError(str(e)) from e
153
+
154
+ data = self.transport.request(
155
+ "GET",
156
+ "/api/SmsSending/SendOneToMany",
157
+ params=payload.model_dump(by_alias=True, exclude_none=True),
158
+ )
159
+
160
+ try:
161
+ return MiMSMSResponse.model_validate(data)
162
+ except ValidationError as e:
163
+ raise MiMSMSResponseParseError(str(e)) from e
164
+
165
+ def send_dynamic_sms(
166
+ self,
167
+ messages: list[dict[str, str]],
168
+ transaction_type: str = "D",
169
+ ) -> MiMSMSResponse:
170
+ """Send dynamic SMS via JSON POST."""
171
+ try:
172
+ sms_data = [DynamicSmsItem(mobile_number=m["number"], message=m["text"]) for m in messages]
173
+ payload = DynamicSmsRequest(
174
+ username=self.config.username,
175
+ apikey=self.config.apikey,
176
+ sender_name=self.config.sender_name,
177
+ sms_data=sms_data,
178
+ transaction_type=transaction_type,
179
+ )
180
+ except ValidationError as e:
181
+ raise MiMSMSValidationError(str(e)) from e
182
+
183
+ data = self.transport.request(
184
+ "POST",
185
+ "/api/SmsSending/DSMS",
186
+ json=payload.model_dump(by_alias=True, exclude_none=True),
187
+ )
188
+
189
+ try:
190
+ return MiMSMSResponse.model_validate(data)
191
+ except ValidationError as e:
192
+ raise MiMSMSResponseParseError(str(e)) from e
193
+
194
+ def check_balance(self) -> float:
195
+ """Check account balance via JSON POST."""
196
+ payload = MiMSMSBaseRequest(
197
+ username=self.config.username,
198
+ apikey=self.config.apikey,
199
+ )
200
+ data = self.transport.request(
201
+ "POST",
202
+ "/api/SmsSending/balanceCheck",
203
+ json=payload.model_dump(by_alias=True, exclude_none=True),
204
+ )
205
+ try:
206
+ val = float(data.get("responseResult", 0.0))
207
+ import math
208
+
209
+ return val if not math.isnan(val) else 0.0
210
+ except (ValueError, TypeError):
211
+ return 0.0
212
+
213
+ def check_balance_get(self) -> float:
214
+ """Check account balance via query-string GET."""
215
+ payload = MiMSMSBaseRequest(
216
+ username=self.config.username,
217
+ apikey=self.config.apikey,
218
+ )
219
+ data = self.transport.request(
220
+ "GET",
221
+ "/api/SmsSending/balanceCheck",
222
+ params=payload.model_dump(by_alias=True, exclude_none=True),
223
+ )
224
+ try:
225
+ val = float(data.get("responseResult", 0.0))
226
+ import math
227
+
228
+ return val if not math.isnan(val) else 0.0
229
+ except (ValueError, TypeError):
230
+ return 0.0
231
+
232
+ def check_dlr(self, trxn_id: str, number: str) -> DlrResponse:
233
+ """Check delivery report for a transaction."""
234
+ try:
235
+ payload = DlrRequest(
236
+ username=self.config.username,
237
+ apikey=self.config.apikey,
238
+ mobile_number=number,
239
+ trxn_id=trxn_id,
240
+ )
241
+ except ValidationError as e:
242
+ raise MiMSMSValidationError(str(e)) from e
243
+
244
+ data = self.transport.request(
245
+ "POST",
246
+ "/api/SmsSending/DlrApi",
247
+ json=payload.model_dump(by_alias=True, exclude_none=True),
248
+ )
249
+
250
+ try:
251
+ return DlrResponse.model_validate(data)
252
+ except ValidationError as e:
253
+ raise MiMSMSResponseParseError(str(e)) from e
254
+
255
+ def _get_auth_params(self) -> dict[str, str]:
256
+ """Get authentication parameters."""
257
+ return {
258
+ "UserName": self.config.username,
259
+ "ApiKey": self.config.apikey,
260
+ }
261
+
262
+ def close(self) -> None:
263
+ """Close the underlying transport."""
264
+ self.transport.close()
265
+
266
+ async def aclose(self) -> None:
267
+ """Close the underlying transport asynchronously."""
268
+ await self.transport.aclose()
269
+
270
+ def __enter__(self) -> "MiMSMSClient":
271
+ return self
272
+
273
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
274
+ self.close()
275
+
276
+ async def __aenter__(self) -> "MiMSMSClient":
277
+ return self
278
+
279
+ async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
280
+ await self.aclose()
@@ -0,0 +1,16 @@
1
+ from pydantic import BaseModel, ConfigDict
2
+
3
+
4
+ class MiMSMSConfig(BaseModel):
5
+ """Configuration model for MiMSMS integration."""
6
+
7
+ model_config = ConfigDict(populate_by_name=True)
8
+
9
+ username: str
10
+ apikey: str
11
+ sender_name: str
12
+ base_url: str = "https://api.mimsms.com"
13
+ timeout: float = 30.0
14
+ verify_ssl: bool = True
15
+ default_transaction_type: str = "T"
16
+ campaign_id: str | None = None