From dff6937da870d467a34f9cfc8fb4b4e0b1e77e95 Mon Sep 17 00:00:00 2001 From: Alexey Polyakov Date: Sat, 9 May 2026 15:50:47 +0300 Subject: [PATCH] =?UTF-8?q?MAX:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BD=D1=82=D0=B0=D0=BA?= =?UTF-8?q?=D1=82=D0=B0=20=D0=BF=D0=BE=20=D0=BD=D0=BE=D0=BC=D0=B5=D1=80?= =?UTF-8?q?=D1=83=20=D1=82=D0=B5=D0=BB=D0=B5=D1=84=D0=BE=D0=BD=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/opcodes.py | 1 + src/oneme/models.py | 4 ++ src/oneme/processors/contacts.py | 110 ++++++++++++++++++++++++++++++- src/oneme/socket.py | 9 +++ src/oneme/websocket.py | 9 +++ 5 files changed, 132 insertions(+), 1 deletion(-) diff --git a/src/common/opcodes.py b/src/common/opcodes.py index b30c26f..c461d91 100644 --- a/src/common/opcodes.py +++ b/src/common/opcodes.py @@ -44,6 +44,7 @@ class Opcodes: CONTACT_MUTUAL = 38 CONTACT_PHOTOS = 39 CONTACT_SORT = 40 + CONTACT_ADD_BY_PHONE = 41 CONTACT_VERIFY = 42 REMOVE_CONTACT_PHOTO = 43 CHAT_INFO = 48 diff --git a/src/oneme/models.py b/src/oneme/models.py index f0cfb73..ee4ccc3 100644 --- a/src/oneme/models.py +++ b/src/oneme/models.py @@ -145,6 +145,10 @@ class ContactListPayloadModel(pydantic.BaseModel): class ContactPresencePayloadModel(pydantic.BaseModel): contactIds: list +class ContactAddByPhonePayloadModel(pydantic.BaseModel): + phone: str + firstName: str + class ContactUpdatePayloadModel(pydantic.BaseModel): action: str contactId: int diff --git a/src/oneme/processors/contacts.py b/src/oneme/processors/contacts.py index 49c23be..278cb2f 100644 --- a/src/oneme/processors/contacts.py +++ b/src/oneme/processors/contacts.py @@ -2,7 +2,7 @@ import pydantic import json import time from classes.baseprocessor import BaseProcessor -from oneme.models import ContactListPayloadModel, ContactPresencePayloadModel, ContactUpdatePayloadModel +from oneme.models import ContactAddByPhonePayloadModel, ContactListPayloadModel, ContactPresencePayloadModel, ContactUpdatePayloadModel class ContactsProcessors(BaseProcessor): async def contact_list(self, payload, seq, writer, userId): @@ -112,6 +112,23 @@ class ContactsProcessors(BaseProcessor): "INSERT INTO contacts (owner_id, contact_id, custom_firstname, custom_lastname, is_blocked) VALUES (%s, %s, %s, %s, FALSE)", (userId, contactId, firstName, lastName) ) + + # Создаем диалог, если его нет + chatId = userId ^ contactId + await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,)) + chat = await cursor.fetchone() + + if not chat: + await cursor.execute( + "INSERT INTO chats (id, owner, type) VALUES (%s, %s, %s)", + (chatId, userId, "DIALOG") + ) + + for uid in [int(userId), int(contactId)]: + await cursor.execute( + "INSERT INTO chat_participants (chat_id, user_id) VALUES (%s, %s)", + (chatId, uid) + ) # а если уже существует, отправляем ошибку else: await self._send_error(seq, self.opcodes.CONTACT_UPDATE, self.error_types.CONTACT_ALREADY_ADDED, writer) @@ -277,6 +294,97 @@ class ContactsProcessors(BaseProcessor): await self._send(writer, packet) + async def contact_add_by_phone(self, payload, seq, writer, userId): + """Добавление контакта по номеру телефона""" + # Валидируем данные пакета + try: + ContactAddByPhonePayloadModel.model_validate(payload) + except pydantic.ValidationError as error: + self.logger.error(f"Возникли ошибки при валидации пакета: {error}") + await self._send_error(seq, self.opcodes.CONTACT_ADD_BY_PHONE, self.error_types.INVALID_PAYLOAD, writer) + return + + phone = payload.get("phone") + firstName = payload.get("firstName") + lastName = payload.get("lastName") + + # Ищем пользователя по номеру телефона + async with self.db_pool.acquire() as conn: + async with conn.cursor() as cursor: + await cursor.execute("SELECT * FROM users WHERE phone = %s", (int(phone),)) + user = await cursor.fetchone() + + if not user: + await self._send_error(seq, self.opcodes.CONTACT_ADD_BY_PHONE, self.error_types.CONTACT_NOT_FOUND, writer) + return + + contactId = user.get("id") + + # Проверяем, не добавлен ли уже контакт + await cursor.execute( + "SELECT * FROM contacts WHERE owner_id = %s AND contact_id = %s", + (userId, contactId) + ) + existing_contact = await cursor.fetchone() + + is_new = existing_contact is None + + if is_new: + # Добавляем контакт + await cursor.execute( + "INSERT INTO contacts (owner_id, contact_id, custom_firstname, custom_lastname) VALUES (%s, %s, %s, %s)", + (userId, contactId, firstName, lastName) + ) + + # Создаем диалог, если его нет + chatId = userId ^ contactId + await cursor.execute("SELECT * FROM chats WHERE id = %s", (chatId,)) + chat = await cursor.fetchone() + + if not chat: + await cursor.execute( + "INSERT INTO chats (id, owner, type) VALUES (%s, %s, %s)", + (chatId, userId, "DIALOG") + ) + + for uid in [int(userId), int(contactId)]: + await cursor.execute( + "INSERT INTO chat_participants (chat_id, user_id) VALUES (%s, %s)", + (chatId, uid) + ) + + # Генерируем профиль + 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 + str(photoId) + + contact = self.tools.generate_profile( + id=user.get("id"), + phone=int(user.get("phone")), + avatarUrl=avatar_url, + photoId=photoId, + updateTime=int(user.get("updatetime")), + firstName=user.get("firstname"), + lastName=user.get("lastname"), + options=json.loads(user.get("options")), + accountStatus=int(user.get("accountstatus")), + description=user.get("description"), + includeProfileOptions=False, + custom_firstname=firstName, + custom_lastname=lastName, + username=user.get("username"), + ) + + response_payload = { + "new": is_new, + "contact": contact + } + + packet = self.proto.pack_packet( + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_ADD_BY_PHONE, payload=response_payload + ) + + await self._send(writer, packet) + async def contact_presence(self, payload, seq, writer): """Обработчик получения статуса контактов""" # Валидируем данные пакета diff --git a/src/oneme/socket.py b/src/oneme/socket.py index 67569e3..2945ca3 100644 --- a/src/oneme/socket.py +++ b/src/oneme/socket.py @@ -311,6 +311,15 @@ class OnemeMobile: writer, userId, ) + case self.opcodes.CONTACT_ADD_BY_PHONE: + await self.auth_required( + userPhone, + self.processors.contact_add_by_phone, + payload, + seq, + writer, + userId, + ) case self.opcodes.CONTACT_PRESENCE: await self.auth_required( userPhone, diff --git a/src/oneme/websocket.py b/src/oneme/websocket.py index b495dba..ea5beaa 100644 --- a/src/oneme/websocket.py +++ b/src/oneme/websocket.py @@ -294,6 +294,15 @@ class OnemeWS: websocket, userId, ) + case self.opcodes.CONTACT_ADD_BY_PHONE: + await self.auth_required( + userPhone, + self.processors.contact_add_by_phone, + payload, + seq, + websocket, + userId, + ) case self.opcodes.CONTACT_PRESENCE: await self.auth_required( userPhone,