diff options
| author | Nathan Perry <np@nathanperry.dev> | 2024-08-16 21:14:30 -0400 |
|---|---|---|
| committer | Nathan Perry <np@nathanperry.dev> | 2024-08-16 21:15:07 -0400 |
| commit | ef056edf92b678265a4666e1f9405e3b0ce66a42 (patch) | |
| tree | b3766d47ae2898d2a46f108de4e2be0ede6c400b /src/commands/meme | |
| parent | c5ce454319a7d54d3967c6ea7695543e943a37b2 (diff) | |
repo: overhaul for multitenancy
Diffstat (limited to 'src/commands/meme')
| -rw-r--r-- | src/commands/meme/create.rs | 2 | ||||
| -rw-r--r-- | src/commands/meme/delete.rs | 2 | ||||
| -rw-r--r-- | src/commands/meme/history.rs | 178 | ||||
| -rw-r--r-- | src/commands/meme/invoke.rs | 77 | ||||
| -rw-r--r-- | src/commands/meme/mod.rs | 25 |
5 files changed, 197 insertions, 87 deletions
diff --git a/src/commands/meme/create.rs b/src/commands/meme/create.rs index c6ed9c6..2d0ee9a 100644 --- a/src/commands/meme/create.rs +++ b/src/commands/meme/create.rs @@ -52,6 +52,7 @@ pub async fn addmeme( let save_result = NewMeme { title, + guild: util::guild_id(ctx)?.get() as _, content: text, image_id, audio_id: None, @@ -177,6 +178,7 @@ pub async fn addaudiomeme( let save_result = NewMeme { title, content: text, + guild: util::guild_id(ctx)?.get() as _, image_id, audio_id: Some(audio_id), metadata_id: 0, diff --git a/src/commands/meme/delete.rs b/src/commands/meme/delete.rs index 8adaa6c..96598d0 100644 --- a/src/commands/meme/delete.rs +++ b/src/commands/meme/delete.rs @@ -19,7 +19,7 @@ use crate::{ pub async fn delmeme(ctx: PoiseContext<'_>, title: String) -> anyhow::Result<()> { let mut conn = connection().await?; - match delete_meme(&mut conn, &title, ctx.author().id.get()).await { + match delete_meme(&mut conn, &title, ctx.author().id.get(), util::guild_id(ctx)?.get()).await { Ok(_) => { util::react(ctx, ReactionType::Unicode("💀".to_owned())).await?; Ok(()) diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs index d6ccdcf..1acb019 100644 --- a/src/commands/meme/history.rs +++ b/src/commands/meme/history.rs @@ -1,3 +1,16 @@ +use crate::{ + commands::game::get_user_id, + db::{ + self, + connection, + InvocationRecord, + Meme, + Metadata, + }, + util, + PoiseContext, + CONFIG, +}; use chrono::TimeZone; use diesel::{ result::Error as DieselError, @@ -7,31 +20,21 @@ use grate::tracing; use itertools::Itertools; use lazy_static::lazy_static; use serenity::{ + all::{ + GuildId, + Mentionable, + }, futures::{ StreamExt, TryStreamExt, }, - prelude::*, }; use tap::Pipe; use timeago::{ Formatter, TimeUnit, }; - -use crate::{ - commands::game::get_user_id, - db::{ - self, - connection, - InvocationRecord, - Meme, - Metadata, - }, - util, - PoiseContext, - CONFIG, -}; +use windows::core::s; lazy_static! { static ref TIME_FORMATTER: Formatter = { @@ -48,9 +51,11 @@ static CLEAN_DATE_FORMAT: &str = "%b %-e %Y"; /// 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 guild_id = util::guild_id(ctx)?; + let mut conn = connection().await?; - let record = match InvocationRecord::last(&mut conn).await { + let record = match InvocationRecord::last(&mut conn, guild_id.get()).await { Ok(x) => x, Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { @@ -70,7 +75,7 @@ pub async fn wat(ctx: PoiseContext<'_>) -> anyhow::Result<()> { match meme { Ok(ref meme) => { let metadata = Metadata::find(&mut conn, meme.metadata_id).await?; - let author = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64).await?; + let author = guild_id.member(&ctx, metadata.created_by as u64).await?; util::reply( ctx, @@ -116,9 +121,11 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option<usize>) -> anyhow::Result< let n = n.min(CONFIG.max_hist); + let guild_id = util::guild_id(ctx)?; + let records = { let mut conn = connection().await?; - InvocationRecord::last_n(&mut conn, n).await? + InvocationRecord::last_n(&mut conn, n, guild_id.get()).await? }; if records.is_empty() { @@ -150,9 +157,7 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option<usize>) -> anyhow::Result< Err(e) => Err(e), }; - let invoker_name = CONFIG - .discord - .guild() + let invoker_name = guild_id .member(&ctx, rec.user_id as u64) .await .map(|m| m.display_name().to_owned()) @@ -160,9 +165,7 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option<usize>) -> anyhow::Result< let result = match meme { Ok((metadata, meme)) => { - let author_name = CONFIG - .discord - .guild() + let author_name = guild_id .member(&ctx, metadata.created_by as u64) .await .map(|m| m.display_name().to_owned()) @@ -210,17 +213,89 @@ pub async fn stats(ctx: PoiseContext<'_>) -> anyhow::Result<()> { user::User, }; + let guild_id = util::guild_id(ctx)?; + let mut conn = connection().await?; - let stats = db::stats(&mut conn).await?; + let stats = db::stats(&mut conn, guild_id.get()).await?; tracing::debug!("reporting stats"); - let rand_user: User = UserId::new(stats.most_random_meme_user).to_user(&ctx).await?; - let direct_user: User = UserId::new(stats.most_directly_named_meme_user).to_user(&ctx).await?; + async fn username(ctx: PoiseContext<'_>, guild_id: GuildId, id: u64) -> anyhow::Result<String> { + let user: User = UserId::new(id).to_user(&ctx).await?; + Ok(user.nick_in(&ctx, guild_id).await.unwrap_or(user.name)) + } + + let most_active_day = if let Some(ref most_active_day) = stats.most_active_day { + let fmt = most_active_day.format(CLEAN_DATE_FORMAT); + let count = stats.most_active_day_count; + + format!("the most active day was *{fmt}* with **{count}** memes\n") + } else { + String::new() + }; + let loudest_day = if let Some(ref loudest_day) = stats.most_audio_active_day { + let fmt = loudest_day.format(CLEAN_DATE_FORMAT); + let count = stats.most_audio_active_count; + format!("and the loudest day was *{fmt}* with **{count}** audio memes\n") + } else { + String::new() + }; + + let rand_user = if let Some(rand_user) = stats.most_random_meme_user { + let rand_user = username(ctx, guild_id, rand_user).await?; + format!( + "**{rand_user}** has invoked the most random memes ({})\n", + stats.most_random_meme_user_count + ) + } else { + String::new() + }; + + let direct_user = if let Some(direct_user) = stats.most_directly_named_meme_user { + let direct_user = username(ctx, guild_id, direct_user).await?; + format!( + "**{direct_user}** has invoked the most memes by name ({})\n", + stats.most_directly_named_meme_count + ) + } else { + String::new() + }; + + let started_recording = if let Some(ref started_recording) = stats.started_recording { + let fst = started_recording.naive_local().date().format(CLEAN_DATE_FORMAT); + let snd = TIME_FORMATTER.convert((chrono::Utc::now() - started_recording).to_std()?); - let rand_user = rand_user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(rand_user.name); - let direct_user = - direct_user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(direct_user.name); + format!("started recording meme invocations on *{fst}* ({snd})\n") + } else { + String::new() + }; + + let most_requested = if let Some(ref most_requested) = stats.most_popular_named_meme { + format!( + "*{most_requested}* was the meme specifically requested the most ({})\n", + stats.most_popular_named_meme_count + ) + } else { + String::new() + }; + + let most_random = if let Some(ref most_random) = stats.most_popular_random_meme { + format!( + "*{most_random}* was the meme specifically requested the most ({})\n", + stats.most_popular_random_meme_count + ) + } else { + String::new() + }; + + let most_overall = if let Some(ref most_overall) = stats.most_popular_meme_overall { + format!( + "*{most_overall}* was the most-memed overall({})\n", + stats.most_popular_meme_overall_count + ) + } else { + String::new() + }; let s = format!( r#" @@ -228,46 +303,23 @@ pub async fn stats(ctx: PoiseContext<'_>) -> anyhow::Result<()> { **{}** memes with audio ({:0.1}%) **{}** memes with images ({:0.1}%) -started recording meme invocations on *{}* ({}) -**{}** total meme invocations recorded +{started_recording}**{}** total meme invocations recorded **{}** of which were random ({:0.1}%) and **{}** were audio ({:0.1}%) -the most active day was *{}* with **{}** memes -and the loudest day was *{}* with **{}** audio memes - -**{}** has invoked the most random memes ({}) -**{}** has invoked the most memes by name ({}) - -*{}* was the meme specifically requested the most ({}) -*{}* was the meme randomly invoked the most ({}) -and *{}* was the most-memed overall ({})"#, +{most_active_day}{loudest_day} +{rand_user}{direct_user} +{most_requested}{most_random}{most_overall}"#, stats.memes_overall, stats.audio_memes, (stats.audio_memes as f64) / (stats.memes_overall as f64) * 100., stats.image_memes, (stats.image_memes as f64) / (stats.memes_overall as f64) * 100., - stats.started_recording.naive_local().date().format(CLEAN_DATE_FORMAT), - TIME_FORMATTER.convert((chrono::Utc::now() - stats.started_recording).to_std().unwrap()), stats.total_meme_invocations, stats.random_meme_invocations, (stats.random_meme_invocations as f64) / (stats.total_meme_invocations as f64) * 100., stats.audio_meme_invocations, (stats.audio_meme_invocations as f64) / (stats.total_meme_invocations as f64) * 100., - stats.most_active_day.format(CLEAN_DATE_FORMAT), - stats.most_active_day_count, - stats.most_audio_active_day.format(CLEAN_DATE_FORMAT), - stats.most_audio_active_count, - rand_user, - stats.most_random_meme_user_count, - direct_user, - stats.most_directly_named_meme_count, - stats.most_popular_named_meme, - stats.most_popular_named_meme_count, - stats.most_popular_random_meme, - stats.most_popular_random_meme_count, - stats.most_popular_meme_overall, - stats.most_popular_meme_overall_count, ); util::reply(ctx, s).await?; @@ -279,13 +331,15 @@ and *{}* was the most-memed overall ({})"#, pub async fn memers(ctx: PoiseContext<'_>) -> anyhow::Result<()> { use serenity::model::id::UserId; - let s = db::memers() + let guild_id = util::guild_id(ctx)?; + + let s = db::memers(guild_id.get()) .await? .into_iter() .pipe(serenity::futures::stream::iter) .then(|info| async move { let user = UserId::new(info.user_id).to_user(&ctx).await?; - let username = user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(user.name); + let username = user.nick_in(&ctx, guild_id).await.unwrap_or(user.name); let res = format!( "**{}**: {} total, {} random, {} specific. favorite meme: *{}* ({})", @@ -349,6 +403,8 @@ pub async fn query(ctx: PoiseContext<'_>, rest: util::RestVec) -> anyhow::Result static ref AGE_REGEX: Regex = Regex::new(r"(?i)(?:age|order)=(.*)").unwrap(); } + let guild_id = util::guild_id(ctx)?; + let mut rest = rest.into_inner(); let creator: Option<u64> = try { @@ -373,13 +429,13 @@ pub async fn query(ctx: PoiseContext<'_>, rest: util::RestVec) -> anyhow::Result let order = order.is_some_and(|o| o.contains("new")); - let iter = db::query_meme(rest.join(" "), creator, order).await?.into_iter(); + let iter = db::query_meme(rest.join(" "), creator, order, guild_id.get()).await?.into_iter(); let result = iter .pipe(serenity::futures::stream::iter) .then(|(meme, metadata)| async move { let user = UserId::new(metadata.created_by as u64).to_user(&ctx).await?; - let username = user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(user.name); + let username = user.nick_in(&ctx, guild_id).await.unwrap_or(user.name); Ok(format!( "*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**", diff --git a/src/commands/meme/invoke.rs b/src/commands/meme/invoke.rs index 37a78f5..aff5c23 100644 --- a/src/commands/meme/invoke.rs +++ b/src/commands/meme/invoke.rs @@ -1,5 +1,6 @@ use diesel::{ result::Error as DieselError, + row::NamedRow, NotFound, }; use grate::tracing; @@ -79,14 +80,23 @@ pub(crate) async fn _meme( return rand_meme(ctx, audio_playback).await; } + let guild_id = util::guild_id(ctx)?; + let mut conn = connection().await?; - let mem = match find_meme(&mut conn, args).await { + let mem = match find_meme(&mut conn, args, guild_id.get()).await { Ok(x) => { - InvocationRecord::create(&mut conn, ctx.author().id.get(), ctx.id(), x.id, false) + InvocationRecord::create( + &mut conn, + ctx.author().id.get(), + ctx.id(), + guild_id.get(), + x.id, + false, + ) .await?; x - }, + } Err(e) => { return if let Some(NotFound) = e.downcast_ref::<DieselError>() { tracing::info!("requested meme not found in database"); @@ -97,30 +107,43 @@ pub(crate) async fn _meme( util::reply(ctx, "what in ryan's name").await?; Err(e.into()) }; - }, + } }; send_meme(ctx, &mem, &mut conn).await } async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyhow::Result<()> { - let mut conn = connection().await?; + let should_audio = util::users_listening(ctx).await?; + let guild_id = util::guild_id(ctx)?; - let should_audio = util::users_listening(ctx.serenity_context()).await?; + let mut conn = connection().await?; let mem = match audio_playback { - AudioPlayback::Required => db::rand_audio_meme(&mut conn).await, - AudioPlayback::Optional => db::rand_meme(&mut conn, should_audio).await, - AudioPlayback::Prohibited => db::rand_silent_meme(&mut conn).await, + AudioPlayback::Required => db::rand_audio_meme(&mut conn, guild_id.get()).await, + AudioPlayback::Optional => db::rand_meme(&mut conn, should_audio, guild_id.get()).await, + AudioPlayback::Prohibited => db::rand_silent_meme(&mut conn, guild_id.get()).await, }; match mem { - Ok(mem) => { - InvocationRecord::create(&mut conn, ctx.author().id.get(), ctx.id(), mem.id, true) + Ok(Some(mem)) => { + InvocationRecord::create( + &mut conn, + ctx.author().id.get(), + util::guild_id(ctx)?.get(), + ctx.id(), + mem.id, + true, + ) .await?; send_meme(ctx, &mem, &mut conn).await?; Ok(()) - }, + } + Ok(None) => { + tracing::info!("random meme not found"); + util::reply(ctx, "i don't know any :(").await?; + Ok(()) + } Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { tracing::info!("random meme not found"); @@ -131,24 +154,40 @@ async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyh util::reply(ctx, "HELP").await?; Err(e.into()) - }, + } } } /// 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?; + let guild = util::guild_id(ctx)?; + let should_audio = util::users_listening(ctx).await?; let mut conn = connection().await?; - let meme = db::rare_meme(&mut conn, should_audio).await; + let meme = db::rare_meme(&mut conn, should_audio, guild.get()).await; match meme { - Ok(meme) => { - InvocationRecord::create(&mut conn, ctx.author().id.get(), ctx.id(), meme.id, true) + Ok(Some(meme)) => { + InvocationRecord::create( + &mut conn, + ctx.author().id.get(), + util::guild_id(ctx)?.get(), + ctx.id(), + meme.id, + true, + ) .await?; send_meme(ctx, &meme, &mut conn).await - }, + } + + Ok(None) => { + tracing::info!("rare meme not found"); + util::reply(ctx, "i don't know any :(").await?; + + Ok(()) + } + Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { tracing::info!("rare meme not found"); @@ -160,6 +199,6 @@ pub async fn rare_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> { util::reply(ctx, "THE MEME MARKET IS IN FREEFALL").await?; Err(e.into()) - }, + } } } diff --git a/src/commands/meme/mod.rs b/src/commands/meme/mod.rs index 7bc8b2e..eb6aa1d 100644 --- a/src/commands/meme/mod.rs +++ b/src/commands/meme/mod.rs @@ -19,6 +19,10 @@ use songbird::input::{ Compose, Input, }; +use std::{ + borrow::ToOwned, + default::Default, +}; pub use self::{ create::*, @@ -102,14 +106,23 @@ async fn send_meme( if let Some(audio) = audio { let audio = audio?; - let (_sb, call) = songbird(ctx).await?; - let mut call = call.lock().await; + let Some(voice_channel) = util::best_voice_channel(ctx) else { + tracing::error!(?ctx, "couldn't find a relevant voice channel"); + util::react(ctx, '🔇').await?; - if call.current_channel().is_none() { - call.join(CONFIG.discord.voice_channel()).await?; - } + return Ok(()); + }; + + { + let (_sb, call) = songbird(ctx).await?; + let mut call = call.lock().await; - call.enqueue_input(Input::Lazy(Box::new(audio))).await; + if call.current_channel().is_none() { + call.join(voice_channel).await?; + } + + call.enqueue_input(Input::Lazy(Box::new(audio))).await; + } util::react(ctx, ReactionType::Unicode("📣".to_owned())).await?; } |
