smsaero-api-async 3.1.0__py3-none-any.whl

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.
smsaero/__init__.py ADDED
@@ -0,0 +1,1534 @@
1
+ """
2
+ This module provides the SmsAero class for interacting with the SmsAero API.
3
+
4
+ The SmsAero class provides methods for sending SMS messages, checking the status of sent messages,
5
+ managing contacts, managing groups, managing the blacklist, and more.
6
+
7
+ Example:
8
+ smsaero = SmsAero(email="user@example.com", api_key="your_api_key")
9
+ smsaero.send_sms(number=1234567890, text="Hello, World!")
10
+
11
+ This module also provides several exception classes for handling errors that may occur when interacting with the API.
12
+
13
+ Classes:
14
+ SmsAero: Provides methods for interacting with the SmsAero API.
15
+ SmsAeroException: The base class for all exceptions raised by this module.
16
+ SmsAeroConnectionException: Raised when a connection error occurs.
17
+ SmsAeroNoMoneyException: Raised when there is not enough money on the account to perform an operation.
18
+ """
19
+
20
+ from typing import Union, List, Dict, Optional
21
+
22
+ import datetime
23
+ import logging
24
+ import time
25
+
26
+ from urllib.parse import urljoin, quote_plus, urlparse
27
+
28
+ import aiohttp
29
+
30
+ __all__ = [
31
+ "SmsAero",
32
+ "SmsAeroException",
33
+ "SmsAeroConnectionException",
34
+ "SmsAeroNoMoneyException",
35
+ ]
36
+
37
+
38
+ class SmsAeroException(Exception):
39
+ """Super class of all SmsAero Errors."""
40
+
41
+
42
+ class SmsAeroConnectionException(SmsAeroException):
43
+ """A Connection error occurred."""
44
+
45
+
46
+ class SmsAeroNoMoneyException(SmsAeroException):
47
+ """No money on the account."""
48
+
49
+
50
+ logger = logging.getLogger(__name__)
51
+
52
+
53
+ class SmsAero:
54
+ """
55
+ The SmsAero class provides methods for interacting with the SmsAero API.
56
+
57
+ This class provides methods for sending SMS messages, checking the status of sent messages,
58
+ managing contacts, managing groups, managing the blacklist, and more.
59
+ """
60
+
61
+ # List of available gateway URLs
62
+ GATE_URLS = [
63
+ "@gate.smsaero.ru/v2/",
64
+ "@gate.smsaero.org/v2/",
65
+ "@gate.smsaero.net/v2/",
66
+ ]
67
+ # Default signature for the messages
68
+ SIGNATURE = "Sms Aero"
69
+
70
+ def __init__(
71
+ self,
72
+ email: str,
73
+ api_key: str,
74
+ signature: str = SIGNATURE,
75
+ timeout: int = 10,
76
+ url_gate: Optional[str] = None,
77
+ test_mode: bool = False,
78
+ ):
79
+ """
80
+ Initializes the SmsAero class.
81
+
82
+ Parameters:
83
+ email (str): The user's email.
84
+ api_key (str): The user's API key. Should be 32 characters.
85
+ signature (str, optional): The signature for the messages.
86
+ timeout (int, optional): The timeout for the requests.
87
+ allow_phone_validation (bool, optional): Whether to allow phone number validation.
88
+ url_gate (str, optional): The gateway URL. For example, '@local.host/v2/'.
89
+ test_mode (bool, optional): Whether to enable test mode.
90
+ """
91
+ self.__user = email
92
+ self.__akey = api_key
93
+ self.__gate = url_gate
94
+ self.__sign = signature
95
+ self.__sess = None
96
+ self.__time = timeout
97
+ self.__test = test_mode
98
+
99
+ self.init_validate(
100
+ api_key,
101
+ signature,
102
+ timeout,
103
+ url_gate,
104
+ test_mode,
105
+ )
106
+
107
+ self.check_and_format_user_gate()
108
+
109
+ async def init_session(self):
110
+ """
111
+ Asynchronously initializes an aiohttp.ClientSession with a custom User-Agent header.
112
+
113
+ This method checks if an existing session is open and closes it before initializing a new session.
114
+ It sets a custom User-Agent header for the session to identify the client in HTTP requests.
115
+ """
116
+ if self.__sess is None or self.__sess.closed:
117
+ self.__sess = aiohttp.ClientSession(headers={"User-Agent": "SAPythonAsyncClient/3.0.0"})
118
+ logging.debug("Initialized aiohttp.ClientSession")
119
+
120
+ async def close_session(self, *_):
121
+ """
122
+ Asynchronously closes the aiohttp.ClientSession if it exists and is open.
123
+
124
+ This method ensures that the ClientSession is properly closed before the object is destroyed or reused,
125
+ preventing potential resource leaks. It checks if the session exists and is not already closed before
126
+ attempting to close it.
127
+ """
128
+ if self.__sess and not self.__sess.closed:
129
+ await self.__sess.close()
130
+ logging.debug("Closed aiohttp.ClientSession")
131
+
132
+ def check_and_format_user_gate(self):
133
+ """
134
+ This method checks and formats the `self.__gate` attribute to ensure it starts with '@' and ends with '/v2/'.
135
+ If `self.__gate` does not start with '@', it prepends '@' to it.
136
+ If `self.__gate` does not end with '/v2/', it appends '/v2/' to it.
137
+ The method does not return any value but modifies the `self.__gate` attribute in-place.
138
+ """
139
+
140
+ if not self.__gate:
141
+ return
142
+
143
+ parsed_url = urlparse(self.__gate)
144
+
145
+ if not parsed_url.path.startswith("@"):
146
+ parsed_url = parsed_url._replace(path="@" + parsed_url.path)
147
+ if parsed_url.path.endswith("/v2"):
148
+ parsed_url = parsed_url._replace(path=parsed_url.path + "/")
149
+ elif not parsed_url.path.endswith("/v2/"):
150
+ parsed_url = parsed_url._replace(path=parsed_url.path + "/v2/")
151
+
152
+ self.__gate = parsed_url.geturl()
153
+
154
+ def get_gate(self):
155
+ """
156
+ Returns the gateway URL.
157
+ """
158
+ return self.__gate
159
+
160
+ def get_gate_urls(self) -> List[str]:
161
+ """
162
+ Returns a list of gateway URLs for sending requests.
163
+ If a gateway URL was specified during initialization, it returns a list with only that URL.
164
+ Otherwise, it returns a list of all available gateway URLs.
165
+ """
166
+ return [self.__gate] if self.__gate else self.GATE_URLS
167
+
168
+ @staticmethod
169
+ def fill_nums(number: Union[int, List[int]]) -> Dict:
170
+ """
171
+ Takes a number or a list of numbers as input.
172
+ Returns:
173
+ Dict: with the key as 'numbers' if input is a list, or 'number' if input is a single number.
174
+ """
175
+ if not number:
176
+ raise ValueError("Number cannot be empty")
177
+ return {"numbers" if isinstance(number, list) else "number": number}
178
+
179
+ @staticmethod
180
+ def check_response(content) -> Dict:
181
+ """
182
+ Checks the response from the server.
183
+
184
+ If the response contains an error message, it raises an appropriate exception.
185
+ If the response is successful, it returns the data from the response.
186
+
187
+ Parameters:
188
+ response (Response): The response from the server.
189
+
190
+ Returns:
191
+ Dict: The data from the response if the request was successful.
192
+ """
193
+ if content.get("result") == "no credits":
194
+ raise SmsAeroNoMoneyException(content["result"])
195
+ if content.get("result") == "reject":
196
+ raise SmsAeroException(content["reason"])
197
+ if not content.get("success"):
198
+ raise SmsAeroException(content.get("message") or "Unknown error")
199
+
200
+ return content.get("data")
201
+
202
+ def build_url(self, proto: str, selector: str, gate: str, page: Optional[int] = None) -> str:
203
+ """
204
+ Builds a URL for the request.
205
+
206
+ Parameters:
207
+ proto (str): The protocol for the URL (e.g., 'http' or 'https').
208
+ selector (str): The selector for the URL.
209
+ gate (str): The gateway for the URL.
210
+ page (Optional[int], optional): The page number for the URL.
211
+
212
+ Returns:
213
+ str: The built URL.
214
+ """
215
+ url = urljoin(f"{proto}://{quote_plus(self.__user)}:{self.__akey}{gate}", selector)
216
+ if page:
217
+ url = urljoin(url, f"?page={int(page)}")
218
+ return url
219
+
220
+ async def request(
221
+ self,
222
+ selector: str,
223
+ data: Optional[Dict] = None,
224
+ page: Optional[int] = None,
225
+ proto: str = "http",
226
+ ) -> Dict:
227
+ """
228
+ Sends a request to the server.
229
+
230
+ Parameters:
231
+ selector (str): The selector for the URL.
232
+ data (Dict[str, Any], optional): The data to be sent in the request. If not specified, no data will be sent.
233
+ page (int, optional): The page number for the URL. If not specified, no page number will be added to the URL.
234
+ proto (str, optional): The protocol for the URL (e.g., 'http' or 'https'). Default is 'https'.
235
+
236
+ Returns:
237
+ Dict: The data from the response if the request was successful.
238
+ """
239
+ await self.init_session()
240
+
241
+ for gate in self.get_gate_urls():
242
+ try:
243
+ url = self.build_url(proto, selector, gate, page)
244
+ if self.__sess is None:
245
+ raise SmsAeroConnectionException("Session is not initialized")
246
+ async with self.__sess.post(url, json=data or {}, timeout=self.__time) as response:
247
+ logger.debug("Sending request to %s with data %s", url, data)
248
+ json = await response.json()
249
+ logger.debug("Received response: %s", json)
250
+ return self.check_response(json)
251
+ except aiohttp.ClientSSLError:
252
+ # switch to http when got ssl error
253
+ proto = "http"
254
+ continue
255
+ except aiohttp.ClientError:
256
+ # next gate
257
+ continue
258
+ raise SmsAeroConnectionException("All gateways are unavailable")
259
+
260
+ def enable_test_mode(self):
261
+ """
262
+ Enables test mode.
263
+ """
264
+ self.__test = True
265
+
266
+ def disable_test_mode(self):
267
+ """
268
+ Disables test mode.
269
+ """
270
+ self.__test = False
271
+
272
+ def is_test_mode_active(self) -> bool:
273
+ """
274
+ Checks if test mode is active.
275
+
276
+ Returns:
277
+ bool: True if test mode is active, False otherwise.
278
+ """
279
+ return self.__test
280
+
281
+ async def is_authorized(self) -> bool:
282
+ """
283
+ Checks if the user is authorized.
284
+
285
+ This method sends a request to the server to check if the user is authorized.
286
+ It does not require any parameters.
287
+
288
+ Returns:
289
+ bool: True if the user is authorized or SmsAeroException if the user is not authorized.
290
+ """
291
+ return await self.request("auth") is None
292
+
293
+ async def send_sms(
294
+ self,
295
+ number: Union[int, List[int]],
296
+ text: str,
297
+ sign: Optional[str] = None,
298
+ date_to_send: Optional[datetime.datetime] = None,
299
+ callback_url: Optional[str] = None,
300
+ ) -> Dict:
301
+ """
302
+ Sends a message to the specified number or numbers.
303
+
304
+ Parameters:
305
+ number (Union[int, List[int]]): The recipient's phone number or a list of phone numbers.
306
+ text (str): The text of the message.
307
+ sign (str, optional): The signature for the message.
308
+ date_to_send (datetime, optional): The date and time when the message should be sent.
309
+ callback_url (str, optional): The URL to which the server will send a request when the message status changes.
310
+
311
+ Returns:
312
+ Dict: The server's response in JSON format.
313
+
314
+ Example response:
315
+ {
316
+ "id": 12345,
317
+ "from": "Sms Aero",
318
+ "number": "79031234567",
319
+ "text": "Hello, World!",
320
+ "status": 0,
321
+ "extendStatus": "queue",
322
+ "channel": "FREE SIGN",
323
+ "cost": 5.49,
324
+ "dateCreate": 1719119523,
325
+ "dateSend": 1719119523
326
+ }
327
+ """
328
+ self.send_sms_validate(number, text, sign, date_to_send, callback_url)
329
+ data: Dict = {"text": text, "sign": sign or self.__sign, "callbackUrl": callback_url}
330
+ data.update(**self.fill_nums(number))
331
+ if date_to_send:
332
+ data.update({"dateSend": int(time.mktime(date_to_send.timetuple()))})
333
+ return await self.request("sms/testsend" if self.__test else "sms/send", data)
334
+
335
+ async def sms_status(self, sms_id: int) -> Dict:
336
+ """
337
+ Retrieves the status of a specific SMS.
338
+
339
+ Parameters:
340
+ sms_id (int): The ID of the SMS.
341
+
342
+ Returns:
343
+ Dict: The server's response in JSON format.
344
+
345
+ Example response:
346
+ {
347
+ "id": 12345,
348
+ "from": "Sms Aero",
349
+ "number": "79031234567",
350
+ "text": "Hello, World!",
351
+ "status": 1,
352
+ "extendStatus": "delivery",
353
+ "channel": "FREE SIGN",
354
+ "cost": "5.49",
355
+ "dateCreate": 1719115820,
356
+ "dateSend": 1719115820,
357
+ "dateAnswer": 1719115825
358
+ }
359
+ """
360
+ return await self.request("sms/teststatus" if self.__test else "sms/status", {"id": int(sms_id)})
361
+
362
+ async def sms_list(
363
+ self,
364
+ number: Optional[Union[int, List[int]]] = None,
365
+ text: Optional[str] = None,
366
+ page: Optional[int] = None,
367
+ ) -> Dict:
368
+ """
369
+ Retrieves a list of SMS messages.
370
+
371
+ Parameters:
372
+ number (Union[int, List[int]], optional): The recipient's phone number or a list of phone numbers.
373
+ text (str, optional): The text of the message.
374
+ page (int, optional): The page number for the URL.
375
+
376
+ Returns:
377
+ Dict: The server's response in JSON format.
378
+
379
+
380
+ Example response:
381
+ {
382
+ "0": {
383
+ "id": 12345,
384
+ "from": "Sms Aero",
385
+ "number": "79031234567",
386
+ "text": "Hello, World!",
387
+ "status": 1,
388
+ "extendStatus": "delivery",
389
+ "channel": "FREE SIGN",
390
+ "cost": "5.49",
391
+ "dateCreate": 1697533302,
392
+ "dateSend": 1697533302,
393
+ "dateAnswer": 1697533306
394
+ },
395
+ "1": {
396
+ ...
397
+ },
398
+ ...
399
+ "links": {
400
+ "self": "/v2/sms/list?page=1",
401
+ "first": "/v2/sms/list?page=1",
402
+ "last": "/v2/sms/list?page=3",
403
+ "next": "/v2/sms/list?page=2"
404
+ },
405
+ "totalCount": "138"
406
+ }
407
+ """
408
+ self.sms_list_validate(number, text, page)
409
+ data: Dict = {}
410
+ if number:
411
+ data.update(self.fill_nums(number))
412
+ if text:
413
+ data.update({"text": text})
414
+ return await self.request("sms/testlist" if self.__test else "sms/list", data, page)
415
+
416
+ async def balance(self) -> Dict:
417
+ """
418
+ Retrieves the balance of the user's account.
419
+
420
+ Returns:
421
+ Dict: The server's response in JSON format.
422
+
423
+ Example response:
424
+ {
425
+ "balance": 337.03
426
+ }
427
+ """
428
+ return await self.request("balance")
429
+
430
+ async def balance_add(self, amount: float, card_id: int) -> Dict:
431
+ """
432
+ Adds a specified amount to the balance using a specified card.
433
+
434
+ Parameters:
435
+ amount (float): The amount to be added to the balance.
436
+ card_id (int): The ID of the card to be used for the transaction.
437
+
438
+ Returns:
439
+ Dict: The server's response in JSON format.
440
+
441
+ Example response:
442
+ {
443
+ "sum": 100
444
+ }
445
+ """
446
+ return await self.request("balance/add", {"sum": float(amount), "cardId": int(card_id)})
447
+
448
+ async def cards(self) -> Dict:
449
+ """
450
+ Retrieves the cards associated with the user's account.
451
+
452
+ This method sends a request to the server to retrieve the cards associated with the user's account.
453
+ It does not require any parameters.
454
+
455
+ Returns:
456
+ Dict: The server's response in JSON format.
457
+
458
+ Example response:
459
+ {
460
+ "0": {
461
+ "id": 12345,
462
+ "number": Visa*****1234
463
+ },
464
+ "1": {
465
+ "id": 12346,
466
+ "number": MasterCard*****4321
467
+ }
468
+ }
469
+ """
470
+ return await self.request("cards")
471
+
472
+ async def tariffs(self) -> Dict:
473
+ """
474
+ Retrieves the tariffs for the user's account.
475
+
476
+ This method sends a request to the server to retrieve the tariffs for the user's account.
477
+ It does not require any parameters.
478
+
479
+ Returns:
480
+ Dict: The server's response in JSON format.
481
+
482
+ Example response:
483
+ {
484
+ "FREE SIGN": {
485
+ "MEGAFON": "8.99",
486
+ "MTS": "4.99",
487
+ "BEELINE": "5.49",
488
+ "TELE2": "4.79",
489
+ "OTHER": "5.19"
490
+ },
491
+ ...
492
+ }
493
+ """
494
+ return await self.request("tariffs")
495
+
496
+ async def sign_list(self, page: Optional[int] = None) -> Dict:
497
+ """
498
+ Retrieves a list of signatures for the user's account.
499
+
500
+ This method sends a request to the server to retrieve a list of signatures for the user's account.
501
+ It accepts an optional parameter for pagination.
502
+
503
+ Parameters:
504
+ page (int, optional): The page number for the URL.
505
+
506
+ Returns:
507
+ Dict: The server's response in JSON format.
508
+
509
+ Example response:
510
+ {
511
+ "totalCount": "1",
512
+ "0": {
513
+ "id": 12345,
514
+ "name": "TestSign",
515
+ "status": 1,
516
+ "extendStatus": "approve",
517
+ "statusOperators": {
518
+ "1": {
519
+ "operator": 1,
520
+ "extendOperator": "MEGAFON",
521
+ "status": 1,
522
+ "extendStatus": "approve"
523
+ },
524
+ "4": {
525
+ "operator": 4,
526
+ "extendOperator": "MTS",
527
+ "status": 1,
528
+ "extendStatus": "approve"
529
+ },
530
+ "5": {
531
+ "operator": 5,
532
+ "extendOperator": "BEELINE",
533
+ "status": 1,
534
+ "extendStatus": "approve"
535
+ },
536
+ "6": {
537
+ "operator": 6,
538
+ "extendOperator": "TELE2",
539
+ "status": 1,
540
+ "extendStatus": "approve"
541
+ }
542
+ }
543
+ }
544
+ }
545
+ """
546
+ return await self.request("sign/list", page=page)
547
+
548
+ async def group_add(self, name: str) -> Dict:
549
+ """
550
+ Adds a new group to the SmsAero service.
551
+
552
+ Parameters:
553
+ name (str): The name of the group to be added.
554
+
555
+ Returns:
556
+ Dict: The server's response in JSON format.
557
+
558
+ Example response:
559
+ {
560
+ "id": 12345,
561
+ "name": "TestGroup",
562
+ "count": 0
563
+ }
564
+ """
565
+ return await self.request("group/add", {"name": name})
566
+
567
+ async def group_delete(self, group_id: int) -> bool:
568
+ """
569
+ Deletes a group from the SmsAero service.
570
+
571
+ Parameters:
572
+ group_id (int): The ID of the group to be deleted.
573
+
574
+ Returns:
575
+ bool: True if deletion was successful, SmsAeroException if deletion was unsuccessful.
576
+ """
577
+ return await self.request("group/delete", {"id": int(group_id)}) is None
578
+
579
+ async def group_delete_all(self) -> bool:
580
+ """
581
+ Deletes all group from the SmsAero service.
582
+
583
+ Returns:
584
+ bool: True if deletion was successful, SmsAeroException if deletion was unsuccessful.
585
+ """
586
+ return await self.request("group/delete-all") is None
587
+
588
+ async def group_list(self, page: Optional[int] = None) -> Dict:
589
+ """
590
+ Retrieves a list of groups from the SmsAero service.
591
+
592
+ Parameters:
593
+ page (Optional[int], optional): The page number for the URL.
594
+
595
+ Returns:
596
+ Dict: The server's response in JSON format.
597
+
598
+ Example response:
599
+ {
600
+ "0": {
601
+ "id": "12345",
602
+ "name": "test_group",
603
+ "countContacts": "0"
604
+ },
605
+ ...,
606
+ "links": {
607
+ "self": "/v2/group/list?page=1",
608
+ "first": "/v2/group/list?page=1",
609
+ "last": "/v2/group/list?page=1"
610
+ },
611
+ "totalCount": "4"
612
+ }
613
+ """
614
+ return await self.request("group/list", page=page)
615
+
616
+ async def contact_add(
617
+ self,
618
+ number: Union[int, List[int]],
619
+ group_id: Optional[int] = None,
620
+ birthday: Optional[str] = None,
621
+ sex: Optional[str] = None,
622
+ last_name: Optional[str] = None,
623
+ first_name: Optional[str] = None,
624
+ surname: Optional[str] = None,
625
+ param1: Optional[str] = None,
626
+ param2: Optional[str] = None,
627
+ param3: Optional[str] = None,
628
+ ) -> Dict:
629
+ """
630
+ Adds a contact or a list of contacts to the user's account.
631
+
632
+ Parameters:
633
+ number (Union[int, List[int]]): The phone number or a list of phone numbers of the contacts.
634
+ group_id (int, optional): The ID of the group to which the contacts belong.
635
+ birthday (str, optional): The birthday of the contact.
636
+ sex (str, optional): The sex of the contact.
637
+ last_name (str, optional): The last name of the contact.
638
+ first_name (str, optional): The first name of the contact.
639
+ surname (str, optional): The surname of the contact.
640
+ param1 (str, optional): The first custom parameter for the contact.
641
+ param2 (str, optional): The second custom parameter for the contact.
642
+ param3 (str, optional): The third custom parameter for the contact.
643
+
644
+ Returns:
645
+ Dict: The server's response in JSON format.
646
+
647
+ Example response:
648
+ {
649
+ "id": 12345,
650
+ "number": "79031234567",
651
+ "sex": "male",
652
+ "lname": "Doe",
653
+ "fname": "John",
654
+ "sname": "Smith",
655
+ "param1": "custom1",
656
+ "param2": "custom2",
657
+ "param3": "custom3",
658
+ "operator": 5,
659
+ "extendOperator": "BEELINE"
660
+ }
661
+ """
662
+
663
+ self.contact_add_validate(
664
+ number, group_id, birthday, sex, last_name, first_name, surname, param1, param2, param3
665
+ )
666
+ return await self.request(
667
+ "contact/add",
668
+ {
669
+ "number": number,
670
+ "groupId": group_id and int(group_id),
671
+ "birthday": birthday,
672
+ "sex": sex,
673
+ "lname": last_name,
674
+ "fname": first_name,
675
+ "sname": surname,
676
+ "param1": param1,
677
+ "param2": param2,
678
+ "param3": param3,
679
+ },
680
+ )
681
+
682
+ async def contact_delete(self, contact_id: int) -> bool:
683
+ """
684
+ Deletes a contact from the user's account.
685
+
686
+ Parameters:
687
+ contact_id (int): The ID of the contact to be deleted.
688
+
689
+ Returns:
690
+ bool: True if deletion was successful, SmsAeroException if deletion was unsuccessful.
691
+ """
692
+ return await self.request("contact/delete", {"id": int(contact_id)}) is None
693
+
694
+ async def contact_delete_all(self) -> bool:
695
+ """
696
+ Deletes all contacts from the user's account.
697
+
698
+ Returns:
699
+ bool: True if deletion was successful, SmsAeroException if deletion was unsuccessful.
700
+ """
701
+ return await self.request("contact/delete-all") is None
702
+
703
+ async def contact_list(
704
+ self,
705
+ number: Optional[Union[int, List[int]]] = None,
706
+ group_id: Optional[int] = None,
707
+ birthday: Optional[str] = None,
708
+ sex: Optional[str] = None,
709
+ operator: Optional[str] = None,
710
+ last_name: Optional[str] = None,
711
+ first_name: Optional[str] = None,
712
+ surname: Optional[str] = None,
713
+ page: Optional[int] = None,
714
+ ) -> Dict:
715
+ """
716
+ Retrieves a list of contacts from the user's account.
717
+
718
+ Parameters:
719
+ number (Optional[Union[int, List[int]]], optional): The phone number or a list of phone numbers of the contacts.
720
+ group_id (int, optional): The ID of the group to which the contacts belong.
721
+ birthday (str, optional): The birthday of the contact.
722
+ sex (str, optional): The sex of the contact.
723
+ operator (str, optional): The operator of the contact.
724
+ last_name (str, optional): The last name of the contact.
725
+ first_name (str, optional): The first name of the contact.
726
+ surname (str, optional): The surname of the contact.
727
+ page (int, optional): The page number for the URL.
728
+
729
+ Returns:
730
+ Dict: The server's response in JSON format.
731
+
732
+ Example response:
733
+ {
734
+ "0": {
735
+ "id": 12345,
736
+ "number": 79031234567,
737
+ "sex": "male",
738
+ "lname": "Doe",
739
+ "fname": "John",
740
+ "sname": "Smith",
741
+ "param1": "",
742
+ "param2": "",
743
+ "param3": "",
744
+ "operator": 5,
745
+ "extendOperator": "BEELINE",
746
+ "groups": [
747
+ {
748
+ "id": 12345,
749
+ "name": "test_group"
750
+ }
751
+ ],
752
+ "hlrStatus": 1,
753
+ "extendHlrStatus": "available"
754
+ },
755
+ ...,
756
+ "links": {
757
+ "self": "/v2/contact/list?page=1",
758
+ "first": "/v2/contact/list?page=1",
759
+ "last": "/v2/contact/list?page=1"
760
+ },
761
+ "totalCount": "5"
762
+ }
763
+ """
764
+
765
+ self.contact_list_validate(number, group_id, birthday, sex, operator, last_name, first_name, surname, page)
766
+ return await self.request(
767
+ "contact/list",
768
+ {
769
+ "number": number,
770
+ "groupId": group_id and int(group_id),
771
+ "birthday": birthday,
772
+ "sex": sex,
773
+ "operator": operator,
774
+ "lname": last_name,
775
+ "fname": first_name,
776
+ "sname": surname,
777
+ },
778
+ page,
779
+ )
780
+
781
+ async def blacklist_add(self, number: Union[int, List[int]]) -> Dict:
782
+ """
783
+ Adds a number or a list of numbers to the blacklist.
784
+
785
+ Parameters:
786
+ number (Union[int, List[int]]): The number or a list of numbers to be added to the blacklist.
787
+
788
+ Returns:
789
+ Dict: The server's response in JSON format.
790
+
791
+ Example response:
792
+ {
793
+ "id": 12345,
794
+ "number": "79031234567",
795
+ "sex": "male",
796
+ "lname": "Doe",
797
+ "fname": "John",
798
+ "sname": "Smith",
799
+ "bday": null,
800
+ "param": null,
801
+ "param2": null,
802
+ "param3": null
803
+ }
804
+ """
805
+ return await self.request("blacklist/add", self.fill_nums(number))
806
+
807
+ async def blacklist_list(self, number: Optional[Union[int, List[int]]] = None, page: Optional[int] = None) -> Dict:
808
+ """
809
+ Retrieves a list of numbers from the blacklist.
810
+
811
+ Parameters:
812
+ number (Optional[Union[int, List[int]]], optional): The number or a list of numbers to be retrieved.
813
+ page (Optional[int], optional): The page number for the URL.
814
+
815
+ Returns:
816
+ Dict: The server's response in JSON format.
817
+
818
+ Example response:
819
+ {
820
+ "0": {
821
+ "id": "12345",
822
+ "number": "79031234567",
823
+ "sex": "male",
824
+ "lname": "Doe",
825
+ "fname": "John",
826
+ "sname": "Smith",
827
+ "bday": null,
828
+ "param": null,
829
+ "param2": null,
830
+ "param3": null
831
+ },
832
+ "links": {
833
+ "self": "/v2/blacklist/list?page=1",
834
+ "first": "/v2/blacklist/list?page=1",
835
+ "last": "/v2/blacklist/list?page=1"
836
+ },
837
+ "totalCount": "1"
838
+ }
839
+ """
840
+ return await self.request("blacklist/list", self.fill_nums(number) if number else None, page)
841
+
842
+ async def blacklist_delete(self, blacklist_id: int) -> bool:
843
+ """
844
+ Deletes a number from the blacklist.
845
+
846
+ Parameters:
847
+ blacklist_id (int): The ID of the number to be deleted from the blacklist.
848
+
849
+ Returns:
850
+ bool: True if deletion was successful, SmsAeroException if deletion was unsuccessful.
851
+ """
852
+ return await self.request("blacklist/delete", {"id": int(blacklist_id)}) is None
853
+
854
+ async def hlr_check(self, number: Union[int, List[int]]) -> Dict:
855
+ """
856
+ Checks the Home Location Register (HLR) for a number or a list of numbers.
857
+
858
+ Parameters:
859
+ number (Union[int, List[int]]): The number or a list of numbers to be checked.
860
+
861
+ Returns:
862
+ Dict: The server's response in JSON format.
863
+
864
+ Example response:
865
+ {
866
+ "id": 12345,
867
+ "number": "79031234567",
868
+ "hlrStatus": 4,
869
+ "extendHlrStatus": "in work"
870
+ }
871
+ """
872
+ return await self.request("hlr/check", self.fill_nums(number))
873
+
874
+ async def hlr_status(self, hlr_id: int) -> Dict:
875
+ """
876
+ Retrieves the status of a specific HLR check.
877
+
878
+ Parameters:
879
+ hlr_id (int): The ID of the HLR check.
880
+
881
+ Returns:
882
+ Dict: The server's response in JSON format.
883
+
884
+ Example response:
885
+ {
886
+ "id": 12345,
887
+ "number": "79031234567",
888
+ "hlrStatus": 1,
889
+ "extendHlrStatus": "available"
890
+ }
891
+ """
892
+ return await self.request("hlr/status", {"id": int(hlr_id)})
893
+
894
+ async def number_operator(self, number: Union[int, List[int]]) -> Dict:
895
+ """
896
+ Retrieves the operator of a number or a list of numbers.
897
+
898
+ Parameters:
899
+ number (Union[int, List[int]]): The number or a list of numbers.
900
+
901
+ Returns:
902
+ Dict: The server's response in JSON format.
903
+
904
+ Example response:
905
+ {
906
+ "number": "79031234567",
907
+ "operator": 5,
908
+ "extendOperator": "BEELINE"
909
+ }
910
+ """
911
+ return await self.request("number/operator", self.fill_nums(number))
912
+
913
+ async def viber_send(
914
+ self,
915
+ sign: str,
916
+ channel: str,
917
+ text: str,
918
+ number: Optional[Union[int, List[int]]] = None,
919
+ group_id: Optional[int] = None,
920
+ image_source: Optional[str] = None,
921
+ text_button: Optional[str] = None,
922
+ link_button: Optional[str] = None,
923
+ date_send: Optional[str] = None,
924
+ sign_sms: Optional[str] = None,
925
+ channel_sms: Optional[str] = None,
926
+ text_sms: Optional[str] = None,
927
+ price_sms: Optional[int] = None,
928
+ ) -> Dict:
929
+ """
930
+ Sends a Viber message.
931
+
932
+ Parameters:
933
+ sign (str): The signature of the message.
934
+ channel (str): The channel of the message.
935
+ text (str): The text of the message.
936
+ number (Union[int, List[int]], optional): The phone number or a list of phone numbers to send the message to.
937
+ group_id (int, optional): The ID of the group to send the message to.
938
+ image_source (str, optional): The source of the image in the message.
939
+ text_button (str, optional): The text of the button in the message.
940
+ link_button (str, optional): The link of the button in the message.
941
+ date_send (str, optional): The date to send the message.
942
+ sign_sms (str, optional): The signature of the SMS fallback.
943
+ channel_sms (str, optional): The channel of the SMS fallback.
944
+ text_sms (str, optional): The text of the SMS fallback.
945
+ price_sms (int, optional): The price of the SMS fallback.
946
+
947
+ Returns:
948
+ Dict: The server's response in JSON format.
949
+
950
+ Example response:
951
+ {
952
+ "id": 12345,
953
+ "number": "79031234567",
954
+ "count": 1,
955
+ "sign": "Viber",
956
+ "channel": "OFFICIAL",
957
+ "text": "Hello, World!",
958
+ "cost": 2.25,
959
+ "status": 1,
960
+ "extendStatus": "moderation",
961
+ "dateCreate": 1511153253,
962
+ "dateSend": 1511153253,
963
+ "countSend": 0,
964
+ "countDelivered": 0,
965
+ "countWrite": 0,
966
+ "countUndelivered": 0,
967
+ "countError": 0
968
+ }
969
+ """
970
+
971
+ self.viber_send_validate(
972
+ sign,
973
+ channel,
974
+ text,
975
+ number,
976
+ group_id,
977
+ image_source,
978
+ text_button,
979
+ link_button,
980
+ date_send,
981
+ sign_sms,
982
+ channel_sms,
983
+ text_sms,
984
+ price_sms,
985
+ )
986
+
987
+ data = {
988
+ "groupId": group_id and int(group_id),
989
+ "sign": sign and str(sign),
990
+ "channel": channel and str(channel),
991
+ "text": text,
992
+ "imageSource": image_source,
993
+ "textButton": text_button,
994
+ "linkButton": link_button,
995
+ "dateSend": date_send,
996
+ "signSms": sign_sms,
997
+ "channelSms": channel_sms,
998
+ "textSms": text_sms,
999
+ "priceSms": price_sms,
1000
+ }
1001
+ if number:
1002
+ data.update(self.fill_nums(number))
1003
+ return await self.request("viber/send", data)
1004
+
1005
+ async def viber_sign_list(self) -> Dict:
1006
+ """
1007
+ Retrieves a list of Viber signs.
1008
+
1009
+ Returns:
1010
+ Dict: The server's response in JSON format.
1011
+
1012
+ Example response:
1013
+ {
1014
+ "0": {
1015
+ "id": 12345,
1016
+ "name": "GOOD SIGN",
1017
+ "status": 1,
1018
+ "extendStatus": "active",
1019
+ "statusOperators": {
1020
+ "1": {
1021
+ "operator": 1,
1022
+ "extendOperator": "MEGAFON",
1023
+ "status": 0,
1024
+ "extendStatus": "moderation"
1025
+ },
1026
+ ....
1027
+ }
1028
+ },
1029
+ ...
1030
+ }
1031
+ """
1032
+ return await self.request("viber/sign/list")
1033
+
1034
+ async def viber_list(self, page: Optional[int] = None) -> Dict:
1035
+ """
1036
+ Retrieves a list of Viber messages.
1037
+
1038
+ Parameters:
1039
+ page (int, optional): The page number for the URL.
1040
+
1041
+ Returns:
1042
+ Dict: The server's response in JSON format.
1043
+
1044
+ Example response:
1045
+ {
1046
+ 0: {
1047
+ "id": 1,
1048
+ "number": "79031234567",
1049
+ "count": 1,
1050
+ "sign": "Viber",
1051
+ "channel": "OFFICIAL",
1052
+ "text": "Hello, World!",
1053
+ "cost": 2.25,
1054
+ "status": 1,
1055
+ "extendStatus": "moderation",
1056
+ "dateCreate": 1511153253,
1057
+ "dateSend": 1511153253,
1058
+ "countSend": 0,
1059
+ "countDelivered": 0,
1060
+ "countWrite": 0,
1061
+ "countUndelivered": 0,
1062
+ "countError": 0
1063
+ },
1064
+ ...
1065
+ "links": {
1066
+ "self": "/v2/viber/list?page=1",
1067
+ "next": "/v2/viber/list?page=2",
1068
+ "last": "/v2/viber/list?page=3"
1069
+ }
1070
+ """
1071
+ self.page_validate(page)
1072
+ return await self.request("viber/list", page=page)
1073
+
1074
+ async def viber_statistics(self, sending_id: int, page: Optional[int] = None) -> Dict:
1075
+ """
1076
+ Retrieves the statistics for a specific Viber message.
1077
+
1078
+ Parameters:
1079
+ sending_id (int): The ID of the Viber message.
1080
+
1081
+ Returns:
1082
+ Dict: The server's response in JSON format.
1083
+
1084
+ Example response:
1085
+ {
1086
+ 0: {
1087
+ "number": "79031234567",
1088
+ "status": 0,
1089
+ "extendStatus": "send",
1090
+ "dateSend": 1511153341
1091
+ },
1092
+ 1: {
1093
+ "number": "79031234568",
1094
+ "status": 2,
1095
+ "extendStatus": "write",
1096
+ "dateSend": 1511153341
1097
+ },
1098
+ "links": {
1099
+ "self": "/v2/viber/statistic?sendingId=1&page=1"
1100
+ }
1101
+ }
1102
+ """
1103
+ self.page_validate(page)
1104
+ return await self.request("viber/statistic", {"sendingId": int(sending_id)}, page=page)
1105
+
1106
+ async def send_telegram(
1107
+ self,
1108
+ number: Union[int, List[int]],
1109
+ code: int,
1110
+ sign: Optional[str] = None,
1111
+ text: Optional[str] = None,
1112
+ ) -> Dict:
1113
+ """
1114
+ Sends a Telegram code to the specified number or numbers.
1115
+
1116
+ Parameters:
1117
+ number (Union[int, List[int]]): The recipient's phone number or a list of phone numbers.
1118
+ code (int): The Telegram code (4 to 8 digits).
1119
+ sign (str, optional): The SMS sender name.
1120
+ text (str, optional): The SMS message text.
1121
+
1122
+ When using text and sign parameters, if the Telegram code is not delivered,
1123
+ an SMS will be sent with the specified values.
1124
+
1125
+ Returns:
1126
+ Dict: The server's response in JSON format.
1127
+
1128
+ Example response:
1129
+ {
1130
+ "id": 1,
1131
+ "number": "79990000000",
1132
+ "telegramCode": "1234",
1133
+ "smsText": "Ваш код 1234",
1134
+ "smsFrom": "SMS Aero",
1135
+ "idSms": null,
1136
+ "status": 0,
1137
+ "extendStatus": "queue",
1138
+ "cost": "1.00",
1139
+ "dateCreate": 1732796285
1140
+ }
1141
+ """
1142
+ self.send_telegram_validate(number, code, sign, text)
1143
+ data: Dict = {"code": int(code)}
1144
+ data.update(**self.fill_nums(number))
1145
+ if sign:
1146
+ data["sign"] = sign
1147
+ if text:
1148
+ data["text"] = text
1149
+ return await self.request("telegram/send", data)
1150
+
1151
+ async def telegram_status(self, telegram_id: int) -> Dict:
1152
+ """
1153
+ Retrieves the status of a Telegram code delivery.
1154
+
1155
+ Parameters:
1156
+ telegram_id (int): The message ID returned by the service when sending.
1157
+
1158
+ Returns:
1159
+ Dict: The server's response in JSON format.
1160
+
1161
+ Example response:
1162
+ {
1163
+ "id": 1,
1164
+ "number": "79990000000",
1165
+ "telegramCode": "1234",
1166
+ "smsText": "Ваш код 1234",
1167
+ "smsFrom": "SMS Aero",
1168
+ "idSms": null,
1169
+ "status": 1,
1170
+ "extendStatus": "delivery",
1171
+ "cost": "1.00",
1172
+ "dateCreate": 1732796285
1173
+ }
1174
+ """
1175
+ return await self.request("telegram/status", {"id": int(telegram_id)})
1176
+
1177
+ def phone_validation(self, number: Union[int, List[int]]) -> None:
1178
+ """
1179
+ Validates the phone number or a list of phone numbers.
1180
+
1181
+ If number is of type int or a list of ints and if it is within the valid length range (7 to 15).
1182
+ If number is not of type int or a list of ints, or is not within the valid length range, it raises exception.
1183
+
1184
+ Parameters:
1185
+ number (Union[int, List[int]]): The phone number or a list of phone numbers to validate.
1186
+
1187
+ Raises:
1188
+ TypeError: If the number is not of type int or a list of ints.
1189
+ ValueError: If the number is not within the valid length range.
1190
+ """
1191
+ if not isinstance(number, (int, list)):
1192
+ raise TypeError("number must be an integer or a list of integers")
1193
+ if isinstance(number, int) and not 7 <= len(str(number)) <= 15:
1194
+ raise ValueError("Length of number must be between 7 and 15")
1195
+ if isinstance(number, list) and any(not (7 <= len(str(num)) <= 15) for num in number):
1196
+ raise ValueError("Length of each number in the list must be between 7 and 15")
1197
+ if isinstance(number, list) and any(not isinstance(num, int) for num in number):
1198
+ raise ValueError("Type of each number in the list must be integer")
1199
+
1200
+ @staticmethod
1201
+ def page_validate(page: Optional[int]) -> None:
1202
+ """
1203
+ Validates the page parameter.
1204
+
1205
+ This function checks if the page is of type int and if it is greater than 0.
1206
+ If the page is not of type int or is less than or equal to 0, it raises an appropriate exception.
1207
+
1208
+ Parameters:
1209
+ page (Optional[int]): The page number to validate.
1210
+
1211
+ Raises:
1212
+ ValueError: If any of the parameters are invalid.
1213
+ TypeError: If any of the parameters have an incorrect type.
1214
+ """
1215
+ if page is not None:
1216
+ if not isinstance(page, int):
1217
+ raise TypeError("page must be an integer")
1218
+ if page <= 0:
1219
+ raise ValueError("page must be greater than 0")
1220
+
1221
+ def send_sms_validate(
1222
+ self,
1223
+ number: Union[int, List[int]],
1224
+ text: str,
1225
+ sign: Optional[str] = None,
1226
+ date_to_send: Optional[datetime.datetime] = None,
1227
+ callback_url: Optional[str] = None,
1228
+ ) -> None:
1229
+ """
1230
+ Validates the parameters for the send_sms method.
1231
+
1232
+ Parameters:
1233
+ number (Union[int, List[int]]): The recipient's phone number or a list of phone numbers.
1234
+ text (str): The text of the message.
1235
+ sign (str, optional): The signature of the message.
1236
+ date_to_send (datetime, optional): The date to send the message.
1237
+ callback_url (str, optional): The callback URL for the message status.
1238
+
1239
+ Raises:
1240
+ TypeError: If any of the parameters have an incorrect type.
1241
+ ValueError: If any of the parameters have an incorrect value.
1242
+ """
1243
+ if not isinstance(text, str):
1244
+ raise TypeError("text must be a string")
1245
+ if not 2 <= len(text) <= 640:
1246
+ raise ValueError("Length of text must be between 2 and 640")
1247
+ if sign is not None and not isinstance(sign, str):
1248
+ raise TypeError("sign must be a string")
1249
+ if date_to_send is not None and not isinstance(date_to_send, datetime.datetime):
1250
+ raise TypeError("date_to_send must be a datetime object")
1251
+ if callback_url is not None and not isinstance(callback_url, str):
1252
+ raise TypeError("callback_url must be a string")
1253
+ if callback_url is not None:
1254
+ parsed_url = urlparse(callback_url)
1255
+ if not all([parsed_url.scheme, parsed_url.netloc, parsed_url.path]):
1256
+ raise ValueError("callback_url must be a valid URL")
1257
+
1258
+ self.phone_validation(number)
1259
+
1260
+ def sms_list_validate(
1261
+ self,
1262
+ number: Optional[Union[int, List[int]]] = None,
1263
+ text: Optional[str] = None,
1264
+ page: Optional[int] = None,
1265
+ ) -> None:
1266
+ """
1267
+ Validates the parameters for the sms_list method.
1268
+
1269
+ Parameters:
1270
+ number (Union[int, List[int]], optional): The recipient's phone number or a list of phone numbers.
1271
+ text (str, optional): The text of the message.
1272
+ page (int, optional): The page number for the URL.
1273
+
1274
+ Raises:
1275
+ TypeError: If any of the parameters have an incorrect type.
1276
+ ValueError: If any of the parameters have an incorrect value.
1277
+ """
1278
+ if number:
1279
+ self.phone_validation(number)
1280
+ if text is not None and not isinstance(text, str):
1281
+ raise TypeError("text must be a string")
1282
+ self.page_validate(page)
1283
+
1284
+ def viber_send_validate(
1285
+ self,
1286
+ sign: str,
1287
+ channel: str,
1288
+ text: str,
1289
+ number: Optional[Union[int, List[int]]] = None,
1290
+ group_id: Optional[int] = None,
1291
+ image_source: Optional[str] = None,
1292
+ text_button: Optional[str] = None,
1293
+ link_button: Optional[str] = None,
1294
+ date_send: Optional[str] = None,
1295
+ sign_sms: Optional[str] = None,
1296
+ channel_sms: Optional[str] = None,
1297
+ text_sms: Optional[str] = None,
1298
+ price_sms: Optional[int] = None,
1299
+ ) -> None:
1300
+ """
1301
+ Validates the parameters for the viber_send method.
1302
+
1303
+ Parameters:
1304
+ number (Union[int, List[int]]): The recipient's phone number or a list of phone numbers.
1305
+ text (str): The text of the message.
1306
+ sign (str, optional): The signature of the message.
1307
+ channel (str, optional): The channel of the message.
1308
+ group_id (int, optional): The ID of the group to send the message to.
1309
+ image_source (str, optional): The source of the image in the message.
1310
+ text_button (str, optional): The text of the button in the message.
1311
+ link_button (str, optional): The link of the button in the message.
1312
+ date_send (str, optional): The date to send the message.
1313
+ sign_sms (str, optional): The signature of the SMS fallback.
1314
+ channel_sms (str, optional): The channel of the SMS fallback.
1315
+ text_sms (str, optional): The text of the SMS fallback.
1316
+ price_sms (int, optional): The price of the SMS fallback.
1317
+
1318
+ Raises:
1319
+ TypeError: If any of the parameters have an incorrect type.
1320
+ ValueError: If any of the parameters have an incorrect value.
1321
+ """
1322
+ if not isinstance(text, str):
1323
+ raise TypeError("Text must be a string.")
1324
+ if not 2 <= len(text) <= 640:
1325
+ raise ValueError("Text length must be between 2 and 640 characters.")
1326
+ if sign is not None and not isinstance(sign, str):
1327
+ raise TypeError("Sign must be a string.")
1328
+ if sign is not None and not 2 <= len(sign) <= 64:
1329
+ raise ValueError("Sign length must be between 2 and 64 characters.")
1330
+ if channel is not None and not isinstance(channel, str):
1331
+ raise TypeError("Channel must be a string.")
1332
+ if group_id is not None and not isinstance(group_id, int):
1333
+ raise TypeError("Group ID must be an integer.")
1334
+ if image_source is not None and not isinstance(image_source, str):
1335
+ raise TypeError("Image source must be a string.")
1336
+ if text_button is not None and not isinstance(text_button, str):
1337
+ raise TypeError("Text button must be a string.")
1338
+ if link_button is not None and not isinstance(link_button, str):
1339
+ raise TypeError("Link button must be a string.")
1340
+ if date_send is not None and not isinstance(date_send, str):
1341
+ raise TypeError("Date send must be a string.")
1342
+ if sign_sms is not None and not isinstance(sign_sms, str):
1343
+ raise TypeError("Sign SMS must be a string.")
1344
+ if channel_sms is not None and not isinstance(channel_sms, str):
1345
+ raise TypeError("Channel SMS must be a string.")
1346
+ if text_sms is not None and not isinstance(text_sms, str):
1347
+ raise TypeError("Text SMS must be a string.")
1348
+ if price_sms is not None and not isinstance(price_sms, int):
1349
+ raise TypeError("Price SMS must be an integer.")
1350
+ if number is not None:
1351
+ self.phone_validation(number)
1352
+
1353
+ def contact_add_validate(
1354
+ self,
1355
+ number: Union[int, List[int]],
1356
+ group_id: Optional[int] = None,
1357
+ birthday: Optional[str] = None,
1358
+ sex: Optional[str] = None,
1359
+ last_name: Optional[str] = None,
1360
+ first_name: Optional[str] = None,
1361
+ surname: Optional[str] = None,
1362
+ param1: Optional[str] = None,
1363
+ param2: Optional[str] = None,
1364
+ param3: Optional[str] = None,
1365
+ ) -> None:
1366
+ """
1367
+ Validates the parameters for the contact_add method.
1368
+
1369
+ Parameters:
1370
+ number (Union[int, List[int]]): The phone number or a list of phone numbers of the contacts.
1371
+ group_id (int, optional): The ID of the group to which the contacts belong.
1372
+ birthday (str, optional): The birthday of the contact.
1373
+ sex (str, optional): The sex of the contact.
1374
+ last_name (str, optional): The last name of the contact.
1375
+ first_name (str, optional): The first name of the contact.
1376
+ surname (str, optional): The surname of the contact.
1377
+ param1 (str, optional): The first custom parameter for the contact.
1378
+ param2 (str, optional): The second custom parameter for the contact.
1379
+ param3 (str, optional): The third custom parameter for the contact.
1380
+
1381
+ Raises:
1382
+ TypeError: If any of the parameters have an incorrect type.
1383
+ ValueError: If any of the parameters have an incorrect value.
1384
+ """
1385
+ if number is not None:
1386
+ self.phone_validation(number)
1387
+ if group_id is not None and not isinstance(group_id, int):
1388
+ raise TypeError("Group ID must be an integer.")
1389
+ if birthday is not None and not isinstance(birthday, str):
1390
+ raise TypeError("Birthday must be a string.")
1391
+ if sex is not None and not isinstance(sex, str):
1392
+ raise TypeError("Sex must be a string.")
1393
+ if last_name is not None and not isinstance(last_name, str):
1394
+ raise TypeError("Last name must be a string.")
1395
+ if first_name is not None and not isinstance(first_name, str):
1396
+ raise TypeError("First name must be a string.")
1397
+ if surname is not None and not isinstance(surname, str):
1398
+ raise TypeError("Surname must be a string.")
1399
+ if param1 is not None and not isinstance(param1, str):
1400
+ raise TypeError("Param1 must be a string.")
1401
+ if param2 is not None and not isinstance(param2, str):
1402
+ raise TypeError("Param2 must be a string.")
1403
+ if param3 is not None and not isinstance(param3, str):
1404
+ raise TypeError("Param3 must be a string.")
1405
+
1406
+ def contact_list_validate(
1407
+ self,
1408
+ number: Optional[Union[int, List[int]]] = None,
1409
+ group_id: Optional[int] = None,
1410
+ birthday: Optional[str] = None,
1411
+ sex: Optional[str] = None,
1412
+ operator: Optional[str] = None,
1413
+ last_name: Optional[str] = None,
1414
+ first_name: Optional[str] = None,
1415
+ surname: Optional[str] = None,
1416
+ page: Optional[int] = None,
1417
+ ) -> None:
1418
+ """
1419
+ Validates the parameters for the contact_list method.
1420
+
1421
+ Parameters:
1422
+ number (Union[int, List[int]], optional): The phone number or a list of phone numbers of the contacts.
1423
+ group_id (int, optional): The ID of the group to which the contacts belong.
1424
+ birthday (str, optional): The birthday of the contact.
1425
+ sex (str, optional): The sex of the contact.
1426
+ operator (str, optional): The operator of the contact.
1427
+ last_name (str, optional): The last name of the contact.
1428
+ first_name (str, optional): The first name of the contact.
1429
+ surname (str, optional): The surname of the contact.
1430
+ page (int, optional): The page number for the URL.
1431
+
1432
+ Raises:
1433
+ TypeError: If any of the parameters have an incorrect type.
1434
+ ValueError: If any of the parameters have an incorrect value.
1435
+ """
1436
+ if number:
1437
+ self.phone_validation(number)
1438
+ if group_id is not None and not isinstance(group_id, int):
1439
+ raise TypeError("Group ID must be an integer.")
1440
+ if birthday is not None and not isinstance(birthday, str):
1441
+ raise TypeError("Birthday must be a string.")
1442
+ if sex is not None and not isinstance(sex, str):
1443
+ raise TypeError("Sex must be a string.")
1444
+ if operator is not None and not isinstance(operator, str):
1445
+ raise TypeError("Operator must be a string.")
1446
+ if last_name is not None and not isinstance(last_name, str):
1447
+ raise TypeError("Last name must be a string.")
1448
+ if first_name is not None and not isinstance(first_name, str):
1449
+ raise TypeError("First name must be a string.")
1450
+ if surname is not None and not isinstance(surname, str):
1451
+ raise TypeError("Surname must be a string.")
1452
+ self.page_validate(page)
1453
+
1454
+ def send_telegram_validate(
1455
+ self,
1456
+ number: Union[int, List[int]],
1457
+ code: int,
1458
+ sign: Optional[str] = None,
1459
+ text: Optional[str] = None,
1460
+ ) -> None:
1461
+ """
1462
+ Validates the parameters for the send_telegram method.
1463
+
1464
+ Parameters:
1465
+ number (Union[int, List[int]]): The recipient's phone number or a list of phone numbers.
1466
+ code (int): The Telegram code (4 to 8 digits).
1467
+ sign (str, optional): The SMS sender name.
1468
+ text (str, optional): The SMS message text.
1469
+
1470
+ Raises:
1471
+ TypeError: If any of the parameters have an incorrect type.
1472
+ ValueError: If any of the parameters have an incorrect value.
1473
+ """
1474
+ if not isinstance(code, int):
1475
+ raise TypeError("code must be an integer")
1476
+ if not 4 <= len(str(code)) <= 8:
1477
+ raise ValueError("Length of code must be between 4 and 8 digits")
1478
+ if sign is not None and not isinstance(sign, str):
1479
+ raise TypeError("sign must be a string")
1480
+ if sign is not None and not 2 <= len(sign) <= 64:
1481
+ raise ValueError("Length of sign must be between 2 and 64 characters")
1482
+ if text is not None and not isinstance(text, str):
1483
+ raise TypeError("text must be a string")
1484
+ if text is not None and not 2 <= len(text) <= 640:
1485
+ raise ValueError("Length of text must be between 2 and 640 characters")
1486
+
1487
+ self.phone_validation(number)
1488
+
1489
+ @staticmethod
1490
+ def init_validate(
1491
+ api_key: str,
1492
+ signature: str = SIGNATURE,
1493
+ timeout: int = 15,
1494
+ url_gate: Optional[str] = None,
1495
+ test_mode: bool = False,
1496
+ ) -> None:
1497
+ """
1498
+ Validates the parameters for the __init__ method of the `SmsAero` class.
1499
+
1500
+ Parameters:
1501
+ email (str): A string representing the user's email.
1502
+ @api_key (str): A string representing the API key for the SmsAero service. Should be 32 characters.
1503
+ signature (str): A string representing the signature for the SmsAero service.
1504
+ timeout (int): An integer representing the timeout for requests to the SmsAero service.
1505
+ url_gate (str): A string representing the URL gate for the SmsAero service.
1506
+ test_mode (bool): A boolean indicating whether test mode is active.
1507
+
1508
+ Raises:
1509
+ ValueError: If any of the parameters are invalid.
1510
+ TypeError: If any of the parameters have an incorrect type.
1511
+ """
1512
+ if not isinstance(api_key, str):
1513
+ raise TypeError("API key must be a string.")
1514
+ if not 16 <= len(api_key) <= 32:
1515
+ raise ValueError("API key length must be between 20 and 32 characters.")
1516
+ if not isinstance(signature, str):
1517
+ raise TypeError("Signature must be a string.")
1518
+ if len(signature) < 2:
1519
+ raise ValueError("Signature length must be at least 2 characters.")
1520
+ if not isinstance(timeout, int):
1521
+ raise TypeError("Timeout must be an integer.")
1522
+ if timeout <= 2:
1523
+ raise ValueError("Timeout must be a positive integer.")
1524
+ if url_gate is not None and not isinstance(url_gate, str):
1525
+ raise TypeError("URL gate must be a string.")
1526
+ if not isinstance(test_mode, bool):
1527
+ raise TypeError("Test mode must be a boolean.")
1528
+
1529
+ async def __aenter__(self):
1530
+ await self.init_session()
1531
+ return self
1532
+
1533
+ async def __aexit__(self, exc_type, exc, tb):
1534
+ await self.close_session()