use crate::album_manager::{Album, AlbumQuery, ImageQuery, ImageSort}; use crate::error::Error; use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType}; use crate::models::random::{Random, RandomConfig}; use crate::rass::RaaSCmd; use crate::{command, group, GlobalData, BAD_APPLE}; use reqwest::Client; use serde::{Deserialize, Serialize}; use serenity::all::{CreateAttachment, CreateMessage}; use serenity::builder::EditMessage; 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, real_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] #[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 async fn new(user_name: &str, global_data: &GlobalData) -> Result { let mut random_image: HashMap = HashMap::new(); let albums: Vec = global_data .picox .query_album(AlbumQuery { album_name: None }) .await?; for album in albums { let images = global_data .picox .query_image(ImageQuery { album: Some(album.album_name.clone()), tags: vec![], order: ImageSort::Random, limit: 1, }) .await?; let image = images.first(); if let Some(image) = image { random_image.insert(album.album_name.clone(), image.link.to_string()); } } Ok(Self { user: user_name.to_string(), random_image, }) } } pub async fn render_random( author_name: &str, global_data: &GlobalData, template: &str, ) -> Result { let random_ctx = RandomCtx::new(author_name, global_data).await?; 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(&global_data.db)? { 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_id .unwrap() .member(&ctx.http, msg.author.id) .await .unwrap(); let reply = render_random( guild_member.display_name(), global_data, &response_template_str, ) .await?; 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).await { 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 {} all {} of them..:", random_name, random.responses.len() )); for resp in random.responses { let random = global_data.db.get::(resp)?; let line_msg = format!("* ({}) ({}) {}", resp, random.score, random.response); 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] #[aliases("real_roll")] #[description("Roll a real die!")] async fn real_roll(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { let mut data = ctx.data.write().await; let global = data.get_mut::().unwrap(); if let Some(raas_handler) = &mut global.bot_state.raas_handler { raas_handler.send_msg_queue.send(RaaSCmd::Roll(3)).await?; msg.reply(&ctx.http, "Sent request to Roll Bot...").await?; if let Some(img) = raas_handler.recv_msg_queue.recv().await { match img { RaaSCmd::Roll(_) => {} RaaSCmd::Img(img_data) => { msg.channel_id .send_message( &ctx.http, CreateMessage::new() .content("Your roll my friend, hope its good I can't read!") .add_file(CreateAttachment::bytes(img_data, "roll.jpg")), ) .await?; } } } else { msg.reply(&ctx.http, "Roll Bot is gone... oh god!").await?; } } else { msg.reply(&ctx.http, "Looks like I can't reach my real flesh") .await?; } Ok(()) } #[command] #[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, EditMessage::new().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(()) }