From 79d284cd6daec93d03f026fff32baaeac35fe95e Mon Sep 17 00:00:00 2001 From: Lucy Bladen Date: Fri, 12 Nov 2021 12:05:26 +0000 Subject: [PATCH] Add files I missed --- .gitignore | 2 + Cargo.toml | 24 +++++++ src/lib.rs | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..98e29a2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "manifold" +version = "0.1.0" +authors = ["Lucy Bladen "] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[build-dependencies] +built = { version = "0.5.1", features = ["git2", "chrono"] } + +[dependencies] +built = { version = "0.5.1", features = ["git2", "chrono"] } +clap = "2.33.3" +diesel = { version = "1.4.8", features = ["sqlite", "r2d2", "chrono"] } +diesel_migrations = "1.4.0" +env_logger = "0.9.0" +log = "0.4.14" +r2d2 = "0.8.9" +rand = "0.8.4" +regex = "1.5.4" +serde_json = "1.0.69" +serenity = { version = "0.10", features = [ "collector" ] } +tokio = { version = "1.13.0", features = ["sync", "macros", "rt-multi-thread"] } diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..6ca0703 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,183 @@ +#[macro_use] extern crate log; + +use std::collections::HashSet; +use std::env; +use std::fs::File; +use std::io::stdout; +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}; +use crate::error::ManifoldResult; +use crate::events::Handler; +use crate::responses::Responses; + +pub mod config; +pub mod error; +pub mod responses; +pub mod events; + +type Pool = diesel::r2d2::Pool>; + +pub struct Db { + pool: Pool, +} + +impl Deref for Db { + type Target = Pool; + + fn deref(&self) -> &Self::Target { &self.pool } +} + +struct ManifoldDatabase; + +impl TypeMapKey for ManifoldDatabase { + type Value = Arc>; +} + +impl TypeMapKey for Responses { + type Value = Arc>; +} + +impl TypeMapKey for ManifoldConfig { + type Value = Arc>; +} + +pub async fn prepare_client(arguments: ArgMatches<'_>) -> 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)?} + } + } + } + + 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 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), + }; + + let framework = StandardFramework::new() + .configure(|c| c + .with_whitespace(true) + .on_mention(Some(bot_id)) + .prefix(&prefix) + .owners(owners) + .case_insensitivity(true) + ) + .before(before); + + let mut client = Client::builder(&token) + .event_handler(Handler::new()) + .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 +} + +#[command] +async fn ping(ctx: &Context, msg: &Message) -> CommandResult { + msg.reply_ping(ctx, "Pong!").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 +}