Доделал авторизацию в ТамТаме

This commit is contained in:
Alexey Polyakov 2026-03-19 00:10:21 +03:00
parent c7eace4648
commit 2dab853569
3 changed files with 157 additions and 0 deletions

View File

@ -27,4 +27,8 @@ class FinalAuthPayloadModel(pydantic.BaseModel):
deviceType: str
tokenType: str
deviceId: str
token: str
class LoginPayloadModel(pydantic.BaseModel):
interactive: bool
token: str

View File

@ -289,3 +289,104 @@ class Processors:
# Отправялем
await self._send(writer, packet)
async def process_login(self, payload, seq, writer):
"""Обработчик авторизации клиента на сервере"""
# Валидируем данные пакета
try:
LoginPayloadModel.model_validate(payload)
except pydantic.ValidationError as error:
self.logger.error(f"Возникли ошибки при валидации пакета: {error}")
await self._send_error(seq, self.proto.LOGIN, self.error_types.INVALID_PAYLOAD, writer)
return
# Получаем данные из пакета
token = payload.get("token")
# Хешируем токен
hashed_token = hashlib.sha256(token.encode()).hexdigest()
# Ищем токен в бд
async with self.db_pool.acquire() as conn:
async with conn.cursor() as cursor:
await cursor.execute("SELECT * FROM tokens WHERE token_hash = %s", (hashed_token,))
token_data = await cursor.fetchone()
# Если токен не найден, отправляем ошибку
if token_data is None:
await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_TOKEN, writer)
return
# Ищем аккаунт пользователя в бд
await cursor.execute("SELECT * FROM users WHERE phone = %s", (token_data.get("phone"),))
user = await cursor.fetchone()
# Ищем данные пользователя в бд
await cursor.execute("SELECT * FROM user_data WHERE phone = %s", (token_data.get("phone"),))
user_data = await cursor.fetchone()
# Аватарка с биографией
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
description = None if not user.get("description") else user.get("description")
# Генерируем профиль
profile = self.tools.generate_profile_tt(
id=user.get("id"),
phone=int(user.get("phone")),
avatarUrl=avatar_url,
photoId=photo_id,
updateTime=int(user.get("updatetime")),
firstName=user.get("firstname"),
lastName=user.get("lastname"),
options=json.loads(user.get("options")),
description=description,
username=user.get("username")
)
chats = await self.tools.generate_chats(
json.loads(user_data.get("chats")),
self.db_pool, user.get("id")
)
# Формируем данные пакета
payload = {
"profile": profile,
"chats": chats,
"chatMarker": 0,
"messages": {},
"contacts": [],
"presence": {},
"config": {
"hash": "0",
"server": {},
"user": json.loads(user_data.get("user_config")),
"chatFolders": {
"FOLDERS": [],
"ALL_FILTER_EXCLUDE": []
}
},
"token": token,
"calls": [],
"videoChatHistory": False,
"drafts": {
"chats": {
"discarded": {},
"saved": {}
},
"users": {
"discarded": {},
"saved": {}
}
},
"time": int(time.time() * 1000)
}
# Собираем пакет
packet = self.proto.pack_packet(
cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.LOGIN, payload=payload
)
# Отправляем
await self._send(writer, packet)
return int(user.get("phone")), int(user.get("id")), hashed_token

View File

@ -86,6 +86,14 @@ class TTMobileServer:
await self.processors._send_error(seq, self.proto.FINAL_AUTH, self.processors.error_types.RATE_LIMITED, writer)
else:
await self.processors.process_final_auth(payload, seq, writer, deviceType, deviceName)
case self.proto.LOGIN:
if not self.auth_rate_limiter.is_allowed(address[0]):
await self.processors._send_error(seq, self.proto.LOGIN, self.processors.error_types.RATE_LIMITED, writer)
else:
userPhone, userId, hashedToken = await self.processors.process_login(payload, seq, writer)
if userPhone:
await self._finish_auth(writer, address, userPhone, userId)
case _:
self.logger.warning(f"Неизвестный опкод {opcode}")
except Exception as e:
@ -95,6 +103,50 @@ class TTMobileServer:
writer.close()
self.logger.info(f"Прекратил работать работать с клиентом {address[0]}:{address[1]}")
async def _finish_auth(self, writer, addr, phone, id):
"""Завершение открытия сессии"""
# Ищем пользователя в словаре
user = self.clients.get(id)
# Добавляем новое подключение в словарь
if user:
user["clients"].append(
{
"writer": writer,
"ip": addr[0],
"port": addr[1],
"protocol": "oneme_mobile"
}
)
else:
self.clients[id] = {
"phone": phone,
"id": id,
"clients": [
{
"writer": writer,
"ip": addr[0],
"port": addr[1],
"protocol": "oneme_mobile"
}
]
}
async def _end_session(self, id, ip, port):
"""Завершение сессии"""
# Получаем пользователя в списке
user = self.clients.get(id)
if not user:
return
# Получаем подключения пользователя
clients = user.get("clients", [])
# Удаляем нужное подключение из словаря
for i, client in enumerate(clients):
if (client.get("ip"), client.get("port")) == (ip, port):
clients.pop(i)
async def start(self):
"""Функция для запуска сервера"""
self.server = await asyncio.start_server(