mod handlers; use crate::handlers::*; use anyhow::{Context as _, Result}; use clap::{value_parser, ArgAction, Parser}; use serde::Deserialize; use serenity::{ all::{ActivityData, GatewayIntents, GuildMemberUpdateEvent, Member, Ready}, async_trait, client::{Context, EventHandler}, Client, }; use std::{fs, path::PathBuf}; use stderrlog::StdErrLog; #[derive(Debug, Deserialize)] struct Config { discord_token: String, } #[derive(Debug, Parser)] struct Args { /// The path to the configuration file. config_path: PathBuf, /// Decreases the log level. #[clap( short, long, conflicts_with("verbose"), action = ArgAction::Count, value_parser = value_parser!(u8).range(..=2) )] quiet: u8, /// Increases the log level. #[clap( short, long, conflicts_with("quiet"), action = ArgAction::Count, value_parser = value_parser!(u8).range(..=3) )] verbose: u8, } #[tokio::main] async fn main() -> Result<()> { let args = Args::parse(); // Set up logging. { let mut logger = StdErrLog::new(); match args.quiet { 0 => logger.verbosity(1 + args.verbose as usize), 1 => logger.verbosity(0), 2 => logger.quiet(true), // UNREACHABLE: A maximum of two occurrences of quiet are allowed. _ => unreachable!(), }; // UNWRAP: No other logger should be set up. logger.show_module_names(true).init().unwrap() } let config_str = fs::read_to_string(&args.config_path) .with_context(|| format!("failed to read {}", args.config_path.display()))?; let config: Config = toml::from_str(&config_str) .with_context(|| format!("failed to parse {}", args.config_path.display()))?; drop(config_str); let handler = MultiHandler(vec![Box::new(PresenceSetter), Box::new(X500Mapper)]); let mut client = Client::builder( &config.discord_token, GatewayIntents::default() | GatewayIntents::GUILD_MEMBERS, ) .event_handler(handler) .await .context("failed to create Discord client")?; client .start() .await .context("failed to start Discord client")?; Ok(()) }