Updated randoms to be less repeaty

This commit is contained in:
Joey Hines 2024-03-22 21:06:06 -06:00
parent a65fdfdd4d
commit 93975f463c
Signed by: joeyahines
GPG Key ID: 995E531F7A569DDB
5 changed files with 156 additions and 17 deletions

View File

@ -1,7 +1,7 @@
use crate::album_manager::{Album, AlbumQuery, ImageQuery, ImageSort}; use crate::album_manager::{Album, AlbumQuery, ImageQuery, ImageSort};
use crate::error::Error; use crate::error::Error;
use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType}; use crate::models::insult_compliment::{RandomResponseTemplate, ResponseType};
use crate::models::random::RandomConfig; use crate::models::random::{Random, RandomConfig};
use crate::{command, group, GlobalData, BAD_APPLE}; use crate::{command, group, GlobalData, BAD_APPLE};
use reqwest::Client; use reqwest::Client;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -106,7 +106,7 @@ pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandR
Some(r) => r, Some(r) => r,
}; };
let response_template_str = match random.get_response()? { let response_template_str = match random.get_response(&global_data.db)? {
None => { None => {
msg.reply(&ctx, format!("I'm all out of material for {}", random_name)) msg.reply(&ctx, format!("I'm all out of material for {}", random_name))
.await?; .await?;
@ -125,7 +125,7 @@ pub async fn random(ctx: &Context, msg: &Message, random_name: &str) -> CommandR
let reply = render_random( let reply = render_random(
guild_member.display_name(), guild_member.display_name(),
global_data, global_data,
response_template_str, &response_template_str,
) )
.await?; .await?;
@ -189,10 +189,16 @@ pub async fn list_random(ctx: &Context, msg: &Message, args: Args) -> CommandRes
let dm_channel = msg.author.id.create_dm_channel(&ctx.http).await?; let dm_channel = msg.author.id.create_dm_channel(&ctx.http).await?;
if let Some(random) = random { if let Some(random) = random {
let mut msg_builder = MessageBuilder::new(); let mut msg_builder = MessageBuilder::new();
msg_builder.push_line(format!("All possible responses for {}:", random_name)); msg_builder.push_line(format!(
"All possible responses for {} all {} of them..:",
random_name,
random.responses.len()
));
for resp in random.responses { for resp in random.responses {
let line_msg = format!("* {}", resp); 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 { if (msg_builder.0.len() + line_msg.len()) > MESSAGE_CODE_LIMIT {
dm_channel.say(&ctx.http, msg_builder.build()).await?; dm_channel.say(&ctx.http, msg_builder.build()).await?;

View File

@ -0,0 +1,57 @@
use crate::models::random::{Random, RandomConfig};
use j_db::database::Database;
use j_db::error::JDbError;
use j_db::migration::Migration;
use j_db::model::JdbModel;
use json::number::Number;
use json::JsonValue;
pub struct Migration4UpdateRandoms {}
impl Migration for Migration4UpdateRandoms {
fn up(&self, db: &Database) -> j_db::error::Result<()> {
let random_config_tree = db.db.open_tree(RandomConfig::tree())?;
for config in random_config_tree.iter() {
let (id, config) = config?;
let mut config = json::parse(std::str::from_utf8(&config).unwrap()).unwrap();
let mut responses: Vec<u64> = Vec::new();
for random in config["responses"].members() {
let random = Random::new(&random.to_string());
let random = db.insert(random);
let random = match random {
Ok(r) => r,
Err(err) => match err {
JDbError::NotUnique => continue,
_ => return Err(err),
},
};
responses.push(random.id().unwrap());
}
config.remove("responses");
let responses: Vec<JsonValue> = responses
.iter()
.map(|id| JsonValue::Number(Number::from(*id)))
.collect();
config["responses"] = JsonValue::Array(responses);
random_config_tree.insert(id, config.to_string().as_bytes())?;
}
Ok(())
}
fn down(&self, _db: &Database) -> j_db::error::Result<()> {
Ok(())
}
fn version(&self) -> u64 {
4
}
}

View File

@ -1,13 +1,15 @@
use crate::migrations::migration2_remove_imgur::Migration2RemoveImgur; use crate::migrations::migration2_remove_imgur::Migration2RemoveImgur;
use crate::migrations::migration3_remove_img::Migration3RemoveImage;
use crate::migrations::migration_4_update_random::Migration4UpdateRandoms;
use j_db::database::Database; use j_db::database::Database;
use j_db::migration; use j_db::migration;
use j_db::migration::Direction; use j_db::migration::Direction;
use crate::migrations::migration3_remove_img::Migration3RemoveImage;
mod migration2_remove_imgur; mod migration2_remove_imgur;
mod migration3_remove_img; mod migration3_remove_img;
mod migration_4_update_random;
const CURRENT_DB_VERSION: u64 = 3; const CURRENT_DB_VERSION: u64 = 4;
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
pub fn do_migration(db: &Database) { pub fn do_migration(db: &Database) {
@ -30,6 +32,14 @@ pub fn do_migration(db: &Database) {
) )
.unwrap(); .unwrap();
} }
4 => {
migration::do_migration::<Migration4UpdateRandoms>(
db,
Migration4UpdateRandoms {},
Direction::Up,
)
.unwrap();
}
_ => {} _ => {}
} }
} }

View File

@ -1,15 +1,53 @@
use crate::error::Error; use crate::error::Error;
use j_db::database::Database; use j_db::database::Database;
use j_db::model::JdbModel; use j_db::model::JdbModel;
use rand::prelude::SliceRandom; use rand::distributions::WeightedIndex;
use rand::prelude::Distribution;
use rand::thread_rng; use rand::thread_rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet;
const MAX_SCORE: u16 = 10;
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Random {
id: Option<u64>,
pub response: String,
pub score: u16,
}
impl Random {
pub fn new(response: &str) -> Self {
Self {
id: None,
response: response.to_string(),
score: MAX_SCORE,
}
}
}
impl JdbModel for Random {
fn id(&self) -> Option<u64> {
self.id
}
fn set_id(&mut self, id: u64) {
self.id = Some(id)
}
fn tree() -> String {
"random_opt".to_string()
}
fn check_unique(&self, other: &Self) -> bool {
!self.response.eq_ignore_ascii_case(&other.response)
}
}
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct RandomConfig { pub struct RandomConfig {
id: Option<u64>, id: Option<u64>,
pub name: String, pub name: String,
pub responses: Vec<String>, pub responses: HashSet<u64>,
} }
impl JdbModel for RandomConfig { impl JdbModel for RandomConfig {
@ -32,18 +70,22 @@ impl JdbModel for RandomConfig {
impl RandomConfig { impl RandomConfig {
pub fn add_random(db: &Database, name: &str, response: &str) -> Result<(), Error> { pub fn add_random(db: &Database, name: &str, response: &str) -> Result<(), Error> {
let mut random = match Self::get_random(db, name)? { let mut randoms = match Self::get_random(db, name)? {
None => db.insert::<RandomConfig>(Self { None => db.insert::<RandomConfig>(Self {
id: None, id: None,
name: name.to_string(), name: name.to_string(),
responses: vec![], responses: HashSet::new(),
})?, })?,
Some(random) => random, Some(random) => random,
}; };
random.responses.push(response.to_string()); let random = Random::new(response);
db.insert::<RandomConfig>(random)?; let random = db.insert(random)?;
randoms.responses.insert(random.id().unwrap());
db.insert::<RandomConfig>(randoms)?;
Ok(()) Ok(())
} }
@ -54,7 +96,31 @@ impl RandomConfig {
.next()) .next())
} }
pub fn get_response(&self) -> Result<Option<&String>, Error> { pub fn get_response(&self, db: &Database) -> Result<Option<String>, Error> {
Ok(self.responses.choose(&mut thread_rng())) let mut responses: Vec<Random> = db
.filter(|_, random: &Random| self.responses.contains(&random.id().unwrap()))?
.collect();
if !responses.iter().any(|r| r.score > 0) {
for response in &mut responses {
response.score = MAX_SCORE;
db.insert(response.clone())?;
}
}
if responses.is_empty() {
return Ok(None);
}
let dist = WeightedIndex::new(responses.iter().map(|r| r.score)).unwrap();
let mut resp = responses[dist.sample(&mut thread_rng())].clone();
resp.score = resp.score.saturating_sub(1);
let resp = db.insert(resp)?;
Ok(Some(resp.response))
} }
} }