use std::error::Error; use elefren::entities::status::Status; use elefren::helpers::cli; use elefren::helpers::toml; use elefren::prelude::*; use clap::{App, Arg}; use regex::Regex; // TODO Put data.toml file path in an env var fn main() -> Result<(), Box> { let matches = App::new("tootre") .version("0.1") .author("annieversary") .about("grep through own toots") .arg( Arg::with_name("regex") .short("r") .long("regex") .value_name("REGEX") .required(true) .help("Regex to use for matching toots"), ) .arg( Arg::with_name("max") .short("m") .long("max") .value_name("MAX") .help("Max number of toots to find"), ) .arg( Arg::with_name("boosts") .short("b") .long("boosts") .help("Includes boosts in the search"), ) .arg( Arg::with_name("v") .short("v") .long("verbose") .help("Sets the level of verbosity"), ) .get_matches(); let max = matches .value_of("max") .map(|val| val.parse::().ok()) .flatten(); let verbose = matches.is_present("v"); let include_retoots = matches.is_present("boosts"); if let Some(regex) = matches.value_of("regex") { run(regex, max, verbose, include_retoots) } else { panic!("Regex was not provided"); } } /// Returns path for config fn get_path() -> std::path::PathBuf { if let Some(mut home) = home::home_dir() { home.push(".tootre.toml"); home } else { ".tootre.toml".into() } } fn run( regex: &str, max_statuses: Option, verbose: bool, include_retoots: bool, ) -> Result<(), Box> { let client = if let Ok(data) = toml::from_file(get_path()) { Mastodon::from(data) } else { register()? }; if verbose { println!("Getting statuses..."); } // Get statuses for own account let acc = client.verify_credentials()?; let statuses = client.statuses(&acc.id, None)?; let regex = Regex::new(regex).unwrap(); let mut count = 0; println!("\n\nResults:\n"); // Search through statuses for status in statuses.items_iter() { // Add it to results if it matches and it isn't a retoot // or if it matches and isn't a retoot if status_matches(&status, ®ex) && (include_retoots || status.reblog.is_none()) { print_status(&status, verbose); count += 1; } // If there's a limit of results, check it and exit if let Some(max) = max_statuses { if count >= max { break; } } } println!("Found {} results", count); Ok(()) } fn print_status(status: &Status, verbose: bool) { if !status.spoiler_text.is_empty() { println!("CW: {}", status.spoiler_text); } println!("Content: {}", status.content); // Print actual url if it's a retoot if let Some(retoot) = &status.reblog { if let Some(url) = &retoot.url { println!("Url: {}", url); } } else if let Some(url) = &status.url { println!("Url: {}", url); } // Print some extra info if verbose { println!("Created at: {}", status.created_at); if let Some(retoot) = &status.reblog { println!("Is retoot: yes ({})", retoot.account.acct); } else { // We don't want to print these if it's a retoot println!("Favs: {}", status.favourites_count); println!("Retoots: {}", status.reblogs_count); } } println!(); } fn status_matches(status: &Status, regex: &Regex) -> bool { regex.is_match(&status.content) || regex.is_match(&status.spoiler_text) } fn register() -> Result> { println!("You are not registered, registering..."); let url = read_line("Enter instance url (eg: https://mastodon.social):")?; let registration = Registration::new(url).client_name("tootre").build()?; let mastodon = cli::authenticate(registration)?; // Save app data for using on the next run. toml::to_file(&mastodon, get_path())?; Ok(mastodon) } pub fn read_line(message: &str) -> Result> { println!("{}", message); let mut input = String::new(); std::io::stdin().read_line(&mut input)?; Ok(input.trim().to_string()) }