#[macro_use] extern crate log; #[macro_use] extern crate serde; use std::collections::HashSet; use std::env; use std::fs::File; use std::ops::Deref; use std::path::PathBuf; use std::sync::Arc; use clap::ArgMatches; use diesel::r2d2::{ConnectionManager}; use diesel::SqliteConnection; use crate::config::{Config, ManifoldConfig}; use serenity::prelude::*; use serenity::framework::standard::{*, macros::*}; use serenity::http::Http; use serenity::model::prelude::{GuildId, Message, UserId}; use crate::error::ManifoldResult; use crate::responses::Responses; pub mod config; pub mod error; pub mod responses; pub mod events; pub mod core_commands; // Retrieve build info from output file pub mod built_info { include!(concat!(env!("OUT_DIR"), "/built.rs")); } use crate::core_commands::*; pub type ManifoldDatabasePool = diesel::r2d2::Pool>; pub struct Db { pub pool: ManifoldDatabasePool, } impl Deref for Db { type Target = ManifoldDatabasePool; fn deref(&self) -> &Self::Target { &self.pool } } pub struct ManifoldDatabase; impl TypeMapKey for ManifoldDatabase { type Value = Arc>; } impl TypeMapKey for Responses { type Value = Arc>; } impl TypeMapKey for ManifoldConfig { type Value = Arc>; } #[group] #[commands(ping, set_config, get_config, version, set_activity, get_environment)] struct Core; pub async fn prepare_client(arguments: ArgMatches, mut framework: StandardFramework, event_handler: T) -> ManifoldResult { let bot_environment = arguments.value_of("environment").unwrap_or("Production").to_string(); let config_file = format!("config/{}", arguments.value_of("config-file").unwrap_or("manifold.json")); if arguments.occurrences_of("make-config") > 0 { if File::open(&config_file).is_ok() { warn!("Not making a new configuration file - configuration exists at {}. Please remove this if you want to generate a fresh config, or provide an alternative path.", &config_file); Err("ConfigExists")? } else { let config = ManifoldConfig::new(); match config.create_default_config(&config_file, &bot_environment) { Ok(_) => warn!("Writing new configuration to {}", &config_file), Err(e) => {error!("Could not write new config! {}", e.details); Err(e)?} } } Err("MadeConfig")? } info!("Reading configuration..."); debug!("Configuration file path: {}", &config_file); let config = load_config(&config_file, &bot_environment); debug!("Current configuration: {:?}", &config); let prefix = config.get_value(&"BotPrefix".to_string()).unwrap(); let manager = ConnectionManager::::new("manifold.db"); let pool = r2d2::Pool::builder() .max_size(1) .build(manager) .expect("Database setup error!"); let mut responses = Responses::new(); responses.reload(&PathBuf::from(config.get_value(&"ResponsesFilePath".to_string()).unwrap_or(&"txt/responses.txt".to_string()))).expect("Could not load responses file!"); let token = env::var("DISCORD_TOKEN").expect( "Could not find an environment variable called DISCORD_TOKEN", ); let application_id: u64 = env::var("APPLICATION_ID").expect( "Could not find an application ID in the APPLICATION_ID environment variable, please provide one", ).parse().expect("That wasn't a number"); let http = Http::new_with_token(&token); let (owners, bot_id) = match http.get_current_application_info().await { Ok(info) => { let mut owners = HashSet::new(); owners.insert(info.owner.id); (owners, info.id) } Err(why) => panic!("Could not get HTTP application information - exiting: {:?}", why), }; framework = framework.configure(|c| c .with_whitespace(true) .on_mention(Some(bot_id)) .prefix(&prefix) .owners(owners) .case_insensitivity(true) ).before(before) .help(&MANIFOLD_HELP); framework.group_add(&CORE_GROUP); let client = Client::builder(&token) .event_handler(event_handler) .application_id(application_id) .framework(framework) .await .expect("Error creating client!"); { let mut data = client.data.write().await; let db = Db { pool }; data.insert::(Arc::new(Mutex::new(config))); data.insert::(Arc::new(Mutex::new(db))); data.insert::(Arc::new(Mutex::new(responses))); } Ok(client) } fn load_config(config_file: &String, bot_environment: &String) -> ManifoldConfig { let mut config = ManifoldConfig::new(); *config.get_path_mut() = PathBuf::from(config_file); *config.get_environment_mut() = bot_environment.to_owned(); config.load_config().expect("Could not load config - halting"); config } #[help] async fn manifold_help( ctx: &Context, msg: &Message, args: Args, help_options: &'static HelpOptions, groups: &[&'static CommandGroup], owners: HashSet ) -> CommandResult { let _ = help_commands::with_embeds(ctx, msg, args, help_options, groups, owners).await; Ok(()) } #[check] #[name = "ModOrHigher"] async fn mod_or_higher_check(ctx: &Context, msg: &Message, _: &mut Args, _: &CommandOptions) -> Result<(), Reason> { let data = ctx.data.read().await; let config = match data.get::() { Some(c) => c.lock().await, None => return Err(Reason::Log("Couldn't lock config".to_string())) }; let role_guild_config = match config.get_value(&"RoleGuild".to_string()) { Some(rgc) => rgc, None => { error!("Guild not configured"); return Err(Reason::Unknown); } }; let role_guild_number = match role_guild_config.parse::() { Ok(rgn) => rgn, Err(e) => { error!("Parse GuildId fail"); return Err(Reason::Log(e.to_string())); } }; let guild_id = GuildId(role_guild_number); let administrator_role = config.get_role(&"Admin".to_string()); if !msg.author.has_role(&ctx, guild_id, administrator_role).await.unwrap_or(false) { return Err(Reason::User("User not administrative".to_string())); } Ok(()) } #[hook] async fn before(_: &Context, msg: &Message, command_name: &str) -> bool { info!("Received command '{}' from user '{}'", command_name, msg.author.name); true }