This repository has been archived on 2024-09-18. You can view files and clone it, but cannot push or open issues or pull requests.
arka-api/api/api_phone_verificator.py
2023-03-06 20:27:57 +03:00

163 lines
7.7 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TODO адаптировать под работу с базой данных вместо локального словаря
from arka.settings import PHONE_VERIFICATION_ENABLE, PHONE_VERIFICATION_ATTEMPTS,\
PHONE_VERIFICATION_ACCESS_KEY, PHONE_VERIFICATION_RESEND_TIME_SECS
from threading import Thread, Lock, Event
import random
import traceback
from datetime import datetime
import requests
class PhoneVerificationService:
__lock = Lock()
__event = Event()
# номера, для которых отправлен запрос верификации (на внешний ресурс)
# имеет структуру:
# "{phone}": {"code": None|int|"FAILED", "attempts": int}
# None в code означает что ответ еще не пришел, остальное вроде понятно
# attempts - сколько попыток верификации осталось (по умолчанию 5)
__codes = {}
# очередь номеров, которые требуют верификации
__to_verify = []
__instance = None
@staticmethod
def __service_run():
while True:
try:
PhoneVerificationService.__event.wait()
phones = None
with PhoneVerificationService.__lock:
if PhoneVerificationService.__event.is_set():
PhoneVerificationService.__event.clear()
phones = PhoneVerificationService.__to_verify.copy()
PhoneVerificationService.__to_verify.clear()
if phones is not None:
for phone in phones:
# тут должна быть проверка, есть ли телефон в списке кодов
obj = {
"code": None,
"attempts": PHONE_VERIFICATION_ATTEMPTS,
"time": datetime.now()
}
with PhoneVerificationService.__lock:
PhoneVerificationService.__codes[phone] = obj
request_success = False
if PHONE_VERIFICATION_ENABLE:
try:
# параметры для sms
params = {
"phone": lambda: phone[1:] if phone.startswith("+") else phone,
"ip": -1,
"api_id": PHONE_VERIFICATION_ACCESS_KEY
}
res = requests.get("https://sms.ru/code/call", params=params, timeout=5)
res_json = res.json()
request_success = True
if res_json["status"] == "OK":
with PhoneVerificationService.__lock:
PhoneVerificationService.__codes[phone]["code"] = res_json["code"]
print(f"Verify code for {phone}: {res_json['code']} (sms)")
else:
with PhoneVerificationService.__lock:
PhoneVerificationService.__codes[phone]["code"] = "FAILED"
print(f"Verify code for {phone}: FAILED (sms)")
except:
if not request_success:
with PhoneVerificationService.__lock:
PhoneVerificationService.__codes[phone]["code"] = "FAILED"
traceback.print_exc()
else:
try:
# для бота vk
code = random.randint(1000, 9999)
params = {
"v": 5.131,
# "user_ids": '352634831,405800248,280108789', # Гоша, Влад, Норик
"chat_id": '1', # конфа
"access_token": PHONE_VERIFICATION_ACCESS_KEY,
"message": f"Верификация для номера {phone}<br>Код: {code}",
"random_id": random.randint(1000, 100000000)
}
res = requests.get("https://api.vk.com/method/messages.send", params=params, timeout=5)
# print(res.content)
request_success = True
obj["code"] = code
with PhoneVerificationService.__lock:
PhoneVerificationService.__codes[phone] = obj
print(f"Verify code for {phone}: {obj['code']} (vk)")
except:
if not request_success:
with PhoneVerificationService.__lock:
PhoneVerificationService.__codes[phone]["code"] = "FAILED"
traceback.print_exc()
except:
traceback.print_exc()
@staticmethod
def create():
if PhoneVerificationService.__instance is None:
PhoneVerificationService.__instance = Thread(target=PhoneVerificationService.__service_run, daemon=True)
PhoneVerificationService.__instance.start()
CHECK_PHONE_NOT_FOUND = "not-found"
CHECK_PHONE_NOT_READY = "not-ready"
CHECK_PHONE_FAILED = "failed"
CHECK_PHONE_INVALID_CODE = "invalid"
CHECK_PHONE_MAX_ATTEMPTS = "max-attempts"
CHECK_PHONE_RESEND_LIMIT = "resend"
@staticmethod
def check_code(phone: str, code: int, auto_pop=False):
with PhoneVerificationService.__lock:
if phone not in PhoneVerificationService.__codes:
return False, PhoneVerificationService.CHECK_PHONE_NOT_FOUND
c = PhoneVerificationService.__codes[phone]
print(f"verify struct: {c}")
if c["code"] is None:
# print(f"[PhoneVerificationService] for phone {phone} code not received yet")
return False, PhoneVerificationService.CHECK_PHONE_NOT_READY
if c["code"] == "FAILED":
if auto_pop:
PhoneVerificationService.__codes.pop(phone)
return False, PhoneVerificationService.CHECK_PHONE_FAILED
if c["attempts"] > 0:
if c["code"] == code:
if auto_pop:
PhoneVerificationService.__codes.pop(phone)
return True, None
else:
# print(f"invalid code! attempts: {c['attempts']}")
PhoneVerificationService.__codes[phone]["attempts"] -= 1
return False, PhoneVerificationService.CHECK_PHONE_INVALID_CODE
else:
return False, PhoneVerificationService.CHECK_PHONE_MAX_ATTEMPTS
@staticmethod
def send_verify(phone: str):
with PhoneVerificationService.__lock:
if phone in PhoneVerificationService.__codes:
c = PhoneVerificationService.__codes[phone]
if (datetime.now() - c["time"]).total_seconds() <= PHONE_VERIFICATION_RESEND_TIME_SECS:
return False, PhoneVerificationService.CHECK_PHONE_RESEND_LIMIT
PhoneVerificationService.__to_verify.append(phone)
PhoneVerificationService.__event.set()
return True, None