use crate::album_manager::Album; use crate::error::Error; use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType}; use crate::models::random::RandomConfig; use crate::{command, group, GlobalData, BAD_APPLE}; use rand::prelude::SliceRandom; use rand::thread_rng; use reqwest::Client; use serde::{Deserialize, Serialize}; use serenity::client::Context; use serenity::constants::MESSAGE_CODE_LIMIT; use serenity::framework::standard::{Args, CommandResult}; use serenity::model::channel::Message; use serenity::utils::MessageBuilder; use std::collections::HashMap; use std::time::Duration; #[group] #[commands(dad_joke, roll, bad_apple, insult, add_random, list_random)] pub struct Joke; #[derive(Clone, Serialize, Deserialize)] struct DadJoke { pub id: String, pub joke: String, pub status: i32, } #[command] #[only_in(guilds)] #[aliases("dad")] #[description("Ask your dad")] async fn dad_joke(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let client = Client::new(); let joke: DadJoke = client .get("https://icanhazdadjoke.com/") .header("Accept", "application/json") .send() .await? .json() .await?; msg.reply(&ctx.http, joke.joke).await?; Ok(()) } #[derive(Serialize, Deserialize)] struct RandomCtx { user: String, random_image: HashMap, } impl RandomCtx { pub fn new(user_name: &str, global_data: &GlobalData) -> Result { let mut random_image: HashMap = HashMap::new(); let albums: Vec = global_data.db.filter(|_, _album: &Album| true)?.collect(); for album in albums { let image = album.images.choose(&mut thread_rng()); if let Some(image) = image { random_image.insert( album.album_name.clone(), image.link(&global_data.cfg.base_url).to_string(), ); } } Ok(Self { user: user_name.to_string(), random_image, }) } } pub fn render_random( author_name: &str, global_data: &GlobalData, template: &str, ) -> Result { let random_ctx = RandomCtx::new(author_name, global_data)?; Ok(tera::Tera::one_off( template, &tera::Context::from_serialize(random_ctx)?, false, )?) } pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandResult { let data = ctx.data.read().await; let global_data = data.get::().unwrap(); let random = match RandomConfig::get_random(&global_data.db, random_name)? { None => return Ok(()), Some(r) => r, }; let response_template_str = match random.get_response()? { None => { msg.reply(&ctx, format!("I'm all out of material for {}", random_name)) .await?; return Ok(()); } Some(resp) => resp, }; let guild_member = msg .guild(&ctx.cache) .unwrap() .member(&ctx.http, msg.author.id) .await?; let reply = render_random( &guild_member.display_name(), global_data, response_template_str, )?; msg.reply(&ctx.http, reply).await?; Ok(()) } #[command] #[example("8ball Funny Response haha")] pub async fn add_random(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { if args.len() < 2 { msg.reply( &ctx.http, "Look kid, you need to provide both the random name and response", ) .await?; return Ok(()); } let data = ctx.data.read().await; let global_data = data.get::().unwrap(); let random_name = args.parse::()?; args.advance(); let random_response = args.rest(); if let Err(err) = render_random(&msg.author.name, global_data, random_response) { msg.reply( &ctx.http, format!("Template failed test render, try again: {}", err), ) .await?; } else { RandomConfig::add_random(&global_data.db, &random_name, random_response)?; msg.reply(&ctx.http, "New random added!").await?; } Ok(()) } #[command] #[example("8ball Funny Response haha")] pub async fn list_random(ctx: &Context, msg: &Message, args: Args) -> CommandResult { if args.is_empty() { msg.reply( &ctx.http, "Look kid, you need to provide both the random name", ) .await?; return Ok(()); } let data = ctx.data.read().await; let global_data = data.get::().unwrap(); let random_name = args.parse::()?; let random = RandomConfig::get_random(&global_data.db, &random_name)?; let dm_channel = msg.author.id.create_dm_channel(&ctx.http).await?; if let Some(random) = random { let mut msg_builder = MessageBuilder::new(); msg_builder.push_line(format!("All possible responses for {}:", random_name)); for resp in random.responses { let line_msg = format!("* {}", resp); if (msg_builder.0.len() + line_msg.len()) > MESSAGE_CODE_LIMIT { dm_channel.say(&ctx.http, msg_builder.build()).await?; msg_builder.0.clear(); } msg_builder.push_line(line_msg); } if !msg_builder.0.is_empty() { dm_channel.say(&ctx.http, msg_builder.build()).await?; } } else { msg.reply( &ctx.http, "*glances to the back room*, nope we ain't got that random", ) .await?; } Ok(()) } #[command] #[aliases("roll")] #[description("Roll a die!")] async fn roll(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let roll = args.rest().parse::(); let reply = match roll { Ok(roll) => format!("You rolled: **{}**", roll), Err(_) => "Error parsing dice roll".to_string(), }; msg.reply(&ctx.http, reply).await?; Ok(()) } #[command] #[only_in(guilds)] #[bucket = "bad_apple"] async fn bad_apple(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let mut frames = BAD_APPLE.split('|'); let first_frame = frames.next().unwrap(); if first_frame.len() > 2000 { msg.reply(&ctx.http, format!("Frame too big: {}", first_frame.len())) .await?; return Ok(()); } let mut bad_apple_msg = msg .reply( &ctx.http, MessageBuilder::default() .push_codeblock_safe(first_frame, None) .build(), ) .await?; for (idx, frame) in frames.enumerate() { tokio::time::sleep(Duration::from_millis(100)).await; if (idx % 10) == 0 { bad_apple_msg .edit(&ctx.http, |m| { m.content( MessageBuilder::default() .push_codeblock_safe(frame, None) .build(), ) }) .await?; } } Ok(()) } #[command] #[aliases("compliment")] async fn insult(ctx: &Context, msg: &Message, args: Args) -> CommandResult { let data = ctx.data.read().await; let global = data.get::().unwrap(); let pool = if msg.content.as_str().starts_with("!insult") { ResponseType::Insult } else if msg.content.as_str().starts_with("!compliment") { ResponseType::Compliment } else { msg.reply(&ctx, "The h*ck did you just say to me??").await?; return Ok(()); }; let target = args.rest(); let output = RandomResponseTemplate::get_random_response(&global.db, pool, target)?; if let Some(output) = output { msg.reply(&ctx.http, output).await?; } else { msg.reply(&ctx, format!("No {}s, mr freeman??", msg.content)) .await?; } Ok(()) }