130 lines
3.8 KiB
Rust
130 lines
3.8 KiB
Rust
use std::{sync::Arc, time::Duration};
|
|
|
|
use clap::Parser;
|
|
use cli::Cli;
|
|
use config::Config;
|
|
|
|
use directus::{client::DirectusClient, model::Sandwich};
|
|
use poise::serenity_prelude as serenity;
|
|
|
|
mod cli;
|
|
mod commands;
|
|
mod config;
|
|
mod directus;
|
|
|
|
type Error = Box<dyn std::error::Error + Send + Sync>;
|
|
type Context<'a> = poise::Context<'a, Data, Error>;
|
|
|
|
pub struct Data {
|
|
pub sandwiches: Vec<Sandwich>,
|
|
pub directus: DirectusClient,
|
|
}
|
|
|
|
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
|
|
match error {
|
|
poise::FrameworkError::Setup { error, .. } => panic!("Failed to start bot: {error:?}"),
|
|
poise::FrameworkError::Command { error, ctx, .. } => {
|
|
println!("Error in command `{}`: {:?}", ctx.command().name, error,);
|
|
}
|
|
error => {
|
|
if let Err(e) = poise::builtins::on_error(error).await {
|
|
println!("Error while handling error: {e}")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let cli = Cli::parse();
|
|
|
|
let config_file_contents =
|
|
std::fs::read_to_string(cli.config).expect("failed to read config file!");
|
|
|
|
let config: Config =
|
|
toml::de::from_str(&config_file_contents).expect("failed to deserialize config file");
|
|
|
|
println!("{config:?}");
|
|
|
|
let options = poise::FrameworkOptions {
|
|
commands: vec![commands::ping::ping(), commands::sandwich::sandwich_list()],
|
|
prefix_options: poise::PrefixFrameworkOptions {
|
|
prefix: Some("!".into()),
|
|
edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan(
|
|
Duration::from_secs(3600),
|
|
))),
|
|
additional_prefixes: vec![
|
|
poise::Prefix::Literal("sandwichmeister,"),
|
|
poise::Prefix::Literal("sandwichmeister"),
|
|
poise::Prefix::Literal("SANDWICHMEISTER,"),
|
|
poise::Prefix::Literal("SANDWICHMEISTER"),
|
|
],
|
|
..Default::default()
|
|
},
|
|
// The global error handler for all error cases that may occur
|
|
on_error: |error| Box::pin(on_error(error)),
|
|
// This code is run before every command
|
|
pre_command: |ctx| {
|
|
Box::pin(async move {
|
|
println!("Executing command {}...", ctx.command().qualified_name);
|
|
})
|
|
},
|
|
// This code is run after a command if it was successful (returned Ok)
|
|
post_command: |ctx| {
|
|
Box::pin(async move {
|
|
println!("Executed command {}!", ctx.command().qualified_name);
|
|
})
|
|
},
|
|
// Every command invocation must pass this check to continue execution
|
|
// command_check: Some(|ctx| {
|
|
// Box::pin(async move {
|
|
// if ctx.author().id == 123456789 {
|
|
// return Ok(false);
|
|
// }
|
|
// Ok(true)
|
|
// })
|
|
// }),
|
|
// Enforce command checks even for owners (enforced by default)
|
|
// Set to true to bypass checks, which is useful for testing
|
|
skip_checks_for_owners: false,
|
|
event_handler: |_ctx, event, _framework, _data| {
|
|
Box::pin(async move {
|
|
println!(
|
|
"Got an event in event handler: {:?}",
|
|
event.snake_case_name()
|
|
);
|
|
Ok(())
|
|
})
|
|
},
|
|
..Default::default()
|
|
};
|
|
|
|
let framework = poise::Framework::builder()
|
|
.setup(move |ctx, _ready, framework| {
|
|
Box::pin(async move {
|
|
println!("Logged in as {}", _ready.user.name);
|
|
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
|
let directus = DirectusClient::new(config.directus);
|
|
let sandwiches = directus
|
|
.get_sandwiches()
|
|
.await
|
|
.expect("failed to load sandwiches from directus!");
|
|
|
|
Ok(Data {
|
|
directus,
|
|
sandwiches,
|
|
})
|
|
})
|
|
})
|
|
.options(options)
|
|
.build();
|
|
|
|
let intents =
|
|
serenity::GatewayIntents::non_privileged() | serenity::GatewayIntents::MESSAGE_CONTENT;
|
|
|
|
let client = serenity::ClientBuilder::new(config.discord.token, intents)
|
|
.framework(framework)
|
|
.await;
|
|
|
|
client.unwrap().start().await.unwrap();
|
|
}
|