1616 - Поддержка Unicode (другие языки + эмодзи) для входящих сообщений
1717 - Встроенный urlencode для исходящих сообщений
1818 - Встроенные часы реального времени с синхронизацией от сервера Telegram
19+ - Возможность OTA обновления прошивки файлом из чата Telegram
1920
2021 AlexGyver, alex@alexgyver.ru
2122 https://alexgyver.ru/
7879 - usrID и ID переименованы в userID и messageID (с сохранением легаси)
7980 - Окончательно убран старый обработчик входящих сообщений
8081 v2.12: поправлены примеры, исправлен парсинг isBot, переделан механизм защиты от длинных сообщений, переделана инициализация
82+ v2.13: Оптимизация памяти. Добавил OTA обновление
8183*/
8284
8385/*
8486 Статусы tick:
8587 0 - ожидание
8688 1 - ОК
87- 2 - Переполнен по ovf
89+ 2 - Переполнен
8890 3 - Ошибка телеграм
8991 4 - Ошибка подключения
9092 5 - не задан chat ID
103105#define FB_ALERT 1
104106
105107#include < Arduino.h>
108+ #include < StreamString.h>
109+ #include " utils.h"
110+
106111#ifdef ESP8266
107112#include < ESP8266WiFi.h>
108113#include < ESP8266HTTPClient.h>
114+ #include < ESP8266httpUpdate.h>
109115#include < WiFiClientSecure.h>
110116#include < WiFiClientSecureBearSSL.h>
111117static BearSSL::WiFiClientSecure _FB_client;
112- static HTTPClient _FB_httpGet, _FB_httpReq ;
118+ static HTTPClient _FB_http ;
113119#else
114120#include < WiFi.h>
115121#include < HTTPClient.h>
122+ #include < HTTPUpdate.h>
116123#include < WiFiClientSecure.h>
117124WiFiClientSecure _FB_client;
118- static HTTPClient _FB_httpGet;
119- #define _FB_httpReq _FB_httpGet
125+ static HTTPClient _FB_http;
120126#endif
121127
122- #include " utils.h"
123-
124128// ================================
125129class FastBot {
126130public:
@@ -134,7 +138,6 @@ class FastBot {
134138 _prd = period;
135139 setBufferSizes (512 , 512 );
136140 _FB_client.setInsecure ();
137- // _FB_httpGet.setTimeout(500);
138141 }
139142
140143 // ===================== НАСТРОЙКИ =====================
@@ -188,18 +191,42 @@ class FastBot {
188191 // ручная проверка обновлений
189192 uint8_t tickManual () {
190193 if (!*_callback) return 7 ;
191- uint8_t status = 1 ;
192194 String req;
193195 _addToken (req);
194196 req += F (" /getUpdates?limit=" );
195- req += ovfFlag ? 1 : _limit; // берём по 1 сообщению если переполнены
197+ req += ovfFlag ? 1 : _limit; // берём по 1 сообщению если переполнен
196198 req += F (" &offset=" );
197199 req += ID;
198- if (_FB_httpGet.begin (_FB_client, req)) {
199- if (_FB_httpGet.GET () == HTTP_CODE_OK) status = parse (_FB_httpGet.getString (), _FB_httpGet.getSize ());
200- else status = 3 ;
201- _FB_httpGet.end ();
202- } else status = 4 ;
200+
201+ if (!_FB_http.begin (_FB_client, req)) return 4 ; // ошибка подключения
202+ if (_FB_http.GET () != HTTP_CODE_OK) {
203+ _FB_http.end ();
204+ return 3 ; // ошибка сервера телеграм
205+ }
206+
207+ // была попытка OTA обновления. Обновляемся после ответа серверу!
208+ if (OTAstate >= 0 ) {
209+ String ota;
210+ if (OTAstate == 0 ) ota = F (" error" );
211+ else if (OTAstate == 1 ) ota = F (" no updates" );
212+ else if (OTAstate == 2 ) ota = F (" OK" );
213+ sendMessage (ota, _otaID);
214+ if (OTAstate == 2 ) ESP.restart ();
215+ OTAstate = -1 ;
216+ }
217+
218+ int size = _FB_http.getSize ();
219+ ovfFlag = size > 25000 ; // 1 полное сообщение на русском языке или ~5 на английском
220+ uint8_t status = 1 ; // OK
221+ if (size) { // не пустой ответ?
222+ StreamString sstring;
223+ if (!ovfFlag && sstring.reserve (size + 1 )) { // не переполнен и хватает памяти
224+ _FB_http.writeToStream (&sstring); // копируем
225+ _FB_http.end (); // завершаем
226+ return parseMessages (sstring); // парсим
227+ } else status = 2 ; // переполнение
228+ } else status = 3 ; // пустой ответ
229+ _FB_http.end ();
203230 return status;
204231 }
205232
@@ -360,32 +387,32 @@ class FastBot {
360387
361388 // ответить на callback текстом и true - предупреждением
362389 uint8_t answer () {
363- if (!_query ) return 5 ;
390+ if (!_query_ptr ) return 5 ;
364391 String req;
365392 _addToken (req);
366393 req += F (" /answerCallbackQuery?callback_query_id=" );
367- req += *_query ;
368- _query = nullptr ;
394+ req += *_query_ptr ;
395+ _query_ptr = nullptr ;
369396 return sendRequest (req);
370397 }
371398
372399 uint8_t answer (const String& text, bool alert = false ) {
373- if (!_query ) return 5 ;
400+ if (!_query_ptr ) return 5 ;
374401 #ifndef FB_NO_URLENCODE
375402 String utext;
376403 FB_urlencode (text, utext);
377404 #endif
378405 String req;
379406 _addToken (req);
380407 req += F (" /answerCallbackQuery?callback_query_id=" );
381- req += *_query ;
408+ req += *_query_ptr ;
382409 #ifndef FB_NO_URLENCODE
383410 _addText (req, utext);
384411 #else
385412 _addText (req, text);
386413 #endif
387414 if (alert) req += F (" &show_alert=True" );
388- _query = nullptr ;
415+ _query_ptr = nullptr ;
389416 return sendRequest (req);
390417 }
391418
@@ -603,21 +630,12 @@ class FastBot {
603630 }
604631
605632 uint8_t sendRequest (String& req) {
633+ if (!_FB_http.begin (_FB_client, req)) return 4 ; // ошибка подключения
606634 uint8_t status = 1 ;
607- if (_FB_httpReq.begin (_FB_client, req)) {
608- if (_FB_httpReq.GET () != HTTP_CODE_OK) status = 3 ;
609- const String& answ = _FB_httpReq.getString ();
610- int16_t st = 0 ;
611- String buf;
612- if (find (answ, buf, st, F (" \" message_id\" :" ), ' ,' , answ.length ())) {
613- _lastBotMsg = buf.toInt ();
614- }
615- if (find (answ, buf, st, F (" \" date\" :" ), ' ,' , answ.length ())) {
616- _unix = buf.toInt ();
617- _lastUpd = millis ();
618- }
619- _FB_httpReq.end ();
620- } else status = 4 ;
635+ if (_FB_http.GET () == HTTP_CODE_OK && _FB_http.getSize ()) { // есть ответ и он не пустой
636+ parseRequest (_FB_http.getString ()); // парсим
637+ } else status = 3 ; // некорректный ответ
638+ _FB_http.end ();
621639 return status;
622640 }
623641
@@ -658,6 +676,17 @@ class FastBot {
658676 bool timeSynced () {
659677 return _unix;
660678 }
679+
680+ uint8_t update () {
681+ if (!_file_ptr) return 5 ;
682+ sendMessage (F (" OTA update..." ), _otaID);
683+ String req;
684+ _addToken (req);
685+ req += F (" /getFile?file_id=" );
686+ req += *_file_ptr;
687+ _file_ptr = nullptr ;
688+ return sendRequest (req);
689+ }
661690
662691private:
663692 // ================ BUILDER ===============
@@ -731,9 +760,7 @@ class FastBot {
731760 return (strPos > 0 ) && (strPos < end);
732761 }
733762
734- uint8_t parse (const String& str, uint16_t size) {
735- ovfFlag = size > 25000 ; // 1 полное сообщение на русском языке или ~5 на английском
736- if (ovfFlag) return 2 ;
763+ uint8_t parseMessages (const String& str) {
737764 if (!str.startsWith (F (" {\" ok\" :true" ))) return 3 ; // ошибка запроса (неправильный токен итд)
738765 int16_t IDpos = str.indexOf (F (" {\" update_id\" :" ), 0 ); // первая позиция ключа update_id
739766
@@ -749,10 +776,11 @@ class FastBot {
749776
750777 String query;
751778 int16_t queryEnd = 0 ;
779+ if (_query_ptr) _query_ptr = nullptr ;
752780 if (find (str, query, textPos, F (" \" callback_query\" :{\" id\" :\" " ), ' ,' , IDpos)) {
753- _query = &query;
781+ _query_ptr = &query;
754782 queryEnd = textPos;
755- } else _query = nullptr ;
783+ }
756784
757785 bool edited = find (str, F (" \" edited_message\" :" ), textPos, IDpos);
758786
@@ -778,6 +806,16 @@ class FastBot {
778806 String date;
779807 find (str, date, textPos, F (" \" date\" :" ), ' ,' , IDpos);
780808
809+ String file;
810+ if (_file_ptr) _file_ptr = nullptr ;
811+ if (find (str, file, textPos, F (" \" file_name\" :\" " ), ' \" ' , IDpos)) {
812+ if (file.endsWith (F (" .bin" ))) {
813+ find (str, file, textPos, F (" \" file_id\" :\" " ), ' \" ' , IDpos);
814+ _file_ptr = &file;
815+ _otaID = chatID;
816+ }
817+ }
818+
781819 // удаление сервисных сообщений
782820 if (clrSrv) {
783821 if (find (str, F (" \" new_chat_title\" " ), textPos, IDpos) ||
@@ -788,7 +826,8 @@ class FastBot {
788826 }
789827
790828 String text;
791- find (str, text, textPos, F (" \" text\" :\" " ), ' \" ' , IDpos);
829+ if (_file_ptr) find (str, text, textPos, F (" \" caption\" :\" " ), ' \" ' , IDpos);
830+ else find (str, text, textPos, F (" \" text\" :\" " ), ' \" ' , IDpos);
792831
793832 String data;
794833 find (str, data, textPos, F (" \" data\" :\" " ), ' \" ' , IDpos);
@@ -805,9 +844,10 @@ class FastBot {
805844 _lastUsrMsg,
806845 text,
807846 data,
808- (bool )_query ,
847+ (bool )_query_ptr ,
809848 edited,
810849 is_bot[0 ] == ' t' ,
850+ (bool )_file_ptr,
811851 (uint32_t )date.toInt (),
812852
813853 // legacy
@@ -817,16 +857,46 @@ class FastBot {
817857 _lastUsrMsg,
818858 };
819859 _callback (msg);
820- if (_query) answer (); // отвечаем на коллбэк, если юзер не ответил
860+ if (_query_ptr) answer (); // отвечаем на коллбэк, если юзер не ответил
861+ if (OTAstate >= 0 ) break ; // обновление! выходим
821862 yield ();
822863 }
823864 if (_incr) ID += counter;
824865 return 1 ;
825866 }
867+
868+ void parseRequest (const String& answ) {
869+ int16_t st = 0 ;
870+ String buf;
871+ if (find (answ, buf, st, F (" \" message_id\" :" ), ' ,' , answ.length ())) {
872+ _lastBotMsg = buf.toInt ();
873+ }
874+ if (find (answ, buf, st, F (" \" date\" :" ), ' ,' , answ.length ())) {
875+ _unix = buf.toInt ();
876+ _lastUpd = millis ();
877+ }
878+ if (find (answ, buf, st, F (" \" file_path\" :\" " ), ' \" ' , answ.length ())) {
879+ String url (F (" https://api.telegram.org/file/bot" ));
880+ url += _token;
881+ url += ' /' ;
882+ url += buf;
883+ #ifdef ESP8266
884+ ESPhttpUpdate.rebootOnUpdate (false );
885+ OTAstate = ESPhttpUpdate.update (_FB_client, url);
886+ #else
887+ WiFiClientSecure client;
888+ client.setInsecure ();
889+ httpUpdate.rebootOnUpdate (false );
890+ OTAstate = httpUpdate.update (client, url);
891+ #endif
892+ }
893+ }
826894
827895 void (*_callback)(FB_msg& msg) = nullptr ;
828896 String _token;
829- String* _query = nullptr ;
897+ String _otaID;
898+ String* _file_ptr = nullptr ;
899+ String* _query_ptr = nullptr ;
830900 uint16_t _ovf, _prd, _limit;
831901 int32_t ID = 0 ;
832902 uint32_t tmr = 0 ;
@@ -836,6 +906,7 @@ class FastBot {
836906 bool notif = true ;
837907 bool clrSrv = false ;
838908 bool ovfFlag = 0 ;
909+ int8_t OTAstate = -1 ;
839910
840911 uint32_t _unix = 0 ;
841912 uint32_t _lastUpd = 0 ;
0 commit comments