diff --git a/celerybeat-schedule-shm b/celerybeat-schedule-shm index 1b1c9ef..bafaecf 100644 Binary files a/celerybeat-schedule-shm and b/celerybeat-schedule-shm differ diff --git a/celerybeat-schedule-wal b/celerybeat-schedule-wal index 407d1a6..55c631a 100644 Binary files a/celerybeat-schedule-wal and b/celerybeat-schedule-wal differ diff --git a/core/apps/eggs/views/add_debt.py b/core/apps/eggs/views/add_debt.py index 37f3255..098b1c4 100644 --- a/core/apps/eggs/views/add_debt.py +++ b/core/apps/eggs/views/add_debt.py @@ -118,6 +118,7 @@ class AddDebtView(APIView): "message": "Qarz muvaffaqiyatli qo'shildi.", "debt_unpaid": f"{market.debt_unpaid}", "market_name": f"{market.name}", + "market_phone": f"{market.phone}", "market_company": f"{market.company_name}", }, status=status.HTTP_200_OK, diff --git a/core/http/tasks/sms.py b/core/http/tasks/sms.py index b7639dd..9872d76 100644 --- a/core/http/tasks/sms.py +++ b/core/http/tasks/sms.py @@ -13,13 +13,17 @@ from core.utils import console def SendConfirm(phone, code): try: service: sms_service.SendService = sms_service.SendService() - service.send_sms( - phone, _("Sizning Tasdiqlash ko'dingiz: %(code)s") % {"code": code} - ) - console.Console().success(f"Success: {phone}-{code}") + message = _("Sizning Tasdiqlash ko'dingiz: %(code)s") % {"code": code} + res = service.send_sms(phone, message) + + if res.get("status") == "success" or res.get("status") == "waiting": + console.Console().success(f"SMS Sent Successfully: {phone}-{code} | Response: {res}") + else: + console.Console().error(f"SMS Failed: {phone}-{code} | Error: {res}") + except Exception as e: console.Console().error( - "Error: {phone}-{code}\n\n{error}".format( - phone=phone, code=code, error=e + "Exception in SendConfirm task: {phone}-{code}\n\n{error}".format( + phone=phone, code=code, error=str(e) ) - ) # noqa + ) diff --git a/core/services/sms_service.py b/core/services/sms_service.py index 554b798..2f6c72a 100755 --- a/core/services/sms_service.py +++ b/core/services/sms_service.py @@ -1,22 +1,29 @@ import requests - +import logging +from django.core.cache import cache from common.env import env +logger = logging.getLogger(__name__) class SendService: GET = "GET" POST = "POST" PATCH = "PATCH" CONTACT = "contact" + CACHE_KEY = "eskiz_token" + TOKEN_LIFETIME = 86400 * 20 # Eskiz tokens usually last 30 days, we cache for 20 def __init__( self, api_url=None, email=None, password=None, callback_url=None ): - self.api_url = api_url or env("SMS_API_URL") + self.api_url = (api_url or env("SMS_API_URL")).rstrip("/") self.email = email or env("SMS_LOGIN") self.password = password or env("SMS_PASSWORD") self.callback_url = callback_url - self.headers = {} + self.headers = { + "Content-Type": "application/json", + "Accept": "application/json", + } self.methods = { "auth_user": "auth/user", @@ -26,66 +33,89 @@ class SendService: } def request(self, api_path, data=None, method=None, headers=None): - incoming_data = {"status": "error"} + incoming_data = {"status": "error", "message": "Unknown error"} + + url = f"{self.api_url}/{api_path}" + final_headers = self.headers.copy() + if headers: + final_headers.update(headers) try: + # For Eskiz, data should be JSON for Notify API response = requests.request( method, - f"{self.api_url}/{api_path}", - data=data, - headers=headers, + url, + json=data if method != self.GET else None, + params=data if method == self.GET else None, + headers=final_headers, + timeout=10 ) - if api_path == self.methods["auth_refresh"]: - if response.status_code == 200: - incoming_data["status"] = "success" - else: + if response.status_code == 401: + # Token might be expired, clear cache + cache.delete(self.CACHE_KEY) + + try: incoming_data = response.json() + except ValueError: + incoming_data = {"status": "error", "message": response.text} + + if response.status_code >= 400: + logger.error(f"Eskiz API Error ({response.status_code}): {incoming_data}") + except requests.RequestException as error: + logger.error(f"Eskiz Request Exception: {str(error)}") raise Exception(str(error)) return incoming_data + def get_token(self): + token = cache.get(self.CACHE_KEY) + if not token: + logger.info("Eskiz token not found in cache, authenticating...") + auth_res = self.auth() + if auth_res.get("status") == "success" or "data" in auth_res: + token = auth_res.get("data", {}).get("token") + if token: + cache.set(self.CACHE_KEY, token, self.TOKEN_LIFETIME) + else: + logger.error(f"Eskiz Auth Failed: {auth_res}") + return token + def auth(self): data = {"email": self.email, "password": self.password} - + # Eskiz login expects JSON return self.request( self.methods["auth_login"], data=data, method=self.POST ) def refresh_token(self): - token = self.auth()["data"]["token"] - self.headers["Authorization"] = "Bearer " + token + token = self.get_token() + headers = {"Authorization": f"Bearer {token}"} - context = { - "headers": self.headers, - "method": self.PATCH, - "api_path": self.methods["auth_refresh"], - } - - return self.request( - context["api_path"], - method=context["method"], - headers=context["headers"], + res = self.request( + self.methods["auth_refresh"], + method=self.PATCH, + headers=headers, ) + if res.get("status") == "success": + # Some Eskiz versions return a new token on refresh + new_token = res.get("data", {}).get("token") + if new_token: + cache.set(self.CACHE_KEY, new_token, self.TOKEN_LIFETIME) + return res def get_my_user_info(self): - token = self.auth()["data"]["token"] - self.headers["Authorization"] = "Bearer " + token - - data = { - "headers": self.headers, - "method": self.GET, - "api_path": self.methods["auth_user"], - } + token = self.get_token() + headers = {"Authorization": f"Bearer {token}"} return self.request( - data["api_path"], method=data["method"], headers=data["headers"] + self.methods["auth_user"], method=self.GET, headers=headers ) def add_sms_contact(self, first_name, phone_number, group): - token = self.auth()["data"]["token"] - self.headers["Authorization"] = "Bearer " + token + token = self.get_token() + headers = {"Authorization": f"Bearer {token}"} data = { "name": first_name, @@ -94,42 +124,32 @@ class SendService: "mobile_phone": phone_number, } - context = { - "headers": self.headers, - "method": self.POST, - "api_path": self.CONTACT, - "data": data, - } - return self.request( - context["api_path"], - data=context["data"], - method=context["method"], - headers=context["headers"], + self.CONTACT, + data=data, + method=self.POST, + headers=headers, ) def send_sms(self, phone_number, message): - token = self.auth()["data"]["token"] - self.headers["Authorization"] = "Bearer " + token + token = self.get_token() + if not token: + return {"status": "error", "message": "Failed to obtain auth token"} + headers = {"Authorization": f"Bearer {token}"} + + # Use string "4546" for sender name data = { - "from": 4546, + "from": "4546", "mobile_phone": phone_number, "callback_url": self.callback_url, "message": message, } - context = { - "headers": self.headers, - "method": self.POST, - "api_path": self.methods["send_message"], - "data": data, - } - res = self.request( - context["api_path"], - data=context["data"], - method=context["method"], - headers=context["headers"], + self.methods["send_message"], + data=data, + method=self.POST, + headers=headers, ) return res \ No newline at end of file