From 501ba27e1cd52741988113ef47ee0fad7d0a5799 Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Tue, 6 Aug 2024 16:32:35 -0400 Subject: fixup unknown command, document commands --- src/commands/game.rs | 47 ++++++++++++++++++++++++++++---------------- src/commands/meme/create.rs | 6 ++++-- src/commands/meme/delete.rs | 4 ++-- src/commands/meme/history.rs | 23 ++++++++++++---------- src/commands/meme/invoke.rs | 47 +++++++++++++++++++++----------------------- src/commands/meme/mod.rs | 1 - src/commands/mod.rs | 17 +++++----------- src/commands/playback.rs | 45 ++++++++++++++++++++++-------------------- src/commands/sound_levels.rs | 9 ++++----- 9 files changed, 104 insertions(+), 95 deletions(-) (limited to 'src/commands') diff --git a/src/commands/game.rs b/src/commands/game.rs index 72633b5..3b16fd3 100644 --- a/src/commands/game.rs +++ b/src/commands/game.rs @@ -9,7 +9,10 @@ use std::{ }, }; -use anyhow::anyhow; +use anyhow::{ + anyhow, + Context, +}; use fnv::{ FnvHashMap, FnvHashSet, @@ -64,7 +67,7 @@ struct ProfileInfo { lazy_static! { static ref USER_MAP_STR: String = { - let default_path = PathBuf::from_str("user_id_mapping").unwrap(); + let default_path = PathBuf::from_str("user_id_mapping.json").unwrap(); let mapping_path = CONFIG.user_id_mapping.as_ref().unwrap_or(&default_path); fs::read_to_string(mapping_path).unwrap_or("{}".to_owned()) @@ -143,11 +146,17 @@ pub fn commands() -> Vec> { vec![installedgame(), ownedgame(), game(), updategaem()] } +/// Find a game everyone can play (marked installed). +/// +/// Looks up users in the general voice channel if no users are passed. #[poise::command(prefix_command, guild_only, category = "gaem", aliases("installedgaem"))] pub async fn installedgame(ctx: PoiseContext<'_>, args: util::RestVec) -> anyhow::Result<()> { _game(ctx, args.into_inner(), GameStatus::Installed).await } +/// Find a game everyone owns. +/// +/// Looks up users in the general voice channel if no users are passed. #[poise::command(prefix_command, guild_only, category = "gaem", aliases("ownedgaem"))] pub async fn ownedgame(ctx: PoiseContext<'_>, args: util::RestVec) -> anyhow::Result<()> { _game(ctx, args.into_inner(), GameStatus::NotInstalled).await @@ -202,8 +211,14 @@ pub fn get_user_id>(g: &Guild, s: S) -> StdResult, args: util::RestVec) -> anyhow::Result<()> { +async fn game( + ctx: PoiseContext<'_>, + #[description = "other users to include"] args: util::RestVec, +) -> anyhow::Result<()> { _game(ctx, args.into_inner(), GameStatus::Installed).await } @@ -417,13 +432,8 @@ async fn load_spreadsheet(client: &reqwest::Client) -> Result>> Ok(resp.value_ranges.into_iter().next().unwrap().values) } -#[poise::command( - slash_command, - prefix_command, - guild_only, - category = "gaem", - aliases("updategame") -)] +/// Find games that are out-of-date on the spreadsheet. +#[poise::command(prefix_command, guild_only, category = "gaem", aliases("updategame"))] pub async fn updategaem(ctx: PoiseContext<'_>, user: Option) -> anyhow::Result<()> { use regex::Regex; use std::borrow::Borrow; @@ -537,13 +547,16 @@ pub async fn updategaem(ctx: PoiseContext<'_>, user: Option) -> anyhow:: .get(u) .send() .await? + .error_for_status() + .context("retrieve steam info http status")? .json::() - .await? - .response - .games - .into_iter() - .map(|ge| ge.app_id) - .collect::>(); + .await + .context("decode steam resp")?; + + let games_owned = + games_owned.response.games.into_iter().map(|ge| ge.app_id).collect::>(); + + debug!("user owns {} steam games", games_owned.len()); let found_games = missing_appids .filter_map(|(ai, x)| { @@ -560,7 +573,7 @@ pub async fn updategaem(ctx: PoiseContext<'_>, user: Option) -> anyhow:: util::reply( ctx, format!( - "{n_missing} games owned on steam that are missing from the list:\n{found_games}" + "{n_missing} games owned on steam that aren't marked on the list:\n{found_games}" ), ) .await?; diff --git a/src/commands/meme/create.rs b/src/commands/meme/create.rs index cad9bfc..e2eacbf 100644 --- a/src/commands/meme/create.rs +++ b/src/commands/meme/create.rs @@ -28,7 +28,8 @@ use crate::{ FFMPEG_COMMAND, }; -#[poise::command(slash_command, prefix_command, guild_only, category = "memes")] +/// Add a text/image meme to the db. +#[poise::command(prefix_command, guild_only, category = "memes")] pub async fn addmeme( ctx: PoiseContext<'_>, title: String, @@ -94,7 +95,8 @@ pub async fn addmeme( Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes")] +/// Add an audiomeme to the meme db. +#[poise::command(prefix_command, guild_only, category = "memes")] pub async fn addaudiomeme( ctx: PoiseContext<'_>, title: String, diff --git a/src/commands/meme/delete.rs b/src/commands/meme/delete.rs index 25ddf0d..4769cc8 100644 --- a/src/commands/meme/delete.rs +++ b/src/commands/meme/delete.rs @@ -10,12 +10,12 @@ use crate::{ connection, delete_meme, }, - msg, util, PoiseContext, }; -#[poise::command(slash_command, prefix_command, guild_only, category = "memes", aliases("delmem"))] +/// Delete a meme by name. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("delmem"))] pub async fn delmeme(ctx: PoiseContext<'_>, title: String) -> anyhow::Result<()> { let mut conn = connection().await?; diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs index cfd78df..335603f 100644 --- a/src/commands/meme/history.rs +++ b/src/commands/meme/history.rs @@ -48,13 +48,8 @@ lazy_static! { static CLEAN_DATE_FORMAT: &str = "%b %-e %Y"; -#[poise::command( - slash_command, - prefix_command, - guild_only, - category = "memes", - aliases("what", "hwaet", "hwæt") -)] +/// Print info about the last meme. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("what", "hwaet", "hwæt"))] pub async fn wat(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let mut conn = connection().await?; @@ -108,7 +103,8 @@ pub async fn wat(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes", aliases("hist"))] +/// Print recent memes and who invoked them. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("hist"))] pub async fn history(ctx: PoiseContext<'_>, n: Option) -> anyhow::Result<()> { let n = n.unwrap_or(CONFIG.default_hist); @@ -204,7 +200,8 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option) -> anyhow::Result< Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes", aliases("stat"))] +/// Print stats about the meme database. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("stat"))] pub async fn stats(ctx: PoiseContext<'_>) -> anyhow::Result<()> { use db; use serenity::model::{ @@ -276,7 +273,8 @@ and *{}* was the most-memed overall ({})"#, Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes")] +/// Print stats about memers. +#[poise::command(prefix_command, guild_only, category = "memes")] pub async fn memers(ctx: PoiseContext<'_>) -> anyhow::Result<()> { use serenity::model::id::UserId; @@ -310,6 +308,11 @@ pub async fn memers(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } +/// Look up a meme by title or content. +/// +/// Can pass: +/// - `by=username` or `creator=username` to look up memes created by a specific user. +/// - `age=new` or `age=old` to sort the result by age. #[poise::command(prefix_command, guild_only, category = "memes")] pub async fn query(ctx: PoiseContext<'_>, rest: util::RestVec) -> anyhow::Result<()> { use regex::Regex; diff --git a/src/commands/meme/invoke.rs b/src/commands/meme/invoke.rs index e399e82..31b0085 100644 --- a/src/commands/meme/invoke.rs +++ b/src/commands/meme/invoke.rs @@ -14,48 +14,50 @@ use crate::{ }, util, PoiseContext, + RestVec, }; -#[poise::command(slash_command, prefix_command, guild_only, category = "memes", aliases("mem"))] -pub async fn meme(ctx: PoiseContext<'_>, #[rest] rest: String) -> anyhow::Result<()> { - _meme(ctx, rest, AudioPlayback::Optional).await +/// Post a meme. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("mem"))] +pub async fn meme(ctx: PoiseContext<'_>, title: RestVec) -> anyhow::Result<()> { + let title = title.into_inner().join(" "); + + _meme(ctx, title.trim(), AudioPlayback::Optional).await } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes")] +/// Post a random omen. +#[poise::command(prefix_command, guild_only, category = "memes", discard_spare_arguments)] pub async fn omen(ctx: PoiseContext<'_>) -> anyhow::Result<()> { _meme(ctx, "", AudioPlayback::Optional).await } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes")] +/// Post a random omen without audio. +#[poise::command(prefix_command, guild_only, category = "memes", discard_spare_arguments)] pub async fn silentomen(ctx: PoiseContext<'_>) -> anyhow::Result<()> { _meme(ctx, "", AudioPlayback::Prohibited).await } -#[poise::command(slash_command, prefix_command, guild_only, category = "memes")] +/// Post a random omen with audio. +#[poise::command(prefix_command, guild_only, category = "memes", discard_spare_arguments)] pub async fn audioomen(ctx: PoiseContext<'_>) -> anyhow::Result<()> { _meme(ctx, "", AudioPlayback::Required).await } -#[poise::command( - slash_command, - prefix_command, - guild_only, - category = "memes", - aliases("audiomeme", "audiomem") -)] -pub async fn audio_meme(ctx: PoiseContext<'_>, #[rest] rest: String) -> anyhow::Result<()> { - _meme(ctx, rest, AudioPlayback::Required).await +/// Post a random meme with audio. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("audiomeme", "audiomem"))] +pub async fn audio_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> { + _meme(ctx, "", AudioPlayback::Required).await } +/// Post a random meme without audio. #[poise::command( - slash_command, prefix_command, guild_only, category = "memes", aliases("silentmeme", "silentmem") )] -pub async fn silent_meme(ctx: PoiseContext<'_>, #[rest] rest: String) -> anyhow::Result<()> { - _meme(ctx, rest, AudioPlayback::Prohibited).await +pub async fn silent_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> { + _meme(ctx, "", AudioPlayback::Prohibited).await } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] @@ -132,13 +134,8 @@ async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyh } } -#[poise::command( - slash_command, - prefix_command, - guild_only, - category = "memes", - aliases("raremem", "rarememe") -)] +/// Post a rare meme. +#[poise::command(prefix_command, guild_only, category = "memes", aliases("raremem", "rarememe"))] pub async fn rare_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let should_audio = util::users_listening(ctx.serenity_context()).await?; diff --git a/src/commands/meme/mod.rs b/src/commands/meme/mod.rs index 0108219..9495ec7 100644 --- a/src/commands/meme/mod.rs +++ b/src/commands/meme/mod.rs @@ -32,7 +32,6 @@ use crate::{ Audio, Meme, }, - msg, util, PoiseContext, CONFIG, diff --git a/src/commands/mod.rs b/src/commands/mod.rs index ba87adb..1ad4a59 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -20,17 +20,9 @@ pub(crate) mod today; pub use self::meme::*; pub fn commands() -> Vec> { - let mut commands = vec![ - playback::play(), - playback::pause(), - playback::resume(), - playback::die(), - playback::list(), - sound_levels::mute(), - sound_levels::unmute(), - roll::roll(), - help(), - ]; + let mut commands = vec![sound_levels::mute(), sound_levels::unmute(), roll::roll(), help()]; + + commands.extend(playback::commands()); #[cfg(feature = "games")] commands.extend(game::commands()); @@ -41,7 +33,8 @@ pub fn commands() -> Vec> { commands } -#[poise::command(slash_command, prefix_command, aliases("halp"))] +/// Print this help text. +#[poise::command(prefix_command, aliases("halp"))] pub async fn help(ctx: PoiseContext<'_>, command: Option) -> anyhow::Result<()> { poise::builtins::pretty_help( ctx, diff --git a/src/commands/playback.rs b/src/commands/playback.rs index 98ae613..48f3286 100644 --- a/src/commands/playback.rs +++ b/src/commands/playback.rs @@ -5,7 +5,10 @@ use log::{ info, warn, }; -use serenity::prelude::*; +use serenity::{ + all::ReactionType, + prelude::*, +}; use songbird::{ input::YoutubeDl, Call, @@ -16,9 +19,14 @@ use crate::{ bot::HttpKey, util, PoiseContext, + PoiseData, CONFIG, }; +pub fn commands() -> impl IntoIterator> { + vec![play(), pause(), resume(), die(), list(), skip()] +} + pub async fn songbird(ctx: PoiseContext<'_>) -> anyhow::Result<(Arc, Arc>)> { let Some(gid) = ctx.guild_id() else { return Err(anyhow::anyhow!("no guild id").into()); @@ -46,7 +54,7 @@ pub async fn _play(ctx: PoiseContext<'_>, url: &url::Url) -> anyhow::Result<()> _ => None, }); - if host.map(|h| h.to_lowercase().contains("imgur")).unwrap_or(false) { + if host.is_some_and(|h| h.to_lowercase().contains("imgur")) { info!("detected imgur link"); if ctx.author().id == 106160362109272064 { @@ -73,10 +81,13 @@ pub async fn _play(ctx: PoiseContext<'_>, url: &url::Url) -> anyhow::Result<()> let input = YoutubeDl::new_ytdl_like("yt-dlp", client.clone(), url.to_string()); call.enqueue_input(input.into()).await; + util::react(ctx, ReactionType::Unicode("📣".to_owned())).await?; + Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only, category = "playback")] +/// Play a link. +#[poise::command(prefix_command, guild_only, category = "playback")] pub async fn play( ctx: PoiseContext<'_>, #[description = "link to play (if absent, resumes playback)"] u: Option, @@ -88,7 +99,8 @@ pub async fn play( _play(ctx, &u).await } -#[poise::command(slash_command, prefix_command, guild_only, category = "playback")] +/// Pause audio playback. +#[poise::command(prefix_command, guild_only, category = "playback")] pub async fn pause(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let (_sb, call) = songbird(ctx).await?; @@ -98,13 +110,8 @@ pub async fn pause(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } -#[poise::command( - slash_command, - prefix_command, - guild_only, - aliases("continue"), - category = "playback" -)] +/// Resume audio playback. +#[poise::command(prefix_command, guild_only, aliases("continue"), category = "playback")] pub async fn resume(ctx: PoiseContext<'_>) -> anyhow::Result<()> { _resume(ctx).await } @@ -118,7 +125,8 @@ async fn _resume(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only, category = "playback", aliases("next"))] +/// Skip the current track in the queue. +#[poise::command(prefix_command, guild_only, category = "playback", aliases("next"))] pub async fn skip(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let (_sb, call) = songbird(ctx).await?; @@ -128,12 +136,12 @@ pub async fn skip(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } +/// Stop playing audio and delete the queue. #[poise::command( - slash_command, prefix_command, guild_only, category = "playback", - aliases("sudoku", "fuckoff", "stop") + aliases("sudoku", "fuckoff", "stop", "kill") )] pub async fn die(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let (_sb, call) = songbird(ctx).await?; @@ -146,13 +154,8 @@ pub async fn die(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } -#[poise::command( - slash_command, - prefix_command, - guild_only, - category = "playback", - aliases("queue") -)] +/// List queued audio. +#[poise::command(prefix_command, guild_only, category = "playback", aliases("queue"))] pub async fn list(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let (_sb, call) = songbird(ctx).await?; diff --git a/src/commands/sound_levels.rs b/src/commands/sound_levels.rs index 9a6cfc6..4946f47 100644 --- a/src/commands/sound_levels.rs +++ b/src/commands/sound_levels.rs @@ -3,10 +3,8 @@ use crate::{ PoiseContext, }; -pub const DEFAULT_VOLUME: f32 = 0.20; -const MAX_VOLUME: f32 = 5.0; - -#[poise::command(slash_command, prefix_command, guild_only)] +/// Mute audio (don't pause). +#[poise::command(prefix_command, guild_only)] pub async fn mute(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let (_sb, call) = songbird(ctx).await?; @@ -16,7 +14,8 @@ pub async fn mute(ctx: PoiseContext<'_>) -> anyhow::Result<()> { Ok(()) } -#[poise::command(slash_command, prefix_command, guild_only)] +/// Unmute audio. +#[poise::command(prefix_command, guild_only)] pub async fn unmute(ctx: PoiseContext<'_>) -> anyhow::Result<()> { let (_sb, call) = songbird(ctx).await?; -- cgit v1.3.1