contract signature part is done
This commit is contained in:
@@ -3,7 +3,7 @@ from datetime import timedelta
|
|||||||
from config.env import env
|
from config.env import env
|
||||||
|
|
||||||
SIMPLE_JWT = {
|
SIMPLE_JWT = {
|
||||||
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
|
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
|
||||||
"REFRESH_TOKEN_LIFETIME": timedelta(days=30),
|
"REFRESH_TOKEN_LIFETIME": timedelta(days=30),
|
||||||
"ROTATE_REFRESH_TOKENS": False,
|
"ROTATE_REFRESH_TOKENS": False,
|
||||||
"BLACKLIST_AFTER_ROTATION": False,
|
"BLACKLIST_AFTER_ROTATION": False,
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ env = environ.Env(
|
|||||||
DB_PORT=(int, 5432),
|
DB_PORT=(int, 5432),
|
||||||
DEBUG=(bool, False),
|
DEBUG=(bool, False),
|
||||||
ALLOWED_HOSTS=(list, ['localhost', '127.0.0.1']),
|
ALLOWED_HOSTS=(list, ['localhost', '127.0.0.1']),
|
||||||
SECRET_KEY=(str)
|
SECRET_KEY=(str),
|
||||||
|
BOT_TOKEN=(str)
|
||||||
)
|
)
|
||||||
@@ -26,7 +26,7 @@ class User(BaseModel, AbstractUser):
|
|||||||
return self.phone
|
return self.phone
|
||||||
|
|
||||||
def generate_code(self):
|
def generate_code(self):
|
||||||
code = ''.join([str(random.randint(0, 100) % 10) for _ in range(4)])
|
code = ''.join([str(random.randint(1, 100) % 10) for _ in range(4)])
|
||||||
expiration_time = timezone.now() + datetime.timedelta(minutes=2)
|
expiration_time = timezone.now() + datetime.timedelta(minutes=2)
|
||||||
VerificationCode.objects.create(
|
VerificationCode.objects.create(
|
||||||
code=code,
|
code=code,
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ from celery import shared_task
|
|||||||
from core.apps.accounts.models.verification_code import VerificationCode
|
from core.apps.accounts.models.verification_code import VerificationCode
|
||||||
from core.apps.accounts.models.user import User
|
from core.apps.accounts.models.user import User
|
||||||
from core.services.sms import send_sms_eskiz
|
from core.services.sms import send_sms_eskiz
|
||||||
|
from core.services.sms_via_bot import send_sms_code
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def create_and_send_sms_code(user):
|
def create_and_send_sms_code(user):
|
||||||
user = User.objects.get(id=user)
|
user = User.objects.get(id=user)
|
||||||
code = user.generate_code()
|
code = user.generate_code()
|
||||||
send_sms_eskiz(user.phone, code)
|
# send_sms_eskiz(user.phone, code)
|
||||||
|
send_sms_code(code, 'auth', user.phone)
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from core.apps.contracts.models.contract import Contract, ContractSide, ContractSignature
|
from core.apps.contracts.models.contract import Contract, ContractSide, ContractSignature, ContractSignatureCode
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Contract)
|
@admin.register(Contract)
|
||||||
class ContractAdmin(admin.ModelAdmin):
|
class ContractAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'contract_number', 'name', 'face_id', 'attach_file', 'add_folder', 'add_notification']
|
list_display = ['id', 'contract_number', 'name', 'face_id', 'attach_file', 'add_folder', 'add_notification']
|
||||||
|
|
||||||
|
|
||||||
@admin.register(ContractSide)
|
@admin.register(ContractSide)
|
||||||
class ContractSideAdmin(admin.ModelAdmin):
|
class ContractSideAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'full_name']
|
list_display = ['id', 'full_name']
|
||||||
@@ -14,4 +15,9 @@ class ContractSideAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(ContractSignature)
|
@admin.register(ContractSignature)
|
||||||
class ContractSignatureAdmin(admin.ModelAdmin):
|
class ContractSignatureAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'user', 'contract', 'status']
|
list_display = ['id', 'contract_side', 'contract', 'status']
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(ContractSignatureCode)
|
||||||
|
class ContractSignatureCodeAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['id', 'code', 'signature']
|
||||||
@@ -8,5 +8,6 @@ STATUS = (
|
|||||||
('created', 'created',),
|
('created', 'created',),
|
||||||
('signed_company', 'signed by company',),
|
('signed_company', 'signed by company',),
|
||||||
('signed_customer', 'signed by customer',),
|
('signed_customer', 'signed by customer',),
|
||||||
('cancelled', 'cancelled')
|
('contract_signed', 'contract signed'),
|
||||||
|
('cancelled', 'cancelled'),
|
||||||
)
|
)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 5.2 on 2025-07-16 17:14
|
# Generated by Django 5.2 on 2025-07-17 14:48
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import uuid
|
import uuid
|
||||||
@@ -25,7 +25,7 @@ class Migration(migrations.Migration):
|
|||||||
('contract_number', models.PositiveIntegerField()),
|
('contract_number', models.PositiveIntegerField()),
|
||||||
('name', models.CharField(max_length=200)),
|
('name', models.CharField(max_length=200)),
|
||||||
('sides', models.CharField(choices=[('two_or_more', 'two or more'), ('customer_only', 'customer only'), ('only_company', 'onlycompany')], max_length=13)),
|
('sides', models.CharField(choices=[('two_or_more', 'two or more'), ('customer_only', 'customer only'), ('only_company', 'onlycompany')], max_length=13)),
|
||||||
('status', models.CharField(choices=[('created', 'created'), ('signed_company', 'signed by company'), ('signed_customer', 'signed by customer'), ('cancelled', 'cancelled')], default='created', max_length=15)),
|
('status', models.CharField(choices=[('created', 'created'), ('signed_company', 'signed by company'), ('signed_customer', 'signed by customer'), ('contract_signed', 'contract signed'), ('cancelled', 'cancelled')], default='created', max_length=15)),
|
||||||
('face_id', models.BooleanField(default=False)),
|
('face_id', models.BooleanField(default=False)),
|
||||||
('attach_file', models.BooleanField(default=False)),
|
('attach_file', models.BooleanField(default=False)),
|
||||||
('add_folder', models.BooleanField(default=False)),
|
('add_folder', models.BooleanField(default=False)),
|
||||||
@@ -69,12 +69,29 @@ class Migration(migrations.Migration):
|
|||||||
('signature_type', models.CharField(blank=True, choices=[('sms_sign', 'sms signature'), ('electronic_sing', 'electronic signature')], max_length=20, null=True)),
|
('signature_type', models.CharField(blank=True, choices=[('sms_sign', 'sms signature'), ('electronic_sing', 'electronic signature')], max_length=20, null=True)),
|
||||||
('is_signature', models.BooleanField(default=False)),
|
('is_signature', models.BooleanField(default=False)),
|
||||||
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contract_signatures', to='contracts.contract')),
|
('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contract_signatures', to='contracts.contract')),
|
||||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contract_users', to=settings.AUTH_USER_MODEL)),
|
('contract_side', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='contract_signatures', to='contracts.contractside')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'contract signatures',
|
'verbose_name': 'contract signature',
|
||||||
|
'verbose_name_plural': 'contract signatures',
|
||||||
'db_table': 'contract_signatures',
|
'db_table': 'contract_signatures',
|
||||||
'unique_together': {('contract', 'user')},
|
'unique_together': {('contract', 'contract_side')},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ContractSignatureCode',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('code', models.PositiveSmallIntegerField()),
|
||||||
|
('expiration_time', models.DateTimeField()),
|
||||||
|
('signature', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='signature_codes', to='contracts.contractsignature')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'contract signature code',
|
||||||
|
'verbose_name_plural': 'contract signature codes',
|
||||||
|
'db_table': 'contract_signature_codes',
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
import random
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.utils import timezone
|
||||||
|
|
||||||
from core.apps.shared.models.base import BaseModel
|
from core.apps.shared.models.base import BaseModel
|
||||||
from core.apps.contracts.enums.contract import SIDES, STATUS
|
from core.apps.contracts.enums.contract import SIDES, STATUS
|
||||||
@@ -56,7 +59,7 @@ class ContractSide(BaseModel):
|
|||||||
|
|
||||||
class ContractSignature(BaseModel):
|
class ContractSignature(BaseModel):
|
||||||
contract = models.ForeignKey(Contract, on_delete=models.CASCADE, related_name='contract_signatures')
|
contract = models.ForeignKey(Contract, on_delete=models.CASCADE, related_name='contract_signatures')
|
||||||
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, related_name='contract_users')
|
contract_side = models.OneToOneField(ContractSide, on_delete=models.CASCADE, related_name='contract_signatures')
|
||||||
|
|
||||||
status = models.CharField(max_length=20, choices=SIGNATURE_STATUS, default='organized')
|
status = models.CharField(max_length=20, choices=SIGNATURE_STATUS, default='organized')
|
||||||
signature_type = models.CharField(max_length=20, choices=SIGNATURE_TYPE, null=True, blank=True)
|
signature_type = models.CharField(max_length=20, choices=SIGNATURE_TYPE, null=True, blank=True)
|
||||||
@@ -64,10 +67,34 @@ class ContractSignature(BaseModel):
|
|||||||
is_signature = models.BooleanField(default=False)
|
is_signature = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.user} user signature for {self.contract} contract'
|
return f'{self.contract_side} user signature for {self.contract} contract'
|
||||||
|
|
||||||
|
def generate_code(self):
|
||||||
|
code = ''.join([str(random.randint(1, 9) % 10) for _ in range(4)])
|
||||||
|
ContractSignatureCode.objects.create(
|
||||||
|
code=code,
|
||||||
|
signature=self,
|
||||||
|
expiration_time = timezone.now() + timedelta(minutes=2)
|
||||||
|
)
|
||||||
|
return code
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'contract signature'
|
verbose_name = 'contract signature'
|
||||||
verbose_name = 'contract signatures'
|
verbose_name_plural = 'contract signatures'
|
||||||
db_table = 'contract_signatures'
|
db_table = 'contract_signatures'
|
||||||
unique_together = ['contract', 'user']
|
unique_together = ['contract', 'contract_side']
|
||||||
|
|
||||||
|
|
||||||
|
class ContractSignatureCode(BaseModel):
|
||||||
|
code = models.PositiveSmallIntegerField()
|
||||||
|
signature = models.ForeignKey(ContractSignature, on_delete=models.CASCADE, related_name='signature_codes')
|
||||||
|
expiration_time = models.DateTimeField()
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.code} - {self.signature}'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = 'contract signature code'
|
||||||
|
verbose_name_plural = 'contract signature codes'
|
||||||
|
db_table = 'contract_signature_codes'
|
||||||
|
|
||||||
@@ -47,5 +47,5 @@ class ContractDetailSerializer(serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Contract
|
model = Contract
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'name', 'file', 'contract_number', 'contract_sides',
|
'id', 'name', 'file', 'status', 'contract_number', 'contract_sides',
|
||||||
]
|
]
|
||||||
@@ -2,8 +2,9 @@ from django.contrib.auth import get_user_model
|
|||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from core.apps.contracts.models.contract import ContractSide, Contract
|
from core.apps.contracts.models.contract import ContractSide, Contract, ContractSignature
|
||||||
from core.apps.contracts.enums.contract_side import ROLE
|
from core.apps.contracts.enums.contract_side import ROLE
|
||||||
|
from core.apps.contracts.serializers.contract_signature import ContractSignatureListSerializer
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
@@ -25,8 +26,14 @@ class ContractSideCreateSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
|
|
||||||
class ContractSideListSerializer(serializers.ModelSerializer):
|
class ContractSideListSerializer(serializers.ModelSerializer):
|
||||||
|
contract_signature = serializers.SerializerMethodField(method_name='get_contract_signature')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ContractSide
|
model = ContractSide
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'full_name', 'user'
|
'id', 'full_name', 'user', 'contract_signature'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_contract_signature(self, obj):
|
||||||
|
contract_signature = obj.contract_signatures
|
||||||
|
return ContractSignatureListSerializer(contract_signature).data
|
||||||
34
core/apps/contracts/serializers/contract_signature.py
Normal file
34
core/apps/contracts/serializers/contract_signature.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from core.apps.contracts.models.contract import ContractSignature, ContractSignatureCode
|
||||||
|
|
||||||
|
|
||||||
|
class ContractSignatureListSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = ContractSignature
|
||||||
|
fields = [
|
||||||
|
'id', 'status', 'signature_type', 'is_signature'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class ContractSignatureSerializer(serializers.Serializer):
|
||||||
|
code = serializers.IntegerField()
|
||||||
|
signature_id = serializers.UUIDField()
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
user = self.context.get('user')
|
||||||
|
signature = ContractSignature.objects.filter(id=data.get('signature_id')).first()
|
||||||
|
if not signature:
|
||||||
|
raise serializers.ValidationError({"detail": "contract signature not found"})
|
||||||
|
if signature.contract_side.user != user:
|
||||||
|
raise serializers.ValidationError({'detail': 'this is not your code'})
|
||||||
|
signature_code = ContractSignatureCode.objects.filter(signature=signature, code=data.get('code')).first()
|
||||||
|
if not signature_code:
|
||||||
|
raise serializers.ValidationError({'detail': 'invalid code'})
|
||||||
|
if signature_code.expiration_time < timezone.now():
|
||||||
|
raise serializers.ValidationError({"detail": 'code is expired'})
|
||||||
|
data['contract'] = signature.contract_side.contract
|
||||||
|
data['contract_signature'] = signature
|
||||||
|
return data
|
||||||
@@ -3,6 +3,8 @@ from django.contrib.auth import get_user_model
|
|||||||
from celery import shared_task
|
from celery import shared_task
|
||||||
|
|
||||||
from core.apps.contracts.models.contract import ContractSide, Contract, ContractSignature
|
from core.apps.contracts.models.contract import ContractSide, Contract, ContractSignature
|
||||||
|
from core.services.sms_via_bot import send_sms_code
|
||||||
|
|
||||||
|
|
||||||
@shared_task
|
@shared_task
|
||||||
def create_contract_side(data):
|
def create_contract_side(data):
|
||||||
@@ -11,7 +13,7 @@ def create_contract_side(data):
|
|||||||
contract = Contract.objects.get(id=data['contract_id'])
|
contract = Contract.objects.get(id=data['contract_id'])
|
||||||
user = User.objects.get(phone=data['phone'])
|
user = User.objects.get(phone=data['phone'])
|
||||||
|
|
||||||
ContractSide.objects.create(
|
contract_side = ContractSide.objects.create(
|
||||||
full_name=data.get('full_name'),
|
full_name=data.get('full_name'),
|
||||||
indentification=data.get('indentification'),
|
indentification=data.get('indentification'),
|
||||||
position=data.get('position'),
|
position=data.get('position'),
|
||||||
@@ -23,5 +25,7 @@ def create_contract_side(data):
|
|||||||
|
|
||||||
ContractSignature.objects.create(
|
ContractSignature.objects.create(
|
||||||
contract=contract,
|
contract=contract,
|
||||||
user=user,
|
contract_side=contract_side,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
14
core/apps/contracts/tasks/contract_signature.py
Normal file
14
core/apps/contracts/tasks/contract_signature.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
from celery import shared_task
|
||||||
|
|
||||||
|
from core.apps.contracts.models.contract import ContractSignature, ContractSignatureCode
|
||||||
|
from core.services.sms_via_bot import send_sms_code
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def send_contract_signature_code(signature_id):
|
||||||
|
contract_signature = get_object_or_404(ContractSignature, id=signature_id)
|
||||||
|
code = contract_signature.generate_code()
|
||||||
|
send_sms_code(code, 'contract', contract_signature.contract_side.user.phone)
|
||||||
|
|
||||||
@@ -2,6 +2,7 @@ from django.urls import path, include
|
|||||||
|
|
||||||
from core.apps.contracts.views import contract as contract_views
|
from core.apps.contracts.views import contract as contract_views
|
||||||
from core.apps.contracts.views import contract_side as contract_side_views
|
from core.apps.contracts.views import contract_side as contract_side_views
|
||||||
|
from core.apps.contracts.views import contract_signature as contract_signature_views
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
@@ -15,5 +16,11 @@ urlpatterns = [
|
|||||||
path('contract_side/', include([
|
path('contract_side/', include([
|
||||||
path('create/', contract_side_views.ConstartSideCreateApiView.as_view(), name='contract-side-create'),
|
path('create/', contract_side_views.ConstartSideCreateApiView.as_view(), name='contract-side-create'),
|
||||||
]
|
]
|
||||||
|
)),
|
||||||
|
path('contract_signature/', include(
|
||||||
|
[
|
||||||
|
path('send_signature_code/<uuid:signature_id>/', contract_signature_views.SendContractSignatureCodeApiView.as_view(), name='send-signature-code'),
|
||||||
|
path('sign_contract/', contract_signature_views.SigningContractApiView.as_view(), name='sign-contract'),
|
||||||
|
]
|
||||||
))
|
))
|
||||||
]
|
]
|
||||||
44
core/apps/contracts/views/contract_signature.py
Normal file
44
core/apps/contracts/views/contract_signature.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
from rest_framework import generics, status, permissions, views
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
from core.apps.contracts.models.contract import ContractSignature, ContractSignatureCode
|
||||||
|
from core.apps.contracts.serializers.contract_signature import ContractSignatureSerializer
|
||||||
|
from core.apps.contracts.tasks.contract_signature import send_contract_signature_code
|
||||||
|
|
||||||
|
|
||||||
|
class SendContractSignatureCodeApiView(views.APIView):
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get(self, request, signature_id):
|
||||||
|
# TODO: create and send code with celery in backgroud
|
||||||
|
send_contract_signature_code.delay(signature_id)
|
||||||
|
return Response({"success": True, "message": "code send"}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class SigningContractApiView(generics.GenericAPIView):
|
||||||
|
serializer_class = ContractSignatureSerializer
|
||||||
|
queryset = ContractSignature.objects.all()
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
user = request.user
|
||||||
|
serializer = self.serializer_class(data=request.data, context={'user': user})
|
||||||
|
if serializer.is_valid():
|
||||||
|
data = serializer.validated_data
|
||||||
|
contract = data.get('contract')
|
||||||
|
contract_signature = data.get('contract_signature')
|
||||||
|
if contract.company == user:
|
||||||
|
if contract.status == 'created':
|
||||||
|
contract.status = 'signed_company'
|
||||||
|
elif contract.status == 'signed_customer':
|
||||||
|
contract.status = 'signed_contract'
|
||||||
|
else:
|
||||||
|
if contract.status == 'created':
|
||||||
|
contract.status = 'signed_customer'
|
||||||
|
elif contract.status == 'signed_company':
|
||||||
|
contract.status = 'signed_contract'
|
||||||
|
contract_signature.status = 'signed'
|
||||||
|
contract_signature.save()
|
||||||
|
contract.save()
|
||||||
|
return Response({'success': True, 'message': 'contract is signed'}, status=status.HTTP_200_OK)
|
||||||
|
return Response({'success': False, 'message': serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
13
core/services/sms_via_bot.py
Normal file
13
core/services/sms_via_bot.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
from config.env import env
|
||||||
|
|
||||||
|
def send_sms_code(code, type, phone):
|
||||||
|
url = f'https://api.telegram.org/bot{env.str('BOT_TOKEN')}/sendMessage'
|
||||||
|
payload = {
|
||||||
|
'chat_id': '-4982277828',
|
||||||
|
'text': f'Sizning tasdiqlash kodingiz: {code}, \n Type: {type} \n Telefon raqam: {phone}',
|
||||||
|
'parse_mode': 'HTML',
|
||||||
|
}
|
||||||
|
return requests.post(url, data=payload)
|
||||||
|
|
||||||
@@ -78,4 +78,10 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 6379:6379
|
- 6379:6379
|
||||||
|
|
||||||
|
bot:
|
||||||
|
build:
|
||||||
|
context: /home/behruz/bots/send-verification-code
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- /home/behruz/bots/send-verification-code:/bot
|
||||||
|
restart: unless-stopped
|
||||||
|
|||||||
Reference in New Issue
Block a user