Added commands for managing randoms, fix db import bug

This commit is contained in:
Joey Hines 2025-07-10 20:40:12 -06:00
parent dfffe202df
commit 7548b38b04
Signed by: joeyahines
GPG Key ID: 38BA6F25C94C9382
11 changed files with 155 additions and 20 deletions

2
Cargo.lock generated
View File

@ -1093,7 +1093,7 @@ dependencies = [
[[package]] [[package]]
name = "fren" name = "fren"
version = "1.8.0" version = "1.9.0"
dependencies = [ dependencies = [
"axum 0.8.1", "axum 0.8.1",
"base64 0.22.1", "base64 0.22.1",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "fren" name = "fren"
version = "1.8.0" version = "1.9.0"
edition = "2024" edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -3,7 +3,7 @@ use crate::error::Error;
use crate::migrations::{CURRENT_DB_VERSION, do_migration}; use crate::migrations::{CURRENT_DB_VERSION, do_migration};
use config::{Config, File}; use config::{Config, File};
use cta_api::CTAClient; use cta_api::CTAClient;
use j_db::database::Database; use j_db::database::{DB_METADATA_ID, Database};
use j_db::metadata::DBMetadata; use j_db::metadata::DBMetadata;
use poise::serenity_prelude::model::id::ChannelId; use poise::serenity_prelude::model::id::ChannelId;
use poise::serenity_prelude::model::prelude::{GuildId, UserId}; use poise::serenity_prelude::model::prelude::{GuildId, UserId};
@ -90,10 +90,9 @@ impl GlobalData {
if version == 0 { if version == 0 {
db.insert::<j_db::metadata::DBMetadata>(DBMetadata { db.insert::<j_db::metadata::DBMetadata>(DBMetadata {
id: None, id: Some(DB_METADATA_ID),
version: CURRENT_DB_VERSION, version: CURRENT_DB_VERSION,
}) })?;
.unwrap();
} }
do_migration(&db); do_migration(&db);

View File

@ -8,6 +8,7 @@ use crate::models::lil_fren::{
}; };
use crate::models::managed_roles::ManagedRole; use crate::models::managed_roles::ManagedRole;
use crate::models::movie::Movie; use crate::models::movie::Movie;
use crate::models::random::{Random, RandomConfig};
use crate::models::social_credit::SocialCreditPhrase; use crate::models::social_credit::SocialCreditPhrase;
use crate::models::task::Task; use crate::models::task::Task;
use crate::user::User; use crate::user::User;
@ -351,3 +352,44 @@ pub async fn remove_movie(
.await?; .await?;
Ok(()) Ok(())
} }
/// Reset Random Scores
#[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn reset_random_score(
ctx: Context<'_>,
#[description = "Random to reset"] random: String,
) -> Result<(), Error> {
RandomConfig::reset_scores(&ctx.data().db, &random)?;
ctx.reply(
"Scores have been reset, hope you still like getting the same response 5 times in a row!",
)
.await?;
Ok(())
}
/// Reset Random Scores
#[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn remove_random_result(
ctx: Context<'_>,
#[description = "ID of the random to remove"] random_id: u64,
) -> Result<(), Error> {
let resp = ctx.data().db.remove::<Random>(random_id)?;
ctx.data()
.db
.filter(|_, _: &RandomConfig| true)?
.for_each(|mut random: RandomConfig| {
if random.responses.remove(&resp.id().unwrap()) {
ctx.data().db.insert(random).ok();
}
});
let mut msg_builder = MessageBuilder::new();
msg_builder.push_line("The following random has been removed: ");
msg_builder.push_codeblock_safe(resp.response, None);
ctx.reply(msg_builder.build()).await?;
Ok(())
}

View File

@ -156,7 +156,7 @@ pub async fn add_random(
Ok(()) Ok(())
} }
/// List all of the random sets /// List all the responses from a random set
#[poise::command(prefix_command, category = "Joke")] #[poise::command(prefix_command, category = "Joke")]
pub async fn list_random( pub async fn list_random(
ctx: Context<'_>, ctx: Context<'_>,
@ -197,6 +197,28 @@ pub async fn list_random(
Ok(()) Ok(())
} }
/// List all random sets
#[poise::command(prefix_command, category = "Joke")]
pub async fn list_randoms(ctx: Context<'_>) -> Result<(), Error> {
let randoms: Vec<RandomConfig> = ctx
.data()
.db
.filter(|_, _random: &RandomConfig| true)?
.collect();
let mut msg_build = MessageBuilder::new();
msg_build.push_line("List of all randoms:");
for random in &randoms {
msg_build.push_line(format!("* {}", random.name));
}
ctx.reply(msg_build.build()).await?;
Ok(())
}
/// Roll a totally not loaded digital die! /// Roll a totally not loaded digital die!
#[poise::command(prefix_command, category = "Joke")] #[poise::command(prefix_command, category = "Joke")]
pub async fn roll( pub async fn roll(

View File

@ -306,6 +306,8 @@ pub async fn run_bot(global_data: GlobalData) {
admin::remove_social_credit_phrase(), admin::remove_social_credit_phrase(),
admin::list_social_credit_phrases(), admin::list_social_credit_phrases(),
admin::remove_movie(), admin::remove_movie(),
admin::reset_random_score(),
admin::remove_random_result(),
album::add_image(), album::add_image(),
album::list_albums(), album::list_albums(),
birthday::add_birthday(), birthday::add_birthday(),
@ -329,6 +331,7 @@ pub async fn run_bot(global_data: GlobalData) {
joke::list_random(), joke::list_random(),
joke::real_roll(), joke::real_roll(),
joke::roll(), joke::roll(),
joke::list_randoms(),
little_fren::adopt(), little_fren::adopt(),
little_fren::checkup(), little_fren::checkup(),
little_fren::feed(), little_fren::feed(),

View File

@ -25,6 +25,7 @@ pub enum Error {
CTAError(cta_api::Error), CTAError(cta_api::Error),
NoImageFound, NoImageFound,
PipelineArgumentError(ModifyImageArgError), PipelineArgumentError(ModifyImageArgError),
NoRandomFound,
} }
impl StdError for Error {} impl StdError for Error {}
@ -114,6 +115,7 @@ impl Display for Error {
Error::CTAError(err) => write!(f, "The CTA had an error, I'm shocked: {}", err), Error::CTAError(err) => write!(f, "The CTA had an error, I'm shocked: {}", err),
Error::NoImageFound => write!(f, "Image not found"), Error::NoImageFound => write!(f, "Image not found"),
Error::PipelineArgumentError(err) => write!(f, "{}", err), Error::PipelineArgumentError(err) => write!(f, "{}", err),
Error::NoRandomFound => write!(f, "No random found"),
} }
} }
} }

View File

@ -1,6 +1,6 @@
use crate::album_manager::{AlbumManager, ImageQuery, ImageSort}; use crate::album_manager::{AlbumManager, ImageQuery, ImageSort};
use crate::error::Error; use crate::error::Error;
use crate::models::motivation::{MotivationConfig}; use crate::models::motivation::MotivationConfig;
use j_db::database::Database; use j_db::database::Database;
use j_db::error::JDbError; use j_db::error::JDbError;
use magick_rust::{CompositeOperator, DrawingWand, FilterType, MagickWand, PixelWand}; use magick_rust::{CompositeOperator, DrawingWand, FilterType, MagickWand, PixelWand};
@ -320,8 +320,8 @@ impl FromStr for ModifyImage {
Err(Self::Err::NotAllArgumentsSupplied) Err(Self::Err::NotAllArgumentsSupplied)
} else { } else {
let alignment_str = args.next().unwrap(); let alignment_str = args.next().unwrap();
let alignment: Alignment = Alignment::from_str(alignment_str) let alignment: Alignment =
.map_err(Self::Err::InvalidAlignment)?; Alignment::from_str(alignment_str).map_err(Self::Err::InvalidAlignment)?;
let background_album = args.next().map(|s| s.to_string()); let background_album = args.next().map(|s| s.to_string());
Ok(Self::GreenScreen { Ok(Self::GreenScreen {

View File

@ -0,0 +1,34 @@
use j_db::database::{DB_METADATA_ID, Database};
use j_db::migration::Migration;
use json::JsonValue;
pub struct Migration8FixMetadataId {}
impl Migration for Migration8FixMetadataId {
fn up(&self, db: &Database) -> j_db::error::Result<()> {
let tree = db.db.open_tree("DBMetadata")?;
tree.iter().for_each(|md| {
if let Ok((md_id, _md_bytes)) = md {
let _ = tree.remove(md_id).is_ok();
}
});
let mut md = JsonValue::new_object();
md["id"] = JsonValue::Number(json::number::Number::from(DB_METADATA_ID));
md["version"] = JsonValue::Number(json::number::Number::from(self.version()));
tree.insert(DB_METADATA_ID.to_be_bytes(), md.to_string().as_bytes())?;
Ok(())
}
fn down(&self, _db: &Database) -> j_db::error::Result<()> {
Ok(())
}
fn version(&self) -> u64 {
8
}
}

View File

@ -2,6 +2,7 @@ use crate::migrations::migration_4_update_random::Migration4UpdateRandoms;
use crate::migrations::migration_5_update_motivation::Migration5UpdateMotivation; use crate::migrations::migration_5_update_motivation::Migration5UpdateMotivation;
use crate::migrations::migration_6_add_social_credit::Migration6AddSocialCredit; use crate::migrations::migration_6_add_social_credit::Migration6AddSocialCredit;
use crate::migrations::migration_7_flip_bounds::Migration7FlipBounds; use crate::migrations::migration_7_flip_bounds::Migration7FlipBounds;
use crate::migrations::migration_8_fix_metadata_id::Migration8FixMetadataId;
use crate::migrations::migration2_remove_imgur::Migration2RemoveImgur; use crate::migrations::migration2_remove_imgur::Migration2RemoveImgur;
use crate::migrations::migration3_remove_img::Migration3RemoveImage; use crate::migrations::migration3_remove_img::Migration3RemoveImage;
use j_db::database::Database; use j_db::database::Database;
@ -14,8 +15,9 @@ mod migration_4_update_random;
mod migration_5_update_motivation; mod migration_5_update_motivation;
mod migration_6_add_social_credit; mod migration_6_add_social_credit;
mod migration_7_flip_bounds; mod migration_7_flip_bounds;
mod migration_8_fix_metadata_id;
pub const CURRENT_DB_VERSION: u64 = 7; pub const CURRENT_DB_VERSION: u64 = 8;
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
pub fn do_migration(db: &Database) { pub fn do_migration(db: &Database) {
@ -64,6 +66,12 @@ pub fn do_migration(db: &Database) {
Direction::Up, Direction::Up,
) )
.unwrap(), .unwrap(),
8 => migration::do_migration::<Migration8FixMetadataId>(
db,
Migration8FixMetadataId {},
Direction::Up,
)
.unwrap(),
_ => {} _ => {}
} }
} }

View File

@ -96,18 +96,20 @@ impl RandomConfig {
.next()) .next())
} }
pub fn get_response(&self, db: &Database) -> Result<Option<String>, Error> { fn get_responses(&self, db: &Database) -> Result<Vec<Random>, Error> {
let mut responses: Vec<Random> = db Ok(db
.filter(|_, random: &Random| self.responses.contains(&random.id().unwrap()))? .filter(|_, random: &Random| self.responses.contains(&random.id().unwrap()))?
.collect(); .collect())
}
if !responses.iter().any(|r| r.score > 0) { pub fn get_response(&self, db: &Database) -> Result<Option<String>, Error> {
for response in &mut responses { let responses = self.get_responses(db)?;
response.score = MAX_SCORE;
db.insert(response.clone())?; let responses = if !responses.iter().any(|r| r.score > 0) {
} self.reset_scores_internal(db, responses)?
} } else {
responses
};
if responses.is_empty() { if responses.is_empty() {
return Ok(None); return Ok(None);
@ -123,4 +125,27 @@ impl RandomConfig {
Ok(Some(resp.response)) Ok(Some(resp.response))
} }
pub fn reset_scores(db: &Database, random: &str) -> Result<(), Error> {
let random = Self::get_random(db, random)?.ok_or(Error::NoRandomFound)?;
let responses = random.get_responses(db)?;
random.reset_scores_internal(db, responses)?;
Ok(())
}
fn reset_scores_internal(
&self,
db: &Database,
mut responses: Vec<Random>,
) -> Result<Vec<Random>, Error> {
for response in &mut responses {
response.score = MAX_SCORE;
db.insert(response.clone())?;
}
Ok(responses)
}
} }