use std::path::PathBuf; use reqwest::Method; use serenity::{ async_trait, model::{ gateway::Ready, interactions::{Interaction, InteractionResponseType}, }, prelude::*, }; use structopt::StructOpt; use geoffrey_models::logging::LogLevel; use geoffrey_models::models::parameters::{CommandRequest, EmptyRequest}; use geoffrey_models::models::player::UserID; use geoffrey_models::models::settings::GeoffreySettings; use crate::api::run_api_query; use crate::bot::formatters::clean_message; use crate::bot::{build_commands, CommandRunner}; use crate::configs::GeoffreyBotConfig; use crate::context::GeoffreyContext; use crate::logging::init_logging; mod api; mod bot; mod configs; mod context; mod error; mod logging; #[derive(Debug, StructOpt, Clone)] #[structopt(name = "GeoffreyBot", about = "Geoffrey Discord Bot")] struct Args { #[structopt(env = "GEOFFREY_BOT_CONFIG", parse(from_os_str))] config: PathBuf, #[structopt( short, long, env = "GEOFFREY_LOG_LEVEL", parse(from_str), default_value = "Info" )] log_level: LogLevel, } struct HttpClient; impl TypeMapKey for HttpClient { type Value = serenity::client::Client; } struct Settings; impl TypeMapKey for Settings { type Value = GeoffreySettings; } struct Handler; #[async_trait] impl EventHandler for Handler { async fn ready(&self, ctx: Context, ready: Ready) { log::info!("{} is connected!", ready.user.name); let mut data = ctx.data.write().await; let mut geoffrey_ctx = data.get_mut::().expect("Unable"); match run_api_query::( geoffrey_ctx, &CommandRequest { token: geoffrey_ctx.cfg.api.token.clone(), user_id: None, params: EmptyRequest {}, }, Method::GET, "model/settings/", ) .await { Ok(settings) => geoffrey_ctx.settings = settings, Err(err) => { log::warn!( "Unable to retrieve Geoffrey global settings, using defaults. Error: {}", err ) } } let command_runner = data .get_mut::() .expect("Unable to open command runner!"); match build_commands(&ctx, command_runner).await { Ok(_) => { log::debug!("Registered the following commands:"); for command in &command_runner.app_commands { log::debug!("{:?}", command) } } Err(e) => { log::warn!("Error registering commands: {:?}", e) } } log::info!("Init completed."); } async fn interaction_create(&self, ctx: Context, interaction: Interaction) { let data = ctx.data.read().await; let geoffrey_ctx = data.get::().unwrap(); let command_runner = data.get::().unwrap(); if let Interaction::ApplicationCommand(command) = interaction { let user_id = UserID::DiscordUUID { discord_uuid: command.user.id.0, }; let command_name = command.data.name.clone(); let msg = match command_runner .run_command( &command_name, geoffrey_ctx.clone(), user_id, command.clone(), ) .await { Ok(msg) => clean_message(ctx.cache, &msg).await, Err(e) => { log::warn!("Error running command '{}': {:?}", command_name, e); return; } }; command .create_interaction_response(&ctx.http, |resp| { resp.kind(InteractionResponseType::ChannelMessageWithSource) .interaction_response_data(|message| message.content(msg)) }) .await .unwrap() } else if let Interaction::Autocomplete(auto_complete) = interaction { auto_complete .create_autocomplete_response(&ctx.http, |resp| resp) .await .unwrap() } else if let Interaction::MessageComponent(msg) = interaction { msg.create_interaction_response(&ctx.http, |resp| resp) .await .unwrap() } else if let Interaction::Ping(_) = interaction { // do nothing } } } #[tokio::main] async fn main() { let args: Args = Args::from_args(); init_logging(args.log_level).expect("Unable to init logging"); let cfg = match GeoffreyBotConfig::new(args.config.as_path()) { Ok(cfg) => cfg, Err(e) => { log::warn!("Error opening config: {}", e); return; } }; let client = Client::builder(cfg.discord.token.clone()) .event_handler(Handler) .application_id(cfg.discord.app_id) .await; let mut client = match client { Ok(client) => client, Err(e) => { log::warn!("Unable to init serenity client: {}", e); return; } }; // Block for initializing global data { let mut data = client.data.write().await; data.insert::(GeoffreyContext { http_client: reqwest::Client::new(), cfg, settings: GeoffreySettings::default(), }); data.insert::(CommandRunner::default()); } if let Err(e) = client.start().await { log::warn!("Client error: {:?}", e); } }