Перейти к содержимому
ГоМастер

Поиск по урокам

Найдите урок, курс, термин или раздел

Урок 22 из 25 ~16 мин +20 XP

Реакции, стикеры и живой тон бота

Реакции и стикеры в telegram-боте — самый дешёвый способ сделать общение живым: одна стикерная кнопка заменяет три строки вежливого текста, а реакция эмодзи подтверждает действие, не порождая лишнего сообщения. В этом уроке отправляем стикеры по file_id через tgbotapi.NewSticker, ставим реакции методом setMessageReaction с типом ReactionTypeEmoji — в базовой библиотеке через generic-запрос — и разбираем границу, за которой эмоции начинают вредить.

Содержание урока · 9 разделов

Бот из первых уроков курса отвечает на каждое действие текстом: приняли, сохранили, готово, неверная команда. В личном диалоге это терпимо, но добавьте бота в рабочий чат на десяток человек — и лента превращается в кашу: восемь коротких подтверждений на каждое реальное сообщение, и всё важное теряется между ними. Телеграфный ответ на это — эмоции: реакция эмодзи на сообщение подтверждает действие без нового сообщения, а стикер вместо шаблонной фразы экономит внимание и читается мгновенно. Bot API умеет и то и другое, причём механизм реакций появился позже обёртки tgbotapi и вызывается через generic-запрос — это полезный навык сам по себе: API Telegram растёт быстрее любой библиотеки, и умение дёрнуть незавёрнутый метод закрывает половину таких пробелов. Разберём обе механики с кодом, лимитами и двумя историями, где эмоциональный тон бота обернулся против него.

Стикеры: NewSticker и file_id без загрузки#

Стикер для Bot API — это медиа-сообщение, и устроен он так же, как фото из урока про медиа: у каждого стикера есть file_id, по которому Telegram отдаёт файл без всякой загрузки с вашей стороны. Бот отправляет стикер конфигурацией tgbotapi.NewSticker(chatID, file): во второй аргумент передаётся идентификатор — в базовой версии библиотеки это tgbotapi.InputFileString(fileID), в свежих форках то же самое выглядит как tgbotapi.FileID(fileID), и смысл не меняется: путь к файлу на серверах Telegram. Где взять file_id: переслать нужный стикер боту и посмотреть в логе Message.Sticker.FileID — приём тот же, что для фото. Хранится идентификатор как обычная строка в конфиге или базе и живёт долго; у стикеров, в отличие от некоторых временных файлов, идентификатор стабильный. Свой набор боту не нужен: слать можно стикеры из любых общедоступных паков, а создание собственного пака через createNewStickerSet — отдельная тема, для тона бота хватит готовых.

Отправка стикера по file_id

GO
func answerWithSticker(bot *tgbotapi.BotAPI, chatID int64) {
        sticker := tgbotapi.NewSticker(chatID,
                tgbotapi.InputFileString("CAACAgIAAxkBAAEB..."))
        // тонкая настройка как у любого сообщения:
        sticker.ReplyMarkup = tgbotapi.NewReplyKeyboard(
                tgbotapi.NewReplyKeyboardRow(
                        tgbotapi.NewKeyboardButton("Ещё"),
                ),
        )
        if _, err := bot.Send(sticker); err != nil {
                log.Println("sticker:", err)
        }
}

StickerConfig ведёт себя как обычное сообщение: та же отправка через bot.Send, тот же ReplyMarkup, тот же chat_id. Ошибка чаще всего означает, что file_id от другого бота — идентификаторы медиа не переносятся между ботами, каждый получает свои. Логирование ошибки обязательно: без него бот молчит, и кажется, что стикер не нравится пользователю, а не библиотеке.

Реакции: setMessageReaction и ReactionTypeEmoji#

Реакция — метод setMessageReaction из Bot API, который базовая обёртка не завершила конфигурацией, поэтому вызывается generic-запросом: bot.MakeRequest с именем метода и картой параметров tgbotapi.Params, где ключи — имена полей API, а значения — строки. Обязательные параметры: chat_id и message_id — на чьё сообщение ставим, и reaction — JSON-массив реакций. Каждый элемент массива — объект типа ReactionTypeEmoji: поле type со значением emoji и поле emoji с самим эмодзи, например огонёк или большой палец. Необязательный is_big делает реакцию крупной, а пустой массив reaction снимает реакцию бота с сообщения. В свежих обёртках и форках библиотеки для этого есть типизированная конфигурация — идея та же, только JSON собирает компилятор, — но схема запроса не менялась: тот же метод, те же поля. Ответ метода — простой True, и ошибка приходит по тем же правилам, что и весь API: код ответа отличен от ожидаемого, в описании причина.

Generic-запрос setMessageReaction

GO
func react(bot *tgbotapi.BotAPI, chatID int64, messageID int, emoji string) error {
        params := tgbotapi.Params{
                "chat_id":    strconv.FormatInt(chatID, 10),
                "message_id": strconv.Itoa(messageID),
                "reaction":   "[{\"type\":\"emoji\",\"emoji\":\"" + emoji + "\"}]",
                "is_big":     "false",
        }
        _, err := bot.MakeRequest("setMessageReaction", params)
        return err
}

// использование: подтверждение без нового сообщения
_ = react(bot, update.Message.Chat.ID, update.Message.MessageID, "👍")

Params — это map[string]string, поэтому числа конвертируются strconv-ом, а JSON-массив реакций собирается строкой. Внутри строки JSON-кавычки экранированы обратными слэшами — типичная опечатка здесь ломает не компиляцию, а запрос: Telegram ответит ошибкой про невалидное поле. Ответ MakeRequest — готовая структура APIResponse, и err отличен от nil при любом отклонении, включая 429.

Когда реакции уместны, а когда вредят#

Реакция — это подтверждение, и её честное место — в конце цепочки, когда действие уже сделано: задача записана, напоминание поставлено, файл сохранён. Пользователь видит жест на своём сообщении, лента не растёт, диалог остаётся чистым. Второй сценарий, где реакции незаменимы, — групповые чаты: бот подтверждает реакции вместо восьми сообщений принято-принято-принято, и рабочий чат остаётся рабочим. Граница уместности тоже ясная. Реакция не заменяет ответ на ошибку: если команда не сработала, человеку нужен текст — что именно не так и как исправить, а большой палец на невыполненном запросе выглядит насмешкой. Реакция не заменяет диалог: анкеты, уточнения и выбор — это сообщения и клавиатуры. И у эмодзи есть смысловая нагрузка: огонь на сообщении об ошибке читается как издёвка, поэтому набор бота стоит держать узким и предсказуемым — палец для успеха, глаза для принятия к сведению, и всё. Спорная точка курса: часть разработчиков ставит реакции на каждое входящее сообщение ради анимации — по опыту это дешёвый эффект, который быстро обесценивает и реакции, и бота.

Лимиты реакций и custom_emoji в чатах#

Пара технических деталей, которые экономят вечер. Набор допустимых эмодзи фиксирован Telegram: реакция ставится только эмодзи из разрешённого списка и только в чатах, где реакции включены на уровне настроек чата — в группе можно ограничить набор, и бот связан теми же ограничениями, что и люди. Попытка поставить странный эмодзи или реакцию в чате с выключенными реакциями вернёт ошибку про недопустимую реакцию — это нормальный ответ API, обрабатывайте его без паники. Тип custom_emoji с анимированными эмодзи существует в схеме реакций, но для ботов привычный и надёжный путь — обычный emoji-набор. Стикеры при этом лимитам послушны точно так же: это обычные медиа-отправки со всеми счётчиками. И тонкое место в группах: бот видит только сообщения, для которых у него есть доступ, и не может ставить реакции на чужие служебные сообщения — если реакция не встаёт, проверьте сначала message_id, а потом разрешения.

Что выбрать под сообщение#

ИнструментКогдаЦена и риск
ТекстОшибка, инструкция, диалогРастит ленту; нужен всегда при сбоях
СтикерПразднование результата, дружеский тонМедийно, но быстро приедается; нужен file_id
РеакцияТихое подтверждение успешного действияНоль новых сообщений; 429 при потоке, обязана идти после дела
Текст + клавиатураВыбор и анкетыFSM и состояния, дольше всего писать

Пачка стикеров в конфиге бота#

Пару слов про выбор стикеров заранее, потому что это решение конфигурации, а не кода. Готовить пачку file_id стоит в одном месте: константы или конфиг-файл с осмысленными ключами — успех, мягкая ошибка, праздник, — и хендлеры берут стикер по ситуации, а не пишут идентификатор в строке. Тогда смена тона бота — это правка конфига без пересборки, а тестовый прогон подменяет пачку на пустую и проверяет, что бот не падает без стикеров. Набор метода getStickerSet позволяет скачать целый пак одним вызовом и собрать идентификаторы пачкой, но для тона бота хватает пяти-семи стикеров из разных паков. И правило для групп: там стикеры быстрее превращаются в шум, чем реакции, потому что стикер — это целое сообщение; в группах бот подтверждает действия реакциями, а стикеры оставляет личному диалогу, где нет чужой ленты, которую можно засорить.

Проверка: стикер по идентификатору

Как бот отправляет стикер, который уже есть в Telegram?

Проверка: реакция не встала

setMessageReaction вернул ошибку про недопустимую реакцию. Самая вероятная причина?

Реакция до сохранения

Прочитайте код и выберите, что он выведет:

case update.Message != nil:
        _ = react(bot, update.Message.Chat.ID,
                update.Message.MessageID, "👍")
        err := saveTask(update.Message.Text)
        if err != nil {
                log.Println("save:", err)
                // ответа об ошибке хендлер не отправляет
        }

Соберите generic-запрос реакции

Впишите тип параметров, ключи полей и метод запроса.

params := tgbotapi.{ "": strconv.FormatInt(chatID, 10), "message_id": strconv.Itoa(messageID), "reaction": "[{\"type\":\"emoji\",\"emoji\":\"👍\"}]", } _, err := bot.("setMessageReaction", params)

Соберите ответ стикером на входящий

Расставьте строки: от входящего сообщения до отправки стикера.

Строки

Ваша программа

Нажимайте на строки слева, чтобы собрать программу

Карточки: реакции и стикеры

Термин

NewSticker

нажмите, чтобы перевернуть
Определение

Конструктор StickerConfig: chatID и файл-идентификатор. Отправка обычным bot.Send; file_id берётся из пересланного стикера и хранится как строка.

1 / 5

Маршрут вокруг темы: урок про медиа и файлы — откуда берётся file_id и как работает передача файлов, ведь стикер живёт тем же механизмом; урок про ошибки и лимиты — 429 и RetryAfter, под которые попадает и setMessageReaction; урок про рассылки — про темп отправок в десятки чатов. Применить эмоции с пользой: урок про напоминания — реакция на принятое напоминание вместо сообщения, урок про Mini Apps — когда интерфейсу тесно в эмодзи и стикерах. Проверить ветки тона тестами поможет урок про тестирование бота. Полный маршрут — на странице курса.

Песочница урока

Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.

Вывод

Запустите код, чтобы увидеть вывод.

Коротко: что запомнить

  • Стикер отправляется по file_id: tgbotapi.NewSticker с InputFileString или FileID — без загрузки байтов и без своего пака.
  • Реакция — метод setMessageReaction: в базовой библиотеке через bot.MakeRequest и tgbotapi.Params, reaction — JSON-массив с элементами ReactionTypeEmoji.
  • Разрешённый набор эмодзи задаёт чат; недопустимый эмодзи — обычная ошибка API, обрабатываемая без паники.
  • Реакция — подтверждение после успешного действия; реакция на ошибку или до сохранения врёт пользователю.
  • Поток реакций подчиняется тем же лимитам, что и рассылки: 429 и RetryAfter никуда не делись.
  • Тон бота — это редкий набор эмодзи и стикеров по делу; анимация на каждое сообщение обесценивает и то и другое.

Домашнее задание

Добавьте боту два инструмента тона: команду, отвечающую стикером по сохранённому в конфиге file_id, и реакцию-подтверждение после успешного добавления записи — setMessageReaction через MakeRequest с ReactionTypeEmoji, только после сохранения и с логированием ошибки. Затем проверьте поведение в группе с ограниченным набором реакций: поставьте эмодзи вне разрешённого списка и убедитесь, что бот отвечает ошибкой без падения. В конце замените одну текстовую очередь подтверждений на реакции и оцените, насколько чище стала лента чата.

Личные заметкиПусто
Хранятся только в вашем браузере (Telegram · Реакции, стикеры и живой тон бота)

Вопросы и ответы по уроку

Может ли бот ставить реакции на любые сообщения?

На те, которые видит, в чатах, где реакции включены: метод требует chat_id и message_id, а набор эмодзи ограничен настройками чата. На служебные сообщения и там, где бот не имеет доступа, реакция вернёт ошибку — это штатный ответ, а не авария.

Стоит ли делать собственный стикерпак для бота?

Для тона — нет: годятся стикеры из любых общедоступных паков по file_id, а создание пака через createNewStickerSet добавляет отдельную тему с правами и обновлениями. Свой пак оправдан, когда стикер несёт бренд или уникальную информацию, которую готовыми паками не передать.

Почему response от MakeRequest называется иначе, чем у Send?

MakeRequest возвращает общую структуру APIResponse без типизированного результата: метод не завершили в библиотеке, поэтому и ответа-типа нет. Для setMessageReaction этого достаточно — метод возвращает True, а ошибки разбираются по коду и описанию как обычно.

Термины урока

Ключевые понятия из этого урока — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылки на практику.

Следующая ступень учебного пути

Prometheus25 уроков~12 чпрактика25 уроков уже доступны

Мониторинг Go с Prometheus: метрики, Grafana, алерты

Курс по метрикам в Go: Counter, Gauge, Histogram, лейблы, PromQL, Grafana-дашборды, алерты и бизнес-метрики. 25 уроков с примерами client_golang.

  • Понимать четыре типа метрик и выбирать правильный
  • Экспортировать метрики из Go через promhttp
  • Инструментировать HTTP-запросы и бизнес-события
  • Писать PromQL-запросы: rate, sum, p99