From ca0762652e293ad9d35b03b537c02d218e44a13f Mon Sep 17 00:00:00 2001 From: Andy Wortman Date: Fri, 10 Nov 2017 04:04:00 -0800 Subject: very hackily add notion of user credentials and PIN auth also fix bug where cached user info takes precedence over (possibly updated) api json user info --- src/commands/auth.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/commands/fav.rs | 12 +++++-- src/commands/follow.rs | 24 +++++++++++-- src/commands/mod.rs | 3 ++ src/commands/twete.rs | 50 +++++++++++++++++++++------ 5 files changed, 165 insertions(+), 15 deletions(-) create mode 100644 src/commands/auth.rs (limited to 'src/commands') diff --git a/src/commands/auth.rs b/src/commands/auth.rs new file mode 100644 index 0000000..0ed006b --- /dev/null +++ b/src/commands/auth.rs @@ -0,0 +1,91 @@ +use tw; +use std; +use std::collections::HashMap; +use hyper; +use ::Queryer; + +use tw::TweetId; + +use commands::Command; + +static FAV_TWEET_URL: &str = "https://api.twitter.com/1.1/favorites/create.json"; +static UNFAV_TWEET_URL: &str = "https://api.twitter.com/1.1/favorites/destroy.json"; + +pub static AUTH: Command = Command { + keyword: "auth", + params: 0, + exec: auth +}; + +static OAUTH_REQUEST_TOKEN_URL: &str = "https://api.twitter.com/oauth/request_token"; +static OAUTH_AUTHORIZE_URL: &str = "https://api.twitter.com/oauth/authorize"; +static OAUTH_ACCESS_TOKEN_URL: &str = "https://api.twitter.com/oauth/access_token"; + +fn auth(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { + // step 0: get an oauth token. + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token with + // callback set to oob so the user will later get a PIN. + // step 1: now present the correect oauth/authorize URL + // this is as far as auth can get (rest depends on user PIN'ing with the right thing) + let res = queryer.raw_issue_request(::signed_api_req(&format!("{}?oauth_callback=oob", OAUTH_REQUEST_TOKEN_URL), hyper::Method::Post, &tweeter.app_key)); + match res { + Ok(bytes) => + match std::str::from_utf8(&bytes) { + Ok(url) => { + let parts: Vec> = url.split("&").map(|part| part.split("=").collect()).collect(); + let mut as_map: HashMap<&str, &str> = HashMap::new(); + for part in parts { + as_map.insert(part[0], part[1]); + } + tweeter.WIP_auth = Some(tw::Credential { + key: as_map["oauth_token"].to_owned(), + secret: as_map["oauth_token_secret"].to_owned() + }); + tweeter.display_info.status(format!("Now enter `pin` with the code at {}?oauth_token={}", OAUTH_AUTHORIZE_URL, as_map["oauth_token"])); + } + Err(_) => + tweeter.display_info.status("couldn't rebuild url".to_owned()) + }, + Err(e) => + tweeter.display_info.status(format!("request token url error: {}", e)) + }; +} + +pub static PIN: Command = Command { + keyword: "pin", + params: 1, + exec: pin +}; + +fn pin(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { + if tweeter.WIP_auth.is_none() { + tweeter.display_info.status("Begin authorizing an account with `auth` first.".to_owned()); + return; + } + + let res = queryer.raw_issue_request(::signed_api_req_with_token(&format!("{}?oauth_verifier={}", OAUTH_ACCESS_TOKEN_URL, line), hyper::Method::Post, &tweeter.app_key, &tweeter.WIP_auth.clone().unwrap())); + match res { + Ok(bytes) => + match std::str::from_utf8(&bytes) { + Ok(url) => { + let parts: Vec> = url.split("&").map(|part| part.split("=").collect()).collect(); + let mut as_map: HashMap<&str, &str> = HashMap::new(); + for part in parts { + as_map.insert(part[0], part[1]); + } + // turns out the "actual" oauth creds are different + tweeter.add_profile(tw::Credential { + key: as_map["oauth_token"].to_owned(), + secret: as_map["oauth_token_secret"].to_owned() + }); + tweeter.WIP_auth = None; + tweeter.state = tw::AppState::Reconnect; + tweeter.display_info.status("Looks like you authed! Connecting...".to_owned()); + }, + Err(_) => + tweeter.display_info.status("couldn't rebuild url".to_owned()) + }, + Err(e) => + tweeter.display_info.status(format!("request token url error: {}", e)) + }; +} diff --git a/src/commands/fav.rs b/src/commands/fav.rs index 6109310..08ad7f0 100644 --- a/src/commands/fav.rs +++ b/src/commands/fav.rs @@ -19,7 +19,11 @@ fn unfav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { match maybe_id { Ok(twid) => { if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self - match queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id), &tweeter.app_key, &user_creds), + None => Err("No logged in user to unfav from".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } @@ -45,7 +49,11 @@ fn fav(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { Ok(twid) => { // tweeter.to_twitter_tweet_id(twid)... if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self - match queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id), &tweeter.app_key, &user_creds), + None => Err("No logged in user to fav from".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } diff --git a/src/commands/follow.rs b/src/commands/follow.rs index b0dc8a7..e9099c9 100644 --- a/src/commands/follow.rs +++ b/src/commands/follow.rs @@ -14,7 +14,13 @@ pub static UNFOLLOW: Command = Command { fn unfl(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { let screen_name = line.trim(); - match queryer.do_api_post(&format!("{}?screen_name={}", FOLLOW_URL, screen_name)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => { + queryer.do_api_post(&format!("{}?screen_name={}", FOLLOW_URL, screen_name), &tweeter.app_key, &user_creds) + }, + None => Err("No logged in user to unfollow from".to_owned()) + }; + match result { Ok(_resp) => (), Err(e) => tweeter.display_info.status(format!("unfl request error: {}", e)) } @@ -28,5 +34,19 @@ pub static FOLLOW: Command = Command { fn fl(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { let screen_name = line.trim(); - tweeter.display_info.status(format!("fl resp: {:?}", queryer.do_api_post(&format!("{}?screen_name={}", UNFOLLOW_URL, screen_name)))); + match tweeter.profile.clone() { + Some(user_creds) => { + tweeter.display_info.status( + format!( + "fl resp: {:?}", + queryer.do_api_post( + &format!("{}?screen_name={}", UNFOLLOW_URL, screen_name), + &tweeter.app_key, + &user_creds + ) + ) + ) + }, + None => tweeter.display_info.status("No logged in user to follow from".to_owned()) + }; } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 9ec6c4b..f7536a0 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -7,6 +7,7 @@ pub struct Command { pub exec: fn(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) } +pub mod auth; pub mod show_cache; pub mod twete; pub mod look_up; @@ -17,6 +18,8 @@ pub mod follow; pub mod thread; pub static COMMANDS: &[&Command] = &[ + &auth::AUTH, + &auth::PIN, &show_cache::SHOW_CACHE, &quit::QUIT, &look_up::LOOK_UP_USER, diff --git a/src/commands/twete.rs b/src/commands/twete.rs index 4452df9..239e039 100644 --- a/src/commands/twete.rs +++ b/src/commands/twete.rs @@ -20,7 +20,11 @@ fn del(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { Ok(twid) => { // TODO this really converts twid to a TweetId::Twitter if let Some(twitter_id) = tweeter.retrieve_tweet(&twid).map(|x| x.id.to_owned()) { - match queryer.do_api_post(&format!("{}/{}.json", DEL_TWEET_URL, twitter_id)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => queryer.do_api_post(&format!("{}/{}.json", DEL_TWEET_URL, twitter_id), &tweeter.app_key, &user_creds), + None => Err("No logged in user to delete as".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } @@ -54,7 +58,11 @@ fn twete(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { pub fn send_twete(text: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { let substituted = ::url_encode(&text); if text.len() <= 140 { - match queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted), &tweeter.app_key, &user_creds), + None => Err("No logged in user to tweet as".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } @@ -166,7 +174,13 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { pub fn send_reply(text: String, twid: TweetId, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { if let Some(twete) = tweeter.retrieve_tweet(&twid).map(|x| x.clone()) { // TODO: no clone when this stops taking &mut self let substituted = ::url_encode(&text); - match queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => { + queryer.do_api_post(&format!("{}?status={}&in_reply_to_status_id={}", CREATE_TWEET_URL, substituted, twete.id), &tweeter.app_key, &user_creds) + }, + None => Err("No logged in user to tweet as".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } @@ -200,13 +214,21 @@ fn quote(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) { twete.id ) ); - match queryer.do_api_post( - &format!("{}?status={}&attachment_url={}", - CREATE_TWEET_URL, - substituted, - attachment_url - ) - ) { + let result = match tweeter.profile.clone() { + Some(user_creds) => { + queryer.do_api_post( + &format!("{}?status={}&attachment_url={}", + CREATE_TWEET_URL, + substituted, + attachment_url + ), + &tweeter.app_key, + &user_creds + ) + }, + None => Err("No logged in user to tweet as".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } @@ -237,7 +259,13 @@ fn retwete(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) Ok(twid) => { // TODO this really converts twid to a TweetId::Twitter if let Some(twitter_id) = tweeter.retrieve_tweet(&twid).map(|x| x.id.to_owned()) { - match queryer.do_api_post(&format!("{}/{}.json", RT_TWEET_URL, twitter_id)) { + let result = match tweeter.profile.clone() { + Some(user_creds) => { + queryer.do_api_post(&format!("{}/{}.json", RT_TWEET_URL, twitter_id), &tweeter.app_key, &user_creds) + }, + None => Err("No logged in user to retweet as".to_owned()) + }; + match result { Ok(_) => (), Err(e) => tweeter.display_info.status(e) } -- cgit v1.1