initial commit
This commit is contained in:
162
api/api_phone_verificator.py
Executable file
162
api/api_phone_verificator.py
Executable file
@@ -0,0 +1,162 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user