feat: Added /quest delete command

- Also fixed formatting and sorting in /scoreboard
This commit is contained in:
Alexey 2025-12-11 17:27:19 +03:00
commit 38e3f149b1
2 changed files with 68 additions and 26 deletions

View file

@ -6,7 +6,7 @@ use crate::{Context, Error};
fn account_balance_string(account: &Account, map: &Map) -> String { fn account_balance_string(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!("{account}: **{full_balance}** points (**{balance}** on balance \ format!("\n{account}: **{full_balance}** points (**{balance}** on balance \
+ **{rooms_value}** unlocked rooms networth)", + **{rooms_value}** unlocked rooms networth)",
account = account_user_id(&account).mention(), account = account_user_id(&account).mention(),
balance = account.balance, balance = account.balance,
@ -48,12 +48,12 @@ pub async fn scoreboard(
accounts.sort_by(|a,b| { accounts.sort_by(|a,b| {
let a_balance = account_full_balance(a, &map); let a_balance = account_full_balance(a, &map);
let b_balance = account_full_balance(b, &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 this_user = ctx.author().id;
let mut reply_string = String::new(); let mut reply_string = "Current scoreboard:".to_string();
for account in accounts { for account in accounts {
let user_id = account_user_id(&account); let user_id = account_user_id(&account);

View file

@ -1,10 +1,29 @@
use std::{future, path::Path, str::FromStr}; 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 squad_quest::{SquadObject, quest::{Quest, QuestDifficulty}};
use toml::value::Date; use toml::value::Date;
use crate::{Context, Error, commands::ERROR_MSG}; use crate::{Context, Error, commands::ERROR_MSG};
async fn find_quest_message(ctx: Context<'_>, id: u16) -> Option<Message>{
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::<Vec<_>>().await;
messages.first().cloned()
}
fn make_quest_message_content(quest: &Quest) -> String { fn make_quest_message_content(quest: &Quest) -> String {
format!("### `#{id}` {name} (+{reward})\n\ format!("### `#{id}` {name} (+{reward})\n\
Difficulty: *{difficulty:?}*\n\ Difficulty: *{difficulty:?}*\n\
@ -20,7 +39,7 @@ fn make_quest_message_content(quest: &Quest) -> String {
#[poise::command( #[poise::command(
prefix_command, prefix_command,
slash_command, slash_command,
subcommands("list", "create", "update", "publish"), subcommands("list", "create", "update", "publish", "delete"),
)] )]
pub async fn quest( pub async fn quest(
_ctx: Context<'_>, _ctx: Context<'_>,
@ -229,28 +248,11 @@ pub async fn update(
let content = make_quest_message_content(&new_quest); let content = make_quest_message_content(&new_quest);
let builder = EditMessage::new().content(content); let builder = EditMessage::new().content(content);
let channel = { let message = find_quest_message(ctx, id);
let dc = ctx.data().discord.clone(); if let Some(mut message) = message.await {
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::<Vec<_>>().await;
messages.first().cloned()
};
if let Some(mut message) = message {
message.edit(ctx, builder).await?; message.edit(ctx, builder).await?;
} else { } else {
let reply_string = format!("Quest #{id} is public, but its message was not found in the {channel}", let reply_string = format!("Quest #{id} is public, but its message was not found in the quest channel",
channel = channel.mention()
); );
ctx.reply(reply_string).await?; ctx.reply(reply_string).await?;
} }
@ -336,3 +338,43 @@ pub async fn publish(
Ok(()) 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(())
}