This commit is contained in:
Joey Hines 2025-03-22 21:30:59 -06:00
parent 1d5b44128b
commit c5cada372e
Signed by: joeyahines
GPG Key ID: 38BA6F25C94C9382
23 changed files with 90 additions and 39 deletions

2
Cargo.lock generated
View File

@ -1080,7 +1080,7 @@ dependencies = [
[[package]] [[package]]
name = "fren" name = "fren"
version = "0.8.0" version = "1.0.0"
dependencies = [ dependencies = [
"axum 0.8.1", "axum 0.8.1",
"base64 0.22.1", "base64 0.22.1",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "fren" name = "fren"
version = "0.8.0" version = "1.0.0"
edition = "2021" 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

@ -69,6 +69,7 @@ impl BotState {
} }
pub struct GlobalData { pub struct GlobalData {
#[allow(dead_code)]
pub args: Args, pub args: Args,
pub cfg: BotConfig, pub cfg: BotConfig,
pub bot_state: Mutex<BotState>, pub bot_state: Mutex<BotState>,

View File

@ -1,4 +1,3 @@
use crate::config::BotConfig;
use crate::discord::Context; use crate::discord::Context;
use crate::error::Error; use crate::error::Error;
use crate::inventory::ItemType; use crate::inventory::ItemType;
@ -21,6 +20,7 @@ pub async fn is_admin(ctx: Context<'_>) -> Result<bool, Error> {
Ok(ctx.data().cfg.admins.contains(&ctx.author().id)) Ok(ctx.data().cfg.admins.contains(&ctx.author().id))
} }
/// Dumps the bot's internal database as JSON
#[poise::command(prefix_command, category = "Admin", check = "is_admin")] #[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn dump_db(ctx: Context<'_>) -> Result<(), Error> { pub async fn dump_db(ctx: Context<'_>) -> Result<(), Error> {
let db_dump = ctx.data().db.dump_db()?; let db_dump = ctx.data().db.dump_db()?;
@ -45,6 +45,7 @@ pub async fn dump_db(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Loads the bot's database state from the attached file
#[poise::command(prefix_command, category = "Admin", check = "is_admin")] #[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn load_db( pub async fn load_db(
ctx: Context<'_>, ctx: Context<'_>,
@ -73,6 +74,7 @@ pub async fn load_db(
Ok(()) Ok(())
} }
/// Add a new token for the bot's API
#[poise::command(prefix_command, category = "Admin", check = "is_admin")] #[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn add_key( pub async fn add_key(
ctx: Context<'_>, ctx: Context<'_>,
@ -102,6 +104,7 @@ pub async fn add_key(
Ok(()) Ok(())
} }
/// Get Lil Buddy's Stats
#[poise::command(prefix_command, category = "Admin", check = "is_admin")] #[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn debug_buddy(ctx: Context<'_>) -> Result<(), Error> { pub async fn debug_buddy(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?; let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -128,6 +131,7 @@ pub async fn debug_buddy(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Draw all the Lil Buddy states for debugging
#[poise::command(prefix_command, category = "Admin", check = "is_admin")] #[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn draw_buddy_states( pub async fn draw_buddy_states(
ctx: Context<'_>, ctx: Context<'_>,
@ -149,6 +153,7 @@ pub async fn draw_buddy_states(
Ok(()) Ok(())
} }
/// I'm from planet Minectaft, plz give op
#[poise::command(prefix_command, category = "Admin", check = "is_admin")] #[poise::command(prefix_command, category = "Admin", check = "is_admin")]
pub async fn op_give( pub async fn op_give(
ctx: Context<'_>, ctx: Context<'_>,
@ -169,6 +174,7 @@ pub async fn op_give(
Ok(()) Ok(())
} }
/// List the tasks the bot is preforming in the background
#[poise::command(prefix_command, category = "Admin")] #[poise::command(prefix_command, category = "Admin")]
pub async fn list_tasks(ctx: Context<'_>) -> Result<(), Error> { pub async fn list_tasks(ctx: Context<'_>) -> Result<(), Error> {
let tasks: Vec<Task> = ctx.data().db.filter(|_, _task: &Task| true)?.collect(); let tasks: Vec<Task> = ctx.data().db.filter(|_, _task: &Task| true)?.collect();

View File

@ -4,6 +4,7 @@ use crate::discord::Context;
use crate::error::Error; use crate::error::Error;
use poise::serenity_prelude::{Attachment, MessageBuilder}; use poise::serenity_prelude::{Attachment, MessageBuilder};
/// Add an image to a new or existing album
#[poise::command(prefix_command, category = "Albums")] #[poise::command(prefix_command, category = "Albums")]
pub async fn add_image( pub async fn add_image(
ctx: Context<'_>, ctx: Context<'_>,
@ -28,6 +29,7 @@ pub async fn add_image(
Ok(()) Ok(())
} }
/// List all albumbs
#[poise::command(prefix_command, category = "Albums")] #[poise::command(prefix_command, category = "Albums")]
pub async fn list_albums(ctx: Context<'_>) -> Result<(), Error> { pub async fn list_albums(ctx: Context<'_>) -> Result<(), Error> {
let albums = ctx let albums = ctx

View File

@ -5,6 +5,7 @@ use poise::serenity_prelude::model::id::UserId;
use poise::serenity_prelude::utils::MessageBuilder; use poise::serenity_prelude::utils::MessageBuilder;
use rand::{rng, Rng}; use rand::{rng, Rng};
/// Add your birthday to the bot, it can probably be trusted with this info
#[poise::command(prefix_command, category = "Birthdays")] #[poise::command(prefix_command, category = "Birthdays")]
pub async fn add_birthday( pub async fn add_birthday(
ctx: Context<'_>, ctx: Context<'_>,
@ -43,6 +44,7 @@ pub async fn add_birthday(
Ok(()) Ok(())
} }
/// List birthdays added to the bot
#[poise::command(prefix_command, category = "Birthdays")] #[poise::command(prefix_command, category = "Birthdays")]
pub async fn list_birthdays(ctx: Context<'_>) -> Result<(), Error> { pub async fn list_birthdays(ctx: Context<'_>) -> Result<(), Error> {
let birthdays: Vec<BirthdayEntry> = let birthdays: Vec<BirthdayEntry> =

View File

@ -1,13 +1,12 @@
use crate::config::GlobalData;
use crate::discord::Context; use crate::discord::Context;
use crate::error::Error; use crate::error::Error;
use std::net::ToSocketAddrs;
#[poise::command(prefix_command)] /// Are you sure?
#[poise::command(prefix_command, category = "Celery Man")]
pub async fn nudetayne(ctx: Context<'_>) -> Result<(), Error> { pub async fn nudetayne(ctx: Context<'_>) -> Result<(), Error> {
if ctx.invoked_command_name() == "nudetayne".to_string() { if ctx.invoked_command_name() == "nudetayne" {
ctx.reply("Not computing, please repeat.").await?; ctx.reply("Not computing, please repeat.").await?;
} else if ctx.invoked_command_name() == "NUDETAYNE".to_string() { } else if ctx.invoked_command_name() == "NUDETAYNE" {
ctx.reply("This is not suitable for work are you sure?") ctx.reply("This is not suitable for work are you sure?")
.await?; .await?;
@ -19,14 +18,16 @@ pub async fn nudetayne(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
#[poise::command(prefix_command)] /// Tayne
#[poise::command(prefix_command, category = "Celery Man")]
pub async fn tayne(ctx: Context<'_>) -> Result<(), Error> { pub async fn tayne(ctx: Context<'_>) -> Result<(), Error> {
ctx.reply("https://media.tenor.com/115eUl2XUaAAAAAM/flarhgunnstow-paul-rudd.gif") ctx.reply("https://media.tenor.com/115eUl2XUaAAAAAM/flarhgunnstow-paul-rudd.gif")
.await?; .await?;
Ok(()) Ok(())
} }
#[poise::command(prefix_command)] /// THE Celeryman
#[poise::command(prefix_command, category = "Celery Man")]
pub async fn celeryman(ctx: Context<'_>) -> Result<(), Error> { pub async fn celeryman(ctx: Context<'_>) -> Result<(), Error> {
ctx.reply("https://media.tenor.com/1iOUXZFLpBgAAAAM/dance-dancing.gif") ctx.reply("https://media.tenor.com/1iOUXZFLpBgAAAAM/dance-dancing.gif")
.await?; .await?;

View File

@ -3,6 +3,7 @@ use crate::error::Error;
use poise::serenity_prelude::builder::EditRole; use poise::serenity_prelude::builder::EditRole;
use poise::serenity_prelude::model::Colour; use poise::serenity_prelude::model::Colour;
/// Set your discord name's color
#[poise::command(prefix_command, guild_only, category = "color")] #[poise::command(prefix_command, guild_only, category = "color")]
pub async fn set_color( pub async fn set_color(
ctx: Context<'_>, ctx: Context<'_>,
@ -45,7 +46,7 @@ pub async fn set_color(
let guild_id = ctx.guild_id().unwrap(); let guild_id = ctx.guild_id().unwrap();
let member = ctx.author_member().await.unwrap(); let member = ctx.author_member().await.unwrap();
let roles = member.roles(&ctx.cache()).unwrap(); let roles = member.roles(ctx.cache()).unwrap();
let color_role = roles.iter().find(|role| role.name.contains("COwOlor")); let color_role = roles.iter().find(|role| role.name.contains("COwOlor"));
@ -77,11 +78,12 @@ pub async fn set_color(
Ok(()) Ok(())
} }
/// Remove your color (good for you!)
#[poise::command(prefix_command, guild_only, category = "Color")] #[poise::command(prefix_command, guild_only, category = "Color")]
pub async fn remove_color(ctx: Context<'_>) -> Result<(), Error> { pub async fn remove_color(ctx: Context<'_>) -> Result<(), Error> {
let guild_id = ctx.guild_id().unwrap(); let guild_id = ctx.guild_id().unwrap();
let member = ctx.author_member().await.unwrap(); let member = ctx.author_member().await.unwrap();
let roles = member.roles(&ctx.cache()).unwrap(); let roles = member.roles(ctx.cache()).unwrap();
let color_role = roles.iter().find(|role| role.name.contains("COwOlor")); let color_role = roles.iter().find(|role| role.name.contains("COwOlor"));
if let Some(role) = color_role { if let Some(role) = color_role {

View File

@ -9,7 +9,7 @@ use poise::serenity_prelude::model::id::UserId;
use poise::serenity_prelude::utils::MessageBuilder; use poise::serenity_prelude::utils::MessageBuilder;
use poise::serenity_prelude::{EmojiIdentifier, Mentionable}; use poise::serenity_prelude::{EmojiIdentifier, Mentionable};
use rand::seq::IteratorRandom; use rand::seq::IteratorRandom;
use rand::{rng, Rng}; use rand::rng;
use std::time::Duration; use std::time::Duration;
fn cleanup_race(db: &Database) -> Result<(), Error> { fn cleanup_race(db: &Database) -> Result<(), Error> {
@ -39,6 +39,7 @@ async fn add_bet(ctx: Context<'_>, bet: Bet, racers: &[Racer]) -> Result<(), Rac
Ok(()) Ok(())
} }
/// Setup an emoji race to bet on
#[poise::command(prefix_command, guild_only, category = "Race")] #[poise::command(prefix_command, guild_only, category = "Race")]
pub async fn race(ctx: Context<'_>) -> Result<(), Error> { pub async fn race(ctx: Context<'_>) -> Result<(), Error> {
cleanup_race(&ctx.data().db)?; cleanup_race(&ctx.data().db)?;
@ -64,6 +65,7 @@ pub async fn race(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Start the race!
#[poise::command(prefix_command, guild_only, category = "Race")] #[poise::command(prefix_command, guild_only, category = "Race")]
pub async fn start_race(ctx: Context<'_>) -> Result<(), Error> { pub async fn start_race(ctx: Context<'_>) -> Result<(), Error> {
let mut racers = Racer::get_racers(&ctx.data().db)?; let mut racers = Racer::get_racers(&ctx.data().db)?;
@ -154,6 +156,7 @@ pub async fn start_race(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Bet on a race of emojis, a sound idea
#[poise::command(prefix_command, guild_only, category = "Race")] #[poise::command(prefix_command, guild_only, category = "Race")]
pub async fn bet( pub async fn bet(
ctx: Context<'_>, ctx: Context<'_>,

View File

@ -7,6 +7,7 @@ use poise::serenity_prelude::model::id::UserId;
use poise::serenity_prelude::prelude::Mentionable; use poise::serenity_prelude::prelude::Mentionable;
use rand::{rng, Rng}; use rand::{rng, Rng};
/// Get your balance or someone else's
#[poise::command(prefix_command, category = "Fren Coin", aliases("audit"))] #[poise::command(prefix_command, category = "Fren Coin", aliases("audit"))]
pub async fn balance( pub async fn balance(
ctx: Context<'_>, ctx: Context<'_>,
@ -26,6 +27,7 @@ pub async fn balance(
Ok(()) Ok(())
} }
/// Gift someone the gift that keeps on giving, Fren Coins!
#[poise::command(prefix_command, category = "Fren Coin", aliases("audit"))] #[poise::command(prefix_command, category = "Fren Coin", aliases("audit"))]
pub async fn gift( pub async fn gift(
ctx: Context<'_>, ctx: Context<'_>,

View File

@ -9,7 +9,6 @@ use chrono::Utc;
use emojis::Group; use emojis::Group;
use log::debug; use log::debug;
use poise::serenity_prelude::all::{CreateAttachment, CreateMessage}; use poise::serenity_prelude::all::{CreateAttachment, CreateMessage};
use poise::serenity_prelude::builder::EditMessage;
use poise::serenity_prelude::constants::MESSAGE_CODE_LIMIT; use poise::serenity_prelude::constants::MESSAGE_CODE_LIMIT;
use poise::serenity_prelude::model::channel::Message; use poise::serenity_prelude::model::channel::Message;
use poise::serenity_prelude::utils::MessageBuilder; use poise::serenity_prelude::utils::MessageBuilder;
@ -21,7 +20,6 @@ use rand::prelude::IndexedRandom;
use rand::rng; use rand::rng;
use reqwest::Client; use reqwest::Client;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use songbird::id::UserId;
use std::collections::HashMap; use std::collections::HashMap;
use std::time::Duration; use std::time::Duration;
@ -32,6 +30,7 @@ struct DadJoke {
pub status: i32, pub status: i32,
} }
/// Ask your dad
#[poise::command(prefix_command, category = "Joke")] #[poise::command(prefix_command, category = "Joke")]
pub async fn dad_joke(ctx: Context<'_>) -> Result<(), Error> { pub async fn dad_joke(ctx: Context<'_>) -> Result<(), Error> {
let client = Client::new(); let client = Client::new();
@ -136,6 +135,7 @@ pub async fn random(
Ok(()) Ok(())
} }
/// Add a random command to the bot
#[poise::command(prefix_command, category = "Joke")] #[poise::command(prefix_command, category = "Joke")]
pub async fn add_random( pub async fn add_random(
ctx: Context<'_>, ctx: Context<'_>,
@ -143,7 +143,7 @@ pub async fn add_random(
#[description = "Random message"] random_response: String, #[description = "Random message"] random_response: String,
) -> Result<(), Error> { ) -> Result<(), Error> {
if let Err(err) = if let Err(err) =
render_random(&ctx.author().display_name(), ctx.data(), &random_response).await render_random(ctx.author().display_name(), ctx.data(), &random_response).await
{ {
ctx.reply(format!("Template failed test render, try again: {}", err)) ctx.reply(format!("Template failed test render, try again: {}", err))
.await?; .await?;
@ -155,6 +155,7 @@ pub async fn add_random(
Ok(()) Ok(())
} }
/// List all of the random sets
#[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<'_>,
@ -195,6 +196,7 @@ pub async fn list_random(
Ok(()) Ok(())
} }
/// 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(
ctx: Context<'_>, ctx: Context<'_>,
@ -214,6 +216,7 @@ pub async fn roll(
Ok(()) Ok(())
} }
/// Roll a totally not loaded, real actual die! (No joke)
#[poise::command(prefix_command, category = "Joke")] #[poise::command(prefix_command, category = "Joke")]
pub async fn real_roll(ctx: Context<'_>) -> Result<(), Error> { pub async fn real_roll(ctx: Context<'_>) -> Result<(), Error> {
let addr = ctx.data().cfg.raas_server.clone(); let addr = ctx.data().cfg.raas_server.clone();
@ -271,6 +274,7 @@ pub async fn real_roll(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Play critically the acclaimed Bad Apple music video as god intended
#[poise::command(prefix_command, category = "Joke")] #[poise::command(prefix_command, category = "Joke")]
pub async fn bad_apple(ctx: Context<'_>) -> Result<(), Error> { pub async fn bad_apple(ctx: Context<'_>) -> Result<(), Error> {
let mut frames = BAD_APPLE.split('|'); let mut frames = BAD_APPLE.split('|');
@ -311,6 +315,7 @@ pub async fn bad_apple(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Insult or compliment your "friends"!
#[poise::command(prefix_command, category = "Joke", aliases("compliment"))] #[poise::command(prefix_command, category = "Joke", aliases("compliment"))]
pub async fn insult( pub async fn insult(
ctx: Context<'_>, ctx: Context<'_>,
@ -355,6 +360,7 @@ fn get_unicode_emojis() -> Vec<String> {
emoji.iter().map(|e| e.to_string()).collect() emoji.iter().map(|e| e.to_string()).collect()
} }
/// Stare into the digital black mirror and see what it reflects
#[poise::command(prefix_command, category = "Joke", aliases("🎱"))] #[poise::command(prefix_command, category = "Joke", aliases("🎱"))]
pub async fn emoji_8ball( pub async fn emoji_8ball(
ctx: Context<'_>, ctx: Context<'_>,

View File

@ -7,6 +7,7 @@ use poise::serenity_prelude::utils::MessageBuilder;
use rand::prelude::IndexedRandom; use rand::prelude::IndexedRandom;
use rand::{rng, Rng}; use rand::{rng, Rng};
/// Adopt a new little buddy!
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")] #[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
pub async fn adopt( pub async fn adopt(
ctx: Context<'_>, ctx: Context<'_>,
@ -34,6 +35,7 @@ pub async fn adopt(
Ok(()) Ok(())
} }
/// Checkup on your little buddy!
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")] #[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
pub async fn checkup(ctx: Context<'_>) -> Result<(), Error> { pub async fn checkup(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?; let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -82,6 +84,7 @@ pub async fn checkup(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Feed your little buddy something totally normal!
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")] #[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
pub async fn feed( pub async fn feed(
ctx: Context<'_>, ctx: Context<'_>,
@ -91,7 +94,7 @@ pub async fn feed(
if let Some(mut lil_fren) = lil_fren { if let Some(mut lil_fren) = lil_fren {
if lil_fren.is_alive() == AliveState::Alive { if lil_fren.is_alive() == AliveState::Alive {
let feed: f32 = rng().gen_range(-0.1..=1.0); let feed: f32 = rng().random_range(-0.1..=1.0);
lil_fren.hunger = (lil_fren.hunger + feed).clamp(-1.0, 1.0); lil_fren.hunger = (lil_fren.hunger + feed).clamp(-1.0, 1.0);
let guild = ctx.guild_id().unwrap(); let guild = ctx.guild_id().unwrap();
@ -117,6 +120,7 @@ pub async fn feed(
Ok(()) Ok(())
} }
/// Give your little buddy the juice of life.
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")] #[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
pub async fn give_water(ctx: Context<'_>) -> Result<(), Error> { pub async fn give_water(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?; let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -149,6 +153,7 @@ pub async fn give_water(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Play with little buddy before he shreds up the remote again
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")] #[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
pub async fn play(ctx: Context<'_>) -> Result<(), Error> { pub async fn play(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?; let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -175,6 +180,7 @@ pub async fn play(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Give your little buddy medicine
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")] #[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
pub async fn give_medicine(ctx: Context<'_>) -> Result<(), Error> { pub async fn give_medicine(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?; let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;

View File

@ -55,7 +55,7 @@ async fn event_handler(
{ {
info!("Starting lil buddy task..."); info!("Starting lil buddy task...");
let data = data.clone(); let data = data.clone();
tokio::spawn(async move { lil_fren_task(data) }); tokio::spawn(async move { lil_fren_task(data).await });
} }
} }
serenity::FullEvent::Message { new_message } => { serenity::FullEvent::Message { new_message } => {
@ -155,6 +155,18 @@ async fn pre_command(ctx: Context<'_>) {
); );
} }
#[poise::command(prefix_command)]
pub async fn help(ctx: Context<'_>, command: Option<String>) -> Result<(), Error> {
let configuration = poise::builtins::HelpConfiguration {
extra_text_at_bottom: "Made with 💖 by Joey",
include_description: false,
..Default::default()
};
poise::builtins::help(ctx, command.as_deref(), configuration).await?;
Ok(())
}
pub async fn run_bot(global_data: GlobalData) { pub async fn run_bot(global_data: GlobalData) {
let framework_options: FrameworkOptions<Arc<GlobalData>, Error> = poise::FrameworkOptions { let framework_options: FrameworkOptions<Arc<GlobalData>, Error> = poise::FrameworkOptions {
prefix_options: poise::PrefixFrameworkOptions { prefix_options: poise::PrefixFrameworkOptions {
@ -165,6 +177,7 @@ pub async fn run_bot(global_data: GlobalData) {
..Default::default() ..Default::default()
}, },
commands: vec![ commands: vec![
help(),
admin::dump_db(), admin::dump_db(),
admin::load_db(), admin::load_db(),
admin::add_key(), admin::add_key(),

View File

@ -57,6 +57,7 @@ pub async fn create_motivation_image(motivation: Motivation) -> Result<Vec<u8>,
Ok(wand.write_image_blob("jpg")?) Ok(wand.write_image_blob("jpg")?)
} }
/// Become motivated
#[poise::command(prefix_command, category = "Motivation")] #[poise::command(prefix_command, category = "Motivation")]
pub async fn motivation( pub async fn motivation(
ctx: Context<'_>, ctx: Context<'_>,
@ -87,6 +88,7 @@ pub async fn motivation(
Ok(()) Ok(())
} }
/// Add an album to the pool of images to be used for motivations
#[poise::command(prefix_command, category = "Motivation")] #[poise::command(prefix_command, category = "Motivation")]
pub async fn motivation_add_album( pub async fn motivation_add_album(
ctx: Context<'_>, ctx: Context<'_>,

View File

@ -9,7 +9,6 @@ use crate::user::{User, UserError};
use poise::serenity_prelude::all::{ use poise::serenity_prelude::all::{
parse_user_mention, CreateAttachment, CreateMessage, EditRole, GuildId, Member, parse_user_mention, CreateAttachment, CreateMessage, EditRole, GuildId, Member,
}; };
use poise::serenity_prelude::model::channel::Message;
use poise::serenity_prelude::model::Colour; use poise::serenity_prelude::model::Colour;
use poise::serenity_prelude::prelude::Mentionable; use poise::serenity_prelude::prelude::Mentionable;
use poise::serenity_prelude::utils::MessageBuilder; use poise::serenity_prelude::utils::MessageBuilder;
@ -20,6 +19,7 @@ use std::hash::Hasher;
use std::sync::Arc; use std::sync::Arc;
use tokio::io::AsyncWriteExt; use tokio::io::AsyncWriteExt;
/// Shop at a real virtual shop in the Metaverse!
#[poise::command(prefix_command, category = "Shop")] #[poise::command(prefix_command, category = "Shop")]
pub async fn shop(ctx: Context<'_>) -> Result<(), Error> { pub async fn shop(ctx: Context<'_>) -> Result<(), Error> {
let bot_user = User::get_user(&ctx.data().db, ctx.cache().current_user().id)?; let bot_user = User::get_user(&ctx.data().db, ctx.cache().current_user().id)?;
@ -40,6 +40,7 @@ pub async fn shop(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Look at your inventory, this button is forced bound to any key other than "B"
#[poise::command(prefix_command, category = "Shop")] #[poise::command(prefix_command, category = "Shop")]
pub async fn inventory(ctx: Context<'_>) -> Result<(), Error> { pub async fn inventory(ctx: Context<'_>) -> Result<(), Error> {
let mut inv_msg = MessageBuilder::new(); let mut inv_msg = MessageBuilder::new();
@ -59,6 +60,7 @@ pub async fn inventory(ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Buy an item from the shop with your hard earned cash
#[poise::command(prefix_command, category = "Shop")] #[poise::command(prefix_command, category = "Shop")]
pub async fn buy( pub async fn buy(
ctx: Context<'_>, ctx: Context<'_>,
@ -84,7 +86,7 @@ pub async fn buy(
ctx.reply(resp).await?; ctx.reply(resp).await?;
} else { } else {
return Err(err.into()); return Err(err);
} }
} else { } else {
ctx.reply(format!("Congrats, you now own a '{}'", item)) ctx.reply(format!("Congrats, you now own a '{}'", item))
@ -150,6 +152,7 @@ async fn blockable_item(
} }
} }
/// Use an item and hope it doesn't use you
#[poise::command(prefix_command, category = "Shop", guild_only, aliases("use"))] #[poise::command(prefix_command, category = "Shop", guild_only, aliases("use"))]
pub async fn use_item( pub async fn use_item(
ctx: Context<'_>, ctx: Context<'_>,
@ -323,6 +326,7 @@ pub async fn use_item(
Ok(()) Ok(())
} }
/// Sell an item for profit
#[poise::command(prefix_command, category = "Shop", aliases("sell"))] #[poise::command(prefix_command, category = "Shop", aliases("sell"))]
pub async fn sell_item( pub async fn sell_item(
ctx: Context<'_>, ctx: Context<'_>,
@ -359,17 +363,19 @@ pub async fn sell_item(
_ => {} _ => {}
} }
} else { } else {
return Err(err.into()); return Err(err);
} }
} }
} }
Ok(()) Ok(())
} }
/// Get help on what an item does
#[poise::command(prefix_command, category = "Shop")] #[poise::command(prefix_command, category = "Shop")]
pub async fn item_help( pub async fn item_help(
ctx: Context<'_>, ctx: Context<'_>,
#[description = "I"] #[description = "Item to get help on"]
#[rest] #[rest]
item: ItemType, item: ItemType,
) -> Result<(), Error> { ) -> Result<(), Error> {

View File

@ -1,10 +1,8 @@
use crate::discord::Context; use crate::discord::Context;
use crate::error::Error; use crate::error::Error;
use magick_rust::bindings::wchar_t;
use poise::async_trait; use poise::async_trait;
use poise::serenity_prelude::all::CreateAttachment; use poise::serenity_prelude::all::CreateAttachment;
use poise::serenity_prelude::builder::CreateMessage; use poise::serenity_prelude::builder::CreateMessage;
use poise::serenity_prelude::model::channel::Message;
use poise::serenity_prelude::model::id::UserId; use poise::serenity_prelude::model::id::UserId;
use poise::serenity_prelude::model::prelude::GuildId; use poise::serenity_prelude::model::prelude::GuildId;
use poise::serenity_prelude::utils::MessageBuilder; use poise::serenity_prelude::utils::MessageBuilder;
@ -132,7 +130,7 @@ pub async fn speak(
voice: &str, voice: &str,
phrase: &str, phrase: &str,
) -> Result<(), VoiceError> { ) -> Result<(), VoiceError> {
let _ = ctx.data().speak_lock.lock(); let _ = ctx.data().speak_lock.lock().await;
let voice_path = match find_voice(&ctx.data().cfg.voice_path, voice).await.unwrap() { let voice_path = match find_voice(&ctx.data().cfg.voice_path, voice).await.unwrap() {
None => return Err(VoiceError::VoiceNotFound(voice.to_string())), None => return Err(VoiceError::VoiceNotFound(voice.to_string())),
@ -231,6 +229,7 @@ pub async fn speak(
Ok(()) Ok(())
} }
/// Speak in the language of the gods
#[poise::command(prefix_command, category = "Voice", guild_only)] #[poise::command(prefix_command, category = "Voice", guild_only)]
pub async fn say( pub async fn say(
ctx: Context<'_>, ctx: Context<'_>,
@ -296,6 +295,7 @@ pub async fn list_words(
Ok(()) Ok(())
} }
/// List all voices the bot has
#[poise::command(prefix_command, category = "Voice", guild_only)] #[poise::command(prefix_command, category = "Voice", guild_only)]
pub async fn list_voices(ctx: Context<'_>) -> Result<(), Error> { pub async fn list_voices(ctx: Context<'_>) -> Result<(), Error> {
let mut voice_message = MessageBuilder::new(); let mut voice_message = MessageBuilder::new();

View File

@ -2,7 +2,7 @@ use crate::discord::voices::VoiceError;
use crate::user; use crate::user;
use magick_rust::MagickError; use magick_rust::MagickError;
use serde::ser::StdError; use serde::ser::StdError;
use std::fmt::{write, Display, Formatter}; use std::fmt::{Display, Formatter};
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]

View File

@ -9,9 +9,8 @@ mod user;
use crate::config::{Args, BotConfig, GlobalData}; use crate::config::{Args, BotConfig, GlobalData};
use crate::discord::run_bot; use crate::discord::run_bot;
use log::{error, info, LevelFilter}; use log::{error, info};
use magick_rust::magick_wand_genesis; use magick_rust::magick_wand_genesis;
use songbird::SerenityInit;
use std::sync::Once; use std::sync::Once;
use structopt::StructOpt; use structopt::StructOpt;

View File

@ -62,6 +62,8 @@ impl Apikey {
general_purpose::STANDARD_NO_PAD.encode(hash) general_purpose::STANDARD_NO_PAD.encode(hash)
} }
#[allow(dead_code)]
pub fn find_key_from_secret(db: &Database, secret: &str) -> Result<Option<Self>, Error> { pub fn find_key_from_secret(db: &Database, secret: &str) -> Result<Option<Self>, Error> {
let hash = Self::hash_secret(secret); let hash = Self::hash_secret(secret);

View File

@ -3,7 +3,6 @@ use j_db::database::Database;
use j_db::model::JdbModel; use j_db::model::JdbModel;
use rand::prelude::IndexedRandom; use rand::prelude::IndexedRandom;
use rand::rng; use rand::rng;
use rand::seq::SliceRandom;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use tera::{Context, Tera}; use tera::{Context, Tera};

View File

@ -194,7 +194,7 @@ pub enum LilFrenState {
impl Distribution<LilFrenState> for StandardUniform { impl Distribution<LilFrenState> for StandardUniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> LilFrenState { fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> LilFrenState {
match rng.gen_range(0..10) { match rng.random_range(0..10) {
0 => LilFrenState::Standing, 0 => LilFrenState::Standing,
1 => LilFrenState::TaxFraud, 1 => LilFrenState::TaxFraud,
2 => LilFrenState::Sick, 2 => LilFrenState::Sick,
@ -253,8 +253,8 @@ impl LilFren {
thirst: 1.0, thirst: 1.0,
entertainment: 1.0, entertainment: 1.0,
state: LilFrenState::Standing, state: LilFrenState::Standing,
smarts: rng().gen_range(0.0..0.5), smarts: rng().random_range(0.0..0.5),
metabolism: rng().gen_range(0.2..0.75), metabolism: rng().random_range(0.2..0.75),
} }
} }

View File

@ -1,7 +1,6 @@
use crate::error::Error; use crate::error::Error;
use crate::user::UserError; use crate::user::UserError;
use j_db::database::Database; use j_db::database::Database;
use j_db::error::JDbError;
use j_db::model::JdbModel; use j_db::model::JdbModel;
use poise::serenity_prelude::{Emoji, EmojiId, MessageBuilder, UserId}; use poise::serenity_prelude::{Emoji, EmojiId, MessageBuilder, UserId};
use rand::{rng, Rng}; use rand::{rng, Rng};
@ -134,7 +133,7 @@ impl Racer {
} }
pub fn update_pos(&mut self) { pub fn update_pos(&mut self) {
let speed_boost = rng().gen_range(-0.75..0.75); let speed_boost = rng().random_range(-0.75..0.75);
self.pos += self.speed + (self.speed * speed_boost); self.pos += self.speed + (self.speed * speed_boost);
self.pos = self.pos.clamp(0.0, RACE_SIZE as f32); self.pos = self.pos.clamp(0.0, RACE_SIZE as f32);

View File

@ -3,7 +3,7 @@ use j_db::database::Database;
use j_db::model::JdbModel; use j_db::model::JdbModel;
use rand::distr::weighted::WeightedIndex; use rand::distr::weighted::WeightedIndex;
use rand::prelude::Distribution; use rand::prelude::Distribution;
use rand::thread_rng; use rand::rng;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::collections::HashSet; use std::collections::HashSet;
@ -115,7 +115,7 @@ impl RandomConfig {
let dist = WeightedIndex::new(responses.iter().map(|r| r.score)).unwrap(); let dist = WeightedIndex::new(responses.iter().map(|r| r.score)).unwrap();
let mut resp = responses[dist.sample(&mut thread_rng())].clone(); let mut resp = responses[dist.sample(&mut rng())].clone();
resp.score = resp.score.saturating_sub(1); resp.score = resp.score.saturating_sub(1);