diff --git a/CMakeLists.txt b/CMakeLists.txt index cc7f084..2a2a6eb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,10 @@ set(CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -DDEBUG") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -march=native -DNDEBUG") -if(UNIX) + + +if(UNIX AND (NOT APPLE)) + message(STATUS "afiejanfonw") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-O1,--sort-common,--as-needed,-z,relro") elseif(WIN32 OR APPLE) #set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-O1,--sort-common,--as-needed") @@ -65,7 +68,15 @@ endif() # Search OpenSSL -if(UNIX OR APPLE) +if (APPLE) + # This is a bug in CMake that causes it to prefer the system version over + # the one in the specified ROOT folder. + find_package(PkgConfig REQUIRED) + find_package(OpenSSL REQUIRED) + set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_ROOT_DIR}/lib/libcrypto.dylib CACHE FILEPATH "" FORCE) + set(OPENSSL_SSL_LIBRARY ${OPENSSL_ROOT_DIR}/lib/libssl.dylib CACHE FILEPATH "" FORCE) + set(OPENSSL_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY} ${OPENSSL_SSL_LIBRARY}) +elseif(UNIX) find_package(PkgConfig REQUIRED) find_package(OpenSSL REQUIRED) if(NOT OPENSSL_FOUND) diff --git a/Doxyfile b/Doxyfile index 02f5413..5c32b80 100644 --- a/Doxyfile +++ b/Doxyfile @@ -935,6 +935,7 @@ EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */third/* EXCLUDE_PATTERNS += */build/* +EXCLUDE_PATTERNS += */test/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the diff --git a/README.md b/README.md index 94dc795..01ea18b 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,16 @@ This is a library for using Twitter API from C++ you can use these endpoint - statuses/update - statuses/destroy/:id +- statuses/retweet/:id - favorites/create - favorites/destroy +- media/upload(support: jpg, jpeg, png, gif, mp4) # Dependency - libcurl(openssl version) - libssl -# Instllation +# Installation ## Ubuntu ``` # apt install clang cmake git libboost-dev libboost-test-dev libcurl4-openssl-dev libssl-dev nunja-build @@ -29,6 +31,17 @@ $ cmake .. -G Ninja $ ninja ``` +## macOS +``` +$ brew install cmake curl git ninja openssl pkg-config +$ git clone https://github.com/koron0902/CocoaTweet +$ cd CocoaTweet +$ mkdir build +$ cd build +$ cmake .. -G ninja -DOPENSSL_ROOT_DIR= # CANNOT detect libssl automatically with homebrew and cmake bug +$ ninja +``` + ## Windows - Get MinGW32 from [here](https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe/)(start download automatically at open link) - Install `mingw32-base-bin` and `mingw32-gcc-g+-bin` via MinGW32 @@ -103,6 +116,14 @@ CocoaTweet::API::API api(key); // Post a tweet api.status().Update("Hello, World!!\nTweet from Cocoa Twitter Library"); +// Upload a media +auto media1 = api.media().Upload("path/to/file/image.jpeg"); +auto media2 = api.media().Upload("path/to/file/image2.png"); +api.status().Update("Upload media from Cocoa Twitter Library", std::vector{media1.id(), media2.id()}); + +// Retweet a tweet +api.status().Retweet("tweet id"); + // Delete a tweet api.status().Destroy("tweet id"); @@ -112,3 +133,5 @@ api.favorite().Create("tweet id"); // un Fav. a tweet api.favorite().Destroy("tweet id"); ``` + +donate by BitCoin : bc1qhpm8tmq72scqpl2ccemcf0ktfjg4rsu73e99tz \ No newline at end of file diff --git a/src/cocoatweet/api/api.cc b/src/cocoatweet/api/api.cc index 42408f4..6d5abd7 100644 --- a/src/cocoatweet/api/api.cc +++ b/src/cocoatweet/api/api.cc @@ -5,6 +5,7 @@ API::API(CocoaTweet::OAuth::Key _key) { oauth_ = std::make_shared(_key); status_ = Statuses::Status(oauth_); favorite_ = Favorites::Favorite(oauth_); + media_ = Medias::Media(oauth_); } Statuses::Status API::status() const { @@ -14,4 +15,8 @@ Statuses::Status API::status() const { Favorites::Favorite API::favorite() const { return favorite_; } + +Medias::Media API::media() const { + return media_; +} } // namespace CocoaTweet::API diff --git a/src/cocoatweet/api/api.h b/src/cocoatweet/api/api.h index 06a36f1..21ba046 100644 --- a/src/cocoatweet/api/api.h +++ b/src/cocoatweet/api/api.h @@ -3,6 +3,7 @@ #include #include +#include #include namespace CocoaTweet::API { @@ -21,9 +22,12 @@ public: /// @param[out] Favorite object typed CococaTweet::API::Favorites::Favorite Favorites::Favorite favorite() const; + Medias::Media media() const; + private: Statuses::Status status_; Favorites::Favorite favorite_; + Medias::Media media_; std::shared_ptr oauth_; }; } // namespace CocoaTweet::API diff --git a/src/cocoatweet/api/interface/httpBase.h b/src/cocoatweet/api/interface/httpBase.h new file mode 100644 index 0000000..b5dffc7 --- /dev/null +++ b/src/cocoatweet/api/interface/httpBase.h @@ -0,0 +1,26 @@ +#ifndef COCOATWEET_API_INTERFACE_HTTPBASE_H_ +#define COCOATWEET_API_INTERFACE_HTTPBASE_H_ + +#include +#include "cocoatweet/oauth/oauth.h" + +namespace CocoaTweet::API::Interface { +class HttpBase { +public: +protected: + std::weak_ptr oauth_; + std::map bodyParam_; + std::string url_; + std::string contentType_; + virtual void process( + std::weak_ptr _oauth, + std::function _callback) = 0; + static size_t curlCallback_(char* _ptr, size_t _size, size_t _nmemb, std::string* _stream) { + int realsize = _size * _nmemb; + _stream->append(_ptr, realsize); + return realsize; + } +}; +} // namespace CocoaTweet::API::Interface + +#endif diff --git a/src/cocoatweet/api/interface/httpPost.cc b/src/cocoatweet/api/interface/httpPost.cc index 618d0c4..ebed8cb 100644 --- a/src/cocoatweet/api/interface/httpPost.cc +++ b/src/cocoatweet/api/interface/httpPost.cc @@ -9,13 +9,11 @@ extern "C" { #include } -namespace CocoaTweet::API::Interface { -size_t HttpPost::curlCallback_(char* _ptr, size_t _size, size_t _nmemb, std::string* _stream) { - int realsize = _size * _nmemb; - _stream->append(_ptr, realsize); - return realsize; -} +#ifndef NDEBUG +#include +#endif +namespace CocoaTweet::API::Interface { void HttpPost::process(std::weak_ptr _oauth, std::function _callback) { // エンドポイントへのパラメータにOAuthパラメータを付加して署名作成 @@ -36,11 +34,20 @@ void HttpPost::process(std::weak_ptr _oauth, // リクエストボディの構築 std::string requestBody = ""; { - std::vector tmp; - for (const auto& [key, value] : bodyParam_) { - tmp.push_back(key + "=" + value); + if (contentType_ == "application/x-www-form-urlencoded") { + std::vector tmp; + for (const auto& [key, value] : bodyParam_) { + tmp.push_back(key + "=" + value); + requestBody = CocoaTweet::Util::join(tmp, "&"); + } + } else if (contentType_ == "multipart/form-data") { + for (const auto& [key, value] : bodyParam_) { + requestBody += (std::string("--") + "milkcocoa0902" + "\r\n"); + requestBody += + ("Content-Disposition: form-data; name=\"" + key + "\";\r\n\r\n" + value + "\r\n"); + } + requestBody += (std::string("--") + "milkcocoa0902" + "--" + "\r\n"); } - requestBody = CocoaTweet::Util::join(tmp, "&"); } // ヘッダの構築 @@ -69,12 +76,22 @@ void HttpPost::process(std::weak_ptr _oauth, curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlCallback_); curl_easy_setopt(curl, CURLOPT_WRITEDATA, (std::string*)&rcv); #ifndef NDEBUG + std::cout << "requestBody : " << requestBody << std::endl; curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); #endif // Headerを保持するcurl_slist*を初期化 struct curl_slist* headers = NULL; // Authorizationをヘッダに追加 headers = curl_slist_append(headers, oauthHeader.c_str()); + + std::string contentType = ""; + if (contentType_ == "application/x-www-form-urlencoded") { + contentType = contentType_; + } else if (contentType_ == "multipart/form-data") { + contentType = contentType_ + "; boundary=milkcocoa0902"; + } + + headers = curl_slist_append(headers, ("Content-Type: " + contentType).c_str()); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); res = curl_easy_perform(curl); curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode); diff --git a/src/cocoatweet/api/interface/httpPost.h b/src/cocoatweet/api/interface/httpPost.h index 5eeeb40..2547796 100644 --- a/src/cocoatweet/api/interface/httpPost.h +++ b/src/cocoatweet/api/interface/httpPost.h @@ -3,18 +3,20 @@ #include #include "cocoatweet/oauth/oauth.h" +#include namespace CocoaTweet::API::Interface { -class HttpPost { +/// @brief class for Send request with POST method +class HttpPost : public HttpBase { public: protected: - std::weak_ptr oauth_; - std::map bodyParam_; - std::string url_; - std::string contentType_; + /// @brief Send HTTP/POST using OAuth object + /// @param[in] std::weak_ptr _oauth : pointer to OAuth object to + /// authenticate + /// @param[in] std::function _callback : + /// callback method for processing to response void process(std::weak_ptr _oauth, std::function _callback); - static size_t curlCallback_(char* _ptr, size_t _size, size_t _nmemb, std::string* _stream); }; } // namespace CocoaTweet::API::Interface diff --git a/src/cocoatweet/api/media/media.cc b/src/cocoatweet/api/media/media.cc new file mode 100644 index 0000000..822e2ac --- /dev/null +++ b/src/cocoatweet/api/media/media.cc @@ -0,0 +1,13 @@ +#include "cocoatweet/api/media/media.h" + +namespace CocoaTweet::API::Medias { +Media::Media(std::shared_ptr _oauth) { + oauth_ = _oauth; +} + +CocoaTweet::API::Model::MediaStore Media::Upload(const std::string& _media) const { + CocoaTweet::API::Medias::Upload upload; + upload.media(_media); + return upload.process(oauth_); +} +} // namespace CocoaTweet::API::Medias diff --git a/src/cocoatweet/api/media/media.h b/src/cocoatweet/api/media/media.h new file mode 100644 index 0000000..d99fdcd --- /dev/null +++ b/src/cocoatweet/api/media/media.h @@ -0,0 +1,28 @@ +#ifndef COCOATWEET_API_MEDIA_MEDIA_H_ +#define COCOATWEET_API_MEDIA_MEDIA_H_ + +#include "cocoatweet/api/interface/groupInterface.h" +#include "cocoatweet/oauth/oauth.h" +#include +#include +#include + +namespace CocoaTweet::API::Medias { + +/// @brief Entory point for statuses/* +class Media : public groupInterface { +public: + /// @brief primary constructor to allow for create NON-INITIALIZED object + Media() = default; + + /// @brief constructor which finally should to be called. + /// @param[in] std::shared_ptr : pointer to OAuth object + Media(std::shared_ptr _oauth); + + CocoaTweet::API::Model::MediaStore Upload(const std::string& _file) const; + +private: +}; +} // namespace CocoaTweet::API::Medias + +#endif diff --git a/src/cocoatweet/api/media/upload.cc b/src/cocoatweet/api/media/upload.cc index e69de29..bd10fc0 100644 --- a/src/cocoatweet/api/media/upload.cc +++ b/src/cocoatweet/api/media/upload.cc @@ -0,0 +1,93 @@ +#include +#include +#include +#include +#include + +namespace CocoaTweet::API::Medias { +const std::map Upload::mimeType = {{".jpg", "image/jpeg"}, + {".jpeg", "image/jpeg"}, + {".png", "image/png"}, + {".gif", "image/gif"}, + {".mp4", "video/mp4"}}; +Upload::Upload() { + url_ = "https://upload.twitter.com/1.1/media/upload.json"; +} + +void Upload::media(const std::string& _media) { + media_ = _media; +} + +void Upload::mediaId(const std::string& _mediaId) {} + +CocoaTweet::API::Model::MediaStore Upload::process( + std::weak_ptr _oauth) { + auto extension = std::filesystem::path(media_).extension().string(); + if (mimeType.count(extension) == 0) { + throw new CocoaTweet::Exception::UnsupportedMediaTypeException( + std::string("media type \"" + extension + "\" is not supported media type")); + } + + auto backup = bodyParam_; + CocoaTweet::API::Model::MediaStore media; + std::ifstream ifs(media_, std::ios::binary); + std::string data((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); + ifs.close(); + + bodyParam_.insert_or_assign("total_bytes", std::to_string(data.size())); + + // INIT + { + contentType_ = "application/x-www-form-urlencoded"; + bodyParam_.insert_or_assign("command", "INIT"); + bodyParam_.insert_or_assign( + "media_type", mimeType.at(std::filesystem::path(media_).extension().string())); + + HttpPost::process(_oauth, + [&media](const unsigned int _responseCode, const std::string& _rsv) { + media = CocoaTweet::API::Model::MediaStore::parse(_responseCode, _rsv); + }); + + bodyParam_.insert_or_assign("media_id", media.id()); + } + + // APPEND + { + contentType_ = "multipart/form-data"; + bodyParam_.erase("media_type"); + bodyParam_.erase("total_bytes"); + + unsigned int segment = 0; + const unsigned long long chunk = 1024 * 512; + while (segment * chunk < data.size()) { + bodyParam_.insert_or_assign("command", "APPEND"); + bodyParam_.insert_or_assign("segment_index", std::to_string(segment)); + bodyParam_.insert_or_assign("media", data.substr(segment * chunk, chunk)); + HttpPost::process(_oauth, [](const unsigned int _responseCode, const std::string& _rsv) { + // std::cout << _responseCode << std::endl << _rsv<< std::endl; + }); + segment++; + } + } + + // FINALIZE + { + contentType_ = "application/x-www-form-urlencoded"; + bodyParam_.insert_or_assign("command", "FINALIZE"); + bodyParam_.erase("segment_index"); + bodyParam_.erase("media"); + HttpPost::process(_oauth, + [&media](const unsigned int _responseCode, const std::string& _rsv) { + std::cout << _responseCode << std::endl << _rsv << std::endl; + media = CocoaTweet::API::Model::MediaStore::parse(_responseCode, _rsv); + }); + } + + // STATUS if needed + {} + + bodyParam_ = backup; + + return media; +} +} // namespace CocoaTweet::API::Medias diff --git a/src/cocoatweet/api/media/upload.h b/src/cocoatweet/api/media/upload.h index d2c7554..a021855 100644 --- a/src/cocoatweet/api/media/upload.h +++ b/src/cocoatweet/api/media/upload.h @@ -1,22 +1,37 @@ #ifndef COCOATWEET_API_MEDIA_UPLOAD_H_ #define COCOATWEET_API_MEDIA_UPLOAD_H_ -#include +#include +#include +#include +#include namespace CocoaTweet::API::Medias { -class Upload : public postInterface { -public: - enum Command : class st::string { - INIT = "INIT", - APPEND = "APPEND", - FINALIZE = "FINALIZE", - }; +/// @brief entry point for using media/upload endpoint +class Upload : public CocoaTweet::API::Interface::HttpPost { +private: + std::string media_; + static const std::map mimeType; - Upload::Upload(); +public: + /// @brief default constructor + Upload(); + + /// @brief set media file with absolute path + /// @param[in] const std::string& _media : absolute path to media should be uploaded + /// @param[out] none void media(const std::string& _media); - void mediaId(const std::string _mediaId); - void process(std::weak_ptr _oauth, Command _command); -} + + /// @brief set media id(no affect to process. will be obsoleted) + void mediaId(const std::string& _mediaId); + + /// @brief upload media + /// @param[in] std::weak_ptr _oauth : pointer to OAuth object for + /// authenticate + /// @param[out] CocoaTweet::API::Model::MediaStore : media upload result. use id() for post + /// tweet. + CocoaTweet::API::Model::MediaStore process(std::weak_ptr _oauth); +}; } // namespace CocoaTweet::API::Medias -#endif \ No newline at end of file +#endif diff --git a/src/cocoatweet/api/model/mediaStore.cc b/src/cocoatweet/api/model/mediaStore.cc new file mode 100644 index 0000000..10f2cd0 --- /dev/null +++ b/src/cocoatweet/api/model/mediaStore.cc @@ -0,0 +1,71 @@ +#include +#include +#include "nlohmann/json.hpp" + +namespace CocoaTweet::API::Model { +MediaStore MediaStore::parse(const unsigned int _responseCode, const std::string& _json) { + auto j = nlohmann::json::parse(_json); + MediaStore media; + + if (_responseCode / 100 == 2) { + if (j.count("media_id_string") != 0) { + media.id(j["media_id_string"]); + } + + if (j.count("size") != 0) { + media.size(j["size"]); + } + + if (j.count("expires_after_secs") != 0) { + media.expires(j["expires_after_secs"]); + } + + if (j.count("processing_info") == 0) { + media.state("succeeded"); + } else { + media.state(j["processing_info"]["state"]); + media.remain(j["processing_info"]["check_after_secs"].get()); + } + } else { + throw new CocoaTweet::Exception::Exception(j["error"]); + } + + return media; +} + +void MediaStore::id(const std::string _id) { + id_ = _id; +} + +void MediaStore::size(const unsigned int _size) { + size_ = _size; +} + +void MediaStore::expires(const unsigned int _ex) { + expires_ = _ex; +} +void MediaStore::state(const std::string _state) { + state_ = _state; +} + +void MediaStore::remain(const unsigned int _remain) { + remain_ = _remain; +} + +const std::string MediaStore::id() const { + return id_; +} +const unsigned int MediaStore::size() const { + return size_; +} +const unsigned int MediaStore::expire() const { + return expires_; +} +const std::string MediaStore::state() const { + return state_; +} + +const unsigned int MediaStore::remain() const { + return remain_; +} +} // namespace CocoaTweet::API::Model diff --git a/src/cocoatweet/api/model/mediaStore.h b/src/cocoatweet/api/model/mediaStore.h new file mode 100644 index 0000000..145282b --- /dev/null +++ b/src/cocoatweet/api/model/mediaStore.h @@ -0,0 +1,90 @@ +#ifndef COCOATWEET_API_MODEL_MEDIASTORE_H_ +#define COCOATWEET_API_MODEL_MEDIASTORE_H_ + +#include + +namespace CocoaTweet::API::Model { + +/// @brief data class for tweet object +class MediaStore final { +public: + /// @brief constructor + MediaStore() = default; + + /// @brief copy constructor + MediaStore(const MediaStore&) = default; + + /// @brief constructor for create object from json response + /// @param[in] const unsigned int _responseCode : http status code which received when post + /// request + /// @param[in] const std::string& _json : received content from twitter endpoint + MediaStore(const unsigned int _responseCode, const std::string& _json) + : MediaStore(MediaStore::parse(_responseCode, _json)) {} + + /// @brief response parser for MediaStore object + /// @param[in] const unsigned int _responseCode : http status code which received when post + /// request + /// @param[in] const std::string& _json : received content from twitter endpoint + /// @param[out] CocoaTweet::API::Model::MediaStore + static MediaStore parse(const unsigned int _responseCode, const std::string& _json); + + /// @brief set id of tweet + /// @param[in] const std::string _id : media id to set + /// @param[out] none + void id(const std::string _id); + + /// @brief set media size in byte + /// @param[in] const unsigned int _size : media size in byte + /// @param[out] none + void size(const unsigned int _size); + + /// @brief set remaining time to expire the media + /// @param[in] const unsigned int : remaining time to expire the media ib sec + /// @param[out] none + void expires(const unsigned int _ex); + + /// @brief set media processing status + /// @param[in] const std::string _state : media processed status + /// @param[out] none + void state(const std::string _state); + + /// @brief set how second need for upload complete + /// @param[in] needed time to upload complete on server in second + /// @param[out] none + void remain(const unsigned int _remain); + + /// @brief get media id + /// @param[in] none + /// @param[out] const std::string : media id + const std::string id() const; + + /// @brief get media size + /// @param[in] none + /// @param[out] const unsigned int : media size in byte + const unsigned int size() const; + + /// @brief get remaining time to expire the media + /// @param[in] none + /// @param[out] const unsigned int : remaining time to expire the media ib sec + const unsigned int expire() const; + + /// @brief get media processing status + /// @param[in] none + /// @param[out] const std::string _state : media processed status + const std::string state() const; + + /// @brief set how second need for upload complete + /// @param[in] none + /// @param[out] needed time to upload complete on server in second + const unsigned int remain() const; + +private: + std::string id_; + unsigned long long size_; + unsigned long long expires_; + std::string state_; + unsigned long long remain_; +}; +} // namespace CocoaTweet::API::Model + +#endif diff --git a/src/cocoatweet/api/model/tweet.h b/src/cocoatweet/api/model/tweet.h index ca80b95..5ad6b6c 100644 --- a/src/cocoatweet/api/model/tweet.h +++ b/src/cocoatweet/api/model/tweet.h @@ -4,20 +4,68 @@ #include namespace CocoaTweet::API::Model { + +/// @brief data class for tweet object class Tweet final { public: - Tweet() = default; + /// @brief constructor + Tweet() = default; + + /// @brief copy constructor Tweet(const Tweet&) = default; + + /// @brief constructor for create object from json response + /// @param[in] const unsigned int _responseCode : http status code which received when post + /// request + /// @param[in] const std::string& _json : received content from twitter endpoint Tweet(const unsigned int _responseCode, const std::string& _json) : Tweet(Tweet::parse(_responseCode, _json)) {} + + /// @brief response parser for tweet object + /// @param[in] const unsigned int _responseCode : http status code which received when post + /// request + /// @param[in] const std::string& _json : received content from twitter endpoint + /// @param[out] CocoaTweet::API::Model::Tweet static Tweet parse(const unsigned int _responseCode, const std::string& _json); + + /// @brief set id of tweet + /// @param[in] const std::string _id : tweet id to set + /// @param[out] none void id(const std::string _id); + + /// @brief set created time of tweet + /// @param[in] const std::string _at : tweet created time to set + /// @param[out] none void createdAt(const std::string _at); + + /// @brief set tweet text + /// @param[in] const std::string _text : text of tweet to set + /// @param[out] none void text(const std::string _text); + + /// @brief set tweet source + /// @param[in] const std::string _source : source information to set + /// @param[out] none void source(const std::string _source); + + /// @brief get tweet id + /// @param[in] none + /// @param[out] const std::string : tweet id const std::string id() const; + + /// @brief get tweet create time + /// @param[in] none + /// @param[out] const std::string : time of tweet created time const std::string createdAt() const; + + /// @brief get tweet text + /// @param[in] none + /// @param[out] const std::string : tweet text const std::string text() const; + + /// @brief get tweet source information + /// @param[in] none + /// @param[out] const std::string : source information const std::string source() const; private: diff --git a/src/cocoatweet/api/status/destroy.h b/src/cocoatweet/api/status/destroy.h index 9cc8dad..6e2a036 100644 --- a/src/cocoatweet/api/status/destroy.h +++ b/src/cocoatweet/api/status/destroy.h @@ -19,7 +19,7 @@ public: /// @brief process request for endpoint /// @param[in] std::weak_ptr _oauth : pointer to oauth object - /// @param[out] none + /// @param[out] CocoaTweet::API::Model::Tweet : request result CocoaTweet::API::Model::Tweet process(std::weak_ptr _oauth); }; } // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/retweet.cc b/src/cocoatweet/api/status/retweet.cc new file mode 100644 index 0000000..0f7877d --- /dev/null +++ b/src/cocoatweet/api/status/retweet.cc @@ -0,0 +1,21 @@ +#include +#include + +namespace CocoaTweet::API::Statuses { + Retweet::Retweet(){} + + void Retweet::id(const std::string& _id){ + contentType_ = "application/x-www-form-urlencoded"; + url_ = "https://api.twitter.com/1.1/statuses/retweet/" + _id + ".json"; + } + + CocoaTweet::API::Model::Tweet Retweet::process(std::weak_ptr _oauth){ + CocoaTweet::API::Model::Tweet tweet; + HttpPost::process(_oauth, + [&tweet](const unsigned int _responseCode, const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet(_responseCode, _rcv); + }); + + return tweet; + } +} diff --git a/src/cocoatweet/api/status/retweet.h b/src/cocoatweet/api/status/retweet.h new file mode 100644 index 0000000..500f5e4 --- /dev/null +++ b/src/cocoatweet/api/status/retweet.h @@ -0,0 +1,18 @@ +#ifndef COCOATWEET_API_STATUS_RETWEET_H_ +#define COCOATWEET_API_STATUS_RETWEET_H_ + +#include +#include + +namespace CocoaTweet::API::Statuses { + class Retweet : public CocoaTweet::API::Interface::HttpPost{ + public: + Retweet(); + + void id(const std::string& _id); + + CocoaTweet::API::Model::Tweet process(std::weak_ptr _oauth); + }; +} + +#endif \ No newline at end of file diff --git a/src/cocoatweet/api/status/status.cc b/src/cocoatweet/api/status/status.cc index ce9d288..02d3adf 100644 --- a/src/cocoatweet/api/status/status.cc +++ b/src/cocoatweet/api/status/status.cc @@ -1,6 +1,7 @@ #include "cocoatweet/api/status/status.h" #include "cocoatweet/api/status/update.h" #include "cocoatweet/api/status/destroy.h" +#include "cocoatweet/api/status/retweet.h" namespace CocoaTweet::API::Statuses { Status::Status(std::shared_ptr _oauth) { @@ -13,9 +14,67 @@ CocoaTweet::API::Model::Tweet Status::Update(const std::string& _status) const { return update.process(oauth_); } +CocoaTweet::API::Model::Tweet Status::Update(const std::string& _status, + const Options _options) const { + CocoaTweet::API::Statuses::Update update; + update.status(_status); + + if (_options.replyToStatusId != defaultOpt_.replyToStatusId) { + update.replyToStatusId(_options.replyToStatusId); + } + + if (_options.autoPopulateReplyMetaData != defaultOpt_.autoPopulateReplyMetaData) { + update.autoPopulateReplyMetaData(_options.autoPopulateReplyMetaData); + } + + if (_options.excludeReplyUserId != defaultOpt_.excludeReplyUserId) { + update.excludeReplyUserId(_options.excludeReplyUserId); + } + + if (_options.attachmentUrl != defaultOpt_.attachmentUrl) { + update.attachmentUrl(_options.attachmentUrl); + } + + if (_options.coord != defaultOpt_.coord) { + update.coord(_options.coord); + } + + if (_options.displayCoord != defaultOpt_.displayCoord) { + update.displayCoord(_options.displayCoord); + } + + if (_options.trimUser != defaultOpt_.trimUser) { + update.trimUser(_options.trimUser); + } + + if (_options.enableDMCommands != defaultOpt_.enableDMCommands) { + update.enableDMCommands(_options.enableDMCommands); + } + + if (_options.failDMCommands != defaultOpt_.failDMCommands) { + update.failDMCommands(_options.failDMCommands); + } + + return update.process(oauth_); +} + +CocoaTweet::API::Model::Tweet Status::Update(const std::string& _status, + std::vector _mediaId) const { + CocoaTweet::API::Statuses::Update update; + update.status(_status); + update.mediaId(_mediaId); + return update.process(oauth_); +} + CocoaTweet::API::Model::Tweet Status::Destroy(const std::string& _id) const { CocoaTweet::API::Statuses::Destroy destroy; destroy.id(_id); return destroy.process(oauth_); } + +CocoaTweet::API::Model::Tweet Status::Retweet(const std::string& _id) const{ + CocoaTweet::API::Statuses::Retweet retweet; + retweet.id(_id); + return retweet.process(oauth_); +} } // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/status.h b/src/cocoatweet/api/status/status.h index b5919da..3423cb4 100644 --- a/src/cocoatweet/api/status/status.h +++ b/src/cocoatweet/api/status/status.h @@ -4,16 +4,66 @@ #include "cocoatweet/api/interface/groupInterface.h" #include "cocoatweet/oauth/oauth.h" #include +#include +#include namespace CocoaTweet::API::Statuses { + +/// @brief Entory point for statuses/* class Status : public groupInterface { public: + struct Options { + std::string replyToStatusId; + bool autoPopulateReplyMetaData = false; + std::vector excludeReplyUserId; + std::string attachmentUrl; + std::pair coord; + bool displayCoord = false; + bool trimUser = false; + bool enableDMCommands = false; + bool failDMCommands = true; + }; + + /// @brief primary constructor to allow for create NON-INITIALIZED object Status() = default; + + /// @brief constructor which finally should to be called. + /// @param[in] std::shared_ptr : pointer to OAuth object Status(std::shared_ptr _oauth); + + /// @brief send request to statuses/update with specified status + /// @details this function throws CocoaTweet::Exception::* if something happen + /// @param[in] const std::string& : tweet text + /// @param[out] CocoaTweet::API::Model::Tweet : Tweet result CocoaTweet::API::Model::Tweet Update(const std::string& _status) const; + + /// @brief send request to statuses/update with specified status + /// @details this function throws CocoaTweet::Exception::* if something happen + /// @param[in] const std::string& : tweet text + /// @param[in] const CocoaTweet::API::Statuses::Status::Options option : status update options + /// for more parameters + /// @param[out] CocoaTweet::API::Model::Tweet : Tweet result + CocoaTweet::API::Model::Tweet Update(const std::string& _status, + const Options _options) const; + + /// @brief send request to statuses/update with specified status + /// @details this function throws CocoaTweet::Exception::* if something happen + /// @param[in] const std::string& : tweet text + /// @param[in] std::vector _mediaId : media id which posted with tweet + /// @param[out] CocoaTweet::API::Model::Tweet : Tweet result + CocoaTweet::API::Model::Tweet Update(const std::string& _status, + std::vector _mediaId) const; + + /// @brief send request to statuses/destroy with specified id + /// @details this function throws CocoaTweet::Exception::* if something happen + /// @param[in] const std::string& : tweet id which should be delete + /// @param[out] CocoaTweet::API::Model::Tweet : Destroy result CocoaTweet::API::Model::Tweet Destroy(const std::string& _id) const; + CocoaTweet::API::Model::Tweet Retweet(const std::string& _id) const; + private: + Options defaultOpt_; }; } // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/update.cc b/src/cocoatweet/api/status/update.cc index e6e8612..3395d65 100644 --- a/src/cocoatweet/api/status/update.cc +++ b/src/cocoatweet/api/status/update.cc @@ -1,4 +1,5 @@ #include "cocoatweet/api/status/update.h" +#include namespace CocoaTweet::API::Statuses { Update::Update() { @@ -11,6 +12,47 @@ void Update::status(const std::string _status) { bodyParam_.insert_or_assign("status", status_); } +void Update::mediaId(const std::vector _media) { + bodyParam_.insert_or_assign("media_ids", CocoaTweet::Util::join(_media, ",")); +} + +void Update::replyToStatusId(const std::string _reply) { + bodyParam_.insert_or_assign("in_reply_to_status_id", _reply); +} + +void Update::autoPopulateReplyMetaData(bool _meta) { + bodyParam_.insert_or_assign("auto_populate_reply_metadata", std::to_string(_meta)); +} + +void Update::excludeReplyUserId(const std::vector _ex) { + bodyParam_.insert_or_assign("exclude_reply_user_ids", CocoaTweet::Util::join(_ex, ",")); +} + +void Update::attachmentUrl(const std::string _url) { + bodyParam_.insert_or_assign("attachment_url", _url); +} + +void Update::coord(std::pair _coord) { + bodyParam_.insert_or_assign("lat", _coord.first); + bodyParam_.insert_or_assign("long", _coord.second); +} + +void Update::displayCoord(bool _disp) { + bodyParam_.insert_or_assign("display_coordinates", std::to_string(_disp)); +} + +void Update::trimUser(bool _trim) { + bodyParam_.insert_or_assign("trim_user", std::to_string(_trim)); +} + +void Update::enableDMCommands(bool _enable) { + bodyParam_.insert_or_assign("enable_dmcommands", std::to_string(_enable)); +} + +void Update::failDMCommands(bool _fail) { + bodyParam_.insert_or_assign("fail_dmcommands", std::to_string(_fail)); +} + CocoaTweet::API::Model::Tweet Update::process(std::weak_ptr _oauth) { CocoaTweet::API::Model::Tweet tweet; HttpPost::process(_oauth, diff --git a/src/cocoatweet/api/status/update.h b/src/cocoatweet/api/status/update.h index 90fa54e..1f3ff38 100644 --- a/src/cocoatweet/api/status/update.h +++ b/src/cocoatweet/api/status/update.h @@ -1,15 +1,47 @@ #ifndef COCOATWEET_API_STATUS_UPDATE_H_ #define COCOATWEET_API_STATUS_UPDATE_H_ -#include #include #include +#include +#include +#include namespace CocoaTweet::API::Statuses { +/// @brief class for using statuses/update endpoint class Update : public CocoaTweet::API::Interface::HttpPost { public: + /// @brief primary constructor Update(); + + /// @brief set tweet text for sending request to statuses/update + /// @param[in] const std::string _status : tweet text + /// @param[out] none void status(const std::string _status); + + void mediaId(const std::vector _media); + + void replyToStatusId(const std::string _reply); + + void autoPopulateReplyMetaData(bool _meta); + + void excludeReplyUserId(const std::vector _ex); + + void attachmentUrl(const std::string _url); + + void coord(std::pair _coord); + + void displayCoord(bool _disp); + + void trimUser(bool _trim); + + void enableDMCommands(bool _enable); + + void failDMCommands(bool _fail); + + /// @brief process request for endpoint + /// @param[in] std::weak_ptr _oauth : pointer to oauth object + /// @param[out] CocoaTweet::API::Model::Tweet : request result CocoaTweet::API::Model::Tweet process(std::weak_ptr _oauth); private: diff --git a/src/cocoatweet/exception/exception.h b/src/cocoatweet/exception/exception.h index 90683e3..35311ea 100644 --- a/src/cocoatweet/exception/exception.h +++ b/src/cocoatweet/exception/exception.h @@ -8,6 +8,7 @@ namespace CocoaTweet::Exception { class Exception : public std::exception { public: Exception(const char* _msg) : msg_(std::string(_msg)) {} + Exception(const std::string& _msg) : msg_(std::string(_msg)) {} const std::string& what() { return msg_; } diff --git a/src/cocoatweet/exception/unsupportedMediaTypeException.h b/src/cocoatweet/exception/unsupportedMediaTypeException.h new file mode 100644 index 0000000..97bf57d --- /dev/null +++ b/src/cocoatweet/exception/unsupportedMediaTypeException.h @@ -0,0 +1,12 @@ +#ifndef COCOATWEET_EXCEPTION_UNSUPPORTEDMEDIATYPEEXCEPTION_H_ +#define COCOATWEET_EXCEPTION_UNSUPPORTEDMEDIATYPEEXCEPTION_H_ + +#include + +namespace CocoaTweet::Exception { +class UnsupportedMediaTypeException final : Exception { + using Exception::Exception; +}; +} // namespace CocoaTweet::Exception + +#endif diff --git a/src/cocoatweet/oauth/key.h b/src/cocoatweet/oauth/key.h index 71828e4..8d51002 100644 --- a/src/cocoatweet/oauth/key.h +++ b/src/cocoatweet/oauth/key.h @@ -1,6 +1,7 @@ #ifndef COCOATWEET_OAUTH_KEY_H_ #define COCOATWEET_OAUTH_KEY_H_ +#include #include namespace CocoaTweet::OAuth { diff --git a/src/cocoatweet/oauth/oauth.h b/src/cocoatweet/oauth/oauth.h index d30dbad..4761ee7 100644 --- a/src/cocoatweet/oauth/oauth.h +++ b/src/cocoatweet/oauth/oauth.h @@ -1,6 +1,7 @@ #ifndef COCOATWEET_OAUTH_OAUTH_H_ #define COCOATWEET_OAUTH_OAUTH_H_ +#include #include #include #include "key.h"