diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-06 21:34:53 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-06 21:34:53 +0100 |
| commit | f471d4caefff51d49b69551412f4859d99f674a2 (patch) | |
| tree | 1b48ef33957c39522eaaa599324e1d01ac85178f /src/commands/utilities.rs | |
| parent | tests: skip the piper smoke test when models are absent (diff) | |
| download | senator-sperma-f471d4caefff51d49b69551412f4859d99f674a2.tar.gz senator-sperma-f471d4caefff51d49b69551412f4859d99f674a2.zip | |
revert required permissionsmain
Signed-off-by: salmonik <salmonik@tuta.io>
Diffstat (limited to '')
| -rw-r--r-- | src/commands/utilities.rs | 62 |
1 files changed, 39 insertions, 23 deletions
diff --git a/src/commands/utilities.rs b/src/commands/utilities.rs index f43b30e..f585d83 100644 --- a/src/commands/utilities.rs +++ b/src/commands/utilities.rs | |||
| @@ -4,11 +4,11 @@ use poise::serenity_prelude as serenity; | |||
| 4 | use tracing::warn; | 4 | use tracing::warn; |
| 5 | 5 | ||
| 6 | use super::autocomplete::any_file; | 6 | use super::autocomplete::any_file; |
| 7 | use crate::music::sanitize_yt_url; | ||
| 8 | use crate::Data; | 7 | use crate::Data; |
| 9 | use crate::copyparty::{html_decode, percent_decode, redact_url}; | 8 | use crate::copyparty::{html_decode, percent_decode, redact_url}; |
| 10 | use crate::error::{Error, Result}; | 9 | use crate::error::{Error, Result}; |
| 11 | use crate::lang; | 10 | use crate::lang; |
| 11 | use crate::music::sanitize_yt_url; | ||
| 12 | 12 | ||
| 13 | type Context<'a> = poise::Context<'a, Data, Error>; | 13 | type Context<'a> = poise::Context<'a, Data, Error>; |
| 14 | 14 | ||
| @@ -90,19 +90,12 @@ fn safe_basename(name: &str) -> String { | |||
| 90 | /// Create a unique per-operation directory and return | 90 | /// Create a unique per-operation directory and return |
| 91 | /// `(directory, input_path, output_path)`. Concurrent commands can never | 91 | /// `(directory, input_path, output_path)`. Concurrent commands can never |
| 92 | /// overwrite or delete each other's files. | 92 | /// overwrite or delete each other's files. |
| 93 | async fn temp_op( | 93 | async fn temp_op(original: &str, output_name: &str) -> std::io::Result<(String, String, String)> { |
| 94 | original: &str, | ||
| 95 | output_name: &str, | ||
| 96 | ) -> std::io::Result<(String, String, String)> { | ||
| 97 | let safe = safe_basename(original); | 94 | let safe = safe_basename(original); |
| 98 | let ext = ext_of(&safe) | 95 | let ext = ext_of(&safe) |
| 99 | .map(|ext| format!(".{ext}")) | 96 | .map(|ext| format!(".{ext}")) |
| 100 | .unwrap_or_default(); | 97 | .unwrap_or_default(); |
| 101 | let dir = format!( | 98 | let dir = format!("{}/op_{}", crate::config::temp_path(), uuid::Uuid::new_v4()); |
| 102 | "{}/op_{}", | ||
| 103 | crate::config::temp_path(), | ||
| 104 | uuid::Uuid::new_v4() | ||
| 105 | ); | ||
| 106 | tokio::fs::create_dir_all(&dir).await?; | 99 | tokio::fs::create_dir_all(&dir).await?; |
| 107 | let input_path = format!("{dir}/input{ext}"); | 100 | let input_path = format!("{dir}/input{ext}"); |
| 108 | let output_path = format!("{dir}/{}", safe_basename(output_name)); | 101 | let output_path = format!("{dir}/{}", safe_basename(output_name)); |
| @@ -303,7 +296,10 @@ async fn compress_attachment_bytes( | |||
| 303 | /// Compress audio bytes for playback using Vorbis (symphonia-decodable). | 296 | /// Compress audio bytes for playback using Vorbis (symphonia-decodable). |
| 304 | /// Uses Vorbis instead of Opus because symphonia 0.6 lacks an Opus decoder. | 297 | /// Uses Vorbis instead of Opus because symphonia 0.6 lacks an Opus decoder. |
| 305 | /// Returns the compressed .ogg bytes on success. | 298 | /// Returns the compressed .ogg bytes on success. |
| 306 | pub(crate) async fn compress_audio_bytes(input_bytes: &[u8], original_name: &str) -> Option<Vec<u8>> { | 299 | pub(crate) async fn compress_audio_bytes( |
| 300 | input_bytes: &[u8], | ||
| 301 | original_name: &str, | ||
| 302 | ) -> Option<Vec<u8>> { | ||
| 307 | let safe_name = safe_basename(original_name); | 303 | let safe_name = safe_basename(original_name); |
| 308 | let stem = stem_of(&safe_name).to_string(); | 304 | let stem = stem_of(&safe_name).to_string(); |
| 309 | let out_filename = format!("compressed_{stem}.ogg"); | 305 | let out_filename = format!("compressed_{stem}.ogg"); |
| @@ -378,9 +374,28 @@ pub(crate) fn extract_urls(content: &str) -> Vec<String> { | |||
| 378 | b.is_ascii_alphanumeric() | 374 | b.is_ascii_alphanumeric() |
| 379 | || matches!( | 375 | || matches!( |
| 380 | b, | 376 | b, |
| 381 | b'-' | b'.' | b'_' | b'~' | b':' | b'/' | b'?' | b'#' | b'[' | b']' | b'@' | 377 | b'-' | b'.' |
| 382 | | b'!' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b'+' | b',' | b';' | 378 | | b'_' |
| 383 | | b'%' | b'=' | 379 | | b'~' |
| 380 | | b':' | ||
| 381 | | b'/' | ||
| 382 | | b'?' | ||
| 383 | | b'#' | ||
| 384 | | b'[' | ||
| 385 | | b']' | ||
| 386 | | b'@' | ||
| 387 | | b'!' | ||
| 388 | | b'$' | ||
| 389 | | b'&' | ||
| 390 | | b'\'' | ||
| 391 | | b'(' | ||
| 392 | | b')' | ||
| 393 | | b'*' | ||
| 394 | | b'+' | ||
| 395 | | b',' | ||
| 396 | | b';' | ||
| 397 | | b'%' | ||
| 398 | | b'=' | ||
| 384 | ) | 399 | ) |
| 385 | } | 400 | } |
| 386 | let bytes = content.as_bytes(); | 401 | let bytes = content.as_bytes(); |
| @@ -445,9 +460,10 @@ async fn find_downloaded_file(dir: &str) -> Option<(String, String)> { | |||
| 445 | Err(_) => continue, | 460 | Err(_) => continue, |
| 446 | }; | 461 | }; |
| 447 | if let Some((t, _)) = &newest | 462 | if let Some((t, _)) = &newest |
| 448 | && *t >= mtime { | 463 | && *t >= mtime |
| 449 | continue; | 464 | { |
| 450 | } | 465 | continue; |
| 466 | } | ||
| 451 | newest = Some((mtime, entry.path().to_string_lossy().into_owned())); | 467 | newest = Some((mtime, entry.path().to_string_lossy().into_owned())); |
| 452 | } | 468 | } |
| 453 | newest.map(|(_, p)| { | 469 | newest.map(|(_, p)| { |
| @@ -540,8 +556,10 @@ async fn run_download( | |||
| 540 | Ok(result) => result.map_err(|e| Error::Custom(lang::fmt_ytdlp_err(&e)))?, | 556 | Ok(result) => result.map_err(|e| Error::Custom(lang::fmt_ytdlp_err(&e)))?, |
| 541 | Err(_) => { | 557 | Err(_) => { |
| 542 | let _ = tokio::fs::remove_dir_all(&dl_dir).await; | 558 | let _ = tokio::fs::remove_dir_all(&dl_dir).await; |
| 543 | ctx.say(lang::fmt_download_failed("download timed out after 10 minutes")) | 559 | ctx.say(lang::fmt_download_failed( |
| 544 | .await?; | 560 | "download timed out after 10 minutes", |
| 561 | )) | ||
| 562 | .await?; | ||
| 545 | return Ok(()); | 563 | return Ok(()); |
| 546 | } | 564 | } |
| 547 | }; | 565 | }; |
| @@ -1043,7 +1061,7 @@ pub async fn message_download(ctx: Context<'_>, message: serenity::Message) -> R | |||
| 1043 | } | 1061 | } |
| 1044 | 1062 | ||
| 1045 | /// Echo text to channel | 1063 | /// Echo text to channel |
| 1046 | #[poise::command(slash_command, required_permissions = "MANAGE_MESSAGES")] | 1064 | #[poise::command(slash_command)] |
| 1047 | pub async fn say(ctx: Context<'_>, #[description = "Message to send"] text: String) -> Result<()> { | 1065 | pub async fn say(ctx: Context<'_>, #[description = "Message to send"] text: String) -> Result<()> { |
| 1048 | // Acknowledge the interaction with an invisible ephemeral reply, delete it, | 1066 | // Acknowledge the interaction with an invisible ephemeral reply, delete it, |
| 1049 | // then post the actual text as a normal channel message (not a reply to the | 1067 | // then post the actual text as a normal channel message (not a reply to the |
| @@ -1059,9 +1077,7 @@ pub async fn say(ctx: Context<'_>, #[description = "Message to send"] text: Stri | |||
| 1059 | warn!("say: failed to delete ephemeral acknowledgement: {e}"); | 1077 | warn!("say: failed to delete ephemeral acknowledgement: {e}"); |
| 1060 | } | 1078 | } |
| 1061 | 1079 | ||
| 1062 | ctx.channel_id() | 1080 | ctx.channel_id().say(ctx.serenity_context(), &text).await?; |
| 1063 | .say(ctx.serenity_context(), &text) | ||
| 1064 | .await?; | ||
| 1065 | Ok(()) | 1081 | Ok(()) |
| 1066 | } | 1082 | } |
| 1067 | 1083 | ||
