Rewritten sendPhoto functions, which are now variadic templated.

This commit is contained in:
Kirill Kirilenko 2016-11-12 01:37:33 +03:00
parent d5e6a89cad
commit adb0630961
8 changed files with 337 additions and 21 deletions

View file

@ -5,6 +5,7 @@
#include "Message.hpp"
#include "Update.hpp"
#include "SendMessageRequest.hpp"
#include "SendPhotoRequest.hpp"
#include <string>
#include <memory>
@ -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<typename... Ts>
Message sendPhoto(ChatId&& chatId, Photo&& photo, Ts&&... args)
{
SendPhotoRequest request(getTelegramMainUrl(), std::forward<ChatId>(chatId), std::forward<Photo>(photo));
setRequestOption(request, std::forward<Ts>(args)...);
return request.execute();
}
/// \brief Get updates using long polling
/// \param offset identifier of the first update to be returned

View file

@ -2,6 +2,7 @@
#define TELEBOTXX_REQUEST_OPTIONS_HPP
#include <string>
#include <vector>
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<char>& 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_;
};

View file

@ -0,0 +1,35 @@
#ifndef TELEBOTXX_SEND_PHOTO_REQUEST_HPP
#define TELEBOTXX_SEND_PHOTO_REQUEST_HPP
#include <telebotxx/RequestOptions.hpp>
#include <telebotxx/Message.hpp>
#include <boost/optional.hpp>
#include <string>
#include <memory>
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> impl_;
};
}
#endif // TELEBOTXX_SEND_PHOTO_REQUEST_HPP

View file

@ -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>(chatId), std::forward<Photo>(photo));
return request.execute();
}
Updates BotApi::getUpdates(int offset, unsigned short limit, unsigned int timeout)

View file

@ -21,6 +21,7 @@ set(SOURCE_FILES Attachment.cpp
User.cpp
RequestOptions.cpp
SendMessageRequest.cpp
SendPhotoRequest.cpp
)
add_library(telebotxx SHARED ${SOURCE_FILES})

View file

@ -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<char>& 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_;
}
}
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_;
}

137
src/SendPhotoRequest.cpp Normal file
View file

@ -0,0 +1,137 @@
#include <telebotxx/SendPhotoRequest.hpp>
#include <telebotxx/Logging.hpp>
#include "JsonObjects.hpp"
#include <cpr/cpr.h>
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
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> caption_;
boost::optional<DisableNotification> disableNotification_;
boost::optional<ReplyTo> replyToMessageId_;
};
SendPhotoRequest::SendPhotoRequest(const std::string& telegramMainUrl, const ChatId& chat, const Photo& photo)
: impl_(std::make_unique<Impl>(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();
}

View file

@ -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<std::size_t>(file.tellg());
file.seekg(0, std::ios::beg);
std::vector<char> 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)