first commit

This commit is contained in:
2025-09-19 15:19:32 +05:00
commit d160410cd9
305 changed files with 9509 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
from .otp import * # noqa
from .sms import * # noqa
from .user import * # noqa

136
core/services/otp.py Normal file
View File

@@ -0,0 +1,136 @@
import requests
from config.env import env
class ConsoleService:
def __init__(self) -> None: ...
def send_sms(self, phone_number, message):
print(phone_number, message)
class EskizService:
GET = "GET"
POST = "POST"
PATCH = "PATCH"
CONTACT = "contact"
def __init__(self, api_url=None, email=None, password=None, callback_url=None):
self.api_url = api_url or env("SMS_API_URL")
self.email = email or env("SMS_LOGIN")
self.password = password or env("SMS_PASSWORD")
self.callback_url = callback_url
self.headers = {}
self.methods = {
"auth_user": "auth/user",
"auth_login": "auth/login",
"auth_refresh": "auth/refresh",
"send_message": "message/sms/send",
}
def request(self, api_path, data=None, method=None, headers=None):
incoming_data = {"status": "error"}
try:
response = requests.request(
method,
f"{self.api_url}/{api_path}",
data=data,
headers=headers,
)
if api_path == self.methods["auth_refresh"]:
if response.status_code == 200:
incoming_data["status"] = "success"
else:
incoming_data = response.json()
except requests.RequestException as error:
raise Exception(str(error))
return incoming_data
def auth(self):
data = {"email": self.email, "password": self.password}
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
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"],
)
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"],
}
return self.request(data["api_path"], method=data["method"], headers=data["headers"])
def add_sms_contact(self, first_name, phone_number, group):
token = self.auth()["data"]["token"]
self.headers["Authorization"] = "Bearer " + token
data = {
"name": first_name,
"email": self.email,
"group": group,
"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"],
)
def send_sms(self, phone_number, message):
token = self.auth()["data"]["token"]
self.headers["Authorization"] = "Bearer " + token
data = {
"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,
}
return self.request(
context["api_path"],
data=context["data"],
method=context["method"],
headers=context["headers"],
)

63
core/services/sms.py Normal file
View File

@@ -0,0 +1,63 @@
import random
from datetime import datetime, timedelta
from config.env import env
from core.apps.accounts.tasks.sms import SendConfirm
from django_core import exceptions, models
class SmsService:
@staticmethod
def send_confirm(phone):
# TODO: Deploy this change when deploying -> code = random.randint(1000, 9999) # noqa
if env.bool("OTP_PROD", False):
code = "".join(str(random.randint(0, 9)) for _ in range(env.int("OTP_SIZE", 4)))
else:
code = env.int("OTP_DEFAULT", 1111)
sms_confirm, status = models.SmsConfirm.objects.get_or_create(phone=phone, defaults={"code": code})
sms_confirm.sync_limits()
if sms_confirm.resend_unlock_time is not None:
expired = sms_confirm.interval(sms_confirm.resend_unlock_time)
exception = exceptions.SmsException(f"Resend blocked, try again in {expired}", expired=expired)
raise exception
sms_confirm.code = code
sms_confirm.try_count = 0
sms_confirm.resend_count += 1
sms_confirm.phone = phone
sms_confirm.expired_time = datetime.now() + timedelta(seconds=models.SmsConfirm.SMS_EXPIRY_SECONDS) # noqa
sms_confirm.resend_unlock_time = datetime.now() + timedelta(
seconds=models.SmsConfirm.SMS_EXPIRY_SECONDS
) # noqa
sms_confirm.save()
SendConfirm.delay(phone, code)
return True
@staticmethod
def check_confirm(phone, code):
sms_confirm = models.SmsConfirm.objects.filter(phone=phone).first()
if sms_confirm is None:
raise exceptions.SmsException("Invalid confirmation code")
sms_confirm.sync_limits()
if sms_confirm.is_expired():
raise exceptions.SmsException("Time for confirmation has expired")
if sms_confirm.is_block():
expired = sms_confirm.interval(sms_confirm.unlock_time)
raise exceptions.SmsException(f"Try again in {expired}")
if str(sms_confirm.code) == str(code):
sms_confirm.delete()
return True
sms_confirm.try_count += 1
sms_confirm.save()
raise exceptions.SmsException("Invalid confirmation code")

64
core/services/user.py Normal file
View File

@@ -0,0 +1,64 @@
from datetime import datetime
from core.services import sms
from django.contrib.auth import get_user_model, hashers
from django.utils.translation import gettext as _
from django_core import exceptions
from rest_framework.exceptions import PermissionDenied
from rest_framework_simplejwt import tokens
class UserService(sms.SmsService):
def get_token(self, user):
refresh = tokens.RefreshToken.for_user(user)
return {
"refresh": str(refresh),
"access": str(refresh.access_token),
}
def create_user(self, phone, first_name, last_name, password):
get_user_model().objects.update_or_create(
phone=phone,
defaults={
"phone": phone,
"first_name": first_name,
"last_name": last_name,
"password": hashers.make_password(password),
},
)
def send_confirmation(self, phone) -> bool:
try:
self.send_confirm(phone)
return True
except exceptions.SmsException as e:
raise PermissionDenied(_("Qayta sms yuborish uchun kuting: {}").format(e.kwargs.get("expired")))
except Exception:
raise PermissionDenied(_("Serverda xatolik yuz berdi"))
def validate_user(self, user) -> dict:
"""
Create user if user not found
"""
if user.validated_at is None:
user.validated_at = datetime.now()
user.save()
token = self.get_token(user)
return token
def is_validated(self, user) -> bool:
"""
User is validated check
"""
if user.validated_at is not None:
return True
return False
def change_password(self, phone, password):
"""
Change password
"""
user = get_user_model().objects.filter(phone=phone).first()
user.set_password(password)
user.save()