diff options
Diffstat (limited to 'src/commands/meme/history.rs')
| -rw-r--r-- | src/commands/meme/history.rs | 216 |
1 files changed, 115 insertions, 101 deletions
diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs index 5e200b1..ed50e27 100644 --- a/src/commands/meme/history.rs +++ b/src/commands/meme/history.rs @@ -1,7 +1,11 @@ +use anyhow::anyhow; use diesel::{ result::Error as DieselError, NotFound, + PgConnection, }; +use itertools::Itertools; +use lazy_static::lazy_static; use log::{ debug, error, @@ -11,25 +15,23 @@ use serenity::{ framework::standard::{ macros::command, Args, + CommandError, + CommandResult, + }, + futures::{ + StreamExt, + TryFutureExt, + TryStreamExt, }, model::channel::Message, prelude::*, }; +use tap::Pipe; use timeago::{ Formatter, TimeUnit, }; -use anyhow::anyhow; -use lazy_static::lazy_static; -use serenity::{ - framework::standard::{ - CommandError, - CommandResult, - }, - futures::TryFutureExt, -}; - use crate::{ db::{ self, @@ -39,7 +41,6 @@ use crate::{ Metadata, }, util, - Result, CONFIG, }; @@ -53,10 +54,10 @@ lazy_static! { }; } -static CLEAN_DATE_FORMAT: &'static str = "%b %-e %Y"; +static CLEAN_DATE_FORMAT: &str = "%b %-e %Y"; #[command] -#[aliases("what")] +#[aliases("what", "hwaet", "hwæt")] pub async fn wat(ctx: &Context, msg: &Message, _: Args) -> CommandResult { let mut conn = connection()?; @@ -80,7 +81,7 @@ pub async fn wat(ctx: &Context, msg: &Message, _: Args) -> CommandResult { match meme { Ok(ref meme) => { let metadata = Metadata::find(&mut conn, meme.metadata_id)?; - let author = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64)?; + let author = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64).await?; util::send( ctx, @@ -98,22 +99,22 @@ pub async fn wat(ctx: &Context, msg: &Message, _: Args) -> CommandResult { Err(e) => { if let Some(NotFound) = e.downcast_ref::<DieselError>() { info!("last meme not found in database"); - return util::send(ctx, msg.channel_id, "heuueueeeeh?", msg.tts).await; + return util::send(ctx, msg.channel_id, "heuueueeeeh?", msg.tts) + .await + .map_err(CommandError::from); } util::send(ctx, msg.channel_id, "do i look like i know what a jpeg is", msg.tts) .await?; - return Err(e); + return Err(e.into()); }, }; - meme.map(|_| {}) + meme.map(|_| {}).map_err(CommandError::from) } #[command] pub async fn history(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { - use itertools::Itertools; - let mut conn = connection()?; let n = args.single_quoted::<usize>().unwrap_or(CONFIG.default_hist); @@ -135,66 +136,76 @@ pub async fn history(ctx: &Context, msg: &Message, mut args: Args) -> CommandRes } info!("reporting meme history (len {})", n); - let resp = records - .into_iter() - .enumerate() - .rev() - .map(|(i, rec)| { - 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 { - "" - }; - 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 - ) - }) - .unwrap_or_else(|e| { - if let Some(variant) = e.downcast_ref::<DieselError>() { - if *variant != NotFound { - error!("error encountered loading meme history: {}", e); - } - } + let resp = serenity::futures::stream::iter(records.into_iter().enumerate().rev()) + .then(|(i, rec)| ir_info(ctx, i, rec, &mut conn)) + .try_collect::<Vec<String>>() + .await?; - 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"); + let resp = resp.join("\n"); - util::send(ctx, msg.channel_id, &resp, false).await + util::send(ctx, msg.channel_id, &resp, false).await.map_err(CommandError::from) +} + +async fn ir_info( + ctx: &Context, + i: usize, + rec: InvocationRecord, + conn: &mut PgConnection, +) -> Result<String, CommandError> { + 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 meme = Meme::find(conn, rec.meme_id) + .and_then(|meme| Metadata::find(conn, meme.metadata_id).map(|metadata| (metadata, meme))); + + let invoker_name = CONFIG + .discord + .guild() + .member(&ctx, rec.user_id as u64) + .await + .map(|m| m.display_name().to_owned()) + .unwrap_or("???".to_owned()); + + let result = match meme { + Ok((metadata, meme)) => { + let author_name = CONFIG + .discord + .guild() + .member(&ctx, metadata.created_by as u64) + .await + .map(|m| m.display_name().to_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 + ) + }, + Err(e) => { + if let Some(variant) = e.downcast_ref::<DieselError>() { + if *variant != NotFound { + error!("error encountered loading meme history: {}", e); + } + } + + format!("{}. [{}{}] not found. invoked by {}.", i + 1, rand, ago, invoker_name) + }, + }; + + Ok(result) } #[command] @@ -211,11 +222,12 @@ pub async fn stats(ctx: &Context, msg: &Message, _: Args) -> CommandResult { debug!("reporting stats"); - 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: 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?; - 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); + 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); let s = format!( r#" @@ -270,15 +282,14 @@ and *{}* was the most-memed overall ({})"#, #[command] pub async fn memers(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { - use db; - use itertools::Itertools; use serenity::model::id::UserId; let s = db::memers()? .into_iter() - .map(|info| { - let user = UserId::new(info.user_id).to_user(&ctx)?; - let username = user.nick_in(&ctx, CONFIG.discord.guild()).unwrap_or(user.name); + .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 res = format!( "**{}**: {} total, {} random, {} specific. favorite meme: *{}* ({})", @@ -290,9 +301,10 @@ pub async fn memers(ctx: &Context, msg: &Message, _args: Args) -> CommandResult info.most_used_meme_count, ); - Ok(res) + Result::<_, CommandError>::Ok(res) }) - .collect::<Result<Vec<_>>>()? + .try_collect::<Vec<String>>() + .await? .into_iter() .join("\n"); @@ -301,11 +313,9 @@ pub async fn memers(ctx: &Context, msg: &Message, _args: Args) -> CommandResult #[command] pub async fn query(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { - use std::borrow::Borrow; - - use itertools::Itertools; use regex::Regex; use serenity::model::id::UserId; + use std::borrow::Borrow; use crate::{ db, @@ -318,11 +328,10 @@ pub async fn query(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul 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 = guild.read().guild(&ctx).ok_or(anyhow!("couldn't find guild"))?; + let guild = + msg.channel_id.to_channel(&ctx).await?.guild().ok_or(anyhow!("couldn't find guild"))?; - let guild = guild.read(); + let guild = guild.guild(&ctx).ok_or(anyhow!("couldn't find guild"))?; let creator: Option<u64> = { let creator = args.quoted().current().map(|s| CREATOR_REGEX.is_match(s)).unwrap_or(false); @@ -354,11 +363,13 @@ pub async fn query(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul } }; - let result = db::query_meme(args.rest(), creator, order)? - .into_iter() - .map(|(meme, metadata)| { - 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); + let iter = db::query_meme(args.rest(), creator, order)?.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); Ok(format!( "*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**", @@ -368,9 +379,12 @@ pub async fn query(ctx: &Context, msg: &Message, mut args: Args) -> CommandResul meme.content.map_or(0, |s| s.len()), meme.image_id.map_or("NO", |_s| "YES"), meme.audio_id.map_or("NO", |_s| "YES"), - )) + )) as Result<String, CommandError> }) - .collect::<Result<Vec<_>>>()? + .try_collect::<Vec<String>>() + .await; + + let result = result? .into_iter() .scan(0, |state, line| { *state = *state + line.len() + 1; |
