feat: Added quest answers

- Added field pending_answers to DiscordConfig
- discord: added /answer
This commit is contained in:
Alexey 2025-12-10 13:06:56 +03:00
commit 3f7e6313b0
6 changed files with 82 additions and 13 deletions

View file

@ -0,0 +1,68 @@
use poise::serenity_prelude::{Attachment, CreateAttachment, CreateMessage, Mentionable};
use crate::{Context, Error};
#[poise::command(
prefix_command,
slash_command,
guild_only,
)]
pub async fn answer(
ctx: Context<'_>,
#[description = "Identifier of the quest to answer to"]
quest_id: u16,
#[description = "Text answer to the quest"]
text: Option<String>,
#[description = "Attachment answer to the quest"]
files: Vec<Attachment>,
) -> Result<(), Error> {
let quests = ctx.data().config.load_quests();
let Some(quest) = quests.iter().find(|q| q.id == quest_id) else {
let reply_string = format!("Quest #{quest_id} not found.");
ctx.reply(reply_string).await?;
return Ok(());
};
if text.is_none() && files.len() == 0 {
let reply_string = "Please specify text or at least one attachment.".to_string();
ctx.reply(reply_string).await?;
return Ok(());
}
let text_ans = match text {
Some(text) => format!("\n### Passed answer:\n{text}"),
None => String::new(),
};
let attachment_notice = if files.len() == 0 { String::new() } else {
"\nPassed answer has attachments.".to_string()
};
let content = format!("# From: {user}\n\
### Quest #{quest_id}: {quest_name}\n\
### Expected answer:\n\
||{quest_answer}||{text_ans}{attachment_notice}",
user = ctx.author().mention(),
quest_name = quest.name,
quest_answer = quest.answer,
);
let mut attachments: Vec<CreateAttachment> = Vec::new();
for file in files {
let attachment = CreateAttachment::url(ctx, &file.url).await?;
attachments.push(attachment);
}
let ans_channel = ctx.data().discord.answers_channel;
let message = CreateMessage::new()
.content(content)
.files(attachments);
ans_channel.send_message(ctx, message).await?;
let reply_string = "Your answer has been posted.".to_string();
ctx.reply(reply_string).await?;
Ok(())
}

View file

@ -26,7 +26,7 @@ pub async fn init(
dc.guild = guild; dc.guild = guild;
let path = &ctx.data().config.full_impl_path().unwrap(); let path = &ctx.data().config.full_impl_path().unwrap();
let reply_string = match dc.save(path.parent().unwrap_or(Path::new("")).to_owned()) { let reply_string = match dc.save(path.parent().unwrap_or(Path::new("")).to_owned()) {
Ok(_) => "Please restart bot to apply changes".to_string(), Ok(_) => "Settings updated, please restart bot to apply changes.".to_string(),
Err(error) => { Err(error) => {
eprintln!("{error}"); eprintln!("{error}");
ERROR_MSG.to_string() ERROR_MSG.to_string()

View file

@ -4,6 +4,7 @@ use crate::{Context, Error};
pub mod quest; pub mod quest;
pub mod init; pub mod init;
pub mod answer;
pub const ERROR_MSG: &str = "Server error :("; pub const ERROR_MSG: &str = "Server error :(";

View file

@ -2,9 +2,7 @@ use std::str::FromStr;
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}; use crate::{Context, Error, commands::ERROR_MSG};
const ERROR_MSG: &str = "Server error :(";
#[poise::command( #[poise::command(
prefix_command, prefix_command,

View file

@ -4,6 +4,15 @@ use poise::serenity_prelude::{ChannelId, GuildId, MessageId};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use squad_quest::{SquadObject, config::Config, error::Error}; use squad_quest::{SquadObject, config::Config, error::Error};
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct DiscordConfig {
pub guild: GuildId,
pub quests_channel: ChannelId,
pub answers_channel: ChannelId,
pub quests_messages: Vec<MessageId>,
pub pending_answers: Vec<MessageId>,
}
pub trait ConfigImpl { pub trait ConfigImpl {
fn discord_impl(&self) -> Result<DiscordConfig, Error>; fn discord_impl(&self) -> Result<DiscordConfig, Error>;
fn init_impl(&self) -> Result<(), Error>; fn init_impl(&self) -> Result<(), Error>;
@ -25,14 +34,6 @@ impl ConfigImpl for Config {
} }
} }
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct DiscordConfig {
pub guild: GuildId,
pub quests_channel: ChannelId,
pub answers_channel: ChannelId,
pub quests_messages: Vec<MessageId>,
}
impl SquadObject for DiscordConfig { impl SquadObject for DiscordConfig {
fn load(path: PathBuf) -> Result<Self, squad_quest::error::Error> { fn load(path: PathBuf) -> Result<Self, squad_quest::error::Error> {
match std::fs::read_to_string(path) { match std::fs::read_to_string(path) {

View file

@ -35,7 +35,8 @@ async fn main() {
commands: vec![ commands: vec![
commands::quest::quest(), commands::quest::quest(),
commands::register(), commands::register(),
commands::init::init() commands::init::init(),
commands::answer::answer(),
], ],
..Default::default() ..Default::default()
}) })