diff --git a/src/discord/admin.rs b/src/discord/admin.rs index d7af309..74bbc0a 100644 --- a/src/discord/admin.rs +++ b/src/discord/admin.rs @@ -16,9 +16,10 @@ use crate::user::User; use j_db::model::JdbModel; use json::JsonValue; use log::info; +use poise::CreateReply; use poise::serenity_prelude::{ Attachment, CreateAttachment, CreateMessage, EditRole, EmojiIdentifier, FormattedTimestamp, - FormattedTimestampStyle, MessageBuilder, Timestamp, UserId, + FormattedTimestampStyle, MESSAGE_CODE_LIMIT, MessageBuilder, Timestamp, UserId, }; use std::borrow::Cow; @@ -197,7 +198,18 @@ pub async fn list_tasks(ctx: Context<'_>) -> Result<(), Error> { )); } - ctx.reply(resp.build()).await?; + let msg = resp.build(); + + if msg.len() < MESSAGE_CODE_LIMIT { + ctx.reply(msg).await?; + } else { + ctx.send( + CreateReply::default() + .reply(true) + .attachment(CreateAttachment::bytes(msg.as_bytes(), "tasks.md")), + ) + .await?; + } Ok(()) } @@ -430,7 +442,18 @@ pub async fn list_listeners(ctx: Context<'_>) -> Result<(), Error> { msg_builder.push_line(format!("\t* Actions: `{:?}`", listener.actions)); } - ctx.reply(msg_builder.build()).await?; + let msg = msg_builder.build(); + + if msg.len() < MESSAGE_CODE_LIMIT { + ctx.reply(msg).await?; + } else { + ctx.send( + CreateReply::default() + .reply(true) + .attachment(CreateAttachment::bytes(msg.as_bytes(), "listeners.md")), + ) + .await?; + } Ok(()) } diff --git a/src/discord/mod.rs b/src/discord/mod.rs index 8e14228..e4787ad 100644 --- a/src/discord/mod.rs +++ b/src/discord/mod.rs @@ -135,22 +135,24 @@ async fn handle_message( User::update_social_credit(&data.db, new_message.author.id, social_credit_change)?; } - let trigger_event = TriggerEvent::new( - new_message.author.id, - TriggerType::OnMessage { - channel_id: Some(new_message.channel_id), - content: Some(new_message.content.clone()), - }, - new_message.id, - new_message.channel_id, - ); + if !new_message.content.starts_with("!") { + let trigger_event = TriggerEvent::new( + new_message.author.id, + TriggerType::OnMessage { + channel_id: Some(new_message.channel_id), + content: Some(new_message.content.clone()), + }, + new_message.id, + new_message.channel_id, + ); - match Listener::process_trigger(ctx, data, trigger_event).await { - Ok(_) => { - debug!("Processed message trigger successfully") - } - Err(err) => { - error!("Failed to process message trigger: {err}") + match Listener::process_trigger(ctx, data, trigger_event).await { + Ok(_) => { + debug!("Processed message trigger successfully") + } + Err(err) => { + error!("Failed to process message trigger: {err}") + } } } @@ -382,6 +384,8 @@ pub async fn run_bot(global_data: GlobalData) { shop::helmet(), shop::emp(), shop::land_mine(), + shop::phrase_canceler(), + shop::nuke(), transit::cta_bets(), voices::list_voices(), voices::list_words(), @@ -417,6 +421,8 @@ pub async fn run_bot(global_data: GlobalData) { | serenity::GatewayIntents::DIRECT_MESSAGES | serenity::GatewayIntents::GUILDS | serenity::GatewayIntents::MESSAGE_CONTENT + | serenity::GatewayIntents::GUILD_MEMBERS + | serenity::GatewayIntents::GUILD_PRESENCES | serenity::GatewayIntents::GUILD_MESSAGE_REACTIONS; let mut client = serenity::ClientBuilder::new(token, intents) diff --git a/src/discord/shop.rs b/src/discord/shop.rs index 1be510a..5e1585d 100644 --- a/src/discord/shop.rs +++ b/src/discord/shop.rs @@ -5,6 +5,7 @@ use crate::event_listener::{Action, Expiration, Listener, TriggerType}; use crate::image_manipulation::create_motivation_image; use crate::inventory::{InventoryError, ItemData, ItemType, Operation, Target}; use crate::user::{User, UserError}; +use chrono::{Duration, Utc}; use poise::serenity_prelude::utils::MessageBuilder; use poise::serenity_prelude::{Channel, CreateMessage, UserId}; use rand::{Rng, rng}; @@ -54,7 +55,7 @@ pub async fn inventory(ctx: Context<'_>) -> Result<(), Error> { Ok(()) } -/// Buy an item from the shop with your hard earned cash +/// Buy an item from the shop with your hard-earned cash #[poise::command(prefix_command, category = "Shop")] pub async fn buy( ctx: Context<'_>, @@ -90,36 +91,42 @@ pub async fn buy( Ok(()) } +/// Protect yourself from the woke mind virus #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn cancel_insurance(ctx: Context<'_>) -> Result<(), Error> { use_item(ctx, ItemType::CancelInsurance, Target::Myself).await?; Ok(()) } +/// Understand the concept of love #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn the_concept_of_love(ctx: Context<'_>) -> Result<(), Error> { use_item(ctx, ItemType::TheConceptOfLove, Target::Myself).await?; Ok(()) } +/// Get a good fortune #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn good_fortune(ctx: Context<'_>, #[rest] _msg: String) -> Result<(), Error> { use_item(ctx, ItemType::TheConceptOfLove, Target::Myself).await?; Ok(()) } +/// Check out your sick NFT #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn nft(ctx: Context<'_>) -> Result<(), Error> { use_item(ctx, ItemType::Nft, Target::Myself).await?; Ok(()) } +/// Present proper id #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn license_to_be_horny(ctx: Context<'_>) -> Result<(), Error> { use_item(ctx, ItemType::LicenseToBeHorny, Target::Myself).await?; Ok(()) } +/// Use a kill gun to kill someone #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn kill_gun( ctx: Context<'_>, @@ -129,6 +136,7 @@ pub async fn kill_gun( Ok(()) } +/// Use a cancel ray to cancel someone #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn cancel_ray( ctx: Context<'_>, @@ -138,19 +146,22 @@ pub async fn cancel_ray( Ok(()) } +/// Use your helmet #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn helmet(ctx: Context<'_>) -> Result<(), Error> { use_item(ctx, ItemType::Helmet, Target::Myself).await?; Ok(()) } +/// Deploy a tactical emp #[poise::command(prefix_command, category = "Shop", guild_only)] pub async fn emp(ctx: Context<'_>) -> Result<(), Error> { use_item(ctx, ItemType::EMP, Target::Everyone).await?; Ok(()) } -#[poise::command(prefix_command, category = "Shop", guild_only)] +/// Deploy a land mine +#[poise::command(prefix_command, category = "Shop")] pub async fn land_mine( ctx: Context<'_>, #[description = "Channel to land mine"] channel: Channel, @@ -183,6 +194,66 @@ pub async fn land_mine( Ok(()) } +/// Use your Phrase Canceler +#[poise::command(prefix_command, category = "Shop")] +pub async fn phrase_canceler( + ctx: Context<'_>, + #[description = "Phrase to cancel"] + #[rest] + phrase: String, +) -> Result<(), Error> { + let used_item = use_item(ctx, ItemType::PhraseCanceler, Target::Other).await?; + + if used_item { + Listener::add_listener(&ctx.data().db, { + Listener::new( + TriggerType::OnMessage { + channel_id: None, + content: Some(phrase), + }, + vec![Action::Cancel { hours: ctx.data().cfg.effect_role_duration as u16}, Action::Speak {msg: "oof, dude. Don't you know the Christopher Columbus connotations of what you just said? Consider yourself canceled.".to_string()}], + 1.0, + Expiration::NumberOfTriggers { triggers: 1 }, + false + ) + })?; + + ctx.author() + .id + .direct_message( + ctx, + CreateMessage::default().content("The video essay has been released, now we wait"), + ) + .await?; + } + + Ok(()) +} + +/// Drop a nuke on this discord +#[poise::command(prefix_command, category = "Shop", guild_only)] +pub async fn nuke(ctx: Context<'_>) -> Result<(), Error> { + if use_item(ctx, ItemType::Nuke, Target::Everyone).await? { + Listener::add_listener(&ctx.data().db, { + Listener::new( + TriggerType::OnMessage { + channel_id: Some(ctx.channel_id()), + content: None, + }, + vec![ + Action::Ghoulify { hours: ctx.data().cfg.effect_role_duration as u16 }, + Action::Speak { msg: "As the radiation exposure sets in, you notice changes. Your flesh begins to deteriorate. Your voice becomes raspy. You are now a ghoul. https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExdjJpcmtwbXRhZml1djd3ZGI3bjhhb3dxYm1vdmVpN2swN2hiZnNzZiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/83uF5ITtkxbUul6gMB/giphy.gif".to_string()} + ], + 0.5, + Expiration::Time { time: Utc::now() + Duration::days(5) }, + false + ) + })?; + } + + Ok(()) +} + pub async fn use_item(ctx: Context<'_>, item: ItemType, target: Target) -> Result { let target_users: Vec = match target { Target::Myself => { @@ -303,17 +374,23 @@ pub async fn restock_shop( .give_item(ItemType::LicenseToBeHorny, rng().random_range(0..=25), None); bot_user .inventory - .give_item(ItemType::KillGun, rng().random_range(1..=3), None); + .give_item(ItemType::KillGun, rng().random_range(1..=5), None); bot_user .inventory - .give_item(ItemType::CancelRay, rng().random_range(1..=3), None); + .give_item(ItemType::CancelRay, rng().random_range(1..=5), None); bot_user .inventory .give_item(ItemType::Helmet, rng().random_range(1..=3), None); - bot_user .inventory - .give_item(ItemType::EMP, rng().random_range(0..=1), None); + .give_item(ItemType::EMP, rng().random_range(1..=2), None); + bot_user + .inventory + .give_item(ItemType::PhraseCanceler, rng().random_range(1..=5), None); + bot_user + .inventory + .give_item(ItemType::LandMine, rng().random_range(1..=5), None); + bot_user.inventory.give_item(ItemType::Nuke, 1, None); loop { let mut dir = tokio::fs::read_dir(&global_data.cfg.nft_path).await?; diff --git a/src/event_listener/mod.rs b/src/event_listener/mod.rs index 49788b7..8da0a2c 100644 --- a/src/event_listener/mod.rs +++ b/src/event_listener/mod.rs @@ -96,6 +96,9 @@ pub enum Action { Cancel { hours: u16, }, + Ghoulify { + hours: u16, + }, UpdateSocialCredit { score_diff: i64, }, @@ -239,6 +242,17 @@ impl Listener { Action::Speak { msg } => { trigger_event.channel_id.say(ctx.http(), msg).await?; } + Action::Ghoulify { hours } => { + User::add_role( + ctx.http(), + &data.db, + trigger_event.triggerer, + data.cfg.guild_id, + UserRole::Ghoul, + Duration::hours(*hours as i64), + ) + .await?; + } } } diff --git a/src/inventory/mod.rs b/src/inventory/mod.rs index be3b488..f2430c5 100644 --- a/src/inventory/mod.rs +++ b/src/inventory/mod.rs @@ -1,3 +1,4 @@ +use poise::ChoiceParameter; use poise::serenity_prelude::UserId; use poise::serenity_prelude::utils::MessageBuilder; use rand::prelude::IndexedRandom; @@ -63,8 +64,12 @@ pub enum ItemType { Helmet, #[name = "EMP"] EMP, - #[name = "EMP"] + #[name = "Land Mine"] LandMine, + #[name = "Phrase Canceler"] + PhraseCanceler, + #[name = "Nuke"] + Nuke, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] @@ -89,7 +94,9 @@ impl ItemType { ItemType::CancelRay => "Used to cancel people. `!use cancel ray @Austin`".to_string(), ItemType::Helmet => "Automatically used to block being killed".to_string(), ItemType::EMP => "Disables weapons and defenses".to_string(), - ItemType::LandMine => "A land mine you can setup for someone to step on".to_string(), + ItemType::LandMine => "A land mine you can set up for someone to step on".to_string(), + ItemType::PhraseCanceler => "Make a term antiquated".to_string(), + ItemType::Nuke => "A nuclear warhead with a vvery large payload".to_string(), } } @@ -135,6 +142,12 @@ impl ItemType { ItemType::LandMine => { None } + ItemType::PhraseCanceler => { + None + } + ItemType::Nuke => { + Some("The Flock Enters A New Age https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExYmdmMWJuamQxZnQxcWlleHJtdXVtY3VyaDZlNHpwZGxobDl4c251ZyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/HhTXt43pk1I1W/giphy.gif".to_string()) + } } } @@ -193,6 +206,10 @@ impl FromStr for ItemType { Ok(ItemType::EMP) } else if item.starts_with("landmine") { Ok(ItemType::LandMine) + } else if item.starts_with("phrasecanceler") { + Ok(ItemType::PhraseCanceler) + } else if item.starts_with("nuke") { + Ok(ItemType::Nuke) } else { Err(InventoryError::UnkownItem) } @@ -201,20 +218,7 @@ impl FromStr for ItemType { impl Display for ItemType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let name = match self { - ItemType::CancelInsurance => "Cancel Insurance".to_string(), - ItemType::TheConceptOfLove => "The Concept of Love".to_string(), - ItemType::GoodFortune => "Good Fortune".to_string(), - ItemType::Nft => "NFT".to_string(), - ItemType::LicenseToBeHorny => "License to be Horny".to_string(), - ItemType::KillGun => "Kill Gun".to_string(), - ItemType::Helmet => "Helmet".to_string(), - ItemType::CancelRay => "Cancel Ray".to_string(), - ItemType::EMP => "EMP".to_string(), - ItemType::LandMine => "Land Mine".to_string(), - }; - - write!(f, "{name}") + write!(f, "{}", self.name()) } } @@ -243,11 +247,13 @@ impl InventorySlot { ItemType::GoodFortune => 75, ItemType::Nft => 250, ItemType::LicenseToBeHorny => 250, - ItemType::KillGun => 2_000, + ItemType::KillGun => 1_000, ItemType::Helmet => 10_000, - ItemType::CancelRay => 2_000, + ItemType::CancelRay => 1_000, ItemType::EMP => 50_000, ItemType::LandMine => 3_000, + ItemType::PhraseCanceler => 2_000, + ItemType::Nuke => 100_000, } } diff --git a/src/user/mod.rs b/src/user/mod.rs index 3c61d2f..d9332e0 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -346,7 +346,7 @@ impl User { return Ok(Some("Sorry this was a pump and dump".to_string())); } } - ItemType::KillGun => { + ItemType::KillGun | ItemType::Nuke => { Self::add_role( ctx.http(), &ctx.data().db,