Появилась FastBot2 - более быстрая и гораздо более универсальная библиотека для Телеграм бота! |
|---|
Многофункциональная быстрая библиотека для телеграм бота на esp8266/esp32
- Работает на стандартных библиотеках
- Опциональный "белый список" ID чатов
- Проверка обновлений вручную или по таймеру
- Отправка/удаление/редактирование/ответ на сообщения
- Чтение и отправка в чатах, группах, каналах
- Изменение названия и описания чата
- Закрепление/открепление сообщений
- Отправка стикеров
- Сообщения с форматированием markdown/html
- Вывод обычного меню
- Вывод инлайн меню с поддержкой кнопок-ссылок
- Поддержка Unicode (другие языки + эмодзи) для входящих сообщений
- Встроенный urlencode для исходящих сообщений
- Встроенные часы реального времени с синхронизацией от сервера Telegram
- Возможность OTA обновления прошивки .bin файлом из чата Telegram (firmware и SPIFFS)
- Отправка файлов из памяти в чат (+ редактирование)
ESP8266 (SDK v2.6+), ESP32
- Инструкция как создать и настроить Telegram бота
- Если бот у вас уже есть, убедитесь что он не в webhook режиме (отключен по умолчанию), иначе esp не сможет принимать сообщения!
- Для того, чтобы бот читал все сообщения в группе (а не только
/команды), нужно отключить параметр Group Privacy в настройках бота Bot Settings в чате с @BotFather. Данный параметр включен по умолчанию! - Для полноценной работы в группе (супергруппе) бота нужно сделать администратором!
Телеграм устанавливает следующие лимиты на отправку сообщений ботом (документация)
- В чат: не чаще раза в секунду. Отправлять чаще можно, но сообщение может не дойти
- В группу: не чаще 20 сообщений в минуту
- Суммарный лимит: не чаще 30 сообщений в секунду
- Бот может читать сообщения, с момента отправки которых прошло меньше 24 часов
- Бот не может писать в личку другому боту
- Бот не видит сообщения от других ботов в группе
- Телеграм разделяет текст на несколько сообщений, если длина текста превышает ~4000 символов! Эти сообщения будут иметь разный messageID в чате
- При ответе на сообщение библиотека парсит текст исходного сообщения, а не ответа
Используйте библиотеку CharDisplay для вывода графиков и рисования в чате!
Подробные уроки по работе с Телеграм ботом при помощи этой библиотеки можно найти на сайте Arduino набора GyverKIT
Universal-Arduino-Telegram-Bot
Для сравнения использовался минимальный пример с отправкой сообщения в чат и выводом входящих сообщений в сериал:
- send - отправка сообщения в чат
- update - проверка входящих сообщений
- free heap - объём свободной оперативной памяти во время работы программы
| Library | Flash, B | SRAM, B | send, ms | update, ms | free heap, B |
|---|---|---|---|---|---|
| Univ..Bot | 400004 | 29848 | 2000 | 1900 | 38592 |
| FastBot | 393220 | 28036 | 70 | 70 | 37552 |
| diff | 6784 | 1812 | 1930 | 1830 | 1040 |
- FastBot легче почти на 7 кБ Flash и 2 кБ SRAM, но занимает на 1 кБ в SRAM больше во время работы программы. Итого легче на 2-1 = 1 кБ SRAM.
- FastBot значительно быстрее обрабатывает чат и отправляет сообщения (на 2 секунды) за счёт ручного парсинга ответа сервера и статически выделенных HTTP клиентов
- Тест проведён в обычном режиме работы FastBot. При активации
FB_DYNAMICбиблиотека будет занимать на 10кб меньше памяти, но работать будет медленнее:- Free heap: 48000 кБ
- Отправка сообщения: 1 секунда
- Запрос обновления: 1 секунда
- Установка
- Инициализация
- Документация
- Использование
- Отправка сообщений
- Парсинг сообщений
- Тикер
- Минимальный пример
- Обращение к сообщениям
- Отправка стикеров
- Меню
- Обычное меню
- Инлайн меню
- Инлайн меню с коллбэком
- Ответ на коллбэк
- Модуль времени
- Время получения сообщения
- Часы реального времени
- Обновление прошивки из чата
- Оформление текста
- Отправка файлов
- Скачивание файлов
- Местоположение
- Всякие трюки
- Версии
- Баги и обратная связь
- Библиотеку можно найти по названию FastBot и установить через менеджер библиотек в:
- Arduino IDE
- Arduino IDE v2
- PlatformIO
- Скачать библиотеку .zip архивом для ручной установки:
- Распаковать и положить в C:\Program Files (x86)\Arduino\libraries (Windows x64)
- Распаковать и положить в C:\Program Files\Arduino\libraries (Windows x32)
- Распаковать и положить в Документы/Arduino/libraries/
- (Arduino IDE) автоматическая установка из .zip: Скетч/Подключить библиотеку/Добавить .ZIP библиотеку… и указать скачанный архив
- Читай более подробную инструкцию по установке библиотек здесь
- Рекомендую всегда обновлять библиотеку: в новых версиях исправляются ошибки и баги, а также проводится оптимизация и добавляются новые фичи
- Через менеджер библиотек IDE: найти библиотеку как при установке и нажать "Обновить"
- Вручную: удалить папку со старой версией, а затем положить на её место новую. "Замену" делать нельзя: иногда в новых версиях удаляются файлы, которые останутся при замене и могут привести к ошибкам!
FastBot bot;
FastBot bot(токен); // с указанием токена// ============== НАСТРОЙКИ ==============voidsetToken(String token); // изменить/задать токен ботаvoidsetChatID(String chatID); // установка ID чата (белый список), необязательно. Можно несколько через запятую ("id1,id2,id3")voidsetChatID(int64_t id); // то же самое, но в int64_t. Передай 0, чтобы отключитьvoidsetPeriod(int period); // период опроса в мс (по умолч. 3500)voidsetLimit(int limit); // кол-во сообщений, которое обрабатывается за один запрос, 1..100. (по умолч. 10)voidsetBufferSizes(uint16_t rx, uint16_t tx); // установить размеры буфера на приём и отправку, по умолч. 512 и 512 байт (только для esp8266)voidskipUpdates(); // пропустить непрочитанные сообщенияvoidsetTextMode(uint8_t mode); // режим текста "для отправки": FB_TEXT, FB_MARKDOWN, FB_HTML (см. пример textMode)voidnotify(bool mode); // true/false вкл/выкл уведомления от сообщений бота (по умолч. вкл)voidclearServiceMessages(bool state); // удалять из чата сервисные сообщения о смене названия и закреплении сообщений (умолч. false)// =============== ПАРСИНГ ===============voidattach(callback); // подключение функции-обработчика сообщенийvoiddetach(); // отключение обработчика сообщений// ================ ТИКЕР ================uint8_ttick(); // проверка обновлений по таймеруuint8_ttickManual(); // ручная проверка обновлений// ============== СООБЩЕНИЯ ==============// отправить сообщение в указанный в setChatID чат/чаты ИЛИ передать id чатаuint8_tsendMessage(String msg);
uint8_tsendMessage(String msg, String id);
// редактировать сообщение (msgid) в указанном в setChatID чате ИЛИ передать id чатаuint8_teditMessage(int32_t msgid, String text);
uint8_teditMessage(int32_t msgid, String text, String id);
// ответить на сообщение с id (replyID) в указанный в setChatID чат ИЛИ указать чатuint8_treplyMessage(String msg, int32_t replyID);
uint8_treplyMessage(String msg, int32_t replyID, String id);
// отправить стикер в указанный в setChatID чат/чаты ИЛИ передать id чатаuint8_tsendSticker(String stickerID);
uint8_tsendSticker(String stickerID, String id);
// ответить на callback текстом (text) и режимом (alert): FB_NOTIF - уведомление в чате, FB_ALERT - окно с кнопкой ОКuint8_tanswer(String text, bool alert);
// не отвечать автоматически на query этого апдейтаvoidnoAnswer();
//отправить уведомление о том, что бот печатает сообщениеuint8_tsendTyping(const String& id);
// =============== УДАЛЕНИЕ ===============// удалить сообщение с id (msgid) в указанном в setChatID чате ИЛИ передать id чата// удаляет любые типы сообщений (текст, стикер, инлайн меню)uint8_tdeleteMessage(int32_t msgid);
uint8_tdeleteMessage(int32_t msgid, String id);
// ============= ОБЫЧНОЕ МЕНЮ =============// показать меню (menu) в указанном в setChatID чате/чатах ИЛИ передать id чата/чатов uint8_tshowMenu(String menu);
uint8_tshowMenu(String menu, String id);
// единоразовое меню (закроется при выборе) в текущем чате ИЛИ передать id чатаuint8_tshowMenu(String menu, true);
uint8_tshowMenu(String menu, String id, true);
// скрыть меню в указанном в setChatID чате/чатах ИЛИ передать id чата/чатов uint8_tcloseMenu();
uint8_tcloseMenu(String id);
// ======== ОБЫЧНОЕ МЕНЮ С ТЕКСТОМ =========// сообщение (msg) + показать меню (menu) в указанном в setChatID чате/чатах ИЛИ передать id чата/чатов uint8_tshowMenuText(String msg, String menu);
uint8_tshowMenuText(String msg, String menu, String id);
// единоразовое меню (закроется при выборе)uint8_tshowMenuText(String msg, String menu, true);
uint8_tshowMenuText(String msg, String menu, String id, true);
// сообщение (msg) + скрыть меню в указанном в setChatID чате/чатах ИЛИ передать id чата/чатов uint8_tcloseMenuText(String msg);
uint8_tcloseMenuText(String msg, String id);
// ============= ИНЛАЙН МЕНЮ =============// сообщение (msg) с инлайн меню (menu) в указанном в setChatID чате/чатах ИЛИ передать id чата/чатовuint8_tinlineMenu(String msg, String menu);
uint8_tinlineMenu(String msg, String menu, String id);
// редактировать меню (msgid) текстом (menu) в указанном в setChatID чате ИЛИ передать id чатаuint8_teditMenu(int32_t msgid, String menu);
uint8_teditMenu(int32_t msgid, String menu, String id);
// ======= ИНЛАЙН МЕНЮ С КОЛЛБЭКОМ =======// сообщение (msg) с инлайн меню (menu) и коллбэком (cbck) в указанном в setChatID чате/чатах ИЛИ передать id чата/чатовuint8_tinlineMenuCallback(String msg, String menu, String cbck);
uint8_tinlineMenuCallback(String msg, String menu, String cbck, String id);
// редактировать меню (msgid) текстом (menu) и коллбэком (cback) в указанном в setChatID чате ИЛИ передать id чатаuint8_teditMenuCallback(int32_t msgid, String menu, String cback);
uint8_teditMenuCallback(int32_t msgid, String menu, String cback, String id);
// ============== ГРУППОВЫЕ ==============// для всех групповых команд бот должен быть админом в чате!// установить имя группы в указанном в setChatID чате ИЛИ передать id чатаuint8_tsetChatTitle(String& title);
uint8_tsetChatTitle(String& title, String& id);
// установить описание группы в указанном в setChatID чате ИЛИ передать id чатаuint8_tsetChatDescription(String& description);
uint8_tsetChatDescription(String& description, String& id);
// закрепить сообщение с ID msgid в указанном в setChatID чате ИЛИ передать id чатаuint8_tpinMessage(int32_t msgid);
uint8_tpinMessage(int32_t msgid, String& id);
// открепить сообщение с ID msgid в указанном в setChatID чате ИЛИ передать id чатаuint8_tunpinMessage(int32_t msgid);
uint8_tunpinMessage(int32_t msgid, String& id);
// открепить все сообщения в указанном в setChatID чате ИЛИ передать id чатаuint8_tunpinAll();
uint8_tunpinAll(String& id);
// ================ ФАЙЛЫ ================// скачать файл из чатаbooldownloadFile(File &f, const String& url);
// отправить файл из байт-буфера buf длиной length, типа type и именем файла name в указанном в setChatID чате ИЛИ передать id чатаuint8_tsendFile(uint8_t* buf, uint32_t length, FB_FileType type, const String& name);
uint8_tsendFile(uint8_t* buf, uint32_t length, FB_FileType type, const String& name, const String& id);
// отправить файл File, типа type и именем файла name в указанном в setChatID чате ИЛИ передать id чатаuint8_tsendFile(File &file, FB_FileType type, const String& name);
uint8_tsendFile(File &file, FB_FileType type, const String& name, const String& id);
// редактировать файл из байт-буфера buf длиной length, типа type и именем файла name в сообщении msgid в указанном в setChatID чате ИЛИ передать id чата// за исключением типа FB_VOICE!uint8_teditFile(uint8_t* buf, uint32_t length, FB_FileType type, const String& name, int32_t msgid);
uint8_teditFile(uint8_t* buf, uint32_t length, FB_FileType type, const String& name, int32_t msgid, const String& id);
// редактировать файл File, типа type и именем файла name в указанном в сообщении msgid в setChatID чате ИЛИ передать id чата// за исключением типа FB_VOICE!uint8_teditFile(File &file, FB_FileType type, const String& name, int32_t msgid);
uint8_teditFile(File &file, FB_FileType type, const String& name, int32_t msgid, const String& id);
// где FB_FileType - тип файлаFB_PHOTO - картинка
FB_AUDIO - аудио
FB_DOC - документ
FB_VIDEO - видео
FB_GIF - анимация
FB_VOICE - голосовое сообщение
// ============= КОМАНДА API =============// отправить команду API в указанном в setChatID чате ИЛИ передать id чата (id сам добавится в команду)// (пример команды: "/sendSticker?sticker=123456")uint8_tsendCommand(String& cmd);
uint8_tsendCommand(String& cmd, String& id);
// ================ СЕРВИС ===============int32_tlastBotMsg(); // ID последнего отправленного ботом сообщенияint32_tlastUsrMsg(); // ID последнего отправленного юзером сообщения
String chatIDs; // указанная в setChatID строка, для отладки и редактирования спискаuint8_tsendRequest(String& req); // отправить запрос (https://api.telegram.org/bot...)voidautoIncrement(boolean incr); // авто инкремент сообщений (по умолч включен)voidincrementID(uint8_t val); // вручную инкрементировать ID на val// ============== СООБЩЕНИЕ ===============// структура FB_msg
String& userID; // ID юзера
String& username; // ник юзера (в API это first_name)bool isBot; // юзер - бот
String& chatID; // ID чатаint32_t messageID; // ID сообщенияbool& edited; // сообщение отредактировано
String& text; // текст сообщения
String& replyText; // текст ответа, если он естьbool query; // запрос
String& data; // callback датаbool isFile; // это файл
String& fileName; // имя файла
String& fileUrl; // адрес файла для загрузкиboolOTA; // файл - запрос на OTA обновлениеuint32_t unix; // время сообщения// ================ ВРЕМЯ =================
FB_Time getTime(int16_t gmt); // получить текущее время, указать часовой пояс (например Москва 3) в часах или минутахbooltimeSynced(); // проверка, синхронизировано ли времяuint32_tgetUnix(); // получить текущее unix время// структура FB_Timeuint8_t second; // секундыuint8_t minute; // минутыuint8_t hour; // часыuint8_t day; // день месяцаuint8_t month; // месяцuint8_t dayWeek; // день недели (пн..вс 1..7)uint16_t year; // год
String timeString(); // строка времени формата ЧЧ:ММ:СС
String dateString(); // строка даты формата ДД.ММ.ГГГГ// ================ ОБНОВЛЕНИЕ =================uint8_tupdate(); // ОТА обновление прошивки, вызывать внутри обработчика сообщения по флагу OTAuint8_tupdateFS(); // ОТА обновление SPIFFS, вызывать внутри обработчика сообщения по флагу OTA// =============== СТАТУС ================// Многие функции возвращают статус:// 0 - ожидание// 1 - ОК// 2 - Переполнен// 3 - Ошибка телеграм// 4 - Ошибка подключения// 5 - не задан chat ID// 6 - множественная отправка, статус неизвестен// 7 - не подключен обработчик// 8 - ошибка файла// =============== УТИЛИТЫ ===============voidFB_unicode(String &s); // перевести unicodevoidFB_urlencode(String& s, String& dest); // urlencode из s в destint64_tFB_str64(const String &s); // перевод из String в int64_t
String FB_64str(int64_t id); // перевод из int64_t в String// ========== ДЕФАЙНЫ НАСТРОЕК ===========// объявлять ПЕРЕД подключением библиотеки
#defineFB_NO_UNICODE// отключить конвертацию Unicode для входящих сообщений (чуть ускорит программу)
#defineFB_NO_URLENCODE// отключить конвертацию urlencode для исходящих сообщений (чуть ускорит программу)
#defineFB_NO_OTA// отключить поддержку OTA обновлений из чата
#defineFB_DYNAMIC// включить динамический режим: библиотека дольше выполняет запрос, но занимает на 10 кб меньше памяти в SRAM
#defineFB_WITH_LOCATION// включить дополнительное поле location (содержащее широту и долготу) в сообщении (см примеры location и sunPosition)Для отправки в чат (сообщения, стикеры, меню и так далее) обязательно должен быть указан ID чата, в который будет осуществляться отправка. Можно указать несколько ID через запятую, в пределах одной строки. Есть два способа указать ID:
- Непосредственно в отправляющую функцию, у них у всех есть такой вариант (см. документацию выше)
bot.sendMessage("Hello!", "123456"); // в один чат
bot.sendMessage("Hello!", "123456,7891011"); // в два чата- Установить ID через
setChatID()и все отправления пойдут в эти чат/чаты, если в отправляющей функции не указан другой ID
bot.setChatID("123456"); // один чат//bot.setChatID("123456,7891011"); // несколько чатов// ...
bot.sendMessage("Hello!"); // уйдёт в "123456"
bot.sendMessage("Hello!", "112233"); // уйдёт в "112233"Примечание: Телеграм разделяет текст на несколько сообщений, если длина текста превышает ~4000 символов! Эти сообщения будут иметь разный messageID в чате.
Сообщения автоматически запрашиваются и читаются в tick(), при поступлении нового сообщения вызывается указанная функция-обработчик:
- Создаём в скетче свою функцию вида
void функция(FB_msg& сообщение) - Вызываем
attach(функция) - Эта функция будет автоматически вызвана при входящем сообщении, если ID чата совпадает или не настроен
- Если обработчик не подключен - сообщения не будут проверяться
- Внутри этой функции можно пользоваться переданной переменной
сообщение, которая имеет типFB_msg(структура) и содержит в себе:String userID- ID пользователяString username- имя пользователя или каналаbool isBot- сообщение от ботаString chatID- ID чатаint32_t messageID- ID сообщения в чатеbool edited- сообщение отредактированоString text- текст сообщения или попдпись к файлуString replyText- текст ответа, если он естьString data- callback данные из меню (если есть)bool query- запросbool isFile- это файлString fileName- имя файлаString fileUrl- адрес файла для загрузкиbool OTA- запрос на OTA обновление (получен .bin файл)uint32_t unix- время сообщенияuint32_t update_id- id апдейтаString query_id- id query
А также String toString() - вся информация из сообщения одной строкой, удобно для отладки (с версии 2.11)
В библиотеке реализован механизм белого списка: можно указать в setChatID() ID чата (или нескольких через запятую), сообщения из которого будут приниматься.
Сообщения из остальных чатов будут игнорироваться.
Для опроса входящих сообщений нужно подключить обработчик сообщений и вызывать tick() в главном цикле программы loop(), опрос происходит по встроенному таймеру.
По умолчанию период опроса установлен 3600 миллисекунд.
Можно опрашивать чаще (сменить период через setPeriod()), но лично у меня с ~2021 года сервер Телеграм стал отвечать не
раньше, чем через ~3 секунды. Если запрашивать обновления чаще этого периода, программа зависает внутри tick() (внутри GET запроса)
в ожидании ответа сервера на остаток от 3 секунд. При периоде ~3600 мс этого не происходит, поэтому я сделал его по умолчанию.
Возможно это зависит от провайдера или страны.
voidsetup() {
// подключаемся к WiFi
bot.attach(newMsg); // подключаем обработчик сообщений
}
voidnewMsg(FB_msg& msg) {
// выводим имя юзера и текст сообщения//Serial.print(msg.username);//Serial.print(", ");//Serial.println(msg.text);// выводим всю информацию о сообщении
Serial.println(msg.toString());
}
voidloop() {
bot.tick();
}Для редактирования и удаления сообщений и меню, а также закрепления сообщений, нужно знать ID сообщения (его номер в чате):
- ID входящего сообщения приходит в обработчик входящих сообщений
- ID последнего принятого сообщения можно получить из
lastUsrMsg() - ID последнего отправленного ботом сообщения можно получить из
lastBotMsg()
Будьте внимательны с ID чата, у всех чатов своя нумерация сообщений!
Для отправки стикера нужно знать ID стикера. Отправь нужный стикер боту @idstickerbot, он пришлёт ID стикера.
Этот ID нужно передать в функцию sendSticker().
Примечание: для всех вариантов меню не производится url encode. Избегайте символов
#и&или используйте уже закодированный url!
Для отправки меню используется строка с именами кнопок и специальным форматированием:
\t- горизонтальное разделение кнопок\n- вертикальное разделение кнопок- Лишние пробелы вырезаются автоматически
Пример меню 3x1: "Menu1 \t Menu2 \t Menu3 \n Menu4"
Результат:
_______________________
| | | |
| Menu1 | Menu2 | Menu3 |
|_______|_______|_______|
| |
| M e n u 4 |
|_______________________|Большое меню в нижней части чата.
showMenu("Menu1 \t Menu2 \t Menu3 \n Menu4");Нажатие на кнопку отправляет текст с кнопки (поле сообщения text).
Меню в сообщении. Требует ввода имени меню.
inlineMenu("MyMenu", "Menu1 \t Menu2 \t Menu3 \n Menu4");Нажатие на кнопку отправляет имя меню (поле сообщения text) и текст с кнопки (поле сообщения data).
Меню в сообщении. Позволяет задать каждой кнопке уникальный текст, который будет отправляться ботом вместе с именем меню. Список коллбэков перечисляется через запятую по порядку кнопок меню:
String menu1 = F("Menu 1 \t Menu 2 \t Menu 3 \n Back");
String cback1 = F("action1,action2,action3,back");
bot.inlineMenuCallback("Menu 1", menu1, cback1);Нажатие на кнопку отправляет имя меню (поле сообщения text) и указанные данные (поле сообщения data).
- (С версии 2.11) если callback задан как http/https адрес, кнопка автоматически станет кнопкой-ссылкой
При нажатии на кнопку инлайн-меню боту отправляется коллбэк, в обработчике сообщения будет поднят флаг query. Сервер Телеграм будет ждать ответа.
Ответить на коллбэк можно при помощи:
answer(текст, FB_NOTIF)- всплывающий текст-уведомлениеanswer(текст, FB_ALERT)- окно с предупреждением и кнопкой ОК
Отвечать нужно внутри обработчика сообщения! Пример:
voidnewMsg(FB_msg& msg) {
if (msg.query) bot.answer("Hello!", true);
}Если ничего не отвечать, библиотека сама отправит пустой ответ и "таймер" на кнопке исчезнет.
В библиотеке есть тип данных FB_Time, который является структурой с полями:
uint8_t second; // секундыuint8_t minute; // минутыuint8_t hour; // часыuint8_t day; // день месяцаuint8_t month; // месяцuint8_t dayWeek; // день недели (пн..вс 1..7)uint16_t year; // годПри создании структуры можно указать unix время и часовой пояс в часах или минутах (например 3 часа ИЛИ 180 минут для Москвы (UTC+3:00), 330 минут для Индии (UTC+5:30)). После этого можно забирать нужные значения времени:
FB_Time t(1651694501, 3);
Serial.print(t.hour);
Serial.print(':');
Serial.print(t.minute);
Serial.print(':');
Serial.print(t.second);
Serial.print('');
Serial.print(t.day);
Serial.print(':');
Serial.print(t.month);
Serial.print(':');
Serial.println(t.year);С версии 2.9 библиотека умеет выводить форматированное время (String):
Serial.print(t.timeString()); // ЧЧ:ММ:СС
Serial.print('');
Serial.println(t.dateString()); // ДД.ММ.ГГГГВ обработчике входящих сообщений у структуры FB_msg есть поле unix, оно хранит время сообщения в unix формате.
Для перевода в более читаемый формат действуем по описанной выше схеме:
voidnewMsg(FB_msg& msg) {
FB_Time t(msg.unix, 3); // передали unix и часовой пояс
Serial.print(t.timeString());
Serial.print('');
Serial.println(t.dateString());
}В ответ на любое сообщение от бота сервер сообщает время отправки в формате unix. С версии 2.6 это время парсится библиотекой и счёт продолжается дальше при помощи стандартных функций времени. Таким образом достаточно один раз отправить сообщение после включения платы, чтобы библиотека синхронизировала часы. При дальнейших отправках время также будет синхронизироваться и уточняться, т.к. вычисляемое средствами esp время будет уходить (~2 секунды в сутки). Инструменты:
uint32_t getUnix()- вернёт текущее время в unix формате или0, если время не синхронизировано.bool timeSynced()- вернётtrueесли часы синхронизированы.FB_Time getTime(gmt)- нужно передать свой часовой пояс, она вернётFB_Time.
Таким образом получить время можно двумя способами (см. пример timeTest):
FB_Time t = bot.getTime(3);
// или
FB_Time t(bot.getUnix(), 3);С версии библиотеки 2.13 появилось обновление прошивки "по воздуху" (OTA) через чат. Для обновления нужно:
- Скомпилировать программу в файл: Arduino IDE/Скетч/Экспорт бинарного файла (файл .bin появится в папке со скетчем)
- Отправить файл в чат с ботом
- Можно добавить подпись к файлу
- Файл может быть переслан из другого чата
- Файл будет обработан как обычное входящее сообщение от пользователя
msg- Подпись к файлу можно получить из поля
msg.text - Название файла можно получить из поля
msg.fileName - Будет поднят флаг
msg.OTA(в том случае, если файл имеет расширение .bin)
- Подпись к файлу можно получить из поля
- Для запуска процесса обновления прошивки нужно вызвать
update()внутри обработчика сообщений- В версии 2.20 появилась возможность обновления SPIFFS - нужно вызвать
updateFS()
- В версии 2.20 появилась возможность обновления SPIFFS - нужно вызвать
- В тот же чат чат будет отправлен статус обновления (OK или error)
- После успешного обновления esp перезагрузится
// обновить, если просто прислали bin файлif (msg.OTA) bot.update();
// обновить, если файл имеет нужную подписьif (msg.OTA && msg.text == "update") bot.update();
// обновить, если файл имеет нужное имяif (msg.OTA && msg.fileName == "update.bin") bot.update();
// обновить, если прислал известный человек (админ)if (msg.OTA && msg.chatID == "123456") bot.update();// обновить SPIFFS, если пришёл файл, в имени которого есть слово spiffsif (msg.OTA && msg.fileName.indexOf("spiffs") > 0) bot.updateFS();Если прошивка весит много - её можно сжать в gzip:
- Рекомендуемый уровень сжатия - 9
- Имя файла должно оканчиваться на .bin.gz
- Файл точно так же отправляется в чат с ботом или пересылается ему
- В прошивке перед подключением всех библиотек нужно объявить
#define ATOMIC_FS_UPDATE
Библиотека поддерживает оформление текста в сообщениях. Разметка оформления выбирается при помощи setTextMode(mode), где mode:
FB_TEXT- по умолчанию (оформление отключено)FB_MARKDOWN- разметка Markdown v2FB_HTML- разметка HTML
Доступные теги описаны в API Telegram. Например для Markdown:
bot.setTextMode(FB_MARKDOWN);
bot.sendMessage(F("*Bold*, ~Strike~, `code`, [alexgyver.ru](https://alexgyver.ru/)"));Выведет в чат: Bold, Strike, code, alexgyver.ru
Внимание! В режиме FB_MARKDOWN нельзя использовать в сообщениях символы
! + #, сообщение не отправится. Возможно получится исправить в будущем (проблема urlencode и экранирования зарезервированных символов).
Отправлять можно файлы следующих типов (тип указывается при отправке), в телеграм это разные типы сообщений:
FB_PHOTO- картинка (jpg, png...)FB_AUDIO- аудио (mp3, wav...)FB_DOC- документ (txt, pdf...)FB_VIDEO- видео (avi, mp4...)FB_GIF- анимация (gif)FB_VOICE- голосовое сообщение (ogg)
Редактировать можно все указанные выше типы сообщений кроме FB_VOICE!
При отправке нужно указать имя файла с таким же расширением, с каким он создан или хранится в памяти.
Библиотека поддерживает два варианта отправки файлов: из буфера (оперативной памяти) и из SPIFFS.
Для отправки нужно передать буфер, его размер, тип файла, его размер и ID чата (без указания ID чата будет использован чат из setChatID). Для редактирования нужно также указать ID сообщения:
uint8_tsendFile(uint8_t* buf, uint32_t length, FB_FileType type, const String& name, const String& id);
uint8_teditFile(uint8_t* buf, uint32_t length, FB_FileType type, const String& name, int32_t msgid, const String& id);Отправим текст в виде текстового файла, таким образом можно вести и выгружать логи:
char buf[] = "Hello, World!";
bot.sendFile((byte*)buf, strlen(buf), FB_DOC, "test.txt", CHAT_ID);Отправим фотографию с камеры (см. пример sendCamPhoto):
frame = esp_camera_fb_get();
bot.sendFile((byte*)frame->buf, frame->len, FB_PHOTO, "photo.jpg", CHAT_ID);Вместо буфера и его размера функция отправки принимает файл, остальное - как при отправке из буфера:
uint8_tsendFile(File &file, FB_FileType type, const String& name, const String& id);
uint8_teditFile(File &file, FB_FileType type, const String& name, int32_t msgid, const String& id);Для работы с файлами таким образом нужно подключить библиотеку, ккоторая определяет класс File, например SPIFFS.h или LittleFS.h.
Подключать библиотеку нужно ДО (выше по коду) подключения FastBot! Иначе функции с File будут недоступны.
Отправим картинку из памяти:
File file = LittleFS.open("/test.png", "r");
bot.sendFile(file, FB_PHOTO, "test.png", CHAT_ID);
file.close();С версии 2.20 в объекте входящего сообщения присутствует ссылка на файл, если в сообщении есть файл. Это позволяет скачать файл во внутреннюю память.
Для скачивания файлов средствами FastBot нужно подключить библиотеку, которая определяет класс File, например SPIFFS.h или LittleFS.h.
Подключать библиотеку нужно ДО (выше по коду) подключения FastBot! Иначе функции с File будут недоступны.
Для скачивания файла нужно открыть/создать файл с правами для записи и передать его в downloadFile() вместе со ссылкой на файл.
voidnewMsg(FB_msg& msg) {
if (msg.isFile) { // это файл
Serial.print("Downloading ");
Serial.println(msg.fileName);
String path = '/' + msg.fileName; // путь вида /filename.xxx
File f = LittleFS.open(path, "w"); // открываем для записиbool status = bot.downloadFile(f, msg.fileUrl); // загружаем
Serial.println(status ? "OK" : "Error"); // статус
}
}При указанной настройке #define FB_WITH_LOCATION бот добавляет поле location в обрабатываемые сообщения (FB_msg):
structFB_Location {
String &latitude;
String &longitude;
};В случае если боту прислали географическое местоположение (location), то поля latitude/longitude заполняюися координатами из полученного ботом location:
// обработчик сообщенийvoidnewMsg(FB_msg& msg) {
if (msg.location.latitude.length() > 0 && msg.location.longitude.length() > 0) {
bot.sendMessage("Lat: " + msg.location.latitude + ", Lon: " + msg.location.longitude, msg.chatID);
}
}См примеры examples/location и examples/sunPosition.
Сообщения отмечаются прочитанными при следующем (относительно текущего обработчика сообщений) обновлении в tick(), то есть спустя как минимум настроенный тайм-аут. Если хочется перезагрузить esp по команде, то вот такая конструкция
voidmessage(FB_msg &msg) {
if (msg.text == "restart") ESP.restart();
}Приведёт к бутлупу (бесконечной перезагрузке), потому что сообщение не отметится прочитанным. Можно поднять флаг, по которому уходить в перезагрузку, предварительно вызвав tickManual:
bool res = 0;
voidmessage(FB_msg &msg) {
if (msg.text == "restart") res = 1;
}
voidloop() {
bot.tick();
if (res) {
bot.tickManual(); // Чтобы отметить сообщение прочитаннымESP.restart();
}
}В библиотеке есть функция skipUpdates, позволяющая пропустить все непрочитанные сообщения. Но иногда бывает удобно ориентироваться по времени.
Если нужно проигнорировать сообщения, отправленные юзером в то время как бот был оффлайн (или выключен), то можно поступить так:
- Запомнить unix-время, когда бот вышел в онлайн
- Сравнивать время текущего сообщения с ним. Если оно меньше - игнорировать сообщение
Пример пропуска сообщений, отправленных до запуска контроллера:
uint32_t startUnix; // храним времяvoidsetup() {
//connectWiFi();
bot.attach(newMsg);
bot.sendMessage("start", "1234"); // отправить сообщение, чтобы получить время
startUnix = bot.getUnix(); // запомнили
}
// обработчик сообщенийvoidnewMsg(FB_msg& msg) {
if (msg.unix < startUnix) return; // игнорировать сообщения// ....
}v1.0
v1.1 - оптимизация
v1.2 - можно задать несколько chatID и отправлять в указанный чат
v1.3 - добавлена возможность задать текст при открытии и закрытии меню
v1.3.1 - исправлены ошибки с 1.3
v1.4 - добавлена возможность удалять сообщения
v1.5 - оптимизация, возможность смены токена, новый парсинг сообщений (id, имя, текст)
v1.5.1 - получаем также ID сообщения
v1.6 - добавлен режим FB_DYNAMIC_HTTP, чтение имени пользователя
v1.7:
- Убрал динамический режим FB_DYNAMIC_HTTP, работает слишком медленно
- Исправил warningи
- Починил работу бота в "группах" (отрицательный ID чата)
- Оптимизация памяти
- Ускорил работу
- Пофиксил работу через раз в сценарии "эхо"
v2.0:
- Убрал минимум в 3200 мс
- Добавил обработку Юникода (русский язык, эмодзи). Спасибо Глебу Жукову!
- Из меню удаляются лишние пробелы, работать стало проще
- Поддержка esp32
- Большая оптимизация
- Добавил коллбэки в inlineMenu
- Добавил ID юзера
- Добавил редактирование сообщений и кучу всего
v2.1:
- Ещё оптимизация
- Добавил форматирование текста (markdown, html)
- Добавил ответ на сообщение
v2.2:
- Большая оптимизация памяти и производительности
- Добавил notify() - уведомления от сообщений бота
- Добавил единоразовый показ клавиатуры
v2.3: Небольшая оптимизация
v2.4: Добавил url encode для текста сообщений
v2.5: Добавил флаги в FB_msg: сообщение отредактировано и сообщение отправлено ботом. Улучшил парсинг текста
v2.6: Добавил встроенные часы реального времени
v2.7: Добавил отправку стикеров
v2.8: Убрал лишний вывод в сериал, GMT можно в минутах
v2.9: Исправлена бага в парсинге, парсинг ускорен, добавлен вывод форматированного времени, добавлена фамилия и время сообщения
v2.10: Добавлены функции для изменения названия и описания чата, закрепления и открепления сообщений. Убраны edit/deleteMessageID, editMenuID
v2.11:
- Оптимизация, исправление багов
- Callback data теперь парсится отдельно в data
- Переделана работа с callback
- Добавлен toString() для FB_msg для отладки
- В callback добавлена обработка url адресов
- Убраны first_name и last_name (с сохранением легаси)
- usrID и ID переименованы в userID и messageID (с сохранением легаси)
- Окончательно убран старый обработчик входящих сообщений
v2.12: поправлены примеры, исправлен парсинг isBot, переделан механизм защиты от длинных сообщений, переделана инициализация
v2.13: Оптимизация памяти. Добавил OTA обновление
v2.14: Улучшен парсинг строки с ID, добавил отключение OTA, добавил парсинг названия группы/канала в username
v2.15: Заплатка для кривой библиотеки ESP32
v2.16: добавлен вывод fileName, пофикшены неотправляемые сообщения в Markdown режиме
v2.17: вывод текста сообщения, на которое ответил юзер + корректная работа с menu в группах
v2.17.1: мелкий фикс #12
v2.18: добавлен режим FB_DYNAMIC: библиотека дольше выполняет запрос, но занимает на 10 кб меньше памяти в SRAM
v2.19: поддержка OTA со сжатием gzip
v2.20:
- добавил OTA обновление SPIFFS + пример
- добавил вывод url файла для скачивания из чата + пример
- добавил возможность скачать файл из чата
- добавил возможность отправки файлов (из SPIFFS или буфера) + пример
- добавил возможность редактирования файлов (из SPIFFS или буфера)
- добавил пример отправки фото с камеры ESP32-CAM
v2.21: ускорил отправку файлов ботом в чат
v2.22: мелкая оптимизация, исправил ошибку компиляции при дефайне FB_NO_OTA
v2.23: пофиксил источник реального времени на editMessage
v2.24: фикс отправки больших файлов #17
v2.25: добавил skipUpdates - пропуск непрочитанных сообщений
v2.26: фикс некорректного отображения цифр после русских букв #37
При нахождении багов создавайте Issue, а лучше сразу пишите на почту alex@alexgyver.ru
Библиотека открыта для доработки и ваших Pull Request'ов!
При сообщении о багах или некорректной работе библиотеки нужно обязательно указывать:
- Версия библиотеки
- Какой используется МК
- Версия SDK (для ESP)
- Версия Arduino IDE
- Корректно ли работают ли встроенные примеры, в которых используются функции и конструкции, приводящие к багу в вашем коде
- Какой код загружался, какая работа от него ожидалась и как он работает в реальности
- В идеале приложить минимальный код, в котором наблюдается баг. Не полотно из тысячи строк, а минимальный код
