From aceb3cc33ee78cce1077252aff8a8808a3195ff1 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Tue, 31 May 2016 09:55:58 -0400 Subject: Added ability to get configuration --- CMakeLists.txt | 2 +- src/client.cpp | 16 ++++++++ src/client.h | 6 +++ src/configuration.cpp | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/configuration.h | 52 ++++++++++++++++++++++++ src/twitter.h | 1 + 6 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/configuration.cpp create mode 100644 src/configuration.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 29a0a75..a0a1b0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ include_directories(vendor/liboauthcpp/include) add_subdirectory(vendor/curlcpp) include_directories(${CURLCPP_SOURCE_DIR}/include) -add_library(twitter++ src/client.cpp src/auth.cpp src/tweet.cpp src/codes.cpp src/notification.cpp src/direct_message.cpp src/list.cpp src/user.cpp) +add_library(twitter++ src/client.cpp src/auth.cpp src/tweet.cpp src/codes.cpp src/notification.cpp src/direct_message.cpp src/list.cpp src/user.cpp src/configuration.cpp) set_property(TARGET twitter++ PROPERTY CXX_STANDARD 11) set_property(TARGET twitter++ PROPERTY CXX_STANDARD_REQUIRED ON) target_link_libraries(twitter++ oauthcpp curlcpp curl pthread) diff --git a/src/client.cpp b/src/client.cpp index b7d1624..cf2b5c4 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -321,6 +321,22 @@ namespace twitter { return _current_user; } + configuration client::getConfiguration() + { + if (!_configuration || (difftime(time(NULL), _last_configuration_update) > 60*60*24)) + { + long response_code; + std::string response_data; + if (performGet("https://api.twitter.com/1.1/help/configuration.json", response_code, response_data)) + { + _configuration = configuration(response_data); + _last_configuration_update = time(NULL); + } + } + + return _configuration; + } + response client::getFriends(std::set& _ret) { if (!_current_user) diff --git a/src/client.h b/src/client.h index c1c6344..f96022d 100644 --- a/src/client.h +++ b/src/client.h @@ -11,6 +11,7 @@ #include #include #include +#include "configuration.h" namespace OAuth { class Consumer; @@ -81,6 +82,8 @@ namespace twitter { const user& getUser() const; + configuration getConfiguration(); + // NOTE: stream setting function calls will fail silently when stream is running void setUserStreamNotifyCallback(stream::notify_callback callback); void setUserStreamReceiveAllReplies(bool _arg); @@ -99,6 +102,9 @@ namespace twitter { user _current_user; stream _user_stream{*this}; + configuration _configuration; + time_t _last_configuration_update; + bool performGet(std::string url, long& response_code, std::string& result); bool performPost(std::string url, std::string dataStr, long& response_code, std::string& result); bool performMultiPost(std::string url, const curl_httppost* fields, long& response_code, std::string& result); diff --git a/src/configuration.cpp b/src/configuration.cpp new file mode 100644 index 0000000..63464ed --- /dev/null +++ b/src/configuration.cpp @@ -0,0 +1,109 @@ +#include "configuration.h" +#include +#include + +using nlohmann::json; + +namespace twitter { + + configuration::configuration() + { + _valid = false; + } + + configuration::configuration(std::string data) + { + _valid = true; + + auto _data = json::parse(data); + + _characters_reserved_per_media = _data.at("characters_reserved_per_media"); + _dm_text_character_limit = _data.at("dm_text_character_limit"); + _max_media_per_upload = _data.at("max_media_per_upload"); + _photo_size_limit = _data.at("photo_size_limit"); + _short_url_length = _data.at("short_url_length"); + _short_https_url_length = _data.at("short_url_length_https"); + + for (json::iterator sizedata = _data.at("photo_sizes").begin(); sizedata != _data.at("photo_sizes").end(); ++sizedata) + { + photosize size; + size.height = sizedata.value().at("h"); + size.width = sizedata.value().at("w"); + if (sizedata.value().at("resize") == "fit") + { + size.resize = resizetype::fit; + } else { + size.resize = resizetype::crop; + } + + _photo_sizes[sizedata.key()] = size; + } + + for (auto path : _data.at("non_username_paths")) + { + _non_username_paths.insert(path.get()); + } + } + + size_t configuration::getCharactersReservedPerMedia() const + { + assert(_valid); + + return _characters_reserved_per_media; + } + + size_t configuration::getDirectMessageCharacterLimit() const + { + assert(_valid); + + return _dm_text_character_limit; + } + + size_t configuration::getMaxMediaPerUpload() const + { + assert(_valid); + + return _max_media_per_upload; + } + + size_t configuration::getPhotoSizeLimit() const + { + assert(_valid); + + return _photo_size_limit; + } + + std::map configuration::getPhotoSizes() const + { + assert(_valid); + + return _photo_sizes; + } + + size_t configuration::getShortUrlLength() const + { + assert(_valid); + + return _short_url_length; + } + + size_t configuration::getShortHttpsUrlLength() const + { + assert(_valid); + + return _short_https_url_length; + } + + std::set configuration::getNonUsernamePaths() const + { + assert(_valid); + + return _non_username_paths; + } + + configuration::operator bool() const + { + return _valid; + } + +}; diff --git a/src/configuration.h b/src/configuration.h new file mode 100644 index 0000000..a15be3d --- /dev/null +++ b/src/configuration.h @@ -0,0 +1,52 @@ +#ifndef CONFIGURATION_H_A7164D18 +#define CONFIGURATION_H_A7164D18 + +#include +#include +#include + +namespace twitter { + + class configuration { + public: + enum class resizetype { + fit, + crop + }; + + struct photosize { + size_t height; + size_t width; + resizetype resize; + }; + + configuration(); + configuration(std::string data); + + size_t getCharactersReservedPerMedia() const; + size_t getDirectMessageCharacterLimit() const; + size_t getMaxMediaPerUpload() const; + size_t getPhotoSizeLimit() const; + std::map getPhotoSizes() const; + size_t getShortUrlLength() const; + size_t getShortHttpsUrlLength() const; + std::set getNonUsernamePaths() const; + + operator bool() const; + + private: + bool _valid = false; + + size_t _characters_reserved_per_media; + size_t _dm_text_character_limit; + size_t _max_media_per_upload; + size_t _photo_size_limit; + std::map _photo_sizes; + size_t _short_url_length; + size_t _short_https_url_length; + std::set _non_username_paths; + }; + +}; + +#endif /* end of include guard: CONFIGURATION_H_A7164D18 */ diff --git a/src/twitter.h b/src/twitter.h index d0b469e..90925df 100644 --- a/src/twitter.h +++ b/src/twitter.h @@ -16,5 +16,6 @@ namespace twitter { #include "notification.h" #include "list.h" #include "direct_message.h" +#include "configuration.h" #endif /* end of include guard: TWITTER_H_AC7A7666 */ -- cgit 1.4.1 From 442f1ee071152be04c4184473ddfee5040795b76 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Wed, 1 Jun 2016 20:34:33 -0400 Subject: Wrapped JSON parsing in exception handling --- src/client.cpp | 88 ++++++++++-- src/client.h | 2 +- src/codes.h | 3 +- src/notification.cpp | 375 ++++++++++++++++++++++++++------------------------- 4 files changed, 270 insertions(+), 198 deletions(-) diff --git a/src/client.cpp b/src/client.cpp index cf2b5c4..1b32b8b 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -105,7 +105,12 @@ namespace twitter { std::string response_data; if (performGet(url, response_code, response_data) && (response_code == 200)) { - _current_user = user(response_data); + try { + _current_user = user(response_data); + } catch (std::invalid_argument e) + { + // Ignore + } } } @@ -145,8 +150,13 @@ namespace twitter { if (response_code == 200) { - result = tweet(response_data); - return response::ok; + try { + result = tweet(response_data); + return response::ok; + } catch (std::invalid_argument e) + { + return response::invalid_response; + } } else { return codeForError(response_code, response_data); } @@ -186,7 +196,14 @@ namespace twitter { return codeForError(response_code, response_data); } - auto response_json = json::parse(response_data); + json response_json; + try { + response_json = json::parse(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } + media_id = response_json["media_id"].get(); curl_httppost* append_form_post = nullptr; @@ -226,7 +243,13 @@ namespace twitter { return codeForError(response_code, response_data); } - response_json = json::parse(response_data); + try { + response_json = json::parse(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } + if (response_json.find("processing_info") != response_json.end()) { std::stringstream datastr; @@ -244,7 +267,13 @@ namespace twitter { return codeForError(response_code, response_data); } - response_json = json::parse(response_data); + try { + response_json = json::parse(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } + if (response_json["processing_info"]["state"] == "succeeded") { break; @@ -316,9 +345,26 @@ namespace twitter { return unfollow(toUnfollow.getID()); } - const user& client::getUser() const + response client::getUser(user& result) { - return _current_user; + if (!_current_user) + { + std::string url = "https://api.twitter.com/1.1/account/verify_credentials.json"; + long response_code; + std::string response_data; + if (performGet(url, response_code, response_data) && (response_code == 200)) + { + try { + _current_user = user(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } + } + } + + result = _current_user; + return response::ok; } configuration client::getConfiguration() @@ -366,7 +412,14 @@ namespace twitter { if (response_code == 200) { - json rjs = json::parse(response_data); + json rjs; + try { + rjs = json::parse(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } + cursor = rjs.at("next_cursor"); result.insert(std::begin(rjs.at("ids")), std::end(rjs.at("ids"))); } else { @@ -408,7 +461,14 @@ namespace twitter { if (response_code == 200) { - json rjs = json::parse(response_data); + json rjs; + try { + rjs = json::parse(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } + cursor = rjs.at("next_cursor"); result.insert(std::begin(rjs.at("ids")), std::end(rjs.at("ids"))); } else { @@ -560,7 +620,13 @@ namespace twitter { response client::codeForError(int response_code, std::string response_data) const { - auto response_json = json::parse(response_data); + json response_json; + try { + response_json = json::parse(response_data); + } catch (std::invalid_argument e) + { + return response::invalid_response; + } std::set error_codes; if (response_json.find("errors") != response_json.end()) diff --git a/src/client.h b/src/client.h index f96022d..6963412 100644 --- a/src/client.h +++ b/src/client.h @@ -80,7 +80,7 @@ namespace twitter { response getFriends(std::set& result); response getFollowers(std::set& result); - const user& getUser() const; + response getUser(user& result); configuration getConfiguration(); diff --git a/src/codes.h b/src/codes.h index 334f0ce..6767805 100644 --- a/src/codes.h +++ b/src/codes.h @@ -21,7 +21,8 @@ namespace twitter { write_restricted, bad_length, unknown_error, - invalid_media + invalid_media, + invalid_response }; }; diff --git a/src/notification.cpp b/src/notification.cpp index 3dcdd90..0397ce5 100644 --- a/src/notification.cpp +++ b/src/notification.cpp @@ -19,240 +19,245 @@ namespace twitter { notification::notification(std::string data, const user& current_user) { - auto _data = json::parse(data); + try { + auto _data = json::parse(data); - if (_data.find("in_reply_to_status_id") != _data.end()) - { - _type = type::tweet; - - new(&_tweet) tweet(data); - } else if (_data.find("event") != _data.end()) - { - std::string event = _data.at("event"); - user source(_data.at("source").dump()); - user target(_data.at("target").dump()); - - if (event == "user_update") + if (_data.find("in_reply_to_status_id") != _data.end()) { - _type = type::update_user; - - new(&_user) user(source); - } else if (event == "block") + _type = type::tweet; + + new(&_tweet) tweet(data); + } else if (_data.find("event") != _data.end()) { - _type = type::block; + std::string event = _data.at("event"); + user source(_data.at("source").dump()); + user target(_data.at("target").dump()); + + if (event == "user_update") + { + _type = type::update_user; - new(&_user) user(target); - } else if (event == "unblock") - { - _type = type::unblock; + new(&_user) user(source); + } else if (event == "block") + { + _type = type::block; - new(&_user) user(target); - } else if (event == "favorite") - { - new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump()); + new(&_user) user(target); + } else if (event == "unblock") + { + _type = type::unblock; - if (current_user == source) + new(&_user) user(target); + } else if (event == "favorite") { - _type = type::favorite; + new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump()); + + if (current_user == source) + { + _type = type::favorite; - new(&_user_and_tweet._user) user(target); - } else { - _type = type::favorited; + new(&_user_and_tweet._user) user(target); + } else { + _type = type::favorited; - new(&_user_and_tweet._user) user(source); - } - } else if (event == "unfavorite") - { - new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump()); - - if (current_user == source) + new(&_user_and_tweet._user) user(source); + } + } else if (event == "unfavorite") { - _type = type::unfavorite; + new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump()); + + if (current_user == source) + { + _type = type::unfavorite; - new(&_user_and_tweet._user) user(target); - } else { - _type = type::unfavorited; + new(&_user_and_tweet._user) user(target); + } else { + _type = type::unfavorited; - new(&_user_and_tweet._user) user(source); - } - } else if (event == "follow") - { - if (current_user == source) + new(&_user_and_tweet._user) user(source); + } + } else if (event == "follow") { - _type = type::follow; + if (current_user == source) + { + _type = type::follow; - new(&_user) user(target); - } else { - _type = type::followed; + new(&_user) user(target); + } else { + _type = type::followed; - new(&_user) user(source); - } - } else if (event == "unfollow") - { - _type = type::unfollow; - - new(&_user) user(target); - } else if (event == "list_created") - { - _type = type::list_created; + new(&_user) user(source); + } + } else if (event == "unfollow") + { + _type = type::unfollow; - new(&_list) list(_data.at("target_object").dump()); - } else if (event == "list_destroyed") - { - _type = type::list_destroyed; + new(&_user) user(target); + } else if (event == "list_created") + { + _type = type::list_created; - new(&_list) list(_data.at("target_object").dump()); - } else if (event == "list_updated") - { - _type = type::list_updated; + new(&_list) list(_data.at("target_object").dump()); + } else if (event == "list_destroyed") + { + _type = type::list_destroyed; - new(&_list) list(_data.at("target_object").dump()); - } else if (event == "list_member_added") - { - new(&_user_and_list._list) list(_data.at("target_object").dump()); + new(&_list) list(_data.at("target_object").dump()); + } else if (event == "list_updated") + { + _type = type::list_updated; - if (current_user == source) + new(&_list) list(_data.at("target_object").dump()); + } else if (event == "list_member_added") { - _type = type::list_add; + new(&_user_and_list._list) list(_data.at("target_object").dump()); + + if (current_user == source) + { + _type = type::list_add; - new(&_user_and_list._user) user(target); - } else { - _type = type::list_added; + new(&_user_and_list._user) user(target); + } else { + _type = type::list_added; - new(&_user_and_list._user) user(source); - } - } else if (event == "list_member_removed") - { - new(&_user_and_list._list) list(_data.at("target_object").dump()); - - if (current_user == source) + new(&_user_and_list._user) user(source); + } + } else if (event == "list_member_removed") { - _type = type::list_remove; + new(&_user_and_list._list) list(_data.at("target_object").dump()); + + if (current_user == source) + { + _type = type::list_remove; - new(&_user_and_list._user) user(target); - } else { - _type = type::list_removed; + new(&_user_and_list._user) user(target); + } else { + _type = type::list_removed; - new(&_user_and_list._user) user(source); - } - } else if (event == "list_member_subscribe") - { - new(&_user_and_list._list) list(_data.at("target_object").dump()); + new(&_user_and_list._user) user(source); + } + } else if (event == "list_member_subscribe") + { + new(&_user_and_list._list) list(_data.at("target_object").dump()); - if (current_user == source) + if (current_user == source) + { + _type = type::list_subscribe; + + new(&_user_and_list._user) user(target); + } else { + _type = type::list_subscribed; + + new(&_user_and_list._user) user(source); + } + } else if (event == "list_member_unsubscribe") { - _type = type::list_subscribe; + new(&_user_and_list._list) list(_data.at("target_object").dump()); + + if (current_user == source) + { + _type = type::list_unsubscribe; - new(&_user_and_list._user) user(target); - } else { - _type = type::list_subscribed; + new(&_user_and_list._user) user(target); + } else { + _type = type::list_unsubscribed; - new(&_user_and_list._user) user(source); + new(&_user_and_list._user) user(source); + } + } else if (event == "quoted_tweet") + { + _type = type::quoted; + + new(&_user_and_tweet._user) user(source); + new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump()); } - } else if (event == "list_member_unsubscribe") + } else if (_data.find("warning") != _data.end()) { - new(&_user_and_list._list) list(_data.at("target_object").dump()); - - if (current_user == source) + new(&_warning) std::string(_data.at("warning").at("message").get()); + + if (_data.at("warning").at("code") == "FALLING_BEHIND") { - _type = type::list_unsubscribe; - - new(&_user_and_list._user) user(target); + _type = type::stall; + } else if (_data.at("warning").at("code") == "FOLLOWS_OVER_LIMIT") + { + _type = type::follow_limit; } else { - _type = type::list_unsubscribed; - - new(&_user_and_list._user) user(source); + _type = type::unknown_warning; } - } else if (event == "quoted_tweet") + } else if (_data.find("delete") != _data.end()) { - _type = type::quoted; - - new(&_user_and_tweet._user) user(source); - new(&_user_and_tweet._tweet) tweet(_data.at("target_object").dump()); - } - } else if (_data.find("warning") != _data.end()) - { - new(&_warning) std::string(_data.at("warning").at("message").get()); + _type = type::deletion; - if (_data.at("warning").at("code") == "FALLING_BEHIND") + _user_id_and_tweet_id._tweet_id = _data.at("delete").at("status").at("id"); + _user_id_and_tweet_id._user_id = _data.at("delete").at("status").at("user_id"); + } else if (_data.find("scrub_geo") != _data.end()) { - _type = type::stall; - } else if (_data.at("warning").at("code") == "FOLLOWS_OVER_LIMIT") + _type = type::scrub_location; + + _user_id_and_tweet_id._tweet_id = _data.at("scrub_geo").at("up_to_status_id"); + _user_id_and_tweet_id._user_id = _data.at("scrub_geo").at("user_id"); + } else if (_data.find("limit") != _data.end()) { - _type = type::follow_limit; - } else { - _type = type::unknown_warning; - } - } else if (_data.find("delete") != _data.end()) - { - _type = type::deletion; + _type = type::limit; - _user_id_and_tweet_id._tweet_id = _data.at("delete").at("status").at("id"); - _user_id_and_tweet_id._user_id = _data.at("delete").at("status").at("user_id"); - } else if (_data.find("scrub_geo") != _data.end()) - { - _type = type::scrub_location; + _limit = _data.at("limit").at("track"); + } else if (_data.find("status_withheld") != _data.end()) + { + _type = type::withhold_status; - _user_id_and_tweet_id._tweet_id = _data.at("scrub_geo").at("up_to_status_id"); - _user_id_and_tweet_id._user_id = _data.at("scrub_geo").at("user_id"); - } else if (_data.find("limit") != _data.end()) - { - _type = type::limit; + _withhold_status._user_id = _data.at("status_withheld").at("user_id"); + _withhold_status._tweet_id = _data.at("status_withheld").at("id"); - _limit = _data.at("limit").at("track"); - } else if (_data.find("status_withheld") != _data.end()) - { - _type = type::withhold_status; + new(&_withhold_status._countries) std::vector(); + for (auto s : _data.at("status_withheld").at("withheld_in_countries")) + { + _withhold_status._countries.push_back(s); + } + } else if (_data.find("user_withheld") != _data.end()) + { + _type = type::withhold_user; - _withhold_status._user_id = _data.at("status_withheld").at("user_id"); - _withhold_status._tweet_id = _data.at("status_withheld").at("id"); + _withhold_user._user_id = _data.at("user_withheld").at("id"); - new(&_withhold_status._countries) std::vector(); - for (auto s : _data.at("status_withheld").at("withheld_in_countries")) + new(&_withhold_user._countries) std::vector(); + for (auto s : _data.at("user_withheld").at("withheld_in_countries")) + { + _withhold_user._countries.push_back(s); + } + } else if (_data.find("disconnect") != _data.end()) { - _withhold_status._countries.push_back(s); - } - } else if (_data.find("user_withheld") != _data.end()) - { - _type = type::withhold_user; + _type = type::disconnect; - _withhold_user._user_id = _data.at("user_withheld").at("id"); + switch (_data.at("disconnect").at("code").get()) + { + case 1: _disconnect = disconnect_code::shutdown; break; + case 2: _disconnect = disconnect_code::duplicate; break; + case 4: _disconnect = disconnect_code::stall; break; + case 5: _disconnect = disconnect_code::normal; break; + case 6: _disconnect = disconnect_code::token_revoked; break; + case 7: _disconnect = disconnect_code::admin_logout; break; + case 9: _disconnect = disconnect_code::limit; break; + case 10: _disconnect = disconnect_code::exception; break; + case 11: _disconnect = disconnect_code::broker; break; + case 12: _disconnect = disconnect_code::load; break; + default: _disconnect = disconnect_code::unknown; + } + } else if (_data.find("friends") != _data.end()) + { + _type = type::friends; - new(&_withhold_user._countries) std::vector(); - for (auto s : _data.at("user_withheld").at("withheld_in_countries")) + new(&_friends) std::set(_data.at("friends").begin(), _data.at("friends").end()); + } else if (_data.find("direct_message") != _data.end()) { - _withhold_user._countries.push_back(s); - } - } else if (_data.find("disconnect") != _data.end()) - { - _type = type::disconnect; - - switch (_data.at("disconnect").at("code").get()) - { - case 1: _disconnect = disconnect_code::shutdown; break; - case 2: _disconnect = disconnect_code::duplicate; break; - case 4: _disconnect = disconnect_code::stall; break; - case 5: _disconnect = disconnect_code::normal; break; - case 6: _disconnect = disconnect_code::token_revoked; break; - case 7: _disconnect = disconnect_code::admin_logout; break; - case 9: _disconnect = disconnect_code::limit; break; - case 10: _disconnect = disconnect_code::exception; break; - case 11: _disconnect = disconnect_code::broker; break; - case 12: _disconnect = disconnect_code::load; break; - default: _disconnect = disconnect_code::unknown; - } - } else if (_data.find("friends") != _data.end()) - { - _type = type::friends; + _type = type::direct; - new(&_friends) std::set(_data.at("friends").begin(), _data.at("friends").end()); - } else if (_data.find("direct_message") != _data.end()) + new(&_direct_message) direct_message(_data.at("direct_message").dump()); + } else { + _type = type::unknown; + } + } catch (std::invalid_argument e) { - _type = type::direct; - - new(&_direct_message) direct_message(_data.at("direct_message").dump()); - } else { - _type = type::unknown; + _type = type::invalid; } } -- cgit 1.4.1