View Categories

Разбор обновлений

Обновления попадают в подключенный обработчик:

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]);
}
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest

0 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Прокрутить вверх