diff --git a/include/telebotxx/Attachment.hpp b/include/telebotxx/Attachment.hpp index a07ea25..c4a16b3 100644 --- a/include/telebotxx/Attachment.hpp +++ b/include/telebotxx/Attachment.hpp @@ -16,7 +16,7 @@ namespace telebotxx Audio, Document, Game, - PhotoSize, + PhotoSizeArray, Sticker, Video, Voice, @@ -40,7 +40,7 @@ namespace telebotxx using AttachmentPtr = std::shared_ptr; - class PhotoSize : public Attachment + class PhotoSize { public: PhotoSize(); @@ -73,6 +73,27 @@ namespace telebotxx using PhotoSizePtr = std::shared_ptr; + class PhotoSizeArray : public Attachment + { + public: + PhotoSizeArray(); + PhotoSizeArray(const PhotoSizeArray&); + PhotoSizeArray(PhotoSizeArray&&); + ~PhotoSizeArray(); + + const std::vector& getArray() const; + void setArray(const std::vector& array); + + void swap(PhotoSizeArray& other) noexcept; + + const PhotoSizeArray& operator=(PhotoSizeArray other) noexcept; + + private: + std::vector array_; + }; + + using PhotoSizeArrayPtr = std::shared_ptr; + class Audio : public Attachment { public: diff --git a/include/telebotxx/Chat.hpp b/include/telebotxx/Chat.hpp index 683f666..91f14ae 100644 --- a/include/telebotxx/Chat.hpp +++ b/include/telebotxx/Chat.hpp @@ -2,6 +2,8 @@ #define TELEBOTXX_CHAT_HPP #include +#include +#include namespace telebotxx { @@ -21,8 +23,8 @@ namespace telebotxx Chat(Chat&&); ~Chat(); - int getId() const; - void setId(int id); + std::int64_t getId() const; + void setId(std::int64_t id); Type getType() const; void setType(Type type); @@ -47,7 +49,7 @@ namespace telebotxx const Chat& operator=(Chat other) noexcept; private: - int id_; + std::int64_t id_; Type type_; std::string title_; std::string username_; @@ -56,6 +58,8 @@ namespace telebotxx bool allAdmins_; }; + using ChatPtr = std::shared_ptr; + void swap(Chat& lhs, Chat& rhs); Chat::Type chatTypeFromString(const std::string& str); diff --git a/include/telebotxx/Message.hpp b/include/telebotxx/Message.hpp new file mode 100644 index 0000000..a20ccb6 --- /dev/null +++ b/include/telebotxx/Message.hpp @@ -0,0 +1,185 @@ +#ifndef TELEBOTXX_MESSAGE_HPP +#define TELEBOTXX_MESSAGE_HPP + +#include "User.hpp" +#include "Chat.hpp" +#include "Attachment.hpp" + +#include +#include +#include + +namespace telebotxx +{ + class MessageEntity + { + public: + enum class Type + { + Mention, + Hashtag, + BotCommand, + Url, + Email, + Bold, + Italic, + Code, + Pre, + TextLink, + TextMention + }; + + MessageEntity(); + MessageEntity(const MessageEntity&); + MessageEntity(MessageEntity&&); + ~MessageEntity(); + + Type getType() const; + void setType(Type type); + + int getOffset() const; + void setOffset(int offset); + + size_t getLength() const; + void setLength(size_t length); + + const std::string& getUrl() const; + void setUrl(const std::string& url); + + const User& getUser() const; + void setUser(const User& user); + + void swap(MessageEntity& other) noexcept; + + const MessageEntity& operator=(MessageEntity other); + + private: + Type type_; + int offset_; + std::size_t length_; + std::string url_; + User user_; + }; + + MessageEntity::Type messageEntityTypeFromString(const std::string& str); + + using MessageEntities = std::vector; + + class Message; + using MessagePtr = std::shared_ptr; + + class Message + { + public: + Message(); + Message(const Message&); + Message(Message&&); + ~Message(); + + int getId() const; + void setId(int id); + + const UserPtr getFrom() const; + void setFrom(UserPtr from); + + time_t getDate() const; + void setDate(time_t date); + + const Chat& getChat() const; + void setChat(const Chat& chat); + + const UserPtr getForwardFrom() const; + void setForwardFrom(UserPtr forwardFrom); + + const ChatPtr getForwardFromChat() const; + void setForwardFromChat(ChatPtr forwardFromChat); + + time_t getForwardDate() const; + void setForwardDate(time_t forwardDate); + + const MessagePtr getReplyToMessage() const; + void setReplyToMessage(MessagePtr replyToMessage); + + time_t getEditDate() const; + void setEditDate(time_t editDate); + + const std::string& getText() const; + void setText(const std::string& text); + + const MessageEntities& getEntities() const; + void setEntities(MessageEntities&& entities); + + const AttachmentPtr getAttachment() const; + void setAttachment(AttachmentPtr attachment); + + const std::string& getCaption() const; + void setCaption(const std::string& caption); + + const UserPtr getNewChatMember() const; + void setNewChatMember(UserPtr newChatMember); + + const UserPtr getLeftChatMember() const; + void setLeftChatMember(UserPtr leftChatMember); + + const std::string& getNewChatTitle() const; + void setNewChatTitle(const std::string& newChatTitle); + + const PhotoSizeArrayPtr getNewChatPhoto() const; + void setNewChatPhoto(PhotoSizeArrayPtr newChatPhoto); + + bool isDeleteChatPhoto() const; + void setDeleteChatPhoto(bool deleteChatPhoto); + + bool isGroupChatCreated() const; + void setGroupChatCreated(bool groupChatCreated); + + bool isSuperGroupChatCreated() const; + void setSuperGroupChatCreated(bool superGroupChatCreated); + + bool isChannelChatCreated() const; + void setChannelChatCreated(bool channelChatCreated); + + std::int64_t getMigrateToChatId() const; + void setMigrateToChatId(std::int64_t migrateToChatId); + + std::int64_t getMigrateFromChatId() const; + void setMigrateFromChatId(std::int64_t migrateFromChatId); + + const MessagePtr getPinnedMessage() const; + void setPinnedMessage(MessagePtr pinnedMessage); + + void swap(Message& other) noexcept; + + const Message& operator=(Message other) noexcept; + + private: + int id_; + UserPtr from_; + std::time_t date_; + Chat chat_; + UserPtr forwardFrom_; + ChatPtr forwardFromChat_; + std::time_t forwardDate_; + MessagePtr replyToMessage_; + std::time_t editDate_; + std::string text_; + MessageEntities entities_; + AttachmentPtr attachment_; + std::string caption_; + UserPtr newChatMember_; + UserPtr leftChatMember_; + std::string newChatTitle_; + PhotoSizeArrayPtr newChatPhoto_; + bool deleteChatPhoto_; + bool groupChatCreated_; + bool superGroupChatCreated_; + bool channelChatCreated_; + std::int64_t migrateToChatId_; + std::int64_t migrateFromChatId_; + MessagePtr pinnedMessage_; + }; + + void swap(Message& lhs, Message& rhs); +} + +#endif // TELEBOTXX_MESSAGE_HPP diff --git a/include/telebotxx/User.hpp b/include/telebotxx/User.hpp index ea5e3fc..00bd1fd 100644 --- a/include/telebotxx/User.hpp +++ b/include/telebotxx/User.hpp @@ -2,6 +2,7 @@ #define TELEBOTXX_USER_H #include +#include namespace telebotxx { @@ -54,6 +55,8 @@ namespace telebotxx std::string lastName_; std::string username_; }; + + using UserPtr = std::shared_ptr; } #endif // TELEBOTXX_USER_H diff --git a/src/Attachment.cpp b/src/Attachment.cpp index e9c35aa..96fd2fa 100644 --- a/src/Attachment.cpp +++ b/src/Attachment.cpp @@ -24,8 +24,7 @@ void Attachment::swap(Attachment& other) noexcept //////////////////////////////////////////////////////////////// PhotoSize::PhotoSize() - : Attachment(Type::PhotoSize), - width_(-1), + : width_(-1), height_(-1), fileSize_(-1) { @@ -77,7 +76,6 @@ void PhotoSize::setFileSize(int fileSize) void PhotoSize::swap(PhotoSize& other) noexcept { - Attachment::swap(other); using std::swap; swap(fileId_, other.fileId_); swap(width_, other.width_); @@ -93,6 +91,39 @@ const PhotoSize& PhotoSize::operator=(PhotoSize other) noexcept //////////////////////////////////////////////////////////////// +PhotoSizeArray::PhotoSizeArray() + : Attachment(Attachment::Type::PhotoSizeArray) +{ +} + +PhotoSizeArray::PhotoSizeArray(const PhotoSizeArray&) = default; +PhotoSizeArray::PhotoSizeArray(PhotoSizeArray&&) = default; +PhotoSizeArray::~PhotoSizeArray() = default; + +const std::vector& PhotoSizeArray::getArray() const +{ + return array_; +} + +void PhotoSizeArray::setArray(const std::vector& array) +{ + array_ = array; +} + +void PhotoSizeArray::swap(PhotoSizeArray& other) noexcept +{ + using std::swap; + swap(array_, other.array_); +} + +const PhotoSizeArray& PhotoSizeArray::operator=(PhotoSizeArray other) noexcept +{ + swap(other); + return *this; +} + +//////////////////////////////////////////////////////////////// + Audio::Audio() : Attachment(Type::Audio), duration_(-1), diff --git a/src/BotApi.cpp b/src/BotApi.cpp index 27b6750..3eb9a7b 100644 --- a/src/BotApi.cpp +++ b/src/BotApi.cpp @@ -1,79 +1,15 @@ #include #include #include +#include "JsonObjects.hpp" #include #include -#include #include #include -namespace telebotxx -{ - const rapidjson::Value& parseResponse(const rapidjson::Document& doc) - { - using namespace rapidjson; - if (!doc.IsObject()) - throw ParseError("Object expected"); - - // Get status - if (!doc.HasMember("ok") || !doc["ok"].IsBool()) - throw ParseError("Field 'ok' not found or has invalid type"); - bool ok = doc["ok"].GetBool(); - - if (ok) - { - if (!doc.HasMember("result") || !doc["result"].IsObject()) - throw ParseError("Field 'result' not found or has invalid type"); - return doc["result"]; - } else - { - if (!doc.HasMember("error_code") || !doc["error_code"].IsInt()) - throw ParseError("Field 'error_code' not found or has invalid type"); - int code = doc["error_code"].GetInt(); - - if (!doc.HasMember("description") || !doc["description"].IsString()) - throw ParseError("Field 'description' not found or has invalid type"); - std::string description(doc["description"].GetString()); - - throw ApiError(code, description); - } - } - - User parseUser(const rapidjson::Value& obj) - { - if (!obj.HasMember("id") || !obj["id"].IsInt()) - throw ParseError("Field 'id' not found or has invalid type"); - int id = obj["id"].GetInt(); - - if (!obj.HasMember("first_name") || !obj["first_name"].IsString()) - throw ParseError("Field 'first_name' not found or has invalid type"); - std::string firstName(obj["first_name"].GetString()); - - std::string lastName; - if (obj.HasMember("last_name")) - { - if (obj["last_name"].IsString()) - lastName = obj["last_name"].GetString(); - else - throw ParseError("Field 'last_name' has invalid type"); - } - - std::string username; - if (obj.HasMember("username")) - { - if (obj["username"].IsString()) - username = obj["username"].GetString(); - else - throw ParseError("Field 'username' has invalid type"); - } - - return User(id, firstName, lastName, username); - } -} - using namespace telebotxx; class BotApi::Impl @@ -94,11 +30,11 @@ public: if (debugMode) std::cout << "Response: " << response << std::endl; - using namespace rapidjson; - Document doc; + rapidjson::Document doc; doc.Parse(response.c_str()); - return parseUser(parseResponse(doc)); + parseResponse(doc); + return *parseUser(doc, "result", REQUIRED); } inline void sendMessage(const std::string& chat, const std::string& text, ParseMode parseMode) @@ -131,12 +67,12 @@ public: if (debugMode) std::cout << "Response: " << response << std::endl; - using namespace rapidjson; - Document doc; + rapidjson::Document doc; doc.Parse(response.c_str()); /// \todo Parse message parseResponse(doc); + MessagePtr message = parseMessage(doc, "result", REQUIRED); } inline void sendPhoto(const std::string& chat, const std::string& filename, const std::string& caption) @@ -152,12 +88,12 @@ public: if (debugMode) std::cout << "Response: " << response << std::endl; - using namespace rapidjson; - Document doc; + rapidjson::Document doc; doc.Parse(response.c_str()); /// \todo Parse message parseResponse(doc); + MessagePtr message = parseMessage(doc, "result", REQUIRED); } inline void sendPhotoUrl(const std::string& chat, const std::string& url, const std::string& caption) @@ -187,12 +123,12 @@ public: if (debugMode) std::cout << "Response: " << response << std::endl; - using namespace rapidjson; - Document doc; + rapidjson::Document doc; doc.Parse(response.c_str()); /// \todo Parse message parseResponse(doc); + MessagePtr message = parseMessage(doc, "result", REQUIRED); } private: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f2564a..dcac6af 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,7 +11,9 @@ set(LIBRARY_OUTPUT_PATH "../lib") set(SOURCE_FILES Attachment.cpp BotApi.cpp Chat.cpp + JsonObjects.cpp Logging.cpp + Message.cpp User.cpp ) diff --git a/src/Chat.cpp b/src/Chat.cpp index 51402d7..ebfdcb7 100644 --- a/src/Chat.cpp +++ b/src/Chat.cpp @@ -14,12 +14,12 @@ Chat::Chat(const Chat&) = default; Chat::Chat(Chat&&) = default; Chat::~Chat() = default; -int Chat::getId() const +std::int64_t Chat::getId() const { return id_; } -void Chat::setId(int id) +void Chat::setId(std::int64_t id) { id_ = id; } diff --git a/src/JsonObjects.cpp b/src/JsonObjects.cpp new file mode 100644 index 0000000..86a1f2c --- /dev/null +++ b/src/JsonObjects.cpp @@ -0,0 +1,287 @@ +#include "JsonObjects.hpp" +#include + +#include + +namespace telebotxx +{ + namespace impl + { + template bool is(const rapidjson::Value& obj); + template<> bool is(const rapidjson::Value& obj) { return obj.IsInt(); } + template<> bool is(const rapidjson::Value& obj) { return obj.IsInt64(); } + template<> bool is(const rapidjson::Value& obj) { return obj.IsBool(); } + template<> bool is(const rapidjson::Value& obj) { return obj.IsString(); } + + template const T get(const rapidjson::Value& obj); + template<> const int get(const rapidjson::Value& obj) { return obj.GetInt(); } + template<> const std::int64_t get(const rapidjson::Value& obj) { return obj.GetInt64(); } + template<> const bool get(const rapidjson::Value& obj) { return obj.GetBool(); } + template<> const std::string get(const rapidjson::Value& obj) { return obj.GetString(); } + + template const T null(); + template<> const int null() { return 0; } + template<> const std::int64_t null() { return 0; } + template<> const bool null() { return false; } + template<> const std::string null() { return ""; } + } + + template + const T parse(const rapidjson::Value& obj, const char* name, bool required) + { + if (obj.HasMember(name)) + { + if (impl::is(obj[name])) + return impl::get(obj[name]); + else + throw ParseError(std::string("Field '")+ name + "' has invalid type"); + } + else if (required) + throw ParseError(std::string("Field '")+ name + "' not found"); + else + return impl::null(); + } + + const rapidjson::Value& parseObject(const rapidjson::Value& parent, const char* name, bool required, bool& found) + { + if (parent.HasMember(name)) + { + if (parent[name].IsObject()) + { + found = true; + return parent[name]; + } + else + throw ParseError(std::string("Field '")+ name + "' has invalid type"); + } + else if (required) + throw ParseError(std::string("Field '")+ name + "' not found"); + else + { + found = false; + return parent; + } + } + + const rapidjson::Value& parseArray(const rapidjson::Value& parent, const char* name, bool required, bool& found) + { + if (parent.HasMember(name)) + { + if (parent[name].IsArray()) + { + found = true; + return parent[name]; + } + else + throw ParseError(std::string("Field '")+ name + "' has invalid type"); + } + else if (required) + throw ParseError(std::string("Field '")+ name + "' not found"); + else + { + found = false; + return parent; + } + } + + std::unique_ptr parsePhotoSize(const rapidjson::Value& obj) + { + auto photo = std::make_unique(); + photo->setFileId(parse(obj, "file_id", REQUIRED)); + photo->setWidth(parse(obj, "width", REQUIRED)); + photo->setHeight(parse(obj, "height", REQUIRED)); + photo->setFileSize(parse(obj, "file_size", OPTIONAL)); + return photo; + } + + std::unique_ptr parsePhotoSize(const rapidjson::Value& parent, const char* name, bool required) + { + bool found; + auto& obj = parseObject(parent, name, required, found); + if (found) + { + return parsePhotoSize(obj); + } + else + return nullptr; + } + + std::unique_ptr parsePhotoSizeArray(const rapidjson::Value& parent, const char* name, bool required) + { + bool found; + auto& obj = parseArray(parent, name, required, found); + if (found) + { + std::vector photos; + for (auto& elem : obj.GetArray()) + { + auto photo = parsePhotoSize(elem); + photos.push_back(std::move(*photo)); + } + auto result = std::make_unique(); + result->setArray(photos); + return result; + } + else + return nullptr; + } + + std::unique_ptr