From adb0630961dc4480cafdce8e51dc54b8b57f2970 Mon Sep 17 00:00:00 2001 From: Kirill Kirilenko Date: Sat, 12 Nov 2016 01:37:33 +0300 Subject: [PATCH] Rewritten sendPhoto functions, which are now variadic templated. --- include/telebotxx/BotApi.hpp | 24 +++-- include/telebotxx/RequestOptions.hpp | 24 ++++- include/telebotxx/SendPhotoRequest.hpp | 35 +++++++ src/BotApi.cpp | 10 +- src/CMakeLists.txt | 1 + src/RequestOptions.cpp | 80 ++++++++++++++- src/SendPhotoRequest.cpp | 137 +++++++++++++++++++++++++ tests/TestApi.cpp | 47 ++++++++- 8 files changed, 337 insertions(+), 21 deletions(-) create mode 100644 include/telebotxx/SendPhotoRequest.hpp create mode 100644 src/SendPhotoRequest.cpp diff --git a/include/telebotxx/BotApi.hpp b/include/telebotxx/BotApi.hpp index e32a4e5..01431e1 100644 --- a/include/telebotxx/BotApi.hpp +++ b/include/telebotxx/BotApi.hpp @@ -5,6 +5,7 @@ #include "Message.hpp" #include "Update.hpp" #include "SendMessageRequest.hpp" +#include "SendPhotoRequest.hpp" #include #include @@ -57,18 +58,23 @@ namespace telebotxx } /// \brief Send image - /// \param [in] chat chat identifier - /// \param [in] filename image location - /// \param [in] caption optional photo caption + /// \param [in] chatId chat identifier + /// \param [in] photo photo /// \return Message object, recieved from the server - Message sendPhoto(const std::string& chat, const std::string& filename, const std::string& caption = ""); + Message sendPhoto(ChatId&& chatId, Photo&& photo); - /// \brief Send image by URL - /// \param [in] chat chat identifier - /// \param [in] url image URL - /// \param [in] caption optional photo caption + /// \brief Send image + /// \param [in] chatId chat identifier + /// \param [in] photo photo + /// \param [in] args parameters /// \return Message object, recieved from the server - Message sendPhotoUrl(const std::string& chat, const std::string& url, const std::string& caption = ""); + template + Message sendPhoto(ChatId&& chatId, Photo&& photo, Ts&&... args) + { + SendPhotoRequest request(getTelegramMainUrl(), std::forward(chatId), std::forward(photo)); + setRequestOption(request, std::forward(args)...); + return request.execute(); + } /// \brief Get updates using long polling /// \param offset identifier of the first update to be returned diff --git a/include/telebotxx/RequestOptions.hpp b/include/telebotxx/RequestOptions.hpp index 6a9d677..ec5afd2 100644 --- a/include/telebotxx/RequestOptions.hpp +++ b/include/telebotxx/RequestOptions.hpp @@ -2,6 +2,7 @@ #define TELEBOTXX_REQUEST_OPTIONS_HPP #include +#include namespace telebotxx { @@ -64,6 +65,20 @@ namespace telebotxx int id_; }; + class Buffer + { + public: + Buffer(const char* buffer, std::size_t size, const std::string& filename); + explicit Buffer(const std::vector& data, const std::string& filename); + const char* data() const; + const std::size_t size() const; + const std::string filename() const; + private: + const char* data_; + std::size_t size_; + std::string filename_; + }; + class File { public: @@ -86,20 +101,27 @@ namespace telebotxx { public: explicit Photo(int id); + explicit Photo(const Buffer&); explicit Photo(const File&); explicit Photo(const Url&); Photo(const Photo&); Photo(Photo&&); ~Photo(); - enum class Type { File, Url, Id }; + enum class Type { Id, Buffer, File, Url}; Type getType() const; + int getId() const; + const Buffer& getBuffer() const; + const File& getFile() const; + const Url& getUrl() const; + private: Type type_; union { int id_; + Buffer buffer_; File file_; Url url_; }; diff --git a/include/telebotxx/SendPhotoRequest.hpp b/include/telebotxx/SendPhotoRequest.hpp new file mode 100644 index 0000000..bfff348 --- /dev/null +++ b/include/telebotxx/SendPhotoRequest.hpp @@ -0,0 +1,35 @@ +#ifndef TELEBOTXX_SEND_PHOTO_REQUEST_HPP +#define TELEBOTXX_SEND_PHOTO_REQUEST_HPP + +#include +#include + +#include + +#include +#include + +namespace telebotxx +{ + class SendPhotoRequest + { + public: + SendPhotoRequest(const std::string& telegramMainUrl, const ChatId& chat, const Photo& photo); + ~SendPhotoRequest(); + + void setCaption(const Caption& caption); + void setDisableNotification(const DisableNotification& disableNotification); + void setReplyToMessageId(const ReplyTo& replyToMessageId); + void setOption(const Caption& caption); + void setOption(const DisableNotification& disableNotification); + void setOption(const ReplyTo& replyToMessageId); + + Message execute(); + + private: + class Impl; + std::unique_ptr impl_; + }; +} + +#endif // TELEBOTXX_SEND_PHOTO_REQUEST_HPP diff --git a/src/BotApi.cpp b/src/BotApi.cpp index 75ccda5..050e146 100644 --- a/src/BotApi.cpp +++ b/src/BotApi.cpp @@ -166,14 +166,10 @@ Message BotApi::sendMessage(ChatId&& chatId, Text&& text) return request.execute(); } -Message BotApi::sendPhoto(const std::string& chat, const std::string& filename, const std::string& caption) +Message BotApi::sendPhoto(ChatId&& chatId, Photo&& photo) { - return impl_->sendPhoto(chat, filename, caption); -} - -Message BotApi::sendPhotoUrl(const std::string& chat, const std::string& url, const std::string& caption) -{ - return impl_->sendPhotoUrl(chat, url, caption); + SendPhotoRequest request(getTelegramMainUrl(), std::forward(chatId), std::forward(photo)); + return request.execute(); } Updates BotApi::getUpdates(int offset, unsigned short limit, unsigned int timeout) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index acb2fdf..f1b48f0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -21,6 +21,7 @@ set(SOURCE_FILES Attachment.cpp User.cpp RequestOptions.cpp SendMessageRequest.cpp + SendPhotoRequest.cpp ) add_library(telebotxx SHARED ${SOURCE_FILES}) diff --git a/src/RequestOptions.cpp b/src/RequestOptions.cpp index d50f187..1bcb01c 100644 --- a/src/RequestOptions.cpp +++ b/src/RequestOptions.cpp @@ -95,6 +95,33 @@ int ReplyTo::value() const //////////////////////////////////////////////////////////////// +Buffer::Buffer(const char *buffer, std::size_t size, const std::string& filename) + : data_(buffer), size_(size), filename_(filename) +{ +} + +Buffer::Buffer(const std::vector& data, const std::string& filename) + : data_(data.data()), size_(data.size()), filename_(filename) +{ +} + +const char* Buffer::data() const +{ + return data_; +} + +const std::size_t Buffer::size() const +{ + return size_; +} + +const std::string Buffer::filename() const +{ + return filename_; +} + +//////////////////////////////////////////////////////////////// + File::File(const std::string& filename) : filename_(filename) { @@ -124,6 +151,11 @@ Photo::Photo(int id) { } +Photo::Photo(const Buffer& buffer) + : type_(Type::Buffer), buffer_(buffer) +{ +} + Photo::Photo(const File& file) : type_(Type::File), file_(file) { @@ -139,12 +171,38 @@ Photo::Photo(const Photo& other) { if (type_ == Type::Id) id_ = other.id_; + else if (type_ == Type::Buffer) + new(&buffer_) Buffer(other.buffer_); else if (type_ == Type::File) new(&file_) File(other.file_); else new(&url_) Url(other.url_); } +Photo::Photo(Photo&& other) +{ + if (type_ == Type::Id) + { + id_ = other.id_; + other.id_ = 0; + } + else if (type_ == Type::Buffer) + { + new(&buffer_) Buffer(std::move(other.buffer_)); + other.buffer_.~Buffer(); + } + else if (type_ == Type::File) + { + new(&file_) File(std::move(other.file_)); + other.file_.~File(); + } + else + { + new(&url_) Url(std::move(other.url_)); + other.url_.~Url(); + } +} + Photo::~Photo() { if (type_ == Type::File) @@ -156,4 +214,24 @@ Photo::~Photo() Photo::Type Photo::getType() const { return type_; -} \ No newline at end of file +} + +int Photo::getId() const +{ + return id_; +} + +const Buffer& Photo::getBuffer() const +{ + return buffer_; +} + +const File& Photo::getFile() const +{ + return file_; +} + +const Url& Photo::getUrl() const +{ + return url_; +} diff --git a/src/SendPhotoRequest.cpp b/src/SendPhotoRequest.cpp new file mode 100644 index 0000000..9c0afab --- /dev/null +++ b/src/SendPhotoRequest.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include "JsonObjects.hpp" + +#include +#include +#include + +using namespace telebotxx; + +class SendPhotoRequest::Impl +{ +public: + Impl(const std::string& telegramMainUrl, const ChatId& chat, const Photo& photo) + : telegramMainUrl_(telegramMainUrl), chatId_(chat), photo_(photo) + { + } + + void setCaption(const Caption& caption) + { + caption_ = caption; + } + + void setDisableNotification(const DisableNotification& disableNotification) + { + disableNotification_ = disableNotification; + } + + void setReplyToMessageId(const ReplyTo& replyToMessageId) + { + replyToMessageId_ = replyToMessageId; + } + + Message execute() + { + cpr::Multipart multipart{}; + + // Add chat_id + if (chatId_.getType() == ChatId::Type::Id) + multipart.parts.push_back({"chat_id", chatId_.getId()}); + else + multipart.parts.push_back({"chat_id", chatId_.getUsername()}); + + // Add photo + if (photo_.getType() == Photo::Type::Id) + multipart.parts.push_back({"photo", photo_.getId()}); + else if (photo_.getType() == Photo::Type::Buffer) + { + const char* data = photo_.getBuffer().data(); + std::size_t size = photo_.getBuffer().size(); + const std::string filename = photo_.getBuffer().filename(); + multipart.parts.push_back({"photo", cpr::Buffer(data, data + size, filename)}); + } + else if (photo_.getType() == Photo::Type::File) + multipart.parts.push_back({"photo", cpr::File(photo_.getFile().getFilename())}); + else if (photo_.getType() == Photo::Type::Url) + multipart.parts.push_back({"photo", photo_.getUrl().getUrl()}); + + // Add caption + if (caption_) + multipart.parts.push_back({"caption", *caption_}); + + // Add disable_notification + if (disableNotification_) + multipart.parts.push_back({"disable_notification", disableNotification_->value()}); + + // Add reply_to_message_id + if (replyToMessageId_) + multipart.parts.push_back({"reply_to_message_id", replyToMessageId_->value()}); + + auto r = cpr::Post(cpr::Url{telegramMainUrl_ + "/sendPhoto"}, multipart); + auto& response = r.text; + + if (debugMode) + std::cout << "Response: " << response << std::endl; + + rapidjson::Document doc; + doc.Parse(response.c_str()); + + /// \todo Parse message + checkResponse(doc); + return *parseMessage(doc, "result", REQUIRED); + } + +private: + std::string telegramMainUrl_; + ChatId chatId_; + Photo photo_; + boost::optional caption_; + boost::optional disableNotification_; + boost::optional replyToMessageId_; +}; + +SendPhotoRequest::SendPhotoRequest(const std::string& telegramMainUrl, const ChatId& chat, const Photo& photo) + : impl_(std::make_unique(telegramMainUrl, chat, photo)) +{ +} + +SendPhotoRequest::~SendPhotoRequest() +{ +} + +void SendPhotoRequest::setCaption(const Caption& caption) +{ + impl_->setCaption(caption); +} + +void SendPhotoRequest::setDisableNotification(const DisableNotification& disableNotification) +{ + impl_->setDisableNotification(disableNotification); +} + +void SendPhotoRequest::setReplyToMessageId(const ReplyTo& replyToMessageId) +{ + impl_->setReplyToMessageId(replyToMessageId); +} + +void SendPhotoRequest::setOption(const Caption& caption) +{ + setCaption(caption); +} + +void SendPhotoRequest::setOption(const DisableNotification& disableNotification) +{ + setDisableNotification(disableNotification); +} + +void SendPhotoRequest::setOption(const ReplyTo& replyToMessageId) +{ + setReplyToMessageId(replyToMessageId); +} + +Message SendPhotoRequest::execute() +{ + return impl_->execute(); +} diff --git a/tests/TestApi.cpp b/tests/TestApi.cpp index 45da09e..a64a964 100644 --- a/tests/TestApi.cpp +++ b/tests/TestApi.cpp @@ -143,18 +143,59 @@ BOOST_AUTO_TEST_SUITE(TestBotApi) )); } - BOOST_AUTO_TEST_CASE(SendPhoto) + BOOST_AUTO_TEST_CASE(SendPhotoFile) { PRINT_TESTNAME; BOOST_REQUIRE(bot); - BOOST_REQUIRE_NO_THROW(bot->sendPhoto(chat, photoFile, "Sample caption")); + BOOST_REQUIRE_NO_THROW(bot->sendPhoto(chat, + Photo{File{photoFile}} + )); + } + + BOOST_AUTO_TEST_CASE(SendPhotoFileWithCaption) + { + PRINT_TESTNAME; + BOOST_REQUIRE(bot); + BOOST_REQUIRE_NO_THROW(bot->sendPhoto(chat, + Photo{File{photoFile}}, + Caption{"Photo with caption"} + )); + } + + BOOST_AUTO_TEST_CASE(SendPhotoInMemory) + { + PRINT_TESTNAME; + BOOST_REQUIRE(bot); + std::ifstream file(photoFile, std::ios::binary | std::ios::ate); + std::size_t size = static_cast(file.tellg()); + file.seekg(0, std::ios::beg); + std::vector buffer(size); + BOOST_REQUIRE(file.read(buffer.data(), size)); + BOOST_REQUIRE_NO_THROW(bot->sendPhoto(chat, + Photo{Buffer{buffer.data(), size, photoFile}}, + Caption{"Photo sent in-memory"} + )); } BOOST_AUTO_TEST_CASE(SendPhotoUrl) { PRINT_TESTNAME; BOOST_REQUIRE(bot); - BOOST_REQUIRE_NO_THROW(bot->sendPhotoUrl(chat, photoUrl, "Sample caption")); + BOOST_REQUIRE_NO_THROW(bot->sendPhoto(chat, + Photo{Url{photoUrl}}, + Caption{"Photo sent by URL"} + )); + } + + BOOST_AUTO_TEST_CASE(SendPhotoWithoutNotification) + { + PRINT_TESTNAME; + BOOST_REQUIRE(bot); + BOOST_REQUIRE_NO_THROW(bot->sendPhoto(chat, + Photo{File{photoFile}}, + Caption{"Photo without notification"}, + DisableNotification() + )); } BOOST_AUTO_TEST_CASE(GetUpdates)