348 lines
9.7 KiB
Rust
348 lines
9.7 KiB
Rust
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<String, String>,
|
|
}
|
|
|
|
impl RandomCtx {
|
|
pub async 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
|
|
.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<String, Error> {
|
|
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::<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(&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::<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).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::<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 {} all {} of them..:",
|
|
random_name,
|
|
random.responses.len()
|
|
));
|
|
|
|
for resp in random.responses {
|
|
let random = global_data.db.get::<Random>(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::<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]
|
|
#[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::<GlobalData>().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::<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(())
|
|
}
|