diff --git a/.gitignore b/.gitignore index 648f8cc..1f05968 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ .gcc-flags.json .pio/ .vscode/ + +# MacOS +.DS_Store diff --git a/src/UniversalTelegramBot.cpp b/src/UniversalTelegramBot.cpp index d500dcf..9e437e8 100644 --- a/src/UniversalTelegramBot.cpp +++ b/src/UniversalTelegramBot.cpp @@ -235,7 +235,7 @@ String UniversalTelegramBot::sendMultipartFormDataToTelegram( client->print(buildCommand(command)); client->println(F(" HTTP/1.1")); // Host header - client->print(F("Host: " TELEGRAM_HOST)); + client->println(F("Host: " TELEGRAM_HOST)); client->println(F("User-Agent: arduino/1.0")); client->println(F("Accept: */*")); @@ -247,7 +247,7 @@ String UniversalTelegramBot::sendMultipartFormDataToTelegram( client->println(String(contentLength)); client->print(F("Content-Type: multipart/form-data; boundary=")); client->println(boundary); - client->println(F("")); + client->println(); client->print(start_request); #ifdef TELEGRAM_DEBUG @@ -318,6 +318,29 @@ bool UniversalTelegramBot::getMe() { return false; } +/********************************************************************************* + * GetMyCommands - Get the command list of the bot from the telegram server * + * Returns the JSON string with the command list for the key "result" in the * + * server response * + ********************************************************************************/ +String UniversalTelegramBot::getMyCommands() +{ + #ifdef TELEGRAM_DEBUG + Serial.println(F("GET getMyCommands")); + #endif + String command = BOT_CMD("getMyCommands"); + String response = sendGetToTelegram(command); // receive reply from telegram.org + #ifdef TELEGRAM_DEBUG + Serial.print(F("incoming message length ")); + Serial.println(response.length()); + Serial.println(F("Creating DynamicJsonBuffer")); + #endif + closeClient(); + + return response; +} + + /********************************************************************************* * SetMyCommands - Update the command list of the bot on the telegram server * * (Argument to pass: Serialied array of BotCommand) * @@ -348,6 +371,30 @@ bool UniversalTelegramBot::setMyCommands(const String& commandArray) { } +bool UniversalTelegramBot::setMyCommandsStr(String commands) +{ + bool sent = false; + String response = ""; + #if defined(_debug) + Serial.println(F("sendSetMyCommands: SEND Post /setMyCommands")); + #endif // defined(_debug) + unsigned long sttime = millis(); + + while (millis() - sttime < 8000ul) + { // loop for a while to send the message + response = sendGetToTelegram(BOT_CMD("setMyCommands") + commands); + #ifdef _debug + Serial.println("setMyCommands response" + response); + #endif + sent = checkForOkResponse(response); + if (sent) break; + } + + closeClient(); + return sent; +} + + /*************************************************************** * GetUpdates - function to receive messages from telegram * * (Argument to pass: the last+1 message to read) * @@ -445,6 +492,8 @@ bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { messages[messageIndex].text = F(""); messages[messageIndex].from_id = F(""); messages[messageIndex].from_name = F(""); + messages[messageIndex].hasPhoto = false; + messages[messageIndex].file_caption = F(""); messages[messageIndex].longitude = 0; messages[messageIndex].latitude = 0; messages[messageIndex].reply_to_message_id = 0; @@ -460,9 +509,13 @@ bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { messages[messageIndex].chat_id = message["chat"]["id"].as(); messages[messageIndex].chat_title = message["chat"]["title"].as(); messages[messageIndex].hasDocument = false; + messages[messageIndex].message_id = message["message_id"].as(); if (message.containsKey("text")) { messages[messageIndex].text = message["text"].as(); - + + } else if (message.containsKey("photo")) { + messages[messageIndex].hasPhoto = true; + messages[messageIndex].file_caption = message["caption"].as(); } else if (message.containsKey("location")) { messages[messageIndex].longitude = message["location"]["longitude"].as(); messages[messageIndex].latitude = message["location"]["latitude"].as(); @@ -479,7 +532,8 @@ bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { messages[messageIndex].reply_to_message_id = message["reply_to_message"]["message_id"]; // no need to check if containsKey["text"]. If it doesn't, it default to null messages[messageIndex].reply_to_text = message["reply_to_message"]["text"].as(); - } + } + } else if (result.containsKey("channel_post")) { JsonObject message = result["channel_post"]; messages[messageIndex].type = F("channel_post"); @@ -487,6 +541,7 @@ bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { messages[messageIndex].date = message["date"].as(); messages[messageIndex].chat_id = message["chat"]["id"].as(); messages[messageIndex].chat_title = message["chat"]["title"].as(); + messages[messageIndex].message_id = message["message_id"].as(); // added message id } else if (result.containsKey("callback_query")) { JsonObject message = result["callback_query"]; @@ -499,6 +554,8 @@ bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { messages[messageIndex].reply_to_text = message["message"]["text"].as(); messages[messageIndex].chat_title = F(""); messages[messageIndex].query_id = message["id"].as(); + messages[messageIndex].message_id = message["message"]["message_id"].as(); // added message id + } else if (result.containsKey("edited_message")) { JsonObject message = result["edited_message"]; messages[messageIndex].type = F("edited_message"); @@ -507,6 +564,7 @@ bool UniversalTelegramBot::processResult(JsonObject result, int messageIndex) { messages[messageIndex].date = message["date"].as(); messages[messageIndex].chat_id = message["chat"]["id"].as(); messages[messageIndex].chat_title = message["chat"]["title"].as(); + messages[messageIndex].message_id = message["message_id"].as(); // added message id if (message.containsKey("text")) { messages[messageIndex].text = message["text"].as(); @@ -554,23 +612,45 @@ bool UniversalTelegramBot::sendSimpleMessage(const String& chat_id, const String return sent; } + bool UniversalTelegramBot::sendMessage(const String& chat_id, const String& text, - const String& parse_mode) { + const String& parse_mode, int message_id) +{ + DynamicJsonDocument payload(maxMessageLength); + payload["chat_id"] = chat_id; + payload["text"] = text; + + if (message_id != 0) + payload["message_id"] = message_id; // added message_id + + if (parse_mode != "") + payload["parse_mode"] = parse_mode; + + return sendPostMessage(payload.as(), message_id); // if message id == 0 then edit is false, else edit is true +} + +/* +bool UniversalTelegramBot::editMessage(const String& chat_id, const String & message_id, const String& text, + const String& parse_mode) +{ DynamicJsonDocument payload(maxMessageLength); payload["chat_id"] = chat_id; payload["text"] = text; + payload["message_id"] = message_id; if (parse_mode != "") payload["parse_mode"] = parse_mode; - return sendPostMessage(payload.as()); + return sendPostMessage(payload.as(), EDIT_TEXT); } +*/ + bool UniversalTelegramBot::sendMessageWithReplyKeyboard( const String& chat_id, const String& text, const String& parse_mode, const String& keyboard, - bool resize, bool oneTime, bool selective) { - + bool resize, bool oneTime, bool selective, bool removeKeyboard) +{ DynamicJsonDocument payload(maxMessageLength); payload["chat_id"] = chat_id; payload["text"] = text; @@ -579,19 +659,24 @@ bool UniversalTelegramBot::sendMessageWithReplyKeyboard( payload["parse_mode"] = parse_mode; JsonObject replyMarkup = payload.createNestedObject("reply_markup"); - + replyMarkup["keyboard"] = serialized(keyboard); // Telegram defaults these values to false, so to decrease the size of the // payload we will only send them if needed - if (resize) - replyMarkup["resize_keyboard"] = resize; - - if (oneTime) - replyMarkup["one_time_keyboard"] = oneTime; - - if (selective) - replyMarkup["selective"] = selective; + if (removeKeyboard) + replyMarkup["remove_keyboard"] = removeKeyboard; + else + { + if (resize) + replyMarkup["resize_keyboard"] = resize; + + if (oneTime) + replyMarkup["one_time_keyboard"] = oneTime; + + if (selective) + replyMarkup["selective"] = selective; + } return sendPostMessage(payload.as()); } @@ -599,25 +684,29 @@ bool UniversalTelegramBot::sendMessageWithReplyKeyboard( bool UniversalTelegramBot::sendMessageWithInlineKeyboard(const String& chat_id, const String& text, const String& parse_mode, - const String& keyboard) { + const String& keyboard, + int message_id) { // added message_id DynamicJsonDocument payload(maxMessageLength); payload["chat_id"] = chat_id; payload["text"] = text; + if (message_id != 0) + payload["message_id"] = message_id; // added message_id + if (parse_mode != "") payload["parse_mode"] = parse_mode; JsonObject replyMarkup = payload.createNestedObject("reply_markup"); replyMarkup["inline_keyboard"] = serialized(keyboard); - return sendPostMessage(payload.as()); + return sendPostMessage(payload.as(), message_id); // if message id == 0 then edit is false, else edit is true } /*********************************************************************** - * SendPostMessage - function to send message to telegram * + * SendPostMessage - function to send message to telegram * * (Arguments to pass: chat_id, text to transmit and markup(optional)) * ***********************************************************************/ -bool UniversalTelegramBot::sendPostMessage(JsonObject payload) { +bool UniversalTelegramBot::sendPostMessage(JsonObject payload, bool edit) { // added message_id bool sent = false; #ifdef TELEGRAM_DEBUG @@ -627,10 +716,13 @@ bool UniversalTelegramBot::sendPostMessage(JsonObject payload) { #endif unsigned long sttime = millis(); - if (payload.containsKey("text")) { - while (millis() - sttime < 8000ul) { // loop for a while to send the message - String response = sendPostToTelegram(BOT_CMD("sendMessage"), payload); - #ifdef TELEGRAM_DEBUG + if (payload.containsKey("text")) + { + String response; + while (millis() < sttime + 8000ul) + { // loop for a while to send the message + String response = sendPostToTelegram((edit ? BOT_CMD("editMessageText") : BOT_CMD("sendMessage")), payload); // if edit is true we send a editMessageText CMD +#ifdef TELEGRAM_DEBUG Serial.println(response); #endif sent = checkForOkResponse(response); @@ -757,6 +849,101 @@ bool UniversalTelegramBot::sendChatAction(const String& chat_id, const String& t return sent; } + +bool UniversalTelegramBot::getChatDescription(String chat_id, String & text) +{ + String command = "bot" + _token + "/getChat?chat_id=" + chat_id; + String response = sendGetToTelegram(command); // receive reply from telegram.org + DynamicJsonDocument doc(maxMessageLength); + DeserializationError error = deserializeJson(doc, response); + JsonObject obj = doc.as(); //there is nothing better right now to use obj.containsKey("result") + closeClient(); + + if (!error) + { + if (obj.containsKey("result")) + { + String descr = doc["result"]["description"]; + text = descr; + return true; + } + } + + return false; +} + + + +bool UniversalTelegramBot::setChatDescription(String chat_id, String text) +{ + bool sent = false; + #ifdef TELEGRAM_DEBUG + Serial.println(F("SEND Chat Description")); + #endif + long sttime = millis(); + + if (text != "") + { + while (millis() < sttime + 8000) + { // loop for a while to send the message + String command = "bot" + _token + "/setChatDescription?chat_id=" + chat_id + "&description=" + text; + String response = sendGetToTelegram(command); + + #ifdef TELEGRAM_DEBUG + Serial.println(response); + #endif + sent = checkForOkResponse(response); + + if (sent) break; + } + } + + closeClient(); + return sent; +} + + + +bool UniversalTelegramBot::restrictChatMember(String chat_id, String user_id, bool permit, String until_date) +{ + bool sent = false; + long sttime = millis(); + + DynamicJsonDocument doc(maxMessageLength); + doc["chat_id"] = chat_id; + doc["user_id"] = user_id; + JsonObject permissions = doc.createNestedObject("permissions"); + permissions["can_send_messages"] = permit ? "True" : "False"; + doc["until_date"] = until_date; + // DeserializationError error = deserializeJson(doc, response); + JsonObject payload = doc.as(); //there is nothing better right now to use obj.containsKey("result") + + #ifdef TELEGRAM_DEBUG + Serial.print(F("SEND User Restriction")); + serializeJson(payload, Serial); + Serial.println(); + #endif + + if (payload.containsKey("text")) + { + while (millis() < sttime + 8000) + { // loop for a while to send the message + String command = "bot" + _token + "/restrictChatMember"; + String response = sendPostToTelegram(command, payload); + #ifdef TELEGRAM_DEBUG + Serial.println(response); + #endif + sent = checkForOkResponse(response); + if (sent) break; + } + } + + closeClient(); + return sent; +} + + + void UniversalTelegramBot::closeClient() { if (client->connected()) { #ifdef TELEGRAM_DEBUG diff --git a/src/UniversalTelegramBot.h b/src/UniversalTelegramBot.h index a43233c..d11b772 100644 --- a/src/UniversalTelegramBot.h +++ b/src/UniversalTelegramBot.h @@ -30,10 +30,12 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include +#define TELEGRAM_MAX_MESSAGE_LENGTH 10500 #define TELEGRAM_HOST "api.telegram.org" #define TELEGRAM_SSL_PORT 443 #define HANDLE_MESSAGES 1 + //unmark following line to enable debug mode //#define _debug @@ -54,10 +56,12 @@ struct telegramMessage { String file_path; String file_name; bool hasDocument; + bool hasPhoto; long file_size; float longitude; float latitude; int update_id; + int message_id; int reply_to_message_id; String reply_to_text; @@ -82,19 +86,22 @@ class UniversalTelegramBot { bool readHTTPAnswer(String &body, String &headers); bool getMe(); + String GetName() { return name; } bool sendSimpleMessage(const String& chat_id, const String& text, const String& parse_mode); - bool sendMessage(const String& chat_id, const String& text, const String& parse_mode = ""); + bool sendMessage(const String& chat_id, const String& text, const String& parse_mode = "", int message_id = 0); + // bool editMessage(const String& chat_id, const String & message_id, const String& text, + // const String& parse_mode = ""); bool sendMessageWithReplyKeyboard(const String& chat_id, const String& text, const String& parse_mode, const String& keyboard, bool resize = false, bool oneTime = false, - bool selective = false); + bool selective = false, bool removeKeyboard = false); bool sendMessageWithInlineKeyboard(const String& chat_id, const String& text, - const String& parse_mode, const String& keyboard); + const String& parse_mode, const String& keyboard, int message_id = 0); bool sendChatAction(const String& chat_id, const String& text); - bool sendPostMessage(JsonObject payload); + bool sendPostMessage(JsonObject payload, bool edit = false); String sendPostPhoto(JsonObject payload); String sendPhotoByBinary(const String& chat_id, const String& contentType, int fileSize, MoreDataAvailable moreDataAvailableCallback, @@ -111,12 +118,18 @@ class UniversalTelegramBot { const String &url = "", int cache_time = 0); + String getMyCommands(); bool setMyCommands(const String& commandArray); + bool setMyCommandsStr(String commands); String buildCommand(const String& cmd); + bool getChatDescription(String chat_id, String & text); + bool setChatDescription(String chat_id, String text); + bool restrictChatMember(String chat_id, String user_id, bool permit, String until_date); int getUpdates(long offset); bool checkForOkResponse(const String& response); + int getLastSentMessageId() { return last_sent_message_id; } telegramMessage messages[HANDLE_MESSAGES]; long last_message_received; String name; @@ -125,7 +138,7 @@ class UniversalTelegramBot { unsigned int waitForResponse = 1500; int _lastError; int last_sent_message_id = 0; - int maxMessageLength = 1500; + int maxMessageLength = TELEGRAM_MAX_MESSAGE_LENGTH; private: // JsonObject * parseUpdates(String response);