Add land mine
This commit is contained in:
parent
0e588d9d62
commit
1669a9cf8f
@ -83,6 +83,7 @@ pub struct GlobalData {
|
||||
pub picox: AlbumManager,
|
||||
pub cta: Mutex<CTAClient>,
|
||||
pub speak_lock: Mutex<()>,
|
||||
pub listener_lock: Mutex<()>,
|
||||
}
|
||||
|
||||
impl GlobalData {
|
||||
@ -93,12 +94,12 @@ impl GlobalData {
|
||||
});
|
||||
|
||||
info!("Adding system listeners...");
|
||||
Listener::add_event(
|
||||
Listener::add_listener(
|
||||
db,
|
||||
Listener::new(
|
||||
TriggerType::OnMessage {
|
||||
channel_id: None,
|
||||
content: "bad bot".to_string(),
|
||||
content: Some("bad bot".to_string()),
|
||||
},
|
||||
vec![Action::React {
|
||||
emoji: "😭".to_string(),
|
||||
@ -108,6 +109,21 @@ impl GlobalData {
|
||||
true,
|
||||
),
|
||||
)?;
|
||||
Listener::add_listener(
|
||||
db,
|
||||
Listener::new(
|
||||
TriggerType::OnMessage {
|
||||
channel_id: None,
|
||||
content: None,
|
||||
},
|
||||
vec![Action::UpdateFrenCoins {
|
||||
fren_coin_diff: 100,
|
||||
}],
|
||||
0.05,
|
||||
Expiration::Never,
|
||||
true,
|
||||
),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -136,6 +152,7 @@ impl GlobalData {
|
||||
picox: AlbumManager::new(cfg.picox.api_base_url, &cfg.picox.token),
|
||||
cta: Mutex::new(CTAClient::new(cfg.cta_key)),
|
||||
speak_lock: Default::default(),
|
||||
listener_lock: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::discord::{Context, get_role};
|
||||
use crate::error::Error;
|
||||
use crate::event_listener::Listener;
|
||||
use crate::inventory::ItemType;
|
||||
use crate::models::api_key::Apikey;
|
||||
use crate::models::lil_fren::{
|
||||
@ -405,3 +406,48 @@ pub async fn op_give_money(
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all the listeners
|
||||
#[poise::command(prefix_command, category = "Admin", check = "is_admin")]
|
||||
pub async fn list_listeners(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let mut msg_builder = MessageBuilder::new();
|
||||
msg_builder.push_line("The following listeners are active:");
|
||||
for listener in ctx.data().db.filter(|_, _listener: &Listener| true)? {
|
||||
msg_builder.push_line(format!(
|
||||
"* id={} `{:?}`:",
|
||||
listener.id().unwrap(),
|
||||
listener.trigger
|
||||
));
|
||||
msg_builder.push_line(format!("\t* Trigger Count: `{:?}`", listener.trigger_count));
|
||||
msg_builder.push_line(format!(
|
||||
"\t* Trigger Chance: `{:?}`",
|
||||
listener.trigger_chance
|
||||
));
|
||||
msg_builder.push_line(format!(
|
||||
"\t* System Listener: `{}`",
|
||||
listener.system_listener
|
||||
));
|
||||
msg_builder.push_line(format!("\t* Actions: `{:?}`", listener.actions));
|
||||
}
|
||||
|
||||
ctx.reply(msg_builder.build()).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove listener
|
||||
#[poise::command(prefix_command, category = "Admin", check = "is_admin")]
|
||||
pub async fn remove_listener(
|
||||
ctx: Context<'_>,
|
||||
#[description = "ID of the listener to remove"] listener_id: u64,
|
||||
) -> Result<(), Error> {
|
||||
let listener = ctx.data().db.remove::<Listener>(listener_id)?;
|
||||
|
||||
ctx.reply(format!(
|
||||
"Removed listener with id={}",
|
||||
listener.id().unwrap()
|
||||
))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -123,8 +123,6 @@ async fn handle_message(
|
||||
}
|
||||
};
|
||||
|
||||
give_coin(&data.db, new_message.author.id, 0.05, 10).await?;
|
||||
|
||||
if let Some(phrase) = SocialCreditPhrase::check_if_match(&data.db, &new_message.content)? {
|
||||
info!(
|
||||
"{} matched phrase '{}' for social credit checking",
|
||||
@ -141,7 +139,7 @@ async fn handle_message(
|
||||
new_message.author.id,
|
||||
TriggerType::OnMessage {
|
||||
channel_id: Some(new_message.channel_id),
|
||||
content: new_message.content.clone(),
|
||||
content: Some(new_message.content.clone()),
|
||||
},
|
||||
new_message.id,
|
||||
new_message.channel_id,
|
||||
@ -325,6 +323,8 @@ pub async fn run_bot(global_data: GlobalData) {
|
||||
admin::reset_random_score(),
|
||||
admin::remove_random_result(),
|
||||
admin::op_give_money(),
|
||||
admin::list_listeners(),
|
||||
admin::remove_listener(),
|
||||
album::add_image(),
|
||||
album::list_albums(),
|
||||
birthday::add_birthday(),
|
||||
@ -372,7 +372,16 @@ pub async fn run_bot(global_data: GlobalData) {
|
||||
shop::item_help(),
|
||||
shop::sell_item(),
|
||||
shop::shop(),
|
||||
shop::use_item(),
|
||||
shop::cancel_insurance(),
|
||||
shop::the_concept_of_love(),
|
||||
shop::good_fortune(),
|
||||
shop::nft(),
|
||||
shop::license_to_be_horny(),
|
||||
shop::kill_gun(),
|
||||
shop::cancel_ray(),
|
||||
shop::helmet(),
|
||||
shop::emp(),
|
||||
shop::land_mine(),
|
||||
transit::cta_bets(),
|
||||
voices::list_voices(),
|
||||
voices::list_words(),
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
use crate::config::GlobalData;
|
||||
use crate::discord::Context;
|
||||
use crate::error::Error;
|
||||
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 poise::ChoiceParameter;
|
||||
use poise::serenity_prelude::UserId;
|
||||
use poise::serenity_prelude::all::parse_user_mention;
|
||||
use poise::serenity_prelude::utils::MessageBuilder;
|
||||
use poise::serenity_prelude::{Channel, CreateMessage, UserId};
|
||||
use rand::{Rng, rng};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
@ -91,48 +90,109 @@ pub async fn buy(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Use an item and hope it doesn't use you
|
||||
#[poise::command(prefix_command, category = "Shop", guild_only, aliases("use"))]
|
||||
pub async fn use_item(
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[poise::command(prefix_command, category = "Shop", guild_only)]
|
||||
pub async fn kill_gun(
|
||||
ctx: Context<'_>,
|
||||
#[description = "item to use"]
|
||||
#[rest]
|
||||
item: String,
|
||||
#[description = "target to kill"] target: poise::serenity_prelude::User,
|
||||
) -> Result<(), Error> {
|
||||
if item.is_empty() {
|
||||
ctx.reply("You need to select one item to use.").await?;
|
||||
return Ok(());
|
||||
use_item(ctx, ItemType::KillGun, Target::User(target.id)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(prefix_command, category = "Shop", guild_only)]
|
||||
pub async fn cancel_ray(
|
||||
ctx: Context<'_>,
|
||||
#[description = "target to cancel"] target: poise::serenity_prelude::User,
|
||||
) -> Result<(), Error> {
|
||||
use_item(ctx, ItemType::CancelRay, Target::User(target.id)).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[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(())
|
||||
}
|
||||
|
||||
#[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)]
|
||||
pub async fn land_mine(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Channel to land mine"] channel: Channel,
|
||||
) -> Result<(), Error> {
|
||||
let used_item = use_item(ctx, ItemType::LandMine, Target::Other).await?;
|
||||
|
||||
if used_item {
|
||||
Listener::add_listener(&ctx.data().db, {
|
||||
Listener::new(
|
||||
TriggerType::OnMessage {
|
||||
channel_id: Some(channel.id()),
|
||||
content: None,
|
||||
},
|
||||
vec![Action::Kill { hours: ctx.data().cfg.effect_role_duration as u16}, Action::Speak {msg: "*click* **BOOM**, sorry kid you've run out of time. That was a landmine. You're dead.".to_string()}],
|
||||
0.95,
|
||||
Expiration::NumberOfTriggers { triggers: 1 },
|
||||
false
|
||||
)
|
||||
})?;
|
||||
|
||||
ctx.author()
|
||||
.id
|
||||
.direct_message(
|
||||
ctx,
|
||||
CreateMessage::default().content("Bomb has been planted"),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let msg_content = item.to_lowercase();
|
||||
let item = match msg_content.parse::<ItemType>() {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
ctx.reply("I don't know what the heck that is tbh.").await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
let target_users: Vec<UserId> = match item.target() {
|
||||
pub async fn use_item(ctx: Context<'_>, item: ItemType, target: Target) -> Result<bool, Error> {
|
||||
let target_users: Vec<UserId> = match target {
|
||||
Target::Myself => {
|
||||
vec![ctx.author().id]
|
||||
}
|
||||
Target::User => {
|
||||
let item_name = format!("{} ", item.name().to_ascii_lowercase());
|
||||
let split = msg_content.split(&item_name);
|
||||
let target = parse_user_mention(split.last().unwrap()).unwrap();
|
||||
let target_member = match ctx.guild_id().unwrap().member(ctx, target).await {
|
||||
Ok(member) => member,
|
||||
Err(_) => {
|
||||
return Err(Error::CommandError(
|
||||
"I have no clue who that is tbh".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
vec![target_member.user.id]
|
||||
Target::User(user_id) => {
|
||||
vec![user_id]
|
||||
}
|
||||
Target::Everyone => ctx.guild().unwrap().members.keys().copied().collect(),
|
||||
Target::Other => vec![],
|
||||
};
|
||||
|
||||
let item_data = match User::try_use_item(&ctx.data().db, ctx.author().id, item, false) {
|
||||
@ -143,7 +203,7 @@ pub async fn use_item(
|
||||
ctx.reply("Looks like you don't have enough of that item to use it")
|
||||
.await?;
|
||||
}
|
||||
return Ok(());
|
||||
return Ok(false);
|
||||
}
|
||||
};
|
||||
|
||||
@ -159,7 +219,7 @@ pub async fn use_item(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Sell an item for profit
|
||||
|
||||
@ -18,7 +18,7 @@ use std::sync::Arc;
|
||||
pub enum TriggerType {
|
||||
OnMessage {
|
||||
channel_id: Option<ChannelId>,
|
||||
content: String,
|
||||
content: Option<String>,
|
||||
},
|
||||
UseItem {
|
||||
item: ItemType,
|
||||
@ -43,10 +43,17 @@ impl TriggerType {
|
||||
} else {
|
||||
true
|
||||
};
|
||||
event_msg
|
||||
.to_ascii_lowercase()
|
||||
.contains(&trigger.to_ascii_lowercase())
|
||||
&& match_channel
|
||||
|
||||
let match_msg = if let Some(trigger) = trigger {
|
||||
let phrase = trigger.to_ascii_lowercase();
|
||||
let event_msg = event_msg.as_deref().unwrap().to_ascii_lowercase();
|
||||
|
||||
event_msg.contains(&phrase)
|
||||
} else {
|
||||
true
|
||||
};
|
||||
|
||||
match_channel && match_msg
|
||||
}
|
||||
(TriggerType::UseItem { item: trigger }, TriggerType::UseItem { item: event_item }) => {
|
||||
trigger == event_item
|
||||
@ -113,8 +120,8 @@ pub enum Action {
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum Expiration {
|
||||
NumberOfTriggers(u16),
|
||||
Time(chrono::DateTime<Utc>),
|
||||
NumberOfTriggers { triggers: u16 },
|
||||
Time { time: chrono::DateTime<Utc> },
|
||||
Never,
|
||||
}
|
||||
|
||||
@ -126,6 +133,7 @@ pub struct Listener {
|
||||
pub trigger_chance: f64,
|
||||
pub expiration: Expiration,
|
||||
pub system_listener: bool,
|
||||
pub trigger_count: u64,
|
||||
}
|
||||
|
||||
impl Listener {
|
||||
@ -143,6 +151,7 @@ impl Listener {
|
||||
trigger_chance,
|
||||
expiration,
|
||||
system_listener,
|
||||
trigger_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,7 +160,7 @@ impl Listener {
|
||||
}
|
||||
|
||||
pub async fn handle_action(
|
||||
&self,
|
||||
&mut self,
|
||||
ctx: &Context,
|
||||
data: &Arc<GlobalData>,
|
||||
trigger_event: &TriggerEvent,
|
||||
@ -233,12 +242,25 @@ impl Listener {
|
||||
}
|
||||
}
|
||||
|
||||
self.trigger_count += 1;
|
||||
|
||||
let expire = match self.expiration {
|
||||
Expiration::NumberOfTriggers { triggers } => self.trigger_count >= triggers as u64,
|
||||
Expiration::Time { time } => time > Utc::now(),
|
||||
Expiration::Never => false,
|
||||
};
|
||||
|
||||
if expire {
|
||||
data.db.remove::<Listener>(self.id.unwrap())?;
|
||||
} else {
|
||||
data.db.insert(self.clone())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_event(db: &Database, new_event: Self) -> Result<Self, Error> {
|
||||
debug!("Adding listener: {new_event:?}");
|
||||
Ok(db.insert(new_event)?)
|
||||
pub fn add_listener(db: &Database, new_listener: Self) -> Result<Self, Error> {
|
||||
debug!("Adding listener: {new_listener:?}");
|
||||
Ok(db.insert(new_listener)?)
|
||||
}
|
||||
|
||||
pub async fn process_trigger(
|
||||
@ -246,11 +268,18 @@ impl Listener {
|
||||
data: &Arc<GlobalData>,
|
||||
trigger_event: TriggerEvent,
|
||||
) -> Result<(), Error> {
|
||||
let triggered_listeners = data.db.filter(|_, listener: &Listener| {
|
||||
// Ignore the bot for triggers
|
||||
if trigger_event.triggerer == ctx.cache.current_user().id {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let _ = data.listener_lock.lock().await;
|
||||
|
||||
let mut triggered_listeners = data.db.filter(|_, listener: &Listener| {
|
||||
listener.check_if_triggers(&trigger_event.trigger_type)
|
||||
})?;
|
||||
|
||||
for listener in triggered_listeners {
|
||||
for mut listener in &mut triggered_listeners {
|
||||
match listener.handle_action(ctx, data, &trigger_event).await {
|
||||
Ok(_) => {
|
||||
debug!("Processed event: {listener:?}");
|
||||
@ -261,6 +290,8 @@ impl Listener {
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the DB is synced to prevent re-triggering of events
|
||||
data.db.db.flush_async().await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -275,6 +306,6 @@ impl j_db::model::JdbModel for Listener {
|
||||
}
|
||||
|
||||
fn tree() -> String {
|
||||
"Events".to_string()
|
||||
"Listener".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
use poise::serenity_prelude::UserId;
|
||||
use poise::serenity_prelude::utils::MessageBuilder;
|
||||
use rand::prelude::IndexedRandom;
|
||||
use rand::rng;
|
||||
@ -62,13 +63,16 @@ pub enum ItemType {
|
||||
Helmet,
|
||||
#[name = "EMP"]
|
||||
EMP,
|
||||
#[name = "EMP"]
|
||||
LandMine,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum Target {
|
||||
Myself,
|
||||
User,
|
||||
User(UserId),
|
||||
Everyone,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl ItemType {
|
||||
@ -85,6 +89,7 @@ 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(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,6 +132,9 @@ impl ItemType {
|
||||
ItemType::EMP => {
|
||||
Some("Humanity has forgotten a time before electricity. A time before all our weapons, toys, gadgets. We no longer have to try and remember that time. That time has returned".to_string())
|
||||
}
|
||||
ItemType::LandMine => {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,20 +157,6 @@ impl ItemType {
|
||||
_ => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target(&self) -> Target {
|
||||
match self {
|
||||
ItemType::CancelInsurance => Target::Myself,
|
||||
ItemType::TheConceptOfLove => Target::Myself,
|
||||
ItemType::GoodFortune => Target::Myself,
|
||||
ItemType::Nft => Target::Myself,
|
||||
ItemType::LicenseToBeHorny => Target::Myself,
|
||||
ItemType::KillGun => Target::User,
|
||||
ItemType::CancelRay => Target::User,
|
||||
ItemType::Helmet => Target::Myself,
|
||||
ItemType::EMP => Target::Everyone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Copy)]
|
||||
@ -197,6 +191,8 @@ impl FromStr for ItemType {
|
||||
Ok(ItemType::CancelRay)
|
||||
} else if item.starts_with("emp") {
|
||||
Ok(ItemType::EMP)
|
||||
} else if item.starts_with("landmine") {
|
||||
Ok(ItemType::LandMine)
|
||||
} else {
|
||||
Err(InventoryError::UnkownItem)
|
||||
}
|
||||
@ -215,6 +211,7 @@ impl Display for ItemType {
|
||||
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}")
|
||||
@ -250,6 +247,7 @@ impl InventorySlot {
|
||||
ItemType::Helmet => 10_000,
|
||||
ItemType::CancelRay => 2_000,
|
||||
ItemType::EMP => 50_000,
|
||||
ItemType::LandMine => 3_000,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user