Ported randoms to the DB

+ Also added commands for adding and listing randoms
+ fmt, clippy failing in a library...
This commit is contained in:
Joey Hines 2023-01-18 19:10:15 -07:00
parent 766dc6f171
commit 95b965a4a9
Signed by: joeyahines
GPG Key ID: 995E531F7A569DDB
5 changed files with 177 additions and 49 deletions

View File

@ -13,7 +13,6 @@ use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use structopt::StructOpt;
use tera::Tera;
use tokio::sync::mpsc::{channel, Receiver, Sender};
use tokio::sync::Mutex;
@ -29,12 +28,6 @@ pub struct AlbumConfig {
pub album_id: String,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RandomConfig {
pub name: String,
pub responses: Vec<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct MotivationConfig {
pub album: Vec<String>,
@ -57,9 +50,6 @@ pub struct BotConfig {
#[serde(default)]
pub albums: Vec<AlbumConfig>,
#[serde(default)]
pub randoms: Vec<RandomConfig>,
pub motivation: MotivationConfig,
pub insults: Vec<InsultComplimentTemplate>,
@ -88,7 +78,6 @@ impl BotConfig {
#[derive(Debug)]
pub struct BotState {
pub accepted_nsfw: Option<UserId>,
pub templates: HashMap<String, Tera>,
pub albums: HashMap<String, Vec<Image>>,
pub bad_apple_running: bool,
}
@ -96,18 +85,6 @@ pub struct BotState {
impl BotState {
pub async fn new(cfg: &BotConfig) -> Result<Self, Error> {
let mut albums: HashMap<String, Vec<Image>> = HashMap::new();
let mut templates = HashMap::new();
for random_config in cfg.randoms.iter() {
let mut templates_set = Tera::default();
for (idx, response) in random_config.responses.iter().enumerate() {
templates_set
.add_raw_template(&idx.to_string(), response)
.unwrap();
}
templates.insert(random_config.name.clone(), templates_set);
}
for album in &cfg.albums {
albums.insert(
@ -118,7 +95,6 @@ impl BotState {
Ok(Self {
accepted_nsfw: None,
templates,
albums,
bad_apple_running: false,
})

View File

@ -1,10 +1,12 @@
use crate::error::Error;
use crate::models::random::RandomConfig;
use crate::{command, group, GlobalData, BAD_APPLE};
use rand::prelude::IteratorRandom;
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;
@ -12,7 +14,7 @@ use std::collections::HashMap;
use std::time::Duration;
#[group]
#[commands(dad_joke, roll, bad_apple, insult)]
#[commands(dad_joke, roll, bad_apple, insult, add_random, list_random)]
pub struct Joke;
#[derive(Clone, Serialize, Deserialize)]
@ -49,10 +51,7 @@ struct RandomCtx {
}
impl RandomCtx {
pub async fn new(
user: serenity::model::guild::Member,
global_data: &GlobalData,
) -> Result<Self, Error> {
pub fn new(user_name: &str, global_data: &GlobalData) -> Result<Self, Error> {
let mut random_image: HashMap<String, String> = HashMap::new();
for album in &global_data.cfg.albums {
@ -64,28 +63,42 @@ impl RandomCtx {
}
Ok(Self {
user: (*user.display_name()).clone(),
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 templates = global_data.bot_state.templates.get(random_name);
let templates = if let Some(templates) = templates {
templates
} else {
return Ok(());
let random = match RandomConfig::get_random(&global_data.db, random_name)? {
None => return Ok(()),
Some(r) => r,
};
let template = {
let mut rng = thread_rng();
templates.get_template_names().choose(&mut rng)
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
@ -94,13 +107,11 @@ pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandR
.member(&ctx.http, msg.author.id)
.await?;
let random_ctx = RandomCtx::new(guild_member, global_data).await?;
let reply = if let Some(template) = template {
templates.render(template, &tera::Context::from_serialize(&random_ctx)?)?
} else {
"Sorry kid, all out of messages.".to_string()
};
let reply = render_random(
&guild_member.display_name(),
global_data,
response_template_str,
)?;
msg.reply(&ctx.http, reply).await?;
@ -108,7 +119,87 @@ pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandR
}
#[command]
#[only_in(guilds)]
#[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.len() < 1 {
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)?;
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 {
msg.channel_id.say(&ctx.http, msg_builder.build()).await?;
msg_builder.0.clear();
}
msg_builder.push_line(line_msg);
}
if !msg_builder.0.is_empty() {
msg.channel_id.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 {
@ -168,7 +259,6 @@ async fn bad_apple(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
#[command]
#[aliases("compliment")]
#[only_in(guilds)]
async fn insult(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
let data = ctx.data.read().await;
let global = data.get::<GlobalData>().unwrap();

View File

@ -4,6 +4,7 @@ mod error;
mod imgur;
mod insult_compliment;
mod inventory;
mod models;
mod user;
use crate::config::{Args, BotConfig, Channel, GlobalData};

1
src/models/mod.rs Normal file
View File

@ -0,0 +1 @@
pub mod random;

60
src/models/random.rs Normal file
View File

@ -0,0 +1,60 @@
use crate::error::Error;
use j_db::database::Database;
use j_db::model::JdbModel;
use rand::prelude::SliceRandom;
use rand::thread_rng;
use serde::{Deserialize, Serialize};
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RandomConfig {
id: Option<u64>,
pub name: String,
pub responses: Vec<String>,
}
impl JdbModel for RandomConfig {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"randoms".to_string()
}
fn check_unique(&self, other: &Self) -> bool {
!self.name.eq_ignore_ascii_case(&other.name)
}
}
impl RandomConfig {
pub fn add_random(db: &Database, name: &str, response: &str) -> Result<(), Error> {
let mut random = match Self::get_random(db, name)? {
None => db.insert::<RandomConfig>(Self {
id: None,
name: name.to_string(),
responses: vec![],
})?,
Some(random) => random,
};
random.responses.push(response.to_string());
db.insert::<RandomConfig>(random)?;
Ok(())
}
pub fn get_random(db: &Database, name: &str) -> Result<Option<Self>, Error> {
Ok(db
.filter(|_, random: &RandomConfig| random.name.eq_ignore_ascii_case(name))?
.next())
}
pub fn get_response(&self) -> Result<Option<&String>, Error> {
Ok(self.responses.choose(&mut thread_rng()))
}
}