diff --git a/.travis.yml b/.travis.yml index 3d93c8c..8fb1cd3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,8 @@ env: - SCRIPT=platformioSingle EXAMPLE_NAME=PhotoFromSD EXAMPLE_FOLDER=/SendPhoto/ BOARDTYPE=ESP8266 BOARD=d1_mini - SCRIPT=platformioSingle EXAMPLE_NAME=PhotoFromURL EXAMPLE_FOLDER=/SendPhoto/ BOARDTYPE=ESP8266 BOARD=d1_mini - SCRIPT=platformioSingle EXAMPLE_NAME=PhotoFromFileID EXAMPLE_FOLDER=/SendPhoto/ BOARDTYPE=ESP8266 BOARD=d1_mini + - SCRIPT=platformioSingle EXAMPLE_NAME=Location EXAMPLE_FOLDER=/ BOARDTYPE=ESP8266 BOARD=d1_mini + - SCRIPT=platformioSingle EXAMPLE_NAME=ChannelPost EXAMPLE_FOLDER=/ BOARDTYPE=ESP8266 BOARD=d1_mini install: diff --git a/README.md b/README.md index 3774034..8e70e79 100644 --- a/README.md +++ b/README.md @@ -53,9 +53,13 @@ Here is a list of features that this library covers. (Note: The examples link to |*Receiving Messages*|Your bot can read messages that are sent to it. This is useful for sending commands to your arduino such as toggle and LED|`int getUpdates(long offset)`

Gets any pending messages from Telegram and stores them in **bot.messages** . Offset should be set to **bot.last_message_received** + 1. Returns the numbers new messages received.| [FlashLED](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/FlashLED/FlashLED.ino) or any other example| |*Sending messages*|Your bot can send messages to any Telegram or group. This can be useful to get the arduino to notify you of an event e.g. Button pressed etc (Note: bots can only message you if you messaged them first)|`bool sendMessage(String chat_id, String text, String parse_mode = "")`

Sends the message to the chat_id. Returns if the message sent or not.| [EchoBot](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/EchoBot/EchoBot.ino#L51) or any other example| |*Reply Keyboards*|Your bot can send [reply keyboards](https://camo.githubusercontent.com/2116a60fa614bf2348074a9d7148f7d0a7664d36/687474703a2f2f692e696d6775722e636f6d2f325268366c42672e6a70673f32) that can be used as a type of menu.|`bool sendMessageWithReplyKeyboard(String chat_id, String text, String parse_mode, String keyboard, bool resize = false, bool oneTime = false, bool selective = false)`

Send a keyboard to the specified chat_id. parse_mode can be left blank. Will return true if the message sends successfully.| [ReplyKeyboard](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/CustomKeyboard/ReplyKeyboardMarkup/ReplyKeyboardMarkup.ino)| -|*Inline Keyboards*|Your bot can send [inline keyboards](https://camo.githubusercontent.com/55dde972426e5bc77120ea17a9c06bff37856eb6/68747470733a2f2f636f72652e74656c656772616d2e6f72672f66696c652f3831313134303939392f312f324a536f55566c574b61302f346661643265323734336463386564613034).

Note: Only URLS are supported currently|`bool sendMessageWithInlineKeyboard(String chat_id, String text, String parse_mode, String keyboard)`

Send a keyboard to the specified chat_id. parse_mode can be left blank. Will return true if the message sends successfully.| [InlineKeyboard](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/CustomKeyboard/InlineKeyboardMarkup/InlineKeyboardMarkup.ino)| +|*Inline Keyboards*|Your bot can send [inline keyboards](https://camo.githubusercontent.com/55dde972426e5bc77120ea17a9c06bff37856eb6/68747470733a2f2f636f72652e74656c656772616d2e6f72672f66696c652f3831313134303939392f312f324a536f55566c574b61302f346661643265323734336463386564613034).

Note: URLS & callbacks are supported currently|`bool sendMessageWithInlineKeyboard(String chat_id, String text, String parse_mode, String keyboard)`

Send a keyboard to the specified chat_id. parse_mode can be left blank. Will return true if the message sends successfully.| [InlineKeyboard](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/CustomKeyboard/InlineKeyboardMarkup/InlineKeyboardMarkup.ino)| |*Send Photos*|It is possible to send phtos from your bot. You can send images from the web or from the arduino directly (Only sending from an SD card has been tested, but it should be able to send from a camera module)|Check the examples for more info| [From URL](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/SendPhoto/PhotoFromURL/PhotoFromURL.ino)

[Binary from SD](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/SendPhoto/PhotoFromSD/PhotoFromSD.ino)

[From File Id](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/blob/master/examples/ESP8266/SendPhoto/PhotoFromFileID/PhotoFromFileID.ino)| -|*Chat Actions*|Your bot can send chat actions, such as *typing* or *sending photo* to let the user know that the bot is doing something. |`bool sendChatAction(String chat_id, String chat_action)`

Send a the chat action to the specified chat_id. There is a set list of chat actions that Telegram support, see the example for details. Will return true if the chat actions sends successfully.| [InlineKeyboard](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/tree/master/examples/ESP8266/ChatAction)| +|*Chat Actions*|Your bot can send chat actions, such as *typing* or *sending photo* to let the user know that the bot is doing something. |`bool sendChatAction(String chat_id, String chat_action)`

Send a the chat action to the specified chat_id. There is a set list of chat actions that Telegram support, see the example for details. Will return true if the chat actions sends successfully.| +|*Location*|Your bot can receive location data, either from a single location data point or live location data. |Check the example.| [Location](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/tree/master/examples/ESP8266/Location/Location.ino)| +|*Channel Post*|Reads posts from channels. |Check the example.| [ChannelPost](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/tree/master/examples/ESP8266/ChannelPost/ChannelPost.ino)| +|*Channel Post*|Reads posts from channels. |Check the example.| [ChannelPost](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/tree/master/examples/ESP8266/ChannelPost/ChannelPost.ino)| +|*Long Poll*|Set how long the bot will wait checking for a new message before returning now messages.

This will decrease the amount of requests and data used by the bot, but it will tie up the arduino while it waits for messages |`bot.LongPoll = 60;`

Where 60 is the amount of seconds it should wait | [LongPoll](https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/tree/master/examples/ESP8266/LongPoll/LongPoll.ino)| The full Telegram Bot API documentation can be read [here](https://core.telegram.org/bots/api). If there is a feature you would like added to the library please either raise a Github issue or please feel free to raise a Pull Request. diff --git a/examples/ESP8266/ChannelPost/ChannelPost.ino b/examples/ESP8266/ChannelPost/ChannelPost.ino new file mode 100644 index 0000000..05ba421 --- /dev/null +++ b/examples/ESP8266/ChannelPost/ChannelPost.ino @@ -0,0 +1,68 @@ +/******************************************************************* +* An example of bot that echos back any messages received, +* including ones from channels +* +* written by Brian Lough +*******************************************************************/ +#include +#include +#include + +// Initialize Wifi connection to the router +char ssid[] = "XXXXXX"; // your network SSID (name) +char password[] = "YYYYYY"; // your network key + +// Initialize Telegram BOT +#define BOTtoken "XXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // your Bot Token (Get from Botfather) + +WiFiClientSecure client; +UniversalTelegramBot bot(BOTtoken, client); + +int Bot_mtbs = 1000; //mean time between scan messages +long Bot_lasttime; //last time messages' scan has been done + +void setup() { + Serial.begin(115200); + + // Set WiFi to station mode and disconnect from an AP if it was Previously + // connected + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + // Attempt to connect to Wifi network: + Serial.print("Connecting Wifi: "); + Serial.println(ssid); + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + if (millis() > Bot_lasttime + Bot_mtbs) { + int numNewMessages = bot.getUpdates(bot.last_message_received + 1); + + while(numNewMessages) { + Serial.println("got response"); + for (int i=0; i #include @@ -22,27 +22,35 @@ int Bot_mtbs = 1000; //mean time between scan messages long Bot_lasttime; //last time messages' scan has been done void handleNewMessages(int numNewMessages) { - Serial.println("handleNewMessages"); - Serial.println(String(numNewMessages)); - - for (int i=0; i Bot_lasttime + Bot_mtbs) { int numNewMessages = bot.getUpdates(bot.last_message_received + 1); - while(numNewMessages) { + while (numNewMessages) { Serial.println("got response"); handleNewMessages(numNewMessages); numNewMessages = bot.getUpdates(bot.last_message_received + 1); diff --git a/examples/ESP8266/Location/Location.ino b/examples/ESP8266/Location/Location.ino new file mode 100644 index 0000000..55ee574 --- /dev/null +++ b/examples/ESP8266/Location/Location.ino @@ -0,0 +1,88 @@ +/******************************************************************* + * An example of recieving location Data + * + * + * By Brian Lough + *******************************************************************/ +#include +#include +#include + +// Initialize Wifi connection to the router +char ssid[] = "SSID"; // your network SSID (name) +char password[] = "password"; // your network key + +// Initialize Telegram BOT +#define BOTtoken "XXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // your Bot Token (Get from Botfather) + +WiFiClientSecure client; +UniversalTelegramBot bot(BOTtoken, client); + +int Bot_mtbs = 1000; //mean time between scan messages +long Bot_lasttime; //last time messages' scan has been done + +void handleNewMessages(int numNewMessages) { + + for (int i = 0; i < numNewMessages; i++) { + + String chat_id = String(bot.messages[i].chat_id); + String text = bot.messages[i].text; + + String from_name = bot.messages[i].from_name; + if (from_name == "") from_name = "Guest"; + + if (bot.messages[i].longitude != 0 || bot.messages[i].latitude != 0) { + Serial.print("Long: "); + Serial.println(String(bot.messages[i].longitude, 6)); + Serial.print("Lat: "); + Serial.println(String(bot.messages[i].latitude, 6)); + + String message = "Long: " + String(bot.messages[i].longitude, 6) + "\n"; + message += "Lat: " + String(bot.messages[i].latitude, 6) + "\n"; + bot.sendMessage(chat_id, message, "Markdown"); + } else if (text == "/start") { + String welcome = "Welcome to Universal Arduino Telegram Bot library, " + from_name + ".\n"; + welcome += "Share a location or a live location and the bot will respond with the co-ords\n"; + + bot.sendMessage(chat_id, welcome, "Markdown"); + } + } +} + +void setup() { + Serial.begin(115200); + + // Set WiFi to station mode and disconnect from an AP if it was Previously connected + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + // attempt to connect to Wifi network: + Serial.print("Connecting Wifi: "); + Serial.println(ssid); + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + if (millis() > Bot_lasttime + Bot_mtbs) { + int numNewMessages = bot.getUpdates(bot.last_message_received + 1); + + while (numNewMessages) { + Serial.println("got response"); + handleNewMessages(numNewMessages); + numNewMessages = bot.getUpdates(bot.last_message_received + 1); + } + + Bot_lasttime = millis(); + } +} diff --git a/examples/ESP8266/LongPoll/LongPoll.ino b/examples/ESP8266/LongPoll/LongPoll.ino new file mode 100644 index 0000000..9840ba0 --- /dev/null +++ b/examples/ESP8266/LongPoll/LongPoll.ino @@ -0,0 +1,68 @@ +/******************************************************************* +* An example of setting a long poll, this will mean the request +* for new messages will wait the specified amount of time before +* returning with no messages +* +* This should reduce amount of data used by the bot +* +* written by Brian Lough +*******************************************************************/ +#include +#include +#include + +// Initialize Wifi connection to the router +char ssid[] = "XXXXXX"; // your network SSID (name) +char password[] = "YYYYYY"; // your network key + +// Initialize Telegram BOT +#define BOTtoken "XXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // your Bot Token (Get from Botfather) + +WiFiClientSecure client; +UniversalTelegramBot bot(BOTtoken, client); + +int Bot_mtbs = 1000; //mean time between scan messages +long Bot_lasttime; //last time messages' scan has been done + +void setup() { + Serial.begin(115200); + + // Set WiFi to station mode and disconnect from an AP if it was Previously + // connected + WiFi.mode(WIFI_STA); + WiFi.disconnect(); + delay(100); + + // Attempt to connect to Wifi network: + Serial.print("Connecting Wifi: "); + Serial.println(ssid); + WiFi.begin(ssid, password); + + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + + bot.longPoll = 60; + + Serial.println(""); + Serial.println("WiFi connected"); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); +} + +void loop() { + if (millis() > Bot_lasttime + Bot_mtbs) { + int numNewMessages = bot.getUpdates(bot.last_message_received + 1); + + while(numNewMessages) { + Serial.println("got response"); + for (int i=0; i sentence=Arduino Telegram Bot library for multiple different architectures. diff --git a/src/UniversalTelegramBot.cpp b/src/UniversalTelegramBot.cpp index fdff248..80d655d 100644 --- a/src/UniversalTelegramBot.cpp +++ b/src/UniversalTelegramBot.cpp @@ -34,14 +34,16 @@ String UniversalTelegramBot::sendGetToTelegram(String command) { // Connect with api.telegram.org if (client->connect(HOST, SSL_PORT)) { - if (_debug) Serial.println(".... connected to server"); + + if (_debug) Serial.println(F(".... connected to server")); + String a=""; char c; int ch_count=0; client->println("GET /"+command); now=millis(); avail=false; - while (millis() - now<1500) { + while (millis() - now < longPoll * 1000 + 1500) { while (client->available()) { char c = client->read(); //Serial.write(c); @@ -75,15 +77,15 @@ String UniversalTelegramBot::sendPostToTelegram(String command, JsonObject& payl // Connect with api.telegram.org if (client->connect(HOST, SSL_PORT)) { // POST URI - client->print("POST /" + command); client->println(" HTTP/1.1"); + client->print("POST /" + command); client->println(F(" HTTP/1.1")); // Host header - client->print("Host:"); client->println(HOST); + client->print(F("Host:")); client->println(HOST); // JSON content type - client->println("Content-Type: application/json"); + client->println(F("Content-Type: application/json")); // Content length int length = payload.measureLength(); - client->print("Content-Length:"); client->println(length); + client->print(F("Content-Length:")); client->println(length); // End of headers client->println(); // POST message body @@ -150,7 +152,7 @@ String UniversalTelegramBot::sendMultipartFormDataToTelegram(String command, Str String headers = ""; long now; bool responseReceived; - String boundry = "------------------------b8f610217e83e29b"; + String boundry = F("------------------------b8f610217e83e29b"); // Connect with api.telegram.org if (client->connect(HOST, SSL_PORT)) { @@ -171,11 +173,11 @@ String UniversalTelegramBot::sendMultipartFormDataToTelegram(String command, Str end_request = end_request + "\r\n"; end_request = end_request + "--" + boundry + "--" + "\r\n"; - client->print("POST /bot"+_token+"/" + command); client->println(" HTTP/1.1"); + client->print("POST /bot"+_token+"/" + command); client->println(F(" HTTP/1.1")); // Host header - client->print("Host: "); client->println(HOST); - client->println("User-Agent: arduino/1.0"); - client->println("Accept: */*"); + client->print(F("Host: ")); client->println(HOST); + client->println(F("User-Agent: arduino/1.0")); + client->println(F("Accept: */*")); int contentLength = fileSize + start_request.length() + end_request.length(); if (_debug) Serial.println("Content-Length: " + String(contentLength)); @@ -198,7 +200,7 @@ String UniversalTelegramBot::sendMultipartFormDataToTelegram(String command, Str if(count == 512){ //yield(); if (_debug) { - Serial.println("Sending full buffer"); + Serial.println(F("Sending full buffer")); } client->write((const uint8_t *)buffer, 512); count = 0; @@ -207,7 +209,7 @@ String UniversalTelegramBot::sendMultipartFormDataToTelegram(String command, Str if(count > 0) { if (_debug) { - Serial.println("Sending remaining buffer"); + Serial.println(F("Sending remaining buffer")); } client->write((const uint8_t *)buffer, count); } @@ -292,6 +294,9 @@ int UniversalTelegramBot::getUpdates(long offset) { if (_debug) Serial.println("GET Update Messages"); String command = "bot"+_token+"/getUpdates?offset="+String(offset)+"&limit="+String(HANDLE_MESSAGES); + if(longPoll > 0) { + command = command + "&timeout=" + String(longPoll); + } String response = sendGetToTelegram(command); //receive reply from telegram.org if (response != "") { @@ -321,14 +326,14 @@ int UniversalTelegramBot::getUpdates(long offset) { } return newMessageIndex; } else { - if (_debug) Serial.println("no new messages"); + if (_debug) Serial.println(F("no new messages")); } } else { - if (_debug) Serial.println("Response contained no 'result'"); + if (_debug) Serial.println(F("Response contained no 'result'")); } } else { // Buffer may not be big enough, increase buffer or reduce max number of messages - if (_debug) Serial.println("Failed to parse update, the message could be too big for the buffer"); + if (_debug) Serial.println(F("Failed to parse update, the message could be too big for the buffer")); } return 0; @@ -340,46 +345,72 @@ bool UniversalTelegramBot::processResult(JsonObject& result, int messageIndex) { // Check have we already dealt with this message (this shouldn't happen!) if (last_message_received != update_id) { last_message_received = update_id; + messages[messageIndex].update_id = update_id; - String text; - String date; - String chat_id; - String chat_title; - String from_id; - String from_name; - String type; - + messages[messageIndex].text = F(""); + messages[messageIndex].from_id = F(""); + messages[messageIndex].from_name = F(""); + messages[messageIndex].longitude = 0; + messages[messageIndex].latitude = 0; if (result.containsKey("message")) { JsonObject& message = result["message"]; - type = "message"; - from_id = message["from"]["id"].as(); - from_name = message["from"]["first_name"].as(); - - text = message["text"].as(); - date = message["date"].as(); - chat_id = message["chat"]["id"].as(); - chat_title = message["chat"]["title"].as(); + messages[messageIndex].type = F("message"); + messages[messageIndex].from_id = message["from"]["id"].as(); + messages[messageIndex].from_name = message["from"]["first_name"].as(); + + messages[messageIndex].date = message["date"].as(); + messages[messageIndex].chat_id = message["chat"]["id"].as(); + messages[messageIndex].chat_title = message["chat"]["title"].as(); + + if (message.containsKey("text")) { + messages[messageIndex].text = message["text"].as(); + + } else if (message.containsKey("location")) { + messages[messageIndex].longitude = message["location"]["longitude"].as(); + messages[messageIndex].latitude = message["location"]["latitude"].as(); + + } } else if (result.containsKey("channel_post")) { JsonObject& message = result["channel_post"]; - type = "channel_post"; + messages[messageIndex].type = F("channel_post"); + + messages[messageIndex].text = message["text"].as(); + messages[messageIndex].date = message["date"].as(); + messages[messageIndex].chat_id = message["chat"]["id"].as(); + messages[messageIndex].chat_title = message["chat"]["title"].as(); + + } else if (result.containsKey("callback_query")) { + JsonObject& message = result["callback_query"]; + messages[messageIndex].type = F("callback_query"); + messages[messageIndex].from_id = message["from"]["id"].as(); + messages[messageIndex].from_name = message["from"]["first_name"].as(); + + messages[messageIndex].text = message["data"].as(); + messages[messageIndex].date = message["date"].as(); + messages[messageIndex].chat_id = F(""); + messages[messageIndex].chat_title = F(""); + } else if (result.containsKey("edited_message")) { + JsonObject& message = result["edited_message"]; + messages[messageIndex].type = F("edited_message"); + messages[messageIndex].from_id = message["from"]["id"].as(); + messages[messageIndex].from_name = message["from"]["first_name"].as(); + + messages[messageIndex].date = message["date"].as(); + messages[messageIndex].chat_id = message["chat"]["id"].as(); + messages[messageIndex].chat_title = message["chat"]["title"].as(); + + if (message.containsKey("text")) { + messages[messageIndex].text = message["text"].as(); + + } else if (message.containsKey("location")) { + messages[messageIndex].longitude = message["location"]["longitude"].as(); + messages[messageIndex].latitude = message["location"]["latitude"].as(); - text = message["text"].as(); - date = message["date"].as(); - chat_id = message["chat"]["id"].as(); - chat_title = message["chat"]["title"].as(); + } } - messages[messageIndex].update_id = update_id; - messages[messageIndex].text = text; - messages[messageIndex].date = date; - messages[messageIndex].chat_id = chat_id; - messages[messageIndex].chat_title = chat_title; - messages[messageIndex].from_id = from_id; - messages[messageIndex].from_name = from_name; - messages[messageIndex].type = type; - return true; } return false; @@ -490,7 +521,7 @@ bool UniversalTelegramBot::sendMessageWithInlineKeyboard(String chat_id, String bool UniversalTelegramBot::sendPostMessage(JsonObject& payload) { bool sent=false; - if (_debug) Serial.println("SEND Post Message"); + if (_debug) Serial.println(F("SEND Post Message")); long sttime=millis(); if (payload.containsKey("text")) { @@ -512,7 +543,7 @@ String UniversalTelegramBot::sendPostPhoto(JsonObject& payload) { bool sent=false; String response = ""; - if (_debug) Serial.println("SEND Post Photo"); + if (_debug) Serial.println(F("SEND Post Photo")); long sttime=millis(); if (payload.containsKey("photo")) { @@ -590,7 +621,7 @@ bool UniversalTelegramBot::checkForOkResponse(String response) { bool UniversalTelegramBot::sendChatAction(String chat_id, String text) { bool sent = false; - if (_debug) Serial.println("SEND Chat Action Message"); + if (_debug) Serial.println(F("SEND Chat Action Message")); long sttime = millis(); if (text != "") { diff --git a/src/UniversalTelegramBot.h b/src/UniversalTelegramBot.h index 56f2263..f0852f7 100644 --- a/src/UniversalTelegramBot.h +++ b/src/UniversalTelegramBot.h @@ -42,6 +42,8 @@ struct telegramMessage{ String from_name; String date; String type; + float longitude; + float latitude; int update_id; }; @@ -83,6 +85,8 @@ class UniversalTelegramBot long last_message_received; String name; String userName; + int longPoll = 0; + bool _debug = false; private: //JsonObject * parseUpdates(String response); @@ -90,7 +94,6 @@ class UniversalTelegramBot Client *client; bool processResult(JsonObject& result, int messageIndex); const int maxMessageLength = 1300; - bool _debug = false; }; #endif