mod botdriver; mod browser; mod chromedriver; mod webserve; use botdriver::BotDriver; use browser::BrowserSession; use chromedriver::Driver; use futures::channel::oneshot; use serde::{Deserialize, Serialize}; use std::{ io::{self, Write}, sync::{ atomic::{AtomicBool, Ordering}, Arc, }, time::Duration, }; use tokio::time; const STATIC_ADDR: &str = "127.0.0.1:8010"; #[tokio::main] async fn main() { if let Err(e) = main_but_with_result().await { println!("frc exited with error: {}", e); std::process::exit(1); } std::process::exit(0); } async fn main_but_with_result() -> Result<(), anyhow::Error> { let mut cfg: FRCConfig = confy::load_path(CFG_NAME)?; if cfg.username == None || cfg.token == None { cfg = create_config()?; confy::store_path(CFG_NAME, cfg.clone())?; } let username = cfg.username.unwrap(); let static_handle = webserve::start_server(STATIC_ADDR.to_string()).await?; println!("Starting frc with username to listen to: {}", username); let mut chr = Driver::new()?; let driver = BrowserSession::new().await?; let browser = driver.handle(); let mut bot = BotDriver::new(cfg.token.unwrap(), username); browser.start(STATIC_ADDR).await?; println!(); println!("#####################"); println!("# launching frc #"); println!("# #"); println!("# frc can be closed #"); println!("# by an interrupt #"); println!("# (Ctl-C) #"); println!("#####################"); println!(); tokio::spawn(async move { let should_exit = Arc::new(AtomicBool::new(false)); let (chr_sender, mut chr_receiver) = oneshot::channel(); chr.exit_waiter(chr_sender); signal_hook::flag::register(signal_hook::consts::SIGINT, Arc::clone(&should_exit)).unwrap(); signal_hook::flag::register(signal_hook::consts::SIGTERM, Arc::clone(&should_exit)) .unwrap(); loop { if should_exit.load(Ordering::Relaxed) || chr_receiver.try_recv().unwrap().is_some() { break; } time::sleep(Duration::from_millis(100)).await; } println!(); println!("closing down"); static_handle.abort(); driver.stop().await.expect("failed killing browser"); let output = chr.exit_with_output().expect("failed killing chromedriver"); println!("chromedriver exited with status code {}", output.status); if output.stderr.len() > 0 { print!("chromedriver stderr output:"); io::stdout().write(&output.stderr).unwrap(); println!(); } if output.stdout.len() > 0 { print!("chromedriver stdout output:"); io::stdout().write(&output.stdout).unwrap(); println!(); } std::process::exit(0); }); bot.listen_and_paste(browser).await.unwrap(); Ok(()) } const CFG_NAME: &str = "frc.toml"; #[derive(Serialize, Deserialize, Clone, Debug)] struct FRCConfig { username: Option, token: Option, } impl ::std::default::Default for FRCConfig { fn default() -> Self { Self { username: None, token: None, } } } fn create_config() -> Result { let username = get_config_from_user("Enter username to listen to:")?; let token = get_config_from_user("Enter telegram bot token:")?; Ok(FRCConfig { username: Some(username), token: Some(token), }) } fn get_config_from_user(prompt: &str) -> Result { print!("{} ", prompt); io::stdout().flush()?; let mut value = String::new(); io::stdin().read_line(&mut value)?; Ok(value.trim().to_string()) }