From 99072def67e54d664edd96b9c0f124c4f09cedee Mon Sep 17 00:00:00 2001 From: Oleg Morozenkov Date: Wed, 29 Jul 2015 14:45:45 +0300 Subject: Fix includes + fix some minor bugs --- CMakeLists.txt | 18 +- include/tgbot/tools/StringTools.h | 3 +- samples/echobot/src/main.cpp | 6 +- src/Api.cpp | 279 ++++++++++++++++++++++ src/TgException.cpp | 30 +++ src/TgTypeParser.cpp | 490 ++++++++++++++++++++++++++++++++++++++ src/net/HttpClient.cpp | 67 ++++++ src/net/HttpParser.cpp | 204 ++++++++++++++++ src/net/TgLongPoll.cpp | 43 ++++ src/net/Url.cpp | 85 +++++++ src/tgbot/Api.cpp | 275 --------------------- src/tgbot/TgException.cpp | 30 --- src/tgbot/TgTypeParser.cpp | 490 -------------------------------------- src/tgbot/net/HttpClient.cpp | 71 ------ src/tgbot/net/HttpParser.cpp | 204 ---------------- src/tgbot/net/TgLongPoll.cpp | 43 ---- src/tgbot/net/Url.cpp | 78 ------ src/tgbot/tools/StringTools.cpp | 113 --------- src/tools/StringTools.cpp | 113 +++++++++ 19 files changed, 1323 insertions(+), 1319 deletions(-) create mode 100644 src/Api.cpp create mode 100644 src/TgException.cpp create mode 100644 src/TgTypeParser.cpp create mode 100644 src/net/HttpClient.cpp create mode 100644 src/net/HttpParser.cpp create mode 100644 src/net/TgLongPoll.cpp create mode 100644 src/net/Url.cpp delete mode 100644 src/tgbot/Api.cpp delete mode 100644 src/tgbot/TgException.cpp delete mode 100644 src/tgbot/TgTypeParser.cpp delete mode 100644 src/tgbot/net/HttpClient.cpp delete mode 100644 src/tgbot/net/HttpParser.cpp delete mode 100644 src/tgbot/net/TgLongPoll.cpp delete mode 100644 src/tgbot/net/Url.cpp delete mode 100644 src/tgbot/tools/StringTools.cpp create mode 100644 src/tools/StringTools.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ba9ae76..8fd3321 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,16 +7,16 @@ option(ENABLE_SAMPLES "Set to ON to enable building of samples" OFF) ### sources set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -include_directories("${PROJECT_SOURCE_DIR}/include") +include_directories(include) set(SRC_LIST - src/tgbot/Api.cpp - src/tgbot/TgTypeParser.cpp - src/tgbot/TgException.cpp - src/tgbot/net/Url.cpp - src/tgbot/net/HttpClient.cpp - src/tgbot/net/HttpParser.cpp - src/tgbot/net/TgLongPoll.cpp - src/tgbot/tools/StringTools.cpp + src/Api.cpp + src/TgTypeParser.cpp + src/TgException.cpp + src/net/Url.cpp + src/net/HttpClient.cpp + src/net/HttpParser.cpp + src/net/TgLongPoll.cpp + src/tools/StringTools.cpp ) ### libs diff --git a/include/tgbot/tools/StringTools.h b/include/tgbot/tools/StringTools.h index 0a01b0d..a8b4af1 100644 --- a/include/tgbot/tools/StringTools.h +++ b/include/tgbot/tools/StringTools.h @@ -63,9 +63,10 @@ std::string generateRandomString(size_t length); /** * Performs url encode. * @param value Source url string + * @param additionalLegitChars Optional. String of chars which will be not encoded in source url string. * @return Encoded url string */ -std::string urlEncode(const std::string& value); +std::string urlEncode(const std::string& value, const std::string additionalLegitChars = ""); /** * Performs url decode. diff --git a/samples/echobot/src/main.cpp b/samples/echobot/src/main.cpp index 7b8d880..61f4019 100644 --- a/samples/echobot/src/main.cpp +++ b/samples/echobot/src/main.cpp @@ -26,8 +26,6 @@ #include #include -#include -#include using namespace std; using namespace TgBot; @@ -52,7 +50,6 @@ int main() { bot.getApi().sendMessage(message->chat->id, "Your message is: " + message->text); }); - printf("try {\n"); try { printf("Bot username: %s\n", bot.getApi().getMe()->username.c_str()); @@ -61,10 +58,9 @@ int main() { printf("Long poll started\n"); longPoll.start(); } - } catch (TgException& e) { + } catch (exception& e) { printf("error: %s\n", e.what()); } - printf("}\n"); return 0; } diff --git a/src/Api.cpp b/src/Api.cpp new file mode 100644 index 0000000..0c04081 --- /dev/null +++ b/src/Api.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2015 Oleg Morozenkov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tgbot/Api.h" + +#include "tgbot/TgTypeParser.h" +#include "tgbot/TgException.h" +#include "tgbot/net/HttpClient.h" + +using namespace std; +using namespace boost::property_tree; + +namespace TgBot { + +Api::Api(const std::string& token) : _token(token) { +} + +User::Ptr Api::getMe() const { + return TgTypeParser::getInstance().parseUser(sendRequest("getMe").find("result")->second); +} + +Message::Ptr Api::sendMessage(int32_t chatId, const string& text, bool disableWebPagePreview, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("text", text)); + if (disableWebPagePreview) { + args.push_back(HttpReqArg("disable_web_page_preview", disableWebPagePreview)); + } + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendMessage", args).find("result")->second); +} + +Message::Ptr Api::forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("from_chat_id", fromChatId)); + args.push_back(HttpReqArg("message_id", messageId)); + return TgTypeParser::getInstance().parseMessage(sendRequest("forwardMessage", args).find("result")->second); +} + +Message::Ptr Api::sendPhoto(int32_t chatId, const InputFile::Ptr& photo, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("photo", photo->data, true, photo->mimeType)); + if (!caption.empty()) { + args.push_back(HttpReqArg("caption", caption)); + } + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendPhoto", args).find("result")->second); +} + +Message::Ptr Api::sendPhoto(int32_t chatId, const string& photo, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("photo", photo)); + if (!caption.empty()) { + args.push_back(HttpReqArg("caption", caption)); + } + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendPhoto", args).find("result")->second); +} + +Message::Ptr Api::sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("audio", audio->data, true, audio->mimeType)); + if (duration) { + args.push_back(HttpReqArg("duration", duration)); + } + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendAudio", args).find("result")->second); +} + +Message::Ptr Api::sendAudio(int32_t chatId, const string& audio, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("audio", audio)); + if (duration) { + args.push_back(HttpReqArg("duration", duration)); + } + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendAudio", args).find("result")->second); +} + +Message::Ptr Api::sendDocument(int32_t chatId, const InputFile::Ptr& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("document", document->data, true, document->mimeType)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendDocument", args).find("result")->second); +} + +Message::Ptr Api::sendDocument(int32_t chatId, const string& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("document", document)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendDocument", args).find("result")->second); +} + +Message::Ptr Api::sendSticker(int32_t chatId, const InputFile::Ptr& sticker, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("sticker", sticker->data, true, sticker->mimeType)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendSticker", args).find("result")->second); +} + +Message::Ptr Api::sendSticker(int32_t chatId, const string& sticker, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("sticker", sticker)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendSticker", args).find("result")->second); +} + +Message::Ptr Api::sendVideo(int32_t chatId, const InputFile::Ptr& video, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("video", video->data, true, video->mimeType)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendVideo", args).find("result")->second); +} + +Message::Ptr Api::sendVideo(int32_t chatId, const string& video, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("video", video)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendVideo", args).find("result")->second); +} + +Message::Ptr Api::sendLocation(int32_t chatId, float latitude, float longitude, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("latitude", latitude)); + args.push_back(HttpReqArg("longitude", longitude)); + if (replyToMessageId) { + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); + } + if (replyMarkup) { + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); + } + return TgTypeParser::getInstance().parseMessage(sendRequest("sendLocation", args).find("result")->second); +} + +void Api::sendChatAction(int32_t chatId, const string& action) const { + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("action", action)); + sendRequest("sendChatAction", args); +} + +UserProfilePhotos::Ptr Api::getUserProfilePhotos(int32_t userId, int32_t offset, int32_t limit) const { + vector args; + args.push_back(HttpReqArg("user_id", userId)); + if (offset) { + args.push_back(HttpReqArg("offset", offset)); + } + limit = max(1, min(100, limit)); + args.push_back(HttpReqArg("limit", limit)); + return TgTypeParser::getInstance().parseUserProfilePhotos(sendRequest("getUserProfilePhotos", args).find("result")->second); +} + +vector Api::getUpdates(int32_t offset, int32_t limit, int32_t timeout) const { + vector args; + if (offset) { + args.push_back(HttpReqArg("offset", offset)); + } + limit = max(1, min(100, limit)); + args.push_back(HttpReqArg("limit", limit)); + if (timeout) { + args.push_back(HttpReqArg("timeout", timeout)); + } + return TgTypeParser::getInstance().parseArray(TgTypeParser::getInstance().parseUpdate, sendRequest("getUpdates", args), "result"); +} + +void Api::setWebhook(const string& url) const { + vector args; + args.push_back(HttpReqArg("url", url)); + sendRequest("setWebhook", args); +} + +boost::property_tree::ptree Api::sendRequest(const std::string& method, const std::vector& args) const { + std::string url = "https://api.telegram.org/bot"; + url += _token; + url += "/"; + url += method; + string serverResponse = HttpClient::getInstance().makeRequest(url, args); + if (serverResponse.find("") != serverResponse.npos) { + throw TgException("Bad request"); + } + ptree result = TgTypeParser::getInstance().parseJson(serverResponse); + try { + if (result.get("ok")) { + return result; + } else { + throw TgException(result.get("description", "")); + } + } catch (boost::property_tree::ptree_error& e) { + throw TgException(""); + } +} + +} diff --git a/src/TgException.cpp b/src/TgException.cpp new file mode 100644 index 0000000..0c118ec --- /dev/null +++ b/src/TgException.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2015 Oleg Morozenkov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tgbot/TgException.h" + +namespace TgBot { + +TgBot::TgException::TgException(const std::string description) : runtime_error(description) { +} + +} diff --git a/src/TgTypeParser.cpp b/src/TgTypeParser.cpp new file mode 100644 index 0000000..9a1f93a --- /dev/null +++ b/src/TgTypeParser.cpp @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2015 Oleg Morozenkov + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software", "to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tgbot/TgTypeParser.h" + +using namespace std; +using namespace boost::property_tree; + +namespace TgBot { + +TgTypeParser& TgTypeParser::getInstance() { + static TgTypeParser result; + return result; +} + +User::Ptr TgTypeParser::parseUser(const ptree& data) const { + User::Ptr result(new User); + result->id = data.get("id"); + result->firstName = data.get("first_name"); + result->lastName = data.get("last_name", ""); + result->username = data.get("username", ""); + return result; +} + +string TgTypeParser::parseUser(const User::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "id", object->id); + appendToJson(result, "first_name", object->firstName); + appendToJson(result, "last_name", object->lastName); + appendToJson(result, "username", object->username); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +GroupChat::Ptr TgTypeParser::parseGroupChat(const ptree& data) const { + GroupChat::Ptr result(new GroupChat); + result->id = data.get("id"); + result->title = data.get("title"); + return result; +} + +string TgTypeParser::parseGroupChat(const GroupChat::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "id", object->id); + appendToJson(result, "title", object->title); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Message::Ptr TgTypeParser::parseMessage(const ptree& data) const { + Message::Ptr result(new Message); + result->messageId = data.get("message_id"); + result->from = parseUser(data.find("from")->second); + result->date = data.get("date"); + result->chat = parseGenericChat(data.find("chat")->second); + result->forwardFrom = tryParse(parseUser, data, "forward_from"); + result->forwardDate = data.get("forward_date", 0); + result->replyToMessage = tryParse(parseMessage, data, "reply_to_message"); + result->text = data.get("text", ""); + result->audio = tryParse