diff --git a/discord/src/cli.rs b/discord/src/cli.rs index fe081ab..2872d38 100644 --- a/discord/src/cli.rs +++ b/discord/src/cli.rs @@ -6,5 +6,5 @@ use clap::Parser; pub struct Cli { /// Path to config.toml #[arg(long, short)] - pub config: PathBuf, + pub config: Option, } diff --git a/discord/src/commands/account.rs b/discord/src/commands/account.rs index e6a8ded..9033d6d 100644 --- a/discord/src/commands/account.rs +++ b/discord/src/commands/account.rs @@ -1,14 +1,21 @@ -use poise::serenity_prelude::{Mentionable, UserId}; +use poise::serenity_prelude::UserId; use squad_quest::{SquadObject, account::Account, map::Map}; -use crate::{Context, Error}; +use crate::{Context, Error, account::fetch_or_init_account}; -fn account_balance_string(account: &Account, map: &Map) -> String { +async fn account_balance_string(ctx: &Context<'_>, account: &Account, map: &Map) -> String { let rooms_value = account_rooms_value(account, map); let full_balance = account_full_balance(account, map); - format!("\n{account}: **{full_balance}** points (**{balance}** on balance \ + let account_id = account_user_id(&account); + + let Ok(user) = account_id + .to_user(ctx) + .await else { + return String::new(); + }; + let name = user.display_name(); + format!("\n{name}: **{full_balance}** points (**{balance}** on balance \ + **{rooms_value}** unlocked rooms networth)", - account = account_user_id(&account).mention(), balance = account.balance, ) } @@ -57,7 +64,7 @@ pub async fn scoreboard( for account in accounts { let user_id = account_user_id(&account); - let mut str = account_balance_string(&account, &map); + let mut str = account_balance_string(&ctx, &account, &map).await; if user_id == this_user { str = format!("__{str}__ << You"); } @@ -69,3 +76,82 @@ pub async fn scoreboard( Ok(()) } + +#[poise::command( + prefix_command, + slash_command, + guild_only, + subcommands("give", "set"), +)] +pub async fn balance( + _ctx: Context<'_>, +) -> Result<(), Error> { + Ok(()) +} + +#[poise::command( + prefix_command, + slash_command, + guild_only, +)] +pub async fn give( + ctx: Context<'_>, + who: UserId, + amount: u32, +) -> Result<(), Error> { + let config = &ctx.data().config; + let mut accounts = config.load_accounts(); + + let user_id = format!("{}", ctx.author().id.get()); + let mut user_account = fetch_or_init_account(config, user_id); + + let who_id = format!("{}", who.get()); + let Some(other_account) = accounts.iter_mut().find(|a| a.id == who_id ) else { + return Err(Error::AccountNotFound); + }; + + if user_account.balance < amount { + return Err(Error::InsufficientFunds(amount)); + } + + user_account.balance -= amount; + other_account.balance += amount; + + let accounts_path = config.full_accounts_path(); + user_account.save(accounts_path.clone())?; + other_account.save(accounts_path)?; + + let reply_string = format!("Given money to user.\n\ + Your new balance: {} points.", user_account.balance); + ctx.reply(reply_string).await?; + + Ok(()) +} + +#[poise::command( + prefix_command, + slash_command, + guild_only, + required_permissions = "ADMINISTRATOR", +)] +pub async fn set( + ctx: Context<'_>, + who: UserId, + amount: u32, +) -> Result<(), Error> { + let mut accounts = ctx.data().config.load_accounts(); + + let who_id = format!("{}", who.get()); + let Some(account) = accounts.iter_mut().find(|a| a.id == who_id ) else { + return Err(Error::AccountNotFound); + }; + + account.balance = amount; + let accounts_path = ctx.data().config.full_accounts_path(); + account.save(accounts_path)?; + + let reply_string = format!("Set user balance to {amount}."); + ctx.reply(reply_string).await?; + + Ok(()) +} diff --git a/discord/src/commands/answer.rs b/discord/src/commands/answer.rs index 32b76ab..fd55666 100644 --- a/discord/src/commands/answer.rs +++ b/discord/src/commands/answer.rs @@ -28,7 +28,9 @@ pub async fn answer( } let quests = ctx.data().config.load_quests(); - let Some(quest) = quests.iter().find(|q| q.id == quest_id) else { + let Some(quest) = quests.iter() + .filter(|q| q.public) + .find(|q| q.id == quest_id) else { return Err(Error::QuestNotFound(quest_id)); }; diff --git a/discord/src/commands/quest.rs b/discord/src/commands/quest.rs index b7e4896..af3af5a 100644 --- a/discord/src/commands/quest.rs +++ b/discord/src/commands/quest.rs @@ -39,7 +39,9 @@ fn make_quest_message_content(quest: &Quest) -> String { #[poise::command( prefix_command, slash_command, + guild_only, subcommands("list", "create", "update", "publish", "delete"), + required_permissions = "ADMINISTRATOR", )] pub async fn quest( _ctx: Context<'_>, @@ -50,6 +52,8 @@ pub async fn quest( #[poise::command( prefix_command, slash_command, + guild_only, + required_permissions = "ADMINISTRATOR", )] pub async fn list( ctx: Context<'_>, diff --git a/discord/src/error.rs b/discord/src/error.rs index feee407..03881e7 100644 --- a/discord/src/error.rs +++ b/discord/src/error.rs @@ -13,6 +13,8 @@ pub enum Error { BothChannelAndUser, SerenityError(serenity::Error), SquadQuestError(squad_quest::error::Error), + AccountNotFound, + InsufficientFunds(u32), } impl From for Error { @@ -38,6 +40,8 @@ impl Display for Error { Self::BothChannelAndUser => write!(f, "both channel and user was specified"), Self::SerenityError(_) => write!(f, "discord interaction error"), Self::SquadQuestError(_) => write!(f, "internal logic error"), + Self::AccountNotFound => write!(f, "account not found"), + Self::InsufficientFunds(amount) => write!(f, "account does not have {amount} points"), } } } @@ -50,7 +54,9 @@ impl std::error::Error for Error { Self::QuestIsCompleted(_) | Self::NoContent | Self::NoChannelOrUser | - Self::BothChannelAndUser => None, + Self::BothChannelAndUser | + Self::AccountNotFound | + Self::InsufficientFunds(_) => None, Self::SerenityError(error) => Some(error), Self::SquadQuestError(error) => Some(error), } diff --git a/discord/src/main.rs b/discord/src/main.rs index 7fa2d26..cb73124 100644 --- a/discord/src/main.rs +++ b/discord/src/main.rs @@ -13,6 +13,8 @@ mod config; mod account; mod error; +const CONFIG_PATH: &str = "cfg/config.toml"; + #[derive(Debug)] struct Data { pub config: Config, @@ -25,7 +27,7 @@ async fn main() { dotenv().unwrap(); let cli = cli::Cli::parse(); - let config = Config::load(cli.config.clone()); + let config = Config::load(cli.config.clone().unwrap_or(CONFIG_PATH.into())); let discord = config.discord_impl().unwrap_or_else(|_| { config.init_impl().unwrap(); config.discord_impl().unwrap() @@ -44,6 +46,7 @@ async fn main() { commands::answer::answer(), commands::social::social(), commands::account::scoreboard(), + commands::account::balance(), ], ..Default::default() })