diff options
author | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-06-06 20:17:58 +0000 |
---|---|---|
committer | Vadim Dashevskiy <watcherhd@gmail.com> | 2012-06-06 20:17:58 +0000 |
commit | ab92c2a5cd5427bf8a33d06afdb64b88d2d640ed (patch) | |
tree | fc82f068ad7bf8e141b6148f2ff191eab56e1f5a /protocols/Twitter/twitter.cpp | |
parent | 441daff7dfc4cd80075cfbec3345192aab4353b5 (diff) |
Twitter updated
git-svn-id: http://svn.miranda-ng.org/main/trunk@337 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/Twitter/twitter.cpp')
-rw-r--r-- | protocols/Twitter/twitter.cpp | 231 |
1 files changed, 164 insertions, 67 deletions
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 <http://www.gnu.org/licenses/>.
*/
-//#include "common.h"
+#include "twitter.h"
+//#include "tc2.h"
#include <windows.h>
#include <cstring>
#include <sstream>
#include <ctime>
-#include "twitter.h"
-
#include "tinyjson.hpp"
#include <boost/lexical_cast.hpp>
+//#include "boost/spirit/include/classic_core.hpp"
+//#include "boost/spirit/include/classic_loops.hpp"
+
typedef json::grammar<char> js;
// utility functions
@@ -34,7 +36,7 @@ typedef json::grammar<char> js; template <typename T>
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<T>(a);
}
@@ -42,7 +44,7 @@ static T cast_and_decode(boost::any &a,bool allow_null) template <>
static std::string cast_and_decode<std::string>(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<std::string>(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<T>(*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_user> twitter::get_friends() std::vector<twitter_user> 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<js::array>(*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<js::object>(**i);
twitter_user user;
user.username = retrieve<std::string>(one,"screen_name");
- user.real_name = retrieve<std::tstring>(one,"name",true);
+ user.real_name = retrieve<std::string>(one,"name",true);
user.profile_image_url = retrieve<std::string>(one,"profile_image_url",true);
- if (one.find("status") != one.end())
+ if(one.find("status") != one.end())
{
js::object &status = retrieve<js::object>(one,"status");
- user.status.text = retrieve<std::tstring>(status,"text");
+ user.status.text = retrieve<std::string>(status,"text");
user.status.id = retrieve<long long>(status,"id");
@@ -148,26 +162,26 @@ std::vector<twitter_user> 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<js::object>(*var);
- if (user_info.find("error") != user_info.end())
+ if(user_info.find("error") != user_info.end())
return false;
info->username = retrieve<std::string>(user_info,"screen_name");
- info->real_name = retrieve<std::tstring>(user_info,"name",true);
+ info->real_name = retrieve<std::string>(user_info,"name",true);
info->profile_image_url = retrieve<std::string>(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<js::object>(*var);
- if (user_info.find("error") != user_info.end())
+ if(user_info.find("error") != user_info.end())
return false;
info->username = retrieve<std::string>(user_info,"screen_name");
- info->real_name = retrieve<std::tstring>(user_info,"name",true);
+ info->real_name = retrieve<std::string>(user_info,"name",true);
info->profile_image_url = retrieve<std::string>(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<js::object>(*var);
ret.username = retrieve<std::string>(user_info,"screen_name");
- ret.real_name = retrieve<std::tstring>(user_info,"name",true);
+ ret.real_name = retrieve<std::string>(user_info,"name",true);
ret.profile_image_url = retrieve<std::string>(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<js::object>(user_info,"status");
- ret.status.text = retrieve<std::tstring>(status,"text");
+ ret.status.text = retrieve<std::string>(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_user> twitter::get_statuses(int count,twitter_id id)
@@ -261,31 +284,45 @@ std::vector<twitter_user> twitter::get_statuses(int count,twitter_id id) using boost::lexical_cast;
std::vector<twitter_user> statuses;
- std::string url = base_url_+"statuses/friends_timeline.json?count="+
+ std::string url = base_url_+"statuses/home_timeline.json?count="+
lexical_cast<std::string>(count);
- if (id != 0)
+ if(id != 0)
url += "&since_id="+boost::lexical_cast<std::string>(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<js::array>(*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<js::object>(**i);
const js::object &user = retrieve<js::object>(one,"user");
twitter_user u;
u.username = retrieve<std::string>(user,"screen_name");
+ bool isTruncated = retrieve<bool>(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<js::object>(one,"retweeted_status");
+ const js::object &RTUser = retrieve<js::object>(Retweet,"user");
+
+ std::string retweeteesName = retrieve<std::string>(RTUser,"screen_name"); // the user that is being retweeted
+ std::string retweetText = retrieve<std::string>(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<std::string>(one,"text"); // so we can just pretend it doesn't happen
+ }
- u.status.text = retrieve<std::tstring>(one,"text");
u.status.id = retrieve<long long>(one,"id");
std::string timestr = retrieve<std::string>(one,"created_at");
u.status.time = parse_time(timestr);
@@ -303,28 +340,28 @@ std::vector<twitter_user> twitter::get_direct(twitter_id id) std::vector<twitter_user> messages;
std::string url = base_url_+"direct_messages.json";
- if (id != 0)
+ if(id != 0)
url += "?since_id="+boost::lexical_cast<std::string>(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<js::array>(*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<js::object>(**i);
twitter_user u;
u.username = retrieve<std::string>(one,"sender_screen_name");
- u.status.text = retrieve<std::tstring>(one,"text");
+ u.status.text = retrieve<std::string>(one,"text");
u.status.id = retrieve<long long>(one,"id");
std::string timestr = retrieve<std::string>(one,"created_at");
u.status.time = parse_time(timestr);
@@ -337,6 +374,66 @@ std::vector<twitter_user> 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<max; i++)
+ {
+ 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]=='.')
+ )
+ {
+ escaped.append( &c[i], 1);
+ }
+ else
+ {
+ escaped.append("%");
+ escaped.append( char2hex(c[i]) );//converts char 255 to string "FF"
+ }
+ }
+ return escaped;
+}
+
+
+wstring twitter::UrlEncode( const wstring& url )
+{
+ // multiple encodings r sux
+ return UTF8ToWide(urlencode(WideToUTF8(url)));
+}
+
+// char2hex and urlencode from http://www.zedwood.com/article/111/cpp-urlencode-function
+// modified according to http://oauth.net/core/1.0a/#encoding_parameters
+//
+//5.1. Parameter Encoding
+//
+//All parameter names and values are escaped using the [RFC3986]
+//percent-encoding (%xx) mechanism. Characters not in the unreserved character set
+//MUST be encoded. Characters in the unreserved character set MUST NOT be encoded.
+//Hexadecimal characters in encodings MUST be upper case.
+//Text names and values MUST be encoded as UTF-8
+// octets before percent-encoding them per [RFC3629].
+//
+// unreserved = ALPHA, DIGIT, '-', '.', '_', '~'
+
+string twitter::char2hex( char dec )
+{
+ char dig1 = (dec&0xF0)>>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);
}
|