From 9da2b2966ece0172caeb5e7d3d60ed0c08a7b3c0 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Wed, 9 Nov 2016 20:28:06 +0300 Subject: [PATCH] getUpdates() implemented. --- include/telebotxx/BotApi.hpp | 8 ++++ include/telebotxx/Update.hpp | 74 ++++++++++++++++++++++++++++++++ src/BotApi.cpp | 48 +++++++++++++++++++++ src/CMakeLists.txt | 1 + src/JsonObjects.cpp | 32 ++++++++++++++ src/JsonObjects.hpp | 8 ++++ src/Update.cpp | 82 ++++++++++++++++++++++++++++++++++++ tests/TestApi.cpp | 20 +++++++++ 8 files changed, 273 insertions(+) create mode 100644 include/telebotxx/Update.hpp create mode 100644 src/Update.cpp diff --git a/include/telebotxx/BotApi.hpp b/include/telebotxx/BotApi.hpp index 9e9a627..17338cb 100644 --- a/include/telebotxx/BotApi.hpp +++ b/include/telebotxx/BotApi.hpp @@ -3,6 +3,7 @@ #include "User.hpp" #include "Message.hpp" +#include "Update.hpp" #include #include @@ -49,6 +50,13 @@ namespace telebotxx /// \return Message object, recieved from the server Message sendPhotoUrl(const std::string& chat, const std::string& url, const std::string& caption = ""); + /// \brief Get updates using long polling + /// \param offset identifier of the first update to be returned + /// \param limit maximum number of updates to be retrieved + /// \param timeout timeout in seconds for long polling + /// \return Updates (vector of Update) + Updates getUpdates(int offset = 0, unsigned short limit = 0, unsigned timeout = 0); + private: class Impl; std::unique_ptr impl_; diff --git a/include/telebotxx/Update.hpp b/include/telebotxx/Update.hpp new file mode 100644 index 0000000..f1b7d9d --- /dev/null +++ b/include/telebotxx/Update.hpp @@ -0,0 +1,74 @@ +#ifndef TELEBOTXX_UPDATE_HPP +#define TELEBOTXX_UPDATE_HPP + +#include "Message.hpp" + +#include +#include + +namespace telebotxx +{ + class Update + { + public: + enum class Type + { + Message, + EditedMessage, + InlineQuery, + ChosenInlineResult, + CallbackQuery + }; + + Update(int id, Type type); + Update(const Update&); + Update(Update&&); + virtual ~Update() = 0; + + int getId() const; + Type getType() const; + + void swap(Update& other) noexcept; + + private: + int id_; + Type updateType_; + }; + + using UpdatePtr = std::shared_ptr; + using Updates = std::vector; + + class MessageUpdate : public Update + { + public: + MessageUpdate(int id, const Message& message); + MessageUpdate(const MessageUpdate&); + MessageUpdate(MessageUpdate&&); + ~MessageUpdate(); + + const Message& getMessage() const; + + void swap(MessageUpdate& other) noexcept; + + const MessageUpdate& operator=(MessageUpdate other); + + private: + Message message_; + }; + + class EditedMessageUpdate: public MessageUpdate + { + public: + EditedMessageUpdate(int id, const Message& message); + EditedMessageUpdate(const EditedMessageUpdate&); + EditedMessageUpdate(EditedMessageUpdate&&); + ~EditedMessageUpdate(); + + void swap(EditedMessageUpdate& other) noexcept; + + const EditedMessageUpdate& operator=(EditedMessageUpdate other); + }; + +} + +#endif // TELEBOTXX_UPDATE_HPP diff --git a/src/BotApi.cpp b/src/BotApi.cpp index f6d4f8e..15ad3fe 100644 --- a/src/BotApi.cpp +++ b/src/BotApi.cpp @@ -131,6 +131,49 @@ public: return *parseMessage(doc, "result", REQUIRED); } + Updates getUpdates(int offset, unsigned short limit, unsigned timeout) + { + // Construct JSON body + using namespace rapidjson; + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + if (offset != 0) + { + writer.String("offset"); + writer.Int(offset); + } + if (limit != 0) + { + writer.String("limit"); + writer.Uint(limit); + } + if (timeout != 0) + { + writer.String("timeout"); + writer.Uint(timeout); + } + writer.EndObject(); + + std::string request = s.GetString(); + + auto r = cpr::Post(cpr::Url{telegramMainUrl_ + "/getUpdates"}, + cpr::Header{{"Content-Type", "application/json"}}, + cpr::Body{request} + ); + auto& response = r.text; + + if (debugMode) + std::cout << "Response: " << response << std::endl; + + rapidjson::Document doc; + doc.Parse(response.c_str()); + + checkResponse(doc); + return *parseUpdates(doc, "result", REQUIRED); + } + private: std::string token_; @@ -164,3 +207,8 @@ Message BotApi::sendPhotoUrl(const std::string& chat, const std::string& url, co { return impl_->sendPhotoUrl(chat, url, caption); } + +Updates BotApi::getUpdates(int offset, unsigned short limit, unsigned int timeout) +{ + return impl_->getUpdates(offset, limit, timeout); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dcac6af..a211430 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ set(SOURCE_FILES Attachment.cpp JsonObjects.cpp Logging.cpp Message.cpp + Update.cpp User.cpp ) diff --git a/src/JsonObjects.cpp b/src/JsonObjects.cpp index a73ddfe..645cd24 100644 --- a/src/JsonObjects.cpp +++ b/src/JsonObjects.cpp @@ -249,6 +249,38 @@ namespace telebotxx return nullptr; } + std::unique_ptr parseUpdate(const rapidjson::Value& obj) + { + int id = parse(obj, "update_id", REQUIRED); + + std::unique_ptr message; + if (message = parseMessage(obj, "message", OPTIONAL)) + return std::make_unique(id, *message); + else if (message = parseMessage(obj, "edited_message", OPTIONAL)) + return std::make_unique(id, *message); + /// \todo: other updates + else + throw ParseError("Unknown update type"); + } + + std::unique_ptr parseUpdates(const rapidjson::Value& parent, const char* name, bool required) + { + bool found; + auto& obj = parseArray(parent, name, required, found); + if (found) + { + std::vector updates; + for (auto& elem : obj.GetArray()) + { + auto update = parseUpdate(elem); + updates.emplace_back(std::move(update)); + } + return std::make_unique(std::move(updates)); + } + else + return nullptr; + } + std::unique_ptr parseUser(const rapidjson::Value& parent, const char* name, bool required) { bool found; diff --git a/src/JsonObjects.hpp b/src/JsonObjects.hpp index 4dc8bcb..4c8f759 100644 --- a/src/JsonObjects.hpp +++ b/src/JsonObjects.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -35,6 +36,13 @@ namespace telebotxx /// \return pointer to Message std::unique_ptr parseMessage(const rapidjson::Value& parent, const char* name, bool required); + /// \brief Parse JSON array of Update + /// \param parent reference to parent JSON object + /// \param name field with array of Update objects + /// \param required REQUIRED or OPTIONAL + /// \return Updates (vector of UpdatePtr> + std::unique_ptr parseUpdates(const rapidjson::Value& parent, const char* name, bool required); + /// \brief Check JSON response /// /// Throws an exception if error code recieved. diff --git a/src/Update.cpp b/src/Update.cpp new file mode 100644 index 0000000..4071f7b --- /dev/null +++ b/src/Update.cpp @@ -0,0 +1,82 @@ +#include + +using namespace telebotxx; + +Update::Update(int id, Type type) + : id_(id), + updateType_(type) +{ +} + +Update::Update(const Update&) = default; +Update::Update(Update&&) = default; +Update::~Update() = default; + +int Update::getId() const +{ + return id_; +} + +Update::Type Update::getType() const +{ + return updateType_; +} + +void Update::swap(Update& other) noexcept +{ + using std::swap; + swap(id_, other.id_); + swap(updateType_, other.updateType_); +} + +//////////////////////////////////////////////////////////////// + +MessageUpdate::MessageUpdate(int id, const Message& message) + : Update(id, Type::Message), + message_(message) +{ +} + +MessageUpdate::MessageUpdate(const MessageUpdate&) = default; +MessageUpdate::MessageUpdate(MessageUpdate&&) = default; +MessageUpdate::~MessageUpdate() = default; + +const Message& MessageUpdate::getMessage() const +{ + return message_; +} + +void MessageUpdate::swap(MessageUpdate& other) noexcept +{ + using std::swap; + Update::swap(other); + swap(message_, other.message_); +} + +const MessageUpdate& MessageUpdate::operator=(MessageUpdate other) +{ + swap(other); + return *this; +} + +//////////////////////////////////////////////////////////////// + +EditedMessageUpdate::EditedMessageUpdate(int id, const Message& message) + : MessageUpdate(id, message) +{ +} + +EditedMessageUpdate::EditedMessageUpdate(const EditedMessageUpdate&) = default; +EditedMessageUpdate::EditedMessageUpdate(EditedMessageUpdate&&) = default; +EditedMessageUpdate::~EditedMessageUpdate() = default; + +void EditedMessageUpdate::swap(EditedMessageUpdate& other) noexcept +{ + MessageUpdate::swap(other); +} + +const EditedMessageUpdate& EditedMessageUpdate::operator=(EditedMessageUpdate other) +{ + swap(other); + return *this; +} diff --git a/tests/TestApi.cpp b/tests/TestApi.cpp index f8fca9a..312d266 100644 --- a/tests/TestApi.cpp +++ b/tests/TestApi.cpp @@ -131,4 +131,24 @@ BOOST_AUTO_TEST_SUITE(TestBotApi) BOOST_REQUIRE_NO_THROW(bot->sendPhotoUrl(chat, photoUrl, "Sample caption")); } + BOOST_AUTO_TEST_CASE(GetUpdates) + { + using namespace telebotxx; + PRINT_TESTNAME; + BOOST_REQUIRE(bot); + Updates updates; + BOOST_REQUIRE_NO_THROW(updates = bot->getUpdates()); + for (auto update : updates) + { + if (update->getType() == Update::Type::Message) + { + auto& message = std::dynamic_pointer_cast(update)->getMessage(); + if (message.getFrom()) + std::cout << *message.getFrom() << ": "; + std::cout << message.getText() << std::endl; + } + } + } + + BOOST_AUTO_TEST_SUITE_END()