From a3966446bab8bf849457c24a9a6d05216f950e11 Mon Sep 17 00:00:00 2001 From: Andy Wortman Date: Sat, 18 Nov 2017 16:49:23 -0800 Subject: groundwork for multi-account use add connection state tracked per-stream, add explicit TwitterProfile mapped to names that can be used for accounts. default names are the handle of the corresponding twitter account. partition out user Credential to be per TwitterProfile, so fav, reply, etc, all come from the selected account. Multiplex twitter connections and message streams across chan (instead of earlier plan, which was to have one chan per thread) --- src/tw/mod.rs | 168 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 101 insertions(+), 67 deletions(-) (limited to 'src/tw/mod.rs') 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, - following: HashSet, - following_history: HashMap, // userid:date?? - pub followers: HashSet, - lost_followers: HashSet, - follower_history: HashMap, // userid:date?? + pub curr_profile: Option, + pub profiles: HashMap, threads: HashMap, // 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, + following_history: HashMap, // userid:date?? + pub followers: HashSet, + lost_followers: HashSet, + follower_history: HashMap // 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 { + 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, 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) { + 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) { - let uid_set = user_ids.into_iter().collect::>(); + self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| { + let uid_set = user_ids.into_iter().collect::>(); - 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) { - let uid_set = user_ids.into_iter().collect::>(); + self.current_profile().map(|profile| profile.to_owned()).map(|mut profile| { + let uid_set = user_ids.into_iter().collect::>(); - 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 { 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 { 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 { - 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 { - 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()); } -- cgit v1.1