Commit graph

9 commits

Author SHA1 Message Date
Folist
c71bf7021c Фикс имени диалога (2 участника = диалог) + диагностика события удаления
- resolveChatName: чат с 2 участниками (я+один) и пустым title теперь
  считается диалогом → имя собеседника вместо "CHAT <id>" (диалоги от
  createDialog приходят с type:"CHAT").
- Временные [dbg]-логи: CHAT_UPDATE (не-реакции) и необработанные опкоды —
  чтобы поймать, чем реально приходит удаление чата (CLOSED не сработал).
2026-08-17 17:52:40 +03:00
Folist
d7c695c8be Пульт управления (inline-кнопки) + поиск контакта + начать чат + reply
Фаза A–E плана «фичи 1,2,3»:
- src/bridge/panel.ts: закреплённый в General пульт с деревом кнопок
  (Найти контакт / Управление чатами / Веб-панель / Система), навигация
  редактированием сообщения, автовосстановление закрепа, /panel.
- Найти контакт (фича 1): По номеру → CONTACT_INFO_BY_PHONE (0x002E,
  not.found→нет), По нику → PUBLIC_SEARCH (0x003C, глобально); карточка с
  «Начать чат», гейт по options ONEME.
- Начать чат: createDialog (MSG_SEND control-аттач chatType DIALOG + userIds,
  отрицательный chatId) → ensureTopicForMaxChat с именем контакта.
- Пауза MAX (фича 3): 10м/1ч/1сут/навсегда, авто-возобновление по таймеру,
  без ложного алярма (disconnect не эмитит disconnected).
- Веб-панель: ссылка для входа (ifconfig.me) + API-ключ; листья ban/unban/
  version/help переиспользуют существующую логику.
- Reply (фича 2): TG→MAX нативно (link {type:REPLY, messageId, chatId} через
  MessageLinkStore), MAX→TG префиксом (link.type==='REPLY', цитата из
  link.message) — нативный reply_parameters MAX→TG отложен.
- Опкоды CONTACT_SEARCH/CONTACT_INFO_BY_PHONE/PUBLIC_SEARCH; методы клиента
  searchContactByPhone/publicSearch/searchContactByName/createDialog;
  sendMessage получил опциональный reply-link. /panel в help и меню команд.
2026-08-17 14:29:03 +03:00
Folist
505833a1fc Реакции больше не пропадают: getReactions читал неверный формат ответа
MSG_GET_REACTIONS реально отвечает {messagesReactions:{"<id>":{counters:[]}}}
(подтверждено кадром 2026-08-15), а getReactions парсил reactionInfo/reactions
— ни одна ветка не совпадала, функция ВСЕГДА возвращала []. Из-за этого
pollReactionRemovals раз в 60с считал, что реакция в MAX снята, и стирал её в
Telegram — отсюда «реакции со временем пропадают». Добавлен разбор реального
формата (по messageId, с фолбэком на единственную запись), старые формы
оставлены как фолбэки.
2026-08-15 14:57:37 +03:00
Folist
7341c226d1 Нормализация variation selector у реакций TG↔MAX (сердечко и др.)
MAX отвергал реакцию ❤ (error.message.like.unknown.like), а 👍 принимал:
Telegram Bot API отдаёт часть реакций в text-форме без U+FE0F (❤ = U+2764),
а MAX хранит emoji-форму (❤️ = U+2764 U+FE0F). Это не разные наборы, а разное
представление одного эмодзи — чинится симметричной нормализацией selector'а,
а не таблицей соответствия.

Новый модуль max/reactions.ts: toMaxReaction (TG→MAX, добавляет FE0F нужным
базовым символам, включая внутри ZWJ-последовательностей вроде ❤‍🔥/🤷‍♂️) и
toTelegramReaction (MAX→TG, убирает FE0F — setMessageReaction хочет bare-форму).
Применено в addReaction, в релее реакций MAX→TG и в re-affirm зонда. Список
проблемных code points — из набора реакций Telegram (Emoji_Presentation=No).
Покрыто тестами (round-trip TG→MAX→TG).
2026-08-15 12:11:00 +03:00
Folist
6142463914 maxFetch: undici-fetch вместо глобального — чинит все загрузки MAX-CDN
Agent из npm-пакета undici v8 нельзя передавать во встроенный fetch Node 22:
у внутреннего undici другой интерфейс диспетчера, и каждый запрос падал с
"invalid onRequestStart method". Ломались обе стороны вложений: MAX→TG
приходили заглушки вместо файлов, TG→MAX выгрузка падала целиком (сообщение
не создавалось — отсюда и «/delete не находит связку»). Текст ходил, потому
что идёт по TCP-сокету мимо fetch. Поймано на тестовом сервере 2026-08-14,
воспроизведено и подтверждено локально: глобальный fetch + npm-Agent падает
с той же ошибкой, undici-fetch + тот же Agent работает.

FormData в upload.ts теперь тоже импортируется из undici — класс в рантайме
тот же, но типы npm-пакета номинально несовместимы с копией undici-types
из @types/node.
2026-08-14 22:06:50 +03:00
Folist
f6ee386a61 Аудит безопасности: HTTPS-панель, скоуп доверия CA, сериализация MAX-запросов
- redactSecrets: маскируются password/trackId/phone и любые строки от 512
  символов — пароль MAX больше не попадает в лог веб-панели открытым текстом
- Веб-панель по умолчанию работает по HTTPS с самоподписанным сертификатом
  (генерируется при первом старте в .data/tls); старые http-ссылки получают
  301-редирект на том же порту (polyglot по первому байту; resume строго в
  process.nextTick — иначе TLS-хендшейк зависает); /apikey даёт https-ссылку;
  PANEL_TLS=off для установки за своим reverse proxy
- Сертификаты Минцифры больше не доверяются всему контейнеру через
  NODE_EXTRA_CA_CERTS — src/max/ca.ts скоупит их только на соединения с MAX
  (ca в tls.connect + undici-агент для CDN-загрузок); Telegram и GitHub
  проверяются только по стандартным корням Mozilla
- Запросы одного опкода к MAX сериализуются (request() в client.ts) — два
  конкурентных MSG_SEND больше не перепутают messageId-связки редактирования
  и удаления
- Починена гонка первого чтения в ChatMapStore: конкурентные обращения
  форкали кэш, и часть upsert'ов молча терялась на диске
- Graceful shutdown по SIGTERM/SIGINT; в панели — реконнект WebSocket и
  периодическое обновление метрик; сравнение id в patchCachedChatLastMessage
  переведено на String() (BigInt-чаты не совпадали)
- Удалён мёртвый код: эндпоинты /api/debug/*, getPollVoters,
  createTelegramBot, цепочка historySynced, getChats(marker)
- React/Tailwind/lucide перенесены в devDependencies — рантайм-образ легче;
  vitest 4, npm audit: 0 уязвимостей; chmod 600 для .env в setup.sh/update.sh
- Новые тесты: redactSecrets, сторы (эвикшн/нормализация/конкурентность),
  сериализация запросов — всего 41
2026-08-14 21:13:26 +03:00
Folist
8afc421b0a Support password-protected MAX accounts (2FA on top of SMS)
Full flow reverse-engineered and confirmed live: CHECK_CODE responds
with a passwordChallenge (trackId + optional hint) instead of a login
token when the account has a password set. A new opcode, CHECK_PASSWORD
(0x0073), exchanges {trackId, password} for the same 663-char login
token CHECK_CODE would have returned directly — from there it's an
ordinary LOGIN.

verifyCode() now returns a discriminated union (ok / password_required)
instead of throwing either way, so callers can actually ask for a
password instead of just failing. Wired through everywhere MAX auth
happens: the web panel gets a new password step (with hint), setup.sh's
console flow prompts for it too, and the dev auth-cli script handles it
via the same file-polling pattern it already used for the SMS code.

A wrong password doesn't burn the trackId (confirmed live) — both the
web UI and setup.sh let you retry it freely without a fresh SMS. It's
only consumed on success, matching MAX's own behavior.
2026-08-14 11:58:47 +03:00
Folist
b42e0cb1df Temporary: log raw CHECK_CODE failure payload for diagnosis 2026-08-14 11:15:08 +03:00
Folist
50c304fa8c Initial public release: MAX <-> Telegram bridge
Two-way message/media bridge between MAX and Telegram forum topics, with
group management, polls, calls, forwarding, and admin commands (/help,
/reboot, /kill, /donate) from Telegram. Includes one-command install
(install.sh) and interactive first-run setup (setup.sh) for a bare Ubuntu
server.
2026-08-13 16:00:10 +03:00