From ab92c2a5cd5427bf8a33d06afdb64b88d2d640ed Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Wed, 6 Jun 2012 20:17:58 +0000 Subject: Twitter updated git-svn-id: http://svn.miranda-ng.org/main/trunk@337 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Twitter/twitter.cpp | 231 ++++++++++++++++++++++++++++++------------ 1 file changed, 164 insertions(+), 67 deletions(-) (limited to 'protocols/Twitter/twitter.cpp') diff --git a/protocols/Twitter/twitter.cpp b/protocols/Twitter/twitter.cpp index 9512292707..ff260f9fbd 100644 --- a/protocols/Twitter/twitter.cpp +++ b/protocols/Twitter/twitter.cpp @@ -15,18 +15,20 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -//#include "common.h" +#include "twitter.h" +//#include "tc2.h" #include #include #include #include -#include "twitter.h" - #include "tinyjson.hpp" #include +//#include "boost/spirit/include/classic_core.hpp" +//#include "boost/spirit/include/classic_loops.hpp" + typedef json::grammar js; // utility functions @@ -34,7 +36,7 @@ typedef json::grammar js; template static T cast_and_decode(boost::any &a,bool allow_null) { - if (allow_null && a.type() == typeid(void)) + if(allow_null && a.type() == typeid(void)) return T(); return boost::any_cast(a); } @@ -42,7 +44,7 @@ static T cast_and_decode(boost::any &a,bool allow_null) template <> static std::string cast_and_decode(boost::any &a,bool allow_null) { - if (allow_null && a.type() == typeid(void)) + if(allow_null && a.type() == typeid(void)) return std::string(); std::string s = boost::any_cast(a); @@ -62,15 +64,15 @@ static T retrieve(const js::object &o,const std::string &key,bool allow_null = f 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()); + 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()); + throw std::exception( ("unable to cast key '"+key+"' to target type").c_str() ); } } @@ -79,18 +81,30 @@ static T retrieve(const js::object &o,const std::string &key,bool allow_null = f twitter::twitter() : base_url_("https://twitter.com/") {} -bool twitter::set_credentials(const std::string &username,const std::string &password, - bool test) +bool twitter::set_credentials(const std::string &username, const std::wstring &consumerKey, const std::wstring &consumerSecret, + const std::wstring &oauthAccessToken, const std::wstring &oauthAccessTokenSecret, const std::wstring &pin, bool test) { - username_ = username; - password_ = password; - - if (test) + username_ = username; + consumerKey_ = consumerKey; + consumerSecret_ = consumerSecret; + oauthAccessToken_ = oauthAccessToken; + oauthAccessTokenSecret_ = oauthAccessTokenSecret; + pin_ = pin; + + if(test) return slurp(base_url_+"account/verify_credentials.json",http::get).code == 200; else return true; } +http::response twitter::request_token() { + return slurp(base_url_+"oauth/request_token",http::get); +} + +http::response twitter::request_access_tokens() { + return slurp(base_url_+"oauth/access_token", http::get); +} + void twitter::set_base_url(const std::string &base_url) { base_url_ = base_url; @@ -111,29 +125,29 @@ std::vector twitter::get_friends() std::vector friends; http::response resp = slurp(base_url_+"statuses/friends.json",http::get); - if (resp.code != 200) + 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)) + const js::variant var = json::parse( resp.data.begin(),resp.data.end() ); + if(var->type() != typeid(js::array)) 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)) + if((*i)->type() == typeid(js::object)) { const js::object &one = boost::any_cast(**i); twitter_user user; user.username = retrieve(one,"screen_name"); - user.real_name = retrieve(one,"name",true); + user.real_name = retrieve(one,"name",true); user.profile_image_url = retrieve(one,"profile_image_url",true); - if (one.find("status") != one.end()) + if(one.find("status") != one.end()) { js::object &status = retrieve(one,"status"); - user.status.text = retrieve(status,"text"); + user.status.text = retrieve(status,"text"); user.status.id = retrieve(status,"id"); @@ -148,26 +162,26 @@ std::vector twitter::get_friends() return friends; } -bool twitter::get_info(const std::tstring &name,twitter_user *info) +bool twitter::get_info(const std::string &name,twitter_user *info) { - if (!info) + if(!info) return false; std::string url = base_url_+"users/show/"+http::url_encode(name)+".json"; http::response resp = slurp(url,http::get); - if (resp.code != 200) + 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::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()) + 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->real_name = retrieve(user_info,"name",true); info->profile_image_url = retrieve(user_info,"profile_image_url",true); return true; @@ -176,26 +190,26 @@ bool twitter::get_info(const std::tstring &name,twitter_user *info) return false; } -bool twitter::get_info_by_email(const std::tstring &email,twitter_user *info) +bool twitter::get_info_by_email(const std::string &email,twitter_user *info) { - if (!info) + if(!info) return false; std::string url = base_url_+"users/show.json?email="+http::url_encode(email); http::response resp = slurp(url,http::get); - if (resp.code != 200) + if(resp.code != 200) throw bad_response(); - js::variant var = json::parse( resp.data.begin(),resp.data.end()); - if (var->type() == typeid(js::object)) + 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()) + 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->real_name = retrieve(user_info,"name",true); info->profile_image_url = retrieve(user_info,"profile_image_url",true); return true; @@ -204,56 +218,65 @@ bool twitter::get_info_by_email(const std::tstring &email,twitter_user *info) return false; } -twitter_user twitter::add_friend(const std::tstring &name) +twitter_user twitter::add_friend(const std::string &name) { std::string url = base_url_+"friendships/create/"+http::url_encode(name)+".json"; twitter_user ret; http::response resp = slurp(url,http::post); - if (resp.code != 200) + if(resp.code != 200) throw bad_response(); - js::variant var = json::parse( resp.data.begin(),resp.data.end()); - if (var->type() != typeid(js::object)) + js::variant var = json::parse( resp.data.begin(),resp.data.end() ); + if(var->type() != typeid(js::object)) 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.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()) + 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"); + ret.status.text = retrieve(status,"text"); } return ret; } -void twitter::remove_friend(const std::tstring &name) +void twitter::remove_friend(const std::string &name) { std::string url = base_url_+"friendships/destroy/"+http::url_encode(name)+".json"; slurp(url,http::post); } -void twitter::set_status(const std::tstring &text) +void twitter::set_status(const std::string &text) { - if (text.size()) + if(text.size()) { - slurp(base_url_+"statuses/update.json",http::post, +/* slurp(base_url_+"statuses/update.json",http::post, "status="+http::url_encode(text)+ - "&source=mirandaim"); + "&source=mirandaim");*/ + + //MessageBox(NULL, UTF8ToWide(text).c_str(), NULL, MB_OK); + std::wstring wTweet = UTF8ToWide(text); + OAuthParameters postParams; + postParams[L"status"] = UrlEncode(wTweet); + + slurp(base_url_+"statuses/update.json",http::post, postParams); } } -void twitter::send_direct(const std::tstring &name,const std::tstring &text) +void twitter::send_direct(const std::string &name,const std::string &text) { - slurp(base_url_+"direct_messages/new.json",http::post, - "user=" +http::url_encode(name)+ - "&text="+http::url_encode(text)); + std::wstring temp = UTF8ToWide(text); + OAuthParameters postParams; + postParams[L"text"] = UrlEncode(temp); + postParams[L"screen_name"] = UTF8ToWide(name); + slurp(base_url_+"direct_messages/new.json", http::post, postParams); } std::vector twitter::get_statuses(int count,twitter_id id) @@ -261,31 +284,45 @@ std::vector twitter::get_statuses(int count,twitter_id id) using boost::lexical_cast; std::vector statuses; - std::string url = base_url_+"statuses/friends_timeline.json?count="+ + std::string url = base_url_+"statuses/home_timeline.json?count="+ lexical_cast(count); - if (id != 0) + if(id != 0) url += "&since_id="+boost::lexical_cast(id); http::response resp = slurp(url,http::get); - if (resp.code != 200) + if(resp.code != 200) throw bad_response(); - js::variant var = json::parse( resp.data.begin(),resp.data.end()); - if (var->type() != typeid(js::array)) + js::variant var = json::parse( resp.data.begin(),resp.data.end() ); + if(var->type() != typeid(js::array)) 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)) + 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"); + bool isTruncated = retrieve(one,"truncated"); + + if (isTruncated) { // the tweet will be truncated unless we take action. i hate you twitter API + + // here we grab the "retweeted_status" um.. section? it's in here that all the info we need is + 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 + 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, + u.status.text = retrieve(one,"text"); // so we can just pretend it doesn't happen + } - 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); @@ -303,28 +340,28 @@ std::vector twitter::get_direct(twitter_id id) std::vector messages; std::string url = base_url_+"direct_messages.json"; - if (id != 0) + if(id != 0) url += "?since_id="+boost::lexical_cast(id); http::response resp = slurp(url,http::get); - if (resp.code != 200) + if(resp.code != 200) throw bad_response(); - js::variant var = json::parse( resp.data.begin(),resp.data.end()); - if (var->type() != typeid(js::array)) + js::variant var = json::parse( resp.data.begin(),resp.data.end() ); + if(var->type() != typeid(js::array)) 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)) + if((*i)->type() == typeid(js::object)) { const js::object &one = boost::any_cast(**i); twitter_user u; u.username = retrieve(one,"sender_screen_name"); - u.status.text = retrieve(one,"text"); + 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); @@ -337,6 +374,66 @@ std::vector twitter::get_direct(twitter_id id) return messages; } +string twitter::urlencode(const string &c) +{ + + string escaped; + int max = c.length(); + for(int i=0; i>4; + char dig2 = (dec&0x0F); + if ( 0<= dig1 && dig1<= 9) dig1+=48; //0,48 in ascii + if (10<= dig1 && dig1<=15) dig1+=65-10; //A,65 in ascii + if ( 0<= dig2 && dig2<= 9) dig2+=48; + if (10<= dig2 && dig2<=15) dig2+=65-10; + + string r; + r.append( &dig1, 1); + r.append( &dig2, 1); + return r; +} + // Some Unices get this, now we do too! time_t timegm(struct tm *t) { @@ -354,7 +451,7 @@ int parse_month(const char *m) { for(size_t i=0; i<12; i++) { - if (strcmp(month_names[i],m) == 0) + if(strcmp(month_names[i],m) == 0) return i; } return -1; @@ -366,13 +463,13 @@ time_t parse_time(const std::string &s) char day[4],month[4]; char plus; int zone; - if (sscanf(s.c_str(),"%3s %3s %d %d:%d:%d %c%d %d", + if(sscanf(s.c_str(),"%3s %3s %d %d:%d:%d %c%d %d", day,month,&t.tm_mday,&t.tm_hour,&t.tm_min,&t.tm_sec, &plus,&zone,&t.tm_year) == 9) { t.tm_year -= 1900; t.tm_mon = parse_month(month); - if (t.tm_mon == -1) + if(t.tm_mon == -1) return 0; return timegm(&t); } -- cgit v1.2.3