diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1277b35 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2021 Keita.S(@milkcocoa0902) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 01ea18b..f22ab7e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ![](http://jenkins.milkcocoa.info/job/CocoaTweet/job/master/badge/icon?style=plastic&config=unittest) ![](https://img.shields.io/badge/libcurl-libcurl4--openssl-blue) ![](https://img.shields.io/badge/libssl-1.1.1f-blue) +![](https://img.shields.io/badge/license-MIT-orange) # CocoaTweet This is a library for using Twitter API from C++ @@ -11,6 +12,8 @@ you can use these endpoint - statuses/update - statuses/destroy/:id - statuses/retweet/:id +- statuses/unretweet/:id +- statuses/user_timeline - favorites/create - favorites/destroy - media/upload(support: jpg, jpeg, png, gif, mp4) @@ -124,6 +127,9 @@ api.status().Update("Upload media from Cocoa Twitter Library", std::vector _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); - }); + HttpPost::process(_oauth, [&tweet](const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet(_rcv); + }); return tweet; } diff --git a/src/cocoatweet/api/favorite/destroy.cc b/src/cocoatweet/api/favorite/destroy.cc index 126eac6..ef88c9a 100644 --- a/src/cocoatweet/api/favorite/destroy.cc +++ b/src/cocoatweet/api/favorite/destroy.cc @@ -14,10 +14,9 @@ void Destroy::id(const std::string& _id) { CocoaTweet::API::Model::Tweet Destroy::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); - }); + HttpPost::process(_oauth, [&tweet](const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet(_rcv); + }); return tweet; } diff --git a/src/cocoatweet/api/interface/httpBase.h b/src/cocoatweet/api/interface/httpBase.h index b5dffc7..e6314a3 100644 --- a/src/cocoatweet/api/interface/httpBase.h +++ b/src/cocoatweet/api/interface/httpBase.h @@ -12,9 +12,8 @@ protected: std::map bodyParam_; std::string url_; std::string contentType_; - virtual void process( - std::weak_ptr _oauth, - std::function _callback) = 0; + 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); diff --git a/src/cocoatweet/api/interface/httpGet.cc b/src/cocoatweet/api/interface/httpGet.cc new file mode 100644 index 0000000..f1f26db --- /dev/null +++ b/src/cocoatweet/api/interface/httpGet.cc @@ -0,0 +1,98 @@ +#include +#include "cocoatweet/util/util.h" +#include +#include +#include +#include +#include +extern "C" { +#include +} + +#ifndef NDEBUG +#include +#endif + +namespace CocoaTweet::API::Interface { +void HttpGet::process(std::weak_ptr _oauth, + std::function _callback) { + auto url = url_; + + // エンドポイントへのパラメータにOAuthパラメータを付加して署名作成 + auto oauth = _oauth.lock(); + auto oauthParam = oauth->oauthParam(); + auto sigingParam = oauthParam; + if (contentType_ == "application/x-www-form-urlencoded") { + for (const auto [k, v] : bodyParam_) { + sigingParam.insert_or_assign(k, v); + } + } + + auto signature = oauth->signature(sigingParam, "GET", url_); + + // 作成した署名をエンドポイントへのパラメータ及びOAuthパラメータに登録 + oauthParam.merge(signature); + + // URLの構築 + { + std::vector tmp; + for (const auto& [key, value] : bodyParam_) { + tmp.push_back(key + "=" + value); + } + url_ += std::string("?" + CocoaTweet::Util::join(tmp, "&")); + } + + // ヘッダの構築 + std::string oauthHeader = "authorization: OAuth "; + { + std::vector tmp; + for (const auto& [key, value] : oauthParam) { + tmp.push_back(key + "=" + CocoaTweet::Util::urlEncode(value)); + } + oauthHeader += CocoaTweet::Util::join(tmp, ","); + } + + // do post + CURL* curl; + CURLcode res; + std::string rcv; + long responseCode; + curl = curl_easy_init(); + url_ = url_; + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, url_.c_str()); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlCallback_); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (std::string*)&rcv); +#ifndef NDEBUG + 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); + curl_easy_cleanup(curl); + } + + if (res != CURLE_OK) { + throw std::runtime_error(std::string("INTERNAL ERROR : curl(") + std::to_string(res) + ")"); + exit(1); + } + + if (_callback) { + _callback(rcv); + } +} +} // namespace CocoaTweet::API::Interface diff --git a/src/cocoatweet/api/interface/httpGet.h b/src/cocoatweet/api/interface/httpGet.h new file mode 100644 index 0000000..bf98421 --- /dev/null +++ b/src/cocoatweet/api/interface/httpGet.h @@ -0,0 +1,23 @@ +#ifndef COCOATWEET_API_INTERFACE_HTTPGET_H_ +#define COCOATWEET_API_INTERFACE_HTTPGET_H_ + +#include +#include "cocoatweet/oauth/oauth.h" +#include + +namespace CocoaTweet::API::Interface { +/// @brief class for Send request with POST method +class HttpGet : public virtual HttpBase { +public: +protected: + /// @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); +}; +} // namespace CocoaTweet::API::Interface + +#endif diff --git a/src/cocoatweet/api/interface/httpPost.cc b/src/cocoatweet/api/interface/httpPost.cc index ebed8cb..378c280 100644 --- a/src/cocoatweet/api/interface/httpPost.cc +++ b/src/cocoatweet/api/interface/httpPost.cc @@ -1,5 +1,11 @@ #include #include "cocoatweet/util/util.h" +#include +#include +#include +#include +#include +#include "nlohmann/json.hpp" #include #include #include @@ -15,7 +21,7 @@ extern "C" { namespace CocoaTweet::API::Interface { void HttpPost::process(std::weak_ptr _oauth, - std::function _callback) { + std::function _callback) { // エンドポイントへのパラメータにOAuthパラメータを付加して署名作成 auto oauth = _oauth.lock(); auto oauthParam = oauth->oauthParam(); @@ -103,8 +109,29 @@ void HttpPost::process(std::weak_ptr _oauth, exit(1); } + if ((responseCode / 100) == 4) { + auto j = nlohmann::json::parse(rcv); + auto error = j["errors"][0]["code"]; + auto message = j["errors"][0]["message"]; + if (j.count("error") != 0) { + // この形式はエラーコードを持たないのでエラー種別が特定できない + throw new CocoaTweet::Exception::Exception(j["error"]); + } + if (error.get() == 144) { + throw CocoaTweet::Exception::TweetNotFoundException(message.get().c_str()); + } else if (error.get() == 32) { + throw CocoaTweet::Exception::AuthenticateException(message.get().c_str()); + } else if (error.get() == 187) { + throw CocoaTweet::Exception::TweetDuplicateException(message.get().c_str()); + } else if (error.get() == 88 || error.get() == 185) { + throw CocoaTweet::Exception::RateLimitException(message.get().c_str()); + } else if (error.get() == 186) { + throw CocoaTweet::Exception::TweetTooLongException(message.get().c_str()); + } + } + if (_callback) { - _callback(responseCode, rcv); + _callback(rcv); } } } // namespace CocoaTweet::API::Interface diff --git a/src/cocoatweet/api/interface/httpPost.h b/src/cocoatweet/api/interface/httpPost.h index 2547796..441fe58 100644 --- a/src/cocoatweet/api/interface/httpPost.h +++ b/src/cocoatweet/api/interface/httpPost.h @@ -16,7 +16,7 @@ protected: /// @param[in] std::function _callback : /// callback method for processing to response void process(std::weak_ptr _oauth, - std::function _callback); + std::function _callback); }; } // namespace CocoaTweet::API::Interface diff --git a/src/cocoatweet/api/media/upload.cc b/src/cocoatweet/api/media/upload.cc index bd10fc0..43de855 100644 --- a/src/cocoatweet/api/media/upload.cc +++ b/src/cocoatweet/api/media/upload.cc @@ -43,10 +43,9 @@ CocoaTweet::API::Model::MediaStore Upload::process( 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); - }); + HttpPost::process(_oauth, [&media](const std::string& _rcv) { + media = CocoaTweet::API::Model::MediaStore::parse(_rcv); + }); bodyParam_.insert_or_assign("media_id", media.id()); } @@ -63,7 +62,7 @@ CocoaTweet::API::Model::MediaStore Upload::process( 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) { + HttpPost::process(_oauth, [](const std::string& _rsv) { // std::cout << _responseCode << std::endl << _rsv<< std::endl; }); segment++; @@ -76,11 +75,9 @@ CocoaTweet::API::Model::MediaStore Upload::process( 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); - }); + HttpPost::process(_oauth, [&media](const std::string& _rcv) { + media = CocoaTweet::API::Model::MediaStore::parse(_rcv); + }); } // STATUS if needed diff --git a/src/cocoatweet/api/model/mediaStore.cc b/src/cocoatweet/api/model/mediaStore.cc index 10f2cd0..a293b3a 100644 --- a/src/cocoatweet/api/model/mediaStore.cc +++ b/src/cocoatweet/api/model/mediaStore.cc @@ -3,31 +3,27 @@ #include "nlohmann/json.hpp" namespace CocoaTweet::API::Model { -MediaStore MediaStore::parse(const unsigned int _responseCode, const std::string& _json) { +MediaStore MediaStore::parse(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("media_id_string") != 0) { + media.id(j["media_id_string"]); + } - if (j.count("size") != 0) { - media.size(j["size"]); - } + 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("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()); - } + if (j.count("processing_info") == 0) { + media.state("succeeded"); } else { - throw new CocoaTweet::Exception::Exception(j["error"]); + media.state(j["processing_info"]["state"]); + media.remain(j["processing_info"]["check_after_secs"].get()); } return media; diff --git a/src/cocoatweet/api/model/mediaStore.h b/src/cocoatweet/api/model/mediaStore.h index 145282b..047dbfd 100644 --- a/src/cocoatweet/api/model/mediaStore.h +++ b/src/cocoatweet/api/model/mediaStore.h @@ -15,18 +15,13 @@ public: 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)) {} + MediaStore(const std::string& _json) : MediaStore(MediaStore::parse(_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); + static MediaStore parse(const std::string& _json); /// @brief set id of tweet /// @param[in] const std::string _id : media id to set diff --git a/src/cocoatweet/api/model/tweet.cc b/src/cocoatweet/api/model/tweet.cc index 3bf3fb7..27064d0 100644 --- a/src/cocoatweet/api/model/tweet.cc +++ b/src/cocoatweet/api/model/tweet.cc @@ -1,36 +1,15 @@ #include -#include -#include -#include -#include -#include #include "nlohmann/json.hpp" namespace CocoaTweet::API::Model { -Tweet Tweet::parse(const unsigned int _responseCode, const std::string& _json) { +Tweet Tweet::parse(const std::string& _json) { auto j = nlohmann::json::parse(_json); Tweet tweet; - if (_responseCode == 200) { - tweet.id(j["id_str"]); - tweet.createdAt(j["created_at"]); - tweet.text(j["text"]); - tweet.source(j["source"]); - } else { - auto error = j["errors"][0]["code"]; - auto message = j["errors"][0]["message"]; - if (error.get() == 144) { - throw CocoaTweet::Exception::TweetNotFoundException(message.get().c_str()); - } else if (error.get() == 32) { - throw CocoaTweet::Exception::AuthenticateException(message.get().c_str()); - } else if (error.get() == 187) { - throw CocoaTweet::Exception::TweetDuplicateException(message.get().c_str()); - } else if (error.get() == 88 || error.get() == 185) { - throw CocoaTweet::Exception::RateLimitException(message.get().c_str()); - } else if (error.get() == 186) { - throw CocoaTweet::Exception::TweetTooLongException(message.get().c_str()); - } - } + tweet.id(j["id_str"]); + tweet.createdAt(j["created_at"]); + tweet.text(j["text"]); + tweet.source(j["source"]); return tweet; } diff --git a/src/cocoatweet/api/model/tweet.h b/src/cocoatweet/api/model/tweet.h index 5ad6b6c..8e16a5c 100644 --- a/src/cocoatweet/api/model/tweet.h +++ b/src/cocoatweet/api/model/tweet.h @@ -15,18 +15,13 @@ public: 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)) {} + Tweet(const std::string& _json) : Tweet(Tweet::parse(_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); + static Tweet parse(const std::string& _json); /// @brief set id of tweet /// @param[in] const std::string _id : tweet id to set diff --git a/src/cocoatweet/api/status/destroy.cc b/src/cocoatweet/api/status/destroy.cc index bee285e..caa0860 100644 --- a/src/cocoatweet/api/status/destroy.cc +++ b/src/cocoatweet/api/status/destroy.cc @@ -11,10 +11,9 @@ void Destroy::id(const std::string _id) { CocoaTweet::API::Model::Tweet Destroy::process( std::weak_ptr _oauth) { CocoaTweet::API::Model::Tweet tweet; - HttpPost::process(_oauth, - [&tweet](const unsigned int _responseCode, const std::string& _rsv) { - tweet = CocoaTweet::API::Model::Tweet::parse(_responseCode, _rsv); - }); + HttpPost::process(_oauth, [&tweet](const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet::parse(_rcv); + }); return tweet; } diff --git a/src/cocoatweet/api/status/retweet.cc b/src/cocoatweet/api/status/retweet.cc index 0f7877d..10203ce 100644 --- a/src/cocoatweet/api/status/retweet.cc +++ b/src/cocoatweet/api/status/retweet.cc @@ -2,20 +2,20 @@ #include namespace CocoaTweet::API::Statuses { - Retweet::Retweet(){} +Retweet::Retweet() {} - void Retweet::id(const std::string& _id){ +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 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); - }); + HttpPost::process(_oauth, [&tweet](const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet(_rcv); + }); return tweet; - } } +} // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/retweet.h b/src/cocoatweet/api/status/retweet.h index 500f5e4..add7a97 100644 --- a/src/cocoatweet/api/status/retweet.h +++ b/src/cocoatweet/api/status/retweet.h @@ -5,14 +5,14 @@ #include namespace CocoaTweet::API::Statuses { - class Retweet : public CocoaTweet::API::Interface::HttpPost{ - public: - Retweet(); +class Retweet : public CocoaTweet::API::Interface::HttpPost { +public: + Retweet(); - void id(const std::string& _id); + void id(const std::string& _id); - CocoaTweet::API::Model::Tweet process(std::weak_ptr _oauth); - }; -} + CocoaTweet::API::Model::Tweet process(std::weak_ptr _oauth); +}; +} // namespace CocoaTweet::API::Statuses #endif \ No newline at end of file diff --git a/src/cocoatweet/api/status/status.cc b/src/cocoatweet/api/status/status.cc index 02d3adf..e2d261a 100644 --- a/src/cocoatweet/api/status/status.cc +++ b/src/cocoatweet/api/status/status.cc @@ -2,6 +2,8 @@ #include "cocoatweet/api/status/update.h" #include "cocoatweet/api/status/destroy.h" #include "cocoatweet/api/status/retweet.h" +#include "cocoatweet/api/status/unretweet.h" +#include "cocoatweet/api/status/userTimeline.h" namespace CocoaTweet::API::Statuses { Status::Status(std::shared_ptr _oauth) { @@ -72,9 +74,22 @@ CocoaTweet::API::Model::Tweet Status::Destroy(const std::string& _id) const { return destroy.process(oauth_); } -CocoaTweet::API::Model::Tweet Status::Retweet(const std::string& _id) const{ +CocoaTweet::API::Model::Tweet Status::Retweet(const std::string& _id) const { CocoaTweet::API::Statuses::Retweet retweet; retweet.id(_id); return retweet.process(oauth_); } + +CocoaTweet::API::Model::Tweet Status::Unretweet(const std::string& _id) const { + CocoaTweet::API::Statuses::Unretweet unretweet; + unretweet.id(_id); + return unretweet.process(oauth_); +} + +std::vector Status::UserTimeline( + const std::string& _screenName) const { + CocoaTweet::API::Statuses::UserTimeline userTimeline; + userTimeline.screenName(_screenName); + return userTimeline.process(oauth_); +} } // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/status.h b/src/cocoatweet/api/status/status.h index 3423cb4..f4d7b7e 100644 --- a/src/cocoatweet/api/status/status.h +++ b/src/cocoatweet/api/status/status.h @@ -61,6 +61,9 @@ public: CocoaTweet::API::Model::Tweet Destroy(const std::string& _id) const; CocoaTweet::API::Model::Tweet Retweet(const std::string& _id) const; + CocoaTweet::API::Model::Tweet Unretweet(const std::string& _id) const; + + std::vector UserTimeline(const std::string& _screenName) const; private: Options defaultOpt_; diff --git a/src/cocoatweet/api/status/unretweet.cc b/src/cocoatweet/api/status/unretweet.cc new file mode 100644 index 0000000..92378da --- /dev/null +++ b/src/cocoatweet/api/status/unretweet.cc @@ -0,0 +1,21 @@ +#include +#include + +namespace CocoaTweet::API::Statuses { +Unretweet::Unretweet() {} + +void Unretweet::id(const std::string& _id) { + contentType_ = "application/x-www-form-urlencoded"; + url_ = "https://api.twitter.com/1.1/statuses/unretweet/" + _id + ".json"; +} + +CocoaTweet::API::Model::Tweet Unretweet::process( + std::weak_ptr _oauth) { + CocoaTweet::API::Model::Tweet tweet; + HttpPost::process(_oauth, [&tweet](const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet(_rcv); + }); + + return tweet; +} +} // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/unretweet.h b/src/cocoatweet/api/status/unretweet.h new file mode 100644 index 0000000..4f2421f --- /dev/null +++ b/src/cocoatweet/api/status/unretweet.h @@ -0,0 +1,18 @@ +#ifndef COCOATWEET_API_STATUS_UNRETWEET_H_ +#define COCOATWEET_API_STATUS_UNRETWEET_H_ + +#include +#include + +namespace CocoaTweet::API::Statuses { +class Unretweet : public CocoaTweet::API::Interface::HttpPost { +public: + Unretweet(); + + void id(const std::string& _id); + + CocoaTweet::API::Model::Tweet process(std::weak_ptr _oauth); +}; +} // namespace CocoaTweet::API::Statuses + +#endif diff --git a/src/cocoatweet/api/status/update.cc b/src/cocoatweet/api/status/update.cc index 3395d65..866ad8b 100644 --- a/src/cocoatweet/api/status/update.cc +++ b/src/cocoatweet/api/status/update.cc @@ -55,10 +55,9 @@ void Update::failDMCommands(bool _fail) { CocoaTweet::API::Model::Tweet Update::process(std::weak_ptr _oauth) { CocoaTweet::API::Model::Tweet tweet; - HttpPost::process(_oauth, - [&tweet](const unsigned int _responseCode, const std::string& _rsv) { - tweet = CocoaTweet::API::Model::Tweet::parse(_responseCode, _rsv); - }); + HttpPost::process(_oauth, [&tweet](const std::string& _rcv) { + tweet = CocoaTweet::API::Model::Tweet::parse(_rcv); + }); return tweet; } diff --git a/src/cocoatweet/api/status/userTimeline.cc b/src/cocoatweet/api/status/userTimeline.cc new file mode 100644 index 0000000..7da4a3f --- /dev/null +++ b/src/cocoatweet/api/status/userTimeline.cc @@ -0,0 +1,27 @@ +#include "cocoatweet/api/status/userTimeline.h" +#include +#include "nlohmann/json.hpp" + +namespace CocoaTweet::API::Statuses { +UserTimeline::UserTimeline() { + contentType_ = "application/x-www-form-urlencoded"; + url_ = "https://api.twitter.com/1.1/statuses/user_timeline.json"; +} + +void UserTimeline::screenName(const std::string& _screenName) { + bodyParam_.insert_or_assign("screen_name", _screenName); +} + +std::vector UserTimeline::process( + std::weak_ptr _oauth) { + std::vector tweet; + HttpGet::process(_oauth, [&tweet](const std::string& _rcv) { + auto json = nlohmann::json::parse(_rcv); + for (auto j : json) { + tweet.push_back(CocoaTweet::API::Model::Tweet::parse(j.dump())); + } + }); + return tweet; +} + +} // namespace CocoaTweet::API::Statuses diff --git a/src/cocoatweet/api/status/userTimeline.h b/src/cocoatweet/api/status/userTimeline.h new file mode 100644 index 0000000..2da1b17 --- /dev/null +++ b/src/cocoatweet/api/status/userTimeline.h @@ -0,0 +1,32 @@ +#ifndef COCOATWEET_API_STATUS_USERTIMELINE_H_ +#define COCOATWEET_API_STATUS_USERTIMELINE_H_ + +#include +#include +#include +#include +#include + +namespace CocoaTweet::API::Statuses { +/// @brief class for using statuses/user_timeline endpoint +class UserTimeline : public CocoaTweet::API::Interface::HttpGet { +public: + /// @brief primary constructor + UserTimeline(); + + /// @brief set screen name to get timeline + /// @param[in] const std::string& _screenName : screen name for getting tweet + void screenName(const std::string& _screenName); + + /// @brief process request for endpoint + /// @param[in] std::weak_ptr _oauth : pointer to oauth object + /// @param[out] std::vector : request result + std::vector process( + std::weak_ptr _oauth); + +private: + std::string status_; +}; +} // namespace CocoaTweet::API::Statuses + +#endif diff --git a/test/api/model/tweet.cc b/test/api/model/tweet.cc index c7450c7..de520d0 100644 --- a/test/api/model/tweet.cc +++ b/test/api/model/tweet.cc @@ -27,70 +27,11 @@ BOOST_AUTO_TEST_CASE(test02) { "source" : "Twitter for Android" })"; - CocoaTweet::API::Model::Tweet tweet(200, json); + CocoaTweet::API::Model::Tweet tweet(json); BOOST_TEST(tweet.id() == "1234567890"); BOOST_TEST(tweet.createdAt() == "Thu Mar 04 00:00:00 +0000 2021"); BOOST_TEST(tweet.text() == "tweet"); BOOST_TEST(tweet.source() == "Twitter for Android"); } -BOOST_AUTO_TEST_CASE(test03) { - std::string json = R"({ - "errors" : [{ - "code" : 32, - "message" : "Could not authenticate you." - }] - })"; - - BOOST_CHECK_THROW(CocoaTweet::API::Model::Tweet(401, json), - CocoaTweet::Exception::AuthenticateException); -} - -BOOST_AUTO_TEST_CASE(test04) { - std::string json = R"({ - "errors" : [{ - "code" : 88, - "message" : "Rate limit exceeded." - }] - })"; - - BOOST_CHECK_THROW(CocoaTweet::API::Model::Tweet(429, json), - CocoaTweet::Exception::RateLimitException); -} - -BOOST_AUTO_TEST_CASE(test05) { - std::string json = R"({ - "errors" : [{ - "code" : 185, - "message" : "User is over daily status update limit." - }] - })"; - - BOOST_CHECK_THROW(CocoaTweet::API::Model::Tweet(403, json), - CocoaTweet::Exception::RateLimitException); -} - -BOOST_AUTO_TEST_CASE(test06) { - std::string json = R"({ - "errors" : [{ - "code" : 186, - "message" : "Tweet needs to be a bit shorter." - }] - })"; - - BOOST_CHECK_THROW(CocoaTweet::API::Model::Tweet(403, json), - CocoaTweet::Exception::TweetTooLongException); -} - -BOOST_AUTO_TEST_CASE(test07) { - std::string json = R"({ - "errors" : [{ - "code" : 187, - "message" : "Status is a duplicate." - }] - })"; - - BOOST_CHECK_THROW(CocoaTweet::API::Model::Tweet(403, json), - CocoaTweet::Exception::TweetDuplicateException); -} BOOST_AUTO_TEST_SUITE_END()