Поделил процессоры в таме
This commit is contained in:
parent
11b2e2748d
commit
9bc6c15d82
|
|
@ -0,0 +1,49 @@
|
||||||
|
import logging
|
||||||
|
from common.config import ServerConfig
|
||||||
|
from common.static import Static
|
||||||
|
from common.tools import Tools
|
||||||
|
from common.proto_tcp import MobileProto
|
||||||
|
from common.proto_web import WebProto
|
||||||
|
from common.opcodes import Opcodes
|
||||||
|
|
||||||
|
class BaseProcessor:
|
||||||
|
def __init__(self, db_pool=None, clients=None, send_event=None, type="socket"):
|
||||||
|
if clients is None:
|
||||||
|
clients = {}
|
||||||
|
self.config = ServerConfig()
|
||||||
|
self.static = Static()
|
||||||
|
self.tools = Tools()
|
||||||
|
self.opcodes = Opcodes()
|
||||||
|
self.error_types = self.static.ErrorTypes()
|
||||||
|
|
||||||
|
self.db_pool = db_pool
|
||||||
|
self.clients = clients
|
||||||
|
self.send_event = send_event
|
||||||
|
self.logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
if type == "socket":
|
||||||
|
self.proto = MobileProto()
|
||||||
|
elif type == "web":
|
||||||
|
self.proto = WebProto()
|
||||||
|
|
||||||
|
async def _send(self, writer, packet):
|
||||||
|
"""Send packet to client."""
|
||||||
|
try:
|
||||||
|
writer.write(packet)
|
||||||
|
await writer.drain()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def _send_error(self, seq, opcode, error_type, writer):
|
||||||
|
"""Send error response."""
|
||||||
|
payload = self.static.ERROR_TYPES.get(error_type, {
|
||||||
|
"localizedMessage": "Неизвестная ошибка",
|
||||||
|
"error": "unknown.error",
|
||||||
|
"message": "Unknown error",
|
||||||
|
"title": "Неизвестная ошибка"
|
||||||
|
})
|
||||||
|
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_ERR, seq=seq, opcode=opcode, payload=payload
|
||||||
|
)
|
||||||
|
await self._send(writer, packet)
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
from .main import MainProcessors
|
||||||
|
from .auth import AuthProcessors
|
||||||
|
|
||||||
|
class Processors(MainProcessors, AuthProcessors):
|
||||||
|
pass
|
||||||
|
|
@ -1,102 +1,32 @@
|
||||||
import hashlib
|
import hashlib
|
||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
import logging
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from common.static import Static
|
from classes.baseprocessor import BaseProcessor
|
||||||
from common.tools import Tools
|
from tamtam.models import (
|
||||||
|
RequestCodePayloadModel,
|
||||||
from common.proto_tcp import MobileProto
|
VerifyCodePayloadModel,
|
||||||
from common.proto_web import WebProto
|
FinalAuthPayloadModel,
|
||||||
from common.opcodes import Opcodes
|
LoginPayloadModel,
|
||||||
|
)
|
||||||
from tamtam.models import *
|
|
||||||
|
|
||||||
|
|
||||||
class Processors:
|
|
||||||
def __init__(self, db_pool=None, clients=None, send_event=None, type="socket"):
|
|
||||||
if clients is None:
|
|
||||||
clients = {} # Более правильная логика
|
|
||||||
self.static = Static()
|
|
||||||
self.tools = Tools()
|
|
||||||
self.opcodes = Opcodes()
|
|
||||||
self.error_types = self.static.ErrorTypes()
|
|
||||||
self.db_pool = db_pool
|
|
||||||
self.logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
if type == "socket":
|
|
||||||
self.proto = MobileProto()
|
|
||||||
elif type == "web":
|
|
||||||
self.proto = WebProto()
|
|
||||||
|
|
||||||
async def _send(self, writer, packet):
|
|
||||||
try:
|
|
||||||
writer.write(packet)
|
|
||||||
await writer.drain()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
async def _send_error(self, seq, opcode, type, writer):
|
|
||||||
payload = self.static.ERROR_TYPES.get(type, {
|
|
||||||
"localizedMessage": "Неизвестная ошибка",
|
|
||||||
"error": "unknown.error",
|
|
||||||
"message": "Unknown error",
|
|
||||||
"title": "Неизвестная ошибка"
|
|
||||||
})
|
|
||||||
|
|
||||||
packet = self.proto.pack_packet(
|
|
||||||
cmd=self.proto.CMD_ERR, seq=seq, opcode=opcode, payload=payload
|
|
||||||
)
|
|
||||||
|
|
||||||
await self._send(writer, packet)
|
|
||||||
|
|
||||||
async def session_init(self, payload, seq, writer):
|
|
||||||
"""Обработчик приветствия"""
|
|
||||||
# Валидируем данные пакета
|
|
||||||
try:
|
|
||||||
HelloPayloadModel.model_validate(payload)
|
|
||||||
except Exception as e:
|
|
||||||
await self._send_error(seq, self.opcodes.SESSION_INIT, self.error_types.INVALID_PAYLOAD, writer)
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
# Получаем данные из пакета
|
|
||||||
device_type = payload.get("userAgent").get("deviceType")
|
|
||||||
device_name = payload.get("userAgent").get("deviceName")
|
|
||||||
|
|
||||||
# Данные пакета
|
|
||||||
payload = {
|
|
||||||
"proxy": "",
|
|
||||||
"logs-enabled": False,
|
|
||||||
"proxy-domains": [],
|
|
||||||
"location": "RU",
|
|
||||||
"libh-enabled": False,
|
|
||||||
"phone-auto-complete-enabled": False
|
|
||||||
}
|
|
||||||
|
|
||||||
# Собираем пакет
|
|
||||||
packet = self.proto.pack_packet(
|
|
||||||
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.SESSION_INIT, payload=payload
|
|
||||||
)
|
|
||||||
|
|
||||||
# Отправляем
|
|
||||||
await self._send(writer, packet)
|
|
||||||
return device_type, device_name
|
|
||||||
|
|
||||||
|
class AuthProcessors(BaseProcessor):
|
||||||
async def auth_request(self, payload, seq, writer):
|
async def auth_request(self, payload, seq, writer):
|
||||||
"""Обработчик запроса кода"""
|
"""Обработчик запроса кода"""
|
||||||
# Валидируем данные пакета
|
# Валидируем данные пакета
|
||||||
try:
|
try:
|
||||||
RequestCodePayloadModel.model_validate(payload)
|
RequestCodePayloadModel.model_validate(payload)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self._send_error(seq, self.opcodes.AUTH_REQUEST, self.error_types.INVALID_PAYLOAD, writer)
|
await self._send_error(seq, self.opcodes.AUTH_REQUEST,
|
||||||
|
self.error_types.INVALID_PAYLOAD, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Извлекаем телефон из пакета
|
# Извлекаем телефон из пакета
|
||||||
phone = re.sub(r'\D', '', payload.get("phone", "")) # Не хардкодим, через регулярки
|
phone = re.sub(r'\D', '', payload.get("phone", ""))
|
||||||
|
|
||||||
# Генерируем токен с кодом
|
# Генерируем токен с кодом
|
||||||
code = f"{secrets.randbelow(1_000_000):06d}" # Старая версия ненадежна, могла отбросить ведущие нули или вообще интерпритировать как систему счисления с основанием 8
|
code = f"{secrets.randbelow(1_000_000):06d}"
|
||||||
token = secrets.token_urlsafe(128)
|
token = secrets.token_urlsafe(128)
|
||||||
|
|
||||||
# Хешируем
|
# Хешируем
|
||||||
|
|
@ -114,8 +44,10 @@ class Processors:
|
||||||
|
|
||||||
# Если пользователь существует, сохраняем токен
|
# Если пользователь существует, сохраняем токен
|
||||||
if user:
|
if user:
|
||||||
# Сохраняем токен
|
await cursor.execute(
|
||||||
await cursor.execute("INSERT INTO auth_tokens (phone, token_hash, code_hash, expires, state) VALUES (%s, %s, %s, %s, %s)", (phone, token_hash, code_hash, expires, "started",))
|
"INSERT INTO auth_tokens (phone, token_hash, code_hash, expires, state) VALUES (%s, %s, %s, %s, %s)",
|
||||||
|
(phone, token_hash, code_hash, expires, "started")
|
||||||
|
)
|
||||||
|
|
||||||
# Данные пакета
|
# Данные пакета
|
||||||
payload = {
|
payload = {
|
||||||
|
|
@ -142,7 +74,8 @@ class Processors:
|
||||||
try:
|
try:
|
||||||
VerifyCodePayloadModel.model_validate(payload)
|
VerifyCodePayloadModel.model_validate(payload)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self._send_error(seq, self.opcodes.AUTH, self.error_types.INVALID_PAYLOAD, writer)
|
await self._send_error(seq, self.opcodes.AUTH,
|
||||||
|
self.error_types.INVALID_PAYLOAD, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Извлекаем данные из пакета
|
# Извлекаем данные из пакета
|
||||||
|
|
@ -157,17 +90,21 @@ class Processors:
|
||||||
async with self.db_pool.acquire() as conn:
|
async with self.db_pool.acquire() as conn:
|
||||||
async with conn.cursor() as cursor:
|
async with conn.cursor() as cursor:
|
||||||
# Ищем токен
|
# Ищем токен
|
||||||
await cursor.execute("SELECT * FROM auth_tokens WHERE token_hash = %s AND expires > UNIX_TIMESTAMP()",
|
await cursor.execute(
|
||||||
(hashed_token,))
|
"SELECT * FROM auth_tokens WHERE token_hash = %s AND expires > UNIX_TIMESTAMP()",
|
||||||
|
(hashed_token,)
|
||||||
|
)
|
||||||
stored_token = await cursor.fetchone()
|
stored_token = await cursor.fetchone()
|
||||||
|
|
||||||
if not stored_token:
|
if not stored_token:
|
||||||
await self._send_error(seq, self.opcodes.AUTH, self.error_types.CODE_EXPIRED, writer)
|
await self._send_error(seq, self.opcodes.AUTH,
|
||||||
|
self.error_types.CODE_EXPIRED, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Проверяем код
|
# Проверяем код
|
||||||
if stored_token.get("code_hash") != hashed_code:
|
if stored_token.get("code_hash") != hashed_code:
|
||||||
await self._send_error(seq, self.opcodes.AUTH, self.error_types.INVALID_CODE, writer)
|
await self._send_error(seq, self.opcodes.AUTH,
|
||||||
|
self.error_types.INVALID_CODE, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ищем аккаунт
|
# Ищем аккаунт
|
||||||
|
|
@ -175,7 +112,10 @@ class Processors:
|
||||||
account = await cursor.fetchone()
|
account = await cursor.fetchone()
|
||||||
|
|
||||||
# Обновляем состояние токена
|
# Обновляем состояние токена
|
||||||
await cursor.execute("UPDATE auth_tokens set state = %s WHERE token_hash = %s", ("verified", hashed_token,))
|
await cursor.execute(
|
||||||
|
"UPDATE auth_tokens set state = %s WHERE token_hash = %s",
|
||||||
|
("verified", hashed_token)
|
||||||
|
)
|
||||||
|
|
||||||
# Генерируем профиль
|
# Генерируем профиль
|
||||||
# Аватарка с биографией
|
# Аватарка с биографией
|
||||||
|
|
@ -219,7 +159,8 @@ class Processors:
|
||||||
try:
|
try:
|
||||||
FinalAuthPayloadModel.model_validate(payload)
|
FinalAuthPayloadModel.model_validate(payload)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_PAYLOAD, writer)
|
await self._send_error(seq, self.opcodes.AUTH_CONFIRM,
|
||||||
|
self.error_types.INVALID_PAYLOAD, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Извлекаем данные из пакета
|
# Извлекаем данные из пакета
|
||||||
|
|
@ -239,17 +180,21 @@ class Processors:
|
||||||
async with self.db_pool.acquire() as conn:
|
async with self.db_pool.acquire() as conn:
|
||||||
async with conn.cursor() as cursor:
|
async with conn.cursor() as cursor:
|
||||||
# Ищем токен
|
# Ищем токен
|
||||||
await cursor.execute("SELECT * FROM auth_tokens WHERE token_hash = %s AND expires > UNIX_TIMESTAMP()",
|
await cursor.execute(
|
||||||
(hashed_token,))
|
"SELECT * FROM auth_tokens WHERE token_hash = %s AND expires > UNIX_TIMESTAMP()",
|
||||||
|
(hashed_token,)
|
||||||
|
)
|
||||||
stored_token = await cursor.fetchone()
|
stored_token = await cursor.fetchone()
|
||||||
|
|
||||||
if stored_token is None:
|
if stored_token is None:
|
||||||
await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_TOKEN, writer)
|
await self._send_error(seq, self.opcodes.AUTH_CONFIRM,
|
||||||
|
self.error_types.INVALID_TOKEN, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Если авторизация только началась - отдаем ошибку
|
# Если авторизация только началась - отдаем ошибку
|
||||||
if stored_token.get("state") == "started":
|
if stored_token.get("state") == "started":
|
||||||
await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_TOKEN, writer)
|
await self._send_error(seq, self.opcodes.AUTH_CONFIRM,
|
||||||
|
self.error_types.INVALID_TOKEN, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ищем аккаунт
|
# Ищем аккаунт
|
||||||
|
|
@ -262,13 +207,13 @@ class Processors:
|
||||||
# Создаем сессию
|
# Создаем сессию
|
||||||
await cursor.execute(
|
await cursor.execute(
|
||||||
"INSERT INTO tokens (phone, token_hash, device_type, device_name, location, time) VALUES (%s, %s, %s, %s, %s, %s)",
|
"INSERT INTO tokens (phone, token_hash, device_type, device_name, location, time) VALUES (%s, %s, %s, %s, %s, %s)",
|
||||||
(stored_token.get("phone"), hashed_login, deviceType, deviceName, "Epstein Island",
|
(stored_token.get("phone"), hashed_login, deviceType, deviceName,
|
||||||
int(time.time()),)
|
"Epstein Island", int(time.time()))
|
||||||
)
|
)
|
||||||
|
|
||||||
# Аватарка с биографией
|
# Аватарка с биографией
|
||||||
photo_id = None if not account.get("avatar_id") else int(account.get("avatar_id"))
|
photo_id = None if not account.get("avatar_id") else int(account.get("avatar_id"))
|
||||||
avatar_url = None if not photo_id else self.config.avatar_base_url + photo_id
|
avatar_url = None if not photo_id else self.config.avatar_base_url + str(photo_id)
|
||||||
description = None if not account.get("description") else account.get("description")
|
description = None if not account.get("description") else account.get("description")
|
||||||
|
|
||||||
# Собираем данные пакета
|
# Собираем данные пакета
|
||||||
|
|
@ -295,7 +240,7 @@ class Processors:
|
||||||
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH_CONFIRM, payload=payload
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH_CONFIRM, payload=payload
|
||||||
)
|
)
|
||||||
|
|
||||||
# Отправялем
|
# Отправляем
|
||||||
await self._send(writer, packet)
|
await self._send(writer, packet)
|
||||||
|
|
||||||
async def login(self, payload, seq, writer):
|
async def login(self, payload, seq, writer):
|
||||||
|
|
@ -303,9 +248,10 @@ class Processors:
|
||||||
# Валидируем данные пакета
|
# Валидируем данные пакета
|
||||||
try:
|
try:
|
||||||
LoginPayloadModel.model_validate(payload)
|
LoginPayloadModel.model_validate(payload)
|
||||||
except pydantic.ValidationError as error:
|
except Exception as e:
|
||||||
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
|
self.logger.error(f"Возникли ошибки при валидации пакета: {e}")
|
||||||
await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_PAYLOAD, writer)
|
await self._send_error(seq, self.opcodes.LOGIN,
|
||||||
|
self.error_types.INVALID_PAYLOAD, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Получаем данные из пакета
|
# Получаем данные из пакета
|
||||||
|
|
@ -322,7 +268,8 @@ class Processors:
|
||||||
|
|
||||||
# Если токен не найден, отправляем ошибку
|
# Если токен не найден, отправляем ошибку
|
||||||
if token_data is None:
|
if token_data is None:
|
||||||
await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_TOKEN, writer)
|
await self._send_error(seq, self.opcodes.LOGIN,
|
||||||
|
self.error_types.INVALID_TOKEN, writer)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ищем аккаунт пользователя в бд
|
# Ищем аккаунт пользователя в бд
|
||||||
|
|
@ -335,7 +282,7 @@ class Processors:
|
||||||
|
|
||||||
# Аватарка с биографией
|
# Аватарка с биографией
|
||||||
photo_id = None if not user.get("avatar_id") else int(user.get("avatar_id"))
|
photo_id = None if not user.get("avatar_id") else int(user.get("avatar_id"))
|
||||||
avatar_url = None if not photo_id else self.config.avatar_base_url + photo_id
|
avatar_url = None if not photo_id else self.config.avatar_base_url + str(photo_id)
|
||||||
description = None if not user.get("description") else user.get("description")
|
description = None if not user.get("description") else user.get("description")
|
||||||
|
|
||||||
# Генерируем профиль
|
# Генерируем профиль
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
from classes.baseprocessor import BaseProcessor
|
||||||
|
from tamtam.models import HelloPayloadModel
|
||||||
|
|
||||||
|
class MainProcessors(BaseProcessor):
|
||||||
|
async def session_init(self, payload, seq, writer):
|
||||||
|
"""Обработчик приветствия"""
|
||||||
|
# Валидируем данные пакета
|
||||||
|
try:
|
||||||
|
HelloPayloadModel.model_validate(payload)
|
||||||
|
except Exception as e:
|
||||||
|
await self._send_error(seq, self.opcodes.SESSION_INIT,
|
||||||
|
self.error_types.INVALID_PAYLOAD, writer)
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
# Получаем данные из пакета
|
||||||
|
device_type = payload.get("userAgent").get("deviceType")
|
||||||
|
device_name = payload.get("userAgent").get("deviceName")
|
||||||
|
|
||||||
|
# Данные пакета
|
||||||
|
payload = {
|
||||||
|
"proxy": "",
|
||||||
|
"logs-enabled": False,
|
||||||
|
"proxy-domains": [],
|
||||||
|
"location": "RU",
|
||||||
|
"libh-enabled": False,
|
||||||
|
"phone-auto-complete-enabled": False
|
||||||
|
}
|
||||||
|
|
||||||
|
# Собираем пакет
|
||||||
|
packet = self.proto.pack_packet(
|
||||||
|
cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.SESSION_INIT, payload=payload
|
||||||
|
)
|
||||||
|
|
||||||
|
# Отправляем
|
||||||
|
await self._send(writer, packet)
|
||||||
|
return device_type, device_name
|
||||||
Loading…
Reference in New Issue