From c7eace46484d1161d07e37db3d8da14f695bb6fb Mon Sep 17 00:00:00 2001 From: Alexey Polyakov Date: Wed, 18 Mar 2026 22:16:31 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=BD=D0=B5=D0=BC=D0=BD=D0=BE=D0=B3=D0=BE=2048=20?= =?UTF-8?q?=D0=BE=D0=BF=D0=BA=D0=BE=D0=B4,=20=D0=B8=20=D0=BF=D0=BE=D1=87?= =?UTF-8?q?=D0=B8=D0=BD=D0=B8=D0=BB=20=D0=BE=D1=82=D0=B4=D0=B0=D1=87=D1=83?= =?UTF-8?q?=20=D0=B8=D1=81=D1=82=D0=BE=D1=80=D0=B8=D0=B8=20=D0=B2=20=D0=B8?= =?UTF-8?q?=D0=B7=D0=B1=D1=80=D0=B0=D0=BD=D0=BD=D0=BE=D0=BC=20(=D0=B8?= =?UTF-8?q?=D0=B7=D0=B1=D1=80=D0=B0=D0=BD=D0=BD=D0=BE=D0=B5=20=D0=B2=D1=81?= =?UTF-8?q?=D0=B5=20=D1=80=D0=B0=D0=B2=D0=BD=D0=BE=20=D0=BD=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=B5=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/tools.py | 87 ++++++++++++++++++++++++++++++++----- src/oneme_tcp/processors.py | 29 ++++++++----- 2 files changed, 95 insertions(+), 21 deletions(-) diff --git a/src/common/tools.py b/src/common/tools.py index dcb3af3..35d93d6 100644 --- a/src/common/tools.py +++ b/src/common/tools.py @@ -79,12 +79,16 @@ class Tools: return contact - def generate_chat(self, id, owner, type, participants, lastMessage, lastEventTime): + def generate_chat(self, id, owner, type, participants, lastMessage, lastEventTime, prevMessageId=0): """Генерация чата""" # Генерируем список участников - result_participants = { - str(participant): 0 for participant in participants - } + if isinstance(participants, dict): + result_participants = {str(k): v for k, v in participants.items()} + else: + # assume list + result_participants = { + str(participant): 0 for participant in participants + } result = None @@ -101,6 +105,7 @@ class Tools: "lastDelayedUpdateTime": 0, "lastFireDelayedErrorTime": 0, "created": 1, + "prevMessageId": prevMessageId, "joinTime": 1, "modified": lastEventTime } @@ -127,11 +132,14 @@ class Tools: chatId, db_pool ) - # Формируем список участников - participants = { - str(participant): 0 for participant in row.get("participants") - } + # Формируем список участников с временем последней активности + participant_ids = json.loads(row.get("participants")) + participants = await self.get_participant_last_activity( + chatId, participant_ids, db_pool + ) + # Получаем ID предыдущего сообщения + prevMessageId = await self.get_previous_message_id(chatId, db_pool) # Выносим результат в лист chats.append( self.generate_chat( @@ -140,7 +148,8 @@ class Tools: row.get("type"), participants, message, - messageTime + messageTime, + prevMessageId ) ) @@ -152,15 +161,23 @@ class Tools: # ID избранного chatId = senderId ^ senderId + # Получаем последнюю активность участника (отправителя) в избранном + participants = await self.get_participant_last_activity( + senderId, [senderId], db_pool + ) + + # Получаем ID предыдущего сообщения для избранного (чат ID = senderId) + prevMessageId = await self.get_previous_message_id(senderId, db_pool) # Хардкодим в лист чатов избранное chats.append( self.generate_chat( chatId, senderId, "DIALOG", - [senderId], + participants, message, - messageTime + messageTime, + prevMessageId ) ) @@ -216,6 +233,54 @@ class Tools: # Возвращаем return message, int(row.get("time")) + async def get_previous_message_id(self, chatId, db_pool): + """Получение ID предыдущего сообщения (второго с конца) в чате.""" + async with db_pool.acquire() as db_connection: + async with db_connection.cursor() as cursor: + await cursor.execute( + "SELECT id FROM `messages` WHERE chat_id = %s ORDER BY time DESC LIMIT 1 OFFSET 1", + (chatId,) + ) + row = await cursor.fetchone() + + # Если результат есть, возвращаем его + if row: + return int(row.get("id")) + + # В ином случае возвращаем 0 + return 0 + + async def get_participant_last_activity(self, chatId, participant_ids, db_pool): + """Возвращает словарь {participant_id: last_activity_time} для участников чата.""" + if not participant_ids: + return {} + + async with db_pool.acquire() as db_connection: + async with db_connection.cursor() as cursor: + # Собираем всех участников + placeholders = ','.join(['%s'] * len(participant_ids)) + query = f""" + SELECT sender, MAX(time) as last_time + FROM messages + WHERE chat_id = %s AND sender IN ({placeholders}) + GROUP BY sender + """ + params = (chatId,) + tuple(participant_ids) + await cursor.execute(query, params) + rows = await cursor.fetchall() + + # Собираем список участников без времени последней активности в чате + result = {str(pid): 0 for pid in participant_ids} + + # Обновляем для каждого участника время последней активности в чате + for row in rows: + sender = str(row["sender"]) + last_time = row["last_time"] + if last_time is not None: + result[sender] = int(last_time) + + return result + async def auth_required(self, userPhone, coro, *args): if userPhone: await coro(*args) diff --git a/src/oneme_tcp/processors.py b/src/oneme_tcp/processors.py index b89f718..12c0c6f 100644 --- a/src/oneme_tcp/processors.py +++ b/src/oneme_tcp/processors.py @@ -1081,21 +1081,30 @@ class Processors: getMessages = payload.get("getMessages", True) messages = [] + # Если пользователь хочет получить историю из избранного, + # то выставляем в качестве ID чата его ID + if chatId == 0: + chatId = senderId + # Проверяем, существует ли чат 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 chatId != senderId: + await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,)) + chat = await cursor.fetchone() - if not chat: - await self._send_error(seq, self.proto.CHAT_HISTORY, self.error_types.CHAT_NOT_FOUND, writer) - return + # Выбрасываем ошибку, если чата нет + if not chat: + await self._send_error(seq, self.proto.CHAT_HISTORY, self.error_types.CHAT_NOT_FOUND, writer) + return - # Проверяем, является ли пользователь участником чата - participants = json.loads(chat.get("participants")) - if int(senderId) not in participants: - await self._send_error(seq, self.proto.CHAT_HISTORY, self.error_types.CHAT_NOT_ACCESS, writer) - return + # Проверяем, является ли пользователь участником чата + participants = json.loads(chat.get("participants")) + if int(senderId) not in participants: + await self._send_error(seq, self.proto.CHAT_HISTORY, self.error_types.CHAT_NOT_ACCESS, writer) + return # Если запрошены сообщения if getMessages: