В этой статье я рассказываю, как настроить уведомления в вашем приложении на Python или в Alertmanager таким образом, чтобы сообщения приходили в определенный Telegram топик.

Некоторые топики в чате команды разработки
Некоторые топики в чате команды разработки

TL;DR

  1. curl-запрос для отправки сообщений в определенный telegram топик:

curl -X POST -H 'Content-Type: application/json' \
  -d '{"reply_to_message_id": "2", "chat_id": "-1001927109642_2", "text": "This is a test message from the alert system. Do not pay attention on it"}' \
  https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage
  1. Ссылка на fork python_telegram_handler репозитория с поддержкой топиков

  2. Ссылка на fork alertmanager репозитория с поддержкой топиков

Задача

Мое имя Вадим Резвов, я начинающий системный администратор.

В мои обязанности входит: мониторинг состояния всех основных сервисов и систем проекта, создание бэкапов, поддержка ключевых компонентов сервера.

В одном из моих текущих рабочих проектов, появились задачи, настроить систему регулярных бэкапов для DB PostgreSQL и настроить мониторинг компонентов системы. 

Команда разработки выдвинула следующие требования:
Сообщения об ошибках при создании бэкапов и сообщения об успешно созданных бэкапах, о сбоях в работе компонентов, должны приходить в определенный telegram топик - “Support”, чтобы не засорять другие топики “General”.

Отправка в топик Telegram curl запросом

Решение задачи я начал с составления curl запроса, который будет отправлять сообщения в определенный telegram топик. 

Этот шаг можно пропустить, curl запрос нужен, чтобы проверить корректность используемых данных: bot token, chat id, message id.

Отдельный топик в группе идентифицируется в Telegram Bot API с помощью id первого сообщение в топике.

Чтобы получить id первого сообщения в топике, в Telegram десктоп клиенте кликните правой кнопкой мыши по первому сообщению в топике, выберите "Copy Message Link".
Вы получите подобную ссылку:
https://t.me/c/-1001234567/1234/1235

Первая часть ссылки - это id чата -1001234567
Вторая часть ссылки - это id первого сообщения в топике: 1234

Извлекаем параметры для curl запроса:

reply_to_message_id: 1234
chat_id: -1001234567890_1234

Для отправки сообщения в топик нам потребуется:

  1. Зарегистрировать бота в BotFather и получить его токен:
    https://core.telegram.org/bots/tutorial#obtain-your-bot-token

  2. Добавить бота в чат как обычного пользователя.

Теперь можно делать curl запрос

# Перед отправкой curl, нужно установить bot token:
export TELEGRAM_BOT_TOKEN=1234567890:aBCd_eFGHjk_etc

# Отправляем curl запрос: 
curl -X POST -H 'Content-Type: application/json' \
  -d '{"reply_to_message_id": "2", "chat_id": "-1001927109642_2", "text": "This is a test message from the alert system. Do not pay attention on it"}' \
  https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage
Должны увидеть такое сообщение в нужном топике
Должны увидеть такое сообщение в нужном топике

Отправка в топик Telegram с помощью python logger

Проект python logger позволяет отправлять уведомления из логгера в Telegram.

Однако в нем нет возможности отправлять сообщения в определенный топик.

Для поддержки отправки в топики, я сделал fork этого репозитория.

и дописал пару строчек кода:
https://github.com/sashgorokhov/python-telegram-handler/compare/master...oktend:python-telegram-handler:master#diff-e5c391ada5f2b1d85ca23fc08080d0d01b7e19ab6cae725398a945aca8eb7309 

Для демонстрации я сделал репозиторий с скриптом для отправки сообщений в топик группы в Telegram:
https://github.com/oktend/python-telegram-topic-notification-example

Ключевой код из примера:

telegram_handler = TelegramHandler(
    level=logging.WARNING,     
    token="1234567890:aBCd_eFGHjk_etc",     
    reply_to_message_id="1234",     
    chat_id="-1001234567890_1234"     
    ) 
telegram_handler.setLevel(logging.WARNING)
log.addHandler(telegram_handler)

Теперь все сообщения из лога уровнем warning и выше будут отправляться в топик чата Telegram.

Я это использую и для отправки сообщений об успешно созданном бэкапе:

log.warning(f"Backup prod data script was successfully ended. timestamp: {now_str}")

Отправка в топик Telegram с помощью alertmanager

При настройке мониторинга мне потребовалось настроить отправку уведомлений в топик, но в стандартном alertmanager не был поддержан параметр reply_to_message_id, который нужен для отправки в топик.

Я решил это созданием fork alertmanager.

и отправил pull request в основной репозиторий alertmanager

Пока PR не принят, я использую сборку из своего репозитория:
https://github.com/oktend/alertmanager/releases/tag/v0.26.0-tg-topic 

Для демонстрации я сделал репозиторий.

Здесь вы найдете docker образ с модифицированным alertmanager и инструкцию по запуску.

Здесь ключевой момент это настройка отправки уведомлений в топик в чате Telegram:

receivers:
- name: 'telegram'
  telegram_configs:
  - bot_token: 1234567890:aBCd_eFGHjk_etc
    api_url: https://api.telegram.org
    chat_id: -1001234567890_1234
    reply_to_message_id: 1234

В своем проекте я использовал Ansible для установки alertmanager, подобный этому:
https://github.com/MiteshSharma/PrometheusAlertManagerWithAnsible

Но для того, чтобы alertmanager умел отправлять сообщения в топик, нужно поменять ссылку на сборку в файле:
https://github.com/MiteshSharma/PrometheusAlertManagerWithAnsible/blob/master/roles/alertmanager/tasks/main.yml#L17 


таким образом:

- name: Download alertmanager  
  unarchive:    
    # src: "https://github.com/prometheus/alertmanager/releases/download/v{{ version }}/alertmanager-{{ version }}.linux-amd64.tar.gz"    
    src: "https://github.com/oktend/alertmanager/releases/download/v0.26.0-tg-topic/alertmanager-0.26.0.linux-amd64.tar.gz"
    dest: /tmp/    
    remote_src: yes

Заключение

Надеюсь, моя статья будет полезна тем, кто столкнется с подобной проблемой. 

Буду рад любой критике, советам, рекомендациям.

Комментарии (2)


  1. maksiplus19
    01.11.2023 06:30

    Я бы рекомендовал использовать message_thread_id (документация)

    Вторая часть ссылки - это id первого сообщения в топике: 1234

    1234 - это и есть message_thread_id данного топика

    Оно работает в текущем виде, но я не нашел в документации упоминания, что в reply_to_message_id можно передавать id топика. Не стоит использовать недокументированный функционал, т.к. он может поменяться в любой момент


  1. ebakirov
    01.11.2023 06:30

    В документации Telegram указано, что нужно использовать message_thread_id.

    Unique identifier for the target message thread (topic) of the forum; for forum supergroups only