From 0b7282b284ceadb7994ccf8a9c5e8a4e40a866a9 Mon Sep 17 00:00:00 2001 From: Alexey Polyakov Date: Fri, 27 Mar 2026 19:26:20 +0300 Subject: [PATCH] =?UTF-8?q?TamTam=20&&=20MAX:=20=D0=B8=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=B8=D1=8F=20(=D0=B2=20=D0=BC=D0=BE=D1=85=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=BD=D0=B0=20=D0=B2=D1=80=D0=BE=D0=B4=D0=B5=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87?= =?UTF-8?q?=D1=88=D0=B5=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/oneme/processors.py | 7 ++- src/tamtam/models.py | 6 +- src/tamtam/processors/__init__.py | 6 +- src/tamtam/processors/history.py | 98 +++++++++++++++++++++++++++++++ src/tamtam/websocket.py | 4 ++ 5 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 src/tamtam/processors/history.py diff --git a/src/oneme/processors.py b/src/oneme/processors.py index 6cb6008..2b76391 100644 --- a/src/oneme/processors.py +++ b/src/oneme/processors.py @@ -1148,7 +1148,7 @@ class Processors: if getMessages: if backward > 0: await cursor.execute( - "SELECT * FROM messages WHERE chat_id = %s AND time < %s ORDER BY id DESC LIMIT %s", + "SELECT * FROM messages WHERE chat_id = %s AND time < %s ORDER BY time ASC LIMIT %s", (chatId, from_time, backward) ) @@ -1168,7 +1168,7 @@ class Processors: if forward > 0: await cursor.execute( - "SELECT * FROM messages WHERE chat_id = %s AND time > %s ORDER BY id ASC LIMIT %s", + "SELECT * FROM messages WHERE chat_id = %s AND time > %s ORDER BY time ASC LIMIT %s", (chatId, from_time, forward) ) @@ -1186,6 +1186,9 @@ class Processors: "reactionInfo": {} }) + # Сортируем сообщения по времени + messages.sort(key=lambda x: x["time"]) + # Формируем ответ payload = { "messages": messages diff --git a/src/tamtam/models.py b/src/tamtam/models.py index 7d72e59..63eefdf 100644 --- a/src/tamtam/models.py +++ b/src/tamtam/models.py @@ -37,4 +37,8 @@ class SearchUsersPayloadModel(pydantic.BaseModel): contactIds: list class PingPayloadModel(pydantic.BaseModel): - interactive: bool \ No newline at end of file + interactive: bool + +class ChatHistoryPayloadModel(pydantic.BaseModel): + chatId: int + backward: int \ No newline at end of file diff --git a/src/tamtam/processors/__init__.py b/src/tamtam/processors/__init__.py index 4c798db..de52007 100644 --- a/src/tamtam/processors/__init__.py +++ b/src/tamtam/processors/__init__.py @@ -1,6 +1,10 @@ from .main import MainProcessors from .auth import AuthProcessors from .search import SearchProcessors +from .history import HistoryProcessors -class Processors(MainProcessors, AuthProcessors, SearchProcessors): +class Processors(MainProcessors, + AuthProcessors, + SearchProcessors, + HistoryProcessors): pass \ No newline at end of file diff --git a/src/tamtam/processors/history.py b/src/tamtam/processors/history.py new file mode 100644 index 0000000..4d5d74c --- /dev/null +++ b/src/tamtam/processors/history.py @@ -0,0 +1,98 @@ +import pydantic +import json +from classes.baseprocessor import BaseProcessor +from tamtam.models import ChatHistoryPayloadModel + +class HistoryProcessors(BaseProcessor): + async def chat_history(self, payload, seq, writer, senderId): + """Обработчик получения истории чата""" + # Валидируем данные пакета + try: + ChatHistoryPayloadModel.model_validate(payload) + except pydantic.ValidationError as error: + self.logger.error(f"Возникли ошибки при валидации пакета: {error}") + await self._send_error(seq, self.opcodes.CHAT_HISTORY, self.error_types.INVALID_PAYLOAD, writer) + return + + # Извлекаем данные из пакета + chatId = payload.get("chatId") + forward = payload.get("forward", 0) + backward = payload.get("backward", 0) + from_time = payload.get("from", 0) + getMessages = payload.get("getMessages", True) + messages = [] + + # Проверяем, существует ли чат + async with self.db_pool.acquire() as conn: + async with conn.cursor() as cursor: + await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,)) + chat = await cursor.fetchone() + + # Выбрасываем ошибку, если чата нет + if not chat: + await self._send_error(seq, self.opcodes.CHAT_HISTORY, self.error_types.CHAT_NOT_FOUND, writer) + return + + # Проверяем, является ли пользователь участником чата + participants = await self.tools.get_chat_participants(chatId, self.db_pool) + if int(senderId) not in participants: + await self._send_error(seq, self.opcodes.CHAT_HISTORY, self.error_types.CHAT_NOT_ACCESS, writer) + return + + # Если запрошены сообщения + if getMessages: + if backward > 0: + await cursor.execute( + "SELECT * FROM messages WHERE chat_id = %s AND time < %s ORDER BY time ASC LIMIT %s", + (chatId, from_time, backward) + ) + + result = await cursor.fetchall() + + for row in result: + messages.append({ + "id": row.get("id"), + "time": int(row.get("time")), + "type": row.get("type"), + "sender": row.get("sender"), + "text": row.get("text"), + "attaches": json.loads(row.get("attaches")), + "elements": json.loads(row.get("elements")), + "reactionInfo": {} + }) + + if forward > 0: + await cursor.execute( + "SELECT * FROM messages WHERE chat_id = %s AND time > %s ORDER BY time ASC LIMIT %s", + (chatId, from_time, forward) + ) + + result = await cursor.fetchall() + + for row in result: + messages.append({ + "id": row.get("id"), + "time": int(row.get("time")), + "type": row.get("type"), + "sender": row.get("sender"), + "text": row.get("text"), + "attaches": json.loads(row.get("attaches")), + "elements": json.loads(row.get("elements")), + "reactionInfo": {} + }) + + # Сортируем сообщения по времени + messages.sort(key=lambda x: x["time"]) + + # Формируем ответ + payload = { + "messages": messages + } + + # Собираем пакет + packet = self.proto.pack_packet( + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CHAT_HISTORY, payload=payload + ) + + # Отправялем + await self._send(writer, packet) \ No newline at end of file diff --git a/src/tamtam/websocket.py b/src/tamtam/websocket.py index 7a20da1..2a260fc 100644 --- a/src/tamtam/websocket.py +++ b/src/tamtam/websocket.py @@ -95,6 +95,10 @@ class TTWebSocketServer: await self.auth_required( userPhone, self.processors.contact_info, payload, seq, websocket ) + case self.opcodes.CHAT_HISTORY: + await self.auth_required( + userPhone, self.processors.chat_history, payload, seq, websocket, userId + ) case _: self.logger.warning(f"Неизвестный опкод {opcode}") except websockets.exceptions.ConnectionClosed: