diff options
57 files changed, 2987 insertions, 1705 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8fd3321..d48f810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,6 +20,9 @@ set(SRC_LIST ) ### libs +# threads +find_package(Threads REQUIRED) + # openssl find_package(OpenSSL REQUIRED) include_directories(${OPENSSL_INCLUDE_DIR}) @@ -30,13 +33,16 @@ find_package(Boost COMPONENTS system iostreams unit_test_framework REQUIRED) include_directories(${Boost_INCLUDE_DIR}) set(LIB_LIST - ${Boost_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} ${OPENSSL_LIBRARIES} + ${Boost_LIBRARIES} ) ### building project add_library(${PROJECT_NAME} ${SRC_LIST}) target_link_libraries(${PROJECT_NAME} ${LIB_LIST}) +install(TARGETS ${PROJECT_NAME} DESTINATION lib) +install(DIRECTORY include/ DESTINATION include) ### tests if (ENABLE_TESTS) @@ -12,7 +12,7 @@ Documentaion is located [here](http://reo7sp.ru/proj/tgbot-cpp/doc) Firstly you need to install some dependencies. You have to have Boost library at the runtime and CMake at the compilation step to be able to use this library. On Debian-based distibutives you can do it with these commands:
```sh
-sudo apt-get install cmake libboost-dev
+sudo apt-get install g++ make binutils cmake libssl-dev libboost-system-dev libboost-iostreams-dev libboost-test-dev
```
To compile the library execute this commands:
diff --git a/include/tgbot/Api.h b/include/tgbot/Api.h index 1b4987a..ebebfd7 100644 --- a/include/tgbot/Api.h +++ b/include/tgbot/Api.h @@ -49,13 +49,13 @@ class Api { friend Bot; public: - Api(const std::string& token); + Api(const std::string& token); - /** - * A simple method for testing your bot's auth token. - * @return Basic information about the bot in form of a User object. - */ - User::Ptr getMe() const; + /** + * A simple method for testing your bot's auth token. + * @return Basic information about the bot in form of a User object. + */ + User::Ptr getMe() const; /** * Use this method to send text messages. @@ -66,7 +66,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - 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 sendMessage(int32_t chatId, const std::string& text, bool disableWebPagePreview = false, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to forward messages of any kind. @@ -75,7 +75,7 @@ public: * @param messageId Unique message identifier. * @return On success, the sent message is returned. */ - Message::Ptr forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const; + Message::Ptr forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const; /** * Use this method to send photos. @@ -86,7 +86,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendPhoto(int32_t chatId, const InputFile::Ptr& photo, const std::string& caption = "", int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendPhoto(int32_t chatId, const InputFile::Ptr& photo, const std::string& caption = "", int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send photos. @@ -97,7 +97,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendPhoto(int32_t chatId, const std::string& photo, const std::string& caption = "", int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendPhoto(int32_t chatId, const std::string& photo, const std::string& caption = "", int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as Document). @@ -108,7 +108,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t duration = 0, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t duration = 0, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. For this to work, your audio must be in an .ogg file encoded with OPUS (other formats may be sent as Document). @@ -119,7 +119,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendAudio(int32_t chatId, const std::string& audio, int32_t duration = 0, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendAudio(int32_t chatId, const std::string& audio, int32_t duration = 0, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send general files. @@ -129,7 +129,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendDocument(int32_t chatId, const InputFile::Ptr& document, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendDocument(int32_t chatId, const InputFile::Ptr& document, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send general files. @@ -139,7 +139,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendDocument(int32_t chatId, const std::string& document, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendDocument(int32_t chatId, const std::string& document, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send .webp stickers. @@ -149,7 +149,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendSticker(int32_t chatId, const InputFile::Ptr& sticker, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendSticker(int32_t chatId, const InputFile::Ptr& sticker, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send .webp stickers. @@ -159,7 +159,7 @@ public: * @param replyMarkup Optional. Additional interface options. A object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendSticker(int32_t chatId, const std::string& sticker, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendSticker(int32_t chatId, const std::string& sticker, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -169,7 +169,7 @@ public: * @param replyMarkup Optional. Additional interface options. An object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendVideo(int32_t chatId, const InputFile::Ptr& video, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendVideo(int32_t chatId, const InputFile::Ptr& video, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document). @@ -179,7 +179,7 @@ public: * @param replyMarkup Optional. Additional interface options. A object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendVideo(int32_t chatId, const std::string& video, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendVideo(int32_t chatId, const std::string& video, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method to send point on the map. @@ -190,7 +190,7 @@ public: * @param replyMarkup Optional. Additional interface options. A object for a custom reply keyboard, instructions to hide keyboard or to force a reply from the user. * @return On success, the sent message is returned. */ - Message::Ptr sendLocation(int32_t chatId, float latitude, float longitude, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; + Message::Ptr sendLocation(int32_t chatId, float latitude, float longitude, int32_t replyToMessageId = 0, const GenericReply::Ptr& replyMarkup = GenericReply::Ptr()) const; /** * Use this method when you need to tell the user that something is happening on the bot's side. The status is set for 5 seconds or less (when a message arrives from your bot, Telegram clients clear its typing status). @@ -199,7 +199,7 @@ public: * @param chatId Unique identifier for the message recipient — User or GroupChat id. * @param action Type of action to broadcast. Choose one, depending on what the user is about to receive: typing for text messages, upload_photo for photos, record_video or upload_video for videos, record_audio or upload_audio for audio files, upload_document for general files, find_location for location data. */ - void sendChatAction(int32_t chatId, const std::string& action) const; + void sendChatAction(int32_t chatId, const std::string& action) const; /** * Use this method to get a list of profile pictures for a user. @@ -208,7 +208,7 @@ public: * @param limit Optional. Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. * @return A UserProfilePhotos object. */ - UserProfilePhotos::Ptr getUserProfilePhotos(int32_t userId, int32_t offset = 0, int32_t limit = 100) const; + UserProfilePhotos::Ptr getUserProfilePhotos(int32_t userId, int32_t offset = 0, int32_t limit = 100) const; /** * Use this method to receive incoming updates using long polling. @@ -219,7 +219,7 @@ public: * @param timeout Optional. Timeout in seconds for long polling. Defaults to 0, i.e. usual short polling. * @return An Array of Update objects */ - std::vector<Update::Ptr> getUpdates(int32_t offset = 0, int32_t limit = 100, int32_t timeout = 0) const; + std::vector<Update::Ptr> getUpdates(int32_t offset = 0, int32_t limit = 100, int32_t timeout = 0) const; /** * Use this method to specify a url and receive incoming updates via an outgoing webhook. Whenever there is an update for the bot, we will send an HTTPS POST request to the specified url, containing a JSON-serialized Update. In case of an unsuccessful request, we will give up after a reasonable amount of attempts. @@ -229,12 +229,12 @@ public: * Ports currently supported for Webhooks: 443, 80, 88, 8443. * @param url Optional. HTTPS url to send updates to. Use an empty string to remove webhook integration. */ - void setWebhook(const std::string& url = "") const; + void setWebhook(const std::string& url = "") const; private: - boost::property_tree::ptree sendRequest(const std::string& method, const std::vector<HttpReqArg>& args = std::vector<HttpReqArg>()) const; + boost::property_tree::ptree sendRequest(const std::string& method, const std::vector<HttpReqArg>& args = std::vector<HttpReqArg>()) const; - const std::string _token; + const std::string _token; }; } diff --git a/include/tgbot/Bot.h b/include/tgbot/Bot.h index 5e96642..36e723b 100644 --- a/include/tgbot/Bot.h +++ b/include/tgbot/Bot.h @@ -38,42 +38,42 @@ namespace TgBot { class Bot { public: - explicit Bot(const std::string& token) : _token(token), _api(token), _eventHandler(&_eventBroadcaster) { - } + explicit Bot(const std::string& token) : _token(token), _api(token), _eventHandler(&_eventBroadcaster) { + } - /** - * @return Token for accessing api. - */ - inline const std::string& getToken() const { - return _token; - } + /** + * @return Token for accessing api. + */ + inline const std::string& getToken() const { + return _token; + } - /** - * @return Object which can execute Telegram Bot API methods. - */ - inline const Api& getApi() const { - return _api; - } + /** + * @return Object which can execute Telegram Bot API methods. + */ + inline const Api& getApi() const { + return _api; + } - /** - * @return Object which holds all event listeners. - */ - inline EventBroadcaster& getEvents() { - return _eventBroadcaster; - } + /** + * @return Object which holds all event listeners. + */ + inline EventBroadcaster& getEvents() { + return _eventBroadcaster; + } - /** - * @return Object which handles new update objects. Usually it's only needed for TgLongPoll, TgWebhookLocalServer and TgWebhookTcpServer objects. - */ - inline const EventHandler& getEventHandler() const { - return _eventHandler; - } + /** + * @return Object which handles new update objects. Usually it's only needed for TgLongPoll, TgWebhookLocalServer and TgWebhookTcpServer objects. + */ + inline const EventHandler& getEventHandler() const { + return _eventHandler; + } private: - const std::string _token; - const Api _api; - EventBroadcaster _eventBroadcaster; - const EventHandler _eventHandler; + const std::string _token; + const Api _api; + EventBroadcaster _eventBroadcaster; + const EventHandler _eventHandler; }; } diff --git a/include/tgbot/EventBroadcaster.h b/include/tgbot/EventBroadcaster.h index 03d7df1..3ec8865 100644 --- a/include/tgbot/EventBroadcaster.h +++ b/include/tgbot/EventBroadcaster.h @@ -43,73 +43,73 @@ class EventBroadcaster { friend EventHandler; public: - typedef std::function<void (const Message::Ptr&)> MessageListener; - - /** - * Registers listener which receives all messages which the bot can ever receive. - * @param listener Listener. - */ - inline void onAnyMessage(const MessageListener& listener) { - _onAnyMessageListeners.push_back(listener); - } - - /** - * Registers listener which receives all messages with commands (messages with leading '/' char). - * @param commandName Command name which listener can handle. - * @param listener Listener. - */ - inline void onCommand(const std::string& commandName, const MessageListener& listener) { - _onCommandListeners[commandName] = listener; - } - - /** - * Registers listener which receives all messages with commands (messages with leading '/' char) which haven't been handled by other listeners. - * @param listener Listener. - */ - inline void onUnknownCommand(const MessageListener& listener) { - _onUnknownCommandListeners.push_back(listener); - } - - /** - * Registers listener which receives all messages without commands (messages with no leading '/' char) - * @param listener Listener. - */ - inline void onNonCommandMessage(const MessageListener& listener) { - _onNonCommandMessageListeners.push_back(listener); - } + typedef std::function<void (const Message::Ptr&)> MessageListener; + + /** + * Registers listener which receives all messages which the bot can ever receive. + * @param listener Listener. + */ + inline void onAnyMessage(const MessageListener& listener) { + _onAnyMessageListeners.push_back(listener); + } + + /** + * Registers listener which receives all messages with commands (messages with leading '/' char). + * @param commandName Command name which listener can handle. + * @param listener Listener. + */ + inline void onCommand(const std::string& commandName, const MessageListener& listener) { + _onCommandListeners[commandName] = listener; + } + + /** + * Registers listener which receives all messages with commands (messages with leading '/' char) which haven't been handled by other listeners. + * @param listener Listener. + */ + inline void onUnknownCommand(const MessageListener& listener) { + _onUnknownCommandListeners.push_back(listener); + } + + /** + * Registers listener which receives all messages without commands (messages with no leading '/' char) + * @param listener 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<std::string, MessageListener>::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<MessageListener> _onAnyMessageListeners; - std::map<std::string, MessageListener> _onCommandListeners; - std::vector<MessageListener> _onUnknownCommandListeners; - std::vector<MessageListener> _onNonCommandMessageListeners; + 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<std::string, MessageListener>::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<MessageListener> _onAnyMessageListeners; + std::map<std::string, MessageListener> _onCommandListeners; + std::vector<MessageListener> _onUnknownCommandListeners; + std::vector<MessageListener> _onNonCommandMessageListeners; }; } diff --git a/include/tgbot/EventHandler.h b/include/tgbot/EventHandler.h index abcf925..1ec2cbe 100644 --- a/include/tgbot/EventHandler.h +++ b/include/tgbot/EventHandler.h @@ -32,23 +32,23 @@ 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); - } - } + 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; + const EventBroadcaster* _broadcaster; }; } diff --git a/include/tgbot/TgException.h b/include/tgbot/TgException.h index 6f01fd3..1a3e172 100644 --- a/include/tgbot/TgException.h +++ b/include/tgbot/TgException.h @@ -35,7 +35,7 @@ namespace TgBot { class TgException : public std::runtime_error { public: - explicit TgException(const std::string description); + explicit TgException(const std::string description); }; } diff --git a/include/tgbot/TgTypeParser.h b/include/tgbot/TgTypeParser.h index 0571c25..759dc2f 100644 --- a/include/tgbot/TgTypeParser.h +++ b/include/tgbot/TgTypeParser.h @@ -50,129 +50,135 @@ namespace TgBot { class TgTypeParser { public: - static TgTypeParser& getInstance(); - - User::Ptr parseUser(const boost::property_tree::ptree& data) const; - std::string parseUser(const User::Ptr& object) const; - GroupChat::Ptr parseGroupChat(const boost::property_tree::ptree& data) const; - std::string parseGroupChat(const GroupChat::Ptr& object) const; - Message::Ptr parseMessage(const boost::property_tree::ptree& data) const; - std::string parseMessage(const Message::Ptr& object) const; - PhotoSize::Ptr parsePhotoSize(const boost::property_tree::ptree& data) const; - std::string parsePhotoSize(const PhotoSize::Ptr& object) const; - Audio::Ptr parseAudio(const boost::property_tree::ptree& data) const; - std::string parseAudio(const Audio::Ptr& object) const; - Document::Ptr parseDocument(const boost::property_tree::ptree& data) const; - std::string parseDocument(const Document::Ptr& object) const; - Sticker::Ptr parseSticker(const boost::property_tree::ptree& data) const; - std::string parseSticker(const Sticker::Ptr& object) const; - Video::Ptr parseVideo(const boost::property_tree::ptree& data) const; - std::string parseVideo(const Video::Ptr& object) const; - Contact::Ptr parseContact(const boost::property_tree::ptree& data) const; - std::string parseContact(const Contact::Ptr& object) const; - Location::Ptr parseLocation(const boost::property_tree::ptree& data) const; - std::string parseLocation(const Location::Ptr& object) const; - Update::Ptr parseUpdate(const boost::property_tree::ptree& data) const; - std::string parseUpdate(const Update::Ptr& object) const; - UserProfilePhotos::Ptr parseUserProfilePhotos(const boost::property_tree::ptree& data) const; - std::string parseUserProfilePhotos(const UserProfilePhotos::Ptr& object) const; - ReplyKeyboardMarkup::Ptr parseReplyKeyboardMarkup(const boost::property_tree::ptree& data) const; - std::string parseReplyKeyboardMarkup(const ReplyKeyboardMarkup::Ptr& object) const; - ReplyKeyboardHide::Ptr parseReplyKeyboardHide(const boost::property_tree::ptree& data) const; - std::string parseReplyKeyboardHide(const ReplyKeyboardHide::Ptr& object) const; - ForceReply::Ptr parseForceReply(const boost::property_tree::ptree& data) const; - std::string parseForceReply(const ForceReply::Ptr& object) const; - GenericChat::Ptr parseGenericChat(const boost::property_tree::ptree& data) const; - std::string parseGenericChat(const GenericChat::Ptr& object) const; - GenericReply::Ptr parseGenericReply(const boost::property_tree::ptree& data) const; - std::string parseGenericReply(const GenericReply::Ptr& object) const; - - inline boost::property_tree::ptree parseJson(const std::string& json) const { - boost::property_tree::ptree tree; - std::istringstream input(json); - boost::property_tree::read_json(input, tree); - return tree; - } - - template<typename T> - std::shared_ptr<T> tryParse(std::shared_ptr<T> (TgTypeParser::*const parseFunc)(const boost::property_tree::ptree&) const, const boost::property_tree::ptree& data, const std::string& keyName) const { - auto treeItem = data.find(keyName); - if (treeItem == data.not_found()) { - return std::shared_ptr<T>(); - } - return (this->*parseFunc)(treeItem->second); - } - - template<typename T> - std::vector<std::shared_ptr<T>> parseArray(std::shared_ptr<T> (TgTypeParser::*const parseFunc)(const boost::property_tree::ptree&) const, const boost::property_tree::ptree& data, const std::string& keyName) const { - std::vector<std::shared_ptr<T>> result; - auto treeItem = data.find(keyName); - if (treeItem == data.not_found()) { - return result; - } - for (const std::pair<const std::string, boost::property_tree::ptree>& innerTreeItem : treeItem->second) { - result.push_back((this->*parseFunc)(innerTreeItem.second)); - } - return result; - } - - template<typename T> - std::vector<std::vector<std::shared_ptr<T>>> parse2DArray(std::shared_ptr<T> (TgTypeParser::*const parseFunc)(const boost::property_tree::ptree&) const, const boost::property_tree::ptree& data, const std::string& keyName) const { - std::vector<std::vector<std::shared_ptr<T>>> result; - auto treeItem = data.find(keyName); - if (treeItem == data.not_found()) { - return result; - } - for (const std::pair<const std::string, boost::property_tree::ptree>& innerTreeItem : treeItem->second) { - std::vector<std::shared_ptr<T>> innerResult; - for (const std::pair<const std::string, boost::property_tree::ptree>& innerInnerTreeItem : innerTreeItem.second) { - innerResult.push_back((this->*parseFunc)(innerInnerTreeItem.second)); - } - result.push_back(innerResult); - } - return result; - } - - template<typename T> - std::string parseArray(std::string (TgTypeParser::*const parseFunc)(const std::shared_ptr<T>&) const, const std::vector<std::shared_ptr<T>>& objects) const { - std::string result; - result += '['; - for (const std::shared_ptr<T>& item : objects) { - result += (this->*parseFunc)(item); - result += ','; - } - result.erase(result.length() - 1); - result += ']'; - return result; - } - - template<typename T> - std::string parse2DArray(std::string (TgTypeParser::*const parseFunc)(const std::shared_ptr<T>&) const, const std::vector<std::vector<std::shared_ptr<T>>>& objects) const { - std::string result; - result += '['; - for (const std::vector<std::shared_ptr<T>>& item : objects) { - result += parseArray(parseFunc, item); - result += ','; - } - result.erase(result.length() - 1); - result += ']'; - return result; - } + template<typename T> + using JsonToTgTypeFunc = std::shared_ptr<T> (TgTypeParser::*)(const boost::property_tree::ptree&) const; + + template<typename T> + using TgTypeToJsonFunc = std::string (TgTypeParser::*)(const std::shared_ptr<T>&) const; + + static TgTypeParser& getInstance(); + + User::Ptr parseJsonAndGetUser(const boost::property_tree::ptree& data) const; + std::string parseUser(const User::Ptr& object) const; + GroupChat::Ptr parseJsonAndGetGroupChat(const boost::property_tree::ptree& data) const; + std::string parseGroupChat(const GroupChat::Ptr& object) const; + Message::Ptr parseJsonAndGetMessage(const boost::property_tree::ptree& data) const; + std::string parseMessage(const Message::Ptr& object) const; + PhotoSize::Ptr parseJsonAndGetPhotoSize(const boost::property_tree::ptree& data) const; + std::string parsePhotoSize(const PhotoSize::Ptr& object) const; + Audio::Ptr parseJsonAndGetAudio(const boost::property_tree::ptree& data) const; + std::string parseAudio(const Audio::Ptr& object) const; + Document::Ptr parseJsonAndGetDocument(const boost::property_tree::ptree& data) const; + std::string parseDocument(const Document::Ptr& object) const; + Sticker::Ptr parseJsonAndGetSticker(const boost::property_tree::ptree& data) const; + std::string parseSticker(const Sticker::Ptr& object) const; + Video::Ptr parseJsonAndGetVideo(const boost::property_tree::ptree& data) const; + std::string parseVideo(const Video::Ptr& object) const; + Contact::Ptr parseJsonAndGetContact(const boost::property_tree::ptree& data) const; + std::string parseContact(const Contact::Ptr& object) const; + Location::Ptr parseJsonAndGetLocation(const boost::property_tree::ptree& data) const; + std::string parseLocation(const Location::Ptr& object) const; + Update::Ptr parseJsonAndGetUpdate(const boost::property_tree::ptree& data) const; + std::string parseUpdate(const Update::Ptr& object) const; + UserProfilePhotos::Ptr parseJsonAndGetUserProfilePhotos(const boost::property_tree::ptree& data) const; + std::string parseUserProfilePhotos(const UserProfilePhotos::Ptr& object) const; + ReplyKeyboardMarkup::Ptr parseJsonAndGetReplyKeyboardMarkup(const boost::property_tree::ptree& data) const; + std::string parseReplyKeyboardMarkup(const ReplyKeyboardMarkup::Ptr& object) const; + ReplyKeyboardHide::Ptr parseJsonAndGetReplyKeyboardHide(const boost::property_tree::ptree& data) const; + std::string parseReplyKeyboardHide(const ReplyKeyboardHide::Ptr& object) const; + ForceReply::Ptr parseJsonAndGetForceReply(const boost::property_tree::ptree& data) const; + std::string parseForceReply(const ForceReply::Ptr& object) const; + GenericChat::Ptr parseJsonAndGetGenericChat(const boost::property_tree::ptree& data) const; + std::string parseGenericChat(const GenericChat::Ptr& object) const; + GenericReply::Ptr parseJsonAndGetGenericReply(const boost::property_tree::ptree& data) const; + std::string parseGenericReply(const GenericReply::Ptr& object) const; + + inline boost::property_tree::ptree parseJson(const std::string& json) const { + boost::property_tree::ptree tree; + std::istringstream input(json); + boost::property_tree::read_json(input, tree); + return tree; + } + + template<typename T> + std::shared_ptr<T> tryParseJson(JsonToTgTypeFunc<T> parseFunc, const boost::property_tree::ptree& data, const std::string& keyName) const { + auto treeItem = data.find(keyName); + if (treeItem == data.not_found()) { + return std::shared_ptr<T>(); + } + return (this->*parseFunc)(treeItem->second); + } + + template<typename T> + std::vector<std::shared_ptr<T>> parseJsonAndGetArray(JsonToTgTypeFunc<T> parseFunc, const boost::property_tree::ptree& data, const std::string& keyName) const { + std::vector<std::shared_ptr<T>> result; + auto treeItem = data.find(keyName); + if (treeItem == data.not_found()) { + return result; + } + for (const std::pair<const std::string, boost::property_tree::ptree>& innerTreeItem : treeItem->second) { + result.push_back((this->*parseFunc)(innerTreeItem.second)); + } + return result; + } + + template<typename T> + std::vector<std::vector<std::shared_ptr<T>>> parseJsonAndGet2DArray(JsonToTgTypeFunc<T> parseFunc, const boost::property_tree::ptree& data, const std::string& keyName) const { + std::vector<std::vector<std::shared_ptr<T>>> result; + auto treeItem = data.find(keyName); + if (treeItem == data.not_found()) { + return result; + } + for (const std::pair<const std::string, boost::property_tree::ptree>& innerTreeItem : treeItem->second) { + std::vector<std::shared_ptr<T>> innerResult; + for (const std::pair<const std::string, boost::property_tree::ptree>& innerInnerTreeItem : innerTreeItem.second) { + innerResult.push_back((this->*parseFunc)(innerInnerTreeItem.second)); + } + result.push_back(innerResult); + } + return result; + } + + template<typename T> + std::string parseArray(TgTypeToJsonFunc<T> parseFunc, const std::vector<std::shared_ptr<T>>& objects) const { + std::string result; + result += '['; + for (const std::shared_ptr<T>& item : objects) { + result += (this->*parseFunc)(item); + result += ','; + } + result.erase(result.length() - 1); + result += ']'; + return result; + } + + template<typename T> + std::string parse2DArray(TgTypeToJsonFunc<T> parseFunc, const std::vector<std::vector<std::shared_ptr<T>>>& objects) const { + std::string result; + result += '['; + for (const std::vector<std::shared_ptr<T>>& item : objects) { + result += parseArray(parseFunc, item); + result += ','; + } + result.erase(result.length() - 1); + result += ']'; + return result; + } private: - template<typename T> - void appendToJson(std::string& json, const std::string& varName, const T& value) const { - if (value == 0) { - return; - } - json += '"'; - json += varName; - json += "\":"; - json += value; - json += ','; - } - - void appendToJson(std::string& json, const std::string& varName, const std::string& value) const; + template<typename T> + void appendToJson(std::string& json, const std::string& varName, const T& value) const { + if (value == 0) { + return; + } + json += '"'; + json += varName; + json += "\":"; + json += value; + json += ','; + } + + void appendToJson(std::string& json, const std::string& varName, const std::string& value) const; }; } diff --git a/include/tgbot/doxygenMain.h b/include/tgbot/doxygenMain.h index 0d25e61..d77efa4 100644 --- a/include/tgbot/doxygenMain.h +++ b/include/tgbot/doxygenMain.h @@ -36,7 +36,7 @@ * Firstly you need to install some dependencies. You have to have boost library at the runtime and cmake at the compilation step to be able to use this library. * On Debian-based distibutives you can do it with these commands: * @code{.sh} - * sudo apt-get install cmake libboost-dev + * sudo apt-get install g++ make binutils cmake libssl-dev libboost-system-dev libboost-iostreams-dev libboost-test-dev * @endcode * * To compile the library execute this commands: diff --git a/include/tgbot/net/HttpClient.h b/include/tgbot/net/HttpClient.h index d39e108..e695398 100644 --- a/include/tgbot/net/HttpClient.h +++ b/include/tgbot/net/HttpClient.h @@ -43,17 +43,17 @@ public: /** * Returns instance which lives during all application lifetime. */ - static HttpClient& getInstance(); + static HttpClient& getInstance(); - /** - * Sends a request to the url. - * If there's no args specified, a GET request will be sent, otherwise a POST request will be sent. - * If at least 1 arg is marked as file, the content type of a request will be multipart/form-data, otherwise it will be application/x-www-form-urlencoded. - */ - std::string makeRequest(const Url& url, const std::vector<HttpReqArg>& args); + /** + * Sends a request to the url. + * If there's no args specified, a GET request will be sent, otherwise a POST request will be sent. + * If at least 1 arg is marked as file, the content type of a request will be multipart/form-data, otherwise it will be application/x-www-form-urlencoded. + */ + std::string makeRequest(const Url& url, const std::vector<HttpReqArg>& args); private: - boost::asio::io_service _ioService; + boost::asio::io_service _ioService; }; } diff --git a/include/tgbot/net/HttpParser.h b/include/tgbot/net/HttpParser.h index dcb857b..83068fb 100644 --- a/include/tgbot/net/HttpParser.h +++ b/include/tgbot/net/HttpParser.h @@ -34,33 +34,33 @@ namespace TgBot { class HttpParser { public: - static HttpParser& getInstance(); + static HttpParser& getInstance(); - std::string generateRequest(const Url& url, const std::vector<HttpReqArg>& args, bool isKeepAlive = false); - std::string generateMultipartFormData(const std::vector<HttpReqArg>& args, const std::string& bondary); - std::string generateMultipartBoundary(const std::vector<HttpReqArg>& args); - std::string generateWwwFormUrlencoded(const std::vector<HttpReqArg>& args); - std::string generateResponse(const std::string& data, const std::string& mimeType = "text/plain", short unsigned statusCode = 200, const std::string& statusStr = "OK", bool isKeepAlive = false); + std::string generateRequest(const Url& url, const std::vector<HttpReqArg>& args, bool isKeepAlive = false); + std::string generateMultipartFormData(const std::vector<HttpReqArg>& args, const std::string& bondary); + std::string generateMultipartBoundary(const std::vector<HttpReqArg>& args); + std::string generateWwwFormUrlencoded(const std::vector<HttpReqArg>& args); + std::string generateResponse(const std::string& data, const std::string& mimeType = "text/plain", short unsigned statusCode = 200, const std::string& statusStr = "OK", bool isKeepAlive = false); - inline std::string parseRequest(const std::string& data, std::map<std::string, std::string>& headers) { - return parseHttp(true, data, headers); - } + inline std::string parseRequest(const std::string& data, std::map<std::string, std::string>& headers) { + return parseHttp(true, data, headers); + } - inline std::string parseRequest(const std::string& data) { - return parseHttp(true, data); - } + inline std::string parseRequest(const std::string& data) { + return parseHttp(true, data); + } - inline std::string parseResponse(const std::string& data, std::map<std::string, std::string>& headers) { - return parseHttp(false, data, headers); - } + inline std::string parseResponse(const std::string& data, std::map<std::string, std::string>& headers) { + return parseHttp(false, data, headers); + } - inline std::string parseResponse(const std::string& data) { - return parseHttp(false, data); - } + inline std::string parseResponse(const std::string& data) { + return parseHttp(false, data); + } private: - std::string parseHttp(bool isRequest, const std::string& data, std::map<std::string, std::string>& headers); - std::string parseHttp(bool isRequest, const std::string& data); + std::string parseHttp(bool isRequest, const std::string& data, std::map<std::string, std::string>& headers); + std::string parseHttp(bool isRequest, const std::string& data); }; } diff --git a/include/tgbot/net/HttpReqArg.h b/include/tgbot/net/HttpReqArg.h index d70e8b5..65f4f52 100644 --- a/include/tgbot/net/HttpReqArg.h +++ b/include/tgbot/net/HttpReqArg.h @@ -36,31 +36,31 @@ namespace TgBot { class HttpReqArg { public: - template<typename T> - HttpReqArg(const std::string& name, const T& value, bool isFile = false, const std::string& mimeType = "text/plain") : - name(name), value(boost::lexical_cast<std::string>(value)), isFile(isFile), mimeType(mimeType) - { - } + template<typename T> + HttpReqArg(const std::string& name, const T& value, bool isFile = false, const std::string& mimeType = "text/plain") : + name(name), value(boost::lexical_cast<std::string>(value)), isFile(isFile), mimeType(mimeType) + { + } - /** - * Name of an argument. - */ - std::string name; + /** + * Name of an argument. + */ + std::string name; - /** - * Value of an argument. - */ - std::string value; + /** + * Value of an argument. + */ + std::string value; - /** - * Should be true if an argument value hold some file contents - */ - bool isFile = false; + /** + * Should be true if an argument value hold some file contents + */ + bool isFile = false; - /** - * Mime type of an argument value. This field makes sense only if isFile is true. - */ - std::string mimeType = "text/plain"; + /** + * Mime type of an argument value. This field makes sense only if isFile is true. + */ + std::string mimeType = "text/plain"; }; } diff --git a/include/tgbot/net/HttpServer.h b/include/tgbot/net/HttpServer.h index 0a66900..bac7382 100644 --- a/include/tgbot/net/HttpServer.h +++ b/include/tgbot/net/HttpServer.h @@ -39,63 +39,63 @@ template<typename Protocol> class HttpServer { private: - class Connection; + class Connection; public: - typedef std::function<std::string (const std::string&, const std::map<std::string, std::string>)> ServerHandler; - - HttpServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const ServerHandler& handler) : _acceptor(acceptor), _handler(handler) { - } - - /** - * Starts receiving new connections. - */ - void start() { - std::shared_ptr<boost::asio::basic_stream_socket<Protocol>> socket(new boost::asio::basic_stream_socket<Protocol>(acceptor->get_io_service())); - std::shared_ptr<Connection<Protocol>> connection(new Connection<Protocol>(socket, _handler)); - acceptor->async_accept(*connection->socket, [this, connection]() { - connection->start(); - start(); - }); - _ioService.run(); - } - - /** - * Stops receiving new connections. - */ - void stop() { - _ioService.stop(); - } + typedef std::function<std::string (const std::string&, const std::map<std::string, std::string>)> ServerHandler; + + HttpServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const ServerHandler& handler) : _acceptor(acceptor), _handler(handler) { + } + + /** + * Starts receiving new connections. + */ + void start() { + std::shared_ptr<boost::asio::basic_stream_socket<Protocol>> socket(new boost::asio::basic_stream_socket<Protocol>(acceptor->get_io_service())); + std::shared_ptr<Connection<Protocol>> connection(new Connection<Protocol>(socket, _handler)); + acceptor->async_accept(*connection->socket, [this, connection]() { + connection->start(); + start(); + }); + _ioService.run(); + } + + /** + * Stops receiving new connections. + */ + void stop() { + _ioService.stop(); + } private: - template<typename Protocol> - class Connection { - - public: - Connection(std::shared_ptr<boost::asio::basic_stream_socket<Protocol>>& socket, const ServerHandler& handler) : socket(socket), _handler(handler) { - boost::asio::socket_base::keep_alive option(true); - socket.set_option(option); - } - - void start() { - data.reserve(10240); - socket->async_receive(data, [this]() { - std::map<std::string, std::string> headers; - std::string body = HttpParser::parseResponse(data, headers); - socket->async_send(_handler(body, headers)); - }); - } - - std::shared_ptr<boost::asio::basic_stream_socket<Protocol>> socket; - std::string data; - - private: - const ServerHandler _handler; - }; - - boost::asio::io_service _ioService; - std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>> _acceptor; - const ServerHandler _handler; + template<typename Protocol> + class Connection { + + public: + Connection(std::shared_ptr<boost::asio::basic_stream_socket<Protocol>>& socket, const ServerHandler& handler) : socket(socket), _handler(handler) { + boost::asio::socket_base::keep_alive option(true); + socket.set_option(option); + } + + void start() { + data.reserve(10240); + socket->async_receive(data, [this]() { + std::map<std::string, std::string> headers; + std::string body = HttpParser::parseResponse(data, headers); + socket->async_send(_handler(body, headers)); + }); + } + + std::shared_ptr<boost::asio::basic_stream_socket<Protocol>> socket; + std::string data; + + private: + const ServerHandler _handler; + }; + + boost::asio::io_service _ioService; + std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>> _acceptor; + const ServerHandler _handler; }; } diff --git a/include/tgbot/net/TgLongPoll.h b/include/tgbot/net/TgLongPoll.h index 932fc6d..949a2a4 100644 --- a/include/tgbot/net/TgLongPoll.h +++ b/include/tgbot/net/TgLongPoll.h @@ -36,18 +36,18 @@ namespace TgBot { class TgLongPoll { public: - TgLongPoll(const Api* api, const EventHandler* eventHandler); - TgLongPoll(const Bot& bot); + TgLongPoll(const Api* api, const EventHandler* eventHandler); + TgLongPoll(const Bot& bot); - /** - * Starts long poll. After new update will come, this method will parse it and send to EventHandler which invokes your listeners. Designed to be executed in a loop. - */ - void start(); + /** + * Starts long poll. After new update will come, this method will parse it and send to EventHandler which invokes your listeners. Designed to be executed in a loop. + */ + void start(); private: - int32_t _lastUpdateId = 0; - const Api* _api; - const EventHandler* _eventHandler; + int32_t _lastUpdateId = 0; + const Api* _api; + const EventHandler* _eventHandler; }; } diff --git a/include/tgbot/net/TgWebhookLocalServer.h b/include/tgbot/net/TgWebhookLocalServer.h index 7835f28..8faf61d 100644 --- a/include/tgbot/net/TgWebhookLocalServer.h +++ b/include/tgbot/net/TgWebhookLocalServer.h @@ -34,16 +34,16 @@ namespace TgBot { class TgWebhookLocalServer : public TgWebhookServer<boost::asio::local::stream_protocol> { public: - TgWebhookLocalServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::local::stream_protocol>>& acceptor, const std::string& path, EventHandler* eventHandler) = delete; + TgWebhookLocalServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::local::stream_protocol>>& acceptor, const std::string& path, EventHandler* eventHandler) = delete; - TgWebhookLocalServer(const std::string& path, const EventHandler* eventHandler) : - TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::local::stream_protocol>>(new boost::asio::local::stream_protocol::acceptor(_ioService, boost::asio::local::stream_protocol::endpoint(path))), path, eventHandler) - { - } + TgWebhookLocalServer(const std::string& path, const EventHandler* eventHandler) : + TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::local::stream_protocol>>(new boost::asio::local::stream_protocol::acceptor(_ioService, boost::asio::local::stream_protocol::endpoint(path))), path, eventHandler) + { + } - TgWebhookLocalServer(const std::string& path, const Bot& bot) : TgWebhookLocalServer(path, &bot.getEventHandler()) { + TgWebhookLocalServer(const std::string& path, const Bot& bot) : TgWebhookLocalServer(path, &bot.getEventHandler()) { - } + } }; } diff --git a/include/tgbot/net/TgWebhookServer.h b/include/tgbot/net/TgWebhookServer.h index a8155ff..f583093 100644 --- a/include/tgbot/net/TgWebhookServer.h +++ b/include/tgbot/net/TgWebhookServer.h @@ -34,22 +34,22 @@ template<typename Protocol> class TgWebhookServer : public HttpServer<Protocol> { public: - TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const ServerHandler& handler) = delete; - - TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const std::string& path, const EventHandler* eventHandler) : - HttpServer(acceptor, [this, eventHandler, &path](const std::string& data, const std::map<std::string, std::string>& headers) -> std::string { - if (headers["method"] == "POST" && headers["path"] == path) { - eventHandler->handleUpdate(TgTypeParser::getInstance().parseUpdate(TgTypeParser::getInstance().parseJson(data))); - } - return HttpParser::generateResponse(""); - }) - { - } - - TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const std::string& path, const Bot& bot) : - TgWebhookServer(acceptor, path, &bot.getEventHandler()) - { - } + TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const ServerHandler& handler) = delete; + + TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const std::string& path, const EventHandler* eventHandler) : + HttpServer(acceptor, [this, eventHandler, &path](const std::string& data, const std::map<std::string, std::string>& headers) -> std::string { + if (headers["method"] == "POST" && headers["path"] == path) { + eventHandler->handleUpdate(TgTypeParser::getInstance().parseUpdate(TgTypeParser::getInstance().parseJson(data))); + } + return HttpParser::generateResponse(""); + }) + { + } + + TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<Protocol>>& acceptor, const std::string& path, const Bot& bot) : + TgWebhookServer(acceptor, path, &bot.getEventHandler()) + { + } }; } diff --git a/include/tgbot/net/TgWebhookTcpServer.h b/include/tgbot/net/TgWebhookTcpServer.h index cac2638..417b162 100644 --- a/include/tgbot/net/TgWebhookTcpServer.h +++ b/include/tgbot/net/TgWebhookTcpServer.h @@ -34,15 +34,15 @@ namespace TgBot { class TgWebhookTcpServer : public TgWebhookServer<boost::asio::ip::tcp> { public: - TgWebhookTcpServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>>& acceptor, const std::string& path, EventHandler* eventHandler) = delete; + TgWebhookTcpServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>>& acceptor, const std::string& path, EventHandler* eventHandler) = delete; - TgWebhookTcpServer(unsigned short port, const std::string& path, const EventHandler* eventHandler) : - TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>>(new boost::asio::ip::tcp::acceptor(_ioService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))), path, eventHandler) - { - } + TgWebhookTcpServer(unsigned short port, const std::string& path, const EventHandler* eventHandler) : + TgWebhookServer(std::shared_ptr<boost::asio::basic_socket_acceptor<boost::asio::ip::tcp>>(new boost::asio::ip::tcp::acceptor(_ioService, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))), path, eventHandler) + { + } - TgWebhookTcpServer(const std::string& path, const Bot& bot) : TgWebhookTcpServer(path, &bot.getEventHandler()) { - } + TgWebhookTcpServer(const std::string& path, const Bot& bot) : TgWebhookTcpServer(path, &bot.getEventHandler()) { + } }; } diff --git a/include/tgbot/net/Url.h b/include/tgbot/net/Url.h index 818e934..56f502a 100644 --- a/include/tgbot/net/Url.h +++ b/include/tgbot/net/Url.h @@ -34,32 +34,32 @@ namespace TgBot { class Url { public: - Url(const std::string& url); + Url(const std::string& url); /** * Protocol part of an url. Example: https:// */ - std::string protocol; + std::string protocol; /** * Host part of an url. Example: www.example.com */ - std::string host; + std::string host; /** * Path part of an url including preceding '/' char. Example: /index.html */ - std::string path; + std::string path; /** * Query part of an url without '?' char. Example: a=1&b=2&c=3 */ - std::string query; + std::string query; /** * Fragment part of an url without '#' char. Example: section1 */ - std::string fragment; + std::string fragment; }; } diff --git a/include/tgbot/tools/StringTools.h b/include/tgbot/tools/StringTools.h index 6c01575..8df7434 100644 --- a/include/tgbot/tools/StringTools.h +++ b/include/tgbot/tools/StringTools.h @@ -82,9 +82,9 @@ std::string urlDecode(const std::string& value); * @return Array of substrings */ inline std::vector<std::string> split(const std::string& str, char delimiter) { - std::vector<std::string> result; - split(str, delimiter, result); - return result; + std::vector<std::string> result; + split(str, delimiter, result); + return result; } } diff --git a/include/tgbot/types/Audio.h b/include/tgbot/types/Audio.h index 8e255fe..e39eb00 100644 --- a/include/tgbot/types/Audio.h +++ b/include/tgbot/types/Audio.h @@ -35,27 +35,27 @@ namespace TgBot { class Audio { public: - typedef std::shared_ptr<Audio> Ptr; + typedef std::shared_ptr<Audio> Ptr; /** * Unique identifier for this file. */ - std::string fileId; + std::string fileId; /** * Duration of the audio in seconds as defined by sender. */ - int32_t duration; + int32_t duration; /** * Optional. MIME type of the file as defined by sender. */ - std::string mimeType; + std::string mimeType; /** * Optional. File size. */ - int32_t fileSize; + int32_t fileSize; }; } diff --git a/include/tgbot/types/Contact.h b/include/tgbot/types/Contact.h index dcd90b0..db2929d 100644 --- a/include/tgbot/types/Contact.h +++ b/include/tgbot/types/Contact.h @@ -35,27 +35,27 @@ namespace TgBot { class Contact { public: - typedef std::shared_ptr<Contact> Ptr; - - /** - * Contact's phone number. - */ - std::string phoneNumber; - - /** - * Contact's first name. - */ - std::string firstName; - - /** - * Optional. Contact's last name. - */ - std::string lastName; - - /** - * Optional. Contact's user identifier in Telegram. - */ - std::string userId; + typedef std::shared_ptr<Contact> Ptr; + + /** + * Contact's phone number. + */ + std::string phoneNumber; + + /** + * Contact's first name. + */ + std::string firstName; + + /** + * Optional. Contact's last name. + */ + std::string lastName; + + /** + * Optional. Contact's user identifier in Telegram. + */ + std::string userId; }; } diff --git a/include/tgbot/types/Document.h b/include/tgbot/types/Document.h index 1fd0522..88613cb 100644 --- a/include/tgbot/types/Document.h +++ b/include/tgbot/types/Document.h @@ -37,32 +37,32 @@ namespace TgBot { class Document { public: - typedef std::shared_ptr<Document> Ptr; + typedef std::shared_ptr<Document> Ptr; /** * Unique file identifier. */ - std::string fileId; + std::string fileId; /** * Optional. Document thumbnail as defined by sender. */ - PhotoSize::Ptr thumb; + PhotoSize::Ptr thumb; /** * Optional. Original filename as defined by sender. */ - std::string fileName; + std::string fileName; /** * Optional. MIME type of the file as defined by sender. */ - std::string mimeType; + std::string mimeType; /** * Optional. File size. */ - int32_t fileSize; + int32_t fileSize; }; } diff --git a/include/tgbot/types/ForceReply.h b/include/tgbot/types/ForceReply.h index 237967e..f46197a 100644 --- a/include/tgbot/types/ForceReply.h +++ b/include/tgbot/types/ForceReply.h @@ -40,17 +40,17 @@ namespace TgBot { class ForceReply : public GenericReply { public: - typedef std::shared_ptr<ForceReply> Ptr; + typedef std::shared_ptr<ForceReply> Ptr; - /** - * Shows reply interface to the user, as if they manually selected the bot‘s message and tapped ’Reply' - */ - const bool forceReply = true; + /** + * Shows reply interface to the user, as if they manually selected the bot‘s message and tapped ’Reply' + */ + const bool forceReply = true; - /** - * Optional. Use this parameter if you want to force reply from specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. - */ - bool selective; + /** + * Optional. Use this parameter if you want to force reply from specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. + */ + bool selective; }; } diff --git a/include/tgbot/types/GenericChat.h b/include/tgbot/types/GenericChat.h index b590312..f1fe6a1 100644 --- a/include/tgbot/types/GenericChat.h +++ b/include/tgbot/types/GenericChat.h @@ -34,14 +34,14 @@ namespace TgBot { class GenericChat { public: - typedef std::shared_ptr<GenericChat> Ptr; + typedef std::shared_ptr<GenericChat> Ptr; - virtual ~GenericChat() { } + virtual ~GenericChat() { } - /** - * Unique identifier for this user, bot or group chat - */ - int32_t id; + /** + * Unique identifier for this user, bot or group chat + */ + int32_t id; }; } diff --git a/include/tgbot/types/GenericReply.h b/include/tgbot/types/GenericReply.h index 124c83a..64e6b6c 100644 --- a/include/tgbot/types/GenericReply.h +++ b/include/tgbot/types/GenericReply.h @@ -34,9 +34,9 @@ namespace TgBot { class GenericReply { public: - typedef std::shared_ptr<GenericReply> Ptr; + typedef std::shared_ptr<GenericReply> Ptr; - virtual ~GenericReply() { } + virtual ~GenericReply() { } }; } diff --git a/include/tgbot/types/GroupChat.h b/include/tgbot/types/GroupChat.h index 5704051..fedb7f0 100644 --- a/include/tgbot/types/GroupChat.h +++ b/include/tgbot/types/GroupChat.h @@ -37,12 +37,12 @@ namespace TgBot { class GroupChat : public GenericChat { public: - typedef std::shared_ptr<GroupChat> Ptr; + typedef std::shared_ptr<GroupChat> Ptr; - /** - * Group name. - */ - std::string title; + /** + * Group name. + */ + std::string title; }; } diff --git a/include/tgbot/types/InputFile.h b/include/tgbot/types/InputFile.h index 900838d..825e677 100644 --- a/include/tgbot/types/InputFile.h +++ b/include/tgbot/types/InputFile.h @@ -36,17 +36,17 @@ namespace TgBot { class InputFile { public: - typedef std::shared_ptr<InputFile> Ptr; + typedef std::shared_ptr<InputFile> Ptr; - /** - * Contents of a file. - */ - std::string data; + /** + * Contents of a file. + */ + std::string data; - /** - * Mime type of a file. - */ - std::string mimeType; + /** + * Mime type of a file. + */ + std::string mimeType; }; } diff --git a/include/tgbot/types/Location.h b/include/tgbot/types/Location.h index b35fdd7..9d9ef72 100644 --- a/include/tgbot/types/Location.h +++ b/include/tgbot/types/Location.h @@ -34,17 +34,17 @@ namespace TgBot { class Location { public: - typedef std::shared_ptr<Location> Ptr; + typedef std::shared_ptr<Location> Ptr; - /** - * Longitude as defined by sender. - */ - float longitude; + /** + * Longitude as defined by sender. + */ + float longitude; - /** - * Latitude as defined by sender. - */ - float latitude; + /** + * Latitude as defined by sender. + */ + float latitude; }; } diff --git a/include/tgbot/types/Message.h b/include/tgbot/types/Message.h index de07563..b8dfcc6 100644 --- a/include/tgbot/types/Message.h +++ b/include/tgbot/types/Message.h @@ -47,117 +47,117 @@ namespace TgBot { class Message { public: - typedef std::shared_ptr<Message> Ptr; - - /** - * Unique message identifier. - */ - int32_t messageId; - - /** - * Sender. - */ - User::Ptr from; - - /** - * Date the message was sent in Unix time. - */ - int32_t date; - - /** - * Conversation the message belongs to — user in case of a private message, GroupChat in case of a group. - */ - GenericChat::Ptr chat; - - /** - * Optional. For forwarded messages, sender of the original message. - */ - User::Ptr forwardFrom; - - /** - * Optional. For forwarded messages, date the original message was sent in Unix time. - */ - int32_t forwardDate; - - /** - * Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply. - */ - Message::Ptr replyToMessage; - - /** - * Optional. For text messages, the actual UTF-8 text of the message. - */ - std::string text; - - /** - * Optional. Message is an audio file, information about the file. - */ - Audio::Ptr audio; - - /** - * Optional. Message is a general file, information about the file. - */ - Document::Ptr document; - - /** - * Optional. Message is a photo, available sizes of the photo. - */ - std::vector<PhotoSize::Ptr> photo; - - /** - * Optional. Message is a sticker, information about the sticker. - */ - Sticker::Ptr sticker; - - /** - * Optional. Message is a video, information about the video. - */ - Video::Ptr video; - - /** - * Optional. Message is a shared contact, information about the contact. - */ - Contact::Ptr contact; - - /** - * Optional. Message is a shared location, information about the location. - */ - Location::Ptr location; - - /** - * Optional. A new member was added to the group, information about them (this member may be bot itself). - */ - User::Ptr newChatParticipant; - - /** - * Optional. A member was removed from the group, information about them (this member may be bot itself). - */ - User::Ptr leftChatParticipant; - - /** - * Optional. A group title was changed to this value. - */ - std::string newChatTitle; - - /** - * Optional. A group photo was change to this value. - */ - std::vector<PhotoSize::Ptr> newChatPhoto; - - /** - * Optional. Informs that the group photo was deleted. - */ - bool deleteChatPhoto; - - /** - * Optional. Informs that the group has been created. - */ - bool groupChatCreated; - - /** - * Optional. Text description of the photo or the video. - */ - std::string caption; + typedef std::shared_ptr<Message> Ptr; + + /** + * Unique message identifier. + */ + int32_t messageId; + + /** + * Sender. + */ + User::Ptr from; + + /** + * Date the message was sent in Unix time. + */ + int32_t date; + + /** + * Conversation the message belongs to — user in case of a private message, GroupChat in case of a group. + */ + GenericChat::Ptr chat; + + /** + * Optional. For forwarded messages, sender of the original message. + */ + User::Ptr forwardFrom; + + /** + * Optional. For forwarded messages, date the original message was sent in Unix time. + */ + int32_t forwardDate; + + /** + * Optional. For replies, the original message. Note that the Message object in this field will not contain further reply_to_message fields even if it itself is a reply. + */ + Message::Ptr replyToMessage; + + /** + * Optional. For text messages, the actual UTF-8 text of the message. + */ + std::string text; + + /** + * Optional. Message is an audio file, information about the file. + */ + Audio::Ptr audio; + + /** + * Optional. Message is a general file, information about the file. + */ + Document::Ptr document; + + /** + * Optional. Message is a photo, available sizes of the photo. + */ + std::vector<PhotoSize::Ptr> photo; + + /** + * Optional. Message is a sticker, information about the sticker. + */ + Sticker::Ptr sticker; + + /** + * Optional. Message is a video, information about the video. + */ + Video::Ptr video; + + /** + * Optional. Message is a shared contact, information about the contact. + */ + Contact::Ptr contact; + + /** + * Optional. Message is a shared location, information about the location. + */ + Location::Ptr location; + + /** + * Optional. A new member was added to the group, information about them (this member may be bot itself). + */ + User::Ptr newChatParticipant; + + /** + * Optional. A member was removed from the group, information about them (this member may be bot itself). + */ + User::Ptr leftChatParticipant; + + /** + * Optional. A group title was changed to this value. + */ + std::string newChatTitle; + + /** + * Optional. A group photo was change to this value. + */ + std::vector<PhotoSize::Ptr> newChatPhoto; + + /** + * Optional. Informs that the group photo was deleted. + */ + bool deleteChatPhoto; + + /** + * Optional. Informs that the group has been created. + */ + bool groupChatCreated; + + /** + * Optional. Text description of the photo or the video. + */ + std::string caption; }; } diff --git a/include/tgbot/types/PhotoSize.h b/include/tgbot/types/PhotoSize.h index c616423..0ccea49 100644 --- a/include/tgbot/types/PhotoSize.h +++ b/include/tgbot/types/PhotoSize.h @@ -35,27 +35,27 @@ namespace TgBot { class PhotoSize { public: - typedef std::shared_ptr<PhotoSize> Ptr; + typedef std::shared_ptr<PhotoSize> Ptr; /** * Unique identifier for this file. */ - std::string fileId; + std::string fileId; /** * Photo width. */ - int32_t width; + int32_t width; /** * Photo height. */ - int32_t height; + int32_t height; /** * Optional. File size. */ - int32_t fileSize; + int32_t fileSize; }; } diff --git a/include/tgbot/types/ReplyKeyboardHide.h b/include/tgbot/types/ReplyKeyboardHide.h index 18aa101..e4c4b20 100644 --- a/include/tgbot/types/ReplyKeyboardHide.h +++ b/include/tgbot/types/ReplyKeyboardHide.h @@ -36,18 +36,18 @@ namespace TgBot { class ReplyKeyboardHide : public GenericReply { public: - typedef std::shared_ptr<ReplyKeyboardHide> Ptr; + typedef std::shared_ptr<ReplyKeyboardHide> Ptr; /** * Requests clients to hide the custom keyboard. */ - const bool hideKeyboard = true; + const bool hideKeyboard = true; /** * Optional. Use this parameter if you want to hide keyboard for specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. * Example: A user votes in a poll, bot returns confirmation message in reply to the vote and hides keyboard for that user, while still showing the keyboard with poll options to users who haven't voted yet. */ - bool selective; + bool selective; }; } diff --git a/include/tgbot/types/ReplyKeyboardMarkup.h b/include/tgbot/types/ReplyKeyboardMarkup.h index 0f9ce0d..92d850e 100644 --- a/include/tgbot/types/ReplyKeyboardMarkup.h +++ b/include/tgbot/types/ReplyKeyboardMarkup.h @@ -38,28 +38,28 @@ namespace TgBot { class ReplyKeyboardMarkup : public GenericReply { public: - typedef std::shared_ptr<ReplyKeyboardMarkup> Ptr; + typedef std::shared_ptr<ReplyKeyboardMarkup> Ptr; - /** - * Array of button rows, each represented by an Array of Strings. - */ - std::vector<std::vector<std::string>> keyboard; + /** + * Array of button rows, each represented by an Array of Strings. + */ + std::vector<std::vector<std::string>> keyboard; - /** - * Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard. - */ - bool resizeKeyboard; + /** + * Optional. Requests clients to resize the keyboard vertically for optimal fit (e.g., make the keyboard smaller if there are just two rows of buttons). Defaults to false, in which case the custom keyboard is always of the same height as the app's standard keyboard. + */ + bool resizeKeyboard; - /** - * Optional. Requests clients to hide the keyboard as soon as it's been used. Defaults to false. - */ - bool oneTimeKeyboard; + /** + * Optional. Requests clients to hide the keyboard as soon as it's been used. Defaults to false. + */ + bool oneTimeKeyboard; - /** - * Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. - * Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard. - */ - bool selective; + /** + * Optional. Use this parameter if you want to show the keyboard to specific users only. Targets: 1) users that are @mentioned in the text of the Message object; 2) if the bot's message is a reply (has reply_to_message_id), sender of the original message. + * Example: A user requests to change the bot‘s language, bot replies to the request with a keyboard to select the new language. Other users in the group don’t see the keyboard. + */ + bool selective; }; } diff --git a/include/tgbot/types/Sticker.h b/include/tgbot/types/Sticker.h index c223081..252fb41 100644 --- a/include/tgbot/types/Sticker.h +++ b/include/tgbot/types/Sticker.h @@ -37,32 +37,32 @@ namespace TgBot { class Sticker { public: - typedef std::shared_ptr<Sticker> Ptr; + typedef std::shared_ptr<Sticker> Ptr; /** * Unique file identifier. */ - std::string fileId; + std::string fileId; /** * Optional. Sticker width. */ - int32_t width; + int32_t width; /** * Optional. Sticker height. */ - int32_t height; + int32_t height; /** * Optional. Optional. Sticker thumbnail in .webp or .jpg format. */ - PhotoSize::Ptr thumb; + PhotoSize::Ptr thumb; /** * Optional. File size. */ - int32_t fileSize; + int32_t fileSize; }; } diff --git a/include/tgbot/types/Update.h b/include/tgbot/types/Update.h index a4d41a4..9bf3a19 100644 --- a/include/tgbot/types/Update.h +++ b/include/tgbot/types/Update.h @@ -36,17 +36,17 @@ namespace TgBot { class Update { public: - typedef std::shared_ptr<Update> Ptr; + typedef std::shared_ptr<Update> Ptr; /** * The update‘s unique identifier. Update identifiers start from a certain positive number and increase sequentially. This ID becomes especially handy if you’re using Webhooks, since it allows you to ignore repeated updates or to restore the correct update sequence, should they get out of order. */ - int32_t updateId; + int32_t updateId; /** * Optional. New incoming message of any kind — text, photo, sticker, etc. */ - Message::Ptr message; + Message::Ptr message; }; } diff --git a/include/tgbot/types/User.h b/include/tgbot/types/User.h index 048d20a..99c7d12 100644 --- a/include/tgbot/types/User.h +++ b/include/tgbot/types/User.h @@ -37,22 +37,22 @@ namespace TgBot { class User : public GenericChat { public: - typedef std::shared_ptr<User> Ptr; + typedef std::shared_ptr<User> Ptr; /** * User‘s or bot’s first name. */ - std::string firstName; + std::string firstName; /** * Optional. User‘s or bot’s last name. */ - std::string lastName; + std::string lastName; /** * Optional. User‘s or bot’s username. */ - std::string username; + std::string username; }; } diff --git a/include/tgbot/types/UserProfilePhotos.h b/include/tgbot/types/UserProfilePhotos.h index 0079d37..7c5781d 100644 --- a/include/tgbot/types/UserProfilePhotos.h +++ b/include/tgbot/types/UserProfilePhotos.h @@ -37,17 +37,17 @@ namespace TgBot { class UserProfilePhotos { public: - typedef std::shared_ptr<UserProfilePhotos> Ptr; + typedef std::shared_ptr<UserProfilePhotos> Ptr; - /** - * Total number of profile pictures the target user has. - */ - int32_t totalCount; + /** + * Total number of profile pictures the target user has. + */ + int32_t totalCount; - /** - * Requested profile pictures (in up to 4 sizes each). - */ - std::vector<std::vector<PhotoSize::Ptr>> photos; + /** + * Requested profile pictures (in up to 4 sizes each). + */ + std::vector<std::vector<PhotoSize::Ptr>> photos; }; } diff --git a/include/tgbot/types/Video.h b/include/tgbot/types/Video.h index 368f3e1..b20d914 100644 --- a/include/tgbot/types/Video.h +++ b/include/tgbot/types/Video.h @@ -37,42 +37,42 @@ namespace TgBot { class Video { public: - typedef std::shared_ptr<Video> Ptr; + typedef std::shared_ptr<Video> Ptr; - /** - * Unique identifier for this file. - */ - std::string fileId; + /** + * Unique identifier for this file. + */ + std::string fileId; - /** - * Video width as defined by sender. - */ - int32_t width; + /** + * Video width as defined by sender. + */ + int32_t width; /** * Video height as defined by sender. */ - int32_t height; + int32_t height; /** * Duration of the video in seconds as defined by sender. */ - int32_t duration; + int32_t duration; /** * Optional. Video thumbnail. */ - PhotoSize::Ptr thumb; + PhotoSize::Ptr thumb; /** * Optional. Mime type of a file as defined by sender */ - std::string mimeType; + std::string mimeType; /** * Optional. File size. */ - int32_t fileSize; + int32_t fileSize; }; } diff --git a/src/Api.cpp b/src/Api.cpp index 0c04081..da0aa7b 100644 --- a/src/Api.cpp +++ b/src/Api.cpp @@ -31,234 +31,234 @@ using namespace boost::property_tree; namespace TgBot { -Api::Api(const std::string& token) : _token(token) { +Api::Api(const string& token) : _token(token) { } User::Ptr Api::getMe() const { - return TgTypeParser::getInstance().parseUser(sendRequest("getMe").find("result")->second); + return TgTypeParser::getInstance().parseJsonAndGetUser(sendRequest("getMe")); } Message::Ptr Api::sendMessage(int32_t chatId, const string& text, bool disableWebPagePreview, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendMessage", args)); } Message::Ptr Api::forwardMessage(int32_t chatId, int32_t fromChatId, int32_t messageId) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("forwardMessage", args)); } Message::Ptr Api::sendPhoto(int32_t chatId, const InputFile::Ptr& photo, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendPhoto", args)); } Message::Ptr Api::sendPhoto(int32_t chatId, const string& photo, const string& caption, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendPhoto", args)); } Message::Ptr Api::sendAudio(int32_t chatId, const InputFile::Ptr& audio, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendAudio", args)); } Message::Ptr Api::sendAudio(int32_t chatId, const string& audio, int32_t duration, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendAudio", args)); } Message::Ptr Api::sendDocument(int32_t chatId, const InputFile::Ptr& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendDocument", args)); } Message::Ptr Api::sendDocument(int32_t chatId, const string& document, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendDocument", args)); } Message::Ptr Api::sendSticker(int32_t chatId, const InputFile::Ptr& sticker, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendSticker", args)); } Message::Ptr Api::sendSticker(int32_t chatId, const string& sticker, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendSticker", args)); } Message::Ptr Api::sendVideo(int32_t chatId, const InputFile::Ptr& video, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendVideo", args)); } Message::Ptr Api::sendVideo(int32_t chatId, const string& video, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendVideo", args)); } Message::Ptr Api::sendLocation(int32_t chatId, float latitude, float longitude, int32_t replyToMessageId, const GenericReply::Ptr& replyMarkup) const { - vector<HttpReqArg> 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); + vector<HttpReqArg> 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().parseJsonAndGetMessage(sendRequest("sendLocation", args)); } void Api::sendChatAction(int32_t chatId, const string& action) const { - vector<HttpReqArg> args; - args.push_back(HttpReqArg("chat_id", chatId)); - args.push_back(HttpReqArg("action", action)); - sendRequest("sendChatAction", args); + vector<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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().parseJsonAndGetUserProfilePhotos(sendRequest("getUserProfilePhotos", args)); } vector<Update::Ptr> Api::getUpdates(int32_t offset, int32_t limit, int32_t timeout) const { - vector<HttpReqArg> 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<Update>(TgTypeParser::getInstance().parseUpdate, sendRequest("getUpdates", args), "result"); + vector<HttpReqArg> 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().parseJsonAndGetArray<Update>(&TgTypeParser::parseJsonAndGetUpdate, sendRequest("getUpdates", args), "result"); } void Api::setWebhook(const string& url) const { - vector<HttpReqArg> args; - args.push_back(HttpReqArg("url", url)); - sendRequest("setWebhook", args); + vector<HttpReqArg> args; + args.push_back(HttpReqArg("url", url)); + sendRequest("setWebhook", args); } -boost::property_tree::ptree Api::sendRequest(const std::string& method, const std::vector<HttpReqArg>& args) const { - std::string url = "https://api.telegram.org/bot"; - url += _token; - url += "/"; +ptree Api::sendRequest(const string& method, const vector<HttpReqArg>& args) const { + string url = "https://api.telegram.org/bot"; + url += _token; + url += "/"; url += method; string serverResponse = HttpClient::getInstance().makeRequest(url, args); if (serverResponse.find("<html>") != serverResponse.npos) { @@ -267,7 +267,7 @@ boost::property_tree::ptree Api::sendRequest(const std::string& method, const st ptree result = TgTypeParser::getInstance().parseJson(serverResponse); try { if (result.get<bool>("ok")) { - return result; + return result.find("result")->second; } else { throw TgException(result.get("description", "")); } diff --git a/src/TgTypeParser.cpp b/src/TgTypeParser.cpp index 9a1f93a..81d2119 100644 --- a/src/TgTypeParser.cpp +++ b/src/TgTypeParser.cpp @@ -28,463 +28,463 @@ using namespace boost::property_tree; namespace TgBot { TgTypeParser& TgTypeParser::getInstance() { - static TgTypeParser result; - return result; + static TgTypeParser result; + return result; } -User::Ptr TgTypeParser::parseUser(const ptree& data) const { - User::Ptr result(new User); - result->id = data.get<int32_t>("id"); - result->firstName = data.get<string>("first_name"); - result->lastName = data.get("last_name", ""); - result->username = data.get("username", ""); - return result; +User::Ptr TgTypeParser::parseJsonAndGetUser(const ptree& data) const { + User::Ptr result(new User); + result->id = data.get<int32_t>("id"); + result->firstName = data.get<string>("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<int32_t>("id"); - result->title = data.get<string>("title"); - return result; + 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::parseJsonAndGetGroupChat(const ptree& data) const { + GroupChat::Ptr result(new GroupChat); + result->id = data.get<int32_t>("id"); + result->title = data.get<string>("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<int32_t>("message_id"); - result->from = parseUser(data.find("from")->second); - result->date = data.get<int32_t>("date"); - result->chat = parseGenericChat(data.find("chat")->second); - result->forwardFrom = tryParse<User>(parseUser, data, "forward_from"); - result->forwardDate = data.get("forward_date", 0); - result->replyToMessage = tryParse<Message>(parseMessage, data, "reply_to_message"); - result->text = data.get("text", ""); - result->audio = tryParse<Audio>(parseAudio, data, "audio"); - result->document = tryParse<Document>(parseDocument, data, "document"); - result->photo = parseArray<PhotoSize>(parsePhotoSize, data, "photo"); - result->sticker = tryParse<Sticker>(parseSticker, data, "sticker"); - result->video = tryParse<Video>(parseVideo, data, "video"); - result->contact = tryParse<Contact>(parseContact, data, "contact"); - result->location = tryParse<Location>(parseLocation, data, "location"); - result->newChatParticipant = tryParse<User>(parseUser, data, "new_chat_participant"); - result->leftChatParticipant = tryParse<User>(parseUser, data, "left_chat_participant"); - result->newChatTitle = data.get("new_chat_title", ""); - result->newChatPhoto = parseArray<PhotoSize>(parsePhotoSize, data, "new_chat_photo"); - result->deleteChatPhoto = data.get("delete_chat_photo", false); - result->groupChatCreated = data.get("group_chat_created", false); - result->caption = data.get("caption", false); - return result; + 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::parseJsonAndGetMessage(const ptree& data) const { + Message::Ptr result(new Message); + result->messageId = data.get<int32_t>("message_id"); + result->from = parseJsonAndGetUser(data.find("from")->second); + result->date = data.get<int32_t>("date"); + result->chat = parseJsonAndGetGenericChat(data.find("chat")->second); + result->forwardFrom = tryParseJson<User>(&TgTypeParser::parseJsonAndGetUser, data, "forward_from"); + result->forwardDate = data.get("forward_date", 0); + result->replyToMessage = tryParseJson<Message>(&TgTypeParser::parseJsonAndGetMessage, data, "reply_to_message"); + result->text = data.get("text", ""); + result->audio = tryParseJson<Audio>(&TgTypeParser::parseJsonAndGetAudio, data, "audio"); + result->document = tryParseJson<Document>(&TgTypeParser::parseJsonAndGetDocument, data, "document"); + result->photo = parseJsonAndGetArray<PhotoSize>(&TgTypeParser::parseJsonAndGetPhotoSize, data, "photo"); + result->sticker = tryParseJson<Sticker>(&TgTypeParser::parseJsonAndGetSticker, data, "sticker"); + result->video = tryParseJson<Video>(&TgTypeParser::parseJsonAndGetVideo, data, "video"); + result->contact = tryParseJson<Contact>(&TgTypeParser::parseJsonAndGetContact, data, "contact"); + result->location = tryParseJson<Location>(&TgTypeParser::parseJsonAndGetLocation, data, "location"); + result->newChatParticipant = tryParseJson<User>(&TgTypeParser::parseJsonAndGetUser, data, "new_chat_participant"); + result->leftChatParticipant = tryParseJson<User>(&TgTypeParser::parseJsonAndGetUser, data, "left_chat_participant"); + result->newChatTitle = data.get("new_chat_title", ""); + result->newChatPhoto = parseJsonAndGetArray<PhotoSize>(&TgTypeParser::parseJsonAndGetPhotoSize, data, "new_chat_photo"); + result->deleteChatPhoto = data.get("delete_chat_photo", false); + result->groupChatCreated = data.get("group_chat_created", false); + result->caption = data.get("caption", false); + return result; } string TgTypeParser::parseMessage(const Message::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "message_id", object->messageId); - appendToJson(result, "from", parseUser(object->from)); - appendToJson(result, "date", object->date); - appendToJson(result, "chat", parseGenericChat(object->chat)); - appendToJson(result, "forward_from", parseUser(object->forwardFrom)); - appendToJson(result, "forward_date", object->forwardDate); - appendToJson(result, "reply_to_message", parseMessage(object->replyToMessage)); - appendToJson(result, "text", object->text); - appendToJson(result, "audio", parseAudio(object->audio)); - appendToJson(result, "document", parseDocument(object->document)); - appendToJson(result, "photo", parseArray(parsePhotoSize, object->photo)); - appendToJson(result, "sticker", parseSticker(object->sticker)); - appendToJson(result, "video", parseVideo(object->video)); - appendToJson(result, "contact", parseContact(object->contact)); - appendToJson(result, "location", parseLocation(object->location)); - appendToJson(result, "new_chat_participant", parseUser(object->newChatParticipant)); - appendToJson(result, "left_chat_participant", parseUser(object->leftChatParticipant)); - appendToJson(result, "new_chat_title", object->newChatTitle); - appendToJson(result, "new_chat_photo", parseArray(parsePhotoSize, object->newChatPhoto)); - appendToJson(result, "delete_chat_photo", object->deleteChatPhoto); - appendToJson(result, "group_chat_created", object->groupChatCreated); - appendToJson(result, "caption", object->caption); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -PhotoSize::Ptr TgTypeParser::parsePhotoSize(const ptree& data) const { - PhotoSize::Ptr result(new PhotoSize); - result->fileId = data.get<string>("file_id"); - result->width = data.get<int32_t>("width"); - result->height = data.get<int32_t>("height"); - result->fileSize = data.get("file_size", 0); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "message_id", object->messageId); + appendToJson(result, "from", parseUser(object->from)); + appendToJson(result, "date", object->date); + appendToJson(result, "chat", parseGenericChat(object->chat)); + appendToJson(result, "forward_from", parseUser(object->forwardFrom)); + appendToJson(result, "forward_date", object->forwardDate); + appendToJson(result, "reply_to_message", parseMessage(object->replyToMessage)); + appendToJson(result, "text", object->text); + appendToJson(result, "audio", parseAudio(object->audio)); + appendToJson(result, "document", parseDocument(object->document)); + appendToJson(result, "photo", parseArray(&TgTypeParser::parsePhotoSize, object->photo)); + appendToJson(result, "sticker", parseSticker(object->sticker)); + appendToJson(result, "video", parseVideo(object->video)); + appendToJson(result, "contact", parseContact(object->contact)); + appendToJson(result, "location", parseLocation(object->location)); + appendToJson(result, "new_chat_participant", parseUser(object->newChatParticipant)); + appendToJson(result, "left_chat_participant", parseUser(object->leftChatParticipant)); + appendToJson(result, "new_chat_title", object->newChatTitle); + appendToJson(result, "new_chat_photo", parseArray(&TgTypeParser::parsePhotoSize, object->newChatPhoto)); + appendToJson(result, "delete_chat_photo", object->deleteChatPhoto); + appendToJson(result, "group_chat_created", object->groupChatCreated); + appendToJson(result, "caption", object->caption); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +PhotoSize::Ptr TgTypeParser::parseJsonAndGetPhotoSize(const ptree& data) const { + PhotoSize::Ptr result(new PhotoSize); + result->fileId = data.get<string>("file_id"); + result->width = data.get<int32_t>("width"); + result->height = data.get<int32_t>("height"); + result->fileSize = data.get("file_size", 0); + return result; } string TgTypeParser::parsePhotoSize(const PhotoSize::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "file_id", object->fileId); - appendToJson(result, "width", object->width); - appendToJson(result, "height", object->height); - appendToJson(result, "file_size", object->fileSize); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -Audio::Ptr TgTypeParser::parseAudio(const ptree& data) const { - Audio::Ptr result(new Audio); - result->fileId = data.get<string>("file_id"); - result->duration = data.get<int32_t>("duration"); - result->mimeType = data.get("mime_type", ""); - result->fileSize = data.get("file_size", 0); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "width", object->width); + appendToJson(result, "height", object->height); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Audio::Ptr TgTypeParser::parseJsonAndGetAudio(const ptree& data) const { + Audio::Ptr result(new Audio); + result->fileId = data.get<string>("file_id"); + result->duration = data.get<int32_t>("duration"); + result->mimeType = data.get("mime_type", ""); + result->fileSize = data.get("file_size", 0); + return result; } string TgTypeParser::parseAudio(const Audio::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "file_id", object->fileId); - appendToJson(result, "duration", object->duration); - appendToJson(result, "mime_type", object->mimeType); - appendToJson(result, "file_size", object->fileSize); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -Document::Ptr TgTypeParser::parseDocument(const ptree& data) const { - Document::Ptr result(new Document); - result->fileId = data.get<string>("file_id"); - result->thumb = parsePhotoSize(data.find("thumb")->second); - result->fileName = data.get("file_name", ""); - result->mimeType = data.get("mime_type", ""); - result->fileSize = data.get("file_size", 0); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "duration", object->duration); + appendToJson(result, "mime_type", object->mimeType); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Document::Ptr TgTypeParser::parseJsonAndGetDocument(const ptree& data) const { + Document::Ptr result(new Document); + result->fileId = data.get<string>("file_id"); + result->thumb = parseJsonAndGetPhotoSize(data.find("thumb")->second); + result->fileName = data.get("file_name", ""); + result->mimeType = data.get("mime_type", ""); + result->fileSize = data.get("file_size", 0); + return result; } string TgTypeParser::parseDocument(const Document::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "file_id", object->fileId); - appendToJson(result, "thumb", parsePhotoSize(object->thumb)); - appendToJson(result, "file_name", object->fileName); - appendToJson(result, "mime_type", object->mimeType); - appendToJson(result, "file_size", object->fileSize); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -Sticker::Ptr TgTypeParser::parseSticker(const ptree& data) const { - Sticker::Ptr result(new Sticker); - result->fileId = data.get<string>("file_id"); - result->width = data.get<int32_t>("width"); - result->height = data.get<int32_t>("height"); - result->thumb = parsePhotoSize(data.find("thumb")->second); - result->fileSize = data.get("file_size", 0); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "thumb", parsePhotoSize(object->thumb)); + appendToJson(result, "file_name", object->fileName); + appendToJson(result, "mime_type", object->mimeType); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Sticker::Ptr TgTypeParser::parseJsonAndGetSticker(const ptree& data) const { + Sticker::Ptr result(new Sticker); + result->fileId = data.get<string>("file_id"); + result->width = data.get<int32_t>("width"); + result->height = data.get<int32_t>("height"); + result->thumb = parseJsonAndGetPhotoSize(data.find("thumb")->second); + result->fileSize = data.get("file_size", 0); + return result; } string TgTypeParser::parseSticker(const Sticker::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "file_id", object->fileId); - appendToJson(result, "width", object->width); - appendToJson(result, "height", object->height); - appendToJson(result, "thumb", parsePhotoSize(object->thumb)); - appendToJson(result, "file_size", object->fileSize); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -Video::Ptr TgTypeParser::parseVideo(const ptree& data) const { - Video::Ptr result(new Video); - result->fileId = data.get<string>("file_id"); - result->width = data.get<int32_t>("width"); - result->height = data.get<int32_t>("height"); - result->duration = data.get<int32_t>("duration"); - result->thumb = parsePhotoSize(data.find("thumb")->second); - result->mimeType = data.get("mime_type", ""); - result->fileSize = data.get("file_size", 0); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "width", object->width); + appendToJson(result, "height", object->height); + appendToJson(result, "thumb", parsePhotoSize(object->thumb)); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Video::Ptr TgTypeParser::parseJsonAndGetVideo(const ptree& data) const { + Video::Ptr result(new Video); + result->fileId = data.get<string>("file_id"); + result->width = data.get<int32_t>("width"); + result->height = data.get<int32_t>("height"); + result->duration = data.get<int32_t>("duration"); + result->thumb = parseJsonAndGetPhotoSize(data.find("thumb")->second); + result->mimeType = data.get("mime_type", ""); + result->fileSize = data.get("file_size", 0); + return result; } string TgTypeParser::parseVideo(const Video::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "file_id", object->fileId); - appendToJson(result, "width", object->width); - appendToJson(result, "height", object->height); - appendToJson(result, "duration", object->duration); - appendToJson(result, "thumb", parsePhotoSize(object->thumb)); - appendToJson(result, "mime_type", object->mimeType); - appendToJson(result, "file_size", object->fileSize); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -Contact::Ptr TgTypeParser::parseContact(const ptree& data) const { - Contact::Ptr result(new Contact); - result->phoneNumber = data.get<string>("phone_number"); - result->firstName = data.get<string>("first_name"); - result->lastName = data.get("last_name", ""); - result->userId = data.get("user_id", ""); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "width", object->width); + appendToJson(result, "height", object->height); + appendToJson(result, "duration", object->duration); + appendToJson(result, "thumb", parsePhotoSize(object->thumb)); + appendToJson(result, "mime_type", object->mimeType); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Contact::Ptr TgTypeParser::parseJsonAndGetContact(const ptree& data) const { + Contact::Ptr result(new Contact); + result->phoneNumber = data.get<string>("phone_number"); + result->firstName = data.get<string>("first_name"); + result->lastName = data.get("last_name", ""); + result->userId = data.get("user_id", ""); + return result; } string TgTypeParser::parseContact(const Contact::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "phone_number", object->phoneNumber); - appendToJson(result, "first_name", object->firstName); - appendToJson(result, "last_name", object->lastName); - appendToJson(result, "user_id", object->userId); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -Location::Ptr TgTypeParser::parseLocation(const ptree& data) const { - Location::Ptr result(new Location); - result->longitude = data.get<float>("longitude"); - result->latitude = data.get<float>("latitude"); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "phone_number", object->phoneNumber); + appendToJson(result, "first_name", object->firstName); + appendToJson(result, "last_name", object->lastName); + appendToJson(result, "user_id", object->userId); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Location::Ptr TgTypeParser::parseJsonAndGetLocation(const ptree& data) const { + Location::Ptr result(new Location); + result->longitude = data.get<float>("longitude"); + result->latitude = data.get<float>("latitude"); + return result; } string TgTypeParser::parseLocation(const Location::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "longitude", object->longitude); - appendToJson(result, "latitude", object->latitude); - result.erase(result.length() - 1); - result += '}'; - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "longitude", object->longitude); + appendToJson(result, "latitude", object->latitude); + result.erase(result.length() - 1); + result += '}'; + return result; } -Update::Ptr TgTypeParser::parseUpdate(const ptree& data) const { - Update::Ptr result(new Update); - result->updateId = data.get<int32_t>("update_id"); - result->message = parseMessage(data.find("message")->second); - return result; +Update::Ptr TgTypeParser::parseJsonAndGetUpdate(const ptree& data) const { + Update::Ptr result(new Update); + result->updateId = data.get<int32_t>("update_id"); + result->message = parseJsonAndGetMessage(data.find("message")->second); + return result; } string TgTypeParser::parseUpdate(const Update::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "update_id", object->updateId); - appendToJson(result, "message", parseMessage(object->message)); - result.erase(result.length() - 1); - result += '}'; - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "update_id", object->updateId); + appendToJson(result, "message", parseMessage(object->message)); + result.erase(result.length() - 1); + result += '}'; + return result; } -UserProfilePhotos::Ptr TgTypeParser::parseUserProfilePhotos(const ptree& data) const { - UserProfilePhotos::Ptr result(new UserProfilePhotos); - result->totalCount = data.get<int32_t>("total_count"); - result->photos = parse2DArray<PhotoSize>(parsePhotoSize, data, "photos"); - return result; +UserProfilePhotos::Ptr TgTypeParser::parseJsonAndGetUserProfilePhotos(const ptree& data) const { + UserProfilePhotos::Ptr result(new UserProfilePhotos); + result->totalCount = data.get<int32_t>("total_count"); + result->photos = parseJsonAndGet2DArray<PhotoSize>(&TgTypeParser::parseJsonAndGetPhotoSize, data, "photos"); + return result; } string TgTypeParser::parseUserProfilePhotos(const UserProfilePhotos::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "total_count", object->totalCount); - appendToJson(result, "photos", parse2DArray(parsePhotoSize, object->photos)); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -ReplyKeyboardMarkup::Ptr TgTypeParser::parseReplyKeyboardMarkup(const boost::property_tree::ptree& data) const { - ReplyKeyboardMarkup::Ptr result(new ReplyKeyboardMarkup); - for (const pair<const string, ptree>& item : data.find("keyboard")->second) { - vector<string> array; - for (const pair<const string, ptree>& innerItem : item.second) { - array.push_back(innerItem.second.data()); - } - result->keyboard.push_back(array); - } - result->resizeKeyboard = data.get<bool>("resize_keyboard"); - result->oneTimeKeyboard = data.get<bool>("one_time_keyboard"); - result->selective = data.get<bool>("selective"); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "total_count", object->totalCount); + appendToJson(result, "photos", parse2DArray(&TgTypeParser::parsePhotoSize, object->photos)); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +ReplyKeyboardMarkup::Ptr TgTypeParser::parseJsonAndGetReplyKeyboardMarkup(const boost::property_tree::ptree& data) const { + ReplyKeyboardMarkup::Ptr result(new ReplyKeyboardMarkup); + for (const pair<const string, ptree>& item : data.find("keyboard")->second) { + vector<string> array; + for (const pair<const string, ptree>& innerItem : item.second) { + array.push_back(innerItem.second.data()); + } + result->keyboard.push_back(array); + } + result->resizeKeyboard = data.get<bool>("resize_keyboard"); + result->oneTimeKeyboard = data.get<bool>("one_time_keyboard"); + result->selective = data.get<bool>("selective"); + return result; } std::string TgTypeParser::parseReplyKeyboardMarkup(const ReplyKeyboardMarkup::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - result += "\"keyboard\":["; - for (vector<string>& item : object->keyboard) { - result += '['; - for (string& innerItem : item) { - result += '"'; - result += innerItem; - result += "\","; - } - result.erase(result.length() - 1); - result += "],"; - } - result.erase(result.length() - 1); - result += "],"; - appendToJson(result, "resize_keyboard", object->resizeKeyboard); - appendToJson(result, "one_time_keyboard", object->oneTimeKeyboard); - appendToJson(result, "selective", object->selective); - result.erase(result.length() - 1); - result += '}'; - return result; -} - -ReplyKeyboardHide::Ptr TgTypeParser::parseReplyKeyboardHide(const boost::property_tree::ptree& data) const { - ReplyKeyboardHide::Ptr result(new ReplyKeyboardHide); - result->selective = data.get<bool>("selective"); - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + result += "\"keyboard\":["; + for (vector<string>& item : object->keyboard) { + result += '['; + for (string& innerItem : item) { + result += '"'; + result += innerItem; + result += "\","; + } + result.erase(result.length() - 1); + result += "],"; + } + result.erase(result.length() - 1); + result += "],"; + appendToJson(result, "resize_keyboard", object->resizeKeyboard); + appendToJson(result, "one_time_keyboard", object->oneTimeKeyboard); + appendToJson(result, "selective", object->selective); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +ReplyKeyboardHide::Ptr TgTypeParser::parseJsonAndGetReplyKeyboardHide(const boost::property_tree::ptree& data) const { + ReplyKeyboardHide::Ptr result(new ReplyKeyboardHide); + result->selective = data.get<bool>("selective"); + return result; } std::string TgTypeParser::parseReplyKeyboardHide(const ReplyKeyboardHide::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "selective", object->selective); - result.erase(result.length() - 1); - result += '}'; - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "selective", object->selective); + result.erase(result.length() - 1); + result += '}'; + return result; } -ForceReply::Ptr TgTypeParser::parseForceReply(const boost::property_tree::ptree& data) const { - ForceReply::Ptr result(new ForceReply); - result->selective = data.get<bool>("selective"); - return result; +ForceReply::Ptr TgTypeParser::parseJsonAndGetForceReply(const boost::property_tree::ptree& data) const { + ForceReply::Ptr result(new ForceReply); + result->selective = data.get<bool>("selective"); + return result; } std::string TgTypeParser::parseForceReply(const ForceReply::Ptr& object) const { - if (!object) { - return ""; - } - string result; - result += '{'; - appendToJson(result, "selective", object->selective); - result.erase(result.length() - 1); - result += '}'; - return result; + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "selective", object->selective); + result.erase(result.length() - 1); + result += '}'; + return result; } -GenericChat::Ptr TgTypeParser::parseGenericChat(const ptree& data) const { - if (data.find("first_name") == data.not_found()) { - return static_pointer_cast<GenericChat>(parseGroupChat(data)); - } else { - return static_pointer_cast<GenericChat>(parseUser(data)); - } +GenericChat::Ptr TgTypeParser::parseJsonAndGetGenericChat(const ptree& data) const { + if (data.find("first_name") == data.not_found()) { + return static_pointer_cast<GenericChat>(parseJsonAndGetGroupChat(data)); + } else { + return static_pointer_cast<GenericChat>(parseJsonAndGetUser(data)); + } } string TgTypeParser::parseGenericChat(const GenericChat::Ptr& object) const { - if (!object) { - return ""; - } - if (dynamic_pointer_cast<User>(object) == nullptr) { - return parseGroupChat(static_pointer_cast<GroupChat>(object)); - } else { - return parseUser(static_pointer_cast<User>(object)); - } -} - -GenericReply::Ptr TgTypeParser::parseGenericReply(const boost::property_tree::ptree& data) const { - if (data.find("force_reply") != data.not_found()) { - return static_pointer_cast<GenericReply>(parseForceReply(data)); - } else if (data.find("hide_keyboard") != data.not_found()) { - return static_pointer_cast<GenericReply>(parseReplyKeyboardHide(data)); - } else { - return static_pointer_cast<GenericReply>(parseReplyKeyboardMarkup(data)); - } + if (!object) { + return ""; + } + if (dynamic_pointer_cast<User>(object) == nullptr) { + return parseGroupChat(static_pointer_cast<GroupChat>(object)); + } else { + return parseUser(static_pointer_cast<User>(object)); + } +} + +GenericReply::Ptr TgTypeParser::parseJsonAndGetGenericReply(const boost::property_tree::ptree& data) const { + if (data.find("force_reply") != data.not_found()) { + return static_pointer_cast<GenericReply>(parseJsonAndGetForceReply(data)); + } else if (data.find("hide_keyboard") != data.not_found()) { + return static_pointer_cast<GenericReply>(parseJsonAndGetReplyKeyboardHide(data)); + } else { + return static_pointer_cast<GenericReply>(parseJsonAndGetReplyKeyboardMarkup(data)); + } } std::string TgTypeParser::parseGenericReply(const GenericReply::Ptr& object) const { - if (!object) { - return ""; - } - if (dynamic_pointer_cast<ForceReply>(object) != nullptr) { - return parseForceReply(static_pointer_cast<ForceReply>(object)); - } else if (dynamic_pointer_cast<ReplyKeyboardHide>(object) != nullptr) { - return parseReplyKeyboardHide(static_pointer_cast<ReplyKeyboardHide>(object)); - } else { - return parseReplyKeyboardMarkup(static_pointer_cast<ReplyKeyboardMarkup>(object)); - } + if (!object) { + return ""; + } + if (dynamic_pointer_cast<ForceReply>(object) != nullptr) { + return parseForceReply(static_pointer_cast<ForceReply>(object)); + } else if (dynamic_pointer_cast<ReplyKeyboardHide>(object) != nullptr) { + return parseReplyKeyboardHide(static_pointer_cast<ReplyKeyboardHide>(object)); + } else { + return parseReplyKeyboardMarkup(static_pointer_cast<ReplyKeyboardMarkup>(object)); + } } void TgTypeParser::appendToJson(string& json, const string& varName, const string& value) const { - if (value.empty()) { - return; - } - json += '"'; - json += varName; - json += "\":"; - if (value.front() != '{') { - json += '"'; - } - json += value; - if (value.back() != '}') { - json += '"'; - } - json += ','; -} - -}
\ No newline at end of file + if (value.empty()) { + return; + } + json += '"'; + json += varName; + json += "\":"; + if (value.front() != '{') { + json += '"'; + } + json += value; + if (value.back() != '}') { + json += '"'; + } + json += ','; +} + +} diff --git a/src/net/HttpClient.cpp b/src/net/HttpClient.cpp index 5027b09..b9813ad 100644 --- a/src/net/HttpClient.cpp +++ b/src/net/HttpClient.cpp @@ -32,36 +32,36 @@ using namespace boost::asio::local; namespace TgBot { HttpClient& HttpClient::getInstance() { - static HttpClient result; - return result; + static HttpClient result; + return result; } string HttpClient::makeRequest(const Url& url, const vector<HttpReqArg>& args) { - ssl::context context(ssl::context::sslv23); - context.set_default_verify_paths(); + ssl::context context(ssl::context::sslv23); + context.set_default_verify_paths(); - ssl::stream<tcp::socket> socket(_ioService, context); - tcp::resolver resolver(_ioService); - tcp::resolver::query query(url.host, url.protocol); + ssl::stream<tcp::socket> socket(_ioService, context); + tcp::resolver resolver(_ioService); + tcp::resolver::query query(url.host, url.protocol); - connect(socket.lowest_layer(), resolver.resolve(query)); + 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<tcp::socket>::client); + socket.set_verify_mode(ssl::verify_none); + socket.set_verify_callback(ssl::rfc2818_verification(url.host)); + socket.handshake(ssl::stream<tcp::socket>::client); - string requestText = HttpParser::getInstance().generateRequest(url, args, false); - write(socket, buffer(requestText.c_str(), requestText.length())); + string requestText = HttpParser::getInstance().generateRequest(url, args, false); + write(socket, buffer(requestText.c_str(), requestText.length())); - string response; - char buff[1024]; - boost::system::error_code error; - while (!error) { - size_t bytes = read(socket, buffer(buff), error); - response += string(buff, bytes); - } + string response; + char buff[1024]; + boost::system::error_code error; + while (!error) { + size_t bytes = read(socket, buffer(buff), error); + response += string(buff, bytes); + } - return HttpParser::getInstance().parseResponse(response); + return HttpParser::getInstance().parseResponse(response); } } diff --git a/src/net/HttpParser.cpp b/src/net/HttpParser.cpp index ae9de62..a2d9108 100644 --- a/src/net/HttpParser.cpp +++ b/src/net/HttpParser.cpp @@ -32,173 +32,173 @@ using namespace boost; namespace TgBot { HttpParser& HttpParser::getInstance() { - static HttpParser result; - return result; + static HttpParser result; + return result; } string HttpParser::generateRequest(const Url& url, const vector<HttpReqArg>& args, bool isKeepAlive) { - string result; - if (args.empty()) { - result += "GET "; - } else { - result += "POST "; - } - result += url.path; - result += url.query.empty() ? "" : "?" + url.query; - result += " HTTP/1.1\r\n"; - result += "Host: "; - result += url.host; - result += "\r\nConnection: "; - if (isKeepAlive) { - result += "keep-alive"; - } else { - result += "close"; - } - result += "\r\n"; - if (args.empty()) { - result += "\r\n"; - } else { - string requestData; - - string bondary = generateMultipartBoundary(args); - if (bondary.empty()) { - result += "Content-Type: application/x-www-form-urlencoded\r\n"; - requestData = generateWwwFormUrlencoded(args); - } else { - result += "Content-Type: multipart/form-data; boundary="; - result += bondary; - result += "\r\n"; - requestData = generateMultipartFormData(args, bondary); - } - - result += "Content-Length: "; - result += lexical_cast<string>(requestData.length()); - result += "\r\n\r\n"; - result += requestData; - } - return result; + string result; + if (args.empty()) { + result += "GET "; + } else { + result += "POST "; + } + result += url.path; + result += url.query.empty() ? "" : "?" + url.query; + result += " HTTP/1.1\r\n"; + result += "Host: "; + result += url.host; + result += "\r\nConnection: "; + if (isKeepAlive) { + result += "keep-alive"; + } else { + result += "close"; + } + result += "\r\n"; + if (args.empty()) { + result += "\r\n"; + } else { + string requestData; + + string bondary = generateMultipartBoundary(args); + if (bondary.empty()) { + result += "Content-Type: application/x-www-form-urlencoded\r\n"; + requestData = generateWwwFormUrlencoded(args); + } else { + result += "Content-Type: multipart/form-data; boundary="; + result += bondary; + result += "\r\n"; + requestData = generateMultipartFormData(args, bondary); + } + + result += "Content-Length: "; + result += lexical_cast<string>(requestData.length()); + result += "\r\n\r\n"; + result += requestData; + } + return result; } string HttpParser::generateMultipartFormData(const vector<HttpReqArg>& args, const string& bondary) { - string result; - for (const HttpReqArg& item : args) { - result += "--"; - result += bondary; - result += "\r\nContent-Disposition: form-data; name=\""; - result += item.name; - result += "\"\r\n"; - if (item.isFile) { - result += "Content-Type: "; - result += item.mimeType; - result += "\r\n"; - } - result += "\r\n"; - result += item.value; - result += "\r\n"; - } - return result; + string result; + for (const HttpReqArg& item : args) { + result += "--"; + result += bondary; + result += "\r\nContent-Disposition: form-data; name=\""; + result += item.name; + result += "\"\r\n"; + if (item.isFile) { + result += "Content-Type: "; + result += item.mimeType; + result += "\r\n"; + } + result += "\r\n"; + result += item.value; + result += "\r\n"; + } + return result; } string HttpParser::generateMultipartBoundary(const vector<HttpReqArg>& args) { - string result; - srand((unsigned int) time(nullptr)); - for (const HttpReqArg& item : args) { - if (item.isFile) { - while (result.empty() || item.value.find(result) != item.value.npos) { - result += StringTools::generateRandomString(4); - } - } - } - return result; + string result; + srand((unsigned int) time(nullptr)); + for (const HttpReqArg& item : args) { + if (item.isFile) { + while (result.empty() || item.value.find(result) != item.value.npos) { + result += StringTools::generateRandomString(4); + } + } + } + return result; } string HttpParser::generateWwwFormUrlencoded(const vector<HttpReqArg>& args) { - string result; - - bool firstRun = true; - for (const HttpReqArg& item : args) { - if (firstRun) { - firstRun = false; - } else { - result += '&'; - } - result += StringTools::urlEncode(item.name); - result += '='; - result += StringTools::urlEncode(item.value); - } - - return result; + string result; + + bool firstRun = true; + for (const HttpReqArg& item : args) { + if (firstRun) { + firstRun = false; + } else { + result += '&'; + } + result += StringTools::urlEncode(item.name); + result += '='; + result += StringTools::urlEncode(item.value); + } + + return result; } string HttpParser::generateResponse(const string& data, const string& mimeType, unsigned short statusCode, const string& statusStr, bool isKeepAlive) { - string result; - result += "HTTP/1.1 "; - result += lexical_cast<string>(statusCode); - result += ' '; - result += statusStr; - result += "\r\nContent-Type: "; - result += mimeType; - result += "\r\nContent-Length: "; - result += lexical_cast<string>(data.length()); - result += "\r\n\r\n"; - result += data; - return result; + string result; + result += "HTTP/1.1 "; + result += lexical_cast<string>(statusCode); + result += ' '; + result += statusStr; + result += "\r\nContent-Type: "; + result += mimeType; + result += "\r\nContent-Length: "; + result += lexical_cast<string>(data.length()); + result += "\r\n\r\n"; + result += data; + return result; } string HttpParser::parseHttp(bool isRequest, const string& data, map<string, string>& headers) { - bool onlyNewLineChar = false; - size_t headerEnd = data.find("\r\n\r\n"); - if (headerEnd == data.npos) { - headerEnd = data.find("\n\n"); - if (headerEnd != data.npos) { - onlyNewLineChar = true; - headerEnd += 2; - } - } else { - headerEnd += 4; - } - - size_t lineStart = 0; - size_t lineEnd = 0; - size_t lineSepPos = 0; - size_t lastLineEnd = data.npos; - while (lastLineEnd != lineEnd) { - lastLineEnd = lineEnd; - if (lineEnd == 0) { - if (isRequest) { - lineSepPos = data.find(' '); - lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n"); - headers["method"] = data.substr(0, lineSepPos); - headers["path"] = data.substr(lineSepPos + 1, data.find(' ', lineSepPos + 1) - lineSepPos - 1); - } else { - lineSepPos = data.find(' '); - lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n"); - headers["status"] = data.substr(lineSepPos + 1, data.find(' ', lineSepPos + 1) - lineSepPos - 1); - } - } else { - lineStart = lineEnd; - lineStart += onlyNewLineChar ? 1 : 2; - lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n", lineStart); - lineSepPos = data.find(':', lineStart); - if (lineEnd >= headerEnd || lastLineEnd == lineEnd || lineSepPos >= headerEnd) { - break; - } - headers[to_lower_copy(data.substr(lineStart, lineSepPos - lineStart))] = trim_copy(data.substr(lineSepPos + 1, lineEnd - lineSepPos - 1)); - } - } - - return headerEnd == data.npos ? "" : data.substr(headerEnd); + bool onlyNewLineChar = false; + size_t headerEnd = data.find("\r\n\r\n"); + if (headerEnd == data.npos) { + headerEnd = data.find("\n\n"); + if (headerEnd != data.npos) { + onlyNewLineChar = true; + headerEnd += 2; + } + } else { + headerEnd += 4; + } + + size_t lineStart = 0; + size_t lineEnd = 0; + size_t lineSepPos = 0; + size_t lastLineEnd = data.npos; + while (lastLineEnd != lineEnd) { + lastLineEnd = lineEnd; + if (lineEnd == 0) { + if (isRequest) { + lineSepPos = data.find(' '); + lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n"); + headers["method"] = data.substr(0, lineSepPos); + headers["path"] = data.substr(lineSepPos + 1, data.find(' ', lineSepPos + 1) - lineSepPos - 1); + } else { + lineSepPos = data.find(' '); + lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n"); + headers["status"] = data.substr(lineSepPos + 1, data.find(' ', lineSepPos + 1) - lineSepPos - 1); + } + } else { + lineStart = lineEnd; + lineStart += onlyNewLineChar ? 1 : 2; + lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n", lineStart); + lineSepPos = data.find(':', lineStart); + if (lineEnd >= headerEnd || lastLineEnd == lineEnd || lineSepPos >= headerEnd) { + break; + } + headers[to_lower_copy(data.substr(lineStart, lineSepPos - lineStart))] = trim_copy(data.substr(lineSepPos + 1, lineEnd - lineSepPos - 1)); + } + } + + return headerEnd == data.npos ? "" : data.substr(headerEnd); } string HttpParser::parseHttp(bool isRequest, const string& data) { - size_t headerEnd = data.find("\r\n\r\n"); - if (headerEnd == data.npos) { - headerEnd = data.find("\n\n"); - } - if (headerEnd == data.npos) { - headerEnd = 0; - } - return data.substr(headerEnd); + size_t headerEnd = data.find("\r\n\r\n"); + if (headerEnd == data.npos) { + headerEnd = data.find("\n\n"); + } + if (headerEnd == data.npos) { + headerEnd = 0; + } + return data.substr(headerEnd); } } diff --git a/src/net/TgLongPoll.cpp b/src/net/TgLongPoll.cpp index f420e92..91bf058 100644 --- a/src/net/TgLongPoll.cpp +++ b/src/net/TgLongPoll.cpp @@ -31,13 +31,13 @@ TgLongPoll::TgLongPoll(const Bot& bot) : TgLongPoll(&bot.getApi(), &bot.getEvent } void TgLongPoll::start() { - std::vector<Update::Ptr> updates = _api->getUpdates(_lastUpdateId, 100, 60); - for (Update::Ptr& item : updates) { - if (item->updateId >= _lastUpdateId) { - _lastUpdateId = item->updateId + 1; - } - _eventHandler->handleUpdate(item); - } + std::vector<Update::Ptr> updates = _api->getUpdates(_lastUpdateId, 100, 60); + for (Update::Ptr& item : updates) { + if (item->updateId >= _lastUpdateId) { + _lastUpdateId = item->updateId + 1; + } + _eventHandler->handleUpdate(item); + } } } diff --git a/src/net/Url.cpp b/src/net/Url.cpp index c773f3b..44d9089 100644 --- a/src/net/Url.cpp +++ b/src/net/Url.cpp @@ -29,52 +29,52 @@ using namespace std; namespace TgBot { Url::Url(const string& url) { - bool isProtocolParsed = false; - bool isHostParsed = false; - bool isPathParsed = false; - bool isQueryParsed = false; + bool isProtocolParsed = false; + bool isHostParsed = false; + bool isPathParsed = false; + bool isQueryParsed = false; - for (size_t i = 0, count = url.length(); i < count; ++i) { - char c = url[i]; + for (size_t i = 0, count = url.length(); i < count; ++i) { + char c = url[i]; - if (!isProtocolParsed) { - if (c == ':') { - isProtocolParsed = true; - i += 2; - } else { - protocol += c; - } - } else if (!isHostParsed) { - if (c == '/') { - isHostParsed = true; - path += '/'; - } else if (c == '?') { - isHostParsed = isPathParsed = true; - path += '/'; - } else if (c == '#') { - isHostParsed = isPathParsed = isQueryParsed = true; - path += '/'; - } else { - host += c; - } - } else if (!isPathParsed) { - if (c == '?') { - isPathParsed = true; - } else if (c == '#') { - isPathParsed = isQueryParsed = true; - } else { - path += c; - } - } else if (!isQueryParsed) { - if (c == '#') { - isQueryParsed = true; - } else { - query += c; - } - } else { - fragment += c; - } - } + if (!isProtocolParsed) { + if (c == ':') { + isProtocolParsed = true; + i += 2; + } else { + protocol += c; + } + } else if (!isHostParsed) { + if (c == '/') { + isHostParsed = true; + path += '/'; + } else if (c == '?') { + isHostParsed = isPathParsed = true; + path += '/'; + } else if (c == '#') { + isHostParsed = isPathParsed = isQueryParsed = true; + path += '/'; + } else { + host += c; + } + } else if (!isPathParsed) { + if (c == '?') { + isPathParsed = true; + } else if (c == '#') { + isPathParsed = isQueryParsed = true; + } else { + path += c; + } + } else if (!isQueryParsed) { + if (c == '#') { + isQueryParsed = true; + } else { + query += c; + } + } else { + fragment += c; + } + } host = StringTools::urlEncode(host, "."); path = StringTools::urlEncode(path, "/"); diff --git a/src/tools/Api.cpp b/src/tools/Api.cpp new file mode 100644 index 0000000..0bd970c --- /dev/null +++ b/src/tools/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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<HttpReqArg> 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<Update::Ptr> Api::getUpdates(int32_t offset, int32_t limit, int32_t timeout) const { + vector<HttpReqArg> 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<Update>(TgTypeParser::getInstance().parseUpdate, sendRequest("getUpdates", args), "result"); +} + +void Api::setWebhook(const string& url) const { + vector<HttpReqArg> args; + args.push_back(HttpReqArg("url", url)); + sendRequest("setWebhook", args); +} + +boost::property_tree::ptree Api::sendRequest(const std::string& method, const std::vector<HttpReqArg>& 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("<html>") != serverResponse.npos) { + throw TgException("Bad request"); + } + ptree result = TgTypeParser::getInstance().parseJson(serverResponse); + try { + if (result.get<bool>("ok")) { + return result; + } else { + throw TgException(result.get("description", "")); + } + } catch (boost::property_tree::ptree_error& e) { + throw TgException(""); + } +} + +} diff --git a/src/tools/StringTools.cpp b/src/tools/StringTools.cpp index deb8fa1..0a6dbc3 100644 --- a/src/tools/StringTools.cpp +++ b/src/tools/StringTools.cpp @@ -35,17 +35,17 @@ bool startsWith(const string& str1, const string& str2) { return false; } string::const_iterator it1(str1.begin()); - string::const_iterator end1(str1.end()); - string::const_iterator it2(str2.begin()); - string::const_iterator end2(str2.end()); - while (it1 != end1 && it2 != end2) { - if (*it1 != *it2) { - return false; - } - ++it1; - ++it2; - } - return true; + string::const_iterator end1(str1.end()); + string::const_iterator it2(str2.begin()); + string::const_iterator end2(str2.end()); + while (it1 != end1 && it2 != end2) { + if (*it1 != *it2) { + return false; + } + ++it1; + ++it2; + } + return true; } bool endsWith(const string& str1, const string& str2) { @@ -53,67 +53,67 @@ bool endsWith(const string& str1, const string& str2) { return false; } string::const_iterator it1(str1.end()); - string::const_iterator begin1(str1.begin()); - string::const_iterator it2(str2.end()); - string::const_iterator begin2(str2.begin()); - while (it1 != begin1 && it2 != begin2) { - if (*it1 != *it2) { - return false; - } - --it1; - --it2; - } - return true; + string::const_iterator begin1(str1.begin()); + string::const_iterator it2(str2.end()); + string::const_iterator begin2(str2.begin()); + while (it1 != begin1 && it2 != begin2) { + if (*it1 != *it2) { + return false; + } + --it1; + --it2; + } + return true; } void split(const string& str, char delimiter, vector<string>& dest) { - istringstream stream(str); - string s; - while (getline(stream, s, delimiter)) { - dest.push_back(s); - } + istringstream stream(str); + string s; + while (getline(stream, s, delimiter)) { + dest.push_back(s); + } } string generateRandomString(size_t length) { - static const string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890-=[]\\;',./!@#$%^&*()_+{}|:\"<>?`~"); - static const size_t charsLen = chars.length(); - string result; - for (int i = 0; i < length; ++i) { - result += chars[rand() % charsLen]; - } - return result; + static const string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890-=[]\\;',./!@#$%^&*()_+{}|:\"<>?`~"); + static const size_t charsLen = chars.length(); + string result; + for (int i = 0; i < length; ++i) { + result += chars[rand() % charsLen]; + } + return result; } string urlEncode(const string& value, const std::string additionalLegitChars) { - static const string legitPunctuation = "-_.~"; - ostringstream result; - result.fill('0'); - result << hex; - for (const char& c : value) { - if (isalnum(c) || legitPunctuation.find(c) != legitPunctuation.npos || additionalLegitChars.find(c) != additionalLegitChars.npos) { - result << c; - } else { - result << '%' << setw(2) << int((unsigned char) c); - } - } + static const string legitPunctuation = "-_.~"; + ostringstream result; + result.fill('0'); + result << hex; + for (const char& c : value) { + if (isalnum(c) || legitPunctuation.find(c) != legitPunctuation.npos || additionalLegitChars.find(c) != additionalLegitChars.npos) { + result << c; + } else { + result << '%' << setw(2) << int((unsigned char) c); + } + } - return result.str(); + return result.str(); } string urlDecode(const string& value) { - string result; - for (size_t i = 0, count = value.length(); i < count; ++i) { - const char c = value[i]; - if (c == '%') { - int t = 0; - sscanf(value.substr(i + 1, 2).c_str(), "%x", &t); - result += (char) t; - i += 2; - } else { - result += c; - } - } - return result; + string result; + for (size_t i = 0, count = value.length(); i < count; ++i) { + const char c = value[i]; + if (c == '%') { + int t = 0; + sscanf(value.substr(i + 1, 2).c_str(), "%x", &t); + result += (char) t; + i += 2; + } else { + result += c; + } + } + return result; } } diff --git a/src/tools/TgException.cpp b/src/tools/TgException.cpp new file mode 100644 index 0000000..0c118ec --- /dev/null +++ b/src/tools/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/tools/TgTypeParser.cpp b/src/tools/TgTypeParser.cpp new file mode 100644 index 0000000..a9c6812 --- /dev/null +++ b/src/tools/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::parseJsonAndGetUser(const ptree& data) const { + User::Ptr result(new User); + result->id = data.get<int32_t>("id"); + result->firstName = data.get<string>("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::parseJsonAndGetGroupChat(const ptree& data) const { + GroupChat::Ptr result(new GroupChat); + result->id = data.get<int32_t>("id"); + result->title = data.get<string>("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::parseJsonAndGetMessage(const ptree& data) const { + Message::Ptr result(new Message); + result->messageId = data.get<int32_t>("message_id"); + result->from = parseJsonAndGetUser(data.find("from")->second); + result->date = data.get<int32_t>("date"); + result->chat = parseJsonAndGetGenericChat(data.find("chat")->second); + result->forwardFrom = tryParseJson<User>(&TgTypeParser::parseJsonAndGetUser, data, "forward_from"); + result->forwardDate = data.get("forward_date", 0); + result->replyToMessage = tryParseJson<Message>(parseJsonAndGetMessage, data, "reply_to_message"); + result->text = data.get("text", ""); + result->audio = tryParseJson<Audio>(parseJsonAndGetAudio, data, "audio"); + result->document = tryParseJson<Document>(parseJsonAndGetDocument, data, "document"); + result->photo = parseArray<PhotoSize>(parseJsonAndGetPhotoSize, data, "photo"); + result->sticker = tryParseJson<Sticker>(parseJsonAndGetSticker, data, "sticker"); + result->video = tryParseJson<Video>(parseJsonAndGetVideo, data, "video"); + result->contact = tryParseJson<Contact>(parseJsonAndGetContact, data, "contact"); + result->location = tryParseJson<Location>(parseJsonAndGetLocation, data, "location"); + result->newChatParticipant = tryParseJson<User>(parseJsonAndGetUser, data, "new_chat_participant"); + result->leftChatParticipant = tryParseJson<User>(parseJsonAndGetUser, data, "left_chat_participant"); + result->newChatTitle = data.get("new_chat_title", ""); + result->newChatPhoto = parseJsonAndGetArray<PhotoSize>(parseJsonAndGetPhotoSize, data, "new_chat_photo"); + result->deleteChatPhoto = data.get("delete_chat_photo", false); + result->groupChatCreated = data.get("group_chat_created", false); + result->caption = data.get("caption", false); + return result; +} + +string TgTypeParser::parseMessage(const Message::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "message_id", object->messageId); + appendToJson(result, "from", parseUser(object->from)); + appendToJson(result, "date", object->date); + appendToJson(result, "chat", parseGenericChat(object->chat)); + appendToJson(result, "forward_from", parseUser(object->forwardFrom)); + appendToJson(result, "forward_date", object->forwardDate); + appendToJson(result, "reply_to_message", parseMessage(object->replyToMessage)); + appendToJson(result, "text", object->text); + appendToJson(result, "audio", parseAudio(object->audio)); + appendToJson(result, "document", parseDocument(object->document)); + appendToJson(result, "photo", parseArray(parsePhotoSize, object->photo)); + appendToJson(result, "sticker", parseSticker(object->sticker)); + appendToJson(result, "video", parseVideo(object->video)); + appendToJson(result, "contact", parseContact(object->contact)); + appendToJson(result, "location", parseLocation(object->location)); + appendToJson(result, "new_chat_participant", parseUser(object->newChatParticipant)); + appendToJson(result, "left_chat_participant", parseUser(object->leftChatParticipant)); + appendToJson(result, "new_chat_title", object->newChatTitle); + appendToJson(result, "new_chat_photo", parseArray(parsePhotoSize, object->newChatPhoto)); + appendToJson(result, "delete_chat_photo", object->deleteChatPhoto); + appendToJson(result, "group_chat_created", object->groupChatCreated); + appendToJson(result, "caption", object->caption); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +PhotoSize::Ptr TgTypeParser::parseJsonAndGetPhotoSize(const ptree& data) const { + PhotoSize::Ptr result(new PhotoSize); + result->fileId = data.get<string>("file_id"); + result->width = data.get<int32_t>("width"); + result->height = data.get<int32_t>("height"); + result->fileSize = data.get("file_size", 0); + return result; +} + +string TgTypeParser::parsePhotoSize(const PhotoSize::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "width", object->width); + appendToJson(result, "height", object->height); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Audio::Ptr TgTypeParser::parseJsonAndGetAudio(const ptree& data) const { + Audio::Ptr result(new Audio); + result->fileId = data.get<string>("file_id"); + result->duration = data.get<int32_t>("duration"); + result->mimeType = data.get("mime_type", ""); + result->fileSize = data.get("file_size", 0); + return result; +} + +string TgTypeParser::parseAudio(const Audio::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "duration", object->duration); + appendToJson(result, "mime_type", object->mimeType); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Document::Ptr TgTypeParser::parseJsonAndGetDocument(const ptree& data) const { + Document::Ptr result(new Document); + result->fileId = data.get<string>("file_id"); + result->thumb = parseJsonAndGetPhotoSize(data.find("thumb")->second); + result->fileName = data.get("file_name", ""); + result->mimeType = data.get("mime_type", ""); + result->fileSize = data.get("file_size", 0); + return result; +} + +string TgTypeParser::parseDocument(const Document::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "thumb", parsePhotoSize(object->thumb)); + appendToJson(result, "file_name", object->fileName); + appendToJson(result, "mime_type", object->mimeType); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Sticker::Ptr TgTypeParser::parseJsonAndGetSticker(const ptree& data) const { + Sticker::Ptr result(new Sticker); + result->fileId = data.get<string>("file_id"); + result->width = data.get<int32_t>("width"); + result->height = data.get<int32_t>("height"); + result->thumb = parseJsonAndGetPhotoSize(data.find("thumb")->second); + result->fileSize = data.get("file_size", 0); + return result; +} + +string TgTypeParser::parseSticker(const Sticker::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "width", object->width); + appendToJson(result, "height", object->height); + appendToJson(result, "thumb", parsePhotoSize(object->thumb)); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Video::Ptr TgTypeParser::parseJsonAndGetVideo(const ptree& data) const { + Video::Ptr result(new Video); + result->fileId = data.get<string>("file_id"); + result->width = data.get<int32_t>("width"); + result->height = data.get<int32_t>("height"); + result->duration = data.get<int32_t>("duration"); + result->thumb = parseJsonAndGetPhotoSize(data.find("thumb")->second); + result->mimeType = data.get("mime_type", ""); + result->fileSize = data.get("file_size", 0); + return result; +} + +string TgTypeParser::parseVideo(const Video::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "file_id", object->fileId); + appendToJson(result, "width", object->width); + appendToJson(result, "height", object->height); + appendToJson(result, "duration", object->duration); + appendToJson(result, "thumb", parsePhotoSize(object->thumb)); + appendToJson(result, "mime_type", object->mimeType); + appendToJson(result, "file_size", object->fileSize); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Contact::Ptr TgTypeParser::parseJsonAndGetContact(const ptree& data) const { + Contact::Ptr result(new Contact); + result->phoneNumber = data.get<string>("phone_number"); + result->firstName = data.get<string>("first_name"); + result->lastName = data.get("last_name", ""); + result->userId = data.get("user_id", ""); + return result; +} + +string TgTypeParser::parseContact(const Contact::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "phone_number", object->phoneNumber); + appendToJson(result, "first_name", object->firstName); + appendToJson(result, "last_name", object->lastName); + appendToJson(result, "user_id", object->userId); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Location::Ptr TgTypeParser::parseJsonAndGetLocation(const ptree& data) const { + Location::Ptr result(new Location); + result->longitude = data.get<float>("longitude"); + result->latitude = data.get<float>("latitude"); + return result; +} + +string TgTypeParser::parseLocation(const Location::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "longitude", object->longitude); + appendToJson(result, "latitude", object->latitude); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +Update::Ptr TgTypeParser::parseJsonAndGetUpdate(const ptree& data) const { + Update::Ptr result(new Update); + result->updateId = data.get<int32_t>("update_id"); + result->message = parseJsonAndGetMessage(data.find("message")->second); + return result; +} + +string TgTypeParser::parseUpdate(const Update::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "update_id", object->updateId); + appendToJson(result, "message", parseMessage(object->message)); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +UserProfilePhotos::Ptr TgTypeParser::parseJsonAndGetUserProfilePhotos(const ptree& data) const { + UserProfilePhotos::Ptr result(new UserProfilePhotos); + result->totalCount = data.get<int32_t>("total_count"); + result->photos = parseJsonAndGet2DArray<PhotoSize>(parseJsonAndGetPhotoSize, data, "photos"); + return result; +} + +string TgTypeParser::parseUserProfilePhotos(const UserProfilePhotos::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "total_count", object->totalCount); + appendToJson(result, "photos", parse2DArray(parsePhotoSize, object->photos)); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +ReplyKeyboardMarkup::Ptr TgTypeParser::parseJsonAndGetReplyKeyboardMarkup(const boost::property_tree::ptree& data) const { + ReplyKeyboardMarkup::Ptr result(new ReplyKeyboardMarkup); + for (const pair<const string, ptree>& item : data.find("keyboard")->second) { + vector<string> array; + for (const pair<const string, ptree>& innerItem : item.second) { + array.push_back(innerItem.second.data()); + } + result->keyboard.push_back(array); + } + result->resizeKeyboard = data.get<bool>("resize_keyboard"); + result->oneTimeKeyboard = data.get<bool>("one_time_keyboard"); + result->selective = data.get<bool>("selective"); + return result; +} + +std::string TgTypeParser::parseReplyKeyboardMarkup(const ReplyKeyboardMarkup::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + result += "\"keyboard\":["; + for (vector<string>& item : object->keyboard) { + result += '['; + for (string& innerItem : item) { + result += '"'; + result += innerItem; + result += "\","; + } + result.erase(result.length() - 1); + result += "],"; + } + result.erase(result.length() - 1); + result += "],"; + appendToJson(result, "resize_keyboard", object->resizeKeyboard); + appendToJson(result, "one_time_keyboard", object->oneTimeKeyboard); + appendToJson(result, "selective", object->selective); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +ReplyKeyboardHide::Ptr TgTypeParser::parseJsonAndGetReplyKeyboardHide(const boost::property_tree::ptree& data) const { + ReplyKeyboardHide::Ptr result(new ReplyKeyboardHide); + result->selective = data.get<bool>("selective"); + return result; +} + +std::string TgTypeParser::parseReplyKeyboardHide(const ReplyKeyboardHide::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "selective", object->selective); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +ForceReply::Ptr TgTypeParser::parseJsonAndGetForceReply(const boost::property_tree::ptree& data) const { + ForceReply::Ptr result(new ForceReply); + result->selective = data.get<bool>("selective"); + return result; +} + +std::string TgTypeParser::parseForceReply(const ForceReply::Ptr& object) const { + if (!object) { + return ""; + } + string result; + result += '{'; + appendToJson(result, "selective", object->selective); + result.erase(result.length() - 1); + result += '}'; + return result; +} + +GenericChat::Ptr TgTypeParser::parseJsonAndGetGenericChat(const ptree& data) const { + if (data.find("first_name") == data.not_found()) { + return static_pointer_cast<GenericChat>(parseJsonAndGetGroupChat(data)); + } else { + return static_pointer_cast<GenericChat>(parseJsonAndGetUser(data)); + } +} + +string TgTypeParser::parseGenericChat(const GenericChat::Ptr& object) const { + if (!object) { + return ""; + } + if (dynamic_pointer_cast<User>(object) == nullptr) { + return parseGroupChat(static_pointer_cast<GroupChat>(object)); + } else { + return parseUser(static_pointer_cast<User>(object)); + } +} + +GenericReply::Ptr TgTypeParser::parseJsonAndGetGenericReply(const boost::property_tree::ptree& data) const { + if (data.find("force_reply") != data.not_found()) { + return static_pointer_cast<GenericReply>(parseJsonAndGetForceReply(data)); + } else if (data.find("hide_keyboard") != data.not_found()) { + return static_pointer_cast<GenericReply>(parseJsonAndGetReplyKeyboardHide(data)); + } else { + return static_pointer_cast<GenericReply>(parseJsonAndGetReplyKeyboardMarkup(data)); + } +} + +std::string TgTypeParser::parseGenericReply(const GenericReply::Ptr& object) const { + if (!object) { + return ""; + } + if (dynamic_pointer_cast<ForceReply>(object) != nullptr) { + return parseForceReply(static_pointer_cast<ForceReply>(object)); + } else if (dynamic_pointer_cast<ReplyKeyboardHide>(object) != nullptr) { + return parseReplyKeyboardHide(static_pointer_cast<ReplyKeyboardHide>(object)); + } else { + return parseReplyKeyboardMarkup(static_pointer_cast<ReplyKeyboardMarkup>(object)); + } +} + +void TgTypeParser::appendToJson(string& json, const string& varName, const string& value) const { + if (value.empty()) { + return; + } + json += '"'; + json += varName; + json += "\":"; + if (value.front() != '{') { + json += '"'; + } + json += value; + if (value.back() != '}') { + json += '"'; + } + json += ','; +} + +} diff --git a/src/tools/net/HttpClient.cpp b/src/tools/net/HttpClient.cpp new file mode 100644 index 0000000..b9813ad --- /dev/null +++ b/src/tools/net/HttpClient.cpp @@ -0,0 +1,67 @@ +/* + * 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/net/HttpClient.h" + +#include <boost/asio/ssl.hpp> + +using namespace std; +using namespace boost::asio; +using namespace boost::asio::ip; +using namespace boost::asio::local; + +namespace TgBot { + +HttpClient& HttpClient::getInstance() { + static HttpClient result; + return result; +} + +string HttpClient::makeRequest(const Url& url, const vector<HttpReqArg>& args) { + ssl::context context(ssl::context::sslv23); + context.set_default_verify_paths(); + + ssl::stream<tcp::socket> 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<tcp::socket>::client); + + string requestText = HttpParser::getInstance().generateRequest(url, args, false); + write(socket, buffer(requestText.c_str(), requestText.length())); + + string response; + char buff[1024]; + boost::system::error_code error; + while (!error) { + size_t bytes = read(socket, buffer(buff), error); + response += string(buff, bytes); + } + + return HttpParser::getInstance().parseResponse(response); +} + +} diff --git a/src/tools/net/HttpParser.cpp b/src/tools/net/HttpParser.cpp new file mode 100644 index 0000000..a2d9108 --- /dev/null +++ b/src/tools/net/HttpParser.cpp @@ -0,0 +1,204 @@ +/* + * 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/net/HttpParser.h" + +#include <boost/algorithm/string.hpp> + +#include "tgbot/tools/StringTools.h" + +using namespace std; +using namespace boost; + +namespace TgBot { + +HttpParser& HttpParser::getInstance() { + static HttpParser result; + return result; +} + +string HttpParser::generateRequest(const Url& url, const vector<HttpReqArg>& args, bool isKeepAlive) { + string result; + if (args.empty()) { + result += "GET "; + } else { + result += "POST "; + } + result += url.path; + result += url.query.empty() ? "" : "?" + url.query; + result += " HTTP/1.1\r\n"; + result += "Host: "; + result += url.host; + result += "\r\nConnection: "; + if (isKeepAlive) { + result += "keep-alive"; + } else { + result += "close"; + } + result += "\r\n"; + if (args.empty()) { + result += "\r\n"; + } else { + string requestData; + + string bondary = generateMultipartBoundary(args); + if (bondary.empty()) { + result += "Content-Type: application/x-www-form-urlencoded\r\n"; + requestData = generateWwwFormUrlencoded(args); + } else { + result += "Content-Type: multipart/form-data; boundary="; + result += bondary; + result += "\r\n"; + requestData = generateMultipartFormData(args, bondary); + } + + result += "Content-Length: "; + result += lexical_cast<string>(requestData.length()); + result += "\r\n\r\n"; + result += requestData; + } + return result; +} + +string HttpParser::generateMultipartFormData(const vector<HttpReqArg>& args, const string& bondary) { + string result; + for (const HttpReqArg& item : args) { + result += "--"; + result += bondary; + result += "\r\nContent-Disposition: form-data; name=\""; + result += item.name; + result += "\"\r\n"; + if (item.isFile) { + result += "Content-Type: "; + result += item.mimeType; + result += "\r\n"; + } + result += "\r\n"; + result += item.value; + result += "\r\n"; + } + return result; +} + +string HttpParser::generateMultipartBoundary(const vector<HttpReqArg>& args) { + string result; + srand((unsigned int) time(nullptr)); + for (const HttpReqArg& item : args) { + if (item.isFile) { + while (result.empty() || item.value.find(result) != item.value.npos) { + result += StringTools::generateRandomString(4); + } + } + } + return result; +} + +string HttpParser::generateWwwFormUrlencoded(const vector<HttpReqArg>& args) { + string result; + + bool firstRun = true; + for (const HttpReqArg& item : args) { + if (firstRun) { + firstRun = false; + } else { + result += '&'; + } + result += StringTools::urlEncode(item.name); + result += '='; + result += StringTools::urlEncode(item.value); + } + + return result; +} + +string HttpParser::generateResponse(const string& data, const string& mimeType, unsigned short statusCode, const string& statusStr, bool isKeepAlive) { + string result; + result += "HTTP/1.1 "; + result += lexical_cast<string>(statusCode); + result += ' '; + result += statusStr; + result += "\r\nContent-Type: "; + result += mimeType; + result += "\r\nContent-Length: "; + result += lexical_cast<string>(data.length()); + result += "\r\n\r\n"; + result += data; + return result; +} + +string HttpParser::parseHttp(bool isRequest, const string& data, map<string, string>& headers) { + bool onlyNewLineChar = false; + size_t headerEnd = data.find("\r\n\r\n"); + if (headerEnd == data.npos) { + headerEnd = data.find("\n\n"); + if (headerEnd != data.npos) { + onlyNewLineChar = true; + headerEnd += 2; + } + } else { + headerEnd += 4; + } + + size_t lineStart = 0; + size_t lineEnd = 0; + size_t lineSepPos = 0; + size_t lastLineEnd = data.npos; + while (lastLineEnd != lineEnd) { + lastLineEnd = lineEnd; + if (lineEnd == 0) { + if (isRequest) { + lineSepPos = data.find(' '); + lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n"); + headers["method"] = data.substr(0, lineSepPos); + headers["path"] = data.substr(lineSepPos + 1, data.find(' ', lineSepPos + 1) - lineSepPos - 1); + } else { + lineSepPos = data.find(' '); + lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n"); + headers["status"] = data.substr(lineSepPos + 1, data.find(' ', lineSepPos + 1) - lineSepPos - 1); + } + } else { + lineStart = lineEnd; + lineStart += onlyNewLineChar ? 1 : 2; + lineEnd = data.find(onlyNewLineChar ? "\n" : "\r\n", lineStart); + lineSepPos = data.find(':', lineStart); + if (lineEnd >= headerEnd || lastLineEnd == lineEnd || lineSepPos >= headerEnd) { + break; + } + headers[to_lower_copy(data.substr(lineStart, lineSepPos - lineStart))] = trim_copy(data.substr(lineSepPos + 1, lineEnd - lineSepPos - 1)); + } + } + + return headerEnd == data.npos ? "" : data.substr(headerEnd); +} + +string HttpParser::parseHttp(bool isRequest, const string& data) { + size_t headerEnd = data.find("\r\n\r\n"); + if (headerEnd == data.npos) { + headerEnd = data.find("\n\n"); + } + if (headerEnd == data.npos) { + headerEnd = 0; + } + return data.substr(headerEnd); +} + +} diff --git a/test/utils.cpp b/src/tools/net/TgLongPoll.cpp index 9a9f48c7..91bf058 100644 --- a/test/utils.cpp +++ b/src/tools/net/TgLongPoll.cpp @@ -20,48 +20,24 @@ * SOFTWARE. */ -#include "utils.h" +#include "tgbot/net/TgLongPoll.h" -#include <sstream> +namespace TgBot { -using namespace std; -using namespace boost; - -string diff(const string& test, const string& expected) { - string result; - result += "\n*** BEGIN ***\n"; +TgLongPoll::TgLongPoll(const Api* api, const EventHandler* eventHandler) : _api(api), _eventHandler(eventHandler) { +} - istringstream ss1(test); - istringstream ss2(expected); +TgLongPoll::TgLongPoll(const Bot& bot) : TgLongPoll(&bot.getApi(), &bot.getEventHandler()) { +} - string s1, s2; - bool r1, r2; - size_t i = 0; - do { - r1 = getline(ss1, s1) ? true : false; - r2 = getline(ss2, s2) ? true : false; - if (r1 && r2 && s1 == s2) { - result += lexical_cast<string>(i); - result += " [=] "; - result += s1; - result += "\n"; - } else { - if (r1) { - result += lexical_cast<string>(i); - result += " [t] "; - result += s1; - result += "\n"; - } - if (r2) { - result += lexical_cast<string>(i); - result += " [e] "; - result += s2; - result += "\n"; - } - } - ++i; - } while (r1 || r2); +void TgLongPoll::start() { + std::vector<Update::Ptr> updates = _api->getUpdates(_lastUpdateId, 100, 60); + for (Update::Ptr& item : updates) { + if (item->updateId >= _lastUpdateId) { + _lastUpdateId = item->updateId + 1; + } + _eventHandler->handleUpdate(item); + } +} - result += "*** END ***\n"; - return result; } diff --git a/src/tools/net/Url.cpp b/src/tools/net/Url.cpp new file mode 100644 index 0000000..44d9089 --- /dev/null +++ b/src/tools/net/Url.cpp @@ -0,0 +1,85 @@ +/* + * 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/net/Url.h" + +#include "tgbot/tools/StringTools.h" + +using namespace std; + +namespace TgBot { + +Url::Url(const string& url) { + bool isProtocolParsed = false; + bool isHostParsed = false; + bool isPathParsed = false; + bool isQueryParsed = false; + + for (size_t i = 0, count = url.length(); i < count; ++i) { + char c = url[i]; + + if (!isProtocolParsed) { + if (c == ':') { + isProtocolParsed = true; + i += 2; + } else { + protocol += c; + } + } else if (!isHostParsed) { + if (c == '/') { + isHostParsed = true; + path += '/'; + } else if (c == '?') { + isHostParsed = isPathParsed = true; + path += '/'; + } else if (c == '#') { + isHostParsed = isPathParsed = isQueryParsed = true; + path += '/'; + } else { + host += c; + } + } else if (!isPathParsed) { + if (c == '?') { + isPathParsed = true; + } else if (c == '#') { + isPathParsed = isQueryParsed = true; + } else { + path += c; + } + } else if (!isQueryParsed) { + if (c == '#') { + isQueryParsed = true; + } else { + query += c; + } + } else { + fragment += c; + } + } + + host = StringTools::urlEncode(host, "."); + path = StringTools::urlEncode(path, "/"); + query = StringTools::urlEncode(query, "&"); + fragment = StringTools::urlEncode(fragment); +} + +} diff --git a/src/tools/tools/StringTools.cpp b/src/tools/tools/StringTools.cpp new file mode 100644 index 0000000..0a6dbc3 --- /dev/null +++ b/src/tools/tools/StringTools.cpp @@ -0,0 +1,119 @@ +/* + * 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/tools/StringTools.h" + +#include <stdlib.h> +#include <iomanip> +#include <stdio.h> + +using namespace std; + +namespace StringTools { + +bool startsWith(const string& str1, const string& str2) { + if (str1.length() < str2.length()) { + return false; + } + string::const_iterator it1(str1.begin()); + string::const_iterator end1(str1.end()); + string::const_iterator it2(str2.begin()); + string::const_iterator end2(str2.end()); + while (it1 != end1 && it2 != end2) { + if (*it1 != *it2) { + return false; + } + ++it1; + ++it2; + } + return true; +} + +bool endsWith(const string& str1, const string& str2) { + if (str1.length() < str2.length()) { + return false; + } + string::const_iterator it1(str1.end()); + string::const_iterator begin1(str1.begin()); + string::const_iterator it2(str2.end()); + string::const_iterator begin2(str2.begin()); + while (it1 != begin1 && it2 != begin2) { + if (*it1 != *it2) { + return false; + } + --it1; + --it2; + } + return true; +} + +void split(const string& str, char delimiter, vector<string>& dest) { + istringstream stream(str); + string s; + while (getline(stream, s, delimiter)) { + dest.push_back(s); + } +} + +string generateRandomString(size_t length) { + static const string chars("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890-=[]\\;',./!@#$%^&*()_+{}|:\"<>?`~"); + static const size_t charsLen = chars.length(); + string result; + for (int i = 0; i < length; ++i) { + result += chars[rand() % charsLen]; + } + return result; +} + +string urlEncode(const string& value, const std::string additionalLegitChars) { + static const string legitPunctuation = "-_.~"; + ostringstream result; + result.fill('0'); + result << hex; + for (const char& c : value) { + if (isalnum(c) || legitPunctuation.find(c) != legitPunctuation.npos || additionalLegitChars.find(c) != additionalLegitChars.npos) { + result << c; + } else { + result << '%' << setw(2) << int((unsigned char) c); + } + } + + return result.str(); +} + +string urlDecode(const string& value) { + string result; + for (size_t i = 0, count = value.length(); i < count; ++i) { + const char c = value[i]; + if (c == '%') { + int t = 0; + sscanf(value.substr(i + 1, 2).c_str(), "%x", &t); + result += (char) t; + i += 2; + } else { + result += c; + } + } + return result; +} + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4139418..65781e6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,12 +1,11 @@ set(TEST_SRC_LIST - main.cpp - utils.cpp - tgbot/net/Url.cpp - tgbot/net/HttpParser.cpp - tgbot/tools/StringTools.cpp + main.cpp + tgbot/net/Url.cpp + tgbot/net/HttpParser.cpp + tgbot/tools/StringTools.cpp ) include_directories("${PROJECT_SOURCE_DIR}/test") add_executable(${PROJECT_NAME}_test ${TEST_SRC_LIST}) target_link_libraries(${PROJECT_NAME}_test TgBot) -add_test(${PROJECT_NAME}_test ${PROJECT_NAME}_test)
\ No newline at end of file +add_test(${PROJECT_NAME}_test ${PROJECT_NAME}_test) diff --git a/test/tgbot/net/HttpParser.cpp b/test/tgbot/net/HttpParser.cpp index 57ba187..362a13f 100644 --- a/test/tgbot/net/HttpParser.cpp +++ b/test/tgbot/net/HttpParser.cpp @@ -32,105 +32,105 @@ using namespace TgBot; BOOST_AUTO_TEST_SUITE(tHttpParser) BOOST_AUTO_TEST_CASE(generateRequest) { - vector<HttpReqArg> args = { HttpReqArg("email", "test@example.com"), HttpReqArg("text", "Hello, world!") }; - string t = HttpParser::getInstance().generateRequest(Url("http://example.com/index.html"), args, true); - string e = "" - "POST /index.html HTTP/1.1\r\n" - "Host: example.com\r\n" - "Connection: keep-alive\r\n" - "Content-Type: application/x-www-form-urlencoded\r\n" - "Content-Length: 49\r\n" - "\r\n" - "email=test%40example.com&text=Hello%2c%20world%21"; - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + vector<HttpReqArg> args = { HttpReqArg("email", "test@example.com"), HttpReqArg("text", "Hello, world!") }; + string t = HttpParser::getInstance().generateRequest(Url("http://example.com/index.html"), args, true); + string e = "" + "POST /index.html HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: keep-alive\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "Content-Length: 49\r\n" + "\r\n" + "email=test%40example.com&text=Hello%2c%20world%21"; + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); } BOOST_AUTO_TEST_CASE(generateMultipartFormData) { - vector<HttpReqArg> args = { HttpReqArg("email", "test@example.com"), HttpReqArg("text", "Hello, world!", true) }; - string boundary = HttpParser::getInstance().generateMultipartBoundary(args); - string t = HttpParser::getInstance().generateMultipartFormData(args, boundary); - string e = "" - "--" + boundary + "\r\n" - "Content-Disposition: form-data; name=\"email\"\r\n" - "\r\n" - "test@example.com\r\n" - "--" + boundary + "\r\n" - "Content-Disposition: form-data; name=\"text\"\r\n" - "Content-Type: text/plain\r\n" - "\r\n" - "Hello, world!\r\n"; - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + vector<HttpReqArg> args = { HttpReqArg("email", "test@example.com"), HttpReqArg("text", "Hello, world!", true) }; + string boundary = HttpParser::getInstance().generateMultipartBoundary(args); + string t = HttpParser::getInstance().generateMultipartFormData(args, boundary); + string e = "" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"email\"\r\n" + "\r\n" + "test@example.com\r\n" + "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"text\"\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "Hello, world!\r\n"; + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); } BOOST_AUTO_TEST_CASE(generateWwwFormUrlencoded) { - vector<HttpReqArg> args = { HttpReqArg("email", "test@example.com"), HttpReqArg("text", "Hello, world!") }; - string t = HttpParser::getInstance().generateWwwFormUrlencoded(args); - string e = "email=test%40example.com&text=Hello%2c%20world%21"; - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + vector<HttpReqArg> args = { HttpReqArg("email", "test@example.com"), HttpReqArg("text", "Hello, world!") }; + string t = HttpParser::getInstance().generateWwwFormUrlencoded(args); + string e = "email=test%40example.com&text=Hello%2c%20world%21"; + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); } BOOST_AUTO_TEST_CASE(generateResponse) { - string t = HttpParser::getInstance().generateResponse("testdata"); - string e = "" - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: 8\r\n" - "\r\n" - "testdata"; - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + string t = HttpParser::getInstance().generateResponse("testdata"); + string e = "" + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 8\r\n" + "\r\n" + "testdata"; + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); } BOOST_AUTO_TEST_CASE(parseRequest) { - string data = "" - "POST /index.html HTTP/1.1\r\n" - "Host: example.com\r\n" - "Connection: keep-alive\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: 8\r\n" - "\r\n" - "testdata"; - - map<string, string> tHeaders; - string t = HttpParser::getInstance().parseRequest(data, tHeaders); - - map<string, string> eHeaders = { - { "method", "POST" }, - { "path", "/index.html" }, - { "host", "example.com" }, - { "connection", "keep-alive" }, - { "content-type", "text/plain" }, - { "content-length", "8" } - }; - string e = "testdata"; - - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); - BOOST_CHECK_MESSAGE(tHeaders == eHeaders, diff(tHeaders, eHeaders, [](const pair<const string, string>& item) -> string { - return item.first + '=' + item.second; - })); + string data = "" + "POST /index.html HTTP/1.1\r\n" + "Host: example.com\r\n" + "Connection: keep-alive\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 8\r\n" + "\r\n" + "testdata"; + + map<string, string> tHeaders; + string t = HttpParser::getInstance().parseRequest(data, tHeaders); + + map<string, string> eHeaders = { + { "method", "POST" }, + { "path", "/index.html" }, + { "host", "example.com" }, + { "connection", "keep-alive" }, + { "content-type", "text/plain" }, + { "content-length", "8" } + }; + string e = "testdata"; + + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + BOOST_CHECK_MESSAGE(tHeaders == eHeaders, diff(tHeaders, eHeaders, [](const pair<const string, string>& item) -> string { + return item.first + '=' + item.second; + })); } BOOST_AUTO_TEST_CASE(parseResponse) { - string data = "" - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: 8\r\n" - "\r\n" - "testdata"; - - map<string, string> tHeaders; - string t = HttpParser::getInstance().parseResponse(data, tHeaders); - - map<string, string> eHeaders = { - { "status", "200" }, - { "content-type", "text/plain" }, - { "content-length", "8" } - }; - string e = "testdata"; - - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); - BOOST_CHECK_MESSAGE(tHeaders == eHeaders, diff(tHeaders, eHeaders, [](const pair<const string, string>& item) -> string { - return item.first + '=' + item.second; - })); + string data = "" + "HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 8\r\n" + "\r\n" + "testdata"; + + map<string, string> tHeaders; + string t = HttpParser::getInstance().parseResponse(data, tHeaders); + + map<string, string> eHeaders = { + { "status", "200" }, + { "content-type", "text/plain" }, + { "content-length", "8" } + }; + string e = "testdata"; + + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + BOOST_CHECK_MESSAGE(tHeaders == eHeaders, diff(tHeaders, eHeaders, [](const pair<const string, string>& item) -> string { + return item.first + '=' + item.second; + })); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/tgbot/net/Url.cpp b/test/tgbot/net/Url.cpp index 0974ab6..abd90cb 100644 --- a/test/tgbot/net/Url.cpp +++ b/test/tgbot/net/Url.cpp @@ -29,30 +29,30 @@ using namespace TgBot; BOOST_AUTO_TEST_SUITE(tUrl) BOOST_AUTO_TEST_CASE(parsingUrlNoPath) { - Url t("https://test.example.com?test=123&123=test#title"); - BOOST_CHECK_EQUAL(t.protocol, "https"); - BOOST_CHECK_EQUAL(t.host, "test.example.com"); - BOOST_CHECK_EQUAL(t.path, "/"); - BOOST_CHECK_EQUAL(t.query, "test=123&123=test"); - BOOST_CHECK_EQUAL(t.fragment, "title"); + Url t("https://test.example.com?test=123&123=test#title"); + BOOST_CHECK_EQUAL(t.protocol, "https"); + BOOST_CHECK_EQUAL(t.host, "test.example.com"); + BOOST_CHECK_EQUAL(t.path, "/"); + BOOST_CHECK_EQUAL(t.query, "test=123&123=test"); + BOOST_CHECK_EQUAL(t.fragment, "title"); } BOOST_AUTO_TEST_CASE(parsingUrlNoPathAndQuery) { - Url t("https://test.example.com#title"); - BOOST_CHECK_EQUAL(t.protocol, "https"); - BOOST_CHECK_EQUAL(t.host, "test.example.com"); - BOOST_CHECK_EQUAL(t.path, "/"); - BOOST_CHECK_EQUAL(t.query, ""); - BOOST_CHECK_EQUAL(t.fragment, "title"); + Url t("https://test.example.com#title"); + BOOST_CHECK_EQUAL(t.protocol, "https"); + BOOST_CHECK_EQUAL(t.host, "test.example.com"); + BOOST_CHECK_EQUAL(t.path, "/"); + BOOST_CHECK_EQUAL(t.query, ""); + BOOST_CHECK_EQUAL(t.fragment, "title"); } BOOST_AUTO_TEST_CASE(parsingUrlFull) { - Url t("https://test.example.com/example-page/index.html?test=123&123=test#title"); - BOOST_CHECK_EQUAL(t.protocol, "https"); - BOOST_CHECK_EQUAL(t.host, "test.example.com"); - BOOST_CHECK_EQUAL(t.path, "/example-page/index.html"); - BOOST_CHECK_EQUAL(t.query, "test=123&123=test"); - BOOST_CHECK_EQUAL(t.fragment, "title"); + Url t("https://test.example.com/example-page/index.html?test=123&123=test#title"); + BOOST_CHECK_EQUAL(t.protocol, "https"); + BOOST_CHECK_EQUAL(t.host, "test.example.com"); + BOOST_CHECK_EQUAL(t.path, "/example-page/index.html"); + BOOST_CHECK_EQUAL(t.query, "test=123&123=test"); + BOOST_CHECK_EQUAL(t.fragment, "title"); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/tgbot/tools/StringTools.cpp b/test/tgbot/tools/StringTools.cpp index c3bf709..11f85af 100644 --- a/test/tgbot/tools/StringTools.cpp +++ b/test/tgbot/tools/StringTools.cpp @@ -35,29 +35,29 @@ using namespace std; BOOST_AUTO_TEST_SUITE(tStringTools) BOOST_AUTO_TEST_CASE(startsWith) { - BOOST_CHECK(StringTools::startsWith("abc123", "abc")); - BOOST_CHECK(StringTools::startsWith("abc", "abc123")); + BOOST_CHECK(StringTools::startsWith("abc123", "abc")); + BOOST_CHECK(StringTools::startsWith("abc", "abc123")); } BOOST_AUTO_TEST_CASE(endsWith) { - BOOST_CHECK(StringTools::endsWith("abc123", "123")); - BOOST_CHECK(StringTools::endsWith("123", "abc123")); + BOOST_CHECK(StringTools::endsWith("abc123", "123")); + BOOST_CHECK(StringTools::endsWith("123", "abc123")); } BOOST_AUTO_TEST_CASE(split) { - BOOST_CHECK(StringTools::split("123 456 789", ' ') == vector<string>({"123", "456", "789"})); + BOOST_CHECK(StringTools::split("123 456 789", ' ') == vector<string>({"123", "456", "789"})); } BOOST_AUTO_TEST_CASE(urlEncode) { - string t = StringTools::urlEncode("`1234567890-qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:ZXCVBNM<>? "); - string e = "%601234567890-qwertyuiop%5b%5d%5casdfghjkl%3b%27zxcvbnm%2c.%2f~%21%40%23%24%25%5e%26%2a%28%29_%2bQWERTYUIOP%7b%7d%7cASDFGHJKL%3aZXCVBNM%3c%3e%3f%20"; - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + string t = StringTools::urlEncode("`1234567890-qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:ZXCVBNM<>? "); + string e = "%601234567890-qwertyuiop%5b%5d%5casdfghjkl%3b%27zxcvbnm%2c.%2f~%21%40%23%24%25%5e%26%2a%28%29_%2bQWERTYUIOP%7b%7d%7cASDFGHJKL%3aZXCVBNM%3c%3e%3f%20"; + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); } BOOST_AUTO_TEST_CASE(urlDecode) { - string t = StringTools::urlDecode("%601234567890-qwertyuiop%5b%5d%5casdfghjkl%3b%27zxcvbnm%2c.%2f~%21%40%23%24%25%5e%26%2a%28%29_%2bQWERTYUIOP%7b%7d%7cASDFGHJKL%3aZXCVBNM%3c%3e%3f%20"); - string e = "`1234567890-qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:ZXCVBNM<>? "; - BOOST_CHECK_MESSAGE(t == e, diff(t, e)); + string t = StringTools::urlDecode("%601234567890-qwertyuiop%5b%5d%5casdfghjkl%3b%27zxcvbnm%2c.%2f~%21%40%23%24%25%5e%26%2a%28%29_%2bQWERTYUIOP%7b%7d%7cASDFGHJKL%3aZXCVBNM%3c%3e%3f%20"); + string e = "`1234567890-qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:ZXCVBNM<>? "; + BOOST_CHECK_MESSAGE(t == e, diff(t, e)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/test/utils.h b/test/utils.h index d8e64e3..485a40e 100644 --- a/test/utils.h +++ b/test/utils.h @@ -24,60 +24,81 @@ #define TGBOT_UTILS_H #include <string> +#include <vector> +#include <sstream> #include <boost/lexical_cast.hpp> -std::string diff(const std::string& test, const std::string& expected); +std::string diff(const std::string& test, const std::string& expected) { + std::vector<std::string> v1, v2; + std::istringstream ss1(test); + std::istringstream ss2(expected); + std::string s1, s2; + bool r1, r2; + do { + r1 = std::getline(ss1, s1) ? true : false; + r2 = std::getline(ss2, s2) ? true : false; + if (r1) { + v1.push_back(s1); + } + if (r2) { + v2.push_back(s2); + } + } while (r1 || r2); + diff(v1, v2, [](const std::string& item) -> std::string { + return std::string(item); + }); +} template<typename T> std::string diff(const T& test, const T& expected, std::string (*toStringFunc)(const typename T::value_type&)) { - std::string result; - result += "\n*** BEGIN *** Count: t="; - result += boost::lexical_cast<std::string>(test.size()); - result += " e="; - result += boost::lexical_cast<std::string>(expected.size()); - result += '\n'; + std::string result; + result += "\n*** BEGIN *** Count: t="; + result += boost::lexical_cast<std::string>(test.size()); + result += " e="; + result += boost::lexical_cast<std::string>(expected.size()); + result += '\n'; - typename T::const_iterator iter1 = test.begin(); - typename T::const_iterator end1 = test.end(); - typename T::const_iterator iter2 = expected.begin(); - typename T::const_iterator end2 = expected.end(); - bool r1, r2; - std::string s1, s2; - size_t i = 0; - do { - r1 = iter1 != end1; - r2 = iter2 != end2; - if (r1) { - s1 = toStringFunc(*iter1++); - } - if (r2) { - s2 = toStringFunc(*iter2++); - } - if (r1 && r2 && s1 == s2) { - result += boost::lexical_cast<std::string>(i); - result += " [=] "; - result += s1; - result += "\n"; - } else { - if (r1) { - result += boost::lexical_cast<std::string>(i); - result += " [t] "; - result += s1; - result += "\n"; - } - if (r2) { - result += boost::lexical_cast<std::string>(i); - result += " [e] "; - result += s2; - result += "\n"; - } - } - ++i; - } while (r1 || r2); + typename T::const_iterator iter1 = test.begin(); + typename T::const_iterator end1 = test.end(); + typename T::const_iterator iter2 = expected.begin(); + typename T::const_iterator end2 = expected.end(); + bool r1, r2; + std::string s1, s2; + size_t i = 0; + do { + r1 = iter1 != end1; + r2 = iter2 != end2; + if (r1) { + s1 = toStringFunc(*iter1++); + } + if (r2) { + s2 = toStringFunc(*iter2++); + } + if (r1 && r2 && s1 == s2) { + result += boost::lexical_cast<std::string>(i); + result += " [=] "; + result += s1; + result += "\n"; + } else { + if (r1) { + result += boost::lexical_cast<std::string>(i); + result += " [t] "; + result += s1; + result += "\n"; + } + if (r2) { + result += boost::lexical_cast<std::string>(i); + result += " [e] "; + result += s2; + result += "\n"; + } + } + ++i; + } while (r1 || r2); - result += "*** END ***\n"; - return result; + result += "*** END ***\n"; + return result; } #endif //TGBOT_UTILS_H |