FrenBot/src/discord/joke.rs
Joey Hines 7847bd38b9
Removed imgur support
+ Added my own basic image handling
+ Probably not perfect but works good enough in testing
+ clippy + fmt
2023-06-04 14:10:01 -06:00

295 lines
7.8 KiB
Rust

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<String, String>,
}
impl RandomCtx {
pub fn new(user_name: &str, global_data: &GlobalData) -> Result<Self, Error> {
let mut random_image: HashMap<String, String> = HashMap::new();
let albums: Vec<Album> = 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<String, Error> {
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::<GlobalData>().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::<GlobalData>().unwrap();
let random_name = args.parse::<String>()?;
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::<GlobalData>().unwrap();
let random_name = args.parse::<String>()?;
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::<ndm::Dice>();
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::<GlobalData>().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(())
}