3#define HEADERS_END "\r\n\r\n"
7 m_botusername.reserve(32);
8 m_rxbuffer.reserve(bufferSize);
9 this->telegramClient = &client;
18 if (!telegramClient->connected())
20 m_lastmsg_timestamp = millis();
26 *((
volatile uint32_t*) 0x60000900) &= ~(1);
30 Serial.println(
"\n\nUnable to connect to Telegram server");
36 static uint32_t lastCTime;
37 log_debug(
"Connected using Telegram hostname\n"
38 "Last connection was %d seconds ago\n",
39 (
int)(millis() - lastCTime) / 1000);
46 *((
volatile uint32_t*) 0x60000900) |= 1;
49 return telegramClient->connected();
60 static uint32_t lastResetTime;
61 if (millis() - lastResetTime > 5000) {
62 lastResetTime = millis();
63 log_info(
"Restart Telegram connection\n");
64 telegramClient->stop();
65 m_lastmsg_timestamp = millis();
66 m_waitingReply =
false;
68 return telegramClient->connected();
75 String httpBuffer((
char *)0);
78 httpBuffer += m_token;
80 httpBuffer += command;
82 httpBuffer +=
" HTTP/1.0"
83 "\nHost: api.telegram.org"
84 "\nConnection: keep-alive"
85 "\nContent-Type: application/json";
86 httpBuffer +=
"\nContent-Length: ";
87 httpBuffer += strlen(payload);
89 httpBuffer += payload;
91 telegramClient->print(httpBuffer);
93 m_waitingReply =
true;
100 telegramClient->stop();
105 while (telegramClient->available())
108 m_rxbuffer += (char)telegramClient->read();
110 m_waitingReply =
false;
111 if (m_rxbuffer.indexOf(
"\"ok\":true") > -1)
123 if (millis() - m_lastmsg_timestamp > 10 * m_minUpdateTime)
129 if (millis() - m_lastUpdateTime > m_minUpdateTime)
131 m_lastUpdateTime = millis();
134 if (m_waitingReply ==
false)
137 snprintf(payload,
BUFFER_SMALL,
"{\"limit\":1,\"timeout\":0,\"offset\":%" INT32 "}", m_lastUpdateId);
142 if (telegramClient->connected() && telegramClient->available())
145 bool close_connection =
false;
146 uint16_t len = 0, pos = 0;
148 while (telegramClient->connected())
150 String line = telegramClient->readStringUntil(
'\n');
153 if (line.indexOf(
"close") > -1)
155 close_connection =
true;
157 if (line.indexOf(
"Content-Length:") > -1)
159 len = line.substring(strlen(
"Content-Length: ")).toInt();
165 for (uint32_t timeout = millis(); (millis() - timeout > 1000) || pos < len;)
167 if (telegramClient->available())
169 m_rxbuffer += (char)telegramClient->read();
173 m_waitingReply =
false;
174 m_lastmsg_timestamp = millis();
176 if (close_connection)
178 telegramClient->stop();
179 log_info(
"Connection closed from server");
182 if (m_rxbuffer.indexOf(
"\"ok\":true") > -1)
184 if (m_sentCallback !=
nullptr && m_waitSent)
186 if (m_rxbuffer.indexOf(String(m_lastSentMsgId)) > -1)
188 m_sentCallback(m_waitSent);
197 if (m_sentCallback !=
nullptr && m_waitSent)
200 m_sentCallback(m_waitSent);
214 if (millis() - m_lastSentTime > m_sentTimeout && m_waitSent && m_sentCallback !=
nullptr)
217 m_sentCallback(m_waitSent);
225 DynamicJsonDocument updateDoc(m_JsonBufferSize);
226 DeserializationError err = deserializeJson(updateDoc, m_rxbuffer);
234 m_lastUpdateId = m_rxbuffer.substring(m_rxbuffer.indexOf(F(
"\"update_id\":")) + strlen(
"\"update_id\":")).toInt() + 1;
239 sendTo(message.
chatId,
"[ERROR] - No memory: inrease buffer size with \"setJsonBufferSize(buf_size)\" method");
242 updateDoc.shrinkToFit();
245 if (!updateDoc.containsKey(
"result"))
248 serializeJsonPretty(updateDoc, Serial);
252 result = updateDoc[
"result"];
253 if (result.is<JsonArray>())
260 if (result[
"message_id"])
262 m_lastSentMsgId = result[
"message_id"];
265 uint32_t updateID = result[
"update_id"];
268 m_lastUpdateId = updateID + 1;
274 if (!result[
"forward_from"])
279 if (result[
"callback_query"][
"id"])
282 message.
sender.
id = result[
"callback_query"][
"from"][
"id"];
283 message.
sender.
username = result[
"callback_query"][
"from"][
"username"].as<String>();
284 message.
sender.
firstName = result[
"callback_query"][
"from"][
"first_name"].as<String>();
285 message.
sender.
lastName = result[
"callback_query"][
"from"][
"last_name"].as<String>();
287 message.
chatId = result[
"callback_query"][
"message"][
"chat"][
"id"];
288 message.
messageID = result[
"callback_query"][
"message"][
"message_id"];
289 message.
date = result[
"callback_query"][
"message"][
"date"];
290 message.
chatInstance = result[
"callback_query"][
"chat_instance"];
293 message.
text = result[
"callback_query"][
"message"][
"text"].as<String>();
297 for (uint8_t i = 0; i < m_keyboardCount; i++)
298 m_keyboards[i]->checkCallback(message);
300 else if (result[
"forward_from"])
303 message.
sender.
id = result[
"forward_from"][
"id"];
304 message.
sender.
username = result[
"forward_from"][
"username"].as<String>();
305 message.
sender.
firstName = result[
"forward_from"][
"first_name"].as<String>();
306 message.
sender.
lastName = result[
"forward_from"][
"last_name"].as<String>();
308 message.
text = result[
"text"].as<String>();
311 else if (result[
"message"][
"message_id"])
315 message.
sender.
id = result[
"message"][
"from"][
"id"];
316 message.
sender.
username = result[
"message"][
"from"][
"username"].as<String>();
317 message.
sender.
firstName = result[
"message"][
"from"][
"first_name"].as<String>();
318 message.
sender.
lastName = result[
"message"][
"from"][
"last_name"].as<String>();
320 message.
messageID = result[
"message"][
"message_id"];
321 message.
chatId = result[
"message"][
"chat"][
"id"];
322 message.
date = result[
"message"][
"date"];
324 if (result[
"message"][
"location"])
331 else if (result[
"message"][
"contact"])
334 message.
contact.
id = result[
"message"][
"contact"][
"user_id"];
335 message.
contact.
firstName = result[
"message"][
"contact"][
"first_name"].as<String>();
336 message.
contact.
lastName = result[
"message"][
"contact"][
"last_name"].as<String>();
337 message.
contact.
phoneNumber = result[
"message"][
"contact"][
"phone_number"].as<String>();
338 message.
contact.
vCard = result[
"message"][
"contact"][
"vcard"].as<String>();
341 else if (result[
"message"][
"new_chat_member"])
344 message.
member.
isBot = result[
"message"][
"new_chat_member"][
"is_bot"];
345 message.
member.
id = result[
"message"][
"new_chat_member"][
"id"];
346 message.
member.
firstName = result[
"message"][
"new_chat_member"][
"first_name"].as<String>();
347 message.
member.
lastName = result[
"message"][
"new_chat_member"][
"last_name"].as<String>();
348 message.
member.
username = result[
"message"][
"new_chat_member"][
"username"].as<String>();
351 else if (result[
"message"][
"left_chat_member"])
354 message.
member.
isBot = result[
"message"][
"new_chat_member"][
"is_bot"];
355 message.
member.
id = result[
"message"][
"new_chat_member"][
"id"];
356 message.
member.
firstName = result[
"message"][
"new_chat_member"][
"first_name"].as<String>();
357 message.
member.
lastName = result[
"message"][
"new_chat_member"][
"last_name"].as<String>();
358 message.
member.
username = result[
"message"][
"new_chat_member"][
"username"].as<String>();
361 else if (result[
"message"][
"document"])
364 message.
document.
file_id = result[
"message"][
"document"][
"file_id"].as<String>();
365 message.
document.
file_name = result[
"message"][
"document"][
"file_name"].as<String>();
366 message.
text = result[
"message"][
"caption"].as<String>();
370 else if (result[
"message"][
"reply_to_message"])
373 message.
text = result[
"message"][
"text"].as<String>();
376 else if (result[
"message"][
"text"])
379 message.
text = result[
"message"][
"text"].as<String>();
399 StaticJsonDocument<BUFFER_SMALL> smallDoc;
400 deserializeJson(smallDoc, m_rxbuffer);
402 m_botusername = smallDoc[
"result"][
"username"].as<String>();
417 StaticJsonDocument<BUFFER_MEDIUM> fileDoc;
418 deserializeJson(fileDoc, m_rxbuffer);
420 doc.
file_path =
"https://api.telegram.org/file/bot";
423 doc.
file_path += fileDoc[
"result"][
"file_path"].as<String>();
424 doc.
file_size = fileDoc[
"result"][
"file_size"].as<
long>();
447 if (!strlen(message))
450 m_lastSentTime = millis();
451 m_lastSentMsgId += 1;
456 root[
"chat_id"] = msg.
chatId;
457 root[
"text"] = message;
459 switch (m_formatType)
464 root[
"parse_mode"] =
"HTML";
467 root[
"parse_mode"] =
"MarkdownV2";
472 root[
"disable_notification"] =
true;
474 if (keyboard !=
nullptr)
481 DynamicJsonDocument doc(ESP.getMaxFreeBlockSize() -
BUFFER_MEDIUM);
483 DynamicJsonDocument doc(ESP.getMaxAllocHeap());
488 deserializeJson(doc, keyboard);
489 JsonObject myKeyb = doc.as<JsonObject>();
490 root[
"reply_markup"] = myKeyb;
493 root[
"reply_markup"][
"selective"] =
true;
494 root[
"reply_markup"][
"force_reply"] =
true;
500 size_t len = measureJson(root);
502 serializeJson(root, payload, len);
505 bool result =
sendCommand(
"sendMessage", payload, wait);
513 "{\"chat_id\":%lld,\"from_chat_id\":%lld,\"message_id\":%" INT32 "}",
516 bool result =
sendCommand(
"forwardMessage", payload);
518 m_lastUpdateTime = millis();
529 "{\"chat_id\":%lld,\"photo\":\"%s\",\"caption\":\"%s\"}",
530 chat_id, url, caption);
544 "{\"chat_id\":%lld,\"video\":\"%s\",\"caption\":\"%s\"}",
545 chat_id, url, caption);
554 if (!strlen(message))
558 root[
"chat_id"] = channel;
559 root[
"text"] = message;
560 root[
"silent"] = silent ?
"true" :
"false";
562 switch (m_formatType)
567 root[
"parse_mode"] =
"HTML";
570 root[
"parse_mode"] =
"MarkdownV2";
574 size_t len = measureJson(root);
576 serializeJson(root, payload, len);
591 "{\"callback_query_id\":%lld,\"text\":\"%s\",\"cache_time\":2,\"show_alert\":%s}",
593 bool result =
sendCommand(
"answerCallbackQuery", payload,
true);
601 "{\"remove_keyboard\":true,\"selective\":%s}", selective ?
"true" :
"false");
608 DocumentType doc,
const char *filename,
const char *caption)
613 return sendStream(chat_id,
"sendDocument",
"application/json",
"document", stream, size, filename, caption);
615 return sendStream(chat_id,
"sendDocument",
"text/csv",
"document", stream, size, filename, caption);
617 return sendStream(chat_id,
"sendDocument",
"application/zip",
"document", stream, size, filename, caption);
619 return sendStream(chat_id,
"sendDocument",
"application/pdf",
"document", stream, size, filename, caption);
621 return sendStream(chat_id,
"sendPhoto",
"image/jpeg",
"photo", stream, size, filename, caption);
623 return sendStream(chat_id,
"sendDocument",
"audio/mp3",
"audio", stream, size, filename, caption);
625 return sendStream(chat_id,
"sendDocument",
"text/plain",
"document", stream, size, filename, caption);
631void AsyncTelegram2::setformData(int64_t chat_id,
const char *cmd,
const char *type,
632 const char *propName,
size_t size, String &formData,
633 String &request,
const char *filename,
const char *caption)
636#define BOUNDARY "----WebKitFormBoundary7MA4YWxkTrZu0gW"
637#define END_BOUNDARY "\r\n--" BOUNDARY "--\r\n"
639 char int64_buf[22] = {0};
640 snprintf(int64_buf,
sizeof(int64_buf),
"%lld", chat_id);
642 formData =
"--" BOUNDARY "\r\nContent-disposition: form-data; name=\"chat_id\"\r\n\r\n";
643 formData += int64_buf;
645 if (caption !=
nullptr)
647 formData +=
"\r\n--" BOUNDARY "\r\nContent-disposition: form-data; name=\"caption\"\r\n\r\n";
651 formData +=
"\r\n--" BOUNDARY "\r\nContent-disposition: form-data; name=\"";
652 formData += propName;
653 formData +=
"\"; filename=\"";
654 formData += filename !=
nullptr ? filename :
"image.jpg";
655 formData +=
"\"\r\nContent-Type: ";
657 formData +=
"\"\r\n\r\n";
658 int contentLength = size + formData.length() + strlen(
END_BOUNDARY);
660 request =
"POST /bot";
664 request +=
" HTTP/1.0\r\nHost: " TELEGRAM_HOST "\r\nContent-Length: ";
665 request += contentLength;
666 request +=
"\r\nContent-Type: multipart/form-data; boundary=" BOUNDARY "\r\n";
669bool AsyncTelegram2::sendStream(int64_t chat_id,
const char *cmd,
const char *type,
const char *propName,
670 Stream &stream,
size_t size,
const char *filename,
const char *caption)
675 m_waitingReply =
true;
677 formData.reserve(512);
679 request.reserve(256);
680 setformData(chat_id, cmd, type, propName, size, formData, request, filename, caption);
683 uint32_t t1 = millis();
684 Serial.println(request);
685 Serial.println(formData);
688 telegramClient->println(request);
690 telegramClient->print(formData);
694 int lastBytes = size - (n_block *
BLOCK_SIZE);
696 for (uint16_t pos = 0; pos < n_block; pos++)
702 stream.readBytes(data, lastBytes);
703 telegramClient->write(data, lastBytes);
707 telegramClient->flush();
710 log_debug(
"Raw upload time: %lums\n", millis() - t1);
717 Serial.println(
"\nError: client not connected");
721bool AsyncTelegram2::sendBuffer(int64_t chat_id,
const char *cmd,
const char *type,
const char *propName, uint8_t *data,
size_t size,
const char *caption)
726 m_waitingReply =
true;
728 formData.reserve(512);
730 request.reserve(256);
731 setformData(chat_id, cmd, type, propName, size, formData, request, caption, caption);
734 uint32_t t1 = millis();
737 telegramClient->println(request);
739 telegramClient->print(formData);
744 int lastBytes = size - (n_block *
BLOCK_SIZE);
746 for (pos = 0; pos < n_block; pos++)
751 telegramClient->write((
const uint8_t *)data + pos *
BLOCK_SIZE, lastBytes);
755 telegramClient->flush();
758 log_debug(
"Raw upload time: %lums\n", millis() - t1);
764 Serial.println(
"\nError: client not connected");
775 StaticJsonDocument<BUFFER_MEDIUM> doc;
776 DeserializationError err = deserializeJson(doc, m_rxbuffer);
783 serializeJsonPretty(doc[
"result"], cmdList);
806 DeserializationError err = deserializeJson(doc, m_rxbuffer);
813 for (JsonObject key : doc[
"result"].as<JsonArray>())
815 if (key[
"command"] == cmd)
821 StaticJsonDocument<256> obj;
822 obj[
"command"] = cmd;
823 obj[
"description"] = desc;
824 doc[
"result"].as<JsonArray>().add(obj);
826 StaticJsonDocument<BUFFER_MEDIUM> doc2;
827 doc2[
"commands"] = doc[
"result"].as<JsonArray>();
829 size_t len = measureJson(doc2);
830 char payload[len + 1];
831 serializeJson(doc2, payload, len);
834 bool result =
sendCommand(
"setMyCommands", payload,
true);
840 String payload =
"{\"chat_id\":";
842 payload +=
",\"message_id\":";
843 payload += message_id;
844 payload +=
", \"text\": \"";
847 if (keyboard.length())
849 payload +=
"\", \"reply_markup\": ";
858 bool result =
sendCommand(
"editMessageText", payload.c_str());
bool setMyCommands(const String &cmd, const String &desc)
bool sendAnimationByUrl(const int64_t &chat_id, const char *url, const char *caption)
void getMyCommands(String &cmdList)
bool getFile(TBDocument &doc)
bool sendTo(const int64_t userid, const char *message, const char *keyboard=nullptr)
bool endQuery(const TBMessage &msg, const char *message, bool alertMode=false)
bool sendPhotoByUrl(const int64_t &chat_id, const char *url, const char *caption)
bool sendDocument(int64_t chat_id, Stream &stream, size_t size, DocumentType doc, const char *filename, const char *caption=nullptr)
MessageType getNewMessage(TBMessage &message)
bool sendCommand(const char *command, const char *payload, bool blocking=false)
bool sendMessage(const TBMessage &msg, const char *message, const char *keyboard=nullptr, bool wait=false)
bool sendToChannel(const char *channel, const char *message, bool silent=false)
AsyncTelegram2(Client &client, uint32_t bufferSize=BUFFER_BIG)
bool removeReplyKeyboard(const TBMessage &msg, const char *message, bool selective=false)
bool forwardMessage(const TBMessage &msg, const int64_t to_chatid)
bool editMessage(int32_t chat_id, int32_t message_id, const String &txt, const String &keyboard)
#define log_debug(format,...)
bool disable_notification