В данной библиотеке, в отличие от многих других, отправка запроса на сервер Telegram идёт не через query string, а в теле запроса типа application/json. Это означает, что отправляемый текст не должен быть urlencoded и не имеет прочих ограничений, и может отправляться в неизменном виде - это сильно экономит время и ресурс контроллера. Также это избавляет от проблем с отправкой сообщений в стилях HTML
и Markdown
.
ID чата и тип Value #
В большинстве команд отправки нужен id чата - уникальный номер чата или группы в Telegram. Это число выходит за рамки типа данных long
, поэтому в библиотеке используется тип данных Value
(документация). Он принимает как числа любого типа, так и строки. Таким образом id чата при работе с FastBot2 можно хранить как в строках в любом виде, так и в целочисленной переменной типа int64_t
(long long
) - очень удобно для взаимодействия и хренения в энергонезависимой памяти (EEPROM/Flash). Например:
Value val;
val = "1234567898765";
val = 1234567898765;
При разборе обновлений id чата является типом Text
, который в свою очередь может конвертироваться в int64_t
:
int64_t chat_id;
void update(fb::Update& u) {
u.message().from().id() == 12345678ll; // сравнение
chat_id = u.message().from().id(); // сохранение в переменную
}
Поэтому для отправки можно использовать id в любом виде.
Отправка сообщений #
FastBot2 удобно оборачивает почти весь API по отправке сообщения в класс fb::Message
. Типичный сценарий использования:
- Создать
fb::Message
- Заполнить обязательные параметры -
text
иchatID
- По желанию заполнить другие параметры
- Отправить в
sendMessage
fb::Message msg;
msg.text = "some text";
msg.chatID = 123456677889; // или в виде строки
bot.sendMessage(msg);
Возможна укороченная запись, Message
имеет конструктор с минимальными обязательными параметрами:
bot.sendMessage(fb::Message("hello!", "12312424353"));
Асинхронность и ID сообщения #
ID отправленного ботом сообщения можно получить из lastBotMessage()
. Нужно понимать, что у каждого чата свой счётчик ID сообщений. Библиотека получает ID отправленного сообщения из ответа сервера, поэтому есть несколько важных моментов.
Сообщение может быть отправлено двумя способами:
- Синхронно (по умолчанию) - библиотека ждёт и обрабатывает ответ сервера внутри отправки сообщения, поэтому сразу после вызова
sendMessage
функцияlastBotMessage()
возвращает актуальный ID отправленного сообщения - Асинхронно - библиотека не ждёт ответа от сервера и получит его в следующем
tick()
, т.е.lastBotMessage()
не выдаст достоверный ID сразу после отправки
При отправке нескольких асинхронных сообщений подряд библиотека принудительно будет ждать ответ сервера для каждого предыдущего сообщения, т.е. сделает их синхронными, чтобы избежать переподключения к серверу
fb::Message msg("hello", 1234567);
bot.sendMessage(msg, false); // асинхронно
Serial.println(bot.lastBotMessage()); // ID может быть некорректным
bot.sendMessage(msg); // синхронно
// библиотека дождётся ответа от предыдущего сообщения перед отправкой нового
Serial.println(bot.lastBotMessage()); // реальный ID
Редактирование сообщений #
Для редактирования текста сообщений используется следующая конструкция:
fb::TextEdit et;
et.text = "edited text";
et.chatID = 123423234;
et.messageID = 234;
bot.editText(et);
Вот так например бот будет удалять сообщения юзера и вместо этого менять текст своего последнего сообщения на текст сообщения юзера:
// удалить сообщение юзера
bot.deleteMessage(u.message().chat().id(), u.message().id());
// редактировать
if (bot.lastBotMessage()) {
fb::TextEdit et;
et.text = u.message().text().toString();
et.chatID = u.message().chat().id();
et.messageID = bot.lastBotMessage();
bot.editText(et);
} else {
// отправить сообщение, если бот не знает id своего последнего сообщения
bot.sendMessage(fb::Message(u.message().text(), u.message().chat().id()));
}
Отдельно можно редактировать меню сообщения через MenuEdit
, см. следующую страницу документации.
Отправка нескольким ID #
Если ID хранятся в текстовом виде в виде CSV списка, то можно использовать парсер Text
:
fb::Message msg;
msg.text = "hello!";
su::TextParser ids("546343285;1234853;8796453678;38347567", ';');
while (ids.parse()) {
msg.chatID = ids;
bot.sendMessage(msg);
}
Если id - это массив int64_t
- то всё проще:
int64_t ids[..]; // массив id
int ids_len; // фактическая длина
// ...
fb::Message msg;
msg.text = "hello!";
for (int i = 0; i < ids_len; i++) {
msg.chatID = ids[i];
bot.sendMessage(msg);
}
Отправка вручную #
Библиотека поддерживает самостоятельную сборку пакетов для отправки на сервер согласно API Telegram. Для сборки используется линейный сборщик json строк gson::string
- вот документация. Создание и отправка пакета выглядит так:
- Начать пакет с указанием команды
- Собрать пакет, указав нужные данные
- Отправить пакет
Отправка сообщения в чат:
// Все команды API Telegram доступны в tg_cmd
fb::Packet p = bot.beginPacket(tg_cmd::sendMessage);
// все ключи объектов API Telegram доступны в tg_api
p.addString(tg_api::text, "message text");
p.addInt(tg_api::chat_id, 12312341231);
bot.sendPacket(p);
Установка команд бота, способ 1 (с элементом json строки):
fb::Packet p = bot.beginPacket(tg_cmd::setMyCommands);
p.beginArr(tg_api::commands);
p.addText(R"(
{"command":"help","description":"Помощь по командам"},
{"command":"info","description":"Информация о настройках"}
)");
p.endArr();
bot.sendPacket(p);
Установка команд бота, способ 2 (полностью нативная сборка):
fb::Packet p = bot.beginPacket(tg_cmd::setMyCommands);
p.beginArr(tg_api::commands);
p.beginObj().addString(tg_api::command, "help").addString(tg_api::description, "Помощь по командам").endObj();
p.beginObj().addString(tg_api::command, "info").addString(tg_api::description, "Информация о настройках").endObj();
p.endArr();
bot.sendPacket(p);
Второй способ #
Подходит для json пакетов, собранных отдельно. Использует больше памяти, чем beginPacket
-sendPacket
(дублирует пакет перед отправкой). Отправлять можно как json объект вида {"key":"value"...}
, так и список значений без фигурных скобок (библиотека добавит их сама): "key":"value"...
. Например:
// свой пакет
bot.sendCommand(tg_cmd::sendMessage, "{\"chat_id\":1234567864,\"text\":\"hello text\"}");
// сборка в gson::string
gson::string g;
g[tg_api::chat_id] = 1234567864;
g[tg_api::text] = "hello text";
bot.sendCommand(tg_cmd::sendMessage, g);
Дополнение стандартных типов #
Во всех стандартных типах библиотеки (Message, Location и проч.) есть поле json
, в которое можно добавлять данные, не предусмотренные библиотекой, но предусмотренные Telegram API. Например:
fb::Message msg("version 3", CHAT_ID);
msg.json[tg_api::business_connection_id] = "12345";
msg.json[tg_api::message_effect_id] = "asdadawdwd";
bot.sendMessage(msg);
Важный момент - нельзя добавлять данные, которые добавляются библиотекой, например поля
text
иchat_id
- телеграм не примет такой запрос с дублирующимися ключами!
Разбор ответа сервера #
Все методы отправки запросов возвращают результат типа fb::Result
- если запрос был синхронный (по умолчанию), то в результате будет распарсенный ответ сервера. Это позволяет отправлять запросы и получать результат на следующей же строчке кода. Например запрос getMe
:
fb::Result res = bot.sendCommand(tg_cmd::getMe);
Serial.println(res[tg_apih::first_name]); // имя бота
Serial.println(res[tg_apih::username]); // ник бота
// res.stringify(Serial); // вывести весь пакет
// Serial.println(res.getRaw()); // вывести ответ сервера текстом как он есть
Для уменьшения использования оперативной памяти рекомендуется оборачивать все запросы с разбором результата в блоки кода {}
, чтобы результат освобождал память после использования:
{
fb::Result res = bot.sendCommand(tg_cmd::getMe);
Serial.println(res[tg_apih::first_name]);
}
{
gson::string g;
g[tg_api::chat_id] = 1234567864;
fb::Result res = bot.sendCommand(tg_cmd::getChat, g);
Serial.println(res[tg_apih::title]);
}
{
fb::Result res = bot.sendMessage(fb::Message("hello!", "12312424353"));
res.stringify(Serial);
}
Также освободить память можно, вызвав reset()
:
fb::Result res1 = bot.sendCommand(tg_cmd::getMe);
Serial.println(res1[tg_apih::first_name]);
res1.reset();
gson::string g;
g[tg_api::chat_id] = 1234567864;
fb::Result res2 = bot.sendCommand(tg_cmd::getChat, g);
Serial.println(res2[tg_apih::title]);
res2.reset();
fb::Result res3 = bot.sendMessage(fb::Message("hello!", "12312424353"));
res3.stringify(Serial);
res3.reset();
HTML и Markdown #
Режимы текста HTML и Markdown поддерживают только конкретные теги, см. API Telegram:
Для настройки режима нужно просто указать его:
fb::Message msg;
msg.mode = fb::Message::Mode::MarkdownV2;
msg.text...
Markdown #
Для режима Markdown нужно соблюдать некоторые правила:
- Любой символ может быть экранирован с помощью
\
, чтобы выводиться просто символом, а не разметкой MD. Например"test\\_"
выведетtest_
, без экранирования будет ошибка - Внутри блоков кода символы
<code> </code>
и
\должны быть экранированы с помошью
` - Внутри блока ссылки в (круглых скобках) все
)
и\
должны быть экранированы с помошью\
- Во всех остальных случаях символы
_
,*
,[
,]
,(
,)
,~
,<code> </code>
,
>,
#,
+,
-,
=,
|,
{,
},
.,
!должны быть экранированы с помошью
`
FastBot2 не экранирует символы! Это нужно делать самостоятельно
Например для вывода текста[hello] - world!
нужно писать"\\[hello\\] \\- world\\!"