Add consequences for being dead or canceled

This commit is contained in:
Joey Hines 2025-08-09 17:46:19 -06:00
parent 5b4c6b50ae
commit c6786a77f7
Signed by: joeyahines
GPG Key ID: 38BA6F25C94C9382
11 changed files with 154 additions and 49 deletions

2
Cargo.lock generated
View File

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

View File

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

View File

@ -1,8 +1,9 @@
use crate::discord::Context;
use crate::discord::is_not_cancelled;
use crate::error::Error;
/// Are you sure?
#[poise::command(prefix_command, category = "Celery Man")]
#[poise::command(prefix_command, category = "Celery Man", check = "is_not_cancelled")]
pub async fn nudetayne(ctx: Context<'_>) -> Result<(), Error> {
if ctx.invoked_command_name() == "nudetayne" {
ctx.reply("Not computing, please repeat.").await?;

View File

@ -1,4 +1,5 @@
use crate::discord::Context;
use crate::discord::is_not_cancelled;
use crate::error::Error;
use crate::models::race;
use crate::models::race::{Bet, NUMBER_OF_RACERS, RACE_SIZE, RaceError, Racer};
@ -40,7 +41,12 @@ async fn add_bet(ctx: Context<'_>, bet: Bet, racers: &[Racer]) -> Result<(), Rac
}
/// Setup an emoji race to bet on
#[poise::command(prefix_command, guild_only, category = "Race")]
#[poise::command(
prefix_command,
guild_only,
category = "Race",
check = "is_not_cancelled"
)]
pub async fn race(ctx: Context<'_>) -> Result<(), Error> {
cleanup_race(&ctx.data().db)?;
@ -154,7 +160,12 @@ pub async fn start_race(ctx: Context<'_>) -> Result<(), Error> {
}
/// 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",
check = "is_not_cancelled"
)]
pub async fn bet(
ctx: Context<'_>,
#[description = "Racer to bet on"] racer: EmojiIdentifier,

View File

@ -1,4 +1,4 @@
use crate::discord::Context;
use crate::discord::{Context, is_alive};
use crate::error::Error;
use crate::user::{User, UserError};
use j_db::database::Database;
@ -28,7 +28,12 @@ pub async fn balance(
}
/// 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"),
check = "is_alive"
)]
pub async fn gift(
ctx: Context<'_>,
#[description = "User to gift coin to"] target: poise::serenity_prelude::User,

View File

@ -1,4 +1,4 @@
use crate::discord::Context;
use crate::discord::{Context, is_not_cancelled};
use crate::error::Error;
use crate::models::lil_fren;
use crate::models::lil_fren::{AliveState, LilFren, LilFrenState};
@ -8,17 +8,22 @@ use rand::prelude::IndexedRandom;
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",
check = "is_not_cancelled"
)]
pub async fn adopt(
ctx: Context<'_>,
#[description = "Lil buddy to adopt"] lil_buddy: EmojiIdentifier,
) -> Result<(), Error> {
if let Some(lil_fren) = LilFren::get_lil_fren(&ctx.data().db)? {
if lil_fren.is_alive() == AliveState::Alive {
ctx.reply("Your buddy is still alive, please take care of him :)")
.await?;
return Ok(());
}
if let Some(lil_fren) = LilFren::get_lil_fren(&ctx.data().db)?
&& lil_fren.is_alive() == AliveState::Alive
{
ctx.reply("Your buddy is still alive, please take care of him :)")
.await?;
return Ok(());
}
LilFren::create_new_lil_fren(&ctx.data().db, lil_buddy.id)?;
@ -36,7 +41,12 @@ pub async fn adopt(
}
/// Checkup on your little buddy!
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
#[poise::command(
prefix_command,
guild_only,
category = "Lil Buddy",
check = "is_not_cancelled"
)]
pub async fn checkup(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -89,7 +99,12 @@ pub async fn checkup(ctx: Context<'_>) -> Result<(), Error> {
}
/// 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",
check = "is_not_cancelled"
)]
pub async fn feed(
ctx: Context<'_>,
#[description = "Item to feed lil buddy"] food: String,
@ -125,7 +140,12 @@ pub async fn feed(
}
/// 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",
check = "is_not_cancelled"
)]
pub async fn give_water(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -158,7 +178,12 @@ pub async fn give_water(ctx: Context<'_>) -> Result<(), Error> {
}
/// 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",
check = "is_not_cancelled"
)]
pub async fn play(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;
@ -185,7 +210,12 @@ pub async fn play(ctx: Context<'_>) -> Result<(), Error> {
}
/// Give your little buddy medicine
#[poise::command(prefix_command, guild_only, category = "Lil Buddy")]
#[poise::command(
prefix_command,
guild_only,
category = "Lil Buddy",
check = "is_not_cancelled"
)]
pub async fn give_medicine(ctx: Context<'_>) -> Result<(), Error> {
let lil_fren = LilFren::get_lil_fren(&ctx.data().db)?;

View File

@ -1,3 +1,4 @@
use rand::seq::IteratorRandom;
mod admin;
mod album;
mod birthday;
@ -22,11 +23,10 @@ use crate::error::Error;
use crate::event_listener::{Listener, TriggerEvent, TriggerType};
use crate::models::social_credit::SocialCreditPhrase;
use crate::models::task::Task;
use crate::user::User;
use crate::user::{User, UserRole};
use log::{debug, error, info};
use poise::serenity_prelude::{GuildId, Http, Message, MessageBuilder, ReactionType, RoleId};
use poise::{FrameworkOptions, find_command, serenity_prelude as serenity};
use rand::prelude::IteratorRandom;
use rand::{Rng, rng};
use songbird::SerenityInit;
use std::sync::Arc;
@ -83,11 +83,11 @@ async fn handle_message(
|| new_message.content.eq_ignore_ascii_case("mhmm")
{
let mut bot_state = data.bot_state.lock().await;
if let Some(u) = bot_state.accepted_nsfw {
if new_message.author.id == u {
new_message.reply(&ctx.http, "||https://cdn.discordapp.com/attachments/614891432079130625/1041545254362423368/unknown.png||").await.unwrap();
bot_state.accepted_nsfw = None;
}
if let Some(u) = bot_state.accepted_nsfw
&& new_message.author.id == u
{
new_message.reply(&ctx.http, "||https://cdn.discordapp.com/attachments/614891432079130625/1041545254362423368/unknown.png||").await.unwrap();
bot_state.accepted_nsfw = None;
}
}
@ -445,3 +445,46 @@ pub async fn get_role(
.map(|(role_id, _)| role_id)
.copied())
}
async fn check_has_user_role(ctx: Context<'_>, user_role: UserRole) -> Result<bool, Error> {
let role = get_role(ctx.http(), ctx.data().cfg.guild_id, &user_role.to_string()).await?;
if let Some(role) = role {
Ok(ctx
.author()
.has_role(&ctx, ctx.data().cfg.guild_id, role)
.await?)
} else {
Ok(false)
}
}
pub async fn is_alive(ctx: Context<'_>) -> Result<bool, Error> {
let is_ghoul = check_has_user_role(ctx, UserRole::Ghoul).await?;
if check_has_user_role(ctx, UserRole::Dead).await? && !is_ghoul {
ctx.reply("Sorry there ghosty looking fella, you appear to be unalived. I only give service to those who are alive.").await?;
Ok(false)
} else {
if is_ghoul && rng().random_bool(0.50) {
let ghoul_responses = [
"Oh god, what's wrong with your face? Looks like someone took it through a blender and lit it on fire",
"You kiss your mother with that mouth?",
"Oh! I love your cosplay, chic fallout is so IN right now frfr",
"Have you tried moisturizer? Seems like it couldn't umm... hurt...",
"OH damn, can I get a warning next time before you jump scare me with that",
];
let resp = ghoul_responses.iter().choose(&mut rng()).unwrap();
ctx.reply(*resp).await?;
}
Ok(true)
}
}
pub async fn is_not_cancelled(ctx: Context<'_>) -> Result<bool, Error> {
if check_has_user_role(ctx, UserRole::Cancelled).await? {
ctx.reply("You're CANCELLED?? Yikes! I can't interact with you, I don't want to become a conservative influencer.").await?;
Ok(false)
} else {
Ok(true)
}
}

View File

@ -1,5 +1,6 @@
use crate::config::GlobalData;
use crate::discord::Context;
use crate::discord::is_alive;
use crate::error::Error;
use crate::event_listener::{Action, Expiration, Listener, TriggerType};
use crate::image_manipulation::create_motivation_image;
@ -15,7 +16,7 @@ use std::sync::Arc;
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", check = "is_alive")]
pub async fn shop(ctx: Context<'_>) -> Result<(), Error> {
let bot_user = User::get_user(&ctx.data().db, ctx.cache().current_user().id)?;
@ -56,7 +57,7 @@ pub async fn inventory(ctx: Context<'_>) -> Result<(), Error> {
}
/// Buy an item from the shop with your hard-earned cash
#[poise::command(prefix_command, category = "Shop")]
#[poise::command(prefix_command, category = "Shop", check = "is_alive")]
pub async fn buy(
ctx: Context<'_>,
#[description = "Item to buy"]
@ -127,7 +128,7 @@ pub async fn license_to_be_horny(ctx: Context<'_>) -> Result<(), Error> {
}
/// Use a kill gun to kill someone
#[poise::command(prefix_command, category = "Shop", guild_only)]
#[poise::command(prefix_command, category = "Shop", guild_only, check = "is_alive")]
pub async fn kill_gun(
ctx: Context<'_>,
#[description = "target to kill"] target: poise::serenity_prelude::User,
@ -137,7 +138,7 @@ pub async fn kill_gun(
}
/// Use a cancel ray to cancel someone
#[poise::command(prefix_command, category = "Shop", guild_only)]
#[poise::command(prefix_command, category = "Shop", guild_only, check = "is_alive")]
pub async fn cancel_ray(
ctx: Context<'_>,
#[description = "target to cancel"] target: poise::serenity_prelude::User,
@ -154,14 +155,14 @@ pub async fn helmet(ctx: Context<'_>) -> Result<(), Error> {
}
/// Deploy a tactical emp
#[poise::command(prefix_command, category = "Shop", guild_only)]
#[poise::command(prefix_command, category = "Shop", guild_only, check = "is_alive")]
pub async fn emp(ctx: Context<'_>) -> Result<(), Error> {
use_item(ctx, ItemType::EMP, Target::Everyone).await?;
Ok(())
}
/// Deploy a land mine
#[poise::command(prefix_command, category = "Shop")]
#[poise::command(prefix_command, category = "Shop", check = "is_alive")]
pub async fn land_mine(
ctx: Context<'_>,
#[description = "Channel to land mine"] channel: Channel,
@ -195,7 +196,7 @@ pub async fn land_mine(
}
/// Use your Phrase Canceler
#[poise::command(prefix_command, category = "Shop")]
#[poise::command(prefix_command, category = "Shop", check = "is_alive")]
pub async fn phrase_canceler(
ctx: Context<'_>,
#[description = "Phrase to cancel"]
@ -231,7 +232,7 @@ pub async fn phrase_canceler(
}
/// Drop a nuke on this discord
#[poise::command(prefix_command, category = "Shop", guild_only)]
#[poise::command(prefix_command, category = "Shop", guild_only, check = "is_alive")]
pub async fn nuke(ctx: Context<'_>) -> Result<(), Error> {
if use_item(ctx, ItemType::Nuke, Target::Everyone).await? {
Listener::add_listener(&ctx.data().db, {
@ -283,10 +284,10 @@ pub async fn use_item(ctx: Context<'_>, item: ItemType, target: Target) -> Resul
User::use_item_on_user(ctx, *target_user, ctx.guild_id().unwrap(), item, &item_data)
.await?;
if let Some(outcome) = outcome {
if ndx + 1 == target_users.len() {
ctx.reply(outcome).await?;
}
if let Some(outcome) = outcome
&& ndx + 1 == target_users.len()
{
ctx.reply(outcome).await?;
}
}
@ -294,7 +295,7 @@ pub async fn use_item(ctx: Context<'_>, item: ItemType, target: Target) -> Resul
}
/// Sell an item for profit
#[poise::command(prefix_command, category = "Shop", aliases("sell"))]
#[poise::command(prefix_command, category = "Shop", aliases("sell"), check = "is_alive")]
pub async fn sell_item(
ctx: Context<'_>,
#[description = "Item to sell"]

View File

@ -1,10 +1,15 @@
use crate::discord::Context;
use crate::discord::{Context, is_alive};
use crate::error::Error;
use crate::models::gogurt_reserves::GogurtReserves;
use poise::serenity_prelude::{Mentionable, MessageBuilder};
/// Contribute to the Gogurt Reserve
#[poise::command(prefix_command, category = "Stonks", aliases("cgr"))]
#[poise::command(
prefix_command,
category = "Stonks",
aliases("cgr"),
check = "is_alive"
)]
pub async fn contribute_to_gogurt_reserve(
ctx: Context<'_>,
#[description = "amount of FCs to spend"] contribute_amount: u64,
@ -21,7 +26,12 @@ pub async fn contribute_to_gogurt_reserve(
}
/// Sell gogurt from the Gogurt Reserve
#[poise::command(prefix_command, category = "Stonks", aliases("sgr"))]
#[poise::command(
prefix_command,
category = "Stonks",
aliases("sgr"),
check = "is_alive"
)]
pub async fn sell_from_gogurt_reserve(
ctx: Context<'_>,
#[description = "number of pounds of gogurt to sell"] gogurt_to_sell: f64,

View File

@ -9,16 +9,16 @@ use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use thiserror::Error;
const OPENING_HOUR: u32 = 8;
const OPENING_HOUR: u32 = 7;
const UPDATE_HOUR: u32 = 12;
const CLOSING_HOUR: u32 = 16;
const CLOSING_HOUR: u32 = 18;
#[derive(Debug, Error)]
pub enum GogurtError {
#[error("Wow, you really don't have enough gogurt. What are you 12?? Just got buy some poor.")]
NotEnoughGogurt,
#[error(
"Sorry, gogurt can only be bought and sold between the hours of 8AM to 4PM Naperville Time."
"Sorry, gogurt can only be bought and sold between the hours of 7AM to 6PM Naperville Time."
)]
OutsideOfGogurtTradingHours,
}

View File

@ -209,12 +209,16 @@ impl Task {
let midday_update_time = GogurtReserves::market_update_time();
let next_check = if chicago_time.time() >= midday_update_time {
chicago_time.with_time(market_open_time)
chicago_time
.with_time(market_open_time)
.unwrap()
.checked_add_days(Days::new(1))
.unwrap()
} else {
chicago_time.with_time(midday_update_time)
chicago_time.with_time(midday_update_time).unwrap()
};
let next_check = next_check.unwrap().with_timezone(&Utc);
let next_check = next_check.with_timezone(&Utc);
Task::add_task(&data.db, TaskType::UpdateGogurtRate, next_check)?;
}