use log::*; use regex::Regex; use std::collections::HashSet; use std::io::Write; use steamguard::Confirmation; use termion::{ event::{Event, Key}, input::TermRead, raw::IntoRawMode, screen::AlternateScreen, }; lazy_static! { static ref CAPTCHA_VALID_CHARS: Regex = Regex::new("^([A-H]|[J-N]|[P-R]|[T-Z]|[2-4]|[7-9]|[@%&])+$").unwrap(); } pub fn validate_captcha_text(text: &String) -> bool { return CAPTCHA_VALID_CHARS.is_match(text); } #[test] fn test_validate_captcha_text() { assert!(validate_captcha_text(&String::from("2WWUA@"))); assert!(validate_captcha_text(&String::from("3G8HT2"))); assert!(validate_captcha_text(&String::from("3J%@X3"))); assert!(validate_captcha_text(&String::from("2GCZ4A"))); assert!(validate_captcha_text(&String::from("3G8HT2"))); assert!(!validate_captcha_text(&String::from("asd823"))); assert!(!validate_captcha_text(&String::from("!PQ4RD"))); assert!(!validate_captcha_text(&String::from("1GQ4XZ"))); assert!(!validate_captcha_text(&String::from("8GO4XZ"))); assert!(!validate_captcha_text(&String::from("IPQ4RD"))); assert!(!validate_captcha_text(&String::from("0PT4RD"))); assert!(!validate_captcha_text(&String::from("APTSRD"))); assert!(!validate_captcha_text(&String::from("AP5TRD"))); assert!(!validate_captcha_text(&String::from("AP6TRD"))); } /// Prompt the user for text input. pub fn prompt() -> String { let mut text = String::new(); let _ = std::io::stdout().flush(); std::io::stdin() .read_line(&mut text) .expect("Did not enter a correct string"); return String::from(text.strip_suffix('\n').unwrap()); } pub fn prompt_captcha_text(captcha_gid: &String) -> String { println!("Captcha required. Open this link in your web browser: https://steamcommunity.com/public/captcha.php?gid={}", captcha_gid); let mut captcha_text; loop { print!("Enter captcha text: "); captcha_text = prompt(); if captcha_text.len() > 0 && validate_captcha_text(&captcha_text) { break; } warn!("Invalid chars for captcha text found in user's input. Prompting again..."); } return captcha_text; } /// Returns a tuple of (accepted, denied). Ignored confirmations are not included. pub fn prompt_confirmation_menu( confirmations: Vec, ) -> (Vec, Vec) { println!("press a key other than enter to show the menu."); let mut to_accept_idx: HashSet = HashSet::new(); let mut to_deny_idx: HashSet = HashSet::new(); let mut screen = AlternateScreen::from(std::io::stdout().into_raw_mode().unwrap()); let stdin = std::io::stdin(); let mut selected_idx = 0; for c in stdin.events() { match c.expect("could not get events") { Event::Key(Key::Char('a')) => { to_accept_idx.insert(selected_idx); to_deny_idx.remove(&selected_idx); } Event::Key(Key::Char('d')) => { to_accept_idx.remove(&selected_idx); to_deny_idx.insert(selected_idx); } Event::Key(Key::Char('i')) => { to_accept_idx.remove(&selected_idx); to_deny_idx.remove(&selected_idx); } Event::Key(Key::Char('A')) => { (0..confirmations.len()).for_each(|i| { to_accept_idx.insert(i); to_deny_idx.remove(&i); }); } Event::Key(Key::Char('D')) => { (0..confirmations.len()).for_each(|i| { to_accept_idx.remove(&i); to_deny_idx.insert(i); }); } Event::Key(Key::Char('I')) => { (0..confirmations.len()).for_each(|i| { to_accept_idx.remove(&i); to_deny_idx.remove(&i); }); } Event::Key(Key::Up) if selected_idx > 0 => { selected_idx -= 1; } Event::Key(Key::Down) if selected_idx < confirmations.len() - 1 => { selected_idx += 1; } Event::Key(Key::Char('\n')) => { break; } Event::Key(Key::Esc) | Event::Key(Key::Ctrl('c')) => { return (vec![], vec![]); } _ => {} } write!( screen, "{}{}{}arrow keys to select, [a]ccept, [d]eny, [i]gnore, [enter] confirm choices\n\n", termion::clear::All, termion::cursor::Goto(1, 1), termion::color::Fg(termion::color::White) ) .unwrap(); for i in 0..confirmations.len() { if selected_idx == i { write!( screen, "\r{} >", termion::color::Fg(termion::color::LightYellow) ) .unwrap(); } else { write!(screen, "\r{} ", termion::color::Fg(termion::color::White)).unwrap(); } if to_accept_idx.contains(&i) { write!( screen, "{}[a]", termion::color::Fg(termion::color::LightGreen) ) .unwrap(); } else if to_deny_idx.contains(&i) { write!( screen, "{}[d]", termion::color::Fg(termion::color::LightRed) ) .unwrap(); } else { write!(screen, "[ ]").unwrap(); } if selected_idx == i { write!( screen, "{}", termion::color::Fg(termion::color::LightYellow) ) .unwrap(); } write!(screen, " {}\n", confirmations[i].description()).unwrap(); } } return ( to_accept_idx.iter().map(|i| confirmations[*i]).collect(), to_deny_idx.iter().map(|i| confirmations[*i]).collect(), ); } pub fn pause() { println!("Press any key to continue..."); let mut stdout = std::io::stdout().into_raw_mode().unwrap(); stdout.flush().unwrap(); std::io::stdin().events().next(); }