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; type Context<'a> = poise::Context<'a, Data, Error>; pub struct Data { pub sandwiches: Vec, 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(); }