From ef512b060f6f0e5453f4c4f81d256ea26ec691c1 Mon Sep 17 00:00:00 2001 From: Alexey Polyakov Date: Sat, 21 Mar 2026 15:05:38 +0300 Subject: [PATCH] =?UTF-8?q?=D0=92=D1=8B=D0=BD=D0=B5=D1=81=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=BE=D0=BA=20=D1=83=D1=87=D0=B0=D1=81=D1=82=D0=BD?= =?UTF-8?q?=D0=B8=D0=BA=D0=BE=D0=B2=20=D1=87=D0=B0=D1=82=D0=B0=20=D0=B2=20?= =?UTF-8?q?=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=83=D1=8E=20=D1=82?= =?UTF-8?q?=D0=B0=D0=B1=D0=BB=D0=B8=D1=86=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/sql_queries.py | 4 +- src/common/tools.py | 28 +++++++++---- src/oneme/models.py | 6 ++- src/oneme/processors.py | 82 +++++++++++++++++++++++++++++---------- src/oneme/socket.py | 4 ++ src/telegrambot/bot.py | 3 +- tables.sql | 15 ++++--- 7 files changed, 103 insertions(+), 39 deletions(-) diff --git a/src/common/sql_queries.py b/src/common/sql_queries.py index 7b47a03..478ddeb 100644 --- a/src/common/sql_queries.py +++ b/src/common/sql_queries.py @@ -13,6 +13,6 @@ class SQLQueries: INSERT_USER_DATA = """ INSERT INTO user_data - (phone, chats, contacts, folders, user_config, chat_config) - VALUES (%s, %s, %s, %s, %s, %s) + (phone, contacts, folders, user_config, chat_config) + VALUES (%s, %s, %s, %s, %s) """ \ No newline at end of file diff --git a/src/common/tools.py b/src/common/tools.py index 9c2c785..ddfee94 100644 --- a/src/common/tools.py +++ b/src/common/tools.py @@ -136,7 +136,7 @@ class Tools: ) # Формируем список участников с временем последней активности - participant_ids = json.loads(row.get("participants")) + participant_ids = await self.get_chat_participants(chatId, db_pool) participants = await self.get_participant_last_activity( chatId, participant_ids, db_pool ) @@ -171,6 +171,7 @@ class Tools: # Получаем ID предыдущего сообщения для избранного (чат ID = senderId) prevMessageId = await self.get_previous_message_id(senderId, db_pool) + # Хардкодим в лист чатов избранное chats.append( self.generate_chat( @@ -195,17 +196,17 @@ class Tools: row = await cursor.fetchone() or {} last_message_id = row.get("id") or 0 # последнее id сообщения в чате + message_id = self.generate_id() + message_time = int(time.time() * 1000) # время отправки сообщения # Вносим новое сообщение в таблицу await cursor.execute( - "INSERT INTO `messages` (chat_id, sender, time, text, attaches, cid, elements, type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s)", - (chatId, senderId, int(time.time() * 1000), text, json.dumps(attaches), cid, json.dumps(elements), type) + "INSERT INTO `messages` (id, chat_id, sender, time, text, attaches, cid, elements, type) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)", + (message_id, chatId, senderId, message_time, text, json.dumps(attaches), cid, json.dumps(elements), type) ) - message_id = cursor.lastrowid # id сообщения - # Возвращаем айдишки - return int(message_id), int(last_message_id) + return int(message_id), int(last_message_id), message_time async def get_last_message(self, chatId, db_pool): """Получение последнего сообщения в чате""" @@ -284,11 +285,22 @@ class Tools: return result + async def get_chat_participants(self, chatId, db_pool): + """Возвращает список ID участников чата из таблицы chat_participants.""" + async with db_pool.acquire() as db_connection: + async with db_connection.cursor() as cursor: + await cursor.execute( + "SELECT user_id FROM chat_participants WHERE chat_id = %s", + (chatId,) + ) + rows = await cursor.fetchall() + return [row["user_id"] for row in rows] + async def auth_required(self, userPhone, coro, *args): if userPhone: await coro(*args) - def generate_user_id(self): + def generate_id(self): # Получаем время в юниксе timestamp = int(time.time()) @@ -300,4 +312,4 @@ class Tools: unique_id = int(hashlib.md5(combined).hexdigest(), 16) % 1000000000 # Возвращаем - return unique_id \ No newline at end of file + return unique_id diff --git a/src/oneme/models.py b/src/oneme/models.py index 26f5508..fec8b80 100644 --- a/src/oneme/models.py +++ b/src/oneme/models.py @@ -126,4 +126,8 @@ class AuthConfirmRegisterPayloadModel(pydantic.BaseModel): class ChatHistoryPayloadModel(pydantic.BaseModel): chatId: int - backward: int \ No newline at end of file + backward: int + +class ChatSubscribePayloadModel(pydantic.BaseModel): + chatId: int + subscribe: bool \ No newline at end of file diff --git a/src/oneme/processors.py b/src/oneme/processors.py index 605b465..f9b5107 100644 --- a/src/oneme/processors.py +++ b/src/oneme/processors.py @@ -365,8 +365,8 @@ class Processors: VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) """, ( - self.tools.generate_user_id(), phone, None, first_name, last_name, None, - json.dumps([]), json.dumps(["ONEME"]), + self.tools.generate_id(), phone, None, first_name, last_name, None, + json.dumps([]), json.dumps(["TT", "ONEME"]), 0, str(now_ms), str(now_s), ) ) @@ -377,12 +377,12 @@ class Processors: await cursor.execute( """ INSERT INTO user_data - (phone, chats, contacts, folders, user_config, chat_config) - VALUES (%s, %s, %s, %s, %s, %s) + (phone, contacts, folders, user_config, chat_config) + VALUES (%s %s, %s, %s, %s) """, ( phone, - json.dumps([]), json.dumps([]), + json.dumps([]), json.dumps(self.static.USER_FOLDERS), json.dumps(self.static.USER_SETTINGS), json.dumps({}), @@ -442,6 +442,9 @@ class Processors: await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_PAYLOAD, writer) return + # Чаты, где состоит пользователь + chats = [] + # Получаем данные из пакета token = payload.get("token") @@ -467,6 +470,18 @@ class Processors: await cursor.execute("SELECT * FROM user_data WHERE phone = %s", (token_data.get("phone"),)) user_data = await cursor.fetchone() + # Ищем все чаты, где состоит пользователь + await cursor.execute( + "SELECT * FROM chat_participants WHERE user_id = %s", + (user.get('id')) + ) + user_chats = await cursor.fetchall() + + for chat in user_chats: + chats.append( + chat.get("chat_id") + ) + # Аватарка с биографией photoId = None if not user.get("avatar_id") else int(user.get("avatar_id")) avatar_url = None if not photoId else self.config.avatar_base_url + photoId @@ -490,8 +505,7 @@ class Processors: ) chats = await self.tools.generate_chats( - json.loads(user_data.get("chats")), - self.db_pool, user.get("id") + chats, self.db_pool, user.get("id") ) # Формируем данные пакета @@ -511,6 +525,10 @@ class Processors: "time": int(time.time() * 1000) } + print( + json.dumps(payload, indent=4) + ) + # Собираем пакет packet = self.proto.pack_packet( cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGIN, payload=payload @@ -609,9 +627,6 @@ class Processors: cid = message.get("cid") or 0 text = message.get("text") or "" - # Время отправки сообщения - messageTime = int(time.time() * 1000) - # Вычисляем ID чата по ID пользователя и ID отправителя, # в случае отсутствия ID чата if not chatId: @@ -637,7 +652,7 @@ class Processors: return # Список участников - participants = json.loads(chat.get("participants")) + participants = await self.tools.get_chat_participants(chatId, db_pool) # Проверяем, является ли отправитель участником чата if int(senderId) not in participants: @@ -645,7 +660,7 @@ class Processors: return # Добавляем сообщение в историю - messageId, lastMessageId = await self.tools.insert_message( + messageId, lastMessageId, messageTime = await self.tools.insert_message( chatId=chatId, senderId=senderId, text=text, @@ -854,10 +869,10 @@ class Processors: if chat: # Проверяем, является ли пользователь участником чата - + participants = await self.tools.get_chat_participants(chatId, self.db_pool) # (в max нельзя смотреть и отправлять сообщения в чат, в котором ты не участник, в отличие от tg (например, комментарии в каналах), # так что надо тоже так делать) - if senderId not in json.loads(chat.get("participants")): + if int(senderId) not in participants: continue # Получаем последнее сообщение из чата @@ -868,8 +883,8 @@ class Processors: # Добавляем чат в список chats.append( self.tools.generate_chat( - chatId, chat.get("owner"), - chat.get("type"), json.loads(chat.get("participants")), + chatId, chat.get("owner"), + chat.get("type"), participants, message, messageTime ) ) @@ -935,10 +950,19 @@ class Processors: # Если диалога нет - создаем if not chat: await cursor.execute( - "INSERT INTO chats (id, owner, type, participants) VALUES (%s, %s, %s, %s)", - (chatId, senderId, "DIALOG", json.dumps([int(senderId), int(user.get("id"))])) + "INSERT INTO chats (id, owner, type) VALUES (%s, %s, %s)", + (chatId, senderId, "DIALOG") ) + # Добавляем участников в таблицу chat_participants + participants = [int(senderId), int(user.get("id"))] + + for user_id in participants: + await cursor.execute( + "INSERT INTO chat_participants (chat_id, user_id) VALUES (%s, %s)", + (chatId, user_id) + ) + # Аватарка с биографией photoId = None if not user.get("avatar_id") else int(user.get("avatar_id")) avatar_url = None if not photoId else self.config.avatar_base_url + photoId @@ -1014,7 +1038,7 @@ class Processors: return # Участники чата - participants = json.loads(chat.get("participants")) + participants = await self.tools.get_chat_participants(chatId, self.db_pool) # Проверяем, является ли отправитель участником чата if int(senderId) not in participants: @@ -1105,7 +1129,7 @@ class Processors: return # Проверяем, является ли пользователь участником чата - participants = json.loads(chat.get("participants")) + 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 @@ -1241,4 +1265,20 @@ class Processors: "eventType": "profile_updated", "profile": profile } - ) \ No newline at end of file + ) + + async def chat_subscribe(self, payload, seq, writer): + # Валидируем входные данные + try: + ChatSubscribePayloadModel.model_validate(payload) + except Exception as e: + await self._send_error(seq, self.opcodes.CHAT_SUBSCRIBE, self.error_types.INVALID_PAYLOAD, writer) + return + + # Созадаем пакет + packet = self.proto.pack_packet( + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CHAT_SUBSCRIBE, payload=None + ) + + # Отправялем + await self._send(writer, packet) diff --git a/src/oneme/socket.py b/src/oneme/socket.py index d1aa8e6..90ea5dc 100644 --- a/src/oneme/socket.py +++ b/src/oneme/socket.py @@ -159,6 +159,10 @@ class OnemeMobileServer: await self.processors.profile( payload, seq, writer, userId=userId, userPhone=userPhone ) + case self.opcodes.CHAT_SUBSCRIBE: + await self.auth_required( + userPhone, self.processors.chat_subscribe, payload, seq, writer + ) case _: self.logger.warning(f"Неизвестный опкод {opcode}") except Exception as e: diff --git a/src/telegrambot/bot.py b/src/telegrambot/bot.py index b3f8118..959aec2 100644 --- a/src/telegrambot/bot.py +++ b/src/telegrambot/bot.py @@ -74,7 +74,7 @@ class TelegramBot: await cursor.execute( self.sql_queries.INSERT_USER, ( - self.tools.generate_user_id(), + self.tools.generate_id(), new_phone, # phone tg_id, # telegram_id message.from_user.first_name[:59], # firstname @@ -93,7 +93,6 @@ class TelegramBot: self.sql_queries.INSERT_USER_DATA, ( new_phone, # phone - json.dumps([]), # chats json.dumps([]), # contacts json.dumps(self.static.USER_FOLDERS), # folders json.dumps(self.static.USER_SETTINGS), # user settings diff --git a/tables.sql b/tables.sql index daa510f..e118764 100644 --- a/tables.sql +++ b/tables.sql @@ -34,7 +34,6 @@ CREATE TABLE `auth_tokens` ( CREATE TABLE `user_data` ( `phone` VARCHAR(20) NOT NULL UNIQUE PRIMARY KEY, - `chats` JSON NOT NULL, `contacts` JSON NOT NULL, `folders` JSON NOT NULL, `user_config` JSON NOT NULL, @@ -44,12 +43,11 @@ CREATE TABLE `user_data` ( CREATE TABLE `chats` ( `id` INT NOT NULL PRIMARY KEY, `owner` INT NOT NULL, - `type` VARCHAR(16) NOT NULL, - `participants` JSON NOT NULL + `type` VARCHAR(16) NOT NULL ); CREATE TABLE `messages` ( - `id` INT AUTO_INCREMENT PRIMARY KEY, + `id` INT NOT NULL PRIMARY KEY, `chat_id` INT NOT NULL, `sender` INT NOT NULL, `time` VARCHAR(32) NOT NULL, @@ -58,4 +56,11 @@ CREATE TABLE `messages` ( `cid` VARCHAR(32) NOT NULL, `elements` JSON NOT NULL, `type` VARCHAR(16) NOT NULL -); \ No newline at end of file +); + +CREATE TABLE `chat_participants` ( + `chat_id` INT NOT NULL, + `user_id` INT NOT NULL, + `joined_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`chat_id`, `user_id`) +);