feat(discord): Added /balance {give,set} commands
- Also, you cannot /answer to unpublished quest - Also, changed /scoreboard to print name instead of mentioning - Also, made --config an option, defaulting to "cfg/config.toml"
This commit is contained in:
parent
99812c5d7c
commit
4ba57b925a
6 changed files with 111 additions and 10 deletions
|
|
@ -6,5 +6,5 @@ use clap::Parser;
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
/// Path to config.toml
|
/// Path to config.toml
|
||||||
#[arg(long, short)]
|
#[arg(long, short)]
|
||||||
pub config: PathBuf,
|
pub config: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
use poise::serenity_prelude::{Mentionable, UserId};
|
use poise::serenity_prelude::UserId;
|
||||||
use squad_quest::{SquadObject, account::Account, map::Map};
|
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 rooms_value = account_rooms_value(account, map);
|
||||||
let full_balance = account_full_balance(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)",
|
+ **{rooms_value}** unlocked rooms networth)",
|
||||||
account = account_user_id(&account).mention(),
|
|
||||||
balance = account.balance,
|
balance = account.balance,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +64,7 @@ pub async fn scoreboard(
|
||||||
for account in accounts {
|
for account in accounts {
|
||||||
let user_id = account_user_id(&account);
|
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 {
|
if user_id == this_user {
|
||||||
str = format!("__{str}__ << You");
|
str = format!("__{str}__ << You");
|
||||||
}
|
}
|
||||||
|
|
@ -69,3 +76,82 @@ pub async fn scoreboard(
|
||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ pub async fn answer(
|
||||||
}
|
}
|
||||||
|
|
||||||
let quests = ctx.data().config.load_quests();
|
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));
|
return Err(Error::QuestNotFound(quest_id));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,9 @@ fn make_quest_message_content(quest: &Quest) -> String {
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
prefix_command,
|
prefix_command,
|
||||||
slash_command,
|
slash_command,
|
||||||
|
guild_only,
|
||||||
subcommands("list", "create", "update", "publish", "delete"),
|
subcommands("list", "create", "update", "publish", "delete"),
|
||||||
|
required_permissions = "ADMINISTRATOR",
|
||||||
)]
|
)]
|
||||||
pub async fn quest(
|
pub async fn quest(
|
||||||
_ctx: Context<'_>,
|
_ctx: Context<'_>,
|
||||||
|
|
@ -50,6 +52,8 @@ pub async fn quest(
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
prefix_command,
|
prefix_command,
|
||||||
slash_command,
|
slash_command,
|
||||||
|
guild_only,
|
||||||
|
required_permissions = "ADMINISTRATOR",
|
||||||
)]
|
)]
|
||||||
pub async fn list(
|
pub async fn list(
|
||||||
ctx: Context<'_>,
|
ctx: Context<'_>,
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ pub enum Error {
|
||||||
BothChannelAndUser,
|
BothChannelAndUser,
|
||||||
SerenityError(serenity::Error),
|
SerenityError(serenity::Error),
|
||||||
SquadQuestError(squad_quest::error::Error),
|
SquadQuestError(squad_quest::error::Error),
|
||||||
|
AccountNotFound,
|
||||||
|
InsufficientFunds(u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<serenity::Error> for Error {
|
impl From<serenity::Error> for Error {
|
||||||
|
|
@ -38,6 +40,8 @@ impl Display for Error {
|
||||||
Self::BothChannelAndUser => write!(f, "both channel and user was specified"),
|
Self::BothChannelAndUser => write!(f, "both channel and user was specified"),
|
||||||
Self::SerenityError(_) => write!(f, "discord interaction error"),
|
Self::SerenityError(_) => write!(f, "discord interaction error"),
|
||||||
Self::SquadQuestError(_) => write!(f, "internal logic 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::QuestIsCompleted(_) |
|
||||||
Self::NoContent |
|
Self::NoContent |
|
||||||
Self::NoChannelOrUser |
|
Self::NoChannelOrUser |
|
||||||
Self::BothChannelAndUser => None,
|
Self::BothChannelAndUser |
|
||||||
|
Self::AccountNotFound |
|
||||||
|
Self::InsufficientFunds(_) => None,
|
||||||
Self::SerenityError(error) => Some(error),
|
Self::SerenityError(error) => Some(error),
|
||||||
Self::SquadQuestError(error) => Some(error),
|
Self::SquadQuestError(error) => Some(error),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ mod config;
|
||||||
mod account;
|
mod account;
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
|
const CONFIG_PATH: &str = "cfg/config.toml";
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Data {
|
struct Data {
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
|
|
@ -25,7 +27,7 @@ async fn main() {
|
||||||
dotenv().unwrap();
|
dotenv().unwrap();
|
||||||
|
|
||||||
let cli = cli::Cli::parse();
|
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(|_| {
|
let discord = config.discord_impl().unwrap_or_else(|_| {
|
||||||
config.init_impl().unwrap();
|
config.init_impl().unwrap();
|
||||||
config.discord_impl().unwrap()
|
config.discord_impl().unwrap()
|
||||||
|
|
@ -44,6 +46,7 @@ async fn main() {
|
||||||
commands::answer::answer(),
|
commands::answer::answer(),
|
||||||
commands::social::social(),
|
commands::social::social(),
|
||||||
commands::account::scoreboard(),
|
commands::account::scoreboard(),
|
||||||
|
commands::account::balance(),
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue