From 6c05b5f1b578d0a6b767b7c22232289af9a96f69 Mon Sep 17 00:00:00 2001 From: Alexey Polyakov Date: Thu, 19 Mar 2026 01:13:12 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A8=D0=B2=D1=8B=D1=80=D0=BD=D1=83=D0=BB=20?= =?UTF-8?q?=D0=B0=D1=80=D1=85=D0=B8=D1=82=D0=B5=D0=BA=D1=82=D1=83=D1=80?= =?UTF-8?q?=D1=83,=20=D1=87=D1=82=D0=BE=D0=B1=D1=8B=20=D0=BF=D0=BE=D0=B7?= =?UTF-8?q?=D0=B6=D0=B5=20=D0=BE=D0=B1=D1=8A=D0=B5=D0=B4=D0=B8=D0=BD=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=BE=D0=BB=D0=BB?= =?UTF-8?q?=D0=B5=D1=80=D1=8B=20=D0=B2=D0=B5=D0=B1=D0=B0=20=D0=B8=20=D1=81?= =?UTF-8?q?=D0=BE=D0=BA=D0=B5=D1=82=D0=B0=20=D0=B2=20=D0=BE=D0=B4=D0=BD?= =?UTF-8?q?=D0=BE,=20=D0=B0=20=D1=82=D0=B0=D0=BA=D0=B6=D0=B5=20=D1=80?= =?UTF-8?q?=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80?= =?UTF-8?q?=D0=BE=D1=86=D0=B5=D1=81=D1=81=D0=BE=D1=80=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/opcodes.py | 156 ++++++++++++++++ .../proto.py => common/proto_tcp.py} | 156 +--------------- src/common/proto_web.py | 48 +++++ src/main.py | 6 +- src/{oneme_tcp => oneme}/__init__.py | 0 src/{oneme_tcp => oneme}/config.py | 0 src/{oneme_tcp => oneme}/controller.py | 15 +- src/{oneme_tcp => oneme}/models.py | 0 src/{oneme_tcp => oneme}/processors.py | 169 ++++++++++-------- src/{oneme_tcp/server.py => oneme/socket.py} | 100 ++++++----- src/{tamtam_tcp => tamtam}/__init__.py | 0 src/{tamtam_tcp => tamtam}/controller.py | 2 +- src/{tamtam_tcp => tamtam}/models.py | 0 src/{tamtam_tcp => tamtam}/processors.py | 58 +++--- src/{tamtam_tcp => tamtam}/proto.py | 0 .../server.py => tamtam/socket.py} | 37 ++-- src/tamtam_ws/__init__.py | 0 src/tamtam_ws/controller.py | 22 --- src/tamtam_ws/models.py | 27 --- src/tamtam_ws/processors.py | 80 --------- src/tamtam_ws/proto.py | 165 ----------------- src/tamtam_ws/server.py | 66 ------- 22 files changed, 411 insertions(+), 696 deletions(-) create mode 100644 src/common/opcodes.py rename src/{oneme_tcp/proto.py => common/proto_tcp.py} (50%) create mode 100644 src/common/proto_web.py rename src/{oneme_tcp => oneme}/__init__.py (100%) rename src/{oneme_tcp => oneme}/config.py (100%) rename src/{oneme_tcp => oneme}/controller.py (83%) rename src/{oneme_tcp => oneme}/models.py (100%) rename src/{oneme_tcp => oneme}/processors.py (88%) rename src/{oneme_tcp/server.py => oneme/socket.py} (68%) rename src/{tamtam_tcp => tamtam}/__init__.py (100%) rename src/{tamtam_tcp => tamtam}/controller.py (89%) rename src/{tamtam_tcp => tamtam}/models.py (100%) rename src/{tamtam_tcp => tamtam}/processors.py (86%) rename src/{tamtam_tcp => tamtam}/proto.py (100%) rename src/{tamtam_tcp/server.py => tamtam/socket.py} (84%) delete mode 100644 src/tamtam_ws/__init__.py delete mode 100644 src/tamtam_ws/controller.py delete mode 100644 src/tamtam_ws/models.py delete mode 100644 src/tamtam_ws/processors.py delete mode 100644 src/tamtam_ws/proto.py delete mode 100644 src/tamtam_ws/server.py diff --git a/src/common/opcodes.py b/src/common/opcodes.py new file mode 100644 index 0000000..b30c26f --- /dev/null +++ b/src/common/opcodes.py @@ -0,0 +1,156 @@ +class Opcodes: + def __init__(self): + pass + + PING = 1 + DEBUG = 2 + RECONNECT = 3 + LOG = 5 + SESSION_INIT = 6 + PROFILE = 16 + AUTH_REQUEST = 17 + AUTH = 18 + LOGIN = 19 + LOGOUT = 20 + SYNC = 21 + CONFIG = 22 + AUTH_CONFIRM = 23 + AUTH_CREATE_TRACK = 112 + AUTH_CHECK_PASSWORD = 113 + AUTH_LOGIN_CHECK_PASSWORD = 115 + AUTH_LOGIN_PROFILE_DELETE = 116 + AUTH_LOGIN_RESTORE_PASSWORD = 101 + AUTH_VALIDATE_PASSWORD = 107 + AUTH_VALIDATE_HINT = 108 + AUTH_VERIFY_EMAIL = 109 + AUTH_CHECK_EMAIL = 110 + AUTH_SET_2FA = 111 + AUTH_2FA_DETAILS = 104 + ASSETS_GET = 26 + ASSETS_UPDATE = 27 + ASSETS_GET_BY_IDS = 28 + ASSETS_LIST_MODIFY = 261 + ASSETS_REMOVE = 259 + ASSETS_MOVE = 260 + ASSETS_ADD = 29 + PRESET_AVATARS = 25 + CONTACT_INFO = 32 + CONTACT_INFO_BY_PHONE = 46 + CONTACT_ADD = 33 + CONTACT_UPDATE = 34 + CONTACT_PRESENCE = 35 + CONTACT_LIST = 36 + CONTACT_SEARCH = 37 + CONTACT_MUTUAL = 38 + CONTACT_PHOTOS = 39 + CONTACT_SORT = 40 + CONTACT_VERIFY = 42 + REMOVE_CONTACT_PHOTO = 43 + CHAT_INFO = 48 + CHAT_HISTORY = 49 + CHAT_MARK = 50 + CHAT_MEDIA = 51 + CHAT_DELETE = 52 + CHATS_LIST = 53 + CHAT_CLEAR = 54 + CHAT_UPDATE = 55 + CHAT_CHECK_LINK = 56 + CHAT_JOIN = 57 + CHAT_LEAVE = 58 + CHAT_MEMBERS = 59 + PUBLIC_SEARCH = 60 + CHAT_PERSONAL_CONFIG = 61 + CHAT_CREATE = 63 + REACTIONS_SETTINGS_GET_BY_CHAT_ID = 258 + CHAT_REACTIONS_SETTINGS_SET = 257 + MSG_SEND = 64 + MSG_TYPING = 65 + MSG_DELETE = 66 + MSG_EDIT = 67 + MSG_DELETE_RANGE = 92 + MSG_REACTION = 178 + MSG_CANCEL_REACTION = 179 + MSG_GET_REACTIONS = 180 + MSG_GET_DETAILED_REACTIONS = 181 + CHAT_SEARCH = 68 + MSG_SHARE_PREVIEW = 70 + MSG_GET = 71 + MSG_SEARCH_TOUCH = 72 + MSG_SEARCH = 73 + MSG_GET_STAT = 74 + CHAT_SUBSCRIBE = 75 + VIDEO_CHAT_START = 76 + VIDEO_CHAT_START_ACTIVE = 78 + CHAT_MEMBERS_UPDATE = 77 + VIDEO_CHAT_HISTORY = 79 + PHOTO_UPLOAD = 80 + STICKER_UPLOAD = 81 + VIDEO_UPLOAD = 82 + VIDEO_PLAY = 83 + VIDEO_CHAT_CREATE_JOIN_LINK = 84 + CHAT_PIN_SET_VISIBILITY = 86 + FILE_UPLOAD = 87 + FILE_DOWNLOAD = 88 + LINK_INFO = 89 + SESSIONS_INFO = 96 + SESSIONS_CLOSE = 97 + PHONE_BIND_REQUEST = 98 + PHONE_BIND_CONFIRM = 99 + GET_INBOUND_CALLS = 103 + EXTERNAL_CALLBACK = 105 + OK_TOKEN = 158 + CHAT_COMPLAIN = 117 + MSG_SEND_CALLBACK = 118 + SUSPEND_BOT = 119 + LOCATION_STOP = 124 + GET_LAST_MENTIONS = 127 + STICKER_CREATE = 193 + STICKER_SUGGEST = 194 + VIDEO_CHAT_MEMBERS = 195 + NOTIF_MESSAGE = 128 + NOTIF_TYPING = 129 + NOTIF_MARK = 130 + NOTIF_CONTACT = 131 + NOTIF_PRESENCE = 132 + NOTIF_CONFIG = 134 + NOTIF_CHAT = 135 + NOTIF_ATTACH = 136 + NOTIF_CALL_START = 137 + NOTIF_CONTACT_SORT = 139 + NOTIF_MSG_DELETE_RANGE = 140 + NOTIF_MSG_DELETE = 142 + NOTIF_MSG_REACTIONS_CHANGED = 155 + NOTIF_MSG_YOU_REACTED = 156 + NOTIF_CALLBACK_ANSWER = 143 + CHAT_BOT_COMMANDS = 144 + BOT_INFO = 145 + NOTIF_LOCATION = 147 + NOTIF_LOCATION_REQUEST = 148 + NOTIF_ASSETS_UPDATE = 150 + NOTIF_DRAFT = 152 + NOTIF_DRAFT_DISCARD = 153 + DRAFT_SAVE = 176 + DRAFT_DISCARD = 177 + CHAT_HIDE = 196 + CHAT_SEARCH_COMMON_PARTICIPANTS = 198 + NOTIF_MSG_DELAYED = 154 + NOTIF_PROFILE = 159 + PROFILE_DELETE = 199 + PROFILE_DELETE_TIME = 200 + WEB_APP_INIT_DATA = 160 + COMPLAIN = 161 + COMPLAIN_REASONS_GET = 162 + FOLDERS_GET = 272 + FOLDERS_GET_BY_ID = 273 + FOLDERS_UPDATE = 274 + FOLDERS_REORDER = 275 + FOLDERS_DELETE = 276 + NOTIF_FOLDERS = 277 + + AUTH_QR_APPROVE = 290 + NOTIF_BANNERS = 292 + CHAT_SUGGEST = 300 + AUDIO_PLAY = 301 + SEND_VOTE = 304 + VOTERS_LIST_BY_ANSWER = 305 + GET_POLL_UPDATES = 306 \ No newline at end of file diff --git a/src/oneme_tcp/proto.py b/src/common/proto_tcp.py similarity index 50% rename from src/oneme_tcp/proto.py rename to src/common/proto_tcp.py index 0c085ba..0da4878 100644 --- a/src/oneme_tcp/proto.py +++ b/src/common/proto_tcp.py @@ -1,6 +1,6 @@ import lz4.block, msgpack, logging, json -class Proto: +class MobileProto: def __init__(self) -> None: self.logger = logging.getLogger(__name__) @@ -95,157 +95,3 @@ class Proto: CMD_NOF = 0x200 CMD_ERR = 0x300 PROTO_VER = 10 - - ### Команды - PING = 1 - DEBUG = 2 - RECONNECT = 3 - LOG = 5 - SESSION_INIT = 6 - PROFILE = 16 - AUTH_REQUEST = 17 - AUTH = 18 - LOGIN = 19 - LOGOUT = 20 - SYNC = 21 - CONFIG = 22 - AUTH_CONFIRM = 23 - AUTH_CREATE_TRACK = 112 - AUTH_CHECK_PASSWORD = 113 - AUTH_LOGIN_CHECK_PASSWORD = 115 - AUTH_LOGIN_PROFILE_DELETE = 116 - AUTH_LOGIN_RESTORE_PASSWORD = 101 - AUTH_VALIDATE_PASSWORD = 107 - AUTH_VALIDATE_HINT = 108 - AUTH_VERIFY_EMAIL = 109 - AUTH_CHECK_EMAIL = 110 - AUTH_SET_2FA = 111 - AUTH_2FA_DETAILS = 104 - ASSETS_GET = 26 - ASSETS_UPDATE = 27 - ASSETS_GET_BY_IDS = 28 - ASSETS_LIST_MODIFY = 261 - ASSETS_REMOVE = 259 - ASSETS_MOVE = 260 - ASSETS_ADD = 29 - PRESET_AVATARS = 25 - CONTACT_INFO = 32 - CONTACT_INFO_BY_PHONE = 46 - CONTACT_ADD = 33 - CONTACT_UPDATE = 34 - CONTACT_PRESENCE = 35 - CONTACT_LIST = 36 - CONTACT_SEARCH = 37 - CONTACT_MUTUAL = 38 - CONTACT_PHOTOS = 39 - CONTACT_SORT = 40 - CONTACT_VERIFY = 42 - REMOVE_CONTACT_PHOTO = 43 - CHAT_INFO = 48 - CHAT_HISTORY = 49 - CHAT_MARK = 50 - CHAT_MEDIA = 51 - CHAT_DELETE = 52 - CHATS_LIST = 53 - CHAT_CLEAR = 54 - CHAT_UPDATE = 55 - CHAT_CHECK_LINK = 56 - CHAT_JOIN = 57 - CHAT_LEAVE = 58 - CHAT_MEMBERS = 59 - PUBLIC_SEARCH = 60 - CHAT_PERSONAL_CONFIG = 61 - CHAT_CREATE = 63 - REACTIONS_SETTINGS_GET_BY_CHAT_ID = 258 - CHAT_REACTIONS_SETTINGS_SET = 257 - MSG_SEND = 64 - MSG_TYPING = 65 - MSG_DELETE = 66 - MSG_EDIT = 67 - MSG_DELETE_RANGE = 92 - MSG_REACTION = 178 - MSG_CANCEL_REACTION = 179 - MSG_GET_REACTIONS = 180 - MSG_GET_DETAILED_REACTIONS = 181 - CHAT_SEARCH = 68 - MSG_SHARE_PREVIEW = 70 - MSG_GET = 71 - MSG_SEARCH_TOUCH = 72 - MSG_SEARCH = 73 - MSG_GET_STAT = 74 - CHAT_SUBSCRIBE = 75 - VIDEO_CHAT_START = 76 - VIDEO_CHAT_START_ACTIVE = 78 - CHAT_MEMBERS_UPDATE = 77 - VIDEO_CHAT_HISTORY = 79 - PHOTO_UPLOAD = 80 - STICKER_UPLOAD = 81 - VIDEO_UPLOAD = 82 - VIDEO_PLAY = 83 - VIDEO_CHAT_CREATE_JOIN_LINK = 84 - CHAT_PIN_SET_VISIBILITY = 86 - FILE_UPLOAD = 87 - FILE_DOWNLOAD = 88 - LINK_INFO = 89 - SESSIONS_INFO = 96 - SESSIONS_CLOSE = 97 - PHONE_BIND_REQUEST = 98 - PHONE_BIND_CONFIRM = 99 - GET_INBOUND_CALLS = 103 - EXTERNAL_CALLBACK = 105 - OK_TOKEN = 158 - CHAT_COMPLAIN = 117 - MSG_SEND_CALLBACK = 118 - SUSPEND_BOT = 119 - LOCATION_STOP = 124 - GET_LAST_MENTIONS = 127 - STICKER_CREATE = 193 - STICKER_SUGGEST = 194 - VIDEO_CHAT_MEMBERS = 195 - NOTIF_MESSAGE = 128 - NOTIF_TYPING = 129 - NOTIF_MARK = 130 - NOTIF_CONTACT = 131 - NOTIF_PRESENCE = 132 - NOTIF_CONFIG = 134 - NOTIF_CHAT = 135 - NOTIF_ATTACH = 136 - NOTIF_CALL_START = 137 - NOTIF_CONTACT_SORT = 139 - NOTIF_MSG_DELETE_RANGE = 140 - NOTIF_MSG_DELETE = 142 - NOTIF_MSG_REACTIONS_CHANGED = 155 - NOTIF_MSG_YOU_REACTED = 156 - NOTIF_CALLBACK_ANSWER = 143 - CHAT_BOT_COMMANDS = 144 - BOT_INFO = 145 - NOTIF_LOCATION = 147 - NOTIF_LOCATION_REQUEST = 148 - NOTIF_ASSETS_UPDATE = 150 - NOTIF_DRAFT = 152 - NOTIF_DRAFT_DISCARD = 153 - DRAFT_SAVE = 176 - DRAFT_DISCARD = 177 - CHAT_HIDE = 196 - CHAT_SEARCH_COMMON_PARTICIPANTS = 198 - NOTIF_MSG_DELAYED = 154 - NOTIF_PROFILE = 159 - PROFILE_DELETE = 199 - PROFILE_DELETE_TIME = 200 - WEB_APP_INIT_DATA = 160 - COMPLAIN = 161 - COMPLAIN_REASONS_GET = 162 - FOLDERS_GET = 272 - FOLDERS_GET_BY_ID = 273 - FOLDERS_UPDATE = 274 - FOLDERS_REORDER = 275 - FOLDERS_DELETE = 276 - NOTIF_FOLDERS = 277 - - AUTH_QR_APPROVE = 290 - NOTIF_BANNERS = 292 - CHAT_SUGGEST = 300 - AUDIO_PLAY = 301 - SEND_VOTE = 304 - VOTERS_LIST_BY_ANSWER = 305 - GET_POLL_UPDATES = 306 \ No newline at end of file diff --git a/src/common/proto_web.py b/src/common/proto_web.py new file mode 100644 index 0000000..8cd166b --- /dev/null +++ b/src/common/proto_web.py @@ -0,0 +1,48 @@ +import json + +class WebProto: + def pack_packet(self, ver=10, cmd=1, seq=0, opcode=1, payload=None): + # а разве не надо в жсон запаковывать ещё + # о всё + return json.dumps({ + "ver": ver, + "cmd": cmd, + "seq": seq, + "opcode": opcode, + "payload": payload + }) + + MAX_PACKET_SIZE = 65536 # 64 KB, заглушка, нужно узнать реальные лимиты и поменять, хотя кто будет это делать... + + def unpack_packet(self, packet): + # try catch чтобы не сыпалось всё при неверных пакетах + if isinstance(packet, (str, bytes)) and len(packet) > self.MAX_PACKET_SIZE: + return {} + + try: + parsed_packet = json.loads(packet) + except (json.JSONDecodeError, TypeError, ValueError): + return {} + + return parsed_packet + # мне кажется долго вручную всё писать + # а как еще + # ну вставить сюда целиком и потом через multiline cursor удалить лишнее + # ну ты удалишь тогда. я на тачпаде + # ладно щас другим способом удалю + # всё нахуй + # TAMTAM SOURCE LEAK 2026 + # так ну че делать будем + # так ну + + # 19 опкод сделан? + # нет сэр пошли библиотеку тамы смотреть + # мб найдем че. она без обфускации + # а ты ее видишь? + # пошли + + ### Констаты протокола + CMD_OK = 1 + CMD_NOF = 2 + CMD_ERR = 3 + PROTO_VER = 10 diff --git a/src/main.py b/src/main.py index a5af91b..b0509c1 100644 --- a/src/main.py +++ b/src/main.py @@ -1,10 +1,9 @@ # Импортирование библиотек import ssl, logging, asyncio from common.config import ServerConfig -from oneme_tcp.controller import OnemeMobileController +from oneme.controller import OnemeMobileController from telegrambot.controller import TelegramBotController -from tamtam_tcp.controller import TTMobileController -from tamtam_ws.controller import TTWSController +from tamtam.controller import TTMobileController # Конфиг сервера server_config = ServerConfig() @@ -75,7 +74,6 @@ async def main(): controllers = { "oneme_mobile": OnemeMobileController(), "tamtam_mobile": TTMobileController(), - "tamtam_ws": TTWSController(), "telegrambot": TelegramBotController() } diff --git a/src/oneme_tcp/__init__.py b/src/oneme/__init__.py similarity index 100% rename from src/oneme_tcp/__init__.py rename to src/oneme/__init__.py diff --git a/src/oneme_tcp/config.py b/src/oneme/config.py similarity index 100% rename from src/oneme_tcp/config.py rename to src/oneme/config.py diff --git a/src/oneme_tcp/controller.py b/src/oneme/controller.py similarity index 83% rename from src/oneme_tcp/controller.py rename to src/oneme/controller.py index 463d3e4..7f96fe8 100644 --- a/src/oneme_tcp/controller.py +++ b/src/oneme/controller.py @@ -1,13 +1,16 @@ import asyncio -from oneme_tcp.server import OnemeMobileServer -from oneme_tcp.proto import Proto +from oneme.socket import OnemeMobileServer +from common.proto_tcp import MobileProto +from common.proto_web import WebProto from classes.controllerbase import ControllerBase from common.config import ServerConfig +from common.opcodes import Opcodes class OnemeMobileController(ControllerBase): def __init__(self): self.config = ServerConfig() - self.proto = Proto() + self.proto = MobileProto() + self.opcodes = Opcodes() async def event(self, target, client, eventData): # Извлекаем тип события и врайтер @@ -34,7 +37,7 @@ class OnemeMobileController(ControllerBase): # Создаем пакет packet = self.proto.pack_packet( - cmd=0, seq=1, opcode=self.proto.NOTIF_MESSAGE, payload=payload + cmd=0, seq=1, opcode=self.opcodes.NOTIF_MESSAGE, payload=payload ) elif eventType == "typing": # Данные события @@ -51,7 +54,7 @@ class OnemeMobileController(ControllerBase): # Создаем пакет packet = self.proto.pack_packet( - cmd=0, seq=1, opcode=self.proto.NOTIF_TYPING, payload=payload + cmd=0, seq=1, opcode=self.opcodes.NOTIF_TYPING, payload=payload ) elif eventType == "profile_updated": # Данные события @@ -64,7 +67,7 @@ class OnemeMobileController(ControllerBase): # Создаем пакет packet = self.proto.pack_packet( - cmd=0, seq=1, opcode=self.proto.NOTIF_PROFILE, payload=payload + cmd=0, seq=1, opcode=self.opcodes.NOTIF_PROFILE, payload=payload ) # Отправляем пакет diff --git a/src/oneme_tcp/models.py b/src/oneme/models.py similarity index 100% rename from src/oneme_tcp/models.py rename to src/oneme/models.py diff --git a/src/oneme_tcp/processors.py b/src/oneme/processors.py similarity index 88% rename from src/oneme_tcp/processors.py rename to src/oneme/processors.py index 12c0c6f..aca5ca6 100644 --- a/src/oneme_tcp/processors.py +++ b/src/oneme/processors.py @@ -1,18 +1,24 @@ -import json, secrets, hashlib, time, logging -from oneme_tcp.models import * -from oneme_tcp.proto import Proto -from oneme_tcp.config import OnemeConfig +import json +import secrets +import hashlib +import time +import logging +from oneme.models import * +from common.proto_tcp import MobileProto +from common.proto_web import WebProto +from common.opcodes import Opcodes +from oneme.config import OnemeConfig from common.tools import Tools from common.config import ServerConfig from common.static import Static from common.sms import send_sms_code class Processors: - def __init__(self, db_pool=None, clients={}, send_event=None, telegram_bot=None): - self.proto = Proto() + def __init__(self, db_pool=None, clients={}, send_event=None, telegram_bot=None, type="socket"): self.tools = Tools() self.config = ServerConfig() self.static = Static() + self.opcodes = Opcodes() self.server_config = OnemeConfig().SERVER_CONFIG self.error_types = self.static.ErrorTypes() self.chat_types = self.static.ChatTypes() @@ -23,6 +29,11 @@ class Processors: self.telegram_bot = telegram_bot self.logger = logging.getLogger(__name__) + if type == "socket": + self.proto = MobileProto() + elif type == "web": + self.proto = WebProto() + async def _send(self, writer, packet): try: writer.write(packet) @@ -44,14 +55,14 @@ class Processors: await self._send(writer, packet) - async def process_hello(self, payload, seq, writer): + async def session_init(self, payload, seq, writer): """Обработчик приветствия""" # Валидируем данные пакета try: HelloPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.SESSION_INIT, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.SESSION_INIT, self.error_types.INVALID_PAYLOAD, writer) return None, None # Получаем данные из пакета @@ -76,50 +87,50 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.SESSION_INIT, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.SESSION_INIT, payload=payload ) # Отправляем await self._send(writer, packet) return deviceType, deviceName - async def process_ping(self, payload, seq, writer): + async def ping(self, payload, seq, writer): """Обработчик пинга""" # Валидируем данные пакета try: PingPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.PING, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.PING, self.error_types.INVALID_PAYLOAD, writer) return # Собираем пакет response = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.PING, payload=None + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.PING, payload=None ) # Отправляем await self._send(writer, response) - async def process_telemetry(self, payload, seq, writer): + async def log(self, payload, seq, writer): """Обработчик телеметрии""" # TODO: можно было бы реализовать валидацию телеметрии, но сейчас это не особо важно # Собираем пакет response = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.LOG, payload=None + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOG, payload=None ) # Отправляем await self._send(writer, response) - async def process_request_code(self, payload, seq, writer): + async def auth_request(self, payload, seq, writer): """Обработчик запроса кода""" try: RequestCodePayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.AUTH_REQUEST, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH_REQUEST, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем телефон из пакета @@ -185,20 +196,20 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.AUTH_REQUEST, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH_REQUEST, payload=payload ) # Отправляем await self._send(writer, packet) self.logger.debug(f"Код для {phone}: {code} (существующий={user_exists})") - async def process_verify_code(self, payload, seq, writer, deviceType, deviceName): + async def auth(self, payload, seq, writer, deviceType, deviceName): """Обработчик проверки кода""" try: VerifyCodePayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.AUTH, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -225,12 +236,12 @@ class Processors: # Если токен просрочен, или его нет - отправляем ошибку if stored_token is None: - await self._send_error(seq, self.proto.AUTH, self.error_types.CODE_EXPIRED, writer) + await self._send_error(seq, self.opcodes.AUTH, self.error_types.CODE_EXPIRED, writer) return # Проверяем код if stored_token.get("code_hash") != hashed_code: - await self._send_error(seq, self.proto.AUTH, self.error_types.INVALID_CODE, writer) + await self._send_error(seq, self.opcodes.AUTH, self.error_types.INVALID_CODE, writer) return # Если это новый пользователь - переводим токен в state='verified' @@ -241,7 +252,7 @@ class Processors: ("verified", hashed_token,) ) packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.AUTH, + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH, payload={ "tokenAttrs": { "REGISTER": { @@ -299,20 +310,20 @@ class Processors: # Создаем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.AUTH, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH, payload=payload ) # Отправляем await self._send(writer, packet) - async def process_auth_confirm(self, payload, seq, writer, deviceType, deviceName): + async def auth_confirm(self, payload, seq, writer, deviceType, deviceName): """Обработчик подтверждения регистрации нового пользователя""" # Валидируем данные пакета try: AuthConfirmRegisterPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.AUTH_CONFIRM, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -338,7 +349,7 @@ class Processors: # Если токен не найден или просрочен - отправляем ошибку if stored_token is None: - await self._send_error(seq, self.proto.AUTH_CONFIRM, self.error_types.CODE_EXPIRED, writer) + await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.CODE_EXPIRED, writer) return phone = stored_token.get("phone") @@ -346,7 +357,7 @@ class Processors: # Проверяем что пользователь с таким телефоном ещё не существует await cursor.execute("SELECT id FROM users WHERE phone = %s", (phone,)) if await cursor.fetchone(): - await self._send_error(seq, self.proto.AUTH_CONFIRM, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_PAYLOAD, writer) return now_ms = int(time.time() * 1000) @@ -421,21 +432,21 @@ class Processors: # Создаем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.AUTH_CONFIRM, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH_CONFIRM, payload=payload ) # Отправляем await self._send(writer, packet) self.logger.info(f"Новый пользователь зарегистрирован: phone={phone} id={user_id} name={first_name} {last_name}") - async def process_login(self, payload, seq, writer): + async def 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) + await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_PAYLOAD, writer) return # Получаем данные из пакета @@ -452,7 +463,7 @@ class Processors: # Если токен не найден, отправляем ошибку if token_data is None: - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_TOKEN, writer) + await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_TOKEN, writer) return # Ищем аккаунт пользователя в бд @@ -509,14 +520,14 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.LOGIN, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGIN, payload=payload ) # Отправляем await self._send(writer, packet) return int(user.get("phone")), int(user.get("id")), hashed_token - async def process_logout(self, seq, writer, hashedToken): + async def logout(self, seq, writer, hashedToken): """Обработчик завершения сессии""" # Удаляем токен из бд async with self.db_pool.acquire() as conn: @@ -525,20 +536,20 @@ class Processors: # Создаем пакет response = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.LOGOUT, payload=None + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGOUT, payload=None ) # Отправляем await self._send(writer, response) - async def process_get_assets(self, payload, seq, writer): + async def assets_update(self, payload, seq, writer): """Обработчик запроса ассетов клиента на сервере""" # Валидируем данные пакета try: AssetsPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.ASSETS_UPDATE, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.ASSETS_UPDATE, self.error_types.INVALID_PAYLOAD, writer) return # TODO: сейчас это заглушка, а попозже нужно сделать полноценную реализацию @@ -551,20 +562,20 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.ASSETS_UPDATE, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.ASSETS_UPDATE, payload=payload ) # Отправляем await self._send(writer, packet) - async def process_get_call_history(self, payload, seq, writer): + async def video_chat_history(self, payload, seq, writer): """Обработчик получения истории звонков""" # Валидируем данные пакета try: GetCallHistoryPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.VIDEO_CHAT_HISTORY, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.VIDEO_CHAT_HISTORY, self.error_types.INVALID_PAYLOAD, writer) return # TODO: сейчас это заглушка, а попозже нужно сделать полноценную реализацию @@ -579,20 +590,20 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.VIDEO_CHAT_HISTORY, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.VIDEO_CHAT_HISTORY, payload=payload ) # Отправляем await self._send(writer, packet) - async def process_send_message(self, payload, seq, writer, senderId, db_pool): + async def msg_send(self, payload, seq, writer, senderId, db_pool): """Функция отправки сообщения""" # Валидируем данные пакета try: SendMessagePayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.MSG_SEND, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.MSG_SEND, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -629,7 +640,7 @@ class Processors: # Если нет такого чата - выбрасываем ошибку if not chat: - await self._send_error(seq, self.proto.MSG_SEND, self.error_types.CHAT_NOT_FOUND, writer) + await self._send_error(seq, self.opcodes.MSG_SEND, self.error_types.CHAT_NOT_FOUND, writer) return # Список участников @@ -637,7 +648,7 @@ class Processors: # Проверяем, является ли отправитель участником чата if int(senderId) not in participants: - await self._send_error(seq, self.proto.MSG_SEND, self.error_types.CHAT_NOT_ACCESS, writer) + await self._send_error(seq, self.opcodes.MSG_SEND, self.error_types.CHAT_NOT_ACCESS, writer) return # Добавляем сообщение в историю @@ -687,20 +698,20 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.MSG_SEND, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.MSG_SEND, payload=payload ) # Отправляем await self._send(writer, packet) - async def process_get_folders(self, payload, seq, writer, senderPhone): + async def folders_get(self, payload, seq, writer, senderPhone): """Синхронизация папок с сервером""" # Валидируем данные пакета try: SyncFoldersPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.FOLDERS_GET, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.FOLDERS_GET, self.error_types.INVALID_PAYLOAD, writer) return # Ищем папки в бд @@ -720,13 +731,13 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.FOLDERS_GET, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.FOLDERS_GET, payload=payload ) # Отправляем await self._send(writer, packet) - async def process_get_sessions(self, payload, seq, writer, senderPhone, hashedToken): + async def sessions_info(self, payload, seq, writer, senderPhone, hashedToken): """Получение активных сессий на аккаунте""" # Готовый список сессий sessions = [] @@ -756,20 +767,20 @@ class Processors: # Создаем пакет response = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.SESSIONS_INFO, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.SESSIONS_INFO, payload=payload ) # Отправляем await self._send(writer, response) - async def process_search_users(self, payload, seq, writer): + async def contact_info(self, payload, seq, writer): """Поиск пользователей по ID""" # Валидируем данные пакета try: SearchUsersPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.CONTACT_INFO, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.CONTACT_INFO, self.error_types.INVALID_PAYLOAD, writer) return # Итоговый список пользователей @@ -818,20 +829,20 @@ class Processors: # Создаем пакет response = self.proto.pack_packet( - seq=seq, opcode=self.proto.CONTACT_INFO, payload=payload + seq=seq, opcode=self.opcodes.CONTACT_INFO, payload=payload ) # Отправляем await self._send(writer, response) - async def process_search_chats(self, payload, seq, writer, senderId): + async def chat_info(self, payload, seq, writer, senderId): """Поиск чатов по ID""" # Валидируем данные пакета try: SearchChatsPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.CHAT_INFO, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.CHAT_INFO, self.error_types.INVALID_PAYLOAD, writer) return # Итоговый список чатов @@ -894,20 +905,20 @@ class Processors: # Собираем пакет response = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.CHAT_INFO, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CHAT_INFO, payload=payload ) # Отправляем await self._send(writer, response) - async def process_search_by_phone(self, payload, seq, writer, senderId): + async def contact_info_by_phone(self, payload, seq, writer, senderId): """Поиск по номеру телефона""" # Валидируем данные пакета try: SearchByPhonePayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.CONTACT_INFO_BY_PHONE, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.CONTACT_INFO_BY_PHONE, self.error_types.INVALID_PAYLOAD, writer) return # Ищем пользователя в бд @@ -918,7 +929,7 @@ class Processors: # Если пользователь не найден, отправляем ошибку if not user: - await self._send_error(seq, self.proto.CONTACT_INFO_BY_PHONE, self.error_types.USER_NOT_FOUND, writer) + await self._send_error(seq, self.opcodes.CONTACT_INFO_BY_PHONE, self.error_types.USER_NOT_FOUND, writer) return # ID чата @@ -964,34 +975,34 @@ class Processors: # Создаем пакет response = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.CONTACT_INFO_BY_PHONE, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CONTACT_INFO_BY_PHONE, payload=payload ) # Отправляем await self._send(writer, response) - async def process_get_call_token(self, payload, seq, writer): + async def ok_token(self, payload, seq, writer): """Получение токена для звонка""" # Валидируем данные пакета try: GetCallTokenPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.OK_TOKEN, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.OK_TOKEN, self.error_types.INVALID_PAYLOAD, writer) return # TODO: когда-то взяться за звонки - await self._send_error(seq, self.proto.OK_TOKEN, self.error_types.NOT_IMPLEMENTED, writer) + await self._send_error(seq, self.opcodes.OK_TOKEN, self.error_types.NOT_IMPLEMENTED, writer) - async def process_typing(self, payload, seq, writer, senderId): + async def msg_typing(self, payload, seq, writer, senderId): """Обработчик события печатания""" # Валидируем данные пакета try: TypingPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.MSG_TYPING, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.MSG_TYPING, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -1006,7 +1017,7 @@ class Processors: # Если чат не найден, отправляем ошибку if not chat: - await self._send_error(seq, self.proto.MSG_TYPING, self.error_types.CHAT_NOT_FOUND, writer) + await self._send_error(seq, self.opcodes.MSG_TYPING, self.error_types.CHAT_NOT_FOUND, writer) return # Участники чата @@ -1014,7 +1025,7 @@ class Processors: # Проверяем, является ли отправитель участником чата if int(senderId) not in participants: - await self._send_error(seq, self.proto.MSG_TYPING, self.error_types.CHAT_NOT_ACCESS, writer) + await self._send_error(seq, self.opcodes.MSG_TYPING, self.error_types.CHAT_NOT_ACCESS, writer) return # Рассылаем событие участникам чата @@ -1033,20 +1044,20 @@ class Processors: # Создаем пакет packet = self.proto.pack_packet( - seq=seq, opcode=self.proto.MSG_TYPING + seq=seq, opcode=self.opcodes.MSG_TYPING ) # Отправляем пакет await self._send(writer, packet) - async def process_complain_reasons_get(self, payload, seq, writer): + async def complain_reasons_get(self, payload, seq, writer): """Обработчик получения причин жалоб""" # Валидируем данные пакета try: ComplainReasonsGetPayloadModel.model_validate(payload) except pydantic.ValidationError as error: self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.COMPLAIN_REASONS_GET, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.COMPLAIN_REASONS_GET, self.error_types.INVALID_PAYLOAD, writer) return # Собираем данные пакета @@ -1057,20 +1068,20 @@ class Processors: # Создаем пакет packet = self.proto.pack_packet( - seq=seq, opcode=self.proto.COMPLAIN_REASONS_GET, payload=payload + seq=seq, opcode=self.opcodes.COMPLAIN_REASONS_GET, payload=payload ) # Отправляем пакет await self._send(writer, packet) - async def process_chat_history(self, payload, seq, writer, senderId): + 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.proto.CHAT_HISTORY, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.CHAT_HISTORY, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -1097,13 +1108,13 @@ class Processors: # Выбрасываем ошибку, если чата нет if not chat: - await self._send_error(seq, self.proto.CHAT_HISTORY, self.error_types.CHAT_NOT_FOUND, writer) + await self._send_error(seq, self.opcodes.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) + await self._send_error(seq, self.opcodes.CHAT_HISTORY, self.error_types.CHAT_NOT_ACCESS, writer) return # Если запрошены сообщения @@ -1155,18 +1166,18 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.CHAT_HISTORY, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.CHAT_HISTORY, payload=payload ) # Отправялем await self._send(writer, packet) - async def process_update_profile(self, payload, seq, writer, userId, userPhone): + async def profile(self, payload, seq, writer, userId, userPhone): # Валидируем входные данные try: UpdateProfilePayloadModel.model_validate(payload) except Exception as e: - await self._send_error(seq, self.proto.PROFILE, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.PROFILE, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем поля из пакета (каждое может быть None) @@ -1226,7 +1237,7 @@ class Processors: # Отправляем ответ на запрос (CMD_OK) packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.PROFILE, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.PROFILE, payload=payload ) await self._send(writer, packet) diff --git a/src/oneme_tcp/server.py b/src/oneme/socket.py similarity index 68% rename from src/oneme_tcp/server.py rename to src/oneme/socket.py index c5481ec..d1aa8e6 100644 --- a/src/oneme_tcp/server.py +++ b/src/oneme/socket.py @@ -1,8 +1,9 @@ import asyncio, logging, traceback -from oneme_tcp.proto import Proto -from oneme_tcp.processors import Processors +from common.proto_tcp import MobileProto +from oneme.processors import Processors from common.rate_limiter import RateLimiter from common.tools import Tools +from common.opcodes import Opcodes class OnemeMobileServer: def __init__(self, host="0.0.0.0", port=443, ssl_context=None, db_pool=None, clients={}, send_event=None, telegram_bot=None): @@ -14,9 +15,10 @@ class OnemeMobileServer: self.db_pool = db_pool self.clients = clients - self.proto = Proto() + self.proto = MobileProto() self.auth_required = Tools().auth_required self.processors = Processors(db_pool=db_pool, clients=clients, send_event=send_event, telegram_bot=telegram_bot) + self.opcodes = Opcodes() # rate limiter anti ddos brute force protection self.auth_rate_limiter = RateLimiter(max_attempts=5, window_seconds=60) @@ -71,90 +73,90 @@ class OnemeMobileServer: payload = packet.get("payload") match opcode: - case self.proto.SESSION_INIT: - deviceType, deviceName = await self.processors.process_hello(payload, seq, writer) - case self.proto.AUTH_REQUEST: + case self.opcodes.SESSION_INIT: + deviceType, deviceName = await self.processors.session_init(payload, seq, writer) + case self.opcodes.AUTH_REQUEST: if not self.auth_rate_limiter.is_allowed(address[0]): - await self.processors._send_error(seq, self.proto.AUTH_REQUEST, self.processors.error_types.RATE_LIMITED, writer) + await self.processors._send_error(seq, self.opcodes.AUTH_REQUEST, self.processors.error_types.RATE_LIMITED, writer) else: - await self.processors.process_request_code(payload, seq, writer) - case self.proto.AUTH: + await self.processors.auth_request(payload, seq, writer) + case self.opcodes.AUTH: if not self.auth_rate_limiter.is_allowed(address[0]): - await self.processors._send_error(seq, self.proto.AUTH, self.processors.error_types.RATE_LIMITED, writer) + await self.processors._send_error(seq, self.opcodes.AUTH, self.processors.error_types.RATE_LIMITED, writer) else: - await self.processors.process_verify_code(payload, seq, writer, deviceType, deviceName) - case self.proto.AUTH_CONFIRM: + await self.processors.auth(payload, seq, writer, deviceType, deviceName) + case self.opcodes.AUTH_CONFIRM: if not self.auth_rate_limiter.is_allowed(address[0]): - await self.processors._send_error(seq, self.proto.AUTH_CONFIRM, self.processors.error_types.RATE_LIMITED, writer) + await self.processors._send_error(seq, self.opcodes.AUTH_CONFIRM, self.processors.error_types.RATE_LIMITED, writer) elif payload and payload.get("tokenType") == "REGISTER": - await self.processors.process_auth_confirm(payload, seq, writer, deviceType, deviceName) + await self.processors.auth_confirm(payload, seq, writer, deviceType, deviceName) else: self.logger.warning(f"AUTH_CONFIRM с неизвестным tokenType: {payload}") - case self.proto.LOGIN: + case self.opcodes.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) + await self.processors._send_error(seq, self.opcodes.LOGIN, self.processors.error_types.RATE_LIMITED, writer) else: - userPhone, userId, hashedToken = await self.processors.process_login(payload, seq, writer) + userPhone, userId, hashedToken = await self.processors.login(payload, seq, writer) if userPhone: await self._finish_auth(writer, address, userPhone, userId) - case self.proto.LOGOUT: - await self.processors.process_logout(seq, writer, hashedToken=hashedToken) + case self.opcodes.LOGOUT: + await self.processors.logout(seq, writer, hashedToken=hashedToken) break - case self.proto.PING: - await self.processors.process_ping(payload, seq, writer) - case self.proto.LOG: - await self.processors.process_telemetry(payload, seq, writer) - case self.proto.ASSETS_UPDATE: + case self.opcodes.PING: + await self.processors.ping(payload, seq, writer) + case self.opcodes.LOG: + await self.processors.log(payload, seq, writer) + case self.opcodes.ASSETS_UPDATE: await self.auth_required( - userPhone, self.processors.process_get_assets, payload, seq, writer + userPhone, self.processors.assets_update, payload, seq, writer ) - case self.proto.VIDEO_CHAT_HISTORY: + case self.opcodes.VIDEO_CHAT_HISTORY: await self.auth_required( - userPhone, self.processors.process_get_call_history, payload, seq, writer + userPhone, self.processors.video_chat_history, payload, seq, writer ) - case self.proto.MSG_SEND: + case self.opcodes.MSG_SEND: await self.auth_required( - userPhone, self.processors.process_send_message, payload, seq, writer, userId, self.db_pool + userPhone, self.processors.msg_send, payload, seq, writer, userId, self.db_pool ) - case self.proto.FOLDERS_GET: + case self.opcodes.FOLDERS_GET: await self.auth_required( - userPhone, self.processors.process_get_folders, payload, seq, writer, userPhone + userPhone, self.processors.folders_get, payload, seq, writer, userPhone ) - case self.proto.SESSIONS_INFO: + case self.opcodes.SESSIONS_INFO: await self.auth_required( - userPhone, self.processors.process_get_sessions, payload, seq, writer, userPhone, hashedToken + userPhone, self.processors.sessions_info, payload, seq, writer, userPhone, hashedToken ) - case self.proto.CHAT_INFO: + case self.opcodes.CHAT_INFO: await self.auth_required( - userPhone, self.processors.process_search_chats, payload, seq, writer, userId + userPhone, self.processors.chat_info, payload, seq, writer, userId ) - case self.proto.CHAT_HISTORY: + case self.opcodes.CHAT_HISTORY: await self.auth_required( - userPhone, self.processors.process_chat_history, payload, seq, writer, userId + userPhone, self.processors.chat_history, payload, seq, writer, userId ) - case self.proto.CONTACT_INFO_BY_PHONE: + case self.opcodes.CONTACT_INFO_BY_PHONE: await self.auth_required( - userPhone, self.processors.process_search_by_phone, payload, seq, writer, userId + userPhone, self.processors.contact_info_by_phone, payload, seq, writer, userId ) - case self.proto.OK_TOKEN: + case self.opcodes.OK_TOKEN: await self.auth_required( - userPhone, self.processors.process_get_call_token, payload, seq, writer + userPhone, self.processors.ok_token, payload, seq, writer ) - case self.proto.MSG_TYPING: + case self.opcodes.MSG_TYPING: await self.auth_required( - userPhone, self.processors.process_typing, payload, seq, writer, userId + userPhone, self.processors.msg_typing, payload, seq, writer, userId ) - case self.proto.CONTACT_INFO: + case self.opcodes.CONTACT_INFO: await self.auth_required( - userPhone, self.processors.process_search_users, payload, seq, writer + userPhone, self.processors.contact_info, payload, seq, writer ) - case self.proto.COMPLAIN_REASONS_GET: + case self.opcodes.COMPLAIN_REASONS_GET: await self.auth_required( - userPhone, self.processors.process_complain_reasons_get, payload, seq, writer + userPhone, self.processors.complain_reasons_get, payload, seq, writer ) - case self.proto.PROFILE: - await self.processors.process_update_profile( + case self.opcodes.PROFILE: + await self.processors.profile( payload, seq, writer, userId=userId, userPhone=userPhone ) case _: diff --git a/src/tamtam_tcp/__init__.py b/src/tamtam/__init__.py similarity index 100% rename from src/tamtam_tcp/__init__.py rename to src/tamtam/__init__.py diff --git a/src/tamtam_tcp/controller.py b/src/tamtam/controller.py similarity index 89% rename from src/tamtam_tcp/controller.py rename to src/tamtam/controller.py index 7555f6b..c21e479 100644 --- a/src/tamtam_tcp/controller.py +++ b/src/tamtam/controller.py @@ -1,5 +1,5 @@ import asyncio -from tamtam_tcp.server import TTMobileServer +from tamtam.socket import TTMobileServer from classes.controllerbase import ControllerBase from common.config import ServerConfig diff --git a/src/tamtam_tcp/models.py b/src/tamtam/models.py similarity index 100% rename from src/tamtam_tcp/models.py rename to src/tamtam/models.py diff --git a/src/tamtam_tcp/processors.py b/src/tamtam/processors.py similarity index 86% rename from src/tamtam_tcp/processors.py rename to src/tamtam/processors.py index ad0a655..e7c0978 100644 --- a/src/tamtam_tcp/processors.py +++ b/src/tamtam/processors.py @@ -6,21 +6,30 @@ import json import re from common.static import Static from common.tools import Tools -from tamtam_tcp.proto import Proto -from tamtam_tcp.models import * + +from common.proto_tcp import MobileProto +from common.proto_web import WebProto +from common.opcodes import Opcodes + +from tamtam.models import * class Processors: - def __init__(self, db_pool=None, clients=None, send_event=None): + def __init__(self, db_pool=None, clients=None, send_event=None, type="socket"): if clients is None: clients = {} # Более правильная логика self.static = Static() - self.proto = Proto() self.tools = Tools() + self.opcodes = Opcodes() self.error_types = self.static.ErrorTypes() self.db_pool = db_pool self.logger = logging.getLogger(__name__) + if type == "socket": + self.proto = MobileProto() + elif type == "web": + self.proto = WebProto() + async def _send(self, writer, packet): try: writer.write(packet) @@ -42,13 +51,13 @@ class Processors: await self._send(writer, packet) - async def process_hello(self, payload, seq, writer): + async def session_init(self, payload, seq, writer): """Обработчик приветствия""" # Валидируем данные пакета try: HelloPayloadModel.model_validate(payload) except Exception as e: - await self._send_error(seq, self.proto.HELLO, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.SESSION_INIT, self.error_types.INVALID_PAYLOAD, writer) return None, None # Получаем данные из пакета @@ -67,20 +76,20 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.HELLO, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.SESSION_INIT, payload=payload ) # Отправляем await self._send(writer, packet) return device_type, device_name - async def process_request_code(self, payload, seq, writer): + async def auth_request(self, payload, seq, writer): """Обработчик запроса кода""" # Валидируем данные пакета try: RequestCodePayloadModel.model_validate(payload) except Exception as e: - await self._send_error(seq, self.proto.REQUEST_CODE, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH_REQUEST, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем телефон из пакета @@ -120,21 +129,20 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.REQUEST_CODE, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH_REQUEST, payload=payload ) # Отправляем await self._send(writer, packet) - self.logger.debug(f"Код для {phone}: {code}") - async def process_verify_code(self, payload, seq, writer): + async def auth(self, payload, seq, writer): """Обработчик проверки кода""" # Валидируем данные пакета try: VerifyCodePayloadModel.model_validate(payload) except Exception as e: - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -154,12 +162,12 @@ class Processors: stored_token = await cursor.fetchone() if not stored_token: - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.CODE_EXPIRED, writer) + await self._send_error(seq, self.opcodes.AUTH, self.error_types.CODE_EXPIRED, writer) return # Проверяем код if stored_token.get("code_hash") != hashed_code: - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_CODE, writer) + await self._send_error(seq, self.opcodes.AUTH, self.error_types.INVALID_CODE, writer) return # Ищем аккаунт @@ -200,18 +208,18 @@ class Processors: } packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.VERIFY_CODE, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH, payload=payload ) await self._send(writer, packet) - async def process_final_auth(self, payload, seq, writer, deviceType, deviceName): + async def auth_confirm(self, payload, seq, writer, deviceType, deviceName): """Обработчик финальной аутентификации""" # Валидируем данные пакета try: FinalAuthPayloadModel.model_validate(payload) except Exception as e: - await self._send_error(seq, self.proto.FINAL_AUTH, self.error_types.INVALID_PAYLOAD, writer) + await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_PAYLOAD, writer) return # Извлекаем данные из пакета @@ -236,12 +244,12 @@ class Processors: stored_token = await cursor.fetchone() if stored_token is None: - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_TOKEN, writer) + await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_TOKEN, writer) return # Если авторизация только началась - отдаем ошибку if stored_token.get("state") == "started": - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_TOKEN, writer) + await self._send_error(seq, self.opcodes.AUTH_CONFIRM, self.error_types.INVALID_TOKEN, writer) return # Ищем аккаунт @@ -284,20 +292,20 @@ class Processors: # Создаем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.FINAL_AUTH, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.AUTH_CONFIRM, payload=payload ) # Отправялем await self._send(writer, packet) - async def process_login(self, payload, seq, writer): + async def 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) + await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_PAYLOAD, writer) return # Получаем данные из пакета @@ -314,7 +322,7 @@ class Processors: # Если токен не найден, отправляем ошибку if token_data is None: - await self._send_error(seq, self.proto.VERIFY_CODE, self.error_types.INVALID_TOKEN, writer) + await self._send_error(seq, self.opcodes.LOGIN, self.error_types.INVALID_TOKEN, writer) return # Ищем аккаунт пользователя в бд @@ -384,7 +392,7 @@ class Processors: # Собираем пакет packet = self.proto.pack_packet( - cmd=self.proto.CMD_OK, seq=seq, opcode=self.proto.LOGIN, payload=payload + cmd=self.proto.CMD_OK, seq=seq, opcode=self.opcodes.LOGIN, payload=payload ) # Отправляем diff --git a/src/tamtam_tcp/proto.py b/src/tamtam/proto.py similarity index 100% rename from src/tamtam_tcp/proto.py rename to src/tamtam/proto.py diff --git a/src/tamtam_tcp/server.py b/src/tamtam/socket.py similarity index 84% rename from src/tamtam_tcp/server.py rename to src/tamtam/socket.py index 1c2d171..ed1820c 100644 --- a/src/tamtam_tcp/server.py +++ b/src/tamtam/socket.py @@ -1,7 +1,8 @@ import asyncio, logging, traceback -from tamtam_tcp.proto import Proto -from tamtam_tcp.processors import Processors +from common.proto_tcp import MobileProto +from tamtam.processors import Processors from common.rate_limiter import RateLimiter +from common.opcodes import Opcodes class TTMobileServer: def __init__(self, host="0.0.0.0", port=443, ssl_context=None, db_pool=None, clients={}, send_event=None): @@ -13,7 +14,9 @@ class TTMobileServer: self.db_pool = db_pool self.clients = clients - self.proto = Proto() + self.opcodes = Opcodes() + + self.proto = MobileProto() self.processors = Processors(db_pool=db_pool, clients=clients, send_event=send_event) # rate limiter @@ -69,28 +72,28 @@ class TTMobileServer: payload = packet.get("payload") match opcode: - case self.proto.HELLO: - deviceType, deviceName = await self.processors.process_hello(payload, seq, writer) - case self.proto.REQUEST_CODE: + case self.opcodes.SESSION_INIT: + deviceType, deviceName = await self.processors.session_init(payload, seq, writer) + case self.opcodes.AUTH_REQUEST: if not self.auth_rate_limiter.is_allowed(address[0]): - await self.processors._send_error(seq, self.proto.REQUEST_CODE, self.processors.error_types.RATE_LIMITED, writer) + await self.processors._send_error(seq, self.opcodes.AUTH_REQUEST, self.processors.error_types.RATE_LIMITED, writer) else: - await self.processors.process_request_code(payload, seq, writer) - case self.proto.VERIFY_CODE: + await self.processors.auth_request(payload, seq, writer) + case self.opcodes.AUTH: if not self.auth_rate_limiter.is_allowed(address[0]): - await self.processors._send_error(seq, self.proto.VERIFY_CODE, self.processors.error_types.RATE_LIMITED, writer) + await self.processors._send_error(seq, self.opcodes.AUTH, self.processors.error_types.RATE_LIMITED, writer) else: - await self.processors.process_verify_code(payload, seq, writer) - case self.proto.FINAL_AUTH: + await self.processors.auth(payload, seq, writer) + case self.opcodes.AUTH_CONFIRM: if not self.auth_rate_limiter.is_allowed(address[0]): - await self.processors._send_error(seq, self.proto.FINAL_AUTH, self.processors.error_types.RATE_LIMITED, writer) + await self.processors._send_error(seq, self.opcodes.AUTH_CONFIRM, self.processors.error_types.RATE_LIMITED, writer) else: - await self.processors.process_final_auth(payload, seq, writer, deviceType, deviceName) - case self.proto.LOGIN: + await self.processors.auth_confirm(payload, seq, writer, deviceType, deviceName) + case self.opcodes.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) + await self.processors._send_error(seq, self.opcodes.LOGIN, self.processors.error_types.RATE_LIMITED, writer) else: - userPhone, userId, hashedToken = await self.processors.process_login(payload, seq, writer) + userPhone, userId, hashedToken = await self.processors.login(payload, seq, writer) if userPhone: await self._finish_auth(writer, address, userPhone, userId) diff --git a/src/tamtam_ws/__init__.py b/src/tamtam_ws/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/tamtam_ws/controller.py b/src/tamtam_ws/controller.py deleted file mode 100644 index 6df93ca..0000000 --- a/src/tamtam_ws/controller.py +++ /dev/null @@ -1,22 +0,0 @@ -import asyncio -from classes.controllerbase import ControllerBase -from common.config import ServerConfig -from tamtam_ws.server import TTWSServer - -class TTWSController(ControllerBase): - def __init__(self): - self.config = ServerConfig() - - def launch(self, api): - async def _start_all(): - await asyncio.gather( - TTWSServer( - host=self.config.host, - port=self.config.tamtam_ws_port, - db_pool=api['db'], - clients=api['clients'], - send_event=api['event'] - ).start() - ) - - return _start_all() \ No newline at end of file diff --git a/src/tamtam_ws/models.py b/src/tamtam_ws/models.py deleted file mode 100644 index 0f98df5..0000000 --- a/src/tamtam_ws/models.py +++ /dev/null @@ -1,27 +0,0 @@ -import pydantic - -class MessageModel(pydantic.BaseModel): - ver: int - cmd: int - seq: int - opcode: int - payload: dict = None - -class UserAgentModel(pydantic.BaseModel): - deviceType: str - appVersion: str - osVersion: str - locale: str - deviceLocale: str - deviceName: str - screen: str - headerUserAgent: str - timezone: str - -class HelloPayloadModel(pydantic.BaseModel): - userAgent: UserAgentModel - deviceId: str - -class RequestCodePayloadModel(pydantic.BaseModel): - phone: str - requestType: str diff --git a/src/tamtam_ws/processors.py b/src/tamtam_ws/processors.py deleted file mode 100644 index 66cba41..0000000 --- a/src/tamtam_ws/processors.py +++ /dev/null @@ -1,80 +0,0 @@ -import hashlib, secrets, random, time, logging, json -from common.static import Static -from common.tools import Tools -from tamtam_ws.proto import Proto -from tamtam_ws.models import * - -class Processors: - def __init__(self, db_pool=None, clients={}, send_event=None): - self.static = Static() - self.tools = Tools() - self.proto = Proto() - self.error_types = self.static.ErrorTypes() - self.db_pool = db_pool - self.logger = logging.getLogger(__name__) - - async def _send(self, writer, packet): - """Отправка пакета""" - try: - await writer.send(packet) - except Exception as error: - self.logger.error(f"Ошибка при отправке пакета - {error}") - - async def _send_error(self, seq, opcode, type, writer): - payload = self.static.ERROR_TYPES.get(type, { - "localizedMessage": "Неизвестная ошибка", - "error": "unknown.error", - "message": "Unknown error", - "title": "Неизвестная ошибка" - }) - - packet = self.proto.pack_packet( - seq=seq, opcode=opcode, payload=payload - ) - - await self._send(writer, packet) - - async def process_hello(self, payload, seq, writer): - """Обработчик приветствия""" - # Валидируем данные пакета - try: - HelloPayloadModel.model_validate(payload) - except pydantic.ValidationError as error: - self.logger.error(f"Возникли ошибки при валидации пакета: {error}") - await self._send_error(seq, self.proto.SESSION_INIT, self.error_types.INVALID_PAYLOAD, writer) - return None, None - - # Получаем данные из пакета - deviceType = payload.get("userAgent").get("deviceType") - deviceName = payload.get("userAgent").get("deviceName") - - # Собираем данные ответа - payload = { - "proxy": "", - "logs-enabled": False, - "proxy-domains": [], - "location": "RU" - } - - # Создаем пакет - packet = self.proto.pack_packet(seq=seq, opcode=self.proto.SESSION_INIT, payload=payload) - - # Отправляем - await self._send(writer, packet) - return deviceType, deviceName - - async def process_ping(self, payload, seq, writer): - """Обработчик пинга""" - # Создаем пакет - packet = self.proto.pack_packet(seq=seq, opcode=self.proto.PING) - - # Отправляем - await self._send(writer, packet) - - async def process_telemetry(self, payload, seq, writer): - """Обработчик телеметрии""" - # Создаем пакет - packet = self.proto.pack_packet(seq=seq, opcode=self.proto.LOG) - - # Отправляем - await self._send(writer, packet) diff --git a/src/tamtam_ws/proto.py b/src/tamtam_ws/proto.py deleted file mode 100644 index dee801d..0000000 --- a/src/tamtam_ws/proto.py +++ /dev/null @@ -1,165 +0,0 @@ -import json - -class Proto: - def pack_packet(self, ver=10, cmd=1, seq=0, opcode=1, payload=None): - # а разве не надо в жсон запаковывать ещё - # о всё - return json.dumps({ - "ver": ver, - "cmd": cmd, - "seq": seq, - "opcode": opcode, - "payload": payload - }) - - MAX_PACKET_SIZE = 65536 # 64 KB, заглушка, нужно узнать реальные лимиты и поменять, хотя кто будет это делать... - - def unpack_packet(self, packet): - # try catch чтобы не сыпалось всё при неверных пакетах - if isinstance(packet, (str, bytes)) and len(packet) > self.MAX_PACKET_SIZE: - return {} - - try: - parsed_packet = json.loads(packet) - except (json.JSONDecodeError, TypeError, ValueError): - return {} - - return parsed_packet - # мне кажется долго вручную всё писать - # а как еще - # ну вставить сюда целиком и потом через multiline cursor удалить лишнее - # ну ты удалишь тогда. я на тачпаде - # ладно щас другим способом удалю - # всё нахуй - # TAMTAM SOURCE LEAK 2026 - # так ну че делать будем - # так ну - - # 19 опкод сделан? - # нет сэр пошли библиотеку тамы смотреть - # мб найдем че. она без обфускации - # а ты ее видишь? - # пошли - - ### Констаты протокола - CMD_OK = 1 - CMD_NOF = 2 - CMD_ERR = 3 - PROTO_VER = 10 - - ### Команды - PING = 1 - LOG = 5 - SESSION_INIT = 6 - PROFILE = 16 - AUTH_REQUEST = 17 - AUTH_CHECK_SCENARIO = 263 - AUTH = 18 - LOGIN = 19 - LOGOUT = 20 - SYNC = 21 - CONFIG = 22 - AUTH_CONFIRM = 23 - ASSETS_GET = 26 - ASSETS_UPDATE = 27 - ASSETS_GET_BY_IDS = 28 - ASSETS_ADD = 29 - ASSETS_REMOVE = 259 - ASSETS_MOVE = 260 - ASSETS_LIST_MODIFY = 261 - CONTACT_INFO = 32 - CONTACT_UPDATE = 34 - CONTACT_PRESENCE = 35 - CONTACT_LIST = 36 - CONTACT_PHOTOS = 39 - CONTACT_CREATE = 41 - REMOVE_CONTACT_PHOTO = 43 - OWN_CONTACT_SEARCH = 44 - CHAT_INFO = 48 - CHAT_HISTORY = 49 - CHAT_MARK = 50 - CHAT_MEDIA = 51 - CHAT_DELETE = 52 - CHAT_LIST = 53 - CHAT_CLEAR = 54 - CHAT_UPDATE = 55 - CHAT_CHECK_LINK = 56 - CHAT_JOIN = 57 - CHAT_LEAVE = 58 - CHAT_MEMBERS = 59 - CHAT_CLOSE = 61 - CHAT_BOT_COMMANDS = 144 - CHAT_SUBSCRIBE = 75 - PUBLIC_SEARCH = 60 - CHAT_CREATE = 63 - MSG_SEND = 64 - MSG_TYPING = 65 - MSG_DELETE = 66 - MSG_EDIT = 67 - CHAT_SEARCH = 68 - MSG_SHARE_PREVIEW = 70 - MSG_SEARCH_TOUCH = 72 - MSG_SEARCH = 73 - MSG_GET_STAT = 74 - MSG_GET = 71 - VIDEO_CHAT_START = 76 - VIDEO_CHAT_JOIN = 102 - VIDEO_CHAT_COMMAND = 78 - VIDEO_CHAT_MEMBERS = 195 - CHAT_MEMBERS_UPDATE = 77 - PHOTO_UPLOAD = 80 - STICKER_UPLOAD = 81 - VIDEO_UPLOAD = 82 - VIDEO_PLAY = 83 - MUSIC_PLAY = 84 - MUSIC_PLAY30 = 85 - FILE_UPLOAD = 87 - FILE_DOWNLOAD = 88 - CHAT_PIN_SET_VISIBILITY = 86 - LINK_INFO = 89 - MESSAGE_LINK = 90 - MSG_CONSTRUCT = 94 - SESSIONS_INFO = 96 - SESSIONS_CLOSE = 97 - PHONE_BIND_REQUEST = 98 - PHONE_BIND_CONFIRM = 99 - UNBIND_OK_PROFILE = 100 - CHAT_COMPLAIN = 117 - MSG_SEND_CALLBACK = 118 - SUSPEND_BOT = 119 - MSG_REACT = 178 - MSG_CANCEL_REACTION = 179 - MSG_GET_REACTIONS = 180 - MSG_GET_DETAILED_REACTIONS = 181 - LOCATION_STOP = 124 - LOCATION_SEND = 125 - LOCATION_REQUEST = 126 - NOTIF_MESSAGE = 128 - NOTIF_TYPING = 129 - NOTIF_MARK = 130 - NOTIF_CONTACT = 131 - NOTIF_PRESENCE = 132 - NOTIF_CONFIG = 134 - NOTIF_CHAT = 135 - NOTIF_ATTACH = 136 - NOTIF_VIDEO_CHAT_START = 137 - NOTIF_VIDEO_CHAT_COMMAND = 138 - NOTIF_CALLBACK_ANSWER = 143 - NOTIF_MSG_CONSTRUCT = 146 - NOTIF_LOCATION = 147 - NOTIF_LOCATION_REQUEST = 148 - NOTIF_ASSETS_UPDATE = 150 - NOTIF_MSG_REACTIONS_CHANGED = 155 - NOTIF_MSG_YOU_REACTED = 156 - NOTIF_DRAFT = 152 - NOTIF_DRAFT_DISCARD = 153 - NOTIF_MSG_DELAYED = 154 - AUTH_CALL_INFO = 256 - CONTACT_INFO_EXTERNAL = 45 - DRAFT_SAVE = 176 - DRAFT_DISCARD = 177 - STICKER_CREATE = 193 - STICKER_SUGGEST = 194 - CHAT_SEARCH_COUNT_MSG = 197 - CHAT_SEARCH_COMMON_PARTICIPANTS = 198 - GET_USER_SCORE = 201 \ No newline at end of file diff --git a/src/tamtam_ws/server.py b/src/tamtam_ws/server.py deleted file mode 100644 index a711e5e..0000000 --- a/src/tamtam_ws/server.py +++ /dev/null @@ -1,66 +0,0 @@ -import asyncio, logging, json -from websockets.asyncio.server import serve -from tamtam_ws.models import * -from pydantic import ValidationError -from tamtam_ws.proto import Proto -from tamtam_ws.processors import Processors - -class TTWSServer: - def __init__(self, host, port, db_pool=None, clients={}, send_event=None, origins=None): - self.host = host - self.port = port - self.proto = Proto() - self.processors = Processors(db_pool=db_pool, clients=clients, send_event=send_event) - self.logger = logging.getLogger(__name__) - self.origins = origins - - async def handle_client(self, websocket): - deviceType = None - deviceName = None - - async for message in websocket: - # Распаковываем пакет - packet = self.proto.unpack_packet(message) - - if not packet: - self.logger.warning("Невалидный пакет от ws клиента") - continue - - # Валидируем структуру пакета - try: - MessageModel.model_validate(packet) - except ValidationError as e: - self.logger.warning(f"Ошибка валидации пакета: {e}") - continue - - # Извлекаем данные из пакета - seq = packet['seq'] - opcode = packet['opcode'] - payload = packet['payload'] - - match opcode: - case self.proto.SESSION_INIT: - # ПРИВЕТ АНДРЕЙ МАЛАХОВ - # не не удаляй этот коммент. пусть останется на релизе аххахаха - deviceType, deviceName = await self.processors.process_hello(payload, seq, websocket) - case self.proto.PING: - await self.processors.process_ping(payload, seq, websocket) - case self.proto.LOG: - # телеметрия аааа слежка цру фсб фбр - # УДАЛЯЕМ MYTRACKER ИЗ TAMTAM ТАМ ВИРУС - # майтрекер отправляет все ваши сообщения на сервер барака обамы. немедленно удаляем!!! - await self.processors.process_telemetry(payload, seq, websocket) - - # лан я пойду. пока - # а ок - - async def start(self): - self.logger.info(f"Вебсокет запущен на порту {self.port}") - - async with serve( - self.handle_client, self.host, self.port, - max_size=65536, - open_timeout=10, - close_timeout=10, - ): - await asyncio.Future() \ No newline at end of file