surfaces/docs/research/telegram-chat-alternatives.md
Mikhail Putilovskij 67499daa61 feat: extend platform mock + add research docs
platform/interface.py:
- Add Attachment, MessageChunk, AgentEvent types
- Add stream_message() to PlatformClient Protocol (door open for streaming)
- Add WebhookReceiver Protocol

platform/mock.py:
- Add attachment_mode config (url/binary/s3)
- Implement stream_message() — single chunk, ready for real streaming
- Add register_webhook_receiver() + simulate_agent_event() for testing

docs/research/:
- telegram-forum-topics.md — aiogram 3.x Forum Topics API, FSM patterns, UX analysis
- fsm-patterns.md — FSM storage options, StateData best practices
- matrix-spaces.md — matrix-nio Space API, room ordering, invite flow
- matrix-events.md — reactions, threads, typing, sync loop pitfalls
- telegram-chat-alternatives.md — 7 alternatives for multi-chat UX, virtual chats in DM recommended
2026-03-30 14:04:34 +03:00

10 KiB
Raw Blame History

Research: Альтернативные варианты организации чатов в Telegram

Сравнительная таблица

Вариант Friction для юзера Техническая сложность Ограничения Статус
1. Виртуальные чаты в DM Нет Низкая Нет РЕКОМЕНДУЕТСЯ
2. Threads / Reply Threads Высокая Средняя Требует группу, бот не создаёт темы
3. Multiple Bot Instances Высокая Средняя Нужно добавлять каждый
4. Inline Mode Нет Низкая Stateless, не для диалогов
5. Бот создаёт приватные группы Очень высокая Очень высокая Bot API не создаёт группы
6. Telegram Web App (TWA) Нет Высокая Нужен web-сервер ⚠️ Phase 2
7. Forum Topics (исходный) Высокая Средняя Пользователь создаёт группу вручную ⚠️ Оставить как опцию

Вариант 1: Виртуальные чаты в DM (РЕКОМЕНДУЕТСЯ)

Как выглядит для пользователя

/start
→ [ Новый чат]  [📋 Мои чаты]  [⚙️ Настройки]

Нажимает "Новый чат"
→ "✅ Чат #1 создан! Начните писать..."

User: "Расскажи про Python"
→ "[Чат #1] Python — это язык программирования..."

Нажимает "Мои чаты"
→ 1⃣ Чат #1 (Python)
  2⃣ Чат #2 (Математика)  — 2 часа назад
  3⃣ Исследование рынка   — вчера

Нажимает на "Чат #2"
→ "Вы в Чате #2. Последние сообщения: ..."
→ Продолжает разговор

Технические детали (aiogram 3.x)

FSM состояния

from aiogram.fsm.state import State, StatesGroup

class UserStates(StatesGroup):
    main_menu = State()
    in_chat = State()
    selecting_chat = State()

Хранение активного чата в StateData

# При создании чата
await state.set_state(UserStates.in_chat)
await state.update_data(active_chat_id=chat_id)

# При получении сообщения
data = await state.get_data()
chat_id = data["active_chat_id"]

Список чатов с инлайн-кнопками

from aiogram.types import InlineKeyboardMarkup, InlineKeyboardButton

@router.message(Command("list"))
async def cmd_list(message: Message, state: FSMContext):
    chats = db.get_user_chats(message.from_user.id)

    buttons = [
        [InlineKeyboardButton(
            text=f"📄 {chat.title or f'Чат #{chat.id[:6]}'}",
            callback_data=f"select:{chat.id}"
        )]
        for chat in chats
    ]
    await message.answer("📋 Ваши чаты:", reply_markup=InlineKeyboardMarkup(inline_keyboard=buttons))

@router.callback_query(F.data.startswith("select:"))
async def switch_chat(callback: CallbackQuery, state: FSMContext):
    chat_id = callback.data.split(":")[1]
    await state.update_data(active_chat_id=chat_id)
    await state.set_state(UserStates.in_chat)
    await callback.message.edit_text(f"✅ Переключился в чат. Пишите...")

Схема БД

CREATE TABLE chats (
    chat_id TEXT PRIMARY KEY,     -- UUID
    user_id INTEGER NOT NULL,
    title TEXT,
    created_at TIMESTAMP,
    updated_at TIMESTAMP
);

CREATE TABLE messages (
    id INTEGER PRIMARY KEY,
    chat_id TEXT NOT NULL,
    user_id INTEGER NOT NULL,
    direction TEXT,               -- 'user' / 'bot'
    content TEXT NOT NULL,
    created_at TIMESTAMP,
    FOREIGN KEY(chat_id) REFERENCES chats(chat_id)
);

Ограничения

  • История только в нашей БД, не в Telegram нативно
  • Нет шаринга чата с другими пользователями

Примеры в реальных ботах

  • ChatGPT Telegram боты — именно этот паттерн, /new → новый разговор, /history → список
  • StudyGPT — каждый урок = отдельный "чат" с контекстом
  • Notion Bot — список "проектов" с переключением

Оценка

Нулевое трение — ничего настраивать не нужно Работает в DM — приватно Стандартный паттерн — пользователи знакомы Полный контроль над UX Масштабируется без ограничений История только в нашей БД (но это нормально)


Вариант 2: Threads / Reply Topics — ОТКЛОНЕНО

Суть

Telegram поддерживает message_thread_id в апдейтах — можно читать из какого треда пришло сообщение и отвечать в него. Но:

  • Бот не может создавать треды — Bot API этого не умеет
  • Работает только в Group/Supergroup, не в DM
  • Пользователь должен создавать каждый тред вручную
  • Не решает проблему friction — делает хуже
# Читать можно
thread_id = message.message_thread_id  # int или None

# Отвечать можно
await message.answer("ответ", message_thread_id=thread_id)

# Создать тред НЕЛЬЗЯ — метода нет в Bot API

Вариант 3: Deep Linking — ОТКЛОНЕНО

Каждый чат = отдельная ссылка вида https://t.me/bot?start=chatid_xyz. Пользователь переходит по ссылке, бот предвыбирает чат.

Проблема: пользователь не знает что такие ссылки существуют, нет discovery, не масштабируется.

@router.message(Command("start"))
async def cmd_start(message: Message):
    args = message.text.split()
    if len(args) > 1:
        chat_id = args[1]  # из deep link
        # предвыбрать чат

Вариант 4: Inline Mode — ОТКЛОНЕНО

Inline mode (@botname query) — fundamentally stateless. Один запрос → несколько результатов → пользователь выбирает. Нет истории, нет контекста. Категорически не подходит для многооборотного диалога с AI.


Вариант 5: Бот создаёт приватные группы — ОТКЛОНЕНО

Telegram Bot API не умеет создавать группы. Единственный workaround — заранее создать 100+ пустых групп и назначать их пользователям. 1000 пользователей = 1000 лишних групп. Абсурд.


Вариант 6: Telegram Web App (TWA) — Phase 2

Суть

Кнопка в боте открывает мини-приложение (веб). Там красивый интерфейс со списком чатов, историей, поиском.

Технические детали

# В боте
from aiogram.types import WebAppInfo, InlineKeyboardButton

web_app_btn = InlineKeyboardButton(
    text="📱 Открыть приложение",
    web_app=WebAppInfo(url="https://lambda.example.com/app")
)
// В TWA (JavaScript)
const tg = window.Telegram.WebApp;
const user_id = tg.initDataUnsafe.user.id;

// Загрузить список чатов
const chats = await fetch('/api/chats', {
    headers: { 'X-Telegram-Init-Data': tg.initData }
}).then(r => r.json());

Оценка

Красивый интерфейс (история, поиск, форматирование) Мобильный-friendly ⚠️ Нужен HTTPS web-сервер ⚠️ Сложнее разрабатывать ⚠️ Усложняет деплой

Вывод: хороший вариант для v2, не для MVP.


Финальные рекомендации

Сейчас (MVP)

Вариант 1 — Виртуальные чаты в DM. Реализуется за 2-3 дня, нулевое friction, стандартный паттерн.

Phase 2

Вариант 6 — TWA как дополнительный UI поверх той же логики. Бэкенд не меняется — просто добавляется web-интерфейс.

Опционально (для пользователей которые хотят)

Forum Topics — оставить как opt-in возможность. Пользователь сам создаёт группу, добавляет бота — получает "продвинутый режим" с нативными Telegram темами.


Что это означает для архитектуры

ChatManager в core/chat.py должен работать одинаково для обоих режимов — и виртуальных чатов в DM, и Forum Topics. Разница только в адаптере:

Telegram DM (virtual) Telegram Forum Matrix
chat_id UUID message_thread_id room_id
Создание В БД create_forum_topic room_create
Отправка send_message(chat_id=user_id) + tag send_message(thread_id=...) room_send
core/ Не знает разницы Не знает разницы Не знает разницы