diff options
Diffstat (limited to 'src/commands/meme')
| -rw-r--r-- | src/commands/meme/create.rs | 127 | ||||
| -rw-r--r-- | src/commands/meme/delete.rs | 24 | ||||
| -rw-r--r-- | src/commands/meme/history.rs | 188 | ||||
| -rw-r--r-- | src/commands/meme/invoke.rs | 81 | ||||
| -rw-r--r-- | src/commands/meme/mod.rs | 115 |
5 files changed, 333 insertions, 202 deletions
diff --git a/src/commands/meme/create.rs b/src/commands/meme/create.rs index 06cc4ef..97c5276 100644 --- a/src/commands/meme/create.rs +++ b/src/commands/meme/create.rs @@ -14,9 +14,9 @@ use log::{ }; use serenity::{ framework::standard::{ + macros::command, Args, Delimiter, - macros::command, }, model::channel::Message, prelude::*, @@ -25,20 +25,28 @@ use url::Url; use anyhow::anyhow; use lazy_static::lazy_static; +use serenity::{ + all::ReactionType, + framework::standard::{ + CommandError, + CommandResult, + }, + futures::TryFutureExt, +}; use crate::{ - Result, audio::{ parse_times, ytdl_url, }, db::{ - Audio, connection, + Audio, Image, NewMeme, }, - util::CtxExt, FFMPEG_COMMAND, + util, + FFMPEG_COMMAND, }; lazy_static! { @@ -46,13 +54,17 @@ lazy_static! { } #[command] -pub fn addmeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { +pub async fn addmeme(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let mut args = Args::new(args.rest(), DELIMS.as_ref()); let title = args.single_quoted::<String>()?; let text = args.rest().to_owned(); - let text = if text.is_empty() { None } else { Some(text) }; + let text = if text.is_empty() { + None + } else { + Some(text) + }; let mut conn = connection()?; @@ -60,13 +72,17 @@ pub fn addmeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { if image.is_none() && text.is_none() { warn!("tried to create non-audio meme with no image or text"); - return ctx.send(msg.channel_id, "hahAA it's empty xdddd", msg.tts); + return util::send(ctx, msg.channel_id, "hahAA it's empty xdddd", msg.tts) + .map_err(CommandError::from) + .await; } - let image_id = image.map(|att| { - let data = att.download()?; - Image::create(&mut conn, &att.filename, data, msg.author.id.0) - }).transpose()?; + let image_id = image + .map(|att| { + let data = att.download()?; + Image::create(&mut conn, &att.filename, data, msg.author.id.get()) + }) + .transpose()?; let save_result = NewMeme { title, @@ -74,25 +90,31 @@ pub fn addmeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { image_id, audio_id: None, metadata_id: 0, - }.save(&mut conn, msg.author.id.0).map(|_| {}); + } + .save(&mut conn, msg.author.id.get()) + .map(|_| {}); use diesel::result::DatabaseErrorKind; match save_result { Ok(_) => msg.react(&ctx, "👌"), Err(e) => { - if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) = e.downcast_ref::<DieselError>() { + if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) = + e.downcast_ref::<DieselError>() + { error!("tried to create meme that already exists"); - msg.react(&ctx, "❌")?; - return ctx.send(msg.channel_id, "that meme already exists", msg.tts); + msg.react(&ctx, ReactionType::Unicode("❌".to_owned())).await?; + return util::send(ctx, msg.channel_id, "that meme already exists", msg.tts) + .map_err(CommandError::from) + .await; } - return Err(e); - } + return Err(e.into()); + }, } } #[command] -pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { +pub async fn addaudiomeme(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let mut args = Args::new(args.rest(), DELIMS.as_ref()); let title = args.single_quoted::<String>()?; @@ -101,8 +123,8 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> let elems = audio_str.split_whitespace().collect::<Vec<_>>(); if elems.len() == 0 { - ctx.send(msg.channel_id, "are you stupid", msg.tts)?; - return Err(anyhow!("no audio link was provided")) + util::send(ctx, msg.channel_id, "are you stupid", msg.tts).await?; + return Err(anyhow!("no audio link was provided").into()); } let audio_link = Url::parse(elems[0])?; @@ -112,16 +134,24 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> let youtube_url = ytdl_url(audio_link.as_str())?; let duration_opts = if let Some(e) = end { - vec! [ - "-ss".to_owned(), start.map_or_else( + vec![ + "-ss".to_owned(), + start.map_or_else( || "00:00:00".to_owned(), - |s| format!("{:02}:{:02}:{:02}", s.num_hours(), s.num_minutes() % 60, s.num_seconds() % 60) + |s| { + format!( + "{:02}:{:02}:{:02}", + s.num_hours(), + s.num_minutes() % 60, + s.num_seconds() % 60 + ) + }, ), - - "-to".to_owned(), format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60), + "-to".to_owned(), + format!("{:02}:{:02}:{:02}", e.num_hours(), e.num_minutes() % 60, e.num_seconds() % 60), ] } else { - vec! [] + vec![] }; let ffmpeg_command = Command::new(&*FFMPEG_COMMAND) @@ -129,13 +159,8 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> .arg(youtube_url) .args(duration_opts) .args(&[ - "-ac", "2", - "-ar", "48000", - "-f", "opus", - "-acodec", "libopus", - "-b:a", "96k", - "-fs", "5M", - "-", + "-ac", "2", "-ar", "48000", "-f", "opus", "-acodec", "libopus", "-b:a", "96k", "-fs", + "5M", "-", ]) .stdout(Stdio::piped()) .stderr(Stdio::null()) @@ -145,15 +170,21 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> let mut audio_reader = ffmpeg_command.stdout.unwrap(); let text = args.rest().to_owned(); - let text = if text.is_empty() { None } else { Some(text) }; + let text = if text.is_empty() { + None + } else { + Some(text) + }; let mut conn = connection()?; - let image = msg.attachments.first() + let image = msg + .attachments + .first() .ok_or(anyhow!("no attachment")) .and_then(|att| { let data = att.download()?; - Image::create(&mut conn, &att.filename, data, msg.author.id.0) + Image::create(&mut conn, &att.filename, data, msg.author.id.get()) }) .ok(); @@ -162,10 +193,12 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> if bytes == 0 { debug!("read 0 bytes from audio reader"); - return ctx.send(msg.channel_id, "🔇🔇🔇🔕🔕🔕🔕🔕🔇🔕🔕🔇🔕🔕📣📢📣📢📣", msg.tts); + return util::send(ctx, msg.channel_id, "🔇🔇🔇🔕🔕🔕🔕🔕🔇🔕🔕🔇🔕🔕📣📢📣📢📣", msg.tts) + .map_err(CommandError::from) + .await; } - let audio_id = Audio::create(&mut conn, audio_data, msg.author.id.0)?; + let audio_id = Audio::create(&mut conn, audio_data, msg.author.id.get())?; let save_result = NewMeme { title, @@ -173,19 +206,25 @@ pub fn addaudiomeme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> image_id: image, audio_id: Some(audio_id), metadata_id: 0, - }.save(&mut conn, msg.author.id.0).map(|_| {}); + } + .save(&mut conn, msg.author.id.get()) + .map(|_| {}); use diesel::result::DatabaseErrorKind; match save_result { - Ok(_) => msg.react(&ctx, "👌"), + Ok(_) => msg.react(&ctx, ReactionType::Unicode("👌".to_owned())), Err(e) => { - if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) = e.downcast_ref::<DieselError>() { + if let Some(DieselError::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) = + e.downcast_ref::<DieselError>() + { error!("tried to create meme that already exists"); - msg.react(&ctx, "❌")?; - return ctx.send(msg.channel_id, "that meme already exists", msg.tts); + msg.react(&ctx, ReactionType::Unicode("❌".to_owned())).await?; + return util::send(ctx, msg.channel_id, "that meme already exists", msg.tts) + .map_err(CommandError::from) + .await; } return Err(e); - } + }, } } diff --git a/src/commands/meme/delete.rs b/src/commands/meme/delete.rs index 7eafc80..c06e9d0 100644 --- a/src/commands/meme/delete.rs +++ b/src/commands/meme/delete.rs @@ -1,45 +1,49 @@ use diesel::{ - NotFound, result::Error as DieselError, + NotFound, }; use log::info; use serenity::{ + all::ReactionType, framework::standard::{ - Args, macros::command, + Args, + CommandResult, }, model::channel::Message, prelude::*, }; use crate::{ - Result, db::{ connection, delete_meme, }, - util::CtxExt, + util, }; #[command] #[aliases("delmem")] -pub fn delmeme(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { +pub async fn delmeme(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let title = args.single_quoted::<String>()?; let mut conn = connection()?; - match delete_meme(&mut conn, &title, msg.author.id.0) { - Ok(_) => msg.react(ctx, "💀"), + match delete_meme(&mut conn, &title, msg.author.id.get()) { + Ok(_) => { + msg.react(ctx, ReactionType::Unicode("💀".to_owned())).await?; + Ok(()) + }, Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { - msg.react(&ctx, "❓")?; + msg.react(&ctx, ReactionType::Unicode("❓".to_owned())).await?; info!("attempted to delete nonexistent meme: '{}'", title); - ctx.send(msg.channel_id, "nice try", msg.tts)?; + util::send(ctx, msg.channel_id, "nice try", msg.tts).await?; return Ok(()); } Err(e)?; Ok(()) - } + }, } } diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs index f9e8851..5e200b1 100644 --- a/src/commands/meme/history.rs +++ b/src/commands/meme/history.rs @@ -1,6 +1,6 @@ use diesel::{ - NotFound, result::Error as DieselError, + NotFound, }; use log::{ debug, @@ -9,8 +9,8 @@ use log::{ }; use serenity::{ framework::standard::{ - Args, macros::command, + Args, }, model::channel::Message, prelude::*, @@ -22,6 +22,13 @@ use timeago::{ use anyhow::anyhow; use lazy_static::lazy_static; +use serenity::{ + framework::standard::{ + CommandError, + CommandResult, + }, + futures::TryFutureExt, +}; use crate::{ db::{ @@ -31,9 +38,9 @@ use crate::{ Meme, Metadata, }, - CONFIG, + util, Result, - util::CtxExt, + CONFIG, }; lazy_static! { @@ -50,7 +57,7 @@ static CLEAN_DATE_FORMAT: &'static str = "%b %-e %Y"; #[command] #[aliases("what")] -pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> { +pub async fn wat(ctx: &Context, msg: &Message, _: Args) -> CommandResult { let mut conn = connection()?; let record = match InvocationRecord::last(&mut conn) { @@ -58,11 +65,13 @@ pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> { Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { info!("found no memes in history"); - return ctx.send(msg.channel_id, "no one has ever memed before", msg.tts); + return util::send(ctx, msg.channel_id, "no one has ever memed before", msg.tts) + .map_err(CommandError::from) + .await; } - ctx.send(msg.channel_id, "BAD MEME BAD MEME", msg.tts)?; - return Err(e); + util::send(ctx, msg.channel_id, "BAD MEME BAD MEME", msg.tts).await?; + return Err(e.into()); }, }; @@ -73,17 +82,27 @@ pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> { let metadata = Metadata::find(&mut conn, meme.metadata_id)?; let author = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64)?; - ctx.send(msg.channel_id, - &format!("that was \"{}\" by {} ({})", - meme.title, author.mention(), metadata.created.date().format(CLEAN_DATE_FORMAT)), msg.tts)? + util::send( + ctx, + msg.channel_id, + &format!( + "that was \"{}\" by {} ({})", + meme.title, + author.mention(), + metadata.created.date().format(CLEAN_DATE_FORMAT) + ), + msg.tts, + ) + .await? }, Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { info!("last meme not found in database"); - return ctx.send(msg.channel_id, "heuueueeeeh?", msg.tts); + return util::send(ctx, msg.channel_id, "heuueueeeeh?", msg.tts).await; } - ctx.send(msg.channel_id, "do i look like i know what a jpeg is", msg.tts)?; + util::send(ctx, msg.channel_id, "do i look like i know what a jpeg is", msg.tts) + .await?; return Err(e); }, }; @@ -92,7 +111,7 @@ pub fn wat(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> { } #[command] -pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { +pub async fn history(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { use itertools::Itertools; let mut conn = connection()?; @@ -101,7 +120,7 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { if n > CONFIG.max_hist { debug!("user requested more than MAX_HIST ({}) items from history", CONFIG.max_hist); - ctx.send(msg.channel_id, "YER PUSHIN ME OVER THE FUCKIN LINE", true)?; + util::send(ctx, msg.channel_id, "YER PUSHIN ME OVER THE FUCKIN LINE", true).await?; } let n = n.min(CONFIG.max_hist); @@ -110,7 +129,9 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { if records.len() == 0 { info!("no memes in history"); - return ctx.send(msg.channel_id, "i don't remember anything :(", msg.tts); + return util::send(ctx, msg.channel_id, "i don't remember anything :(", msg.tts) + .map_err(CommandError::from) + .await; } info!("reporting meme history (len {})", n); @@ -119,18 +140,41 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { .enumerate() .rev() .map(|(i, rec)| { - let dt = chrono::DateTime::from_utc(rec.time, chrono::Utc{}); + let dt = chrono::DateTime::from_utc(rec.time, chrono::Utc {}); let ago = TIME_FORMATTER.convert((chrono::Utc::now() - dt).to_std().unwrap()); - let rand = if rec.random { "R, " } else { "" }; + let rand = if rec.random { + "R, " + } else { + "" + }; Meme::find(&mut conn, rec.meme_id) .and_then(|meme| { Metadata::find(&mut conn, meme.metadata_id).map(|metadata| (metadata, meme)) }) .map(|(metadata, meme)| { - let author_name = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64).map(|m| m.display_name().into_owned()).unwrap_or("???".to_owned()); - let invoker_name = CONFIG.discord.guild().member(&ctx, rec.user_id as u64).map(|m| m.display_name().into_owned()).unwrap_or("???".to_owned()); - format!("{}. [{}{}] \"{}\" by {} ({}). invoked by {}.", i + 1, rand, ago, meme.title, author_name, metadata.created.date().format(CLEAN_DATE_FORMAT), invoker_name) + let author_name = CONFIG + .discord + .guild() + .member(&ctx, metadata.created_by as u64) + .map(|m| m.display_name().into_owned()) + .unwrap_or("???".to_owned()); + let invoker_name = CONFIG + .discord + .guild() + .member(&ctx, rec.user_id as u64) + .map(|m| m.display_name().into_owned()) + .unwrap_or("???".to_owned()); + format!( + "{}. [{}{}] \"{}\" by {} ({}). invoked by {}.", + i + 1, + rand, + ago, + meme.title, + author_name, + metadata.created.date().format(CLEAN_DATE_FORMAT), + invoker_name + ) }) .unwrap_or_else(|e| { if let Some(variant) = e.downcast_ref::<DieselError>() { @@ -139,18 +183,23 @@ pub fn history(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { } } - let invoker_name = CONFIG.discord.guild().member(&ctx, rec.user_id as u64).map(|m| m.display_name().into_owned()).unwrap_or("???".to_owned()); + let invoker_name = CONFIG + .discord + .guild() + .member(&ctx, rec.user_id as u64) + .map(|m| m.display_name().into_owned()) + .unwrap_or("???".to_owned()); format!("{}. [{}{}] not found. invoked by {}.", i + 1, rand, ago, invoker_name) }) }) .join("\n"); - ctx.send(msg.channel_id, &resp, false) + util::send(ctx, msg.channel_id, &resp, false).await } #[command] #[aliases("stat")] -pub fn stats(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> { +pub async fn stats(ctx: &Context, msg: &Message, _: Args) -> CommandResult { use db; use serenity::model::{ id::UserId, @@ -162,8 +211,8 @@ pub fn stats(ctx: &mut Context, msg: &Message, _: Args) -> Result<()> { debug!("reporting stats"); - let rand_user: User = UserId(stats.most_random_meme_user).to_user(&ctx)?; - let direct_user: User = UserId(stats.most_directly_named_meme_user).to_user(&ctx)?; + let rand_user: User = UserId::new(stats.most_random_meme_user).to_user(&ctx)?; + let direct_user: User = UserId::new(stats.most_directly_named_meme_user).to_user(&ctx)?; let rand_user = rand_user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(rand_user.name); let direct_user = direct_user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(direct_user.name); @@ -200,29 +249,35 @@ and *{}* was the most-memed overall ({})"#, (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, + 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, ); - ctx.send(msg.channel_id, s, msg.tts) + + util::send(ctx, msg.channel_id, s, msg.tts).map_err(CommandError::from).await } #[command] -pub fn memers(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { +pub async fn memers(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { use db; use itertools::Itertools; - use serenity::model::{ - id::UserId, - }; + use serenity::model::id::UserId; let s = db::memers()? .into_iter() .map(|info| { - let user = UserId(info.user_id).to_user(&ctx)?; + let user = UserId::new(info.user_id).to_user(&ctx)?; let username = user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(user.name); let res = format!( @@ -241,11 +296,11 @@ pub fn memers(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { .into_iter() .join("\n"); - ctx.send(msg.channel_id, &s, msg.tts) + util::send(ctx, msg.channel_id, &s, msg.tts).map_err(CommandError::from).await } #[command] -pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { +pub async fn query(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { use std::borrow::Borrow; use itertools::Itertools; @@ -253,8 +308,8 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { use serenity::model::id::UserId; use crate::{ - game::get_user_id, db, + game::get_user_id, CONFIG, }; @@ -263,24 +318,21 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { static ref AGE_REGEX: Regex = Regex::new(r"(?i)(?:age|order)=(.*)").unwrap(); } - let guild = msg.channel_id.to_channel(&ctx)? - .guild() - .ok_or(anyhow!("couldn't find guild"))?; + let guild = msg.channel_id.to_channel(&ctx)?.guild().ok_or(anyhow!("couldn't find guild"))?; - let guild = guild.read() - .guild(&ctx) - .ok_or(anyhow!("couldn't find guild"))?; + let guild = guild.read().guild(&ctx).ok_or(anyhow!("couldn't find guild"))?; - let guild = guild - .read(); + let guild = guild.read(); let creator: Option<u64> = { let creator = args.quoted().current().map(|s| CREATOR_REGEX.is_match(s)).unwrap_or(false); if creator { args.single_quoted::<String>() .ok() - .and_then(|s| CREATOR_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned())) - .and_then(|s| get_user_id(guild.borrow(), s).ok().map(|s| s.0)) + .and_then(|s| { + CREATOR_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned()) + }) + .and_then(|s| get_user_id(guild.borrow(), s).ok().map(UserId::get)) } else { None } @@ -290,8 +342,11 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { let order = args.quoted().current().map(|s| AGE_REGEX.is_match(s)).unwrap_or(false); if order { - args.single_quoted::<String>().ok() - .and_then(|s| AGE_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned())) + args.single_quoted::<String>() + .ok() + .and_then(|s| { + AGE_REGEX.captures(&s).and_then(|c| c.get(1)).map(|x| x.as_str().to_owned()) + }) .map(|s: String| s.contains("new")) .unwrap_or(true) } else { @@ -302,16 +357,17 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { let result = db::query_meme(args.rest(), creator, order)? .into_iter() .map(|(meme, metadata)| { - let user = UserId(metadata.created_by as u64).to_user(&ctx)?; + let user = UserId::new(metadata.created_by as u64).to_user(&ctx)?; let username = user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(user.name); - Ok(format!("*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**", - meme.title, - username, - metadata.created.date().format(CLEAN_DATE_FORMAT), - meme.content.map_or(0, |s| s.len()), - meme.image_id.map_or("NO", |_s| "YES"), - meme.audio_id.map_or("NO", |_s| "YES"), + Ok(format!( + "*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**", + meme.title, + username, + metadata.created.date().format(CLEAN_DATE_FORMAT), + meme.content.map_or(0, |s| s.len()), + meme.image_id.map_or("NO", |_s| "YES"), + meme.audio_id.map_or("NO", |_s| "YES"), )) }) .collect::<Result<Vec<_>>>()? @@ -329,9 +385,11 @@ pub fn query(ctx: &mut Context, msg: &Message, mut args: Args) -> Result<()> { if result.len() == 0 { info!("no memes matched query"); - return ctx.send(msg.channel_id, "no match".to_owned(), msg.tts); - } + return util::send(ctx, msg.channel_id, "no match".to_owned(), msg.tts) + .map_err(CommandError::from) + .await; + } - ctx.send(msg.channel_id, &result, msg.tts) + util::send(ctx, msg.channel_id, &result, msg.tts).map_err(CommandError::from).await } diff --git a/src/commands/meme/invoke.rs b/src/commands/meme/invoke.rs index de0272f..03c6251 100644 --- a/src/commands/meme/invoke.rs +++ b/src/commands/meme/invoke.rs @@ -1,63 +1,65 @@ use diesel::{ - NotFound, result::Error as DieselError, + NotFound, }; use itertools::Itertools; use log::info; use serenity::{ framework::standard::{ - Args, macros::command, + Args, + CommandError, + CommandResult, }, + futures::TryFutureExt, model::channel::Message, prelude::*, }; use crate::{ commands::meme::send_meme, - Result, db::{ self, connection, find_meme, InvocationRecord, }, - util::CtxExt, + util, }; #[command] #[aliases("mem")] -pub fn meme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { +pub async fn meme(ctx: &Context, msg: &Message, args: Args) -> CommandResult { _meme(ctx, msg, args, AudioPlayback::Optional) } #[command] -pub fn omen(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { +pub async fn omen(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let args = Args::new("", &[]); _meme(ctx, msg, args, AudioPlayback::Optional) } #[command] -pub fn silentomen(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { +pub async fn silentomen(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let args = Args::new("", &[]); _meme(ctx, msg, args, AudioPlayback::Prohibited) } #[command] -pub fn audioomen(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { +pub async fn audioomen(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let args = Args::new("", &[]); _meme(ctx, msg, args, AudioPlayback::Required) } #[command] #[aliases("audiomeme", "audiomem")] -pub fn audio_meme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { +pub async fn audio_meme(ctx: &Context, msg: &Message, args: Args) -> CommandResult { _meme(ctx, msg, args, AudioPlayback::Required) } #[command] #[aliases("silentmeme", "silentmem")] -pub fn silent_meme(ctx: &mut Context, msg: &Message, args: Args) -> Result<()> { +pub async fn silent_meme(ctx: &Context, msg: &Message, args: Args) -> CommandResult { _meme(ctx, msg, args, AudioPlayback::Prohibited) } @@ -68,9 +70,14 @@ enum AudioPlayback { Prohibited, } -fn _meme(ctx: &mut Context, msg: &Message, args: Args, audio_playback: AudioPlayback) -> Result<()> { +async fn _meme( + ctx: &Context, + msg: &Message, + args: Args, + audio_playback: AudioPlayback, +) -> CommandResult { if args.len() == 0 || audio_playback != AudioPlayback::Optional { - return rand_meme(ctx, msg, audio_playback); + return rand_meme(ctx, msg, audio_playback).await; } let search = args.raw().join(" "); @@ -78,28 +85,34 @@ fn _meme(ctx: &mut Context, msg: &Message, args: Args, audio_playback: AudioPlay let mut conn = connection()?; let mem = match find_meme(&mut conn, search) { Ok(x) => { - InvocationRecord::create(&mut conn, msg.author.id.0, msg.id.0, x.id, false)?; + InvocationRecord::create(&mut conn, msg.author.id.get(), msg.id.get(), x.id, false)?; x }, Err(e) => { return if let Some(NotFound) = e.downcast_ref::<DieselError>() { info!("requested meme not found in database"); - ctx.send(msg.channel_id, "c'mon baby, guesstimate", msg.tts) + util::send(ctx, msg.channel_id, "c'mon baby, guesstimate", msg.tts) + .await + .map_err(CommandError::from) } else { - ctx.send(msg.channel_id, "what in ryan's name", msg.tts)?; - Err(e) + util::send(ctx, msg.channel_id, "what in ryan's name", msg.tts).await?; + Err(e.into()) }; }, }; - send_meme(ctx, &mem, &mut conn, msg) + send_meme(ctx, &mem, &mut conn, msg).await } -fn rand_meme(ctx: &Context, message: &Message, audio_playback: AudioPlayback) -> Result<()> { +async fn rand_meme( + ctx: &Context, + message: &Message, + audio_playback: AudioPlayback, +) -> CommandResult { let mut conn = connection()?; - let should_audio = ctx.users_listening()?; + let should_audio = util::users_listening(ctx).await?; let mem = match audio_playback { AudioPlayback::Required => db::rand_audio_meme(&mut conn), @@ -109,28 +122,36 @@ fn rand_meme(ctx: &Context, message: &Message, audio_playback: AudioPlayback) -> match mem { Ok(mem) => { - InvocationRecord::create(&mut conn, message.author.id.0, message.id.0, mem.id, true)?; - send_meme(ctx, &mem, &mut conn, message)?; + InvocationRecord::create( + &mut conn, + message.author.id.get(), + message.id.get(), + mem.id, + true, + )?; + send_meme(ctx, &mem, &mut conn, message).await?; Ok(()) }, Err(e) => { match e.downcast_ref::<DieselError>() { Some(NotFound) => { info!("random meme not found"); - return ctx.send(message.channel_id, "i don't know any :(", message.tts) + return util::send(ctx, message.channel_id, "i don't know any :(", message.tts) + .map_err(CommandError::from) + .await; }, _ => {}, } - ctx.send(message.channel_id, "HELP", message.tts)?; - return Err(e); + util::send(ctx, message.channel_id, "HELP", message.tts).await?; + return Err(e.into()); }, } } #[command] #[aliases("rarememe", "raremem")] -pub fn rare_meme(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { +pub async fn rare_meme(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let should_audio = ctx.users_listening()?; let mut conn = connection()?; @@ -138,19 +159,23 @@ pub fn rare_meme(ctx: &mut Context, msg: &Message, _args: Args) -> Result<()> { let meme = db::rare_meme(&mut conn, should_audio); match meme { Ok(meme) => { - InvocationRecord::create(&mut conn, msg.author.id.0, msg.id.0, meme.id, true)?; + InvocationRecord::create(&mut conn, msg.author.id.get(), msg.id.get(), meme.id, true)?; send_meme(ctx, &meme, &mut conn, msg) }, Err(e) => { match e.downcast_ref::<DieselError>() { Some(NotFound) => { info!("rare meme not found"); - return ctx.send(msg.channel_id, "i don't know any :(", msg.tts) + return util::send(ctx, msg.channel_id, "i don't know any :(", msg.tts) + .map_err(CommandError::from) + .await; }, _ => {}, } - ctx.send(msg.channel_id, "THE MEME MARKET IS IN FREEFALL", msg.tts)?; + util::send(ctx, msg.channel_id, "THE MEME MARKET IS IN FREEFALL", msg.tts) + .map_err(CommandError::from) + .await?; Err(e) }, diff --git a/src/commands/meme/mod.rs b/src/commands/meme/mod.rs index 6ce30b6..31d9b78 100644 --- a/src/commands/meme/mod.rs +++ b/src/commands/meme/mod.rs @@ -1,9 +1,16 @@ use diesel::PgConnection; use log::debug; -use rand::{Rng, thread_rng}; +use rand::random; use serenity::{ - framework::standard::macros::group, - http::AttachmentType, + all::ReactionType, + builder::{ + CreateAttachment, + CreateMessage, + }, + framework::standard::{ + macros::group, + CommandResult, + }, model::channel::Message, prelude::*, }; @@ -14,7 +21,6 @@ use crate::{ PlayQueue, }, db::Meme, - Result, }; pub use self::{ @@ -24,68 +30,67 @@ pub use self::{ invoke::*, }; -mod history; mod create; -mod invoke; mod delete; +mod history; +mod invoke; -group!({ - name: "memes", - options: { - only_in: "guild", - }, - commands: [ - meme, - audio_meme, - silent_Meme, - omen, - audioomen, - silentomen, - addmeme, - addaudiomeme, - delmeme, - wat, - stats, - history, - rare_meme, - memers, - query, - ], -}); +#[group] +#[commands( + meme, + audio_meme, + silent_meme, + omen, + audioomen, + silentomen, + addmeme, + addaudiomeme, + delmeme, + wat, + stats, + history, + rare_meme, + memers, + query +)] +struct Memes; -fn send_meme(ctx: &Context, t: &Meme, conn: &mut PgConnection, msg: &Message) -> Result<()> { - let should_tts = t.content.as_ref().map(|t| t.len() > 0).unwrap_or(false) && - thread_rng().gen::<u32>() % 25 == 0; +async fn send_meme( + ctx: &Context, + t: &Meme, + conn: &mut PgConnection, + msg: &Message, +) -> CommandResult { + let should_tts = + t.content.as_ref().map(|t| t.len() > 0).unwrap_or(false) && random::<u32>() % 25 == 0; debug!("sending meme (tts: {}): {:?}", should_tts, t); let image = t.image(conn); let audio = t.audio(conn); + let cmsg = { + let ret = CreateMessage::default().tts(should_tts); + + match t.content { + Some(ref text) if text.len() > 0 => ret.content(text), + _ => ret, + } + }; + match image { Some(image) => { let image = image?; - msg.channel_id.send_files(ctx, vec!(AttachmentType::Bytes((&image.data, &image.filename))), |m| { - let ret = m.tts(should_tts); + let att = CreateAttachment::bytes(image.data.as_slice(), &image.filename); - match t.content { - Some(ref text) if text.len() > 0 => ret.content(text), - _ => ret, - } - })?; + msg.channel_id.send_files(ctx, vec![att], cmsg).await?; }, None => match t.content { - Some(_) => { msg.channel_id.send_message(ctx, |m| { - let ret = m.tts(should_tts); - - match t.content { - Some(ref text) if text.len() > 0 => ret.content(text), - _ => ret, - } - })?; }, + Some(_) => { + msg.channel_id.send_message(ctx, cmsg).await?; + }, None => {}, - }, }; @@ -96,19 +101,19 @@ fn send_meme(ctx: &Context, t: &Meme, conn: &mut PgConnection, msg: &Message) -> let audio = audio?; { - let queue_lock = ctx.data.write().get::<PlayQueue>().cloned().unwrap(); + let queue_lock = ctx.data.write().await.get::<PlayQueue>().cloned().unwrap(); let mut play_queue = queue_lock.write().unwrap(); - play_queue.meme_queue.push_back(PlayArgs{ - initiator: msg.author.name.clone(), - data: ::either::Right(audio.data.clone()), + play_queue.meme_queue.push_back(PlayArgs { + initiator: msg.author.name.clone(), + data: ::either::Right(audio.data.clone()), sender_channel: msg.channel_id, - start: None, - end: None, + start: None, + end: None, }); } - msg.react(ctx, "📣")?; + msg.react(ctx, ReactionType::Unicode("📣".to_owned())).await?; } Ok(()) |
