use once_cell::sync::Lazy; use rand::seq::SliceRandom; use std::io::BufRead; static FONTS: &[&str] = &[ "Albura/Albura-Regular.ttf", "AmaticSC-Bold.ttf", "Exo-Regular.otf", "EBGaramond08-Regular.otf", "Gidole-Regular.ttf", "Londrina/LondrinaBook-Regular.otf", "PlayenSans/PlayenSans-Regular.otf", "Playfulist.otf", "ProzaLibre-Regular.ttf", ]; static USER_AGENTS: &[&str] = &[ r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", r"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36", r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.44", r"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36", r"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", r"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0", r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36", r"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", r"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/111.0", r"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36", ]; static WORDS: Lazy> = Lazy::new(|| { let file = std::fs::File::open("lefff-3.4.mlex").unwrap(); let file_buf = std::io::BufReader::new(file); let mut words = Vec::new(); for line in file_buf.lines() { let line = line.unwrap(); if line.as_bytes().first() != Some(&b't') { continue; } let mut cols = line.split('\t'); let Some(word) = cols.next() else { continue }; let class = cols.next(); match class { Some("nc") => {} Some("adj") => { let Some(flex) = cols.nth(1) else { continue }; if flex.contains('p') || flex.contains('m') { continue; } } _ => continue, } let mut capitalized = String::from("T"); capitalized.push_str(&word[1..]); words.push(capitalized); } //eprintln!("Words: {}", words.len()); words }); #[tokio::main] async fn main() { Lazy::force(&WORDS); tide::log::start(); let mut app = tide::new(); app.at("/").get(handle_request); app.listen(std::net::SocketAddr::new( std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)), 8137, )) .await .unwrap(); } async fn handle_request<'a>(_req: tide::Request<()>) -> tide::Result { let mut rng = rand::thread_rng(); let word = WORDS.choose(&mut rng).unwrap(); let client = reqwest::blocking::Client::builder() .user_agent(*USER_AGENTS.choose(&mut rng).unwrap()) .build() .unwrap(); let resp = match client .get(format!( "https://www.bing.com/images/search?q={word}&FORM=HDRSC3" )) .send() { Ok(resp) => resp, Err(e) => { eprintln!("Error requesting: {e:?}"); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body("Error requesting search engine") .build()); } }; if !resp.status().is_success() { eprintln!("Search engine status: {}", resp.status()); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body(format!("Search engine status: {}", resp.status())) .build()); } let page = match resp.text() { Ok(page) => page, Err(e) => { eprintln!("Error getting text: {e:?}"); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body("Error reading search result") .build()); } }; let pattern = r#"mediaurl="#; let url_start = match page.find(pattern) { Some(idx) => idx, None => { eprintln!("Error: cannot find pattern"); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body("Error: pattern not found in search result") .build()); } } + pattern.len(); let Some(url_len) = page[url_start..].find('&') else { eprintln!("Error: cannot find url end"); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body("Error: url end not found in search result") .build()); }; if url_len > 1024 { eprintln!("Error: url too long {url_len}"); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body("Error: url too long in search result") .build()); } let img_url_encoded = &page[url_start..url_start + url_len]; let img_url = match percent_encoding::percent_decode_str(img_url_encoded).decode_utf8() { Ok(img_url) => img_url, Err(e) => { eprintln!("Error: percent decoding url {e:?}"); return Ok(tide::Response::builder(500) .content_type(tide::http::mime::PLAIN) .body("Error: cannot decode url from search result") .build()); } }; if img_url.contains('"') { panic!("url contains quotes"); } let font = *FONTS.choose(&mut rng).unwrap(); Ok(tide::Response::builder(200) .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Headers", "*") .content_type(tide::http::mime::HTML) .body(format!(r#" MLeTomatic

Monnaie Libre et {word}

{word}
MLeTomaticImages trouvées par Bing, soumises au droit d'auteur. Mots tirés de Lefff, lire la licence dans le dépôt.
"#)) .build()) }