├── .github └── FUNDING.yml ├── README.md ├── esp32InlineArticleBot.ino ├── UniversalTelegramBot1_2.h ├── UniversalTelegramBotRZO.h ├── TelegramSendPhotoChunked1_2.ino ├── esp32UploadTimedBot.ino ├── esp32InlinePhotoUpload.ino ├── esp32InlineStickerBot.ino ├── UniversalTelegramBot1_2.cpp └── UniversalTelegramBotRZO.cpp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: robotzero # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esp32cam-telegram 2 | 3 | Some Arduino scripts to do various things with ESP32 based modules and the Telegram Bot API. 4 | 5 | ### TelegramSendPhotoChunked1_2.ino 6 | Use an ESP32-CAM to send photos on request to Telegram via a Bot. Details on the blog here: https://robotzero.one/telegram-bot-esp32cam/ 7 | 8 | ### esp32UploadTimedBot.ino 9 | Use this to create a bot that takes and uploads a photo every X minutes. 10 | 11 | ### esp32InlinePhotoUpload.ino 12 | Taking a photo using 'inline' mode so the bot can be accessed in any chat. 13 | 14 | ### esp32InlineArticleBot.ino 15 | Using the Inline Article mode of Telegram Bots to request sensor data and send any important ones into the chat. Works with any ESP32. 16 | 17 | ### esp32InlineStickerBot.ino 18 | This creates an Inline bot that when requested, offers a preset list of stickers to insert into the chat. Works with any ESP32. 19 | 20 | --- 21 | 22 | The following two files are required UniversalTelegramBotRZO.cpp and UniversalTelegramBotRZO.h for all the scripts above. 23 | 24 | UniversalTelegramBot1_2.cpp and UniversalTelegramBot1_2.h are the 1.2 version release of the https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/tree/V1.2.0 library used as the base for the RZO files. 25 | 26 | -------------------------------------------------------------------------------- /esp32InlineArticleBot.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | Inline bot that sends simulated sensor status to any Telegram chat 3 | 4 | written by Giacarlo Bacchio (Gianbacchio on Github) 5 | adapted by Brian Lough 6 | bodged by Robot Zero One 7 | 8 | Using the article message type you can request sensor data and 9 | send any important ones into the chat 10 | *******************************************************************/ 11 | #include 12 | #include 13 | #include "UniversalTelegramBotRZO.h" 14 | 15 | // Initialize Wifi connection to the router 16 | char ssid[] = "NSA"; // your network SSID (name) 17 | char password[] = "orange"; // your network key 18 | 19 | String chat_id; 20 | 21 | // Initialize Telegram BOT 22 | #define BOTtoken "101010101" // your Bot Token (Get from Botfather) 23 | String token = BOTtoken; 24 | 25 | WiFiClientSecure client; 26 | UniversalTelegramBot bot(BOTtoken, client); 27 | 28 | int Bot_mtbs = 3000; //mean time between scan messages 29 | long Bot_lasttime; //last time messages' scan has been done 30 | 31 | 32 | void setup() 33 | { 34 | Serial.begin(115200); 35 | 36 | Serial.print("Connecting Wifi: "); 37 | Serial.println(ssid); 38 | 39 | WiFi.mode(WIFI_STA); 40 | WiFi.begin(ssid, password); 41 | 42 | while (WiFi.status() != WL_CONNECTED) { 43 | Serial.print("."); 44 | delay(500); 45 | } 46 | 47 | Serial.println(""); 48 | Serial.println("WiFi connected"); 49 | Serial.print("IP address: "); 50 | Serial.println(WiFi.localIP()); 51 | } 52 | 53 | 54 | void loop() { 55 | if (millis() > Bot_lasttime + Bot_mtbs) { 56 | int numNewMessages = bot.getUpdates(bot.last_message_received + 1); 57 | 58 | while (numNewMessages) { 59 | Serial.println("got response"); 60 | chat_id = bot.messages[0].chat_id; 61 | 62 | for (int i = 0; i < numNewMessages; i++) { 63 | 64 | // simulate reactor core readings. Don't have access to actual nuclear power station 65 | int reactor_core_reading_1 = random(5); 66 | int reactor_core_reading_2 = random(5); 67 | int reactor_core_reading_3 = random(5); 68 | int reactor_core_reading_4 = random(5); 69 | 70 | String reactor_1_thumburl = "https://robotzero.one/reactor-gifs/" + String(reactor_core_reading_1) + ".gif"; 71 | String reactor_2_thumburl = "https://robotzero.one/reactor-gifs/" + String(reactor_core_reading_2) + ".gif"; 72 | String reactor_3_thumburl = "https://robotzero.one/reactor-gifs/" + String(reactor_core_reading_3) + ".gif"; 73 | String reactor_4_thumburl = "https://robotzero.one/reactor-gifs/" + String(reactor_core_reading_4) + ".gif"; 74 | 75 | if (bot.messages[i].type == "inline_query") { // inline request from Telegram 76 | String reactors_json = "[" 77 | "{\"type\": \"article\", \"id\": \"rndm_1\", \"thumb_url\": \"" + reactor_1_thumburl + "\", \"title\": \"Reactor 1\", \"description\": \"Request Reactor 1 Check\", \"input_message_content\": {\"message_text\": \"Check Reactor One!!\", \"parse_mode\": \"HTML\" }}," 78 | "{\"type\": \"article\", \"id\": \"rndm_2\", \"thumb_url\": \"" + reactor_2_thumburl + "\", \"title\": \"Reactor 2\", \"description\": \"Request Reactor 2 Check\", \"input_message_content\": {\"message_text\": \"Check Reactor Two!!\", \"parse_mode\": \"HTML\" }}," 79 | "{\"type\": \"article\", \"id\": \"rndm_3\", \"thumb_url\": \"" + reactor_3_thumburl + "\", \"title\": \"Reactor 3\", \"description\": \"Request Reactor 3 Check\", \"input_message_content\": {\"message_text\": \"Check Reactor Three!!\", \"parse_mode\": \"HTML\" }}," 80 | "{\"type\": \"article\", \"id\": \"rndm_4\", \"thumb_url\": \"" + reactor_4_thumburl + "\", \"title\": \"Reactor 4\", \"description\": \"Request Reactor 4 Check\", \"input_message_content\": {\"message_text\": \"Check Reactor Four!!\", \"parse_mode\": \"HTML\" }}" 81 | "]"; 82 | String thing = "/bot" + token + "/answerInlineQuery?inline_query_id=" + bot.messages[i].inline_query_id + "&cache_time=5&results=" + reactors_json; 83 | bot.sendGetToTelegram(thing); 84 | } else { 85 | // 86 | } 87 | 88 | } 89 | numNewMessages = bot.getUpdates(bot.last_message_received + 1); 90 | } 91 | Bot_lasttime = millis(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /UniversalTelegramBot1_2.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018 Brian Lough. All right reserved. 3 | 4 | UniversalTelegramBot - Library to create your own Telegram Bot using 5 | ESP8266 or ESP32 on Arduino IDE. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef UniversalTelegramBot1_2_h 23 | #define UniversalTelegramBot1_2_h 24 | 25 | #define ARDUINOJSON_DECODE_UNICODE 1 26 | #define ARDUINOJSON_USE_LONG_LONG 1 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define HOST "api.telegram.org" 33 | #define SSL_PORT 443 34 | #define HANDLE_MESSAGES 1 35 | 36 | //unmark following line to enable debug mode 37 | //#define _debug 38 | 39 | typedef bool (*MoreDataAvailable)(); 40 | typedef byte (*GetNextByte)(); 41 | typedef byte* (*GetNextBuffer)(); 42 | typedef int (GetNextBufferLen)(); 43 | 44 | struct telegramMessage { 45 | String text; 46 | String chat_id; 47 | String chat_title; 48 | String from_id; 49 | String from_name; 50 | String date; 51 | String type; 52 | float longitude; 53 | float latitude; 54 | int update_id; 55 | }; 56 | 57 | class UniversalTelegramBot { 58 | public: 59 | UniversalTelegramBot(String token, Client &client); 60 | String sendGetToTelegram(String command); 61 | String sendPostToTelegram(String command, JsonObject payload); 62 | String 63 | sendMultipartFormDataToTelegram(String command, String binaryProperyName, 64 | String fileName, String contentType, 65 | String chat_id, int fileSize, 66 | MoreDataAvailable moreDataAvailableCallback, 67 | GetNextByte getNextByteCallback, 68 | GetNextBuffer getNextBufferCallback, 69 | GetNextBufferLen getNextBufferLenCallback); 70 | 71 | bool getMe(); 72 | 73 | bool sendSimpleMessage(String chat_id, String text, String parse_mode); 74 | bool sendMessage(String chat_id, String text, String parse_mode = ""); 75 | bool sendMessageWithReplyKeyboard(String chat_id, String text, 76 | String parse_mode, String keyboard, 77 | bool resize = false, bool oneTime = false, 78 | bool selective = false); 79 | bool sendMessageWithInlineKeyboard(String chat_id, String text, 80 | String parse_mode, String keyboard); 81 | 82 | bool sendChatAction(String chat_id, String text); 83 | 84 | bool sendPostMessage(JsonObject payload); 85 | String sendPostPhoto(JsonObject payload); 86 | String sendPhotoByBinary(String chat_id, String contentType, int fileSize, 87 | MoreDataAvailable moreDataAvailableCallback, 88 | GetNextByte getNextByteCallback, 89 | GetNextBuffer getNextBufferCallback, 90 | GetNextBufferLen getNextBufferLenCallback); 91 | String sendPhoto(String chat_id, String photo, String caption = "", 92 | bool disable_notification = false, 93 | int reply_to_message_id = 0, String keyboard = ""); 94 | 95 | int getUpdates(long offset); 96 | bool checkForOkResponse(String response); 97 | telegramMessage messages[HANDLE_MESSAGES]; 98 | long last_message_received; 99 | String name; 100 | String userName; 101 | int longPoll = 0; 102 | int waitForResponse = 1500; 103 | 104 | private: 105 | // JsonObject * parseUpdates(String response); 106 | String _token; 107 | Client *client; 108 | void closeClient(); 109 | const int maxMessageLength = 1500; 110 | bool processResult(JsonObject result, int messageIndex); 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /UniversalTelegramBotRZO.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018 Brian Lough. All right reserved. 3 | 4 | UniversalTelegramBot - Library to create your own Telegram Bot using 5 | ESP8266 or ESP32 on Arduino IDE. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #ifndef UniversalTelegramBotRZO_h 23 | #define UniversalTelegramBotRZO_h 24 | 25 | #define ARDUINOJSON_DECODE_UNICODE 1 26 | #define ARDUINOJSON_USE_LONG_LONG 1 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define HOST "api.telegram.org" 33 | #define SSL_PORT 443 34 | #define HANDLE_MESSAGES 1 35 | 36 | //unmark following line to enable debug mode 37 | #define _debug 38 | 39 | typedef bool (*MoreDataAvailable)(); 40 | typedef byte (*GetNextByte)(); 41 | typedef byte* (*GetNextBuffer)(); 42 | typedef int (GetNextBufferLen)(); 43 | 44 | struct telegramMessage { 45 | String inline_query; 46 | String inline_query_id; 47 | String sticker_id; 48 | String sticker_uid; 49 | int sticker_width; 50 | int sticker_height; 51 | bool sticker_animated; 52 | 53 | String text; 54 | String chat_id; 55 | String chat_title; 56 | String from_id; 57 | String from_name; 58 | String date; 59 | String type; 60 | float longitude; 61 | float latitude; 62 | int update_id; 63 | }; 64 | 65 | class UniversalTelegramBot { 66 | public: 67 | UniversalTelegramBot(String token, Client &client); 68 | String sendGetToTelegram(String command); 69 | String sendPostToTelegram(String command, JsonObject payload); 70 | String 71 | sendMultipartFormDataToTelegram(String command, String binaryProperyName, 72 | String fileName, String contentType, 73 | String chat_id, int fileSize, 74 | MoreDataAvailable moreDataAvailableCallback, 75 | GetNextByte getNextByteCallback, 76 | GetNextBuffer getNextBufferCallback, 77 | GetNextBufferLen getNextBufferLenCallback); 78 | 79 | bool getMe(); 80 | 81 | bool sendSimpleMessage(String chat_id, String text, String parse_mode); 82 | bool sendMessage(String chat_id, String text, String parse_mode = ""); 83 | bool sendMessageWithReplyKeyboard(String chat_id, String text, 84 | String parse_mode, String keyboard, 85 | bool resize = false, bool oneTime = false, 86 | bool selective = false); 87 | bool sendMessageWithInlineKeyboard(String chat_id, String text, 88 | String parse_mode, String keyboard); 89 | 90 | bool sendChatAction(String chat_id, String text); 91 | 92 | bool sendPostMessage(JsonObject payload); 93 | String sendPostPhoto(JsonObject payload); 94 | String sendPhotoByBinary(String chat_id, String contentType, int fileSize, 95 | MoreDataAvailable moreDataAvailableCallback, 96 | GetNextByte getNextByteCallback, 97 | GetNextBuffer getNextBufferCallback, 98 | GetNextBufferLen getNextBufferLenCallback); 99 | String sendPhoto(String chat_id, String photo, String caption = "", 100 | bool disable_notification = false, 101 | int reply_to_message_id = 0, String keyboard = ""); 102 | 103 | int getUpdates(long offset); 104 | bool checkForOkResponse(String response); 105 | telegramMessage messages[HANDLE_MESSAGES]; 106 | long last_message_received; 107 | String name; 108 | String userName; 109 | int longPoll = 0; 110 | int waitForResponse = 1500; 111 | 112 | private: 113 | // JsonObject * parseUpdates(String response); 114 | String _token; 115 | Client *client; 116 | void closeClient(); 117 | const int maxMessageLength = 1500; 118 | bool processResult(JsonObject result, int messageIndex); 119 | }; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /TelegramSendPhotoChunked1_2.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | An example of bot that echos back any messages received 3 | * * 4 | written by Giacarlo Bacchio (Gianbacchio on Github) 5 | adapted by Brian Lough 6 | *******************************************************************/ 7 | #include 8 | #include 9 | #include "esp_camera.h" 10 | #include "UniversalTelegramBotRZO.h" 11 | 12 | // Initialize Wifi connection to the router 13 | char ssid[] = "NSA"; // your network SSID (name) 14 | char password[] = "orange"; // your network key 15 | String chat_id; 16 | 17 | // Initialize Telegram BOT 18 | #define BOTtoken "Your Bot token" // your Bot Token (Get from Botfather) 19 | String token = BOTtoken; 20 | 21 | WiFiClientSecure client; 22 | UniversalTelegramBot bot(BOTtoken, client); 23 | 24 | int Bot_mtbs = 3000; //mean time between scan messages 25 | long Bot_lasttime; //last time messages' scan has been done 26 | 27 | camera_fb_t * fb; 28 | uint8_t* fb_buffer; 29 | size_t fb_length; 30 | int currentByte; 31 | 32 | #define PWDN_GPIO_NUM 32 33 | #define RESET_GPIO_NUM -1 34 | #define XCLK_GPIO_NUM 0 35 | #define SIOD_GPIO_NUM 26 36 | #define SIOC_GPIO_NUM 27 37 | 38 | #define Y9_GPIO_NUM 35 39 | #define Y8_GPIO_NUM 34 40 | #define Y7_GPIO_NUM 39 41 | #define Y6_GPIO_NUM 36 42 | #define Y5_GPIO_NUM 21 43 | #define Y4_GPIO_NUM 19 44 | #define Y3_GPIO_NUM 18 45 | #define Y2_GPIO_NUM 5 46 | #define VSYNC_GPIO_NUM 25 47 | #define HREF_GPIO_NUM 23 48 | #define PCLK_GPIO_NUM 22 49 | 50 | void setup() 51 | { 52 | Serial.begin(115200); 53 | 54 | camera_config_t config; 55 | config.ledc_channel = LEDC_CHANNEL_0; 56 | config.ledc_timer = LEDC_TIMER_0; 57 | config.pin_d0 = Y2_GPIO_NUM; 58 | config.pin_d1 = Y3_GPIO_NUM; 59 | config.pin_d2 = Y4_GPIO_NUM; 60 | config.pin_d3 = Y5_GPIO_NUM; 61 | config.pin_d4 = Y6_GPIO_NUM; 62 | config.pin_d5 = Y7_GPIO_NUM; 63 | config.pin_d6 = Y8_GPIO_NUM; 64 | config.pin_d7 = Y9_GPIO_NUM; 65 | config.pin_xclk = XCLK_GPIO_NUM; 66 | config.pin_pclk = PCLK_GPIO_NUM; 67 | config.pin_vsync = VSYNC_GPIO_NUM; 68 | config.pin_href = HREF_GPIO_NUM; 69 | config.pin_sscb_sda = SIOD_GPIO_NUM; 70 | config.pin_sscb_scl = SIOC_GPIO_NUM; 71 | config.pin_pwdn = PWDN_GPIO_NUM; 72 | config.pin_reset = RESET_GPIO_NUM; 73 | config.xclk_freq_hz = 20000000; 74 | config.pixel_format = PIXFORMAT_JPEG; 75 | //init with high specs to pre-allocate larger buffers 76 | if (psramFound()) { 77 | config.frame_size = FRAMESIZE_UXGA; 78 | config.jpeg_quality = 10; 79 | config.fb_count = 1; // Trying to reduce memory use 80 | } else { 81 | config.frame_size = FRAMESIZE_SVGA; 82 | config.jpeg_quality = 12; 83 | config.fb_count = 1; 84 | } 85 | 86 | // camera init 87 | esp_err_t err = esp_camera_init(&config); 88 | if (err != ESP_OK) { 89 | Serial.printf("Camera init failed with error 0x%x", err); 90 | return; 91 | } 92 | 93 | sensor_t * s = esp_camera_sensor_get(); 94 | 95 | s->set_framesize(s, FRAMESIZE_XGA); 96 | 97 | // Attempt to connect to Wifi network: 98 | Serial.print("Connecting Wifi: "); 99 | Serial.println(ssid); 100 | 101 | // Set WiFi to station mode and disconnect from an AP if it was Previously 102 | // connected 103 | WiFi.mode(WIFI_STA); 104 | WiFi.begin(ssid, password); 105 | 106 | while (WiFi.status() != WL_CONNECTED) { 107 | Serial.print("."); 108 | delay(500); 109 | } 110 | 111 | Serial.println(""); 112 | Serial.println("WiFi connected"); 113 | Serial.print("IP address: "); 114 | Serial.println(WiFi.localIP()); 115 | } 116 | 117 | bool isMoreDataAvailable() { 118 | return (fb_length - currentByte); 119 | } 120 | 121 | uint8_t photoNextByte() { 122 | currentByte++; 123 | return (fb_buffer[currentByte - 1]); 124 | } 125 | 126 | void take_send_photo(String chat_id) 127 | { 128 | camera_fb_t * fb = NULL; 129 | fb = esp_camera_fb_get(); 130 | currentByte = 0; 131 | fb_length = fb->len; 132 | fb_buffer = fb->buf; 133 | bot.sendPhotoByBinary(chat_id, "image/jpeg", fb->len, isMoreDataAvailable, photoNextByte, nullptr, nullptr); 134 | esp_camera_fb_return(fb); 135 | fb_length = NULL; 136 | fb_buffer = NULL; 137 | } 138 | 139 | void loop() { 140 | if (millis() > Bot_lasttime + Bot_mtbs) { 141 | int numNewMessages = bot.getUpdates(bot.last_message_received + 1); 142 | 143 | while (numNewMessages) { 144 | Serial.println("got response"); 145 | chat_id = bot.messages[0].chat_id; 146 | 147 | for (int i = 0; i < numNewMessages; i++) { 148 | //bot.sendMessage(bot.messages[i].chat_id, bot.messages[i].text, ""); 149 | take_send_photo(chat_id); 150 | } 151 | numNewMessages = bot.getUpdates(bot.last_message_received + 1); 152 | } 153 | 154 | Bot_lasttime = millis(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /esp32UploadTimedBot.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | Basic bot command example for ESP32 Camera 3 | written by Giacarlo Bacchio (Gianbacchio on Github) 4 | adapted by Brian Lough 5 | bodged by Robot Zero One 6 | 7 | Send a command to your bot to request a new photo every X minutes 8 | *******************************************************************/ 9 | #include 10 | #include 11 | #include "esp_camera.h" 12 | #include "UniversalTelegramBotRZO.h" 13 | 14 | 15 | // Initialize Wifi connection to the router 16 | char ssid[] = "NSA"; // your network SSID (name) 17 | char password[] = "orange"; // your network key 18 | 19 | String chat_id; 20 | long photo_sent; 21 | String last_chat_id; 22 | long millsecs_delay; 23 | 24 | // Initialize Telegram BOT 25 | #define BOTtoken "10101010101010" // your Bot Token (Get from Botfather) 26 | String token = BOTtoken; 27 | 28 | WiFiClientSecure client; 29 | UniversalTelegramBot bot(BOTtoken, client); 30 | 31 | int Bot_mtbs = 3000; //mean time between scan messages 32 | long Bot_lasttime; //last time messages' scan has been done 33 | 34 | camera_fb_t * fb; 35 | uint8_t* fb_buffer; 36 | size_t fb_length; 37 | int currentByte; 38 | 39 | // (Ai- Thinker defines) 40 | #define PWDN_GPIO_NUM 32 41 | #define RESET_GPIO_NUM -1 42 | #define XCLK_GPIO_NUM 0 43 | #define SIOD_GPIO_NUM 26 44 | #define SIOC_GPIO_NUM 27 45 | 46 | #define Y9_GPIO_NUM 35 47 | #define Y8_GPIO_NUM 34 48 | #define Y7_GPIO_NUM 39 49 | #define Y6_GPIO_NUM 36 50 | #define Y5_GPIO_NUM 21 51 | #define Y4_GPIO_NUM 19 52 | #define Y3_GPIO_NUM 18 53 | #define Y2_GPIO_NUM 5 54 | #define VSYNC_GPIO_NUM 25 55 | #define HREF_GPIO_NUM 23 56 | #define PCLK_GPIO_NUM 22 57 | 58 | 59 | 60 | void setup() 61 | { 62 | Serial.begin(115200); 63 | 64 | // comment out for Ai-thinker 65 | pinMode(13, INPUT_PULLUP); 66 | pinMode(14, INPUT_PULLUP); 67 | 68 | camera_config_t config; 69 | config.ledc_channel = LEDC_CHANNEL_0; 70 | config.ledc_timer = LEDC_TIMER_0; 71 | config.pin_d0 = Y2_GPIO_NUM; 72 | config.pin_d1 = Y3_GPIO_NUM; 73 | config.pin_d2 = Y4_GPIO_NUM; 74 | config.pin_d3 = Y5_GPIO_NUM; 75 | config.pin_d4 = Y6_GPIO_NUM; 76 | config.pin_d5 = Y7_GPIO_NUM; 77 | config.pin_d6 = Y8_GPIO_NUM; 78 | config.pin_d7 = Y9_GPIO_NUM; 79 | config.pin_xclk = XCLK_GPIO_NUM; 80 | config.pin_pclk = PCLK_GPIO_NUM; 81 | config.pin_vsync = VSYNC_GPIO_NUM; 82 | config.pin_href = HREF_GPIO_NUM; 83 | config.pin_sscb_sda = SIOD_GPIO_NUM; 84 | config.pin_sscb_scl = SIOC_GPIO_NUM; 85 | config.pin_pwdn = PWDN_GPIO_NUM; 86 | config.pin_reset = RESET_GPIO_NUM; 87 | config.xclk_freq_hz = 20000000; 88 | config.pixel_format = PIXFORMAT_JPEG; 89 | //init with high specs to pre-allocate larger buffers 90 | if (psramFound()) { 91 | config.frame_size = FRAMESIZE_UXGA; 92 | config.jpeg_quality = 10; 93 | config.fb_count = 1; // Trying to reduce memory use 94 | } else { 95 | config.frame_size = FRAMESIZE_SVGA; 96 | config.jpeg_quality = 12; 97 | config.fb_count = 1; 98 | } 99 | 100 | // camera init 101 | esp_err_t err = esp_camera_init(&config); 102 | if (err != ESP_OK) { 103 | Serial.printf("Camera init failed with error 0x%x", err); 104 | return; 105 | } 106 | 107 | sensor_t * s = esp_camera_sensor_get(); 108 | 109 | s->set_framesize(s, FRAMESIZE_XGA); 110 | 111 | Serial.print("Connecting Wifi: "); 112 | Serial.println(ssid); 113 | 114 | WiFi.mode(WIFI_STA); 115 | WiFi.begin(ssid, password); 116 | 117 | while (WiFi.status() != WL_CONNECTED) { 118 | Serial.print("."); 119 | delay(500); 120 | } 121 | 122 | Serial.println(""); 123 | Serial.println("WiFi connected"); 124 | Serial.print("IP address: "); 125 | Serial.println(WiFi.localIP()); 126 | } 127 | 128 | bool isMoreDataAvailable() { 129 | return (fb_length - currentByte); 130 | } 131 | 132 | uint8_t photoNextByte() { 133 | currentByte++; 134 | return (fb_buffer[currentByte - 1]); 135 | } 136 | 137 | void take_send_photo(String chat_id) 138 | { 139 | camera_fb_t * fb = NULL; 140 | fb = esp_camera_fb_get(); 141 | currentByte = 0; 142 | fb_length = fb->len; 143 | fb_buffer = fb->buf; 144 | 145 | bot.sendChatAction(chat_id, "upload_photo"); 146 | bot.sendPhotoByBinary(chat_id, "image/jpeg", fb->len, isMoreDataAvailable, photoNextByte, nullptr, nullptr); 147 | 148 | esp_camera_fb_return(fb); 149 | fb_length = NULL; 150 | fb_buffer = NULL; 151 | } 152 | 153 | void loop() { 154 | if (millis() > Bot_lasttime + Bot_mtbs) { 155 | int numNewMessages = bot.getUpdates(bot.last_message_received + 1); 156 | 157 | while (numNewMessages) { 158 | Serial.println("got response"); 159 | chat_id = bot.messages[0].chat_id; 160 | 161 | for (int i = 0; i < numNewMessages; i++) { 162 | 163 | if (bot.messages[i].type == "inline_query") { 164 | // 165 | } else { 166 | String delay_command = (bot.messages[i].text); // message sent to bot ie /5 for 5 minutes 167 | delay_command.remove(0, 1); // remove / 168 | last_chat_id = bot.messages[i].chat_id; 169 | bot.sendMessage(last_chat_id, "Sending a photo every " + delay_command + " minutes", ""); 170 | millsecs_delay = delay_command.toInt() * 60 * 1000; 171 | } 172 | 173 | } 174 | numNewMessages = bot.getUpdates(bot.last_message_received + 1); 175 | } 176 | if (millis() > photo_sent + millsecs_delay) { 177 | take_send_photo(last_chat_id); 178 | photo_sent = millis(); 179 | } 180 | 181 | Bot_lasttime = millis(); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /esp32InlinePhotoUpload.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | Inline bot that will send captured photos to any Telegram chat 3 | 4 | written by Giacarlo Bacchio (Gianbacchio on Github) 5 | adapted by Brian Lough 6 | bodged by Robot Zero One 7 | 8 | Workaround for not being able to directly upload a photo into the 9 | inline options for an inline bot in Telegram 10 | *******************************************************************/ 11 | #include 12 | #include 13 | #include "esp_camera.h" 14 | #include "UniversalTelegramBotRZO.h" 15 | 16 | // Initialize Wifi connection to the router 17 | char ssid[] = "NSA"; // your network SSID (name) 18 | char password[] = "orange"; // your network key 19 | 20 | String chat_id; 21 | String last_chat_id; 22 | // Initialize Telegram BOT 23 | #define BOTtoken "1010101010101" // your Bot Token (Get from Botfather) 24 | String token = BOTtoken; 25 | 26 | WiFiClientSecure client; 27 | UniversalTelegramBot bot(BOTtoken, client); 28 | 29 | int Bot_mtbs = 3000; //mean time between scan messages 30 | long Bot_lasttime; //last time messages' scan has been done 31 | 32 | camera_fb_t * fb; 33 | uint8_t* fb_buffer; 34 | size_t fb_length; 35 | int currentByte; 36 | 37 | 38 | // Ai-Thinker 39 | #define PWDN_GPIO_NUM 32 40 | #define RESET_GPIO_NUM -1 41 | #define XCLK_GPIO_NUM 0 42 | #define SIOD_GPIO_NUM 26 43 | #define SIOC_GPIO_NUM 27 44 | 45 | #define Y9_GPIO_NUM 35 46 | #define Y8_GPIO_NUM 34 47 | #define Y7_GPIO_NUM 39 48 | #define Y6_GPIO_NUM 36 49 | #define Y5_GPIO_NUM 21 50 | #define Y4_GPIO_NUM 19 51 | #define Y3_GPIO_NUM 18 52 | #define Y2_GPIO_NUM 5 53 | #define VSYNC_GPIO_NUM 25 54 | #define HREF_GPIO_NUM 23 55 | #define PCLK_GPIO_NUM 22 56 | 57 | 58 | void setup() 59 | { 60 | Serial.begin(115200); 61 | 62 | camera_config_t config; 63 | config.ledc_channel = LEDC_CHANNEL_0; 64 | config.ledc_timer = LEDC_TIMER_0; 65 | config.pin_d0 = Y2_GPIO_NUM; 66 | config.pin_d1 = Y3_GPIO_NUM; 67 | config.pin_d2 = Y4_GPIO_NUM; 68 | config.pin_d3 = Y5_GPIO_NUM; 69 | config.pin_d4 = Y6_GPIO_NUM; 70 | config.pin_d5 = Y7_GPIO_NUM; 71 | config.pin_d6 = Y8_GPIO_NUM; 72 | config.pin_d7 = Y9_GPIO_NUM; 73 | config.pin_xclk = XCLK_GPIO_NUM; 74 | config.pin_pclk = PCLK_GPIO_NUM; 75 | config.pin_vsync = VSYNC_GPIO_NUM; 76 | config.pin_href = HREF_GPIO_NUM; 77 | config.pin_sscb_sda = SIOD_GPIO_NUM; 78 | config.pin_sscb_scl = SIOC_GPIO_NUM; 79 | config.pin_pwdn = PWDN_GPIO_NUM; 80 | config.pin_reset = RESET_GPIO_NUM; 81 | config.xclk_freq_hz = 20000000; 82 | config.pixel_format = PIXFORMAT_JPEG; 83 | //init with high specs to pre-allocate larger buffers 84 | if (psramFound()) { 85 | config.frame_size = FRAMESIZE_UXGA; 86 | config.jpeg_quality = 10; 87 | config.fb_count = 1; // Trying to reduce memory use 88 | } else { 89 | config.frame_size = FRAMESIZE_SVGA; 90 | config.jpeg_quality = 12; 91 | config.fb_count = 1; 92 | } 93 | 94 | // camera init 95 | esp_err_t err = esp_camera_init(&config); 96 | if (err != ESP_OK) { 97 | Serial.printf("Camera init failed with error 0x%x", err); 98 | return; 99 | } 100 | 101 | sensor_t * s = esp_camera_sensor_get(); 102 | 103 | s->set_framesize(s, FRAMESIZE_QVGA); // keep low resolution to avoid timeouts 104 | 105 | Serial.print("Connecting Wifi: "); 106 | Serial.println(ssid); 107 | 108 | WiFi.mode(WIFI_STA); 109 | WiFi.begin(ssid, password); 110 | 111 | while (WiFi.status() != WL_CONNECTED) { 112 | Serial.print("."); 113 | delay(500); 114 | } 115 | 116 | Serial.println(""); 117 | Serial.println("WiFi connected"); 118 | Serial.print("IP address: "); 119 | Serial.println(WiFi.localIP()); 120 | } 121 | 122 | bool isMoreDataAvailable() { 123 | return (fb_length - currentByte); 124 | } 125 | 126 | uint8_t photoNextByte() { 127 | currentByte++; 128 | return (fb_buffer[currentByte - 1]); 129 | } 130 | 131 | void take_send_photo(String chat_id, String inline_query_id) 132 | { 133 | camera_fb_t * fb = NULL; 134 | fb = esp_camera_fb_get(); 135 | currentByte = 0; 136 | fb_length = fb->len; 137 | fb_buffer = fb->buf; 138 | 139 | // send the photo to the private channel 140 | String response = bot.sendPhotoByBinary(chat_id, "image/jpeg", fb->len, isMoreDataAvailable, photoNextByte, nullptr, nullptr); 141 | 142 | // get the new photo id from the response 143 | DynamicJsonDocument doc(1200); 144 | DeserializationError error = deserializeJson(doc, response); 145 | if (error) { 146 | Serial.print(F("deserializeJson() failed: ")); 147 | Serial.println(error.c_str()); 148 | return; 149 | } 150 | String photoID = doc["result"]["photo"][0]["file_id"].as(); 151 | 152 | // better random id needed 153 | long randNumber = random(3000); 154 | String numberString = String(randNumber); 155 | String temp_json = "[{\"type\": \"photo\", \"id\": \"" + numberString + "\", \"photo_file_id\": \"" + photoID + "\", \"caption\": \"Robot Posted to Chat!\"}]"; 156 | 157 | // finally answer inline request 158 | String thing = "/bot" + token + "/answerInlineQuery?inline_query_id=" + inline_query_id + "&cache_time=0&results=" + temp_json; 159 | bot.sendGetToTelegram(thing); 160 | 161 | esp_camera_fb_return(fb); 162 | fb_length = NULL; 163 | fb_buffer = NULL; 164 | 165 | } 166 | 167 | void loop() { 168 | if (millis() > Bot_lasttime + Bot_mtbs) { 169 | int numNewMessages = bot.getUpdates(bot.last_message_received + 1); 170 | 171 | while (numNewMessages) { 172 | Serial.println("got response"); 173 | chat_id = bot.messages[0].chat_id; 174 | 175 | for (int i = 0; i < numNewMessages; i++) { 176 | 177 | if (bot.messages[i].type == "inline_query") { // inline request from Telegram 178 | take_send_photo("-1001165258995", bot.messages[i].inline_query_id); // -100xxxxxxxx Private Channel id 179 | } else { 180 | // 181 | } 182 | 183 | } 184 | numNewMessages = bot.getUpdates(bot.last_message_received + 1); 185 | } 186 | Bot_lasttime = millis(); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /esp32InlineStickerBot.ino: -------------------------------------------------------------------------------- 1 | /******************************************************************* 2 | Basic inline sticker bot code for ESP32 3 | written by Giacarlo Bacchio (Gianbacchio on Github) 4 | adapted by Brian Lough 5 | bodged by Robot Zero One 6 | 7 | Basic script showing how an inline bot running on the ESP32 can 8 | display a fixed set of stickers when summoned with the @bot 9 | command 10 | *******************************************************************/ 11 | #include 12 | #include 13 | #include "esp_camera.h" 14 | #include "UniversalTelegramBotRZO.h" 15 | 16 | 17 | // Initialize Wifi connection to the router 18 | char ssid[] = "NSA"; // your network SSID (name) 19 | char password[] = "orange"; // your network key 20 | 21 | String chat_id; 22 | 23 | // Initialize Telegram BOT 24 | #define BOTtoken "10101010101" // your Bot Token (Get from Botfather) 25 | String token = BOTtoken; 26 | 27 | WiFiClientSecure client; 28 | UniversalTelegramBot bot(BOTtoken, client); 29 | 30 | int Bot_mtbs = 3000; //mean time between scan messages 31 | long Bot_lasttime; //last time messages' scan has been done 32 | 33 | camera_fb_t * fb; 34 | uint8_t* fb_buffer; 35 | size_t fb_length; 36 | int currentByte; 37 | 38 | // (Ai- Thinker defines) 39 | #define PWDN_GPIO_NUM 32 40 | #define RESET_GPIO_NUM -1 41 | #define XCLK_GPIO_NUM 0 42 | #define SIOD_GPIO_NUM 26 43 | #define SIOC_GPIO_NUM 27 44 | 45 | #define Y9_GPIO_NUM 35 46 | #define Y8_GPIO_NUM 34 47 | #define Y7_GPIO_NUM 39 48 | #define Y6_GPIO_NUM 36 49 | #define Y5_GPIO_NUM 21 50 | #define Y4_GPIO_NUM 19 51 | #define Y3_GPIO_NUM 18 52 | #define Y2_GPIO_NUM 5 53 | #define VSYNC_GPIO_NUM 25 54 | #define HREF_GPIO_NUM 23 55 | #define PCLK_GPIO_NUM 22 56 | 57 | void setup() 58 | { 59 | Serial.begin(115200); 60 | 61 | camera_config_t config; 62 | config.ledc_channel = LEDC_CHANNEL_0; 63 | config.ledc_timer = LEDC_TIMER_0; 64 | config.pin_d0 = Y2_GPIO_NUM; 65 | config.pin_d1 = Y3_GPIO_NUM; 66 | config.pin_d2 = Y4_GPIO_NUM; 67 | config.pin_d3 = Y5_GPIO_NUM; 68 | config.pin_d4 = Y6_GPIO_NUM; 69 | config.pin_d5 = Y7_GPIO_NUM; 70 | config.pin_d6 = Y8_GPIO_NUM; 71 | config.pin_d7 = Y9_GPIO_NUM; 72 | config.pin_xclk = XCLK_GPIO_NUM; 73 | config.pin_pclk = PCLK_GPIO_NUM; 74 | config.pin_vsync = VSYNC_GPIO_NUM; 75 | config.pin_href = HREF_GPIO_NUM; 76 | config.pin_sscb_sda = SIOD_GPIO_NUM; 77 | config.pin_sscb_scl = SIOC_GPIO_NUM; 78 | config.pin_pwdn = PWDN_GPIO_NUM; 79 | config.pin_reset = RESET_GPIO_NUM; 80 | config.xclk_freq_hz = 20000000; 81 | config.pixel_format = PIXFORMAT_JPEG; 82 | //init with high specs to pre-allocate larger buffers 83 | if (psramFound()) { 84 | config.frame_size = FRAMESIZE_UXGA; 85 | config.jpeg_quality = 10; 86 | config.fb_count = 1; // Trying to reduce memory use 87 | } else { 88 | config.frame_size = FRAMESIZE_SVGA; 89 | config.jpeg_quality = 12; 90 | config.fb_count = 1; 91 | } 92 | 93 | // camera init 94 | esp_err_t err = esp_camera_init(&config); 95 | if (err != ESP_OK) { 96 | Serial.printf("Camera init failed with error 0x%x", err); 97 | return; 98 | } 99 | 100 | sensor_t * s = esp_camera_sensor_get(); 101 | 102 | s->set_framesize(s, FRAMESIZE_XGA); 103 | 104 | Serial.print("Connecting Wifi: "); 105 | Serial.println(ssid); 106 | 107 | WiFi.mode(WIFI_STA); 108 | WiFi.begin(ssid, password); 109 | 110 | while (WiFi.status() != WL_CONNECTED) { 111 | Serial.print("."); 112 | delay(500); 113 | } 114 | 115 | Serial.println(""); 116 | Serial.println("WiFi connected"); 117 | Serial.print("IP address: "); 118 | Serial.println(WiFi.localIP()); 119 | } 120 | 121 | bool isMoreDataAvailable() { 122 | return (fb_length - currentByte); 123 | } 124 | 125 | uint8_t photoNextByte() { 126 | currentByte++; 127 | return (fb_buffer[currentByte - 1]); 128 | } 129 | 130 | void take_send_photo(String chat_id, String message) 131 | { 132 | camera_fb_t * fb = NULL; 133 | fb = esp_camera_fb_get(); 134 | currentByte = 0; 135 | fb_length = fb->len; 136 | fb_buffer = fb->buf; 137 | 138 | bot.sendChatAction(chat_id, "upload_photo"); 139 | String response = bot.sendPhotoByBinary(chat_id, "image/jpeg", fb->len, isMoreDataAvailable, photoNextByte, nullptr, nullptr); 140 | 141 | esp_camera_fb_return(fb); 142 | fb_length = NULL; 143 | fb_buffer = NULL; 144 | } 145 | 146 | void loop() { 147 | if (millis() > Bot_lasttime + Bot_mtbs) { 148 | int numNewMessages = bot.getUpdates(bot.last_message_received + 1); 149 | 150 | while (numNewMessages) { 151 | Serial.println("got response"); 152 | chat_id = bot.messages[0].chat_id; 153 | 154 | for (int i = 0; i < numNewMessages; i++) { 155 | 156 | if (bot.messages[i].type == "inline_query") { 157 | String stickers_json = "[" 158 | "{\"type\": \"sticker\", \"id\": \"rndm_1\", \"sticker_file_id\": \"CAACAgIAAxkBAAIEGF5ZAu8QOdzQwuVRC8bx1RzCiYmFAAIVAAPANk8TzVamO2GeZOcYBA\"}," 159 | "{\"type\": \"sticker\", \"id\": \"rndm_2\", \"sticker_file_id\": \"CAACAgIAAxkBAAIEl15aieJNgLyeZBIWIEY9dtLD_DmIAAKjAQACVp29CrgdFd218xk9GAQ\"}," 160 | "{\"type\": \"sticker\", \"id\": \"rndm_3\", \"sticker_file_id\": \"CAACAgIAAxkBAAIEml5ailRBIGearS6ZYit_DQPYe0ypAAILAwAC8-O-CwVcfQRNCcKwGAQ\"}," 161 | "{\"type\": \"sticker\", \"id\": \"rndm_4\", \"sticker_file_id\": \"CAACAgIAAxkBAAIEm15ail7AkPKmb9IXAydc_MlJ4Lb-AAJUCAACYyviCTfLtlzTZ0XfGAQ\"}," 162 | "{\"type\": \"sticker\", \"id\": \"rndm_5\", \"sticker_file_id\": \"CAACAgIAAxkBAAIEnF5ail8XyYPkm0Uh725Vyx-jXXwpAAKPAQACVp29CpMEvY4OJJHGGAQ\"}" 163 | "]"; 164 | String thing = "/bot" + token + "/answerInlineQuery?inline_query_id=" + bot.messages[i].inline_query_id + "&results=" + stickers_json; 165 | bot.sendGetToTelegram(thing); 166 | 167 | } else { 168 | // probably don't want a photo as well so comment this line out. 169 | take_send_photo(bot.messages[i].chat_id, bot.messages[i].text); 170 | } 171 | 172 | } 173 | numNewMessages = bot.getUpdates(bot.last_message_received + 1); 174 | } 175 | 176 | Bot_lasttime = millis(); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /UniversalTelegramBot1_2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018 Brian Lough. All right reserved. 3 | 4 | UniversalTelegramBot - Library to create your own Telegram Bot using 5 | ESP8266 or ESP32 on Arduino IDE. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | /* 23 | **** Note Regarding Client Connection Keeping **** 24 | Client connection is established in functions that directly involve use of 25 | client, i.e sendGetToTelegram, sendPostToTelegram, and 26 | sendMultipartFormDataToTelegram. It is closed at the end of 27 | sendMultipartFormDataToTelegram, but not at the end of sendGetToTelegram and 28 | sendPostToTelegram as these may need to keep the connection alive for respose 29 | / response checking. Re-establishing a connection then wastes time which is 30 | noticeable in user experience. Due to this, it is important that connection 31 | be closed manually after calling sendGetToTelegram or sendPostToTelegram by 32 | calling closeClient(); Failure to close connection causes memory leakage and 33 | SSL errors 34 | */ 35 | 36 | #include "UniversalTelegramBot1_2.h" 37 | 38 | UniversalTelegramBot::UniversalTelegramBot(String token, Client &client) { 39 | _token = token; 40 | #ifdef ARDUINO_ESP8266_RELEASE_2_5_0 41 | //client->setInsecure(); 42 | #endif 43 | this->client = &client; 44 | } 45 | 46 | String UniversalTelegramBot::sendGetToTelegram(String command) { 47 | String mess = ""; 48 | long now; 49 | bool avail; 50 | 51 | // Connect with api.telegram.org if not already connected 52 | if (!client->connected()) { 53 | #ifdef _debug 54 | Serial.println(F("[BOT]Connecting to server")); 55 | #endif 56 | if (!client->connect(HOST, SSL_PORT)) { 57 | #ifdef _debug 58 | Serial.println(F("[BOT]Conection error")); 59 | #endif 60 | } 61 | } 62 | if (client->connected()) { 63 | 64 | #ifdef _debug 65 | Serial.println(F(".... connected to server")); 66 | #endif 67 | 68 | String a = ""; 69 | char c; 70 | int ch_count = 0; 71 | client->println("GET /" + command); 72 | now = millis(); 73 | avail = false; 74 | while (millis() - now < longPoll * 1000 + waitForResponse) { 75 | while (client->available()) { 76 | char c = client->read(); 77 | if (ch_count < maxMessageLength) { 78 | mess = mess + c; 79 | ch_count++; 80 | } 81 | avail = true; 82 | } 83 | if (avail) { 84 | #ifdef _debug 85 | Serial.println(); 86 | Serial.println(mess); 87 | Serial.println(); 88 | #endif 89 | break; 90 | } 91 | } 92 | } 93 | 94 | return mess; 95 | } 96 | 97 | String UniversalTelegramBot::sendPostToTelegram(String command, JsonObject payload) { 98 | 99 | String body = ""; 100 | String headers = ""; 101 | long now; 102 | bool responseReceived = false; 103 | 104 | // Connect with api.telegram.org if not already connected 105 | if (!client->connected()) { 106 | #ifdef _debug 107 | Serial.println(F("[BOT Client]Connecting to server")); 108 | #endif 109 | if (!client->connect(HOST, SSL_PORT)) { 110 | #ifdef _debug 111 | Serial.println(F("[BOT Client]Conection error")); 112 | #endif 113 | } 114 | } 115 | if (client->connected()) { 116 | // POST URI 117 | client->print("POST /" + command); 118 | client->println(F(" HTTP/1.1")); 119 | // Host header 120 | client->print(F("Host:")); 121 | client->println(HOST); 122 | // JSON content type 123 | client->println(F("Content-Type: application/json")); 124 | 125 | // Content length 126 | int length = measureJson(payload); 127 | client->print(F("Content-Length:")); 128 | client->println(length); 129 | // End of headers 130 | client->println(); 131 | // POST message body 132 | String out; 133 | serializeJson(payload, out); 134 | 135 | client->println(out); 136 | 137 | int ch_count = 0; 138 | now = millis(); 139 | bool finishedHeaders = false; 140 | bool currentLineIsBlank = true; 141 | while (millis() - now < waitForResponse) { 142 | while (client->available()) { 143 | char c = client->read(); 144 | responseReceived = true; 145 | 146 | if (!finishedHeaders) { 147 | if (currentLineIsBlank && c == '\n') { 148 | finishedHeaders = true; 149 | } else { 150 | headers = headers + c; 151 | } 152 | } else { 153 | if (ch_count < maxMessageLength) { 154 | body = body + c; 155 | ch_count++; 156 | } 157 | } 158 | 159 | if (c == '\n') currentLineIsBlank = true; 160 | else if (c != '\r') currentLineIsBlank = false; 161 | 162 | } 163 | 164 | if (responseReceived) { 165 | #ifdef _debug 166 | Serial.println(); 167 | Serial.println(body); 168 | Serial.println(); 169 | #endif 170 | break; 171 | } 172 | } 173 | } 174 | 175 | return body; 176 | } 177 | 178 | String UniversalTelegramBot::sendMultipartFormDataToTelegram( 179 | String command, String binaryProperyName, String fileName, 180 | String contentType, String chat_id, int fileSize, 181 | MoreDataAvailable moreDataAvailableCallback, 182 | GetNextByte getNextByteCallback, 183 | GetNextBuffer getNextBufferCallback, 184 | GetNextBufferLen getNextBufferLenCallback) { 185 | 186 | String body = ""; 187 | String headers = ""; 188 | long now; 189 | bool responseReceived = false; 190 | bool finishedHeaders = false; 191 | bool currentLineIsBlank = true; 192 | 193 | String boundry = F("------------------------b8f610217e83e29b"); 194 | 195 | // Connect with api.telegram.org if not already connected 196 | if (!client->connected()) { 197 | #ifdef _debug 198 | Serial.println(F("[BOT Client]Connecting to server")); 199 | #endif 200 | if (!client->connect(HOST, SSL_PORT)) { 201 | #ifdef _debug 202 | Serial.println(F("[BOT Client]Conection error")); 203 | #endif 204 | } 205 | } 206 | if (client->connected()) { 207 | 208 | String start_request = ""; 209 | String end_request = ""; 210 | 211 | start_request = start_request + "--" + boundry + "\r\n"; 212 | start_request = start_request + "content-disposition: form-data; name=\"chat_id\"" + "\r\n"; 213 | start_request = start_request + "\r\n"; 214 | start_request = start_request + chat_id + "\r\n"; 215 | start_request = start_request + "--" + boundry + "\r\n"; 216 | start_request = start_request + "content-disposition: form-data; name=\"" + binaryProperyName + "\"; filename=\"" + fileName + "\"" + "\r\n"; 217 | start_request = start_request + "Content-Type: " + contentType + "\r\n"; 218 | start_request = start_request + "\r\n"; 219 | 220 | end_request = end_request + "\r\n"; 221 | end_request = end_request + "--" + boundry + "--" + "\r\n"; 222 | 223 | client->print("POST /bot" + _token + "/" + command); 224 | client->println(F(" HTTP/1.1")); 225 | // Host header 226 | client->print(F("Host: ")); 227 | client->println(HOST); 228 | client->println(F("User-Agent: arduino/1.0")); 229 | client->println(F("Accept: */*")); 230 | 231 | int contentLength = fileSize + start_request.length() + end_request.length(); 232 | #ifdef _debug 233 | Serial.println("Content-Length: " + String(contentLength)); 234 | #endif 235 | client->print("Content-Length: "); 236 | client->println(String(contentLength)); 237 | client->println("Content-Type: multipart/form-data; boundary=" + boundry); 238 | client->println(""); 239 | client->print(start_request); 240 | 241 | #ifdef _debug 242 | Serial.print("Start request: " + start_request); 243 | #endif 244 | 245 | if (getNextByteCallback == nullptr) { 246 | while (moreDataAvailableCallback()) { 247 | client->write((const uint8_t *)getNextBufferCallback(), getNextBufferLenCallback()); 248 | #ifdef _debug 249 | Serial.println(F("Sending photo from buffer")); 250 | #endif 251 | } 252 | } else { 253 | #ifdef _debug 254 | Serial.println(F("Sending photo by binary")); 255 | #endif 256 | byte buffer[512]; 257 | int count = 0; 258 | char ch; 259 | while (moreDataAvailableCallback()) { 260 | buffer[count] = getNextByteCallback(); 261 | count++; 262 | if (count == 512) { 263 | // yield(); 264 | #ifdef _debug 265 | Serial.println(F("Sending binary photo full buffer")); 266 | #endif 267 | client->write((const uint8_t *)buffer, 512); 268 | count = 0; 269 | } 270 | } 271 | 272 | if (count > 0) { 273 | #ifdef _debug 274 | Serial.println(F("Sending binary photo remaining buffer")); 275 | #endif 276 | client->write((const uint8_t *)buffer, count); 277 | } 278 | } 279 | 280 | client->print(end_request); 281 | #ifdef _debug 282 | Serial.print("End request: " + end_request); 283 | #endif 284 | int ch_count = 0; 285 | now = millis(); 286 | 287 | while (millis() - now < waitForResponse) { 288 | while (client->available()) { 289 | char c = client->read(); 290 | responseReceived = true; 291 | 292 | if (!finishedHeaders) { 293 | if (currentLineIsBlank && c == '\n') { 294 | finishedHeaders = true; 295 | } else { 296 | headers = headers + c; 297 | } 298 | } else { 299 | if (ch_count < maxMessageLength) { 300 | body = body + c; 301 | ch_count++; 302 | } 303 | } 304 | 305 | if (c == '\n') currentLineIsBlank = true; 306 | else if (c != '\r') currentLineIsBlank = false; 307 | } 308 | 309 | if (responseReceived) { 310 | #ifdef _debug 311 | Serial.println(); 312 | Serial.println(body); 313 | Serial.println(); 314 | #endif 315 | break; 316 | } 317 | } 318 | } 319 | 320 | closeClient(); 321 | return body; 322 | } 323 | 324 | 325 | bool UniversalTelegramBot::getMe() { 326 | String command = "bot" + _token + "/getMe"; 327 | String response = sendGetToTelegram(command); // receive reply from telegram.org 328 | DynamicJsonDocument doc(maxMessageLength); 329 | DeserializationError error = deserializeJson(doc, response); 330 | JsonObject obj = doc.as(); //there is nothing better right now to use obj.containsKey("result") 331 | closeClient(); 332 | 333 | if (!error) { 334 | if (obj.containsKey("result")) { 335 | String _name = doc["result"]["first_name"]; 336 | String _username = doc["result"]["username"]; 337 | name = _name; 338 | userName = _username; 339 | return true; 340 | } 341 | } 342 | 343 | return false; 344 | } 345 | 346 | /*************************************************************** 347 | * GetUpdates - function to receive messages from telegram * 348 | * (Argument to pass: the last+1 message to read) * 349 | * Returns the number of new messages * 350 | ***************************************************************/ 351 | int UniversalTelegramBot::getUpdates(long offset) { 352 | 353 | #ifdef _debug 354 | Serial.println(F("GET Update Messages")); 355 | #endif 356 | String command = "bot" + _token + "/getUpdates?offset=" + String(offset) + "&limit=" + String(HANDLE_MESSAGES); 357 | if (longPoll > 0) { 358 | command = command + "&timeout=" + String(longPoll); 359 | } 360 | String response = sendGetToTelegram(command); // receive reply from telegram.org 361 | 362 | if (response == "") { 363 | #ifdef _debug 364 | Serial.println(F("Received empty string in response!")); 365 | #endif 366 | // close the client as there's nothing to do with an empty string 367 | closeClient(); 368 | return 0; 369 | } else { 370 | #ifdef _debug 371 | Serial.print(F("incoming message length ")); 372 | Serial.println(response.length()); 373 | Serial.println(F("Creating DynamicJsonBuffer")); 374 | #endif 375 | 376 | // Parse response into Json object 377 | DynamicJsonDocument doc(maxMessageLength); 378 | DeserializationError error = deserializeJson(doc, response); 379 | #ifdef _debug 380 | Serial.print(F("GetUpdates parsed jsonDoc: ")); 381 | serializeJson(doc, Serial); 382 | Serial.println(); 383 | #endif 384 | 385 | JsonObject obj = doc.as(); //there is nothing better right now 386 | if (!error) { 387 | #ifdef _debug 388 | Serial.print(F("GetUpdates parsed jsonObj: ")); 389 | serializeJson(obj, Serial); 390 | Serial.println(); 391 | #endif 392 | if (obj.containsKey("result")) { 393 | int resultArrayLength = doc["result"].size(); 394 | if (resultArrayLength > 0) { 395 | int newMessageIndex = 0; 396 | // Step through all results 397 | for (int i = 0; i < resultArrayLength; i++) { 398 | JsonObject result = doc["result"][i]; 399 | if (processResult(result, newMessageIndex)) newMessageIndex++; 400 | } 401 | // We will keep the client open because there may be a response to be 402 | // given 403 | return newMessageIndex; 404 | } else { 405 | #ifdef _debug 406 | Serial.println(F("no new messages")); 407 | #endif 408 | } 409 | } else { 410 | #ifdef _debug 411 | Serial.println(F("Response contained no 'result'")); 412 | #endif 413 | } 414 | } else { // Parsing failed 415 | if (response.length() < 2) { // Too short a message. Maybe a connection issue 416 | #ifdef _debug 417 | Serial.println(F("Parsing error: Message too short")); 418 | #endif 419 | } else { 420 | // Buffer may not be big enough, increase buffer or reduce max number of 421 | // messages 422 | #ifdef _debug 423 | Serial.print(F("Failed to parse update, the message could be too " 424 | "big for the buffer. Error code: ")); 425 | Serial.println(error.c_str()); // debug print of parsing error 426 | #endif 427 | } 428 | } 429 | // Close the client as no response is to be given 430 | closeClient(); 431 | return 0; 432 | } 433 | } 434 | 435 | bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { 436 | int update_id = result["update_id"]; 437 | // Check have we already dealt with this message (this shouldn't happen!) 438 | if (last_message_received != update_id) { 439 | last_message_received = update_id; 440 | messages[messageIndex].update_id = update_id; 441 | messages[messageIndex].text = F(""); 442 | messages[messageIndex].from_id = F(""); 443 | messages[messageIndex].from_name = F(""); 444 | messages[messageIndex].longitude = 0; 445 | messages[messageIndex].latitude = 0; 446 | 447 | if (result.containsKey("message")) { 448 | JsonObject message = result["message"]; 449 | messages[messageIndex].type = F("message"); 450 | messages[messageIndex].from_id = message["from"]["id"].as(); 451 | messages[messageIndex].from_name = message["from"]["first_name"].as(); 452 | messages[messageIndex].date = message["date"].as(); 453 | messages[messageIndex].chat_id = message["chat"]["id"].as(); 454 | messages[messageIndex].chat_title = message["chat"]["title"].as(); 455 | 456 | if (message.containsKey("text")) { 457 | messages[messageIndex].text = message["text"].as(); 458 | 459 | } else if (message.containsKey("location")) { 460 | messages[messageIndex].longitude = message["location"]["longitude"].as(); 461 | messages[messageIndex].latitude = message["location"]["latitude"].as(); 462 | } 463 | } else if (result.containsKey("channel_post")) { 464 | JsonObject message = result["channel_post"]; 465 | messages[messageIndex].type = F("channel_post"); 466 | messages[messageIndex].text = message["text"].as(); 467 | messages[messageIndex].date = message["date"].as(); 468 | messages[messageIndex].chat_id = message["chat"]["id"].as(); 469 | messages[messageIndex].chat_title = message["chat"]["title"].as(); 470 | 471 | } else if (result.containsKey("callback_query")) { 472 | JsonObject message = result["callback_query"]; 473 | messages[messageIndex].type = F("callback_query"); 474 | messages[messageIndex].from_id = message["from"]["id"].as(); 475 | messages[messageIndex].from_name = message["from"]["first_name"].as(); 476 | messages[messageIndex].text = message["data"].as(); 477 | messages[messageIndex].date = message["date"].as(); 478 | messages[messageIndex].chat_id = message["message"]["chat"]["id"].as(); 479 | messages[messageIndex].chat_title = F(""); 480 | 481 | } else if (result.containsKey("edited_message")) { 482 | JsonObject message = result["edited_message"]; 483 | messages[messageIndex].type = F("edited_message"); 484 | messages[messageIndex].from_id = message["from"]["id"].as(); 485 | messages[messageIndex].from_name = message["from"]["first_name"].as(); 486 | messages[messageIndex].date = message["date"].as(); 487 | messages[messageIndex].chat_id = message["chat"]["id"].as(); 488 | messages[messageIndex].chat_title = message["chat"]["title"].as(); 489 | 490 | if (message.containsKey("text")) { 491 | messages[messageIndex].text = message["text"].as(); 492 | 493 | } else if (message.containsKey("location")) { 494 | messages[messageIndex].longitude = message["location"]["longitude"].as(); 495 | messages[messageIndex].latitude = message["location"]["latitude"].as(); 496 | } 497 | } 498 | return true; 499 | } 500 | return false; 501 | } 502 | 503 | /*********************************************************************** 504 | * SendMessage - function to send message to telegram * 505 | * (Arguments to pass: chat_id, text to transmit and markup(optional)) * 506 | ***********************************************************************/ 507 | bool UniversalTelegramBot::sendSimpleMessage(String chat_id, String text, 508 | String parse_mode) { 509 | 510 | bool sent = false; 511 | #ifdef _debug 512 | Serial.println(F("sendSimpleMessage: SEND Simple Message")); 513 | #endif 514 | long sttime = millis(); 515 | 516 | if (text != "") { 517 | while (millis() < sttime + 8000) { // loop for a while to send the message 518 | String command = "bot" + _token + "/sendMessage?chat_id=" + chat_id + 519 | "&text=" + text + "&parse_mode=" + parse_mode; 520 | String response = sendGetToTelegram(command); 521 | #ifdef _debug 522 | Serial.println(response); 523 | #endif 524 | sent = checkForOkResponse(response); 525 | if (sent) break; 526 | } 527 | } 528 | closeClient(); 529 | return sent; 530 | } 531 | 532 | bool UniversalTelegramBot::sendMessage(String chat_id, String text, 533 | String parse_mode) { 534 | 535 | DynamicJsonDocument payload(maxMessageLength); 536 | payload["chat_id"] = chat_id; 537 | payload["text"] = text; 538 | 539 | if (parse_mode != "") 540 | payload["parse_mode"] = parse_mode; 541 | 542 | return sendPostMessage(payload.as()); 543 | } 544 | 545 | bool UniversalTelegramBot::sendMessageWithReplyKeyboard( 546 | String chat_id, String text, String parse_mode, String keyboard, 547 | bool resize, bool oneTime, bool selective) { 548 | 549 | DynamicJsonDocument payload(maxMessageLength); 550 | payload["chat_id"] = chat_id; 551 | payload["text"] = text; 552 | 553 | if (parse_mode != "") 554 | payload["parse_mode"] = parse_mode; 555 | 556 | JsonObject replyMarkup = payload.createNestedObject("reply_markup"); 557 | 558 | // Reply keyboard is an array of arrays. 559 | // Outer array represents rows 560 | // Inner arrays represents columns 561 | // This example "ledon" and "ledoff" are two buttons on the top row 562 | // and "status is a single button on the next row" 563 | DynamicJsonDocument keyboardBuffer(maxMessageLength); // creating a buffer enough to keep keyboard string 564 | deserializeJson(keyboardBuffer, keyboard); 565 | replyMarkup["keyboard"] = keyboardBuffer.as(); 566 | 567 | // Telegram defaults these values to false, so to decrease the size of the 568 | // payload we will only send them if needed 569 | if (resize) 570 | replyMarkup["resize_keyboard"] = resize; 571 | 572 | if (oneTime) 573 | replyMarkup["one_time_keyboard"] = oneTime; 574 | 575 | if (selective) 576 | replyMarkup["selective"] = selective; 577 | 578 | return sendPostMessage(payload.as()); 579 | } 580 | 581 | bool UniversalTelegramBot::sendMessageWithInlineKeyboard(String chat_id, 582 | String text, 583 | String parse_mode, 584 | String keyboard) { 585 | 586 | DynamicJsonDocument payload(maxMessageLength); 587 | payload["chat_id"] = chat_id; 588 | payload["text"] = text; 589 | 590 | if (parse_mode != "") 591 | payload["parse_mode"] = parse_mode; 592 | 593 | JsonObject replyMarkup = payload.createNestedObject("reply_markup"); 594 | DynamicJsonDocument keyboardBuffer(maxMessageLength); // assuming keyboard buffer will alwas be limited to 1024 bytes 595 | deserializeJson(keyboardBuffer, keyboard); 596 | replyMarkup["inline_keyboard"] = keyboardBuffer.as(); 597 | return sendPostMessage(payload.as()); 598 | } 599 | 600 | /*********************************************************************** 601 | * SendPostMessage - function to send message to telegram * 602 | * (Arguments to pass: chat_id, text to transmit and markup(optional)) * 603 | ***********************************************************************/ 604 | bool UniversalTelegramBot::sendPostMessage(JsonObject payload) { 605 | 606 | bool sent = false; 607 | #ifdef _debug 608 | Serial.print(F("sendPostMessage: SEND Post Message: ")); 609 | serializeJson(payload, Serial); 610 | Serial.println(); 611 | #endif 612 | long sttime = millis(); 613 | 614 | if (payload.containsKey("text")) { 615 | while (millis() < sttime + 8000) { // loop for a while to send the message 616 | String command = "bot" + _token + "/sendMessage"; 617 | String response = sendPostToTelegram(command, payload); 618 | #ifdef _debug 619 | Serial.println(response); 620 | #endif 621 | sent = checkForOkResponse(response); 622 | if (sent) break; 623 | } 624 | } 625 | 626 | closeClient(); 627 | return sent; 628 | } 629 | 630 | String UniversalTelegramBot::sendPostPhoto(JsonObject payload) { 631 | 632 | bool sent = false; 633 | String response = ""; 634 | #ifdef _debug 635 | Serial.println(F("sendPostPhoto: SEND Post Photo")); 636 | #endif 637 | long sttime = millis(); 638 | 639 | if (payload.containsKey("photo")) { 640 | while (millis() < sttime + 8000) { // loop for a while to send the message 641 | String command = "bot" + _token + "/sendPhoto"; 642 | response = sendPostToTelegram(command, payload); 643 | #ifdef _debug 644 | Serial.println(response); 645 | #endif 646 | sent = checkForOkResponse(response); 647 | if (sent) break; 648 | 649 | } 650 | } 651 | 652 | closeClient(); 653 | return response; 654 | } 655 | 656 | String UniversalTelegramBot::sendPhotoByBinary( 657 | String chat_id, String contentType, int fileSize, 658 | MoreDataAvailable moreDataAvailableCallback, 659 | GetNextByte getNextByteCallback, GetNextBuffer getNextBufferCallback, GetNextBufferLen getNextBufferLenCallback) { 660 | 661 | #ifdef _debug 662 | Serial.println(F("sendPhotoByBinary: SEND Photo")); 663 | #endif 664 | 665 | String response = sendMultipartFormDataToTelegram("sendPhoto", "photo", "img.jpg", 666 | contentType, chat_id, fileSize, 667 | moreDataAvailableCallback, getNextByteCallback, getNextBufferCallback, getNextBufferLenCallback); 668 | 669 | #ifdef _debug 670 | Serial.println(response); 671 | #endif 672 | 673 | return response; 674 | } 675 | 676 | 677 | 678 | String UniversalTelegramBot::sendPhoto(String chat_id, String photo, 679 | String caption, 680 | bool disable_notification, 681 | int reply_to_message_id, 682 | String keyboard) { 683 | 684 | DynamicJsonDocument payload(maxMessageLength); 685 | payload["chat_id"] = chat_id; 686 | payload["photo"] = photo; 687 | 688 | if (caption) 689 | payload["caption"] = caption; 690 | 691 | if (disable_notification) 692 | payload["disable_notification"] = disable_notification; 693 | 694 | if (reply_to_message_id && reply_to_message_id != 0) 695 | payload["reply_to_message_id"] = reply_to_message_id; 696 | 697 | if (keyboard) { 698 | JsonObject replyMarkup = payload.createNestedObject("reply_markup"); 699 | DynamicJsonDocument keyboardBuffer(maxMessageLength); // assuming keyboard buffer will alwas be limited to 1024 bytes 700 | deserializeJson(keyboardBuffer, keyboard); 701 | replyMarkup["keyboard"] = keyboardBuffer.as(); 702 | } 703 | 704 | return sendPostPhoto(payload.as()); 705 | } 706 | 707 | bool UniversalTelegramBot::checkForOkResponse(String response) { 708 | int responseLength = response.length(); 709 | 710 | for (int m = 5; m < responseLength + 1; m++) { 711 | if (response.substring(m - 10, m) == 712 | "{\"ok\":true") { // Chek if message has been properly sent 713 | return true; 714 | } 715 | } 716 | 717 | return false; 718 | } 719 | 720 | bool UniversalTelegramBot::sendChatAction(String chat_id, String text) { 721 | 722 | bool sent = false; 723 | #ifdef _debug 724 | Serial.println(F("SEND Chat Action Message")); 725 | #endif 726 | long sttime = millis(); 727 | 728 | if (text != "") { 729 | while (millis() < sttime + 8000) { // loop for a while to send the message 730 | String command = "bot" + _token + "/sendChatAction?chat_id=" + chat_id + 731 | "&action=" + text; 732 | String response = sendGetToTelegram(command); 733 | 734 | #ifdef _debug 735 | Serial.println(response); 736 | #endif 737 | sent = checkForOkResponse(response); 738 | 739 | if (sent) break; 740 | 741 | } 742 | } 743 | 744 | closeClient(); 745 | return sent; 746 | } 747 | 748 | void UniversalTelegramBot::closeClient() { 749 | if (client->connected()) { 750 | #ifdef _debug 751 | Serial.println(F("Closing client")); 752 | #endif 753 | client->stop(); 754 | } 755 | } 756 | -------------------------------------------------------------------------------- /UniversalTelegramBotRZO.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018 Brian Lough. All right reserved. 3 | 4 | UniversalTelegramBot - Library to create your own Telegram Bot using 5 | ESP8266 or ESP32 on Arduino IDE. 6 | 7 | This library is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | /* 23 | **** Note Regarding Client Connection Keeping **** 24 | Client connection is established in functions that directly involve use of 25 | client, i.e sendGetToTelegram, sendPostToTelegram, and 26 | sendMultipartFormDataToTelegram. It is closed at the end of 27 | sendMultipartFormDataToTelegram, but not at the end of sendGetToTelegram and 28 | sendPostToTelegram as these may need to keep the connection alive for respose 29 | / response checking. Re-establishing a connection then wastes time which is 30 | noticeable in user experience. Due to this, it is important that connection 31 | be closed manually after calling sendGetToTelegram or sendPostToTelegram by 32 | calling closeClient(); Failure to close connection causes memory leakage and 33 | SSL errors 34 | */ 35 | 36 | #include "UniversalTelegramBotRZO.h" 37 | 38 | UniversalTelegramBot::UniversalTelegramBot(String token, Client &client) { 39 | _token = token; 40 | #ifdef ARDUINO_ESP8266_RELEASE_2_5_0 41 | //client->setInsecure(); 42 | #endif 43 | this->client = &client; 44 | } 45 | 46 | String UniversalTelegramBot::sendGetToTelegram(String command) { 47 | String mess = ""; 48 | long now; 49 | bool avail; 50 | 51 | // Connect with api.telegram.org if not already connected 52 | if (!client->connected()) { 53 | #ifdef _debug 54 | Serial.println(F("[BOT]Connecting to server")); 55 | #endif 56 | if (!client->connect(HOST, SSL_PORT)) { 57 | #ifdef _debug 58 | Serial.println(F("[BOT]Conection error")); 59 | #endif 60 | } 61 | } 62 | if (client->connected()) { 63 | 64 | #ifdef _debug 65 | Serial.println(F(".... connected to server")); 66 | #endif 67 | 68 | String a = ""; 69 | char c; 70 | int ch_count = 0; 71 | client->println("GET /" + command); 72 | now = millis(); 73 | avail = false; 74 | while (millis() - now < longPoll * 1000 + waitForResponse) { 75 | while (client->available()) { 76 | char c = client->read(); 77 | if (ch_count < maxMessageLength) { 78 | mess = mess + c; 79 | ch_count++; 80 | } 81 | avail = true; 82 | } 83 | if (avail) { 84 | #ifdef _debug 85 | Serial.println(); 86 | Serial.println(mess); 87 | Serial.println(); 88 | #endif 89 | break; 90 | } 91 | } 92 | } 93 | 94 | return mess; 95 | } 96 | 97 | String UniversalTelegramBot::sendPostToTelegram(String command, JsonObject payload) { 98 | 99 | String body = ""; 100 | String headers = ""; 101 | long now; 102 | bool responseReceived = false; 103 | 104 | // Connect with api.telegram.org if not already connected 105 | if (!client->connected()) { 106 | #ifdef _debug 107 | Serial.println(F("[BOT Client]Connecting to server")); 108 | #endif 109 | if (!client->connect(HOST, SSL_PORT)) { 110 | #ifdef _debug 111 | Serial.println(F("[BOT Client]Conection error")); 112 | #endif 113 | } 114 | } 115 | if (client->connected()) { 116 | // POST URI 117 | client->print("POST /" + command); 118 | client->println(F(" HTTP/1.1")); 119 | // Host header 120 | client->print(F("Host:")); 121 | client->println(HOST); 122 | // JSON content type 123 | client->println(F("Content-Type: application/json")); 124 | 125 | // Content length 126 | int length = measureJson(payload); 127 | client->print(F("Content-Length:")); 128 | client->println(length); 129 | // End of headers 130 | client->println(); 131 | // POST message body 132 | String out; 133 | serializeJson(payload, out); 134 | 135 | client->println(out); 136 | 137 | int ch_count = 0; 138 | now = millis(); 139 | bool finishedHeaders = false; 140 | bool currentLineIsBlank = true; 141 | while (millis() - now < waitForResponse) { 142 | while (client->available()) { 143 | char c = client->read(); 144 | responseReceived = true; 145 | 146 | if (!finishedHeaders) { 147 | if (currentLineIsBlank && c == '\n') { 148 | finishedHeaders = true; 149 | } else { 150 | headers = headers + c; 151 | } 152 | } else { 153 | if (ch_count < maxMessageLength) { 154 | body = body + c; 155 | ch_count++; 156 | } 157 | } 158 | 159 | if (c == '\n') currentLineIsBlank = true; 160 | else if (c != '\r') currentLineIsBlank = false; 161 | 162 | } 163 | 164 | if (responseReceived) { 165 | #ifdef _debug 166 | Serial.println(); 167 | Serial.println(body); 168 | Serial.println(); 169 | #endif 170 | break; 171 | } 172 | } 173 | } 174 | 175 | return body; 176 | } 177 | 178 | String UniversalTelegramBot::sendMultipartFormDataToTelegram( 179 | String command, String binaryProperyName, String fileName, 180 | String contentType, String chat_id, int fileSize, 181 | MoreDataAvailable moreDataAvailableCallback, 182 | GetNextByte getNextByteCallback, 183 | GetNextBuffer getNextBufferCallback, 184 | GetNextBufferLen getNextBufferLenCallback) { 185 | 186 | String body = ""; 187 | String headers = ""; 188 | long now; 189 | bool responseReceived = false; 190 | bool finishedHeaders = false; 191 | bool currentLineIsBlank = true; 192 | 193 | String boundry = F("------------------------b8f610217e83e29b"); 194 | 195 | // Connect with api.telegram.org if not already connected 196 | if (!client->connected()) { 197 | #ifdef _debug 198 | Serial.println(F("[BOT Client]Connecting to server")); 199 | #endif 200 | if (!client->connect(HOST, SSL_PORT)) { 201 | #ifdef _debug 202 | Serial.println(F("[BOT Client]Conection error")); 203 | #endif 204 | } 205 | } 206 | if (client->connected()) { 207 | 208 | String start_request = ""; 209 | String end_request = ""; 210 | 211 | start_request = start_request + "--" + boundry + "\r\n"; 212 | start_request = start_request + "content-disposition: form-data; name=\"chat_id\"" + "\r\n"; 213 | start_request = start_request + "\r\n"; 214 | start_request = start_request + chat_id + "\r\n"; 215 | start_request = start_request + "--" + boundry + "\r\n"; 216 | start_request = start_request + "content-disposition: form-data; name=\"" + binaryProperyName + "\"; filename=\"" + fileName + "\"" + "\r\n"; 217 | start_request = start_request + "Content-Type: " + contentType + "\r\n"; 218 | start_request = start_request + "\r\n"; 219 | 220 | end_request = end_request + "\r\n"; 221 | end_request = end_request + "--" + boundry + "--" + "\r\n"; 222 | 223 | client->print("POST /bot" + _token + "/" + command); 224 | client->println(F(" HTTP/1.1")); 225 | // Host header 226 | client->print(F("Host: ")); 227 | client->println(HOST); 228 | client->println(F("User-Agent: arduino/1.0")); 229 | client->println(F("Accept: */*")); 230 | 231 | int contentLength = fileSize + start_request.length() + end_request.length(); 232 | #ifdef _debug 233 | Serial.println("Content-Length: " + String(contentLength)); 234 | #endif 235 | client->print("Content-Length: "); 236 | client->println(String(contentLength)); 237 | client->println("Content-Type: multipart/form-data; boundary=" + boundry); 238 | client->println(""); 239 | client->print(start_request); 240 | 241 | #ifdef _debug 242 | Serial.print("Start request: " + start_request); 243 | #endif 244 | 245 | if (getNextByteCallback == nullptr) { 246 | while (moreDataAvailableCallback()) { 247 | client->write((const uint8_t *)getNextBufferCallback(), getNextBufferLenCallback()); 248 | #ifdef _debug 249 | Serial.println(F("Sending photo from buffer")); 250 | #endif 251 | } 252 | } else { 253 | #ifdef _debug 254 | Serial.println(F("Sending photo by binary")); 255 | #endif 256 | byte buffer[512]; 257 | int count = 0; 258 | char ch; 259 | while (moreDataAvailableCallback()) { 260 | buffer[count] = getNextByteCallback(); 261 | count++; 262 | if (count == 512) { 263 | // yield(); 264 | #ifdef _debug 265 | Serial.println(F("Sending binary photo full buffer")); 266 | #endif 267 | client->write((const uint8_t *)buffer, 512); 268 | count = 0; 269 | } 270 | } 271 | 272 | if (count > 0) { 273 | #ifdef _debug 274 | Serial.println(F("Sending binary photo remaining buffer")); 275 | #endif 276 | client->write((const uint8_t *)buffer, count); 277 | } 278 | } 279 | 280 | client->print(end_request); 281 | #ifdef _debug 282 | Serial.print("End request: " + end_request); 283 | #endif 284 | int ch_count = 0; 285 | now = millis(); 286 | 287 | while (millis() - now < waitForResponse) { 288 | while (client->available()) { 289 | char c = client->read(); 290 | responseReceived = true; 291 | 292 | if (!finishedHeaders) { 293 | if (currentLineIsBlank && c == '\n') { 294 | finishedHeaders = true; 295 | } else { 296 | headers = headers + c; 297 | } 298 | } else { 299 | if (ch_count < maxMessageLength) { 300 | body = body + c; 301 | ch_count++; 302 | } 303 | } 304 | 305 | if (c == '\n') currentLineIsBlank = true; 306 | else if (c != '\r') currentLineIsBlank = false; 307 | } 308 | 309 | if (responseReceived) { 310 | #ifdef _debug 311 | Serial.println(); 312 | Serial.println(body); 313 | Serial.println(); 314 | #endif 315 | break; 316 | } 317 | } 318 | } 319 | 320 | closeClient(); 321 | return body; 322 | } 323 | 324 | 325 | bool UniversalTelegramBot::getMe() { 326 | String command = "bot" + _token + "/getMe"; 327 | String response = sendGetToTelegram(command); // receive reply from telegram.org 328 | DynamicJsonDocument doc(maxMessageLength); 329 | DeserializationError error = deserializeJson(doc, response); 330 | JsonObject obj = doc.as(); //there is nothing better right now to use obj.containsKey("result") 331 | closeClient(); 332 | 333 | if (!error) { 334 | if (obj.containsKey("result")) { 335 | String _name = doc["result"]["first_name"]; 336 | String _username = doc["result"]["username"]; 337 | name = _name; 338 | userName = _username; 339 | return true; 340 | } 341 | } 342 | 343 | return false; 344 | } 345 | 346 | /*************************************************************** 347 | GetUpdates - function to receive messages from telegram 348 | (Argument to pass: the last+1 message to read) 349 | Returns the number of new messages 350 | ***************************************************************/ 351 | int UniversalTelegramBot::getUpdates(long offset) { 352 | 353 | #ifdef _debug 354 | Serial.println(F("GET Update Messages")); 355 | #endif 356 | String command = "bot" + _token + "/getUpdates?offset=" + String(offset) + "&limit=" + String(HANDLE_MESSAGES); 357 | if (longPoll > 0) { 358 | command = command + "&timeout=" + String(longPoll); 359 | } 360 | String response = sendGetToTelegram(command); // receive reply from telegram.org 361 | 362 | if (response == "") { 363 | #ifdef _debug 364 | Serial.println(F("Received empty string in response!")); 365 | #endif 366 | // close the client as there's nothing to do with an empty string 367 | closeClient(); 368 | return 0; 369 | } else { 370 | #ifdef _debug 371 | Serial.print(F("incoming message length ")); 372 | Serial.println(response.length()); 373 | Serial.println(F("Creating DynamicJsonBuffer")); 374 | #endif 375 | 376 | // Parse response into Json object 377 | DynamicJsonDocument doc(maxMessageLength); 378 | DeserializationError error = deserializeJson(doc, response); 379 | #ifdef _debug 380 | Serial.print(F("GetUpdates parsed jsonDoc: ")); 381 | serializeJson(doc, Serial); 382 | Serial.println(); 383 | #endif 384 | 385 | JsonObject obj = doc.as(); //there is nothing better right now 386 | if (!error) { 387 | #ifdef _debug 388 | Serial.print(F("GetUpdates parsed jsonObj: ")); 389 | serializeJson(obj, Serial); 390 | Serial.println(); 391 | #endif 392 | if (obj.containsKey("result")) { 393 | int resultArrayLength = doc["result"].size(); 394 | if (resultArrayLength > 0) { 395 | int newMessageIndex = 0; 396 | // Step through all results 397 | for (int i = 0; i < resultArrayLength; i++) { 398 | JsonObject result = doc["result"][i]; 399 | if (processResult(result, newMessageIndex)) newMessageIndex++; 400 | } 401 | // We will keep the client open because there may be a response to be 402 | // given 403 | return newMessageIndex; 404 | } else { 405 | #ifdef _debug 406 | Serial.println(F("no new messages")); 407 | #endif 408 | } 409 | } else { 410 | #ifdef _debug 411 | Serial.println(F("Response contained no 'result'")); 412 | #endif 413 | } 414 | } else { // Parsing failed 415 | if (response.length() < 2) { // Too short a message. Maybe a connection issue 416 | #ifdef _debug 417 | Serial.println(F("Parsing error: Message too short")); 418 | #endif 419 | } else { 420 | // Buffer may not be big enough, increase buffer or reduce max number of 421 | // messages 422 | #ifdef _debug 423 | Serial.print(F("Failed to parse update, the message could be too " 424 | "big for the buffer. Error code: ")); 425 | Serial.println(error.c_str()); // debug print of parsing error 426 | #endif 427 | } 428 | } 429 | // Close the client as no response is to be given 430 | closeClient(); 431 | return 0; 432 | } 433 | } 434 | 435 | bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { 436 | int update_id = result["update_id"]; 437 | // Check have we already dealt with this message (this shouldn't happen!) 438 | if (last_message_received != update_id) { 439 | last_message_received = update_id; 440 | messages[messageIndex].update_id = update_id; 441 | messages[messageIndex].inline_query = F(""); 442 | messages[messageIndex].text = F(""); 443 | messages[messageIndex].from_id = F(""); 444 | messages[messageIndex].from_name = F(""); 445 | messages[messageIndex].longitude = 0; 446 | messages[messageIndex].latitude = 0; 447 | 448 | if (result.containsKey("inline_query")) { 449 | JsonObject inline_query = result["inline_query"]; 450 | messages[messageIndex].type = F("inline_query"); 451 | messages[messageIndex].inline_query_id = inline_query["id"].as(); 452 | messages[messageIndex].from_id = inline_query["from"]["id"].as(); 453 | messages[messageIndex].from_name = inline_query["from"]["first_name"].as(); 454 | messages[messageIndex].date = inline_query["date"].as(); 455 | messages[messageIndex].chat_id = inline_query["chat"]["id"].as(); 456 | messages[messageIndex].chat_title = inline_query["chat"]["title"].as(); 457 | 458 | if (inline_query.containsKey("query")) { 459 | messages[messageIndex].text = inline_query["query"].as(); 460 | 461 | } else if (inline_query.containsKey("location")) { 462 | messages[messageIndex].longitude = inline_query["location"]["longitude"].as(); 463 | messages[messageIndex].latitude = inline_query["location"]["latitude"].as(); 464 | } 465 | } else if (result.containsKey("message")) { 466 | JsonObject message = result["message"]; 467 | messages[messageIndex].type = F("message"); 468 | messages[messageIndex].from_id = message["from"]["id"].as(); 469 | messages[messageIndex].from_name = message["from"]["first_name"].as(); 470 | 471 | messages[messageIndex].sticker_id = message["sticker"]["file_id"].as(); 472 | messages[messageIndex].sticker_uid = message["sticker"]["file_unique_id"].as(); 473 | messages[messageIndex].sticker_width = message["sticker"]["width"].as(); 474 | messages[messageIndex].sticker_height = message["sticker"]["height"].as(); 475 | messages[messageIndex].sticker_animated = message["sticker"]["is_animated"].as(); 476 | 477 | messages[messageIndex].date = message["date"].as(); 478 | messages[messageIndex].chat_id = message["chat"]["id"].as(); 479 | messages[messageIndex].chat_title = message["chat"]["title"].as(); 480 | 481 | if (message.containsKey("text")) { 482 | messages[messageIndex].text = message["text"].as(); 483 | 484 | } else if (message.containsKey("location")) { 485 | messages[messageIndex].longitude = message["location"]["longitude"].as(); 486 | messages[messageIndex].latitude = message["location"]["latitude"].as(); 487 | } 488 | } else if (result.containsKey("channel_post")) { 489 | JsonObject message = result["channel_post"]; 490 | messages[messageIndex].type = F("channel_post"); 491 | messages[messageIndex].text = message["text"].as(); 492 | messages[messageIndex].date = message["date"].as(); 493 | messages[messageIndex].chat_id = message["chat"]["id"].as(); 494 | messages[messageIndex].chat_title = message["chat"]["title"].as(); 495 | 496 | } else if (result.containsKey("callback_query")) { 497 | JsonObject message = result["callback_query"]; 498 | messages[messageIndex].type = F("callback_query"); 499 | messages[messageIndex].from_id = message["from"]["id"].as(); 500 | messages[messageIndex].from_name = message["from"]["first_name"].as(); 501 | messages[messageIndex].text = message["data"].as(); 502 | messages[messageIndex].date = message["date"].as(); 503 | messages[messageIndex].chat_id = message["message"]["chat"]["id"].as(); 504 | messages[messageIndex].chat_title = F(""); 505 | 506 | } else if (result.containsKey("edited_message")) { 507 | JsonObject message = result["edited_message"]; 508 | messages[messageIndex].type = F("edited_message"); 509 | messages[messageIndex].from_id = message["from"]["id"].as(); 510 | messages[messageIndex].from_name = message["from"]["first_name"].as(); 511 | messages[messageIndex].date = message["date"].as(); 512 | messages[messageIndex].chat_id = message["chat"]["id"].as(); 513 | messages[messageIndex].chat_title = message["chat"]["title"].as(); 514 | 515 | if (message.containsKey("text")) { 516 | messages[messageIndex].text = message["text"].as(); 517 | 518 | } else if (message.containsKey("location")) { 519 | messages[messageIndex].longitude = message["location"]["longitude"].as(); 520 | messages[messageIndex].latitude = message["location"]["latitude"].as(); 521 | } 522 | } 523 | return true; 524 | } 525 | return false; 526 | } 527 | 528 | /*********************************************************************** 529 | SendMessage - function to send message to telegram 530 | (Arguments to pass: chat_id, text to transmit and markup(optional)) 531 | ***********************************************************************/ 532 | bool UniversalTelegramBot::sendSimpleMessage(String chat_id, String text, 533 | String parse_mode) { 534 | 535 | bool sent = false; 536 | #ifdef _debug 537 | Serial.println(F("sendSimpleMessage: SEND Simple Message")); 538 | #endif 539 | long sttime = millis(); 540 | 541 | if (text != "") { 542 | while (millis() < sttime + 8000) { // loop for a while to send the message 543 | String command = "bot" + _token + "/sendMessage?chat_id=" + chat_id + 544 | "&text=" + text + "&parse_mode=" + parse_mode; 545 | String response = sendGetToTelegram(command); 546 | #ifdef _debug 547 | Serial.println(response); 548 | #endif 549 | sent = checkForOkResponse(response); 550 | if (sent) break; 551 | } 552 | } 553 | closeClient(); 554 | return sent; 555 | } 556 | 557 | bool UniversalTelegramBot::sendMessage(String chat_id, String text, 558 | String parse_mode) { 559 | 560 | DynamicJsonDocument payload(maxMessageLength); 561 | payload["chat_id"] = chat_id; 562 | payload["text"] = text; 563 | 564 | if (parse_mode != "") 565 | payload["parse_mode"] = parse_mode; 566 | 567 | return sendPostMessage(payload.as()); 568 | } 569 | 570 | bool UniversalTelegramBot::sendMessageWithReplyKeyboard( 571 | String chat_id, String text, String parse_mode, String keyboard, 572 | bool resize, bool oneTime, bool selective) { 573 | 574 | DynamicJsonDocument payload(maxMessageLength); 575 | payload["chat_id"] = chat_id; 576 | payload["text"] = text; 577 | 578 | if (parse_mode != "") 579 | payload["parse_mode"] = parse_mode; 580 | 581 | JsonObject replyMarkup = payload.createNestedObject("reply_markup"); 582 | 583 | // Reply keyboard is an array of arrays. 584 | // Outer array represents rows 585 | // Inner arrays represents columns 586 | // This example "ledon" and "ledoff" are two buttons on the top row 587 | // and "status is a single button on the next row" 588 | DynamicJsonDocument keyboardBuffer(maxMessageLength); // creating a buffer enough to keep keyboard string 589 | deserializeJson(keyboardBuffer, keyboard); 590 | replyMarkup["keyboard"] = keyboardBuffer.as(); 591 | 592 | // Telegram defaults these values to false, so to decrease the size of the 593 | // payload we will only send them if needed 594 | if (resize) 595 | replyMarkup["resize_keyboard"] = resize; 596 | 597 | if (oneTime) 598 | replyMarkup["one_time_keyboard"] = oneTime; 599 | 600 | if (selective) 601 | replyMarkup["selective"] = selective; 602 | 603 | return sendPostMessage(payload.as()); 604 | } 605 | 606 | bool UniversalTelegramBot::sendMessageWithInlineKeyboard(String chat_id, 607 | String text, 608 | String parse_mode, 609 | String keyboard) { 610 | 611 | DynamicJsonDocument payload(maxMessageLength); 612 | payload["chat_id"] = chat_id; 613 | payload["text"] = text; 614 | 615 | if (parse_mode != "") 616 | payload["parse_mode"] = parse_mode; 617 | 618 | JsonObject replyMarkup = payload.createNestedObject("reply_markup"); 619 | DynamicJsonDocument keyboardBuffer(maxMessageLength); // assuming keyboard buffer will alwas be limited to 1024 bytes 620 | deserializeJson(keyboardBuffer, keyboard); 621 | replyMarkup["inline_keyboard"] = keyboardBuffer.as(); 622 | return sendPostMessage(payload.as()); 623 | } 624 | 625 | /*********************************************************************** 626 | SendPostMessage - function to send message to telegram 627 | (Arguments to pass: chat_id, text to transmit and markup(optional)) 628 | ***********************************************************************/ 629 | bool UniversalTelegramBot::sendPostMessage(JsonObject payload) { 630 | 631 | bool sent = false; 632 | #ifdef _debug 633 | Serial.print(F("sendPostMessage: SEND Post Message: ")); 634 | serializeJson(payload, Serial); 635 | Serial.println(); 636 | #endif 637 | long sttime = millis(); 638 | 639 | if (payload.containsKey("text")) { 640 | while (millis() < sttime + 8000) { // loop for a while to send the message 641 | String command = "bot" + _token + "/sendMessage"; 642 | String response = sendPostToTelegram(command, payload); 643 | #ifdef _debug 644 | Serial.println(response); 645 | #endif 646 | sent = checkForOkResponse(response); 647 | if (sent) break; 648 | } 649 | } 650 | 651 | closeClient(); 652 | return sent; 653 | } 654 | 655 | String UniversalTelegramBot::sendPostPhoto(JsonObject payload) { 656 | 657 | bool sent = false; 658 | String response = ""; 659 | #ifdef _debug 660 | Serial.println(F("sendPostPhoto: SEND Post Photo")); 661 | #endif 662 | long sttime = millis(); 663 | 664 | if (payload.containsKey("photo")) { 665 | while (millis() < sttime + 8000) { // loop for a while to send the message 666 | String command = "bot" + _token + "/sendPhoto"; 667 | response = sendPostToTelegram(command, payload); 668 | #ifdef _debug 669 | Serial.println(response); 670 | #endif 671 | sent = checkForOkResponse(response); 672 | if (sent) break; 673 | 674 | } 675 | } 676 | 677 | closeClient(); 678 | return response; 679 | } 680 | 681 | String UniversalTelegramBot::sendPhotoByBinary( 682 | String chat_id, String contentType, int fileSize, 683 | MoreDataAvailable moreDataAvailableCallback, 684 | GetNextByte getNextByteCallback, GetNextBuffer getNextBufferCallback, GetNextBufferLen getNextBufferLenCallback) { 685 | 686 | #ifdef _debug 687 | Serial.println(F("sendPhotoByBinary: SEND Photo")); 688 | #endif 689 | 690 | String response = sendMultipartFormDataToTelegram("sendPhoto", "photo", "img.jpg", 691 | contentType, chat_id, fileSize, 692 | moreDataAvailableCallback, getNextByteCallback, getNextBufferCallback, getNextBufferLenCallback); 693 | 694 | #ifdef _debug 695 | Serial.println(response); 696 | #endif 697 | 698 | return response; 699 | } 700 | 701 | 702 | 703 | String UniversalTelegramBot::sendPhoto(String chat_id, String photo, 704 | String caption, 705 | bool disable_notification, 706 | int reply_to_message_id, 707 | String keyboard) { 708 | 709 | DynamicJsonDocument payload(maxMessageLength); 710 | payload["chat_id"] = chat_id; 711 | payload["photo"] = photo; 712 | 713 | if (caption) 714 | payload["caption"] = caption; 715 | 716 | if (disable_notification) 717 | payload["disable_notification"] = disable_notification; 718 | 719 | if (reply_to_message_id && reply_to_message_id != 0) 720 | payload["reply_to_message_id"] = reply_to_message_id; 721 | 722 | if (keyboard) { 723 | JsonObject replyMarkup = payload.createNestedObject("reply_markup"); 724 | DynamicJsonDocument keyboardBuffer(maxMessageLength); // assuming keyboard buffer will alwas be limited to 1024 bytes 725 | deserializeJson(keyboardBuffer, keyboard); 726 | replyMarkup["keyboard"] = keyboardBuffer.as(); 727 | } 728 | 729 | return sendPostPhoto(payload.as()); 730 | } 731 | 732 | bool UniversalTelegramBot::checkForOkResponse(String response) { 733 | int responseLength = response.length(); 734 | 735 | for (int m = 5; m < responseLength + 1; m++) { 736 | if (response.substring(m - 10, m) == 737 | "{\"ok\":true") { // Chek if message has been properly sent 738 | return true; 739 | } 740 | } 741 | 742 | return false; 743 | } 744 | 745 | bool UniversalTelegramBot::sendChatAction(String chat_id, String text) { 746 | 747 | bool sent = false; 748 | #ifdef _debug 749 | Serial.println(F("SEND Chat Action Message")); 750 | #endif 751 | long sttime = millis(); 752 | 753 | if (text != "") { 754 | while (millis() < sttime + 8000) { // loop for a while to send the message 755 | String command = "bot" + _token + "/sendChatAction?chat_id=" + chat_id + 756 | "&action=" + text; 757 | String response = sendGetToTelegram(command); 758 | 759 | #ifdef _debug 760 | Serial.println(response); 761 | #endif 762 | sent = checkForOkResponse(response); 763 | 764 | if (sent) break; 765 | 766 | } 767 | } 768 | 769 | closeClient(); 770 | return sent; 771 | } 772 | 773 | void UniversalTelegramBot::closeClient() { 774 | if (client->connected()) { 775 | #ifdef _debug 776 | Serial.println(F("Closing client")); 777 | #endif 778 | client->stop(); 779 | } 780 | } 781 | --------------------------------------------------------------------------------