From 38e3f149b1b96252c4fe8b0278a08e97e1d27b3b Mon Sep 17 00:00:00 2001 From: 2ndbeam <2ndbeam@disroot.org> Date: Thu, 11 Dec 2025 17:27:19 +0300 Subject: [PATCH] feat: Added /quest delete command - Also fixed formatting and sorting in /scoreboard --- discord/src/commands/account.rs | 6 +-- discord/src/commands/quest.rs | 88 ++++++++++++++++++++++++--------- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/discord/src/commands/account.rs b/discord/src/commands/account.rs index 0497787..e6a8ded 100644 --- a/discord/src/commands/account.rs +++ b/discord/src/commands/account.rs @@ -6,7 +6,7 @@ use crate::{Context, Error}; fn account_balance_string(account: &Account, map: &Map) -> String { let rooms_value = account_rooms_value(account, map); let full_balance = account_full_balance(account, map); - format!("{account}: **{full_balance}** points (**{balance}** on balance \ + format!("\n{account}: **{full_balance}** points (**{balance}** on balance \ + **{rooms_value}** unlocked rooms networth)", account = account_user_id(&account).mention(), balance = account.balance, @@ -48,12 +48,12 @@ pub async fn scoreboard( accounts.sort_by(|a,b| { let a_balance = account_full_balance(a, &map); let b_balance = account_full_balance(b, &map); - a_balance.cmp(&b_balance) + b_balance.cmp(&a_balance) }); let this_user = ctx.author().id; - let mut reply_string = String::new(); + let mut reply_string = "Current scoreboard:".to_string(); for account in accounts { let user_id = account_user_id(&account); diff --git a/discord/src/commands/quest.rs b/discord/src/commands/quest.rs index 733be65..7b97a13 100644 --- a/discord/src/commands/quest.rs +++ b/discord/src/commands/quest.rs @@ -1,10 +1,29 @@ use std::{future, path::Path, str::FromStr}; -use poise::serenity_prelude::{CreateMessage, EditMessage, Mentionable, futures::StreamExt}; +use poise::serenity_prelude::{CreateMessage, EditMessage, Message, futures::StreamExt}; use squad_quest::{SquadObject, quest::{Quest, QuestDifficulty}}; use toml::value::Date; use crate::{Context, Error, commands::ERROR_MSG}; +async fn find_quest_message(ctx: Context<'_>, id: u16) -> Option{ + let _ = ctx.defer().await; + let channel = { + let dc = ctx.data().discord.clone(); + let guard = dc.lock().expect("shouldn't be locked"); + guard.quests_channel + }; + let messages = channel.messages_iter(ctx) + .filter_map(|m| async move { if m.is_ok() { Some(m.unwrap()) } else { + eprintln!("{}", m.unwrap_err()); + None + } }) + .filter(|m| { + future::ready(m.content.contains(&format!("#{id}"))) + }) + .collect::>().await; + messages.first().cloned() +} + fn make_quest_message_content(quest: &Quest) -> String { format!("### `#{id}` {name} (+{reward})\n\ Difficulty: *{difficulty:?}*\n\ @@ -20,7 +39,7 @@ fn make_quest_message_content(quest: &Quest) -> String { #[poise::command( prefix_command, slash_command, - subcommands("list", "create", "update", "publish"), + subcommands("list", "create", "update", "publish", "delete"), )] pub async fn quest( _ctx: Context<'_>, @@ -229,33 +248,16 @@ pub async fn update( let content = make_quest_message_content(&new_quest); let builder = EditMessage::new().content(content); - let channel = { - let dc = ctx.data().discord.clone(); - let guard = dc.lock().expect("shouldn't be locked"); - guard.quests_channel - }; - let message = { - let messages = channel.messages_iter(ctx) - .filter_map(|m| async move { if m.is_ok() { Some(m.unwrap()) } else { - eprintln!("{}", m.unwrap_err()); - None - } }) - .filter(|m| { - future::ready(m.content.contains(&format!("#{id}"))) - }) - .collect::>().await; - messages.first().cloned() - }; - if let Some(mut message) = message { + let message = find_quest_message(ctx, id); + if let Some(mut message) = message.await { message.edit(ctx, builder).await?; } else { - let reply_string = format!("Quest #{id} is public, but its message was not found in the {channel}", - channel = channel.mention() + let reply_string = format!("Quest #{id} is public, but its message was not found in the quest channel", ); ctx.reply(reply_string).await?; } } - + let path = conf.full_quests_path(); let reply_string = match new_quest.save(path) { Err(error) => { @@ -336,3 +338,43 @@ pub async fn publish( Ok(()) } + +#[poise::command( + prefix_command, + slash_command, + required_permissions = "ADMINISTRATOR", + guild_only, +)] +pub async fn delete( + ctx: Context<'_>, + id: u16, +) -> Result<(), Error> { + if let Some(msg) = find_quest_message(ctx, id).await { + msg.delete(ctx).await?; + } + + let mut path = ctx.data().config.full_quests_path(); + path.push(format!("{id}.toml")); + if let Err(error) = Quest::delete(path) { + eprintln!("{error}"); + let reply_string = ERROR_MSG.to_string(); + ctx.reply(reply_string).await?; + return Ok(()); + } + + let mut accounts = ctx.data().config.load_accounts(); + let accounts_path = ctx.data().config.full_accounts_path(); + + for account in accounts.iter_mut().filter(|a| a.quests_completed.contains(&id)) { + let index = account.quests_completed.iter().position(|qid| *qid == id).expect("We just filtered it"); + account.quests_completed.remove(index); + if let Err(error) = account.save(accounts_path.clone()) { + eprintln!("{error}"); + } + } + + let reply_string = format!("Successfully deleted quest #{id}"); + ctx.reply(reply_string).await?; + + Ok(()) +}