From 7d4f2aedb2d0e6dff7a4330ed596ba0a291792b8 Mon Sep 17 00:00:00 2001 From: YouKnow Date: Wed, 2 Oct 2024 15:56:21 +0330 Subject: [PATCH] Added async services and code cleanup ... --- setup.py | 12 +- sms_ir/__init__.py | 3 +- sms_ir/async_services/__init__.py | 289 +++++++++++++++++++++++++++++ sms_ir/async_services/requester.py | 32 ++++ sms_ir/base_requester.py | 21 +++ sms_ir/mixins.py | 71 ------- sms_ir/services.py | 288 ---------------------------- sms_ir/sync_services/__init__.py | 281 ++++++++++++++++++++++++++++ sms_ir/sync_services/requester.py | 53 ++++++ 9 files changed, 684 insertions(+), 366 deletions(-) create mode 100644 sms_ir/async_services/__init__.py create mode 100644 sms_ir/async_services/requester.py create mode 100644 sms_ir/base_requester.py delete mode 100644 sms_ir/mixins.py delete mode 100644 sms_ir/services.py create mode 100644 sms_ir/sync_services/__init__.py create mode 100644 sms_ir/sync_services/requester.py diff --git a/setup.py b/setup.py index 881a48a..3576d11 100644 --- a/setup.py +++ b/setup.py @@ -5,15 +5,15 @@ setuptools.setup( name="smsir-python", - version="1.0.7", + version="1.1.7", author="Mojtaba Akbari", author_email="mojtaba.akbari.221b@gmail.com", - packages=["sms_ir"], + packages=["sms_ir", "sms_ir/async_services", "sms_ir/sync_services"], description="Python Package of SMS.ir Panel ", long_description=description, long_description_content_type="text/markdown", url="https://github.com/IPeCompany/SmsPanelV2.Python", - license='MIT', - python_requires='>=3.7', - install_requires=[], -) \ No newline at end of file + license="MIT", + python_requires=">=3.7", + install_requires=["aiohttp"], +) diff --git a/sms_ir/__init__.py b/sms_ir/__init__.py index b81b411..9711ef4 100644 --- a/sms_ir/__init__.py +++ b/sms_ir/__init__.py @@ -1 +1,2 @@ -from .services import SmsIr \ No newline at end of file +from .sync_services import SmsIr +from .async_services import AsyncSmsIr diff --git a/sms_ir/async_services/__init__.py b/sms_ir/async_services/__init__.py new file mode 100644 index 0000000..55abaff --- /dev/null +++ b/sms_ir/async_services/__init__.py @@ -0,0 +1,289 @@ +from typing import List, Optional + +from aiohttp import ClientResponse + +from .requester import Requestser + +class AsyncSmsIr: + def __init__( + self, + api_key: str, + linenumber: Optional[int] = None, + ) -> None: + headers = { + "X-API-KEY": api_key, + "ACCEPT": "application/json", + "Content-Type": "application/json", + } + + self._linenumber = linenumber + self._requester = Requestser(headers) + + async def close(self): + """ + Close the session connection + """ + await self._requester.close() + + async def send_sms( + self, + number: str, + message: str, + linenumber: Optional[int] = None, + ) -> ClientResponse: + """ + Send message to specific mobile number + """ + + return await self.send_bulk_sms( + numbers=[number], + message=message, + linenumber=linenumber, + ) + + async def send_bulk_sms( + self, + numbers: List[str], + message: str, + linenumber: Optional[int] = None, + ) -> ClientResponse: + """ + Send message to multiple mobile numbers + """ + + url = "/v1/send/bulk/" + + data = { + "lineNumber": linenumber or self._linenumber, + "MessageText": message, + "Mobiles": numbers, + } + + return await self._requester.post( + url, + data, + ) + + async def send_like_to_like( + self, + numbers: List[str], + messages: List[str], + linenumber: Optional[int] = None, + send_date_time: Optional[str] = None, + ) -> ClientResponse: + """ + Send multiple messages to multiple mobile numbers pair to pair + """ + + url = "/v1/send/liketolike/" + + data = { + "lineNumber": linenumber or self._linenumber, + "MessageTexts": messages, + "Mobiles": numbers, + "SendDateTime": send_date_time, + } + + return await self._requester.post( + url, + data, + ) + + async def delete_scheduled( + self, + pack_id: int, + ) -> ClientResponse: + """ + Delete scheduled message pack + """ + + url = f"/v1/send/scheduled/{pack_id}/" + + return await self._requester.delete( + url, + ) + + async def send_verify_code( + self, + number: int, + template_id: int, + **parameters: dict[str, str], + ) -> ClientResponse: + """ + Send verification code with preasync defined template + """ + + url = "/v1/send/verify/" + + data = { + "Mobile": number, + "TemplateId": template_id, + "Parameters": [dict(name=k, value=v) for k, v in parameters.items()], + } + + return await self._requester.post( + url, + data, + ) + + async def report_message( + self, + message_id: int, + ) -> ClientResponse: + """ + get report of sent message + """ + + url = f"/v1/send/{message_id}/" + + return await self._requester.get( + url, + ) + + async def report_pack( + self, + pack_id: int, + ) -> ClientResponse: + """ + get report of sent message pack + """ + + url = f"/v1/send/pack/{pack_id}/" + + return await self._requester.get( + url, + ) + + async def report_today( + self, + page_size: int = 10, + page_number: int = 1, + ) -> ClientResponse: + """ + get report of Today sent Messages + """ + + url = "/v1/send/live/" + + params = { + "pageSize": page_size, + "pageNumber": page_number, + } + + return await self._requester.get( + url, + params, + ) + + async def report_archived( + self, + from_date: Optional[int] = None, + to_date: Optional[int] = None, + page_size: int = 10, + page_number: int = 1, + ) -> ClientResponse: + """ + get report of Archived Messages + """ + + url = "/v1/send/archive/" + + params = { + "fromDate": from_date, + "toDate": to_date, + "pageSize": page_size, + "pageNumber": page_number, + } + + return await self._requester.get( + url, + params, + ) + + async def report_latest_received( + self, + count: int, + ) -> ClientResponse: + """ + get report of latest received messages + """ + + url = "/v1/receive/latest/" + + params = { + "count": count, + } + + return await self._requester.get( + url, + params, + ) + + async def report_today_received( + self, + page_size: int = 10, + page_number: int = 1, + ) -> ClientResponse: + """ + get report of today received messages + """ + + url = "/v1/receive/live/" + + params = { + "pageSize": page_size, + "pageNumber": page_number, + } + + return await self._requester.get( + url, + params, + ) + + async def report_archived_received( + self, + from_date: Optional[int] = None, + to_date: Optional[int] = None, + page_size: int = 10, + page_number: int = 1, + ) -> ClientResponse: + """ + get report of today received messages + """ + + url = "/v1/receive/archive/" + + params = { + "fromDate": from_date, + "toDate": to_date, + "pageSize": page_size, + "pageNumber": page_number, + } + + return await self._requester.get( + url, + params, + ) + + async def get_credit(self) -> ClientResponse: + """ + get account credit + """ + + url = "/v1/credit/" + + return await self._requester.get( + url, + ) + + async def get_line_numbers(self) -> ClientResponse: + """ + get account line numbers + """ + + url = "/v1/line/" + + return await self._requester.get( + url, + ) + diff --git a/sms_ir/async_services/requester.py b/sms_ir/async_services/requester.py new file mode 100644 index 0000000..3de1f21 --- /dev/null +++ b/sms_ir/async_services/requester.py @@ -0,0 +1,32 @@ +from aiohttp import ClientSession, ClientResponse + +from ..base_requester import BaseRequester + + +class Requestser(BaseRequester): + def __init__(self, headers: dict[str, str]) -> None: + super().__init__() + self._session = ClientSession(base_url=self.endpoint, headers=headers) + + async def close(self): + await self._session.close() + + async def post(self, url: str, json) -> ClientResponse: + self.logger.info(f"send request to {url}") + return await self._session.post( + url, + json=json, + ) + + async def delete(self, url: str) -> ClientResponse: + self.logger.info(f"send request to {url}") + return await self._session.delete( + url, + ) + + async def get(self, url: str, params=None) -> ClientResponse: + self.logger.info(f"send request to {url}") + return await self._session.get( + url, + params=params, + ) diff --git a/sms_ir/base_requester.py b/sms_ir/base_requester.py new file mode 100644 index 0000000..b2ab139 --- /dev/null +++ b/sms_ir/base_requester.py @@ -0,0 +1,21 @@ +import logging +import sys + +ENDPOINT = "https://api.sms.ir" + +class BaseRequester: + def __init__(self) -> None: + self.endpoint = ENDPOINT + + # setup logging + self.log_level = logging.INFO + + log_format = logging.Formatter("[%(asctime)s] [%(levelname)s] - %(message)s") + self.logger = logging.getLogger(__name__) + self.logger.setLevel(self.log_level) + + # writing to stdout + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(self.log_level) + handler.setFormatter(log_format) + self.logger.addHandler(handler) diff --git a/sms_ir/mixins.py b/sms_ir/mixins.py deleted file mode 100644 index 0614a48..0000000 --- a/sms_ir/mixins.py +++ /dev/null @@ -1,71 +0,0 @@ -import logging -import sys -import requests -from requests import ( - ReadTimeout, - ConnectTimeout, - HTTPError, - Timeout, - ConnectionError, -) - - -class LoggerMixin: - def config_logger(self): - self.log_level=logging.INFO - - log_format = logging.Formatter('[%(asctime)s] [%(levelname)s] - %(message)s') - self.logger = logging.getLogger(__name__) - self.logger.setLevel(self.log_level) - - # writing to stdout - handler = logging.StreamHandler(sys.stdout) - handler.setLevel(self.log_level) - handler.setFormatter(log_format) - self.logger.addHandler(handler) - - -class RequestsMixin : - def post(self, url, data): - try : - self.logger.info("send request to %s", url) - return requests.post( - url, - headers=self._headers, - json=data, - ) - except (ConnectTimeout, HTTPError, ReadTimeout, Timeout, ConnectionError) as e: - self.logger.error(str(e)) - return self.fake_response(e.request) - - def delete(self, url): - try : - self.logger.info("send request to %s", url) - return requests.delete( - url, - headers=self._headers, - ) - except (ConnectTimeout, HTTPError, ReadTimeout, Timeout, ConnectionError) as e: - self.logger.error(str(e)) - return self.fake_response(e.request) - - def get(self, url, params=None): - try : - self.logger.info("send request to %s", url) - return requests.get( - url, - headers=self._headers, - params=params, - ) - except (ConnectTimeout, HTTPError, ReadTimeout, Timeout, ConnectionError) as e: - self.logger.error(str(e)) - return self.fake_response(e.request) - - def fake_response(self, request): - response = requests.models.Response() - - response.status_code = 503 - response.request = request - response.url = request.url - - return response \ No newline at end of file diff --git a/sms_ir/services.py b/sms_ir/services.py deleted file mode 100644 index 50da5f7..0000000 --- a/sms_ir/services.py +++ /dev/null @@ -1,288 +0,0 @@ -from typing import List -from requests.models import Response -from typing import TypeVar -from .mixins import RequestsMixin, LoggerMixin - -T = TypeVar('T', int, None) -S = TypeVar('S', str, None) - - -class SmsIr(RequestsMixin, LoggerMixin): - ENDPOINT = 'https://api.sms.ir' - - def __init__( - self, - api_key: str, - linenumber: T = None, - ) -> None: - - self.config_logger() - - self._linenumber = linenumber - self._headers = { - "X-API-KEY": api_key, - 'ACCEPT': 'application/json', - 'Content-Type': 'application/json', - } - - def send_sms( - self, - number: str, - message: str, - linenumber: T = None, - ) -> Response: - """ - Send message to specific mobile number - """ - - return self.send_bulk_sms( - numbers=[number], - message=message, - linenumber=linenumber, - ) - - def send_bulk_sms( - self, - numbers: List[str], - message: str, - linenumber: T = None, - ) -> Response: - """ - Send message to multiple mobile numbers - """ - - url = f'{self.ENDPOINT}/v1/send/bulk/' - - data = { - 'lineNumber': linenumber or self._linenumber, - 'MessageText': message, - 'Mobiles': numbers, - } - - return self.post( - url, - data, - ) - - def send_like_to_like( - self, - numbers: List[str], - messages: List[str], - linenumber: T =None, - send_date_time: S =None, - ) -> Response: - """ - Send multiple messages to multiple mobile numbers pair to pair - """ - - url = f'{self.ENDPOINT}/v1/send/liketolike/' - - data = { - 'lineNumber': linenumber or self._linenumber, - 'MessageTexts' : messages, - 'Mobiles': numbers, - 'SendDateTime': send_date_time, - } - - return self.post( - url, - data, - ) - - def delete_scheduled( - self, - pack_id: int, - ) -> Response: - """ - Delete scheduled message pack - """ - - url = f'{self.ENDPOINT}/v1/send/scheduled/{pack_id}/' - - return self.delete( - url, - ) - - def send_verify_code( - self, - number: int, - template_id: int, - **parameters: dict[str,str], - ) -> Response: - """ - Send verification code with predefined template - """ - - url = f'{self.ENDPOINT}/v1/send/verify/' - - data = { - 'Mobile' : number, - 'TemplateId' : template_id, - 'Parameters' : [dict(name=k,value=v) for k,v in parameters.items()], - } - - return self.post( - url, - data, - ) - - def report_message( - self, - message_id: int, - ) -> Response: - """ - get report of sent message - """ - - url = f'{self.ENDPOINT}/v1/send/{message_id}/' - - return self.get( - url, - ) - - def report_pack( - self, - pack_id: int, - ) -> Response: - """ - get report of sent message pack - """ - - url = f'{self.ENDPOINT}/v1/send/pack/{pack_id}/' - - return self.get( - url, - ) - - def report_today( - self, - page_size: int =10, - page_number: int =1, - ) -> Response: - """ - get report of Today sent Messages - """ - - url = f'{self.ENDPOINT}/v1/send/live/' - - params = { - 'pageSize' : page_size, - 'pageNumber' : page_number, - } - - return self.get( - url, - params, - ) - - def report_archived( - self, - from_date: T =None, - to_date: T =None, - page_size: int =10, - page_number: int =1, - ) -> Response: - """ - get report of Archived Messages - """ - - url = f'{self.ENDPOINT}/v1/send/archive/' - - params = { - 'fromDate': from_date, - 'toDate' : to_date, - 'pageSize' : page_size, - 'pageNumber' : page_number, - } - - return self.get( - url, - params, - ) - - def report_latest_received( - self, - count: int, - ) -> Response: - """ - get report of latest received messages - """ - - url = f'{self.ENDPOINT}/v1/receive/latest/' - - params = { - 'count': count, - } - - return self.get( - url, - params, - ) - - def report_today_received( - self, - page_size: int =10, - page_number: int =1, - ) -> Response: - """ - get report of today received messages - """ - - url = f'{self.ENDPOINT}/v1/receive/live/' - - params = { - 'pageSize': page_size, - 'pageNumber': page_number, - } - - return self.get( - url, - params, - ) - - def report_archived_received( - self, - from_date: T =None, - to_date: T =None, - page_size: int =10, - page_number: int =1, - ) -> Response: - """ - get report of today received messages - """ - - url = f'{self.ENDPOINT}/v1/receive/archive/' - - params = { - 'fromDate' : from_date, - 'toDate' : to_date, - 'pageSize' : page_size, - 'pageNumber' : page_number, - } - - return self.get( - url, - params, - ) - - def get_credit(self) -> Response: - """ - get account credit - """ - - url = f'{self.ENDPOINT}/v1/credit/' - - return self.get( - url, - ) - - def get_line_numbers(self) -> Response: - """ - get account line numbers - """ - - url = f'{self.ENDPOINT}/v1/line/' - - return self.get( - url, - ) diff --git a/sms_ir/sync_services/__init__.py b/sms_ir/sync_services/__init__.py new file mode 100644 index 0000000..21a9f48 --- /dev/null +++ b/sms_ir/sync_services/__init__.py @@ -0,0 +1,281 @@ +from typing import List, Optional +from requests.models import Response +from .requester import Requestser + + +class SmsIr: + def __init__( + self, + api_key: str, + linenumber: Optional[int] = None, + ) -> None: + headers = { + "X-API-KEY": api_key, + "ACCEPT": "application/json", + "Content-Type": "application/json", + } + + self._linenumber = linenumber + self._requester = Requestser(headers) + + def send_sms( + self, + number: str, + message: str, + linenumber: Optional[int] = None, + ) -> Response: + """ + Send message to specific mobile number + """ + + return self.send_bulk_sms( + numbers=[number], + message=message, + linenumber=linenumber, + ) + + def send_bulk_sms( + self, + numbers: List[str], + message: str, + linenumber: Optional[int] = None, + ) -> Response: + """ + Send message to multiple mobile numbers + """ + + url = "/v1/send/bulk/" + + data = { + "lineNumber": linenumber or self._linenumber, + "MessageText": message, + "Mobiles": numbers, + } + + return self._requester.post( + url, + data, + ) + + def send_like_to_like( + self, + numbers: List[str], + messages: List[str], + linenumber: Optional[int] = None, + send_date_time: Optional[str] = None, + ) -> Response: + """ + Send multiple messages to multiple mobile numbers pair to pair + """ + + url = "/v1/send/liketolike/" + + data = { + "lineNumber": linenumber or self._linenumber, + "MessageTexts": messages, + "Mobiles": numbers, + "SendDateTime": send_date_time, + } + + return self._requester.post( + url, + data, + ) + + def delete_scheduled( + self, + pack_id: int, + ) -> Response: + """ + Delete scheduled message pack + """ + + url = f"/v1/send/scheduled/{pack_id}/" + + return self._requester.delete( + url, + ) + + def send_verify_code( + self, + number: int, + template_id: int, + **parameters: dict[str, str], + ) -> Response: + """ + Send verification code with predefined template + """ + + url = "/v1/send/verify/" + + data = { + "Mobile": number, + "TemplateId": template_id, + "Parameters": [dict(name=k, value=v) for k, v in parameters.items()], + } + + return self._requester.post( + url, + data, + ) + + def report_message( + self, + message_id: int, + ) -> Response: + """ + get report of sent message + """ + + url = f"/v1/send/{message_id}/" + + return self._requester.get( + url, + ) + + def report_pack( + self, + pack_id: int, + ) -> Response: + """ + get report of sent message pack + """ + + url = f"/v1/send/pack/{pack_id}/" + + return self._requester.get( + url, + ) + + def report_today( + self, + page_size: int = 10, + page_number: int = 1, + ) -> Response: + """ + get report of Today sent Messages + """ + + url = "/v1/send/live/" + + params = { + "pageSize": page_size, + "pageNumber": page_number, + } + + return self._requester.get( + url, + params, + ) + + def report_archived( + self, + from_date: Optional[int] = None, + to_date: Optional[int] = None, + page_size: int = 10, + page_number: int = 1, + ) -> Response: + """ + get report of Archived Messages + """ + + url = "/v1/send/archive/" + + params = { + "fromDate": from_date, + "toDate": to_date, + "pageSize": page_size, + "pageNumber": page_number, + } + + return self._requester.get( + url, + params, + ) + + def report_latest_received( + self, + count: int, + ) -> Response: + """ + get report of latest received messages + """ + + url = "/v1/receive/latest/" + + params = { + "count": count, + } + + return self._requester.get( + url, + params, + ) + + def report_today_received( + self, + page_size: int = 10, + page_number: int = 1, + ) -> Response: + """ + get report of today received messages + """ + + url = "/v1/receive/live/" + + params = { + "pageSize": page_size, + "pageNumber": page_number, + } + + return self._requester.get( + url, + params, + ) + + def report_archived_received( + self, + from_date: Optional[int] = None, + to_date: Optional[int] = None, + page_size: int = 10, + page_number: int = 1, + ) -> Response: + """ + get report of today received messages + """ + + url = "/v1/receive/archive/" + + params = { + "fromDate": from_date, + "toDate": to_date, + "pageSize": page_size, + "pageNumber": page_number, + } + + return self._requester.get( + url, + params, + ) + + def get_credit(self) -> Response: + """ + get account credit + """ + + url = "/v1/credit/" + + return self._requester.get( + url, + ) + + def get_line_numbers(self) -> Response: + """ + get account line numbers + """ + + url = "/v1/line/" + + return self._requester.get( + url, + ) diff --git a/sms_ir/sync_services/requester.py b/sms_ir/sync_services/requester.py new file mode 100644 index 0000000..36fdb76 --- /dev/null +++ b/sms_ir/sync_services/requester.py @@ -0,0 +1,53 @@ +import requests +from requests import RequestException + +from ..base_requester import BaseRequester + + +class Requestser(BaseRequester): + def __init__(self, headers: dict[str, str]) -> None: + super().__init__() + self._session = requests.Session() + self._session.headers.update(headers) + + def post(self, url: str, json): + try: + url = self.endpoint + url + self.logger.info("send request to %s", url) + return self._session.post( + url, + json=json, + ) + except RequestException as e: + self.logger.error(str(e)) + return self.fake_response(e.request) + + def delete(self, url: str): + try: + url = self.endpoint + url + self.logger.info("send request to %s", url) + return self._session.delete(url) + except RequestException as e: + self.logger.error(str(e)) + return self.fake_response(e.request) + + def get(self, url: str, params=None): + try: + url = self.endpoint + url + self.logger.info("send request to %s", url) + return self._session.get( + url, + params=params, + ) + except RequestException as e: + self.logger.error(str(e)) + return self.fake_response(e.request) + + def fake_response(self, request): + response = requests.models.Response() + + response.status_code = 503 + response.request = request + response.url = request.url + + return response