feat: Implementation config
- Bump version to 0.7.0 - Added Config::init_path - Added Error::IsNotImplemented - discord: added implementation config init/load - discord: added /init - discord: added /quest update
This commit is contained in:
parent
b92eaa1241
commit
520992187d
10 changed files with 147 additions and 12 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,4 +1,5 @@
|
|||
/target
|
||||
/cli/target
|
||||
/discord/target
|
||||
/discord/cfg
|
||||
.env
|
||||
|
|
|
|||
6
Cargo.lock
generated
6
Cargo.lock
generated
|
|
@ -1599,7 +1599,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "squad-quest"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml",
|
||||
|
|
@ -1607,7 +1607,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "squad-quest-cli"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"clap",
|
||||
|
|
@ -1618,7 +1618,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "squad-quest-discord"
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"dotenvy",
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
members = ["cli", "discord"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.6.0"
|
||||
version = "0.7.0"
|
||||
edition = "2024"
|
||||
repository = "https://2ndbeam.ru/git/2ndbeam/squad-quest"
|
||||
license = "MIT"
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ license.workspace = true
|
|||
chrono = "0.4.42"
|
||||
clap = { version = "4.5.53", features = ["derive"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
squad-quest = { version = "0.6.0", path = ".." }
|
||||
squad-quest = { version = "0.7.0", path = ".." }
|
||||
toml = "0.9.8"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ clap = { version = "4.5.53", features = ["derive"] }
|
|||
dotenvy = "0.15.7"
|
||||
poise = "0.6.1"
|
||||
serde = "1.0.228"
|
||||
squad-quest = { version = "0.6.0", path = ".." }
|
||||
squad-quest = { version = "0.7.0", path = ".." }
|
||||
tokio = { version = "1.48.0", features = ["rt-multi-thread"] }
|
||||
toml = "0.9.8"
|
||||
|
|
|
|||
38
discord/src/commands/init.rs
Normal file
38
discord/src/commands/init.rs
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
use std::path::Path;
|
||||
|
||||
use poise::serenity_prelude::{ChannelId};
|
||||
use squad_quest::SquadObject;
|
||||
|
||||
use crate::{commands::ERROR_MSG, Context, Error};
|
||||
|
||||
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
required_permissions = "ADMINISTRATOR",
|
||||
guild_only,
|
||||
)]
|
||||
pub async fn init(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Channel to post quests to"]
|
||||
quests_channel: ChannelId,
|
||||
#[description = "Channel to post answers to check"]
|
||||
answers_channel: ChannelId,
|
||||
) -> Result<(), Error> {
|
||||
let mut dc = ctx.data().discord.clone();
|
||||
let guild = ctx.guild_id().unwrap();
|
||||
dc.quests_channel = quests_channel;
|
||||
dc.answers_channel = answers_channel;
|
||||
dc.guild = guild;
|
||||
let path = &ctx.data().config.full_impl_path().unwrap();
|
||||
let reply_string = match dc.save(path.parent().unwrap_or(Path::new("")).to_owned()) {
|
||||
Ok(_) => "Please restart bot to apply changes".to_string(),
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
ERROR_MSG.to_string()
|
||||
},
|
||||
};
|
||||
ctx.reply(reply_string).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -3,7 +3,9 @@
|
|||
use crate::{Context, Error};
|
||||
|
||||
pub mod quest;
|
||||
pub mod init;
|
||||
|
||||
pub const ERROR_MSG: &str = "Server error :(";
|
||||
|
||||
#[poise::command(prefix_command)]
|
||||
pub async fn register(ctx: Context<'_>) -> Result<(), Error> {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const ERROR_MSG: &str = "Server error :(";
|
|||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
subcommands("list", "create"),
|
||||
subcommands("list", "create", "update"),
|
||||
)]
|
||||
pub async fn quest(
|
||||
_ctx: Context<'_>,
|
||||
|
|
@ -146,3 +146,82 @@ pub async fn create(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
required_permissions = "ADMINISTRATOR",
|
||||
guild_only,
|
||||
)]
|
||||
pub async fn update(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Quest identifier"]
|
||||
id: u16,
|
||||
#[description = "Quest difficulty"]
|
||||
difficulty: Option<DifficultyWrapper>,
|
||||
#[description = "Reward for the quest"]
|
||||
reward: Option<u32>,
|
||||
#[description = "Quest name"]
|
||||
name: Option<String>,
|
||||
#[description = "Quest description"]
|
||||
description: Option<String>,
|
||||
#[description = "Quest answer, visible to admins"]
|
||||
answer: Option<String>,
|
||||
#[description = "Date of publication (in format of YYYY-MM-DD, e.g. 2025-12-24)"]
|
||||
available: Option<DateWrapper>,
|
||||
#[description = "Quest deadline (in format of YYYY-MM-DD, e.g. 2025-12-24)"]
|
||||
deadline: Option<DateWrapper>,
|
||||
#[description = "Clear availability and deadline if checked"]
|
||||
#[rename = "override"]
|
||||
should_override: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
let conf = &ctx.data().config;
|
||||
let quests = conf.load_quests();
|
||||
let Some(quest) = quests.iter().find(|q| q.id == id) else {
|
||||
let reply_string = format!("Quest #{id} not found");
|
||||
ctx.reply(reply_string).await?;
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let difficulty = match difficulty {
|
||||
Some(d) => d.into(),
|
||||
None => quest.difficulty
|
||||
};
|
||||
|
||||
let available_on: Option<Date>;
|
||||
let dead_line: Option<Date>;
|
||||
|
||||
match should_override.unwrap_or(false) {
|
||||
true => {
|
||||
available_on = available.map(|v| v.into());
|
||||
dead_line = deadline.map(|v| v.into());
|
||||
},
|
||||
false => {
|
||||
available_on = available.map_or_else(|| quest.available_on.clone(), |v| Some(v.into()));
|
||||
dead_line = deadline.map_or_else(|| quest.deadline.clone(), |v| Some(v.into()));
|
||||
},
|
||||
}
|
||||
|
||||
let new_quest = Quest {
|
||||
id,
|
||||
difficulty,
|
||||
reward: reward.unwrap_or(quest.reward),
|
||||
name: name.unwrap_or(quest.name.clone()),
|
||||
description: description.unwrap_or(quest.description.clone()),
|
||||
answer: answer.unwrap_or(quest.answer.clone()),
|
||||
public: quest.public,
|
||||
available_on,
|
||||
deadline: dead_line,
|
||||
};
|
||||
|
||||
let path = conf.full_quests_path();
|
||||
let reply_string = match new_quest.save(path) {
|
||||
Err(error) => {
|
||||
eprintln!("{error}");
|
||||
ERROR_MSG.to_string()
|
||||
},
|
||||
Ok(_) => format!("Updated quest #{id}"),
|
||||
};
|
||||
ctx.reply(reply_string).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::{io::Write, path::PathBuf};
|
||||
use std::{io::Write, path::{Path, PathBuf}};
|
||||
|
||||
use poise::serenity_prelude::{ChannelId, GuildId, MessageId};
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
|
@ -6,18 +6,26 @@ use squad_quest::{SquadObject, config::Config, error::Error};
|
|||
|
||||
pub trait ConfigImpl {
|
||||
fn discord_impl(&self) -> Result<DiscordConfig, Error>;
|
||||
fn init_impl(&self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl ConfigImpl for Config {
|
||||
fn discord_impl(&self) -> Result<DiscordConfig, Error> {
|
||||
let Some(path) = &self.impl_path else {
|
||||
let Some(path) = &self.full_impl_path() else {
|
||||
return Err(Error::IsNotImplemented);
|
||||
};
|
||||
DiscordConfig::load(path.clone())
|
||||
}
|
||||
fn init_impl(&self) -> Result<(), Error> {
|
||||
let Some(path) = self.full_impl_path() else {
|
||||
return Err(Error::IsNotImplemented);
|
||||
};
|
||||
let dc = DiscordConfig::default();
|
||||
dc.save(path.parent().unwrap_or(Path::new("")).to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Serialize, Deserialize, Default, Clone)]
|
||||
pub struct DiscordConfig {
|
||||
pub guild: GuildId,
|
||||
pub quests_channel: ChannelId,
|
||||
|
|
|
|||
|
|
@ -22,14 +22,21 @@ async fn main() {
|
|||
|
||||
let cli = cli::Cli::parse();
|
||||
let config = Config::load(cli.config.clone());
|
||||
let discord = config.discord_impl().expect("config does not define impl_path");
|
||||
let discord = config.discord_impl().unwrap_or_else(|_| {
|
||||
config.init_impl().unwrap();
|
||||
config.discord_impl().unwrap()
|
||||
});
|
||||
|
||||
let token = std::env::var("DISCORD_TOKEN").expect("missing DISCORD_TOKEN");
|
||||
let intents = serenity::GatewayIntents::non_privileged();
|
||||
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
commands: vec![commands::quest::quest(), commands::register()],
|
||||
commands: vec![
|
||||
commands::quest::quest(),
|
||||
commands::register(),
|
||||
commands::init::init()
|
||||
],
|
||||
..Default::default()
|
||||
})
|
||||
.setup(|ctx, _ready, framework| {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue