diff --git a/Cargo.lock b/Cargo.lock index fef0310..28079ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,7 +34,7 @@ dependencies = [ "accesskit_consumer", "atspi-common", "serde", - "thiserror", + "thiserror 1.0.68", "zvariant", ] @@ -190,7 +190,7 @@ dependencies = [ "ndk-context", "ndk-sys 0.5.0+25.2.9519653", "num_enum 0.7.2", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -448,7 +448,7 @@ dependencies = [ "secrecy", "serde", "serde_json", - "thiserror", + "thiserror 1.0.68", "tokio", "tokio-stream", "tokio-util", @@ -813,7 +813,7 @@ dependencies = [ "polling 3.5.0", "rustix 0.38.31", "slab", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -2087,6 +2087,16 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +dependencies = [ + "mac", + "new_debug_unreachable", +] + [[package]] name = "futures" version = "0.3.30" @@ -2478,6 +2488,30 @@ version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" +[[package]] +name = "html2text" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccdcacfbf50bfa32830312fe5cef296925e1a04ace93c579e4c94f5045805d06" +dependencies = [ + "html5ever", + "tendril", + "thiserror 2.0.12", + "unicode-width", +] + +[[package]] +name = "html5ever" +version = "0.31.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953cbbe631aae7fc0a112702ad5d3aaf09da38beaf45ea84610d6e1c358f569c" +dependencies = [ + "log", + "mac", + "markup5ever", + "match_token", +] + [[package]] name = "http" version = "1.1.0" @@ -2725,7 +2759,7 @@ dependencies = [ "rowan", "smol_str", "strum", - "thiserror", + "thiserror 1.0.68", "url", ] @@ -3156,7 +3190,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.68", "walkdir", ] @@ -3170,7 +3204,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.68", "walkdir", ] @@ -3185,7 +3219,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.68", "walkdir", "windows-sys 0.45.0", ] @@ -3440,6 +3474,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "mac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" + [[package]] name = "mach2" version = "0.4.2" @@ -3458,6 +3498,28 @@ dependencies = [ "libc", ] +[[package]] +name = "markup5ever" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a8096766c229e8c88a3900c9b44b7e06aa7f7343cc229158c3e58ef8f9973a" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "match_token" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "memchr" version = "2.7.1" @@ -3596,7 +3658,7 @@ dependencies = [ "ndk-sys 0.4.1+23.1.7779620", "num_enum 0.5.11", "raw-window-handle 0.5.2", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -3612,7 +3674,7 @@ dependencies = [ "num_enum 0.7.2", "raw-window-handle 0.5.2", "raw-window-handle 0.6.0", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -3639,6 +3701,12 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.24.3" @@ -4041,6 +4109,44 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -4175,6 +4281,12 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + [[package]] name = "prettyplease" version = "0.2.16" @@ -4269,6 +4381,7 @@ dependencies = [ "flume", "futures", "hound", + "html2text", "humantime", "once_cell", "open", @@ -4285,7 +4398,6 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", - "urlencoding", "uuid", "windows 0.52.0", ] @@ -4312,7 +4424,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "socket2 0.5.6", - "thiserror", + "thiserror 1.0.68", "tokio", "tracing", ] @@ -4329,7 +4441,7 @@ dependencies = [ "rustc-hash 2.0.0", "rustls", "slab", - "thiserror", + "thiserror 1.0.68", "tinyvec", "tracing", ] @@ -4461,7 +4573,7 @@ checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox 0.1.3", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -4559,7 +4671,7 @@ dependencies = [ "nom", "pin-project-lite", "reqwest", - "thiserror", + "thiserror 1.0.68", ] [[package]] @@ -4991,6 +5103,12 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "skia-bindings" version = "0.70.0" @@ -5054,7 +5172,7 @@ checksum = "ebf98969a520e19ec675435b8c21b883d4a06489947e137bbd30859b50ad0c36" dependencies = [ "i-slint-compiler", "spin_on", - "thiserror", + "thiserror 1.0.68", "toml_edit 0.21.1", ] @@ -5099,7 +5217,7 @@ dependencies = [ "log", "memmap2 0.9.4", "rustix 0.38.31", - "thiserror", + "thiserror 1.0.68", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -5219,6 +5337,31 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "string_cache" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", + "serde", +] + +[[package]] +name = "string_cache_codegen" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + [[package]] name = "strsim" version = "0.11.1" @@ -5260,7 +5403,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70" dependencies = [ "kurbo", - "siphasher", + "siphasher 0.3.11", ] [[package]] @@ -5412,6 +5555,17 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "tendril" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +dependencies = [ + "futf", + "mac", + "utf-8", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -5433,7 +5587,16 @@ version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02dd99dc800bbb97186339685293e1cc5d9df1f8fae2d0aecd9ff1c77efea892" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.68", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] @@ -5447,6 +5610,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "thread_local" version = "1.1.8" @@ -5716,7 +5890,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.68", "time", "tracing-subscriber", ] @@ -5879,6 +6053,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" +[[package]] +name = "unicode-width" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c" + [[package]] name = "unicode-xid" version = "0.2.4" @@ -5936,7 +6116,7 @@ dependencies = [ "log", "roxmltree", "simplecss", - "siphasher", + "siphasher 0.3.11", "svgtypes", "usvg-tree", ] @@ -5968,6 +6148,12 @@ dependencies = [ "tiny-skia-path", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8_iter" version = "1.0.4" @@ -6285,6 +6471,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + [[package]] name = "weezl" version = "0.1.8" diff --git a/Cargo.toml b/Cargo.toml index 7c8db45..cf4fbba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,4 +48,4 @@ windows = { version = "0.52.0", features = [ "Win32_Media_Audio_Endpoints", ] } duckduckgo_search = "0.1.3" -urlencoding = "2.1" +html2text = "0.15.1" diff --git a/src/main.rs b/src/main.rs index 10983b0..a772c54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,6 @@ mod windows_volume; use tracing::{debug, error, info, instrument, warn}; use tracing_appender::rolling::{RollingFileAppender, Rotation}; use duckduckgo_search::DuckDuckGoSearch; -use urlencoding::encode; use crate::speakstream::ss::SpeakStream; @@ -410,6 +409,18 @@ fn call_fn( } } + "open_url" => { + let args: serde_json::Value = serde_json::from_str(fn_args).unwrap(); + let url = args["url"].as_str().unwrap_or(""); + match tokio::runtime::Runtime::new() { + Ok(rt) => match rt.block_on(open_url(url)) { + Ok(content) => Some(content), + Err(err) => Some(format!("Failed to open url: {}", err)), + }, + Err(err) => Some(format!("Failed to create runtime: {}", err)), + } + } + "get_location" => match tokio::runtime::Runtime::new() { Ok(rt) => match rt.block_on(get_location()) { Ok(loc) => Some(format!("Approximate location: {}", loc)), @@ -847,8 +858,7 @@ async fn web_search(query: &str) -> Result { } println!("{}{}", "search query: ".purple(), query); let ddg = DuckDuckGoSearch::new(); - let encoded_query = encode(query); - match ddg.search(&encoded_query).await { + match ddg.search(query).await { Ok(results) => { println!("{}{:?}", "raw web search results: ".purple(), results); if results.is_empty() { @@ -896,6 +906,30 @@ async fn get_location() -> Result { )) } +async fn open_url(url: &str) -> Result { + if url.trim().is_empty() { + return Err("empty url".into()); + } + + let resp = reqwest::get(url) + .await + .map_err(|e| format!("Request failed: {}", e))?; + + if !resp.status().is_success() { + return Err(format!("Request failed with status: {}", resp.status())); + } + + let html = resp + .text() + .await + .map_err(|e| format!("Failed to read response: {}", e))?; + + let text = html2text::from_read(html.as_bytes(), 80) + .map_err(|e| format!("Failed to parse html: {}", e))?; + + Ok(text.chars().take(2000).collect()) +} + fn get_currently_active_log_file() -> Option { let mut entries: Vec<_> = fs::read_dir(&*LOGS_DIR) .expect("Failed to read directory") @@ -1461,6 +1495,16 @@ async fn main() -> Result<(), Box> { })) .build().unwrap(), + ChatCompletionFunctionsArgs::default() + .name("open_url") + .description("Fetches a web page and returns plain text content.") + .parameters(json!({ + "type": "object", + "properties": {"url": {"type": "string"}}, + "required": ["url"], + })) + .build().unwrap(), + ChatCompletionFunctionsArgs::default() .name("set_timer_at") .description("Sets a timer to go off at a specific time. Pass the time as rfc3339 datetime string. Example: \"2024-12-04T00:44:00-08:00\". The description field is optional, add descriptions that will tell you what to remind the user to do, if anything, after the timer goes off.")