AsyncTelegram2

Loading...
Searching...
No Matches
AsyncTelegram2.cpp
Go to the documentation of this file.
1#include "AsyncTelegram2.h"
2
3#define HEADERS_END "\r\n\r\n"
4
5 AsyncTelegram2::AsyncTelegram2(Client &client, uint32_t bufferSize)
6{
7 m_botusername.reserve(32); // Telegram username is 5-32 chars lenght
8 m_rxbuffer.reserve(bufferSize);
9 this->telegramClient = &client;
10 m_minUpdateTime = MIN_UPDATE_TIME;
11}
12
13 AsyncTelegram2::~AsyncTelegram2(){};
14
15 bool AsyncTelegram2::checkConnection()
16{
17 // Start connection with Telegramn server (if necessary)
18 if (!telegramClient->connected())
19 {
20 m_lastmsg_timestamp = millis();
21 log_info("Start handshaking...");
22
23 // ESP8266 Soft watch dog reset issue
24 #ifdef ESP8266
25 ESP.wdtDisable();
26 *((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF
27 #endif
28 if (!telegramClient->connect(TELEGRAM_HOST, TELEGRAM_PORT))
29 {
30 Serial.println("\n\nUnable to connect to Telegram server");
31 reset();
32 }
33#if DEBUG_ENABLE
34 else
35 {
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);
40 lastCTime = millis();
41 }
42#endif
43 }
44 #ifdef ESP8266
45 ESP.wdtEnable(10000);
46 *((volatile uint32_t*) 0x60000900) |= 1; // Hardware WDT ON
47 #endif
48
49 return telegramClient->connected();
50}
51
52 bool AsyncTelegram2::begin()
53{
54 checkConnection();
55 return getMe();
56}
57
58 bool AsyncTelegram2::reset(void)
59{
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;
67 }
68 return telegramClient->connected();
69}
70
71 bool AsyncTelegram2::sendCommand(const char *command, const char *payload, bool blocking)
72{
73 if (checkConnection())
74 {
75 String httpBuffer((char *)0);
76 httpBuffer.reserve(BUFFER_BIG);
77 httpBuffer = "POST https://" TELEGRAM_HOST "/bot";
78 httpBuffer += m_token;
79 httpBuffer += "/";
80 httpBuffer += command;
81 // Let's use 1.0 protocol in order to avoid chunked transfer encoding
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);
88 httpBuffer += "\n\n";
89 httpBuffer += payload;
90 // Send the whole request in one go is much faster
91 telegramClient->print(httpBuffer);
92
93 m_waitingReply = true;
94 // Blocking mode
95 if (blocking)
96 {
97 if (!telegramClient->find((char *)HEADERS_END))
98 {
99 log_error("Invalid HTTP response");
100 telegramClient->stop();
101 return false;
102 }
103 // If there are incoming bytes available from the server, read them and print them:
104 m_rxbuffer = "";
105 while (telegramClient->available())
106 {
107 yield();
108 m_rxbuffer += (char)telegramClient->read();
109 }
110 m_waitingReply = false;
111 if (m_rxbuffer.indexOf("\"ok\":true") > -1)
112 return true;
113 }
114 }
115
116 return false;
117}
118
119 bool AsyncTelegram2::getUpdates()
120{
121
122 // No response from Telegram server for a long time
123 if (millis() - m_lastmsg_timestamp > 10 * m_minUpdateTime)
124 {
125 reset();
126 }
127
128 // Send message to Telegram server only if enough time has passed since last
129 if (millis() - m_lastUpdateTime > m_minUpdateTime)
130 {
131 m_lastUpdateTime = millis();
132
133 // If previuos reply from server was received (and parsed)
134 if (m_waitingReply == false)
135 {
136 char payload[BUFFER_SMALL];
137 snprintf(payload, BUFFER_SMALL, "{\"limit\":1,\"timeout\":0,\"offset\":%" INT32 "}", m_lastUpdateId);
138 sendCommand("getUpdates", payload);
139 }
140 }
141
142 if (telegramClient->connected() && telegramClient->available())
143 {
144 // We have a message, parse data received
145 bool close_connection = false;
146 uint16_t len = 0, pos = 0;
147 // Skip headers
148 while (telegramClient->connected())
149 {
150 String line = telegramClient->readStringUntil('\n');
151 if (line == "\r")
152 break;
153 if (line.indexOf("close") > -1)
154 {
155 close_connection = true;
156 }
157 if (line.indexOf("Content-Length:") > -1)
158 {
159 len = line.substring(strlen("Content-Length: ")).toInt();
160 }
161 }
162
163 // If there are incoming bytes available from the server, read them and store:
164 m_rxbuffer = "";
165 for (uint32_t timeout = millis(); (millis() - timeout > 1000) || pos < len;)
166 {
167 if (telegramClient->available())
168 {
169 m_rxbuffer += (char)telegramClient->read();
170 pos++;
171 }
172 }
173 m_waitingReply = false;
174 m_lastmsg_timestamp = millis();
175
176 if (close_connection)
177 {
178 telegramClient->stop();
179 log_info("Connection closed from server");
180 }
181
182 if (m_rxbuffer.indexOf("\"ok\":true") > -1)
183 {
184 if (m_sentCallback != nullptr && m_waitSent)
185 {
186 if (m_rxbuffer.indexOf(String(m_lastSentMsgId)) > -1)
187 {
188 m_sentCallback(m_waitSent);
189 m_waitSent = false;
190 }
191 }
192 return true;
193 }
194 else
195 {
196 log_error(m_rxbuffer.c_str());
197 if (m_sentCallback != nullptr && m_waitSent)
198 {
199 m_waitSent = false;
200 m_sentCallback(m_waitSent);
201 }
202 return false;
203 }
204 }
205 return false;
206}
207
208// Parse message received from Telegram server
209 MessageType AsyncTelegram2::getNewMessage(TBMessage &message)
210{
211 message.messageType = MessageNoData;
212
213 // Last sent message timeout
214 if (millis() - m_lastSentTime > m_sentTimeout && m_waitSent && m_sentCallback != nullptr)
215 {
216 m_waitSent = false;
217 m_sentCallback(m_waitSent);
218 }
219
220 // We have a message, parse data received
221 if (getUpdates())
222 {
223 JsonVariant result;
224 { // Add as scope in order to destroy updateDoc as soon as possible
225 DynamicJsonDocument updateDoc(m_JsonBufferSize);
226 DeserializationError err = deserializeJson(updateDoc, m_rxbuffer);
227 if (err)
228 {
229 log_error("deserializeJson() failed\n");
230 log_debug("%s", err.c_str());
231 log_error();
232 log_error(m_rxbuffer);
233 // Skip this message id due to the impossibility to parse correctly
234 m_lastUpdateId = m_rxbuffer.substring(m_rxbuffer.indexOf(F("\"update_id\":")) + strlen("\"update_id\":")).toInt() + 1;
235 // int64_t chat_id = m_rxbuffer.substring( m_rxbuffer.indexOf("{\"id\":") + strlen("{\"id\":")).toInt();
236 m_rxbuffer = "";
237
238 // Inform the user about parsing error (blocking)
239 sendTo(message.chatId, "[ERROR] - No memory: inrease buffer size with \"setJsonBufferSize(buf_size)\" method");
240 return MessageNoData;
241 }
242 updateDoc.shrinkToFit();
243 m_rxbuffer = "";
244
245 if (!updateDoc.containsKey("result"))
246 {
247 log_error("JSON data not expected");
248 serializeJsonPretty(updateDoc, Serial);
249 return MessageNoData;
250 }
251
252 result = updateDoc["result"];
253 if (result.is<JsonArray>())
254 {
255 result = result[0];
256 }
257 }
258
259 // This a reply after send message
260 if (result["message_id"])
261 {
262 m_lastSentMsgId = result["message_id"];
263 }
264
265 uint32_t updateID = result["update_id"];
266 if (updateID)
267 {
268 m_lastUpdateId = updateID + 1;
269 }
270 else
271 {
272 // In case of forwarded message reply, we need to get original text
273 // so don't skip parsing the reply to just sent forwarMessage command
274 if (!result["forward_from"])
275 return MessageNoData;
276 }
277
278 debugJson(result, Serial);
279 if (result["callback_query"]["id"])
280 {
281 // this is a callback query
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>();
286
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"];
291 message.callbackQueryID = result["callback_query"]["id"];
292 message.callbackQueryData = result["callback_query"]["data"].as<String>();
293 message.text = result["callback_query"]["message"]["text"].as<String>();
294 message.messageType = MessageQuery;
295
296 // Check if callback function is defined for this button query
297 for (uint8_t i = 0; i < m_keyboardCount; i++)
298 m_keyboards[i]->checkCallback(message);
299 }
300 else if (result["forward_from"])
301 {
302 // this is a forwarded message from user or group
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>();
307
308 message.text = result["text"].as<String>();
309 message.messageType = MessageForwarded;
310 }
311 else if (result["message"]["message_id"])
312 {
313
314 // this is a message
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>();
319
320 message.messageID = result["message"]["message_id"];
321 message.chatId = result["message"]["chat"]["id"];
322 message.date = result["message"]["date"];
323
324 if (result["message"]["location"])
325 {
326 // this is a location message
327 message.location.longitude = result["message"]["location"]["longitude"];
328 message.location.latitude = result["message"]["location"]["latitude"];
329 message.messageType = MessageLocation;
330 }
331 else if (result["message"]["contact"])
332 {
333 // this is a contact message
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>();
339 message.messageType = MessageContact;
340 }
341 else if (result["message"]["new_chat_member"])
342 {
343 // this is a add member message
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>();
349 message.messageType = MessageNewMember;
350 }
351 else if (result["message"]["left_chat_member"])
352 {
353 // this is a left member message
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>();
359 message.messageType = MessageLeftMember;
360 }
361 else if (result["message"]["document"])
362 {
363 // this is a document message
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>();
367 message.document.file_exists = getFile(message.document);
368 message.messageType = MessageDocument;
369 }
370 else if (result["message"]["reply_to_message"])
371 {
372 // this is a reply to message
373 message.text = result["message"]["text"].as<String>();
374 message.messageType = MessageReply;
375 }
376 else if (result["message"]["text"])
377 {
378 // this is a text message
379 message.text = result["message"]["text"].as<String>();
380 message.messageType = MessageText;
381 }
382 }
383 m_lastSentMsgId = message.messageID;
384 return message.messageType;
385 }
386 return MessageNoData; // waiting for reply from server
387}
388
389
390// Blocking getMe function (we wait for a reply from Telegram server)
391 bool AsyncTelegram2::getMe()
392{
393 // getMe has to be blocking (wait server reply)
394 if (!sendCommand("getMe", "", true))
395 {
396 log_error("getMe error ");
397 return false;
398 }
399 StaticJsonDocument<BUFFER_SMALL> smallDoc;
400 deserializeJson(smallDoc, m_rxbuffer);
401 debugJson(smallDoc, Serial);
402 m_botusername = smallDoc["result"]["username"].as<String>();
403 return true;
404}
405
406 bool AsyncTelegram2::getFile(TBDocument &doc)
407{
408 char cmd[BUFFER_SMALL];
409 snprintf(cmd, BUFFER_SMALL, "getFile?file_id=%s", doc.file_id.c_str());
410
411 // getFile has to be blocking (wait server reply
412 if (!sendCommand(cmd, "", true))
413 {
414 log_error("getFile error");
415 return false;
416 }
417 StaticJsonDocument<BUFFER_MEDIUM> fileDoc;
418 deserializeJson(fileDoc, m_rxbuffer);
419 debugJson(fileDoc, Serial);
420 doc.file_path = "https://api.telegram.org/file/bot";
421 doc.file_path += m_token;
422 doc.file_path += "/";
423 doc.file_path += fileDoc["result"]["file_path"].as<String>();
424 doc.file_size = fileDoc["result"]["file_size"].as<long>();
425 return true;
426}
427
428 bool AsyncTelegram2::noNewMessage()
429{
430
431 TBMessage msg;
432 this->reset();
433 this->getNewMessage(msg);
434 while (!this->getUpdates())
435 {
436 delay(100);
437 // if(millis() - startTime > 10000UL)
438 // break;
439 }
440 // log_debug("\n");
441 return true;
442}
443
444 bool AsyncTelegram2::sendMessage(const TBMessage &msg, const char *message, const char *keyboard, bool wait)
445{
446
447 if (!strlen(message))
448 return false;
449 m_waitSent = true;
450 m_lastSentTime = millis();
451 m_lastSentMsgId += 1;
452
453 DynamicJsonDocument root(BUFFER_BIG);
454 // Backward compatibility
455 // root["chat_id"] = msg.sender.id != 0 ? msg.sender.id : msg.chatId;
456 root["chat_id"] = msg.chatId;
457 root["text"] = message;
458
459 switch (m_formatType)
460 {
461 case FormatStyle::NONE:
462 break;
463 case FormatStyle::HTML:
464 root["parse_mode"] = "HTML";
465 break;
466 case FormatStyle::MARKDOWN:
467 root["parse_mode"] = "MarkdownV2";
468 break;
469 }
470
471 if (msg.disable_notification)
472 root["disable_notification"] = true;
473
474 if (keyboard != nullptr)
475 {
476 if (strlen(keyboard) || msg.force_reply)
477 {
478 // DynamicJsonDocument doc(BUFFER_BIG);
479
480#if defined(ESP8266)
481 DynamicJsonDocument doc(ESP.getMaxFreeBlockSize() - BUFFER_MEDIUM);
482#elif defined(ESP32)
483 DynamicJsonDocument doc(ESP.getMaxAllocHeap());
484#else
485 DynamicJsonDocument doc(BUFFER_BIG);
486#endif
487
488 deserializeJson(doc, keyboard);
489 JsonObject myKeyb = doc.as<JsonObject>();
490 root["reply_markup"] = myKeyb;
491 if (msg.force_reply)
492 {
493 root["reply_markup"]["selective"] = true;
494 root["reply_markup"]["force_reply"] = true;
495 }
496 }
497 }
498 root.shrinkToFit();
499
500 size_t len = measureJson(root);
501 char payload[len];
502 serializeJson(root, payload, len);
503
504 debugJson(root, Serial);
505 bool result = sendCommand("sendMessage", payload, wait);
506 return result;
507}
508
509 bool AsyncTelegram2::forwardMessage(const TBMessage &msg, const int64_t to_chatid)
510{
511 char payload[BUFFER_SMALL];
512 snprintf(payload, BUFFER_SMALL,
513 "{\"chat_id\":%lld,\"from_chat_id\":%lld,\"message_id\":%" INT32 "}",
514 to_chatid, msg.chatId, msg.messageID);
515
516 bool result = sendCommand("forwardMessage", payload);
517 log_debug("%s\n", payload);
518 m_lastUpdateTime = millis();
519 return result;
520}
521
522 bool AsyncTelegram2::sendPhotoByUrl(const int64_t &chat_id, const char *url, const char *caption)
523{
524 if (!strlen(url))
525 return false;
526
527 char payload[BUFFER_SMALL];
528 snprintf(payload, BUFFER_SMALL,
529 "{\"chat_id\":%lld,\"photo\":\"%s\",\"caption\":\"%s\"}",
530 chat_id, url, caption);
531
532 bool result = sendCommand("sendPhoto", payload);
533 log_debug("%s", payload);
534 return result;
535}
536
537 bool AsyncTelegram2::sendAnimationByUrl(const int64_t &chat_id, const char *url, const char *caption)
538{
539 if (!strlen(url))
540 return false;
541
542 char payload[BUFFER_SMALL];
543 snprintf(payload, BUFFER_SMALL,
544 "{\"chat_id\":%lld,\"video\":\"%s\",\"caption\":\"%s\"}",
545 chat_id, url, caption);
546
547 bool result = sendCommand("sendVideo", payload);
548 log_debug("%s", payload);
549 return result;
550}
551
552 bool AsyncTelegram2::sendToChannel(const char *channel, const char *message, bool silent)
553{
554 if (!strlen(message))
555 return false;
556
557 DynamicJsonDocument root(BUFFER_BIG);
558 root["chat_id"] = channel;
559 root["text"] = message;
560 root["silent"] = silent ? "true" : "false";
561
562 switch (m_formatType)
563 {
564 case FormatStyle::NONE:
565 break;
566 case FormatStyle::HTML:
567 root["parse_mode"] = "HTML";
568 break;
569 case FormatStyle::MARKDOWN:
570 root["parse_mode"] = "MarkdownV2";
571 break;
572 }
573
574 size_t len = measureJson(root);
575 char payload[len];
576 serializeJson(root, payload, len);
577 payload[len] = '\0';
578 log_debug("%s", payload);
579
580 bool result = sendCommand("sendMessage", payload);
581
582 return result;
583}
584
585 bool AsyncTelegram2::endQuery(const TBMessage &msg, const char *message, bool alertMode)
586{
587 if (!msg.callbackQueryID)
588 return false;
589 char payload[BUFFER_SMALL];
590 snprintf(payload, BUFFER_SMALL,
591 "{\"callback_query_id\":%lld,\"text\":\"%s\",\"cache_time\":2,\"show_alert\":%s}",
592 msg.callbackQueryID, message, alertMode ? "true" : "false");
593 bool result = sendCommand("answerCallbackQuery", payload, true);
594 return result;
595}
596
597 bool AsyncTelegram2::removeReplyKeyboard(const TBMessage &msg, const char *message, bool selective)
598{
599 char payload[BUFFER_SMALL];
600 snprintf(payload, BUFFER_SMALL,
601 "{\"remove_keyboard\":true,\"selective\":%s}", selective ? "true" : "false");
602 bool result = sendMessage(msg, message, payload);
603 return result;
604}
605
606// enum DocumentType { DOCUMENT, PHOTO, ANIMATION, AUDIO, VOICE, VIDEO};
607 bool AsyncTelegram2::sendDocument(int64_t chat_id, Stream &stream, size_t size,
608 DocumentType doc, const char *filename, const char *caption)
609{
610 switch (doc)
611 {
612 case JSON:
613 return sendStream(chat_id, "sendDocument", "application/json", "document", stream, size, filename, caption);
614 case CSV:
615 return sendStream(chat_id, "sendDocument", "text/csv", "document", stream, size, filename, caption);
616 case ZIP:
617 return sendStream(chat_id, "sendDocument", "application/zip", "document", stream, size, filename, caption);
618 case PDF:
619 return sendStream(chat_id, "sendDocument", "application/pdf", "document", stream, size, filename, caption);
620 case PHOTO:
621 return sendStream(chat_id, "sendPhoto", "image/jpeg", "photo", stream, size, filename, caption);
622 case AUDIO:
623 return sendStream(chat_id, "sendDocument", "audio/mp3", "audio", stream, size, filename, caption);
624 default:
625 return sendStream(chat_id, "sendDocument", "text/plain", "document", stream, size, filename, caption);
626 }
627
628 return false;
629}
630
631 void 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)
634{
635
636#define BOUNDARY "----WebKitFormBoundary7MA4YWxkTrZu0gW"
637#define END_BOUNDARY "\r\n--" BOUNDARY "--\r\n"
638
639 char int64_buf[22] = {0};
640 snprintf(int64_buf, sizeof(int64_buf), "%lld", chat_id);
641
642 formData = "--" BOUNDARY "\r\nContent-disposition: form-data; name=\"chat_id\"\r\n\r\n";
643 formData += int64_buf;
644
645 if (caption != nullptr)
646 {
647 formData += "\r\n--" BOUNDARY "\r\nContent-disposition: form-data; name=\"caption\"\r\n\r\n";
648 formData += caption;
649 }
650
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: ";
656 formData += type;
657 formData += "\"\r\n\r\n";
658 int contentLength = size + formData.length() + strlen(END_BOUNDARY);
659
660 request = "POST /bot";
661 request += m_token;
662 request += "/";
663 request += cmd;
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";
667}
668
669 bool 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)
671{
672 bool res = false;
673 if (checkConnection())
674 {
675 m_waitingReply = true;
676 String formData;
677 formData.reserve(512);
678 String request;
679 request.reserve(256);
680 setformData(chat_id, cmd, type, propName, size, formData, request, filename, caption);
681
682#if DEBUG_ENABLE
683 uint32_t t1 = millis();
684 Serial.println(request);
685 Serial.println(formData);
686#endif
687 // Send POST request to host
688 telegramClient->println(request);
689 // Body of request
690 telegramClient->print(formData);
691
692 uint8_t data[BLOCK_SIZE];
693 int n_block = trunc(size / BLOCK_SIZE);
694 int lastBytes = size - (n_block * BLOCK_SIZE);
695
696 for (uint16_t pos = 0; pos < n_block; pos++)
697 {
698 stream.readBytes(data, BLOCK_SIZE);
699 telegramClient->write(data, BLOCK_SIZE);
700 yield();
701 }
702 stream.readBytes(data, lastBytes);
703 telegramClient->write(data, lastBytes);
704
705 // Close the request form-data
706 telegramClient->println(END_BOUNDARY);
707 telegramClient->flush();
708
709#if DEBUG_ENABLE
710 log_debug("Raw upload time: %lums\n", millis() - t1);
711 t1 = millis();
712#endif
713
714 // Handle reply with getUpdates() method
715 }
716 else
717 Serial.println("\nError: client not connected");
718 return res;
719}
720
721 bool AsyncTelegram2::sendBuffer(int64_t chat_id, const char *cmd, const char *type, const char *propName, uint8_t *data, size_t size, const char *caption)
722{
723 bool res = false;
724 if (checkConnection())
725 {
726 m_waitingReply = true;
727 String formData;
728 formData.reserve(512);
729 String request;
730 request.reserve(256);
731 setformData(chat_id, cmd, type, propName, size, formData, request, caption, caption);
732
733#if DEBUG_ENABLE
734 uint32_t t1 = millis();
735#endif
736 // Send POST request to host
737 telegramClient->println(request);
738 // Body of request
739 telegramClient->print(formData);
740
741 // Serial.println(telegramClient->write((const uint8_t *) data, size));
742 uint16_t pos = 0;
743 int n_block = trunc(size / BLOCK_SIZE);
744 int lastBytes = size - (n_block * BLOCK_SIZE);
745
746 for (pos = 0; pos < n_block; pos++)
747 {
748 telegramClient->write((const uint8_t *)data + pos * BLOCK_SIZE, BLOCK_SIZE);
749 yield();
750 }
751 telegramClient->write((const uint8_t *)data + pos * BLOCK_SIZE, lastBytes);
752
753 // Close the request form-data
754 telegramClient->println(END_BOUNDARY);
755 telegramClient->flush();
756
757#if DEBUG_ENABLE
758 log_debug("Raw upload time: %lums\n", millis() - t1);
759 t1 = millis();
760#endif
761
762 }
763 else
764 Serial.println("\nError: client not connected");
765 return res;
766}
767
768 void AsyncTelegram2::getMyCommands(String &cmdList)
769{
770 if (!sendCommand("getMyCommands", "", true))
771 {
772 log_error("getMyCommands error ");
773 return;
774 }
775 StaticJsonDocument<BUFFER_MEDIUM> doc;
776 DeserializationError err = deserializeJson(doc, m_rxbuffer);
777 if (err)
778 {
779 return;
780 }
781 debugJson(doc, Serial);
782 // cmdList = doc["result"].as<String>();
783 serializeJsonPretty(doc["result"], cmdList);
784}
785
786 bool AsyncTelegram2::deleteMyCommands()
787{
788 if (!sendCommand("deleteMyCommands", "", true))
789 {
790 log_error("getMyCommands error ");
791 return "";
792 }
793 return true;
794}
795
796 bool AsyncTelegram2::setMyCommands(const String &cmd, const String &desc)
797{
798
799 // get actual list of commands
800 if (!sendCommand("getMyCommands", "", true))
801 {
802 log_error("getMyCommands error ");
803 return "";
804 }
805 DynamicJsonDocument doc(BUFFER_MEDIUM);
806 DeserializationError err = deserializeJson(doc, m_rxbuffer);
807 if (err)
808 {
809 return false;
810 }
811
812 // Check if command already present in list
813 for (JsonObject key : doc["result"].as<JsonArray>())
814 {
815 if (key["command"] == cmd)
816 {
817 return false;
818 }
819 }
820
821 StaticJsonDocument<256> obj;
822 obj["command"] = cmd;
823 obj["description"] = desc;
824 doc["result"].as<JsonArray>().add(obj);
825
826 StaticJsonDocument<BUFFER_MEDIUM> doc2;
827 doc2["commands"] = doc["result"].as<JsonArray>();
828
829 size_t len = measureJson(doc2);
830 char payload[len + 1];
831 serializeJson(doc2, payload, len);
832 debugJson(doc2, Serial);
833
834 bool result = sendCommand("setMyCommands", payload, true);
835 return result;
836}
837
838 bool AsyncTelegram2::editMessage(int32_t chat_id, int32_t message_id, const String &txt, const String &keyboard)
839{
840 String payload = "{\"chat_id\":";
841 payload += chat_id;
842 payload += ",\"message_id\":";
843 payload += message_id;
844 payload += ", \"text\": \"";
845 payload += txt;
846
847 if (keyboard.length())
848 {
849 payload += "\", \"reply_markup\": ";
850 payload += keyboard;
851 payload += "}";
852 }
853 else
854 {
855 payload += "\"}";
856 }
857
858 bool result = sendCommand("editMessageText", payload.c_str());
859 return result;
860}
END_BOUNDARY
#define END_BOUNDARY
BOUNDARY
#define BOUNDARY
HEADERS_END
#define HEADERS_END
Definition: AsyncTelegram2.cpp:3
AsyncTelegram2.h
TELEGRAM_PORT
#define TELEGRAM_PORT
Definition: AsyncTelegram2.h:63
MIN_UPDATE_TIME
#define MIN_UPDATE_TIME
Definition: AsyncTelegram2.h:52
INT32
#define INT32
Definition: AsyncTelegram2.h:41
BLOCK_SIZE
#define BLOCK_SIZE
Definition: AsyncTelegram2.h:54
TELEGRAM_HOST
#define TELEGRAM_HOST
Definition: AsyncTelegram2.h:61
BUFFER_MEDIUM
#define BUFFER_MEDIUM
Definition: DataStructures.h:6
BUFFER_BIG
#define BUFFER_BIG
Definition: DataStructures.h:5
MessageType
MessageType
Definition: DataStructures.h:9
MessageDocument
@ MessageDocument
Definition: DataStructures.h:15
MessageNewMember
@ MessageNewMember
Definition: DataStructures.h:17
MessageForwarded
@ MessageForwarded
Definition: DataStructures.h:19
MessageText
@ MessageText
Definition: DataStructures.h:11
MessageNoData
@ MessageNoData
Definition: DataStructures.h:10
MessageReply
@ MessageReply
Definition: DataStructures.h:16
MessageLocation
@ MessageLocation
Definition: DataStructures.h:13
MessageQuery
@ MessageQuery
Definition: DataStructures.h:12
MessageContact
@ MessageContact
Definition: DataStructures.h:14
MessageLeftMember
@ MessageLeftMember
Definition: DataStructures.h:18
BUFFER_SMALL
#define BUFFER_SMALL
Definition: DataStructures.h:7
AsyncTelegram2::setMyCommands
bool setMyCommands(const String &cmd, const String &desc)
Definition: AsyncTelegram2.cpp:796
AsyncTelegram2::sendAnimationByUrl
bool sendAnimationByUrl(const int64_t &chat_id, const char *url, const char *caption)
Definition: AsyncTelegram2.cpp:537
AsyncTelegram2::getMyCommands
void getMyCommands(String &cmdList)
Definition: AsyncTelegram2.cpp:768
AsyncTelegram2::NONE
@ NONE
Definition: AsyncTelegram2.h:425
AsyncTelegram2::HTML
@ HTML
Definition: AsyncTelegram2.h:426
AsyncTelegram2::MARKDOWN
@ MARKDOWN
Definition: AsyncTelegram2.h:427
AsyncTelegram2::begin
bool begin(void)
Definition: AsyncTelegram2.cpp:52
AsyncTelegram2::getFile
bool getFile(TBDocument &doc)
Definition: AsyncTelegram2.cpp:406
AsyncTelegram2::sendTo
bool sendTo(const int64_t userid, const char *message, const char *keyboard=nullptr)
Definition: AsyncTelegram2.h:183
AsyncTelegram2::DocumentType
DocumentType
Definition: AsyncTelegram2.h:197
AsyncTelegram2::CSV
@ CSV
Definition: AsyncTelegram2.h:205
AsyncTelegram2::ZIP
@ ZIP
Definition: AsyncTelegram2.h:198
AsyncTelegram2::PHOTO
@ PHOTO
Definition: AsyncTelegram2.h:200
AsyncTelegram2::AUDIO
@ AUDIO
Definition: AsyncTelegram2.h:202
AsyncTelegram2::JSON
@ JSON
Definition: AsyncTelegram2.h:206
AsyncTelegram2::PDF
@ PDF
Definition: AsyncTelegram2.h:199
AsyncTelegram2::checkConnection
bool checkConnection()
Definition: AsyncTelegram2.cpp:15
AsyncTelegram2::endQuery
bool endQuery(const TBMessage &msg, const char *message, bool alertMode=false)
Definition: AsyncTelegram2.cpp:585
AsyncTelegram2::deleteMyCommands
bool deleteMyCommands()
Definition: AsyncTelegram2.cpp:786
AsyncTelegram2::sendPhotoByUrl
bool sendPhotoByUrl(const int64_t &chat_id, const char *url, const char *caption)
Definition: AsyncTelegram2.cpp:522
AsyncTelegram2::getUpdates
bool getUpdates()
Definition: AsyncTelegram2.cpp:119
AsyncTelegram2::~AsyncTelegram2
~AsyncTelegram2()
Definition: AsyncTelegram2.cpp:13
AsyncTelegram2::sendDocument
bool sendDocument(int64_t chat_id, Stream &stream, size_t size, DocumentType doc, const char *filename, const char *caption=nullptr)
Definition: AsyncTelegram2.cpp:607
AsyncTelegram2::getNewMessage
MessageType getNewMessage(TBMessage &message)
Definition: AsyncTelegram2.cpp:209
AsyncTelegram2::sendCommand
bool sendCommand(const char *command, const char *payload, bool blocking=false)
Definition: AsyncTelegram2.cpp:71
AsyncTelegram2::sendMessage
bool sendMessage(const TBMessage &msg, const char *message, const char *keyboard=nullptr, bool wait=false)
Definition: AsyncTelegram2.cpp:444
AsyncTelegram2::reset
bool reset(void)
Definition: AsyncTelegram2.cpp:58
AsyncTelegram2::sendToChannel
bool sendToChannel(const char *channel, const char *message, bool silent=false)
Definition: AsyncTelegram2.cpp:552
AsyncTelegram2::AsyncTelegram2
AsyncTelegram2(Client &client, uint32_t bufferSize=BUFFER_BIG)
Definition: AsyncTelegram2.cpp:5
AsyncTelegram2::noNewMessage
bool noNewMessage()
Definition: AsyncTelegram2.cpp:428
AsyncTelegram2::removeReplyKeyboard
bool removeReplyKeyboard(const TBMessage &msg, const char *message, bool selective=false)
Definition: AsyncTelegram2.cpp:597
AsyncTelegram2::getMe
bool getMe()
Definition: AsyncTelegram2.cpp:391
AsyncTelegram2::forwardMessage
bool forwardMessage(const TBMessage &msg, const int64_t to_chatid)
Definition: AsyncTelegram2.cpp:509
AsyncTelegram2::editMessage
bool editMessage(int32_t chat_id, int32_t message_id, const String &txt, const String &keyboard)
Definition: AsyncTelegram2.cpp:838
log_debug
#define log_debug(format,...)
Definition: serial_log.h:44
log_error
#define log_error(x)
Definition: serial_log.h:47
debugJson
#define debugJson(X, Y)
Definition: serial_log.h:49
log_info
#define log_info(x)
Definition: serial_log.h:46
TBContact::phoneNumber
String phoneNumber
Definition: DataStructures.h:37
TBContact::firstName
String firstName
Definition: DataStructures.h:38
TBContact::vCard
String vCard
Definition: DataStructures.h:40
TBContact::lastName
String lastName
Definition: DataStructures.h:39
TBContact::id
int64_t id
Definition: DataStructures.h:36
TBDocument
Definition: DataStructures.h:43
TBDocument::file_size
int32_t file_size
Definition: DataStructures.h:45
TBDocument::file_exists
bool file_exists
Definition: DataStructures.h:44
TBDocument::file_path
String file_path
Definition: DataStructures.h:48
TBDocument::file_name
String file_name
Definition: DataStructures.h:47
TBDocument::file_id
String file_id
Definition: DataStructures.h:46
TBLocation::latitude
float latitude
Definition: DataStructures.h:32
TBLocation::longitude
float longitude
Definition: DataStructures.h:31
TBMessage
Definition: DataStructures.h:51
TBMessage::contact
TBContact contact
Definition: DataStructures.h:64
TBMessage::member
TBUser member
Definition: DataStructures.h:62
TBMessage::text
String text
Definition: DataStructures.h:68
TBMessage::disable_notification
bool disable_notification
Definition: DataStructures.h:55
TBMessage::chatId
int64_t chatId
Definition: DataStructures.h:59
TBMessage::document
TBDocument document
Definition: DataStructures.h:65
TBMessage::callbackQueryID
int64_t callbackQueryID
Definition: DataStructures.h:66
TBMessage::date
int32_t date
Definition: DataStructures.h:57
TBMessage::callbackQueryData
String callbackQueryData
Definition: DataStructures.h:67
TBMessage::sender
TBUser sender
Definition: DataStructures.h:61
TBMessage::chatInstance
int32_t chatInstance
Definition: DataStructures.h:58
TBMessage::messageType
MessageType messageType
Definition: DataStructures.h:52
TBMessage::location
TBLocation location
Definition: DataStructures.h:63
TBMessage::force_reply
bool force_reply
Definition: DataStructures.h:56
TBMessage::messageID
int32_t messageID
Definition: DataStructures.h:60
TBUser::firstName
String firstName
Definition: DataStructures.h:25
TBUser::id
int64_t id
Definition: DataStructures.h:24
TBUser::isBot
bool isBot
Definition: DataStructures.h:23
TBUser::username
String username
Definition: DataStructures.h:27
TBUser::lastName
String lastName
Definition: DataStructures.h:26