aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/commands/auth.rs7
-rw-r--r--src/commands/fav.rs8
-rw-r--r--src/commands/follow.rs12
-rw-r--r--src/commands/help.rs2
-rw-r--r--src/commands/quit.rs2
-rw-r--r--src/commands/show_cache.rs2
-rw-r--r--src/commands/twete.rs50
-rw-r--r--src/display/mod.rs9
-rw-r--r--src/main.rs110
-rw-r--r--src/tw/mod.rs168
10 files changed, 223 insertions, 147 deletions
diff --git a/src/commands/auth.rs b/src/commands/auth.rs
index 7d01451..65dcaf5 100644
--- a/src/commands/auth.rs
+++ b/src/commands/auth.rs
@@ -4,8 +4,6 @@ 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";
@@ -79,10 +77,11 @@ fn pin(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
as_map.insert(part[0], part[1]);
}
// turns out the "actual" oauth creds are different
- tweeter.add_profile(tw::Credential {
+ // TODO: profile names?
+ tweeter.add_profile(tw::TwitterProfile::new(tw::Credential {
key: as_map["oauth_token"].to_owned(),
secret: as_map["oauth_token_secret"].to_owned()
- });
+ }, tw::user::User::default()), Some("iximeow".to_owned()));
tweeter.WIP_auth = None;
tweeter.state = tw::AppState::Reconnect;
tweeter.display_info.status("Looks like you authed! Connecting...".to_owned());
diff --git a/src/commands/fav.rs b/src/commands/fav.rs
index 7a4852e..5c12535 100644
--- a/src/commands/fav.rs
+++ b/src/commands/fav.rs
@@ -21,8 +21,8 @@ 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
- 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),
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => queryer.do_api_post(&format!("{}?id={}", UNFAV_TWEET_URL, twete.id), &tweeter.app_key, &user_profile.creds),
None => Err("No logged in user to unfav from".to_owned())
};
match result {
@@ -53,8 +53,8 @@ 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
- 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),
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => queryer.do_api_post(&format!("{}?id={}", FAV_TWEET_URL, twete.id), &tweeter.app_key, &user_profile.creds),
None => Err("No logged in user to fav from".to_owned())
};
match result {
diff --git a/src/commands/follow.rs b/src/commands/follow.rs
index 3a29252..6e29788 100644
--- a/src/commands/follow.rs
+++ b/src/commands/follow.rs
@@ -16,9 +16,9 @@ pub static UNFOLLOW: Command = Command {
fn unfl(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let screen_name = line.trim();
- 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)
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => {
+ queryer.do_api_post(&format!("{}?screen_name={}", FOLLOW_URL, screen_name), &tweeter.app_key, &user_profile.creds)
},
None => Err("No logged in user to unfollow from".to_owned())
};
@@ -38,15 +38,15 @@ pub static FOLLOW: Command = Command {
fn fl(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let screen_name = line.trim();
- match tweeter.profile.clone() {
- Some(user_creds) => {
+ match tweeter.current_profile().map(|profile| profile.to_owned()) {
+ Some(user_profile) => {
tweeter.display_info.status(
format!(
"fl resp: {:?}",
queryer.do_api_post(
&format!("{}?screen_name={}", UNFOLLOW_URL, screen_name),
&tweeter.app_key,
- &user_creds
+ &user_profile.creds
)
)
)
diff --git a/src/commands/help.rs b/src/commands/help.rs
index c99681e..445684b 100644
--- a/src/commands/help.rs
+++ b/src/commands/help.rs
@@ -1,8 +1,6 @@
use tw;
use ::Queryer;
-use tw::TweetId;
-
use commands::Command;
pub static HELP: Command = Command {
diff --git a/src/commands/quit.rs b/src/commands/quit.rs
index 6638299..0f5c582 100644
--- a/src/commands/quit.rs
+++ b/src/commands/quit.rs
@@ -3,8 +3,6 @@ use ::Queryer;
use commands::Command;
-use std::process::exit;
-
pub static QUIT: Command = Command {
keyword: "q",
params: 0,
diff --git a/src/commands/show_cache.rs b/src/commands/show_cache.rs
index fffcdb6..6dda8dc 100644
--- a/src/commands/show_cache.rs
+++ b/src/commands/show_cache.rs
@@ -12,6 +12,7 @@ pub static SHOW_CACHE: Command = Command {
};
fn show_cache(_line: String, tweeter: &mut tw::TwitterCache, mut queryer: &mut Queryer) {
+ /*
tweeter.display_info.status("----* USERS *----".to_owned());
for (uid, user) in &tweeter.users {
tweeter.display_info.status(format!("User: {} -> {:?}", uid, user));
@@ -30,4 +31,5 @@ fn show_cache(_line: String, tweeter: &mut tw::TwitterCache, mut queryer: &mut Q
None => { tweeter.display_info.status(" ...".to_owned()); }
}
}
+ */
}
diff --git a/src/commands/twete.rs b/src/commands/twete.rs
index c5c0c1a..0d82b76 100644
--- a/src/commands/twete.rs
+++ b/src/commands/twete.rs
@@ -22,8 +22,8 @@ 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()) {
- 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),
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => queryer.do_api_post(&format!("{}/{}.json", DEL_TWEET_URL, twitter_id), &tweeter.app_key, &user_profile.creds),
None => Err("No logged in user to delete as".to_owned())
};
match result {
@@ -61,8 +61,8 @@ 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);
- let result = match tweeter.profile.clone() {
- Some(user_creds) => queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted), &tweeter.app_key, &user_creds),
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => queryer.do_api_post(&format!("{}?status={}", CREATE_TWEET_URL, substituted), &tweeter.app_key, &user_profile.creds),
None => Err("No logged in user to tweet as".to_owned())
};
match result {
@@ -83,6 +83,13 @@ pub static THREAD: Command = Command {
// the difference between threading and replying is not including
// yourself in th @'s.
fn thread(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+ let user_profile = match tweeter.current_profile().map(|profile| profile.to_owned()) {
+ Some(profile) => profile,
+ None => {
+ tweeter.display_info.status("To reply you must be authenticated as a user.".to_owned());
+ return;
+ }
+ };
let mut text: String = line.trim().to_string();
if let Some(id_end_idx) = text.find(" ") {
let reply_bare = text.split_off(id_end_idx + 1);
@@ -95,8 +102,8 @@ fn thread(line: String, 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 handle = &tweeter.retrieve_user(&twete.author_id).unwrap().handle.to_owned();
// TODO: definitely breaks if you change your handle right now
- if handle == &tweeter.current_user.handle {
- send_reply(reply.to_owned(), twid, tweeter, queryer);
+ if handle == &user_profile.user.handle {
+ send_reply(reply.to_owned(), twid, tweeter, queryer, user_profile.creds);
} else {
tweeter.display_info.status("you can only thread your own tweets".to_owned());
// ask if it should .@ instead?
@@ -125,6 +132,13 @@ pub static REP: Command = Command {
};
fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
+ let user_profile = match tweeter.current_profile().map(|profile| profile.to_owned()) {
+ Some(profile) => profile,
+ None => {
+ tweeter.display_info.status("To reply you must be authenticated as a user.".to_owned());
+ return;
+ }
+ };
let mut text: String = line.trim().to_string();
let reply_bare = match text.find(" ") {
None => "".to_owned(),
@@ -152,8 +166,8 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
// if you're directly replying to yourself, i trust you know what you're doing and
// want to @ yourself again (this keeps self-replies from showing up on your
// profile as threaded tweets, f.ex)
- if !(ats.len() > 0 && &ats[0] == &tweeter.current_user.handle) {
- ats.remove_item(&tweeter.current_user.handle);
+ if !(ats.len() > 0 && &ats[0] == &user_profile.user.handle) {
+ ats.remove_item(&user_profile.user.handle);
}
//let ats_vec: Vec<&str> = ats.into_iter().collect();
//let full_reply = format!("{} {}", ats_vec.join(" "), reply);
@@ -161,7 +175,7 @@ fn rep(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
let full_reply = format!("{} {}", decorated_ats.join(" "), reply);
if reply.len() > 0 {
- send_reply(full_reply, twid, tweeter, queryer);
+ send_reply(full_reply, twid, tweeter, queryer, user_profile.creds);
} else {
tweeter.display_info.mode = Some(::display::DisplayMode::Reply(twid, full_reply));
}
@@ -175,11 +189,11 @@ 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) {
+pub fn send_reply(text: String, twid: TweetId, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer, user_creds: tw::Credential) {
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);
- let result = match tweeter.profile.clone() {
- Some(user_creds) => {
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => {
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())
@@ -220,8 +234,8 @@ fn quote(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
twete.id
)
);
- let result = match tweeter.profile.clone() {
- Some(user_creds) => {
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => {
queryer.do_api_post(
&format!("{}?status={}&attachment_url={}",
CREATE_TWEET_URL,
@@ -229,7 +243,7 @@ fn quote(line: String, tweeter: &mut tw::TwitterCache, queryer: &mut Queryer) {
attachment_url
),
&tweeter.app_key,
- &user_creds
+ &user_profile.creds
)
},
None => Err("No logged in user to tweet as".to_owned())
@@ -267,9 +281,9 @@ 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()) {
- 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)
+ let result = match tweeter.current_profile() {
+ Some(user_profile) => {
+ queryer.do_api_post(&format!("{}/{}.json", RT_TWEET_URL, twitter_id), &tweeter.app_key, &user_profile.creds)
},
None => Err("No logged in user to retweet as".to_owned())
};
diff --git a/src/display/mod.rs b/src/display/mod.rs
index 954ed38..88722cc 100644
--- a/src/display/mod.rs
+++ b/src/display/mod.rs
@@ -119,10 +119,11 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
*/
let (cursor_x, cursor_y) = match tweeter.display_info.mode.clone() {
None => {
+ let handle = tweeter.current_profile().map(|profile| profile.user.handle.to_owned()).unwrap_or("_default_".to_owned());
print!("{}{}", cursor::Goto(1, height - 6), clear::CurrentLine);
- print!("{}{}@{}>{}", cursor::Goto(1, height - 5), clear::CurrentLine, tweeter.current_user.handle, tweeter.display_info.input_buf.clone().into_iter().collect::<String>());
+ print!("{}{}@{}>{}", cursor::Goto(1, height - 5), clear::CurrentLine, handle, tweeter.display_info.input_buf.clone().into_iter().collect::<String>());
print!("{}{}", cursor::Goto(1, height - 4), clear::CurrentLine);
- ((1 + tweeter.current_user.handle.len() + 2 + tweeter.display_info.input_buf.len()) as u16, height as u16 - 5)
+ ((1 + handle.len() + 2 + tweeter.display_info.input_buf.len()) as u16, height as u16 - 5)
}
Some(DisplayMode::Compose(x)) => {
let mut lines: Vec<String> = vec![];
@@ -143,7 +144,7 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
);
lines_drawn += 1;
}
- h += (lines_drawn - 3);
+ h += lines_drawn - 3;
(cursor_idx as u16 + 3, height as u16 - 5) // TODO: panic on underflow
}
Some(DisplayMode::Reply(twid, msg)) => {
@@ -166,7 +167,7 @@ pub fn paint(tweeter: &mut ::tw::TwitterCache) -> Result<(), std::io::Error> {
);
lines_drawn += 1;
}
- h += (lines_drawn - 3);
+ h += lines_drawn - 3;
(cursor_idx as u16 + 3, height as u16 - 5) // TODO: panic on underflow
}
};
diff --git a/src/main.rs b/src/main.rs
index bfb60f9..cd15ea9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -180,6 +180,16 @@ fn inner_signed_api_req(url: &str, method: Method, app_cred: &tw::Credential, ma
req
}
+static mut connection_id: u8 = 0;
+
+fn get_id() -> u8 {
+ unsafe {
+ let curr_id = connection_id;
+ connection_id += 1;
+ curr_id
+ }
+}
+
fn main() {
//Track words
@@ -200,11 +210,13 @@ fn main() {
tweeter.display_info.status("Cache loaded".to_owned());
- let mut maybe_twete_rx = tweeter.profile.clone()
- .map(|user_creds| {
- let rx = connect_twitter_stream(tweeter.app_key.clone(), user_creds);
- tweeter.display_info.status("Twitter stream open".to_owned());
- rx
+ let (tweet_tx, mut twete_rx) = chan::sync::<Vec<u8>>(0);
+ let (coordination_tx, mut coordination_rx) = chan::sync::<TwitterConnectionState>(0);
+
+ tweeter.current_profile()
+ .map(|user_profile| user_profile.to_owned())
+ .map(|user_profile| {
+ connect_twitter_stream(tweeter.app_key.clone(), user_profile.creds, tweet_tx.clone(), coordination_tx.clone(), get_id());
});
std::thread::spawn(move || {
@@ -240,10 +252,11 @@ fn main() {
};
loop {
- match do_ui(ui_rx, maybe_twete_rx, &mut tweeter, &mut queryer) {
- Some((new_ui_rx, new_maybe_twete_rx)) => {
+ match do_ui(ui_rx, twete_rx, coordination_rx, &mut tweeter, &mut queryer) {
+ Some((new_ui_rx, new_twete_rx, new_coordination_rx)) => {
ui_rx = new_ui_rx;
- maybe_twete_rx = new_maybe_twete_rx;
+ twete_rx = new_twete_rx;
+ coordination_rx = new_coordination_rx;
},
None => {
break;
@@ -314,8 +327,15 @@ fn handle_input(event: termion::event::Event, tweeter: &mut tw::TwitterCache, qu
}
Some(display::DisplayMode::Reply(twid, msg)) => {
if x == '\n' {
- // TODO: move this somewhere better.
- ::commands::twete::send_reply(msg, twid, tweeter, queryer);
+ match tweeter.current_profile().map(|profile| profile.to_owned()) {
+ Some(profile) => {
+ // TODO: move this somewhere better.
+ ::commands::twete::send_reply(msg, twid, tweeter, queryer, profile.creds);
+ },
+ None => {
+ tweeter.display_info.status("Cannot reply when not logged in".to_owned());
+ }
+ }
tweeter.display_info.mode = None;
} else {
tweeter.display_info.mode = Some(display::DisplayMode::Reply(twid, format!("{}{}", msg, x)));
@@ -356,35 +376,32 @@ fn handle_twitter_line(line: Vec<u8>, mut tweeter: &mut tw::TwitterCache, mut qu
}
}
-fn do_ui(ui_rx_orig: chan::Receiver<Result<termion::event::Event, std::io::Error>>, maybe_twete_rx: Option<chan::Receiver<Vec<u8>>>, mut tweeter: &mut tw::TwitterCache, mut queryer: &mut ::Queryer) -> Option<(chan::Receiver<Result<termion::event::Event, std::io::Error>>, Option<chan::Receiver<Vec<u8>>>)> {
+fn do_ui(
+ ui_rx_orig: chan::Receiver<Result<termion::event::Event, std::io::Error>>,
+ twete_rx: chan::Receiver<Vec<u8>>,
+ coordination_rx: chan::Receiver<TwitterConnectionState>,
+ mut tweeter: &mut tw::TwitterCache,
+ mut queryer: &mut ::Queryer
+) -> Option<(chan::Receiver<Result<termion::event::Event, std::io::Error>>, chan::Receiver<Vec<u8>>, chan::Receiver<TwitterConnectionState>)> {
loop {
let ui_rx_a = &ui_rx_orig;
let ui_rx_b = &ui_rx_orig;
- match &maybe_twete_rx {
- &Some(ref twete_rx) => {
- chan_select! {
- twete_rx.recv() -> twete => match twete {
- Some(line) => handle_twitter_line(line, tweeter, queryer),
- None => {
- tweeter.display_info.status("Twitter stream hung up...".to_owned());
- return Some((ui_rx_orig.clone(), None))
- }
- },
- ui_rx_a.recv() -> user_input => match user_input {
- Some(Ok(event)) => handle_input(event, tweeter, queryer),
- Some(Err(_)) => (), /* stdin closed? */
- None => return None // UI ded
- }
- }
+ chan_select! {
+ coordination_rx.recv() -> coordination => {
+ tweeter.display_info.status(format!("{:?}", coordination));
},
- &None => {
- chan_select! {
- ui_rx_a.recv() -> user_input => match user_input {
- Some(Ok(event)) => handle_input(event, tweeter, queryer),
- Some(Err(_)) => (), /* stdin closed? */
- None => return None // UI ded
- }
+ twete_rx.recv() -> twete => match twete {
+ Some(line) => handle_twitter_line(line, tweeter, queryer),
+ None => {
+ tweeter.display_info.status("Twitter stream hung up...".to_owned());
+ display::paint(tweeter).unwrap();
+ return None // if the twitter channel died, something real bad happeneed?
}
+ },
+ ui_rx_a.recv() -> user_input => match user_input {
+ Some(Ok(event)) => handle_input(event, tweeter, queryer),
+ Some(Err(_)) => (), /* stdin closed? */
+ None => return None // UI ded
}
}
@@ -424,7 +441,8 @@ fn do_ui(ui_rx_orig: chan::Receiver<Result<termion::event::Event, std::io::Error
}
tw::AppState::Reconnect => {
tweeter.state = tw::AppState::View;
- return Some((ui_rx_orig.clone(), tweeter.profile.clone().map(|creds| connect_twitter_stream(tweeter.app_key.clone(), creds))));
+ // TODO: reconnect *which*?
+ return None // Some((ui_rx_orig.clone(), tweeter.profile.clone().map(|creds| connect_twitter_stream(tweeter.app_key.clone(), creds))));
},
tw::AppState::Shutdown => {
tweeter.display_info.status("Saving cache...".to_owned());
@@ -471,10 +489,22 @@ fn url_encode(s: &str) -> String {
.replace("]", "%5d")
}
-fn connect_twitter_stream(app_cred: tw::Credential, user_cred: tw::Credential) -> chan::Receiver<Vec<u8>> {
- let (twete_tx, twete_rx) = chan::sync::<Vec<u8>>(0);
-
+// let (twete_tx, twete_rx) = chan::sync::<Vec<u8>>(0);
+#[derive(Debug)]
+enum TwitterConnectionState {
+ Connecting(u8),
+ Connected(u8),
+ Closed(u8)
+}
+fn connect_twitter_stream(
+ app_cred: tw::Credential,
+ user_cred: tw::Credential,
+ twete_tx: chan::Sender<Vec<u8>>,
+ coordination_tx: chan::Sender<TwitterConnectionState>,
+ conn_id: u8
+) {
std::thread::spawn(move || {
+ coordination_tx.send(TwitterConnectionState::Connecting(conn_id));
let mut core = Core::new().unwrap();
let connector = HttpsConnector::new(1, &core.handle()).unwrap();
@@ -491,6 +521,7 @@ fn connect_twitter_stream(app_cred: tw::Credential, user_cred: tw::Credential) -
println!("Twitter stream connect was abnormal: {}", status);
println!("result: {:?}", res);
}
+ coordination_tx.send(TwitterConnectionState::Connected(conn_id));
LineStream::new(res.body()
.map(|chunk| futures::stream::iter_ok(chunk.into_iter()))
.flatten())
@@ -507,7 +538,6 @@ fn connect_twitter_stream(app_cred: tw::Credential, user_cred: tw::Credential) -
Ok(_good) => (),
Err(e) => println!("Error in setting up: {}", e)
}
+ coordination_tx.send(TwitterConnectionState::Closed(conn_id));
});
-
- twete_rx
}
diff --git a/src/tw/mod.rs b/src/tw/mod.rs
index acee0c2..52ab31c 100644
--- a/src/tw/mod.rs
+++ b/src/tw/mod.rs
@@ -98,20 +98,14 @@ pub struct TwitterCache {
pub app_key: Credential,
// right now we're stuck assuming one profile.
// alts and such will be others here.
- pub profile: Option<Credential>,
- following: HashSet<String>,
- following_history: HashMap<String, (String, i64)>, // userid:date??
- pub followers: HashSet<String>,
- lost_followers: HashSet<String>,
- follower_history: HashMap<String, (String, i64)>, // userid:date??
+ pub curr_profile: Option<String>,
+ pub profiles: HashMap<String, TwitterProfile>,
threads: HashMap<String, u64>, // thread : latest_tweet_in_thread
#[serde(skip)]
pub needs_save: bool,
#[serde(skip)]
pub caching_permitted: bool,
#[serde(skip)]
- pub current_user: User,
- #[serde(skip)]
id_conversions: IdConversions,
#[serde(skip)]
pub display_info: display::DisplayInfo,
@@ -271,6 +265,31 @@ fn parse_word_command<'a, 'b>(line: &'b str, commands: &[&'a Command]) -> Option
return None
}
+#[derive(Serialize, Deserialize, Clone)]
+pub struct TwitterProfile {
+ pub creds: Credential,
+ pub user: User,
+ following: HashSet<String>,
+ following_history: HashMap<String, (String, i64)>, // userid:date??
+ pub followers: HashSet<String>,
+ lost_followers: HashSet<String>,
+ follower_history: HashMap<String, (String, i64)> // userid:date??
+}
+
+impl TwitterProfile {
+ pub fn new(creds: Credential, user: User) -> TwitterProfile {
+ TwitterProfile {
+ creds: creds,
+ user: user,
+ following: HashSet::new(),
+ following_history: HashMap::new(),
+ followers: HashSet::new(),
+ lost_followers: HashSet::new(),
+ follower_history: HashMap::new()
+ }
+ }
+}
+
impl TwitterCache {
const PROFILE_DIR: &'static str = "cache/";
const TWEET_CACHE: &'static str = "cache/tweets.json";
@@ -289,15 +308,10 @@ impl TwitterCache {
// So, supporting multiple profiles will be ... interesting?
// how do we support a variable number of channels? which will be necessary as we'll
// have one channel up per twitter stream...
- profile: None, // this will become a HashMap when multiple profiles are supported
- following: HashSet::new(),
- following_history: HashMap::new(),
- followers: HashSet::new(),
- lost_followers: HashSet::new(),
- follower_history: HashMap::new(),
+ curr_profile: None,
+ profiles: HashMap::new(),
needs_save: false,
caching_permitted: true,
- current_user: User::default(),
threads: HashMap::new(),
id_conversions: IdConversions::default(),
display_info: display::DisplayInfo::default(),
@@ -305,6 +319,17 @@ impl TwitterCache {
}
}
+ pub fn current_profile(&self) -> Option<&TwitterProfile> {
+ match &self.curr_profile {
+ &Some(ref profile_name) => self.profiles.get(profile_name),
+ &None => None
+ }
+ }
+
+ pub fn current_handle(&self) -> Option<String> {
+ self.current_profile().map(|profile| profile.user.handle.to_owned())
+ }
+
// TODO: pull out the "Cache" part of TwitterCache, it can be serialized/deserialized - the
// rest of the history is just for the running instance..
pub fn handle_user_input(&mut self, line: Vec<u8>, mut queryer: &mut Queryer) {
@@ -322,8 +347,8 @@ impl TwitterCache {
cache.caching_permitted = false;
cache
}
- pub fn add_profile(&mut self, creds: Credential) {
- self.profile = Some(creds);
+ pub fn add_profile(&mut self, profile: TwitterProfile, name: Option<String>) {
+ self.profiles.insert(name.unwrap_or(profile.user.handle.to_owned()), profile);
if self.caching_permitted {
self.store_cache();
}
@@ -578,84 +603,96 @@ impl TwitterCache {
self.users.get(user_id)
}
pub fn set_following(&mut self, user_ids: Vec<String>) {
- let uid_set = user_ids.into_iter().collect::<HashSet<String>>();
+ self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| {
+ let uid_set = user_ids.into_iter().collect::<HashSet<String>>();
- let new_uids = &uid_set - &self.following;
- for user in &new_uids {
- self.display_info.status(format!("New following! {}", user));
- self.add_following(user);
- }
+ let new_uids = &uid_set - &profile.following;
+ for user in &new_uids {
+ self.display_info.status(format!("New following! {}", user));
+ self.add_following(user);
+ }
- let lost_uids = &self.following - &uid_set;
- for user in &lost_uids {
- self.display_info.status(format!("Bye, friend! {}", user));
- self.remove_following(user);
- }
+ let lost_uids = &profile.following - &uid_set;
+ for user in &lost_uids {
+ self.display_info.status(format!("Bye, friend! {}", user));
+ self.remove_following(user);
+ }
+ });
}
pub fn set_followers(&mut self, user_ids: Vec<String>) {
- let uid_set = user_ids.into_iter().collect::<HashSet<String>>();
+ self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| {
+ let uid_set = user_ids.into_iter().collect::<HashSet<String>>();
- let new_uids = &uid_set - &self.followers;
- for user in &new_uids {
- self.display_info.status(format!("New follower! {}", user));
- self.add_follower(user);
- }
+ let new_uids = &uid_set - &profile.followers;
+ for user in &new_uids {
+ self.display_info.status(format!("New follower! {}", user));
+ self.add_follower(user);
+ }
- let lost_uids = &self.followers - &uid_set;
- for user in &lost_uids {
- self.display_info.status(format!("Bye, friend! {}", user));
- self.remove_follower(user);
- }
+ let lost_uids = &profile.followers - &uid_set;
+ for user in &lost_uids {
+ self.display_info.status(format!("Bye, friend! {}", user));
+ self.remove_follower(user);
+ }
+ });
}
pub fn add_following(&mut self, user_id: &String) {
- self.needs_save = true;
- self.following.insert(user_id.to_owned());
- self.following_history.insert(user_id.to_owned(), ("following".to_string(), Utc::now().timestamp()));
+ self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| {
+ self.needs_save = true;
+ profile.following.insert(user_id.to_owned());
+ profile.following_history.insert(user_id.to_owned(), ("following".to_string(), Utc::now().timestamp()));
+ });
}
pub fn remove_following(&mut self, user_id: &String) {
- self.needs_save = true;
- self.following.remove(user_id);
- self.following_history.insert(user_id.to_owned(), ("unfollowing".to_string(), Utc::now().timestamp()));
+ self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| {
+ self.needs_save = true;
+ profile.following.remove(user_id);
+ profile.following_history.insert(user_id.to_owned(), ("unfollowing".to_string(), Utc::now().timestamp()));
+ });
}
pub fn add_follower(&mut self, user_id: &String) {
- self.needs_save = true;
- self.followers.insert(user_id.to_owned());
- self.lost_followers.remove(user_id);
- self.follower_history.insert(user_id.to_owned(), ("follow".to_string(), Utc::now().timestamp()));
+ self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| {
+ self.needs_save = true;
+ profile.followers.insert(user_id.to_owned());
+ profile.lost_followers.remove(user_id);
+ profile.follower_history.insert(user_id.to_owned(), ("follow".to_string(), Utc::now().timestamp()));
+ });
}
pub fn remove_follower(&mut self, user_id: &String) {
- self.needs_save = true;
- self.followers.remove(user_id);
- self.lost_followers.insert(user_id.to_owned());
- self.follower_history.insert(user_id.to_owned(), ("unfollow".to_string(), Utc::now().timestamp()));
+ self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| {
+ self.needs_save = true;
+ profile.followers.remove(user_id);
+ profile.lost_followers.insert(user_id.to_owned());
+ profile.follower_history.insert(user_id.to_owned(), ("unfollow".to_string(), Utc::now().timestamp()));
+ });
}
fn look_up_user(&mut self, id: &str, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
let url = &format!("{}?user_id={}", ::USER_LOOKUP_URL, id);
- match self.profile {
- Some(ref user_creds) => queryer.do_api_get(url, &self.app_key, &user_creds),
+ match self.current_profile() {
+ Some(ref user_profile) => queryer.do_api_get(url, &self.app_key, &user_profile.creds),
None => Err("No authorized user to conduct lookup".to_owned())
}
}
fn look_up_tweet(&mut self, id: &str, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
let url = &format!("{}&id={}", ::TWEET_LOOKUP_URL, id);
- match self.profile {
- Some(ref user_creds) => queryer.do_api_get(url, &self.app_key, &user_creds),
+ match self.current_profile() {
+ Some(ref user_profile) => queryer.do_api_get(url, &self.app_key, &user_profile.creds),
None => Err("No authorized user to conduct lookup".to_owned())
}
}
pub fn get_settings(&self, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
- match self.profile {
- Some(ref user_creds) => queryer.do_api_get(::ACCOUNT_SETTINGS_URL, &self.app_key, &user_creds),
+ match self.current_profile() {
+ Some(ref user_profile) => queryer.do_api_get(::ACCOUNT_SETTINGS_URL, &self.app_key, &user_profile.creds),
None => Err("No authorized user to request settings".to_owned())
}
}
pub fn get_followers(&self, queryer: &mut ::Queryer) -> Result<serde_json::Value, String> {
- match self.profile {
- Some(ref user_creds) => queryer.do_api_get(::GET_FOLLOWER_IDS_URL, &self.app_key, &user_creds),
+ match self.current_profile() {
+ Some(ref user_profile) => queryer.do_api_get(::GET_FOLLOWER_IDS_URL, &self.app_key, &user_profile.creds),
None => Err("No authorized user to request followers".to_owned())
}
}
@@ -746,12 +783,9 @@ fn handle_twitter_welcome(
let settings = tweeter.get_settings(queryer).unwrap();
let maybe_my_name = settings["screen_name"].as_str();
if let Some(my_name) = maybe_my_name {
- tweeter.current_user = User {
- id: "".to_string(),
- handle: my_name.to_owned(),
- name: my_name.to_owned()
- };
- tweeter.display_info.status(format!("You are {}", tweeter.current_user.handle))
+ // TODO: come back to this when custom profile names are supported?
+ tweeter.curr_profile = Some(my_name.to_owned());
+ tweeter.display_info.status(format!("You are {}", my_name))
} else {
tweeter.display_info.status("Unable to make API call to figure out who you are...".to_string());
}