Legan Studio
Все статьи
~ 6 мин чтения

Topics в Telegram-форумах: как использовать в ботах

Forum topics в супергруппах: создание тем через createForumTopic, маршрутизация сообщений, модерация и сценарии для саппорта и сообществ.

  • Telegram
  • forum
  • topics
  • сообщества

Forum topics — это режим супергруппы, который превращает её в форум с разделами (темами). Каждая тема — отдельная ветка с собственным заголовком, иконкой и набором сообщений. Для бота это удобный инструмент маршрутизации: один чат для всей команды поддержки, но каждая заявка живёт в своей теме.

Разберём, как технически работают topics в Bot API, как создавать темы программно, как маршрутизировать сообщения и какие подводные ловят разработчики при миграции старых чатов.

Что такое forum topics

Forum mode включается у супергруппы через настройки админа: Settings → Topics → On. После этого каждое сообщение в чате привязано к теме (message_thread_id), и UI клиента показывает список тем как вкладки или отдельные ветки.

Технически в Bot API тема — это объект с полями:

{
  "message_thread_id": 12345,
  "name": "Заявка #842 — Иван Петров",
  "icon_color": 7322096,
  "icon_custom_emoji_id": "5312536423851630001"
}

message_thread_id — это ID первого сообщения темы, и одновременно идентификатор всей ветки. Все методы отправки (sendMessage, sendPhoto, sendInvoice) принимают опциональный параметр message_thread_id, чтобы положить сообщение в нужную тему.

Когда forum mode имеет смысл

Хорошие сценарии:

  • Саппорт: каждая заявка → отдельная тема, видно историю в одном месте.
  • Сообщество разработчиков: темы по технологиям (Python, Go, Frontend).
  • Внутренние команды: одна тема на проект или клиента.
  • Школы: тема на каждый класс или предмет.

Плохие сценарии:

  • Канал с пассивной аудиторией — у форумов нет broadcast-режима.
  • Маркетинговый чат — пользователи теряются в темах, лучше обычная группа.
  • Менее 30 активных участников — overhead не оправдан.

Создание темы из бота: createForumTopic

Бот, добавленный админом с правом manage_topics, может создавать темы:

from aiogram import Bot
from aiogram.types import ForumTopic

async def create_ticket_topic(bot: Bot, chat_id: int, ticket_id: int, user_name: str) -> int:
    topic: ForumTopic = await bot.create_forum_topic(
        chat_id=chat_id,
        name=f"Заявка #{ticket_id} — {user_name}",
        icon_color=0x6FB9F0,  # голубой
        icon_custom_emoji_id=None,
    )
    return topic.message_thread_id

Топик-цвет выбирается из 6 фиксированных значений: 0x6FB9F0, 0xFFD67E, 0xCB86DB, 0x8EEE98, 0xFF93B2, 0xFB6F5F. Для кастомного эмодзи нужен icon_custom_emoji_id из ваших премиум-стикеров.

Маршрутизация: входящее сообщение → нужная тема

Сценарий саппорта: юзер пишет в DM → бот создаёт тему в общем рабочем чате → команда отвечает в теме → бот пересылает ответ обратно в DM.

from aiogram import Router, F
from aiogram.types import Message

router = Router()

# 1. Юзер пишет в DM боту
@router.message(F.chat.type == "private")
async def from_user(message: Message, bot: Bot):
    ticket = await tickets.get_or_create(user_id=message.from_user.id)
    if not ticket.thread_id:
        thread_id = await create_ticket_topic(
            bot, SUPPORT_CHAT_ID, ticket.id, message.from_user.full_name
        )
        ticket.thread_id = thread_id
        await tickets.save(ticket)
    await bot.copy_message(
        chat_id=SUPPORT_CHAT_ID,
        message_thread_id=ticket.thread_id,
        from_chat_id=message.chat.id,
        message_id=message.message_id,
    )

# 2. Оператор отвечает в теме
@router.message(F.chat.id == SUPPORT_CHAT_ID, F.message_thread_id)
async def from_operator(message: Message, bot: Bot):
    ticket = await tickets.find_by_thread(message.message_thread_id)
    if not ticket:
        return
    await bot.copy_message(
        chat_id=ticket.user_id,
        from_chat_id=message.chat.id,
        message_id=message.message_id,
    )

copy_message сохраняет содержимое (текст, медиа, кнопки), но не показывает «Forwarded from» — пользователь видит чистое сообщение.

Закрытие и переоткрытие темы

Когда заявка решена, тему можно закрыть, чтобы новые сообщения от юзера автоматически попадали в архив:

await bot.close_forum_topic(SUPPORT_CHAT_ID, message_thread_id=thread_id)
# При новой жалобе:
await bot.reopen_forum_topic(SUPPORT_CHAT_ID, message_thread_id=thread_id)

Закрытая тема перестаёт принимать сообщения от обычных юзеров (только от админов и бота). Это удобный способ сегментировать активные/архивные тикеты.

МетодЗачем
createForumTopicновая тема
editForumTopicпереименовать, сменить иконку
closeForumTopicзакрыть для записи
reopenForumTopicснова разрешить запись
deleteForumTopicудалить со всеми сообщениями
unpinAllForumTopicMessagesснять все закрепы
getForumTopicIconStickersполучить набор премиум-эмодзи

События темы в апдейтах

Бот получает события forum_topic_created, forum_topic_edited, forum_topic_closed, forum_topic_reopened как сервисные сообщения:

@router.message(F.forum_topic_created)
async def on_topic_created(message: Message):
    name = message.forum_topic_created.name
    await db.log_topic(message.chat.id, message.message_thread_id, name)

Это полезно для аудита: видеть, кто и когда создал какую тему вручную.

Лимиты и подводные

  • Максимум 1 тема в секунду на чат через createForumTopic. При массовом создании нужна очередь.
  • Лимит 1024 тем на чат не задокументирован, но на практике после ~2000 тем UI клиента начинает тормозить.
  • General topic (главная тема) удалить нельзя, можно только скрыть через hideGeneralForumTopic.
  • Конвертация старого чата в форум необратима — все сообщения уезжают в General topic.
  • Боты-юзеры (не клиент-сессии) не могут создавать темы без явного права manage_topics у админа.

Поиск по темам

Telegram не отдаёт через Bot API список тем — только через клиент-сессию (Telethon/Pyrogram, метод GetForumTopics). В простом сценарии бот сам ведёт реестр в БД:

class Topic(Base):
    __tablename__ = "topics"
    chat_id: int
    thread_id: int
    name: str
    user_id: int
    status: str  # open|closed|deleted
    created_at: datetime

Поиск по name/user_id/status выполняется через ваш индекс, не через Telegram.

Топ-5 ошибок при работе с темами

  1. Забывают передать message_thread_id в sendMessage → сообщение уходит в General.
  2. Не различают message_thread_id и message_id (это разные сущности с одинаковым числовым значением для первого сообщения темы).
  3. Создают новую тему на каждое сообщение юзера вместо одной на тикет.
  4. Не закрывают темы после решения — чат разрастается до 1000+ открытых.
  5. Удаляют тему, забывая очистить thread_id в БД — следующая заявка от того же юзера уходит в null.

Итого

Forum topics — мощный инструмент для саппорта, сообществ и внутренних команд: один чат, понятная структура, чистая маршрутизация. Для бота это значит грамотный createForumTopic, передачу message_thread_id во все методы отправки и хранение карты «юзер ↔ тема» в собственной БД. Сделайте это правильно — и команда из 5 операторов закроет в день в 3–4 раза больше тикетов, чем в плоской группе.

Частые вопросы

В чём разница между forum topic и обычным reply-thread?

Reply-thread — это виртуальная цепочка ответов, она не создаёт отдельную сущность в чате и не маршрутизируется через Bot API. Forum topic — это именованная вкладка с собственным message_thread_id, которую видят все участники как отдельный раздел. Для маршрутизации заявок и тематических обсуждений подходит только forum topic.

Можно ли превратить обычный чат в форум?

Да, через настройки супергруппы → Topics → On. Все существующие сообщения уезжают в General topic, и обратной конвертации нет. Перед миграцией убедитесь, что участникам объяснили новую структуру: форум сильно меняет привычный UX чата.

Как раздавать права на конкретные темы?

Bot API этого не умеет. Права (post, edit, delete) настраиваются на уровне всего чата через promoteChatMember, и распространяются на все темы. Если нужно ограничить доступ к темам — закройте их через closeForumTopic и пускайте писать только админов.

Сколько тем можно держать открытыми?

Жёсткого лимита нет, но клиенты Telegram комфортно работают до ~500 тем. После этого UI начинает тормозить, поиск по темам становится медленным. В саппорте лучше закрывать решённые тикеты сразу — это и UX-чище, и быстрее.

Можно ли отправить пост сразу во все темы?

Нет, broadcast по темам в Bot API нет. Нужно итерироваться по списку thread_id из вашей БД и слать каждое сообщение отдельно с пейсингом 1 мсг/сек на чат. Для рассылок такого типа форум — плохое решение, лучше канал.

Как боту узнать список всех тем чата?

Через Bot API — никак. Бот видит тему только когда в ней появляется новое сообщение или он сам её создал. Для полного списка нужна client-сессия (Telethon, метод GetForumTopics) от имени админа. Альтернатива — вести собственный реестр тем в БД с момента подключения бота.

Что делать с General topic?

General — главная тема, удалить нельзя. Можно скрыть через hideGeneralForumTopic, тогда у участников она пропадёт из списка, но сервисные сообщения (вход/выход, смена названия) всё равно будут уходить туда. В сценариях саппорта обычно General используется как «приёмная» для нераспределённых заявок.