aboutsummaryrefslogtreecommitdiffstats
path: root/src/commands/autocomplete.rs
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-07-08 02:13:35 +0200
committersalmonik <raisersoft@proton.me>2026-07-08 02:13:35 +0200
commitcd8ed8bc5793985df2c1692072a932ceee517b95 (patch)
tree42d18a4140259b5c68b4dd05b7fcb398f78b8d7f /src/commands/autocomplete.rs
parentMerge pull request 'Voice related fixes, alongside simplifying assistant' (#5... (diff)
downloadsenator-sperma-cd8ed8bc5793985df2c1692072a932ceee517b95.tar.gz
senator-sperma-cd8ed8bc5793985df2c1692072a932ceee517b95.zip
OPEN THE RUST GATES
Signed-off-by: salmonik <raisersoft@proton.me>
Diffstat (limited to '')
-rw-r--r--src/commands/autocomplete.rs128
1 files changed, 128 insertions, 0 deletions
diff --git a/src/commands/autocomplete.rs b/src/commands/autocomplete.rs
new file mode 100644
index 0000000..497aec9
--- /dev/null
+++ b/src/commands/autocomplete.rs
@@ -0,0 +1,128 @@
1use poise::serenity_prelude as serenity;
2
3use crate::Data;
4use crate::copyparty::{html_decode, percent_decode};
5use crate::error::Error;
6
7type Context<'a> = poise::Context<'a, Data, Error>;
8
9const MAX_CHOICE_LEN: usize = 99;
10
11fn filename_label(raw_path: &str) -> Option<String> {
12 let raw_name = raw_path.rsplit('/').next()?;
13 if raw_name.is_empty() {
14 return None;
15 }
16 let decoded = html_decode(&percent_decode(raw_name));
17 if decoded.len() > MAX_CHOICE_LEN {
18 let mut s: String = decoded.chars().take(MAX_CHOICE_LEN - 1).collect();
19 s.push('\u{2026}');
20 Some(s)
21 } else {
22 Some(decoded)
23 }
24}
25
26pub async fn audio_file<'a>(
27 ctx: Context<'_>,
28 partial: &'a str,
29) -> Vec<serenity::AutocompleteChoice> {
30 eprintln!("[DEBUG] autocomplete audio_file(partial={partial:?})");
31 let results = ctx
32 .data()
33 .copyparty
34 .search(partial, 25, &["mp3", "wav", "ogg", "flac", "m4a", "opus"])
35 .await;
36 match results {
37 Ok(hits) => {
38 eprintln!("[DEBUG] audio_file got {} raw hits", hits.len());
39 let mut choices = Vec::new();
40 for (i, hit) in hits.into_iter().enumerate() {
41 let rp = match &hit.rp {
42 Some(p) => p.clone(),
43 None => continue,
44 };
45 eprintln!(
46 "[DEBUG] hit[{i}]: rp={rp:?}, url={:?}, name={:?}, path={:?}",
47 hit.url, hit.name, hit.path
48 );
49 let Some(label) = filename_label(&rp) else {
50 continue;
51 };
52 ctx.data()
53 .ac_stash(ctx.author().id, label.clone(), hit)
54 .await;
55 eprintln!("[DEBUG] -> cached as {label:?}");
56 choices.push(serenity::AutocompleteChoice::new(label.clone(), rp));
57 if choices.len() >= 25 {
58 break;
59 }
60 }
61 eprintln!("[DEBUG] audio_file returning {} choices", choices.len());
62 choices
63 }
64 Err(e) => {
65 eprintln!("[DEBUG] audio_file search failed: {e}");
66 vec![]
67 }
68 }
69}
70
71pub async fn any_file<'a>(ctx: Context<'_>, partial: &'a str) -> Vec<serenity::AutocompleteChoice> {
72 eprintln!("[DEBUG] autocomplete any_file(partial={partial:?})");
73 let results = ctx.data().copyparty.search(partial, 25, &[]).await;
74 match results {
75 Ok(hits) => {
76 eprintln!("[DEBUG] any_file got {} raw hits", hits.len());
77 let mut choices = Vec::new();
78 for (i, hit) in hits.into_iter().enumerate() {
79 let rp = match &hit.rp {
80 Some(p) => p.clone(),
81 None => continue,
82 };
83 eprintln!(
84 "[DEBUG] hit[{i}]: rp={rp:?}, url={:?}, name={:?}, path={:?}",
85 hit.url, hit.name, hit.path
86 );
87 let Some(label) = filename_label(&rp) else {
88 continue;
89 };
90 ctx.data()
91 .ac_stash(ctx.author().id, label.clone(), hit)
92 .await;
93 eprintln!("[DEBUG] -> cached as {label:?}");
94 choices.push(serenity::AutocompleteChoice::new(label.clone(), label));
95 if choices.len() >= 25 {
96 break;
97 }
98 }
99 eprintln!("[DEBUG] any_file returning {} choices", choices.len());
100 choices
101 }
102 Err(e) => {
103 eprintln!("[DEBUG] any_file search failed: {e}");
104 vec![]
105 }
106 }
107}
108
109pub async fn trigger_name<'a>(ctx: Context<'_>, partial: &'a str) -> Vec<String> {
110 eprintln!("[DEBUG] autocomplete trigger_name(partial={partial:?})");
111 let db_pool = &ctx.data().db_pool;
112 match crate::database::list_triggers(db_pool).await {
113 Ok(triggers) => {
114 let names: Vec<_> = triggers
115 .into_iter()
116 .map(|t| t.name)
117 .filter(|name| partial.is_empty() || name.starts_with(partial))
118 .take(25)
119 .collect();
120 eprintln!("[DEBUG] trigger_name returning {} names", names.len());
121 names
122 }
123 Err(e) => {
124 eprintln!("[DEBUG] trigger_name query failed: {e}");
125 vec![]
126 }
127 }
128}