From cdd84a443d08a4f5748b52b102ce17dfd267acc5 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Wed, 29 Apr 2015 17:27:23 +0000 Subject: twitter moved to the kernel json driver git-svn-id: http://svn.miranda-ng.org/main/trunk@13248 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Twitter/src/twitter.cpp | 334 +++++++++++++------------------------- 1 file changed, 115 insertions(+), 219 deletions(-) (limited to 'protocols/Twitter/src/twitter.cpp') diff --git a/protocols/Twitter/src/twitter.cpp b/protocols/Twitter/src/twitter.cpp index 1a762ec08d..06ac5f8792 100644 --- a/protocols/Twitter/src/twitter.cpp +++ b/protocols/Twitter/src/twitter.cpp @@ -18,57 +18,10 @@ along with this program. If not, see . #include "stdafx.h" #include "twitter.h" - -#include "tinyjson.hpp" -#include - -typedef json::grammar js; +#include "utility.h" // utility functions -template -static T cast_and_decode(boost::any &a, bool allow_null) -{ - if (allow_null && a.type() == typeid(void)) - return T(); - return boost::any_cast(a); -} - -template <> -static std::string cast_and_decode(boost::any &a, bool allow_null) -{ - if (allow_null && a.type() == typeid(void)) - return std::string(); - std::string s = boost::any_cast(a); - - // Twitter *only* encodes < and >, so decode them - size_t off; - while ((off = s.find("<")) != std::string::npos) - s.replace(off, 4, "<"); - while ((off = s.find(">")) != std::string::npos) - s.replace(off, 4, ">"); - - return s; -} - -template -static T retrieve(const js::object &o, const std::string &key, bool allow_null = false) -{ - using boost::any_cast; - - js::object::const_iterator i = o.find(key); - if (i == o.end()) - throw std::exception(("unable to retrieve key '" + key + "'").c_str()); - try { - return cast_and_decode(*i->second, allow_null); - } - catch (const boost::bad_any_cast &) { - throw std::exception(("unable to cast key '" + key + "' to target type").c_str()); - } -} - - - twitter::twitter() : base_url_("https://api.twitter.com/") {} @@ -113,85 +66,39 @@ const std::string & twitter::get_base_url() const return base_url_; } -// this whole function is wrong i think. should be calling friends/ids, not followers -/*js::array twitter::buildFriendList() { - - INT_PTR friendCursor = -1; - js::array IDs; // an array for the userIDs. i dunno if js::array is the right thing to use..? - js::array masterIDs; // the list that contains all the users that the user follows - - std::vector friends; - - while (friendCursor != 0) { - http::response resp = slurp(base_url_ + "/1.1/followers/ids.json?cursor=" + friendCursor + "&screen_name=" + username_,http::get); - if(resp.code != 200) - throw bad_response(); - - const js::variant var = json::parse( resp.data.begin(),resp.data.end() ); // pull the data out of the http response - if(var->type() == typeid(js::object)) // make sure the parsed data is of type js::object (??) - { - const js::object &friendIDs = boost::any_cast(*var); // cast the object into the type we can use - if(friendIDs.find("error") != friendIDs.end()) // don't really know why error should be at the end here? - throw std::exception("error while parsing friendIDs object from ids.json"); - - // ok need to find out how to convert all the IDs into an array. dunno if i can magically make it happen, or - // if i will have to parse it myself and add them one by one :( - IDs = retrieve(friendIDs,"ids"); - for(js::array::const_iterator i=IDs.begin(); i!=IDs.end(); ++i) { - //debugLogA("friends ID: " + i); - // add array to master array - js::object one = boost::any_cast(**i); - masterIDs.push_back(one); // i don't understand this. how do we push into the array? should i just use C++ arrays (list?) and bail on boost? - } - - // now we need to pick out the cursor stuff, and keep punching IDs into the array - } - else { - throw std::exception("in buildFriendList(), return type is not js::object"); - } - } - - - }*/ - - std::vector twitter::get_friends() { - // maybe once i have the buildFriendLIst() func working.. but for now let's just get twitter working. - //js::array friendArray = buildFriendList(); - std::vector friends; http::response resp = slurp(base_url_ + "1.1/statuses/friends.json", http::get); if (resp.code != 200) throw bad_response(); - const js::variant var = json::parse(resp.data.begin(), resp.data.end()); - if (var->type() != typeid(js::array)) + JSONNODE *root = json_parse(resp.data.c_str()); + if (root == NULL) throw std::exception("unable to parse response"); - const js::array &list = boost::any_cast(*var); - for (js::array::const_iterator i = list.begin(); i != list.end(); ++i) { - if ((*i)->type() == typeid(js::object)) { - const js::object &one = boost::any_cast(**i); + JSONNODE *nodes = json_as_array(root); + for (int i = 0;; i++) { + JSONNODE *pNode = json_at(nodes, i); + if (pNode == NULL) + break; - twitter_user user; - user.username = retrieve(one, "screen_name"); - user.real_name = retrieve(one, "name", true); - user.profile_image_url = retrieve(one, "profile_image_url", true); + twitter_user user; + user.username = json_as_pstring(json_get(pNode, "screen_name")); + user.real_name = json_as_pstring(json_get(pNode, "name")); + user.profile_image_url = json_as_pstring(json_get(pNode, "profile_image_url")); - if (one.find("status") != one.end()) { - js::object &status = retrieve(one, "status"); - user.status.text = retrieve(status, "text"); + JSONNODE *pStatus = json_get(pNode, "status"); + if (pStatus != NULL) { + user.status.text = json_as_pstring(json_get(pStatus, "text")); + user.status.id = json_as_int(json_get(pStatus, "id")); - user.status.id = retrieve(status, "id"); - - std::string timestr = retrieve(status, "created_at"); - user.status.time = parse_time(timestr); - } - - friends.push_back(user); + std::string timestr = json_as_pstring(json_get(pStatus, "created_at")); + user.status.time = parse_time(timestr); } + + friends.push_back(user); } return friends; @@ -208,20 +115,17 @@ bool twitter::get_info(const std::string &name, twitter_user *info) if (resp.code != 200) throw bad_response(); - const js::variant var = json::parse(resp.data.begin(), resp.data.end()); - if (var->type() == typeid(js::object)) { - const js::object &user_info = boost::any_cast(*var); - if (user_info.find("error") != user_info.end()) - return false; - - info->username = retrieve(user_info, "screen_name"); - info->real_name = retrieve(user_info, "name", true); - info->profile_image_url = retrieve(user_info, "profile_image_url", true); + JSONNODE *root = json_parse(resp.data.c_str()); + if (root == NULL) + return false; - return true; - } - else + if (json_get(root, "error") != NULL) return false; + + info->username = json_as_pstring(json_get(root, "screen_name")); + info->real_name = json_as_pstring(json_get(root, "name")); + info->profile_image_url = json_as_pstring(json_get(root, "profile_image_url")); + return true; } bool twitter::get_info_by_email(const std::string &email, twitter_user *info) @@ -235,44 +139,40 @@ bool twitter::get_info_by_email(const std::string &email, twitter_user *info) if (resp.code != 200) throw bad_response(); - js::variant var = json::parse(resp.data.begin(), resp.data.end()); - if (var->type() == typeid(js::object)) { - const js::object &user_info = boost::any_cast(*var); - if (user_info.find("error") != user_info.end()) - return false; - - info->username = retrieve(user_info, "screen_name"); - info->real_name = retrieve(user_info, "name", true); - info->profile_image_url = retrieve(user_info, "profile_image_url", true); + JSONNODE *root = json_parse(resp.data.c_str()); + if (root == NULL) + return false; - return true; - } - else + if (json_get(root, "error") != NULL) return false; + + info->username = json_as_pstring(json_get(root, "screen_name")); + info->real_name = json_as_pstring(json_get(root, "name")); + info->profile_image_url = json_as_pstring(json_get(root, "profile_image_url")); + return true; } twitter_user twitter::add_friend(const std::string &name) { std::string url = base_url_ + "1.1/friendships/create/" + http::url_encode(name) + ".json"; - twitter_user ret; http::response resp = slurp(url, http::post); if (resp.code != 200) throw bad_response(); - js::variant var = json::parse(resp.data.begin(), resp.data.end()); - if (var->type() != typeid(js::object)) + JSONNODE *root = json_parse(resp.data.c_str()); + if (root == NULL) throw std::exception("unable to parse response"); - const js::object &user_info = boost::any_cast(*var); - ret.username = retrieve(user_info, "screen_name"); - ret.real_name = retrieve(user_info, "name", true); - ret.profile_image_url = retrieve(user_info, "profile_image_url", true); - - if (user_info.find("status") != user_info.end()) { - // TODO: fill in more fields - const js::object &status = retrieve(user_info, "status"); - ret.status.text = retrieve(status, "text"); + twitter_user ret; + ret.username = json_as_pstring(json_get(root, "screen_name")); + ret.real_name = json_as_pstring(json_get(root, "name")); + ret.profile_image_url = json_as_pstring(json_get(root, "profile_image_url")); + + JSONNODE *pStatus = json_get(root, "status"); + if (pStatus != NULL) { + ret.status.text = json_as_pstring(json_get(pStatus, "text")); + ret.status.id = json_as_int(json_get(pStatus, "id")); } return ret; @@ -307,70 +207,67 @@ void twitter::send_direct(const std::string &name, const std::string &text) std::vector twitter::get_statuses(int count, twitter_id id) { - using boost::lexical_cast; std::vector statuses; std::string url = base_url_ + "1.1/statuses/home_timeline.json?count=" + - lexical_cast(count); + int2str(count); if (id != 0) - url += "&since_id=" + boost::lexical_cast(id); + url += "&since_id=" + int2str(id); http::response resp = slurp(url, http::get); if (resp.code != 200) throw bad_response(); - js::variant var = json::parse(resp.data.begin(), resp.data.end()); - if (var->type() != typeid(js::array)) + JSONNODE *root = json_parse(resp.data.c_str()); + if (root == NULL) throw std::exception("unable to parse response"); - const js::array &list = boost::any_cast(*var); - for (js::array::const_iterator i = list.begin(); i != list.end(); ++i) { - if ((*i)->type() == typeid(js::object)) { - const js::object &one = boost::any_cast(**i); - const js::object &user = retrieve(one, "user"); - - twitter_user u; - u.username = retrieve(user, "screen_name"); - u.profile_image_url = retrieve(user, "profile_image_url"); - - // the tweet will be truncated unless we take action. i hate you twitter API - if (one.find("retweeted_status") != one.end()) { - //MessageBox(NULL, L"retweeted: TRUE", L"long tweets", MB_OK); - // here we grab the "retweeted_status" um.. section? it's in here that all the info we need is - // at this point the user will get no tweets and an error popup if the tweet happens to be exactly 140 chars, start with - // "RT @", end in " ...", and notactually be a real retweet. it's possible but unlikely, wish i knew how to get - // the retweet_count variable to work :( - const js::object &Retweet = retrieve(one, "retweeted_status"); - const js::object &RTUser = retrieve(Retweet, "user"); - - std::string retweeteesName = retrieve(RTUser, "screen_name"); // the user that is being retweeted - std::string retweetText = retrieve(Retweet, "text"); // their tweet in all it's untruncated glory - - // fix "&" in the tweets :( - for (size_t pos = 0; (pos = retweetText.find("&", pos)) != std::string::npos; pos++) { - retweetText.replace(pos, 5, "&"); - } - - u.status.text = "RT @" + retweeteesName + " " + retweetText; // mash it together in some format people will understand - } - else { - // if it's not truncated, then the twitter API returns the native RT correctly anyway, - std::string rawText = retrieve(one, "text"); - // ok here i'm trying some way to fix all the "&" things that are showing up - // i dunno why it's happening, so i'll just find and replace each occurance :/ - for (size_t pos = 0; (pos = rawText.find("&", pos)) != std::string::npos; pos++) { - rawText.replace(pos, 5, "&"); - } - - u.status.text = rawText; - } - - u.status.id = retrieve(one, "id"); - std::string timestr = retrieve(one, "created_at"); - u.status.time = parse_time(timestr); - - statuses.push_back(u); + JSONNODE *pNodes = json_as_array(root); + for (int i = 0;; i++) { + JSONNODE *pNode = json_at(pNodes, i); + if (pNode == NULL) + break; + + JSONNODE *pUser = json_get(pNode, "user"); + + twitter_user u; + u.username = json_as_pstring(json_get(pUser, "screen_name")); + u.profile_image_url = json_as_pstring(json_get(pUser, "profile_image_url")); + + // the tweet will be truncated unless we take action. i hate you twitter API + JSONNODE *pStatus = json_get(pNode, "retweeted_status"); + if (pStatus != NULL) { + // here we grab the "retweeted_status" um.. section? it's in here that all the info we need is + // at this point the user will get no tweets and an error popup if the tweet happens to be exactly 140 chars, start with + // "RT @", end in " ...", and notactually be a real retweet. it's possible but unlikely, wish i knew how to get + // the retweet_count variable to work :( + JSONNODE *pUser2 = json_get(pStatus, "user"); + + std::string retweeteesName = json_as_pstring(json_get(pUser2, "screen_name")); // the user that is being retweeted + std::string retweetText = json_as_pstring(json_get(pUser2, "text")); // their tweet in all it's untruncated glory + + // fix "&" in the tweets :( + for (size_t pos = 0; (pos = retweetText.find("&", pos)) != std::string::npos; pos++) + retweetText.replace(pos, 5, "&"); + + u.status.text = "RT @" + retweeteesName + " " + retweetText; // mash it together in some format people will understand + } + else { + // if it's not truncated, then the twitter API returns the native RT correctly anyway, + std::string rawText = json_as_pstring(json_get(pNode, "text")); + // ok here i'm trying some way to fix all the "&" things that are showing up + // i dunno why it's happening, so i'll just find and replace each occurance :/ + for (size_t pos = 0; (pos = rawText.find("&", pos)) != std::string::npos; pos++) + rawText.replace(pos, 5, "&"); + + u.status.text = rawText; } + + u.status.id = json_as_int(json_get(pNode, "id")); + std::string timestr = json_as_pstring(json_get(pNode, "created_at")); + u.status.time = parse_time(timestr); + + statuses.push_back(u); } @@ -383,31 +280,30 @@ std::vector twitter::get_direct(twitter_id id) std::string url = base_url_ + "1.1/direct_messages.json"; if (id != 0) - url += "?since_id=" + boost::lexical_cast(id); + url += "?since_id=" + int2str(id); http::response resp = slurp(url, http::get); if (resp.code != 200) throw bad_response(); - js::variant var = json::parse(resp.data.begin(), resp.data.end()); - if (var->type() != typeid(js::array)) + JSONNODE *root = json_parse(resp.data.c_str()); + if (root == NULL) throw std::exception("unable to parse response"); - const js::array &list = boost::any_cast(*var); - for (js::array::const_iterator i = list.begin(); i != list.end(); ++i) { - if ((*i)->type() == typeid(js::object)) { - const js::object &one = boost::any_cast(**i); - - twitter_user u; - u.username = retrieve(one, "sender_screen_name"); + JSONNODE *pNodes = json_as_array(root); + for (int i = 0;; i++) { + JSONNODE *pNode = json_at(pNodes, i); + if (pNode == NULL) + break; - u.status.text = retrieve(one, "text"); - u.status.id = retrieve(one, "id"); - std::string timestr = retrieve(one, "created_at"); - u.status.time = parse_time(timestr); + twitter_user u; + u.username = json_as_pstring(json_get(pNode, "sender_screen_name")); - messages.push_back(u); - } + u.status.text = json_as_pstring(json_get(pNode, "text")); + u.status.id = json_as_int(json_get(pNode, "id")); + std::string timestr = json_as_pstring(json_get(pNode, "created_at")); + u.status.time = parse_time(timestr); + messages.push_back(u); } return messages; @@ -422,8 +318,8 @@ string twitter::urlencode(const string &c) if ((48 <= c[i] && c[i] <= 57) ||//0-9 (65 <= c[i] && c[i] <= 90) ||//ABC...XYZ (97 <= c[i] && c[i] <= 122) || //abc...xyz - (c[i] == '~' || c[i] == '-' || c[i] == '_' || c[i] == '.') - ) { + (c[i] == '~' || c[i] == '-' || c[i] == '_' || c[i] == '.')) + { escaped.append(&c[i], 1); } else { -- cgit v1.2.3