Обновления попадают в подключенный обработчик:
void update(fb::Update& u) {
}
Каждый вызов этой функции - одно "сообщение" от бота. Здесь fb::Update
- это класс, в котором удобно доступны самые основные инструменты для работы с ботом и с сообщениями, как в виде значений, так и в виде вложенных классов. В "ручном режиме" разбора JSON библиотека поддерживает весь Telegram Bot API и позволяет получать из обновлений все возможные данные.
Например выведем текст сообщения:
void update(fb::Update& u) {
Serial.println(u.message().text());
}
Отличие FastBot2 состоит в том, что библиотека не разбирает пакет обновления по отдельным переменным: данные извлекаются в момент обращения к ним. Это сильно быстрее и расходует в сотни раз меньше памяти.
Тип данных Text #
Все значения, которые можно получить из обновления (вручную ил из Update), представлены типом данных Text
- "обёртка" для текстовых данных, которые хранятся где-то в памяти (в данном случае в буфере ответа сервера Телеграм). Text
позволяет:
- Печататься в любой
Print
(напримерSerial
) - Сравниваться со строками любого типа
- Экспортироваться в любой тип данных
- Имеет встроенный декодер Unicode (для кириллицы и смайликов)
- Может посчитать свой хэш для быстрого сравнения строк
Это очень мощный инструмент! Рекомендую изучить полную документацию, чтобы знать все возможности и разбирать апдейты с удовольствием
Примеры #
void update(fb::Update& u) {
// сравнение
u.message().text() == "abcd"; // со строкой
u.message().from().id() == 12345678ll; // с int64
uint32_t id = u.message().id(); // вывод в число
// вывод в String с преобразованием юникода (кириллицы)
String text = u.message().text().decodeUnicode();
Serial.println(text);
// пример с разбором callback query через хэш
switch (u.query().data().hash()) {
case SH("abc123"):
// ....
break;
case SH("hfghfgh"):
// ....
break;
case SH("test222"):
// ....
break;
}
}
В примере с хэшем строка, указанная в
SH
, не существует в программе: вместо этого компилятор подставляет её хэш (число). А вswitch
мы подаём хэш пришедшей строки. Это позволяет максимально быстро, оптимально и очень удобно сравнивать строки в сценариях, когда приходящий текст может иметь известный набор значений. В данном случае - обработка query, очень типовая задача (определение кнопки клавиатуры, на которую нажал юзер).
Личка с админом #
Телеграм бот - публичная штука, любой пользователь может найти вашего бота в поиске и взаимодействовать с ним. Чтобы ограничить круг лиц (админов), которые могут работать с ботом (или иметь дополнительные функции), достаточно вручную фильтровать обновления по id юзера. Несколько способов:
void update(fb::Update& u) {
// один админ
// Если не наш id - выходим
if (u.message().from().id() != 12345678ll) return;
// несколько админов
switch (u.message().from().id().toInt64()) {
// id админов
case 1323245345:
case 45345346:
case 3452536456:
case 3453454:
break;
// чужой - выходим
default:
return;
}
// несколько админов динамически (массив id)
// int64_t admin_ids[], int admin_len - объявлено выше
int64_t thisId = u.message().from().id();
bool ok = false;
for (int i = 0; i < admin_len; i++) {
if (admin_ids[i] == thisId) {
ok = true;
break;
}
}
if (!ok) return; // это не админ
}
Переполнение стека #
При создании больших программ с большим количеством кода внутри обработчика обновлений на esp8266 можно столкнуться с исключением (перезагрузка МК) по причине core panic
или stack smashed
, это происходит по причине переполнения стека памяти (на esp32 он выделен в два раза больше и получить это исключение практически невозможно). Проблема особенно хорошо проявляется при скачивании файлов внутри обработчика обновлений и наличии большого количества другого кода. Для избежания случайных перезагрузок рекомендуется разделять обработчик на несколько функций, это также позволит лучше структурировать программу и разделять на файлы/блоки:
// обработка сообщений
void uMessage(fb::Update& u) {
}
// обработка файлов
void uDocument(fb::Update& u) {
}
// обработка query коллбэков
void uQuery(fb::Update& u) {
}
// основной обработчик
void update(fb::Update& u) {
if (u.isMessage()) uMessage(u);
if (u.message().hasDocument()) uDocument(u);
if (u.isQuery()) uQuery(u);
}
void setup() {
bot.attachUpdate(update);
}
Разбор вручную #
Для работы напрямую с API Telegram вам понадобится официальная документация.
Класс Update
, а также все вложенные классы (User
, Message
...) имеют доступ к gson::Entry
пакета (документация здесь), который представляет собой распарсенный JSON пакет с хэшированными ключами. FastBot2 в свою очередь хранит хэши всех команд API Telegram (имеют префикс tg_apih
). Поэтому для получения доступа ко всему содержимому нужно обратиться к объекту через квадратные скобки с хэшем. Например, для получения текста сообщения код будет такой:
void update(fb::Update& u) {
Serial.println(u[tg_apih::message][tg_apih::text]);
// "изнутри" этот код аналогичен u.message().text()
}
Для проверки наличия в текущем обновлении или вложенном объекте нужно информации нужно использовать has
, чтобы обезопасить программу от чтения несуществующих данных:
void update(fb::Update& u) {
// если обновление содержит сообщение
if (u.has[tg_apih::message]) {
// то вывести текст сообщения
Serial.println(u[tg_apih::message][tg_apih::text]);
}
// по сути это аналог встроенного u.isMessage()
}
Также в целях отладки можно вывести содержимое любого JSON элемента с форматированием:
void update(fb::Update& u) {
u[tg_apih::message].stringify(Serial);
}
Примечание: результат доступа к значению через []
также является типом Text
, то есть доступны все его возможности:
void update(fb::Update& u) {
// сравнение
u[tg_apih::message][tg_apih::text] == "1234";
// конвертация
float v = u[tg_apih::message][tg_apih::text];
// и так далее
}
Комбинированный доступ #
Все подклассы внутри Update
имеют доступ через []
, то есть доступ к данным можно комбинировать. Например получить текст сообщения вот так:
void update(fb::Update& u) {
Serial.println(u.message()[tg_apih::text]);
// или получить значения, для которых в библиотеке не предусмотрено функций
Serial.println(u.message()[tg_apih::is_from_offline]);
}