From 51d2176d1535c8c8176426909e1c7b70633d794b Mon Sep 17 00:00:00 2001 From: Oleg Morozenkov Date: Thu, 9 Jul 2015 15:03:59 +0300 Subject: Refactoring --- src/tgbot/Api.cpp | 207 ++++++++------- src/tgbot/Api.h | 10 +- src/tgbot/Bot.cpp | 52 ---- src/tgbot/Bot.h | 43 +-- src/tgbot/EventBroadcaster.h | 96 +++++++ src/tgbot/EventHandler.h | 56 ++++ src/tgbot/EventManager.cpp | 56 ---- src/tgbot/EventManager.h | 75 ------ src/tgbot/Http.cpp | 172 ------------ src/tgbot/Http.h | 125 --------- src/tgbot/Parser.cpp | 485 ---------------------------------- src/tgbot/Parser.h | 178 ------------- src/tgbot/TgTypeParser.cpp | 490 +++++++++++++++++++++++++++++++++++ src/tgbot/TgTypeParser.h | 180 +++++++++++++ src/tgbot/Url.cpp | 65 ----- src/tgbot/Url.h | 43 --- src/tgbot/net/HttpClient.cpp | 71 +++++ src/tgbot/net/HttpClient.h | 49 ++++ src/tgbot/net/HttpParser.cpp | 204 +++++++++++++++ src/tgbot/net/HttpParser.h | 68 +++++ src/tgbot/net/HttpReqArg.h | 50 ++++ src/tgbot/net/HttpServer.h | 93 +++++++ src/tgbot/net/TgLongPoll.cpp | 43 +++ src/tgbot/net/TgLongPoll.h | 48 ++++ src/tgbot/net/TgWebhookLocalServer.h | 47 ++++ src/tgbot/net/TgWebhookServer.h | 57 ++++ src/tgbot/net/TgWebhookTcpServer.h | 46 ++++ src/tgbot/net/Url.cpp | 78 ++++++ src/tgbot/net/Url.h | 44 ++++ src/tgbot/tools/StringTools.cpp | 31 ++- src/tgbot/tools/StringTools.h | 1 + 31 files changed, 1861 insertions(+), 1402 deletions(-) delete mode 100644 src/tgbot/Bot.cpp create mode 100644 src/tgbot/EventBroadcaster.h create mode 100644 src/tgbot/EventHandler.h delete mode 100644 src/tgbot/EventManager.cpp delete mode 100644 src/tgbot/EventManager.h delete mode 100644 src/tgbot/Http.cpp delete mode 100644 src/tgbot/Http.h delete mode 100644 src/tgbot/Parser.cpp delete mode 100644 src/tgbot/Parser.h create mode 100644 src/tgbot/TgTypeParser.cpp create mode 100644 src/tgbot/TgTypeParser.h delete mode 100644 src/tgbot/Url.cpp delete mode 100644 src/tgbot/Url.h create mode 100644 src/tgbot/net/HttpClient.cpp create mode 100644 src/tgbot/net/HttpClient.h create mode 100644 src/tgbot/net/HttpParser.cpp create mode 100644 src/tgbot/net/HttpParser.h create mode 100644 src/tgbot/net/HttpReqArg.h create mode 100644 src/tgbot/net/HttpServer.h create mode 100644 src/tgbot/net/TgLongPoll.cpp create mode 100644 src/tgbot/net/TgLongPoll.h create mode 100644 src/tgbot/net/TgWebhookLocalServer.h create mode 100644 src/tgbot/net/TgWebhookServer.h create mode 100644 src/tgbot/net/TgWebhookTcpServer.h create mode 100644 src/tgbot/net/Url.cpp create mode 100644 src/tgbot/net/Url.h (limited to 'src/tgbot') diff --git a/src/tgbot/Api.cpp b/src/tgbot/Api.cpp index 9dd7314..8da88c8 100644 --- a/src/tgbot/Api.cpp +++ b/src/tgbot/Api.cpp @@ -22,241 +22,240 @@ #include "Api.h" -#include - -#include "tgbot/Bot.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(Bot* const bot) : _bot(bot) { +Api::Api(const std::string& token) : _token(token) { } User::Ptr Api::getMe() const { - return _bot->getParser().parseUser(sendRequest("getMe").find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("text", text)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("text", text)); if (disableWebPagePreview) { - args.push_back(Http::Argument("disable_web_page_preview", disableWebPagePreview)); + args.push_back(HttpReqArg("disable_web_page_preview", disableWebPagePreview)); } if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendMessage", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("from_chat_id", fromChatId)); - args.push_back(Http::Argument("message_id", messageId)); - return _bot->getParser().parseMessage(sendRequest("forwardMessage", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("photo", photo->data, true, photo->mimeType)); + 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(Http::Argument("caption", caption)); + args.push_back(HttpReqArg("caption", caption)); } if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendPhoto", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("photo", photo)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("photo", photo)); if (!caption.empty()) { - args.push_back(Http::Argument("caption", caption)); + args.push_back(HttpReqArg("caption", caption)); } if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendPhoto", args).find("result")->second); + return TgTypeParser::getInstance().parseMessage(sendRequest("sendPhoto", args).find("result")->second); } Message::Ptr Api::sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector args; - args.push_back(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("audio", audio->data, true, audio->mimeType)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("audio", audio->data, true, audio->mimeType)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendAudio", args).find("result")->second); + return TgTypeParser::getInstance().parseMessage(sendRequest("sendAudio", args).find("result")->second); } Message::Ptr Api::sendAudio(int32_t chatId, const string& audio, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector args; - args.push_back(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("audio", audio)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("audio", audio)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendAudio", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("document", document->data, true, document->mimeType)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("document", document->data, true, document->mimeType)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendDocument", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("document", document)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("document", document)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendDocument", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("sticker", sticker->data, true, sticker->mimeType)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("sticker", sticker->data, true, sticker->mimeType)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendSticker", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("sticker", sticker)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("sticker", sticker)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendSticker", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("video", video->data, true, video->mimeType)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("video", video->data, true, video->mimeType)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendVideo", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("video", video)); + vector args; + args.push_back(HttpReqArg("chat_id", chatId)); + args.push_back(HttpReqArg("video", video)); if (replyToMessageId) { - args.push_back(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendVideo", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("latitude", latitude)); - args.push_back(Http::Argument("longitude", longitude)); + 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(Http::Argument("reply_to_message_id", replyToMessageId)); + args.push_back(HttpReqArg("reply_to_message_id", replyToMessageId)); } if (replyMarkup) { - args.push_back(Http::Argument("reply_markup", _bot->getParser().parseGenericReply(replyMarkup))); + args.push_back(HttpReqArg("reply_markup", TgTypeParser::getInstance().parseGenericReply(replyMarkup))); } - return _bot->getParser().parseMessage(sendRequest("sendLocation", args).find("result")->second); + 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(Http::Argument("chat_id", chatId)); - args.push_back(Http::Argument("action", action)); + 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(Http::Argument("user_id", userId)); + vector args; + args.push_back(HttpReqArg("user_id", userId)); if (offset) { - args.push_back(Http::Argument("offset", offset)); + args.push_back(HttpReqArg("offset", offset)); } limit = max(1, min(100, limit)); - args.push_back(Http::Argument("limit", limit)); - return _bot->getParser().parseUserProfilePhotos(sendRequest("getUserProfilePhotos", args).find("result")->second); + 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; + vector args; if (offset) { - args.push_back(Http::Argument("offset", offset)); + args.push_back(HttpReqArg("offset", offset)); } limit = max(1, min(100, limit)); - args.push_back(Http::Argument("limit", limit)); + args.push_back(HttpReqArg("limit", limit)); if (timeout) { - args.push_back(Http::Argument("timeout", timeout)); + args.push_back(HttpReqArg("timeout", timeout)); } - return _bot->getParser().parseArray(_bot->getParser().parseUpdate, sendRequest("getUpdates", args), "result"); + return TgTypeParser::getInstance().parseArray(TgTypeParser::getInstance().parseUpdate, sendRequest("getUpdates", args), "result"); } void Api::setWebhook(const string& url) const { - vector args; - args.push_back(Http::Argument("url", url)); + 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 { +boost::property_tree::ptree Api::sendRequest(const std::string& method, const std::vector& args) const { std::string url = "https://api.telegram.org/bot"; - url += _bot->getToken(); + url += _token; url += "/"; url += method; try { - ptree result = _bot->getParser().parseJson(_bot->getHttp().makeRequest(url, args)); + ptree result = TgTypeParser::getInstance().parseJson(HttpClient::getInstance().makeRequest(url, args)); if (result.get("ok")) { return result; } else { diff --git a/src/tgbot/Api.h b/src/tgbot/Api.h index c10fe56..384de55 100644 --- a/src/tgbot/Api.h +++ b/src/tgbot/Api.h @@ -28,7 +28,7 @@ #include -#include "tgbot/Http.h" +#include "tgbot/net/HttpReqArg.h" #include "tgbot/types/User.h" #include "tgbot/types/Message.h" #include "tgbot/types/GenericReply.h" @@ -45,6 +45,8 @@ class Api { friend Bot; public: + Api(const std::string& token); + User::Ptr getMe() const; Message::Ptr sendMessage(int32_t chatId, const std::string& text, bool disableWebPagePreview = false, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; Message::Ptr forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const; @@ -65,11 +67,9 @@ public: void setWebhook(const std::string& url = "") const; private: - explicit Api(Bot* const bot); - - boost::property_tree::ptree sendRequest(const std::string& method, const std::vector& args = std::vector()) const; + boost::property_tree::ptree sendRequest(const std::string& method, const std::vector& args = std::vector()) const; - Bot* const _bot; + const std::string _token; }; } diff --git a/src/tgbot/Bot.cpp b/src/tgbot/Bot.cpp deleted file mode 100644 index 091b548..0000000 --- a/src/tgbot/Bot.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 "Bot.h" - -using namespace std; -using namespace boost::property_tree; - -namespace TgBot { - -Bot::Bot(const string& token) : - _token(token), - _api(this), - _events(this), - _http(), - _parser(), - _webhooksServerHandler([this](const string& data) { - _events.handleUpdate(_parser.parseUpdate(_parser.parseJson(data))); - }) -{ -} - -void Bot::startLongPoll() { - std::vector updates = _api.getUpdates(_lastUpdateId, 100, 60); - for (Update::Ptr& item : updates) { - if (item->updateId >= _lastUpdateId) { - _lastUpdateId = item->updateId + 1; - } - _events.handleUpdate(item); - } -} - -} diff --git a/src/tgbot/Bot.h b/src/tgbot/Bot.h index 488ceb1..eeef27a 100644 --- a/src/tgbot/Bot.h +++ b/src/tgbot/Bot.h @@ -26,25 +26,15 @@ #include #include "tgbot/Api.h" -#include "tgbot/EventManager.h" -#include "tgbot/Http.h" -#include "tgbot/Parser.h" +#include "tgbot/EventBroadcaster.h" +#include "tgbot/EventHandler.h" namespace TgBot { class Bot { public: - explicit Bot(const std::string& token); - - void startLongPoll(); - - inline void startServer(unsigned short port) { - _http.startServer(port, _webhooksServerHandler); - } - - inline void startServer(const std::string& unixSocketPath) { - _http.startServer(unixSocketPath, _webhooksServerHandler); + explicit Bot(const std::string& token) : _token(token), _api(token), _eventHandler(&_eventBroadcaster) { } inline const std::string& getToken() const { @@ -55,34 +45,19 @@ public: return _api; } - inline const EventManager& getEvents() const { - return _events; - } - - inline EventManager& getEvents() { - return _events; - } - - inline const Http& getHttp() const { - return _http; - } - - inline Http& getHttp() { - return _http; + inline EventBroadcaster& getEvents() { + return _eventBroadcaster; } - inline const Parser& getParser() const { - return _parser; + inline const EventHandler& getEventHandler() const { + return _eventHandler; } private: const std::string _token; const Api _api; - EventManager _events; - Http _http; - const Parser _parser; - const Http::ServerHandler _webhooksServerHandler; - int32_t _lastUpdateId = 0; + EventBroadcaster _eventBroadcaster; + const EventHandler _eventHandler; }; } diff --git a/src/tgbot/EventBroadcaster.h b/src/tgbot/EventBroadcaster.h new file mode 100644 index 0000000..a6074e8 --- /dev/null +++ b/src/tgbot/EventBroadcaster.h @@ -0,0 +1,96 @@ +/* + * 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. + */ + +#ifndef TGBOT_EVENTBROADCASTER_H +#define TGBOT_EVENTBROADCASTER_H + +#include +#include +#include +#include + +#include "tgbot/types/Message.h" + +namespace TgBot { + +class EventHandler; + +class EventBroadcaster { + +friend EventHandler; + +public: + typedef std::function MessageListener; + + inline void onAnyMessage(const MessageListener& listener) { + _onAnyMessageListeners.push_back(listener); + } + + inline void onCommand(const std::string& commandName, const MessageListener& listener) { + _onCommandListeners[commandName] = listener; + } + + inline void onUnknownCommand(const MessageListener& listener) { + _onUnknownCommandListeners.push_back(listener); + } + + inline void onNonCommandMessage(const MessageListener& listener) { + _onNonCommandMessageListeners.push_back(listener); + } + +private: + inline void broadcastAnyMessage(const Message::Ptr& message) const { + for (const MessageListener& item : _onAnyMessageListeners) { + item(message); + } + } + + inline bool broadcastCommand(const std::string command, const Message::Ptr& message) const { + std::map::const_iterator iter = _onCommandListeners.find(command); + if (iter == _onCommandListeners.end()) { + return false; + } + iter->second(message); + return true; + } + + inline void broadcastUnknownCommand(const Message::Ptr& message) const { + for (const MessageListener& item : _onUnknownCommandListeners) { + item(message); + } + } + + inline void broadcastNonCommandMessage(const Message::Ptr& message) const { + for (const MessageListener& item : _onNonCommandMessageListeners) { + item(message); + } + } + + std::vector _onAnyMessageListeners; + std::map _onCommandListeners; + std::vector _onUnknownCommandListeners; + std::vector _onNonCommandMessageListeners; +}; + +} + +#endif //TGBOT_EVENTBROADCASTER_H diff --git a/src/tgbot/EventHandler.h b/src/tgbot/EventHandler.h new file mode 100644 index 0000000..abcf925 --- /dev/null +++ b/src/tgbot/EventHandler.h @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef TGBOT_EVENTHANDLER_H +#define TGBOT_EVENTHANDLER_H + +#include "tgbot/EventBroadcaster.h" +#include "tgbot/types/Update.h" +#include "tgbot/tools/StringTools.h" + +namespace TgBot { + +class EventHandler { + +public: + explicit EventHandler(const EventBroadcaster* broadcaster) : _broadcaster(broadcaster) { + } + + inline void handleUpdate(const Update::Ptr& update) const { + _broadcaster->broadcastAnyMessage(update->message); + if (StringTools::startsWith(update->message->text, "/")) { + std::string command = update->message->text.substr(1, update->message->text.find(' ') - 2); + if (!_broadcaster->broadcastCommand(command, update->message)) { + _broadcaster->broadcastUnknownCommand(update->message); + } + } else { + _broadcaster->broadcastNonCommandMessage(update->message); + } + } + +private: + const EventBroadcaster* _broadcaster; +}; + +} + +#endif //TGBOT_EVENTHANDLER_H diff --git a/src/tgbot/EventManager.cpp b/src/tgbot/EventManager.cpp deleted file mode 100644 index fdd4859..0000000 --- a/src/tgbot/EventManager.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 "EventManager.h" - -#include "tgbot/tools/StringTools.h" - -using namespace std; - -namespace TgBot { - -EventManager::EventManager(Bot* const bot) : _bot(bot) { -} - -void EventManager::handleUpdate(const Update::Ptr& update) { - for (EventManager::Listener& item : _onAnyMessageListeners) { - item(update->message, _bot); - } - if (StringTools::startsWith(update->message->text, "/")) { - string command = update->message->text.substr(1, update->message->text.find(' ') - 2); - for (pair& item : _onCommandListeners) { - if (item.first == command) { - item.second(update->message, _bot); - return; - } - } - for (EventManager::Listener& item : _onUnknownCommandListeners) { - item(update->message, _bot); - } - } else { - for (EventManager::Listener& item : _onNonCommandMessageListeners) { - item(update->message, _bot); - } - } -} - -} diff --git a/src/tgbot/EventManager.h b/src/tgbot/EventManager.h deleted file mode 100644 index cfe0b04..0000000 --- a/src/tgbot/EventManager.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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. - */ - -#ifndef TGBOT_CPP_EVENTMANAGER_H -#define TGBOT_CPP_EVENTMANAGER_H - -#include -#include -#include -#include - -#include "tgbot/types/Update.h" -#include "tgbot/types/Message.h" - -namespace TgBot { - -class Bot; - -class EventManager { - -friend Bot; - -public: - typedef std::function Listener; - - inline void onAnyMessage(const Listener& listener) { - _onAnyMessageListeners.push_back(listener); - } - - inline void onCommand(const std::string& commandName, const Listener& listener) { - _onCommandListeners[commandName] = listener; - } - - inline void onUnknownCommand(const Listener& listener) { - _onUnknownCommandListeners.push_back(listener); - } - - inline void onNonCommandMessage(const Listener& listener) { - _onNonCommandMessageListeners.push_back(listener); - } - -private: - explicit EventManager(Bot* const bot); - - void handleUpdate(const Update::Ptr& update); - - Bot* const _bot; - std::vector _onAnyMessageListeners; - std::map _onCommandListeners; - std::vector _onUnknownCommandListeners; - std::vector _onNonCommandMessageListeners; -}; - -} - -#endif //TGBOT_CPP_EVENTMANAGER_H diff --git a/src/tgbot/Http.cpp b/src/tgbot/Http.cpp deleted file mode 100644 index d512e88..0000000 --- a/src/tgbot/Http.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * 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 "Http.h" - -#include - -#include - -#include "tgbot/tools/StringTools.h" - -using namespace std; -using namespace boost::asio; -using namespace boost::asio::ip; -using namespace boost::asio::local; -using boost::lexical_cast; - -namespace TgBot { - -string Http::makeRequest(const Url& url, const vector& args) { - string result; - - ssl::context context(ssl::context::sslv23); - context.set_default_verify_paths(); - - ssl::stream socket(_ioService, context); - tcp::resolver resolver(_ioService); - tcp::resolver::query query(url.host, url.protocol); - connect(socket.lowest_layer(), resolver.resolve(query)); - - socket.set_verify_mode(ssl::verify_none); - socket.set_verify_callback(ssl::rfc2818_verification(url.host)); - socket.handshake(ssl::stream::client); - - string requestText; - if (args.empty()) { - requestText += "GET "; - } else { - requestText += "POST "; - } - requestText += url.path; - requestText += url.query.empty() ? "" : "?" + url.query; - requestText += " HTTP/1.1\r\n"; - requestText += "Host: "; - requestText += url.host; - requestText += "\r\nConnection: close\r\n"; - if (args.empty()) { - requestText += "\r\n"; - } else { - string requestData; - - bool isMultipart = false; - string bondary; - srand((unsigned int) time(nullptr)); - for (const Argument& item : args) { - if (item.isFile) { - isMultipart = true; - while (bondary.empty() || item.value.find(bondary) != item.value.npos) { - bondary += StringTools::generateRandomString(4); - } - } - } - if (isMultipart) { - requestText += "Content-Type: multipart/form-data; boundary="; - requestText += bondary; - requestText += "\r\n"; - for (const Argument& item : args) { - requestData += "--"; - requestData += bondary; - requestData += "\r\nContent-Disposition: form-data; name=\""; - requestData += item.name; - requestData += "\"\r\n"; - if (item.isFile) { - requestData += "Content-Type: "; - requestData += item.mimeType; - requestData += "\r\n"; - } - requestData += "\r\n"; - requestData += item.value; - requestData += "\r\n\r\n"; - } - } else { - requestText += "Content-Type: application/x-www-form-urlencoded\r\n"; - bool firstRun = true; - for (const Argument& item : args) { - if (firstRun) { - firstRun = false; - } else { - requestData += '&'; - } - requestData += StringTools::urlEncode(item.name); - requestData += '='; - requestData += StringTools::urlEncode(item.value); - } - } - - requestText += "Content-Length: "; - requestText += lexical_cast(requestData.length()); - requestText += "\r\n\r\n"; - requestText += requestData; - } - write(socket, buffer(requestText.c_str(), requestText.length())); - - char buff[1024]; - boost::system::error_code error; - while (!error) { - size_t bytes = read(socket, buffer(buff), error); - result += string(buff, bytes); - } - - cout << "REQUEST" << endl << requestText << endl << "RESPONSE" << endl << result << endl; - - size_t headerEnd = result.find("\r\n\r\n"); - if (headerEnd == result.npos) { - headerEnd = result.find("\n\n"); - } - if (headerEnd == result.npos) { - headerEnd = 0; - } - result.erase(0, headerEnd); - - return result; -} - -void Http::startServer(unsigned short port, const Http::ServerHandler& handler) { - stopServer(); - _serverHandler = handler; - tcp::acceptor* acceptor = new tcp::acceptor(_ioService, tcp::endpoint(tcp::v4(), port)); - _tcpServer = new Server(this, acceptor); - _ioService.run(); -} - -void Http::startServer(const std::string& unixSocketPath, const Http::ServerHandler& handler) { - stopServer(); - _serverHandler = handler; - stream_protocol::acceptor* acceptor = new stream_protocol::acceptor(_ioService, stream_protocol::endpoint(unixSocketPath)); - _unixSocketServer = new Server(this, acceptor); - _ioService.run(); -} - -void Http::stopServer() { - _ioService.stop(); - if (_tcpServer != nullptr) { - delete _tcpServer; - _tcpServer = nullptr; - } - if (_unixSocketServer != nullptr) { - delete _unixSocketServer; - _unixSocketServer = nullptr; - } -} - -} \ No newline at end of file diff --git a/src/tgbot/Http.h b/src/tgbot/Http.h deleted file mode 100644 index aedcd93..0000000 --- a/src/tgbot/Http.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * 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. - */ - -#ifndef TGBOT_CPP_HTTP_H -#define TGBOT_CPP_HTTP_H - -#include - -#include -#include - -#include "tgbot/Url.h" - -namespace TgBot { - -class Http { - -public: - typedef std::function ServerHandler; - - class Argument { - - public: - template - Argument(const std::string& name, const T& value, bool isFile = false, const std::string& mimeType = "") : - name(name), value(boost::lexical_cast(value)), isFile(isFile), mimeType(mimeType) - { - } - - std::string name; - std::string value; - bool isFile = false; - std::string mimeType; - }; - - std::string makeRequest(const Url& url, const std::vector& args); - void startServer(unsigned short port, const ServerHandler& handler); - void startServer(const std::string& unixSocketPath, const ServerHandler& handler); - void stopServer(); - -private: - template - class Connection { - - public: - Connection(Http* http, boost::asio::basic_stream_socket* socket) : http(http), socket(socket) { - } - - ~Connection() { - delete socket; - } - - void start() { - data.reserve(10240); - socket->async_receive(data, [this]() { - size_t headerEnd = data.find("\r\n\r\n"); - if (headerEnd == data.npos) { - headerEnd = data.find("\n\n"); - } - if (headerEnd == data.npos) { - headerEnd = 0; - } - data.erase(0, headerEnd); - http->_serverHandler(data); - }); - } - - Http* http; - boost::asio::basic_stream_socket* socket; - std::string data; - }; - - template - class Server { - - public: - Server(Http* http, boost::asio::basic_socket_acceptor* acceptor) : http(http), acceptor(acceptor) { - } - - ~Server() { - delete acceptor; - } - - void start() { - boost::asio::basic_stream_socket* socket = new boost::asio::basic_stream_socket(acceptor->get_io_service()); - std::shared_ptr> connection(new Connection(http, socket)); - acceptor->async_accept(*connection->socket, [this, connection]() { - connection->start(); - start(); - }); - } - - Http* http; - boost::asio::basic_socket_acceptor* acceptor; - }; - - - boost::asio::io_service _ioService; - Server* _tcpServer = nullptr; - Server* _unixSocketServer = nullptr; - ServerHandler _serverHandler; -}; - -} - -#endif //TGBOT_CPP_HTTP_H diff --git a/src/tgbot/Parser.cpp b/src/tgbot/Parser.cpp deleted file mode 100644 index 3730607..0000000 --- a/src/tgbot/Parser.cpp +++ /dev/null @@ -1,485 +0,0 @@ -/* - * 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 "Parser.h" - -using namespace std; -using namespace boost::property_tree; - -namespace TgBot { - -User::Ptr Parser::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 Parser::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 Parser::parseGroupChat(const ptree& data) const { - GroupChat::Ptr result(new GroupChat); - result->id = data.get("id"); - result->title = data.get("title"); - return result; -} - -string Parser::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 Parser::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