W.I.P.: OpenMAX 0.1.0 (#28)

* Различные фиксы (#14)

* Исключаем только ошибку валидации

* Небольшие правки вебсокета тамтама

* Теперь номера брутить чутка сложнее

* Авторизация теперь проверяется для некоторых команд

* Теперь проверяется orign у вебсокета тамтама

* Дополнил пример env

* Починил немного сокет тамтама

* Теперь все должно работать исправно

* Документация: описал про SESSION_INIT

* Update deviceId description in tamtam_ws.md

* feat: 16 опкод для обновления профиля

* feat: 23 опкод для регистрации, смс шлюз, докер

* SQLite: Ошибка

И вообще не юзайте sqlite пожалуйста, он баганный

* Серверный конфиг снова сломался

* Починил изменение профиля

* История сообщений (вроде как даже работает)

* Дополнил немного 48 опкод, и починил отдачу истории в избранном (избранное все равно не работает)

* Доделал авторизацию в ТамТаме

* Швырнул архитектуру, чтобы позже объединить контроллеры веба и сокета в одно, а также разделить процессоры

* Удалил tamtam.proto, поскольку нигде больше не используется

* Поделил процессоры в таме

* Коды стран

* Генерируем айди, за вместо того, чтобы писать его попорядку

* Вынес список участников чата в отдельную таблицу

* Теперь мы не должны отправлять 128 опкод самому себе. вроде

* ТамТам: реализация 32 опкода. Надеюсь, будет работать

* ТамТам: обработчик 5 и 1 опкода

* ТамТам: починил 32 опкод и добавил серверный конфиг. Избранное больше не добавляется в список чатов
MAX: починил 32 опкод

* TT: oops

* MAX: заглушка в 158 опкоде

* Common: поправил 1 прикол

* TamTam: websocket transport for web version

* TamTam && MAX: история (в мохе она вроде теперь получше работает)

* Правка контроллеров небольшая

* MAX: Разделил процессоры

* TG Bot: переписал на aiogram

* Common: фикс года

* Начальная реализация транспорта ws для max web и прочие улучшения

* Common: добавил сжатие lz4

* Исправление конфликтов

* TT: фикс вебсокета

* Fix OpenMAX mobile compatibility and Telegram auth fallback (#30)

* Fix OpenMAX mobile compatibility and Telegram auth fallback

* Common: Убрал скобку в конфиге

---------

Co-authored-by: Alexey Polyakov <starwear3000@mail.ru>

* MAX: заглушка для баннеров, правка пакета со списком жалоб, отдача контактов и прочие улучшения

* oops

* MAX: Рабочие баннеры

* MAX: обновление настроек приватности

* MAX: пуши через firebase (особо не тестил, вроде работает)

* MAX: Рефактор папок

* MAX: фикс версий < 25.8.0

* MAX: отдача черного списка

* Теперь ID пользователей идут по порядку, как и раньше

* MAX: создание папок

* MAX: oops

* MAX: добавление/удаление контактов, статусы

* MAX & TT: теперь в качестве страны локации используется настоящая страна пользователя, а также зафиксировал версии библиотек в зависимостях

* TT: ну вроде шире поддержка, а вообще обратная совместимость с максом клас

* oops

* TG Bot: возможность включения белого списка

* Решение проблемы обработки sigterm

* nothing

* fix

* TG Bot: fix

* TG Bot: fix username

* MAX && TT: общение в таме, и корректировки под веб морду

* MAX: Почистил серверный конфиг

* nothing

* MAX: web fix

* MAX: web fix 2

* oops

* nothing

* MAX: теперь для избранного не сравниваем айди с нулём

* Вынес sqlite в отдельный модуль

* MAX: блокировка и разблокировка контакта

* oops

* TT: тоже самое что в макс

* MAX && TT: обновление контакта

* MAX: добавление контакта по номеру телефона

* MAX: исправление уязвимости с избранными

* Генерируем айди пользователей рандомно (опять, да)

* update sql scheme

* Фикс сборки избранного

* Попытка починить историю (спойлер, нихуя не получилось)

* MAX: история таки заработала — cid/link/reactionInfo обязательны в схеме

Десктопный MAX подключается через TCP (mobile-протокол) и парсит
msgpack по фиксированной схеме. Если в сообщении выпадает любое из
полей — клиент молча обрывает соединение. После 87cfc19 как раз
такие условные `if elements: ...` / `if link: ...` (а link и
reaction_info там всегда были `{}`, то есть falsy) вырезали поля
из ответа CHAT_HISTORY и MSG_SEND, чем и сломали историю.

- src/common/tools.py: новый build_message_dict() — единая сборка
  тела сообщения, где все поля (id, cid, time, type, sender, text,
  attaches, elements, reactionInfo, link) присутствуют ВСЕГДА.
  get_last_message переписан через него.
- src/oneme/processors/history.py: chat_history использует
  build_message_dict вместо ручной логики с условными if-ками.
- src/oneme/processors/messages.py: msg_send.bodyMessage теперь
  отдает cid / reactionInfo / link даже пустыми и приводит id
  к int для mobile, str для web.

Цепная польза: auth.py LOGIN bootstrap (через generate_chats →
get_last_message) и search.py тоже теперь шлют корректную схему.

* MAX: bootstrap-история в LOGIN — клиент перестал думать что всё уже синканулось

В ответе LOGIN сервер слал messages: {} и chatMarker: 0. Десктопный
клиент в этом случае считает, что локальная история уже синхронизирована
со старого запуска, и НЕ отправляет CHAT_HISTORY (49) при открытии чата.
В окне видно только lastMessage из chats[], а вся реальная переписка —
ничерта.

- src/common/tools.py: collect_bootstrap_history(chatIds, ...) —
  собирает карту {chatId: [последние N сообщений]}, в т.ч. избранное
  под клиентским id = senderId ^ senderId.
- src/oneme/processors/auth.py: подсовываем эту карту в
  payload.messages, chatMarker = текущее время вместо 0.

* fix(chat history): фикс

* fix(chat history): фикс

* fix(chat history): фикс

* fix(chat history): фикс

* fix(chat history): фикс

* fix(chat history): фикс

* fix(chat history): фикс

* MAX & TT: теперь полноценный чёрный список

* TT: регистрация через клиент

* Вроде как нормальные заглушки под ассеты

* maybe fixes

---------

Co-authored-by: WowInceptionGood <143893762+WowInceptionGood@users.noreply.github.com>
Co-authored-by: zavolo <me@kirillka.ru>
Co-authored-by: Aleksandr Kosachev <38947781+gohryt@users.noreply.github.com>
This commit is contained in:
Alexey Polyakov
2026-07-08 00:11:48 +03:00
committed by GitHub
parent c53c3cd195
commit fc6ec5eec5
94 changed files with 8955 additions and 2756 deletions
+83 -24
View File
@@ -1,10 +1,18 @@
# Импортирование библиотек
import ssl, logging, asyncio
import asyncio
import logging
import signal
import ssl
import sys
import traceback
from common.config import ServerConfig
from oneme_tcp.controller import OnemeMobileController
from common.push import PushService
from common.sqlite import SQLitePoolCompat
from oneme.controller import OnemeController
from tamtam.controller import TTController
from telegrambot.controller import TelegramBotController
from tamtam_tcp.controller import TTMobileController
from tamtam_ws.controller import TTWSController
# Конфиг сервера
server_config = ServerConfig()
@@ -15,6 +23,7 @@ async def init_db():
if server_config.db_type == "mysql":
import aiomysql
db = await aiomysql.create_pool(
host=server_config.db_host,
port=server_config.db_port,
@@ -22,16 +31,19 @@ async def init_db():
password=server_config.db_password,
db=server_config.db_name,
cursorclass=aiomysql.DictCursor,
autocommit=True
autocommit=True,
)
elif server_config.db_type == "sqlite":
import aiosqlite
raw_db = await aiosqlite.connect(server_config.db_file)
db["acquire"] = raw_db
raw_db = await aiosqlite.connect(server_config.db_file, isolation_level=None)
raw_db.row_factory = aiosqlite.Row
db = SQLitePoolCompat(raw_db)
# Возвращаем
return db
def init_ssl():
"""Создание контекста SSL"""
# Создаем контекст SSL
@@ -45,7 +57,7 @@ def set_logging():
"""Настройка уровня логирования"""
# Настройка уровня логирования
log_level = server_config.log_level
if log_level == "debug":
logging.basicConfig(level=logging.DEBUG)
elif log_level == "info":
@@ -53,40 +65,87 @@ def set_logging():
else:
logging.basicConfig(level=None)
async def main():
"""Запуск сервера"""
async def api_event(target, eventData):
for client in api.get("clients").get(target, {}).get("clients", {}):
await controllers[client["protocol"]].event(target, client, eventData)
set_logging()
db = await init_db()
ssl_context = init_ssl()
clients = {}
push_service = PushService(server_config.firebase_credentials_path)
async def api_event(target, eventData):
target_clients = api.get("clients", {}).get(target, {}).get("clients", [])
for client in target_clients:
await controllers[client["protocol"]].event(target, client, eventData)
# Если у пользователя нет активных подключений
# и это новое сообщение - отсылаем пуш
if not target_clients and eventData.get("eventType") == "new_msg":
message = eventData.get("message", {})
sender_id = message.get("sender")
text = message.get("text", "")
chat_id = eventData.get("chatId", "")
msg_id = message.get("id", 0)
await push_service.send_to_user(
db, target,
sender_id=sender_id,
msg_id=msg_id,
chat_id=chat_id,
text=text,
)
api = {
"db": db,
"ssl": ssl_context,
"clients": clients,
"event": api_event
"event": api_event,
"origins": server_config.origins,
}
controllers = {
"oneme_mobile": OnemeMobileController(),
"tamtam_mobile": TTMobileController(),
"tamtam_ws": TTWSController(),
"telegrambot": TelegramBotController()
"oneme": OnemeController(),
"tamtam": TTController(),
"telegrambot": TelegramBotController(),
}
api["telegram_bot"] = controllers["telegrambot"]
tasks = [
controller.launch(api)
for controller in controllers.values()
]
loop = asyncio.get_running_loop()
running_tasks = []
def _shutdown(sig):
logging.info(f"Получен сигнал {sig}, завершаем все задачи...")
for task in running_tasks:
task.cancel()
if sys.platform != "win32":
for sig in (signal.SIGTERM, signal.SIGINT):
loop.add_signal_handler(sig, _shutdown, sig)
coros = [controller.launch(api) for controller in controllers.values()]
running_tasks.extend(asyncio.create_task(coro) for coro in coros)
# Запускаем контроллеры
await asyncio.gather(*tasks)
try:
await asyncio.gather(*running_tasks)
except asyncio.CancelledError:
logging.info("Все задачи завершены")
except Exception as e:
logging.error(
f"Произошла неизвестная ошибка: {e}"
)
traceback.print_exc()
finally:
if hasattr(db, 'close'):
db.close()
await db.wait_closed()
elif hasattr(db, 'connection') and hasattr(db.connection, 'close'):
await db.connection.close()
if __name__ == "__main__":
asyncio.run(main())
asyncio.run(main())