diff --git a/.gitignore b/.gitignore index 97d3f1d..472dc8e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ Conf.toml /data /log -join_audio.mp3 \ No newline at end of file +*.mp3 +.env \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index d4460f1..b4afc3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" +checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" [[package]] name = "arc-swap" @@ -91,6 +91,17 @@ dependencies = [ "webpki-roots 0.21.0", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "audiopus" version = "0.2.0" @@ -300,6 +311,39 @@ dependencies = [ "syn", ] +[[package]] +name = "diesel" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b28135ecf6b7d446b43e27e225622a038cc4e2930a1022f51cdb97ada19b8e4d" +dependencies = [ + "bitflags", + "byteorder", + "diesel_derives", + "pq-sys", +] + +[[package]] +name = "diesel_derives" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f5098f628d02a7a0f68ddba586fb61e80edec3bdc1be3b921f4ceec60858d3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf3cde8413353dc7f5d72fa8ce0b99a560a359d2c5ef1e5817ca731cd9008f4c" +dependencies = [ + "migrations_internals", + "migrations_macros", +] + [[package]] name = "digest" version = "0.9.0" @@ -343,6 +387,19 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "env_logger" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + [[package]] name = "flate2" version = "1.0.20" @@ -827,6 +884,27 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "migrations_internals" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b4fc84e4af020b837029e017966f86a1c2d5e83e64b589963d5047525995860" +dependencies = [ + "diesel", +] + +[[package]] +name = "migrations_macros" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9753f12909fd8d923f75ae5c3258cae1ed3c8ec052e1b38c93c21a6d157f789c" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "mime" version = "0.3.16" @@ -1135,6 +1213,15 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +[[package]] +name = "pq-sys" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" +dependencies = [ + "vcpkg", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -1361,14 +1448,34 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb5d2a036dc6d2d8fd16fde3498b04306e29bd193bf306a57427019b823d5acd" +[[package]] +name = "rusty-bot-convert-data" +version = "0.1.0" +dependencies = [ + "anyhow", + "diesel", + "env_logger", + "log", + "rusty-bot-database", + "serde", + "serde_json", +] + [[package]] name = "rusty-bot-database" version = "0.1.0" +dependencies = [ + "diesel", + "diesel_migrations", + "log", + "thiserror", +] [[package]] name = "rusty_bot" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "chrono", "ctrlc", @@ -1378,6 +1485,7 @@ dependencies = [ "log4rs", "rand 0.8.3", "reqwest", + "rusty-bot-database", "serde", "serde_json", "serenity", @@ -1738,19 +1846,28 @@ dependencies = [ ] [[package]] -name = "thiserror" -version = "1.0.24" +name = "termcolor" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0f4a65597094d4483ddaed134f409b2cb7c1beccf25201a9f73c719254fa98e" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.24" +version = "1.0.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7765189610d8241a44529806d6fd1f2e0a08734313a35d5b3a556f92b381f3c0" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" dependencies = [ "proc-macro2", "quote", @@ -2239,6 +2356,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index c086aeb..aaa691d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ "rusty-bot", - "rusty-bot-database" + "rusty-bot-database", + "rusty-bot-convert-data" ] \ No newline at end of file diff --git a/log4rs.yaml b/log4rs.yaml index 794b89c..cd2e091 100644 --- a/log4rs.yaml +++ b/log4rs.yaml @@ -11,4 +11,6 @@ root: - file-debug loggers: rusty_bot: + level: trace + rusty_bot_database: level: trace \ No newline at end of file diff --git a/rusty-bot-convert-data/Cargo.toml b/rusty-bot-convert-data/Cargo.toml new file mode 100644 index 0000000..3502238 --- /dev/null +++ b/rusty-bot-convert-data/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "rusty-bot-convert-data" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +rusty-bot-database = { path = "../rusty-bot-database" } +log = "0.4.14" +env_logger = "0.9.0" +diesel = { version = "1.4", features = ["postgres"] } \ No newline at end of file diff --git a/rusty-bot-convert-data/src/main.rs b/rusty-bot-convert-data/src/main.rs new file mode 100644 index 0000000..0fd5ec9 --- /dev/null +++ b/rusty-bot-convert-data/src/main.rs @@ -0,0 +1,64 @@ +use std::{env::args, path::Path}; + +use anyhow::Context; +use diesel::{query_builder::AsQuery, RunQueryDsl}; +use env_logger::Env; +use rusty_bot_database::{establish_connection, models::NewSettings}; + +mod models; + +fn main() -> anyhow::Result<()> { + env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); + + let mut args = args().skip(1); + + let data_path = args.next().expect("missing data path"); + let data_path = Path::new(&data_path); + let database_url = args.next().expect("missing database url"); + + let conn = establish_connection(&database_url)?; + + let guild_options_path = data_path.join("guilds_options"); + for entry in std::fs::read_dir(&guild_options_path) + .with_context(move || format!("failed to open {:?}", guild_options_path))? + .filter_map(|e| e.ok()) + { + match serde_json::from_reader::(std::fs::File::open( + entry.path(), + )?) { + Ok(options) => { + let id = entry + .file_name() + .to_string_lossy() + .split('.') + .next() + .unwrap() + .parse::() + .unwrap(); + + let new_settings = NewSettings::new( + id, + options.mute_id, + Some(options.roulette_options.kick_enabled), + options.mention_log_channel, + ); + + if let Err(e) = diesel::insert_into(rusty_bot_database::schema::settings::table) + .values(&new_settings) + .as_query() + .execute(&conn) + .context("failed to instart data") + { + println!("{:?}", e); + } else { + log::info!("inserted"); + } + } + Err(e) => { + log::error!("While parsing guild option {}", e); + } + } + } + + Ok(()) +} diff --git a/rusty-bot-convert-data/src/models.rs b/rusty-bot-convert-data/src/models.rs new file mode 100644 index 0000000..bf4d09f --- /dev/null +++ b/rusty-bot-convert-data/src/models.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct GuildOptions { + pub(crate) mute_id: Option, + pub(crate) mention_log_channel: Option, + pub(crate) roulette_options: RouletteOptions, +} + +#[derive(Debug, Serialize, Deserialize)] +pub(crate) struct RouletteOptions { + pub(crate) kick_enabled: bool, +} diff --git a/rusty-bot-database/Cargo.toml b/rusty-bot-database/Cargo.toml index 5d6a58e..ef6b975 100644 --- a/rusty-bot-database/Cargo.toml +++ b/rusty-bot-database/Cargo.toml @@ -6,3 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +diesel = { version = "1.4", features = ["postgres"] } +thiserror = "1.0" +log = "0.4" +diesel_migrations = "1.4" \ No newline at end of file diff --git a/rusty-bot-database/README.md b/rusty-bot-database/README.md new file mode 100644 index 0000000..5cf2fc3 --- /dev/null +++ b/rusty-bot-database/README.md @@ -0,0 +1,5 @@ +# RustyBot Database + +## TODO + +![Screenshot](schema/database.svg) \ No newline at end of file diff --git a/rusty-bot-database/diesel.toml b/rusty-bot-database/diesel.toml new file mode 100644 index 0000000..92267c8 --- /dev/null +++ b/rusty-bot-database/diesel.toml @@ -0,0 +1,5 @@ +# For documentation on how to configure this file, +# see diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/schema.rs" diff --git a/rusty-bot-database/migrations/.gitkeep b/rusty-bot-database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/rusty-bot-database/migrations/00000000000000_diesel_initial_setup/down.sql b/rusty-bot-database/migrations/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 0000000..a9f5260 --- /dev/null +++ b/rusty-bot-database/migrations/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,6 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/rusty-bot-database/migrations/00000000000000_diesel_initial_setup/up.sql b/rusty-bot-database/migrations/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 0000000..d68895b --- /dev/null +++ b/rusty-bot-database/migrations/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/rusty-bot-database/migrations/2022-03-12-100614_create_tables/down.sql b/rusty-bot-database/migrations/2022-03-12-100614_create_tables/down.sql new file mode 100644 index 0000000..579c854 --- /dev/null +++ b/rusty-bot-database/migrations/2022-03-12-100614_create_tables/down.sql @@ -0,0 +1,13 @@ +-- This file should undo anything in `up.sql` + +drop table if exists music_info; + +drop table if exists music_session_list; + +drop table if exists music; + +drop table if exists music_session; + +drop table if exists settings; + + diff --git a/rusty-bot-database/migrations/2022-03-12-100614_create_tables/up.sql b/rusty-bot-database/migrations/2022-03-12-100614_create_tables/up.sql new file mode 100644 index 0000000..4dfa621 --- /dev/null +++ b/rusty-bot-database/migrations/2022-03-12-100614_create_tables/up.sql @@ -0,0 +1,53 @@ +CREATE TABLE MUSIC +( + musicID SERIAL NOT NULL, + musicSourceURL TEXT NOT NULL, + musicUserRequesterID BIGINT NOT NULL, -- TO BE TRANSMUTED TO U64 ? + PRIMARY KEY (musicid) +); + +CREATE TABLE MUSIC_INFO +( + musicInfoSourceURL TEXT NOT NULL, + musicInfoTitle TEXT NOT NULL, + musicInfoThumbnailURL TEXT, + musicInfoLength INTEGER NOT NULL, + musicInfoMusicID INTEGER NOT NULL, + PRIMARY KEY (musicInfoSourceURL) +); + +CREATE TABLE MUSIC_SESSION +( + musicSessionID SERIAL NOT NULL, + musicSessionCurrentPlayingIndex SMALLINT DEFAULT 0, -- START FROM 0 + musicSessionGuildID BIGINT NOT NULL, + musicSessionTextChannelID BIGINT NOT NULL, + musicSessionPlayingChannelID BIGINT NOT NULL, + PRIMARY KEY (musicsessionid) +); + +CREATE TABLE MUSIC_SESSION_LIST +( + musicSessionID INTEGER NOT NULL, + musicID INTEGER NOT NULL, + musicSessionListIndex SMALLINT NOT NULL, + PRIMARY KEY (musicSessionID, musicID, musicSessionListIndex) +); + +CREATE TABLE SETTINGS +( + settingsGuildID BIGINT NOT NULL, + settingsMuteRoleID BIGINT, + settingsRouletteKickEnabled BOOLEAN DEFAULT FALSE, + settingsLogChannelID BIGINT, + PRIMARY KEY (settingsGuildID) +); + +ALTER TABLE MUSIC_INFO + ADD FOREIGN KEY (musicInfoMusicID) REFERENCES MUSIC (musicID); + +ALTER TABLE MUSIC_SESSION_LIST + ADD FOREIGN KEY (musicID) REFERENCES MUSIC (musicID); + +ALTER TABLE MUSIC_SESSION_LIST + ADD FOREIGN KEY (musicSessionID) REFERENCES MUSIC_SESSION (musicSessionID); \ No newline at end of file diff --git a/rusty-bot-database/schema/database.svg b/rusty-bot-database/schema/database.svg new file mode 100644 index 0000000..ce7a3f8 --- /dev/null +++ b/rusty-bot-database/schema/database.svg @@ -0,0 +1,244 @@ + + + + \n\n + Généré par Mocodo 2.3.7 le Tue, 15 Mar 2022 13:53:31 + + + + + + 0,N + + 0,1 + + + + + + + PLAYING_ON_MUSIC_SESSION + + + + + 0,1 + + + 1,1 + + + + + + LOG_CHANNEL + + + + + 1,1 + + + 0,1 + + + + + + TEXT_CHANNEL + + + + + 0,1 + + + 0,N + + + + + + MUSIC_METADATAS + + + + + 1,1 + + + 0,N + + + + + + MUSIC_REQUESTED_BY + + + + + 0,1 + + 1,1 + + + + + + + SETTINGS_OF_GUILD + + + + + 0,1 + + 1,1 + + + + + + + PLAYING_ON_GUILD + + + + + 0,1 + + 1,1 + + + + + + + PLAYING_ON_CHANNEL + + + + + 1,1 + + 0,1 + + + + + + + MUTE_ROLE + + + + + + + + + + + GUILD + guildID + + + + + + + + + + + + SETTINGS + settingsID + + rouletteKickEnabled + + + + + + + + + + + DISCORD_USER + userId + + + + + + + + + + + + MUSIC_INFO + musicSourceURL + + musicTitle + musicThumbnailURL + musicLength + + + + + + + + + + + MUSIC + musicID + + musicSourceURL + + + + + + + + + + + ROLE + roleID + + + + + + + + + + + + MUSIC_SESSION + musicSessionID + + currentPlayingIndex + + + + + + + + + + + CHANNEL + channelID + + + \ No newline at end of file diff --git a/rusty-bot-database/schema/mocodo.txt b/rusty-bot-database/schema/mocodo.txt new file mode 100644 index 0000000..420f332 --- /dev/null +++ b/rusty-bot-database/schema/mocodo.txt @@ -0,0 +1,21 @@ +DISCORD_USER: userId +PLAYING_ON_MUSIC_SESSION, 0N MUSIC, 01> MUSIC_SESSION +MUSIC_SESSION: musicSessionID, currentPlayingIndex +TEXT_CHANNEL, 11> CHANNEL, 01 MUSIC_SESSION +CHANNEL: channelID + +MUSIC_REQUESTED_BY, 11> DISCORD_USER, 0N MUSIC +MUSIC: musicID, musicSourceURL +PLAYING_ON_GUILD, 01 MUSIC_SESSION, 11> GUILD +PLAYING_ON_CHANNEL, 01 MUSIC_SESSION, 11> CHANNEL +LOG_CHANNEL, 01> CHANNEL, 11 SETTINGS + +MUSIC_INFO: musicSourceURL, musicTitle, musicThumbnailURL, musicLength +MUSIC_METADATAS, 01> MUSIC_INFO, 0N MUSIC +GUILD: guildID +SETTINGS_OF_GUILD, 01 SETTINGS, 11> GUILD +SETTINGS: settingsID, rouletteKickEnabled + +::: +ROLE: roleID +MUTE_ROLE, 11 SETTINGS, 01> ROLE \ No newline at end of file diff --git a/rusty-bot-database/src/error.rs b/rusty-bot-database/src/error.rs new file mode 100644 index 0000000..7264e1a --- /dev/null +++ b/rusty-bot-database/src/error.rs @@ -0,0 +1,23 @@ +use thiserror::Error; + +#[derive(Error, Debug, PartialEq)] +pub enum Error { + #[error("Failed to fetch")] + Database(#[from] diesel::result::Error), + #[error("Failed to connect to db")] + Connection(#[from] diesel::result::ConnectionError), + #[error("Failed to run migrations")] + Migrations(#[from] diesel_migrations::RunMigrationsError) +} + +impl Error { + pub fn is_not_found_error(&self) -> bool { + if let Error::Database(e) = self { + e == &diesel::result::Error::NotFound + } else { + false + } + } +} + +pub type Result = std::result::Result; diff --git a/rusty-bot-database/src/lib.rs b/rusty-bot-database/src/lib.rs index 59aeef4..206e349 100644 --- a/rusty-bot-database/src/lib.rs +++ b/rusty-bot-database/src/lib.rs @@ -1 +1,24 @@ -// KEEP \ No newline at end of file +#[macro_use] +pub extern crate diesel; + +#[macro_use] +extern crate diesel_migrations; + +use diesel::prelude::*; + +pub mod error; +pub mod models; +pub mod queries; +pub mod schema; + +use error::*; + +diesel_migrations::embed_migrations!(); + +pub fn establish_connection(database_url: &str) -> Result { + let conn = PgConnection::establish(&database_url)?; + + embedded_migrations::run(&conn)?; + + Ok(conn) +} diff --git a/rusty-bot-database/src/models.rs b/rusty-bot-database/src/models.rs new file mode 100644 index 0000000..9d3d1e0 --- /dev/null +++ b/rusty-bot-database/src/models.rs @@ -0,0 +1,78 @@ +#[derive(Queryable, Debug)] +pub struct Music { + pub id: i32, + pub music_source_url: String, + pub music_requester_id: i64, +} + +#[derive(Queryable, Debug)] +pub struct MusicInfo { + pub source_url: String, + pub info_title: Option, + pub thumbnail_url: Option, + pub length: i32, + pub music_id: i64, +} + +// TODO FIX SCHEMA DIR +#[derive(Queryable, Debug)] +pub struct MusicSession { + pub id: i32, + pub current_playing_index: Option, + pub guild_id: i64, + pub text_channel_id: i64, + pub playing_channel_id: i64, +} + +#[derive(Queryable, Debug)] +pub struct MusicSessionList { + pub session_id: i32, + pub music_id: i32, + pub index: i16, +} + +#[derive(Queryable, Debug)] +pub struct Settings { + pub guild_id: i64, + pub mute_role_id: Option, + pub roulette_kick_enabled: Option, + pub log_channel_id: Option, +} + +impl Settings { + pub fn get_mute_role_id(&self) -> Option { + self.mute_role_id + .map(|id| unsafe { std::mem::transmute(id) }) + } +} + +use super::schema::settings; + +#[derive(Insertable)] +#[table_name = "settings"] +pub struct NewSettings { + #[column_name = "settingsguildid"] + pub guild_id: i64, + #[column_name = "settingsmuteroleid"] + pub mute_role_id: Option, + #[column_name = "settingsroulettekickenabled"] + pub roulette_kick_enabled: Option, + #[column_name = "settingslogchannelid"] + pub log_channel_id: Option, +} + +impl NewSettings { + pub fn new( + guild_id: u64, + mute_role_id: Option, + roulette_kick_enabled: Option, + log_channel_id: Option, + ) -> Self { + Self { + guild_id: unsafe { std::mem::transmute(guild_id) }, + mute_role_id: mute_role_id.map(|f| unsafe { std::mem::transmute(f) }), + roulette_kick_enabled, + log_channel_id: log_channel_id.map(|f| unsafe { std::mem::transmute(f) }), + } + } +} diff --git a/rusty-bot-database/src/queries.rs b/rusty-bot-database/src/queries.rs new file mode 100644 index 0000000..5aa0bb9 --- /dev/null +++ b/rusty-bot-database/src/queries.rs @@ -0,0 +1,141 @@ +use diesel::prelude::*; +use diesel::{QueryDsl, RunQueryDsl}; + +use crate::models::{NewSettings, Settings}; +use crate::schema; + +use crate::Result; + +pub trait RustyConnectionExt { + fn fetch_settings(&self, guild_id: u64) -> Result; + fn settings_edit_kick(&self, guild_id: u64, disable: bool) -> Result<()>; + fn settings_fetch_mute_role(&self, guild_id: u64) -> Result>; + fn settings_edit_mute_role(&self, guild_id: u64, mute_role: Option) -> Result<()>; + fn settings_fetch_log_channel(&self, guild_id: u64) -> Result>; + fn settings_edit_log_channel(&self, guild_id: u64, channel: Option) -> Result<()>; + fn insert_settings(&self, settings: NewSettings) -> Result<()>; +} + +impl RustyConnectionExt for PgConnection { + fn fetch_settings(&self, guild_id: u64) -> Result { + use schema::settings::dsl::*; + + let id = unsafe { std::mem::transmute::(guild_id) }; + + let res = settings + .filter(settingsguildid.eq(id)) + .first::(self)?; + + Ok(res) + } + + fn settings_edit_kick(&self, guild_id: u64, disable: bool) -> Result<()> { + use schema::settings::dsl::*; + + let id = unsafe { std::mem::transmute::(guild_id) }; + + if diesel::update(settings) + .set(settingsroulettekickenabled.eq(!disable)) + .filter(settingsguildid.eq(id)) + .execute(self)? + == 0 + { + log::error!("inserting"); + diesel::insert_into(super::schema::settings::table) + .values(&NewSettings::new(guild_id, None, Some(!disable), None)) + .execute(self)?; + } + + Ok(()) + } + + fn settings_edit_mute_role(&self, guild_id: u64, mute_role: Option) -> Result<()> { + use schema::settings::dsl::*; + + let id = unsafe { std::mem::transmute::(guild_id) }; + let mute_role = mute_role.map(|id| unsafe { std::mem::transmute::(id) }); + + if diesel::update(settings) + .set(settingsmuteroleid.eq(mute_role)) + .filter(settingsguildid.eq(id)) + .execute(self)? + == 0 + { + diesel::insert_into(super::schema::settings::table) + .values(&NewSettings { + guild_id: id, + mute_role_id: mute_role, + roulette_kick_enabled: None, + log_channel_id: None, + }) + .execute(self)?; + } + + Ok(()) + } + + fn settings_fetch_mute_role(&self, guild_id: u64) -> Result> { + use schema::settings::dsl::*; + + let id: QueryResult> = settings + .select(settingsmuteroleid) + .filter(settingsguildid.eq(unsafe { std::mem::transmute::(guild_id) })) + .get_result(self); + + if id == Err(diesel::result::Error::NotFound) { + Ok(None) + } else { + let id = id?; + Ok(id.map(|id| unsafe { std::mem::transmute(id) })) + } + } + + fn settings_fetch_log_channel(&self, guild_id: u64) -> Result> { + use schema::settings::dsl::*; + + let id: QueryResult> = settings + .select(settingslogchannelid) + .filter(settingsguildid.eq(unsafe { std::mem::transmute::(guild_id) })) + .get_result(self); + + if id == Err(diesel::result::Error::NotFound) { + Ok(None) + } else { + let id = id?; + Ok(id.map(|id| unsafe { std::mem::transmute(id) })) + } + } + + fn settings_edit_log_channel(&self, guild_id: u64, channel: Option) -> Result<()> { + use schema::settings::dsl::*; + + let id = unsafe { std::mem::transmute::(guild_id) }; + let channel = channel.map(|id| unsafe { std::mem::transmute::(id) }); + + if diesel::update(settings) + .set(settingslogchannelid.eq(channel)) + .filter(settingsguildid.eq(id)) + .execute(self)? + == 0 + { + diesel::insert_into(super::schema::settings::table) + .values(&NewSettings { + guild_id: id, + mute_role_id: None, + roulette_kick_enabled: None, + log_channel_id: channel, + }) + .execute(self)?; + } + + Ok(()) + } + + fn insert_settings(&self, settings: NewSettings) -> Result<()> { + diesel::insert_into(super::schema::settings::table) + .values(&settings) + .execute(self)?; + + Ok(()) + } +} diff --git a/rusty-bot-database/src/schema.rs b/rusty-bot-database/src/schema.rs new file mode 100644 index 0000000..aebfafd --- /dev/null +++ b/rusty-bot-database/src/schema.rs @@ -0,0 +1,56 @@ +table! { + music (musicid) { + musicid -> Int4, + musicsourceurl -> Text, + musicuserrequesterid -> Int8, + } +} + +table! { + music_info (musicinfosourceurl) { + musicinfosourceurl -> Text, + musicinfotitle -> Text, + musicinfothumbnailurl -> Nullable, + musicinfolength -> Int4, + musicinfomusicid -> Int4, + } +} + +table! { + music_session (musicsessionid) { + musicsessionid -> Int4, + musicsessioncurrentplayingindex -> Nullable, + musicsessionguildid -> Int8, + musicsessiontextchannelid -> Int8, + musicsessionplayingchannelid -> Int8, + } +} + +table! { + music_session_list (musicsessionid, musicid, musicsessionlistindex) { + musicsessionid -> Int4, + musicid -> Int4, + musicsessionlistindex -> Int2, + } +} + +table! { + settings (settingsguildid) { + settingsguildid -> Int8, + settingsmuteroleid -> Nullable, + settingsroulettekickenabled -> Nullable, + settingslogchannelid -> Nullable, + } +} + +joinable!(music_info -> music (musicinfomusicid)); +joinable!(music_session_list -> music (musicid)); +joinable!(music_session_list -> music_session (musicsessionid)); + +allow_tables_to_appear_in_same_query!( + music, + music_info, + music_session, + music_session_list, + settings, +); diff --git a/rusty-bot/Cargo.toml b/rusty-bot/Cargo.toml index 8c661ca..b14bed2 100644 --- a/rusty-bot/Cargo.toml +++ b/rusty-bot/Cargo.toml @@ -3,6 +3,7 @@ name = "rusty_bot" version = "0.1.0" authors = ["oupson"] edition = "2018" +default-run = "rusty_bot" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -25,3 +26,5 @@ log = "0.4" log4rs = "1.0" ctrlc = "3.1" songbird = { version = "0.2", features = ["driver", "builtin-queue", "yt-dlp"], optional = true } +rusty-bot-database = { path = "../rusty-bot-database" } +anyhow = "1.0.56" \ No newline at end of file diff --git a/rusty-bot/src/commands/interaction.rs b/rusty-bot/src/commands/interaction.rs index 7368bda..ff0218f 100644 --- a/rusty-bot/src/commands/interaction.rs +++ b/rusty-bot/src/commands/interaction.rs @@ -1,3 +1,4 @@ +use rusty_bot_database::queries::RustyConnectionExt; use serenity::{ client::Context, framework::standard::CommandResult, @@ -14,7 +15,7 @@ use serenity::{ }, }; -use crate::{data::GuildOptionsKey, utils::message::embed_author}; +use crate::{utils::message::embed_author, DatabaseKey}; pub(crate) async fn handle_interaction(ctx: &Context, interaction: &Interaction) -> CommandResult { match interaction { @@ -83,14 +84,13 @@ async fn goulag( author: &Member, ) -> CommandResult { let ctx_data = ctx.data.read().await; - let ctx_data = ctx_data - .get::() - .expect("Failed to get guild cache"); - let guild_options = ctx_data.get(&guild_id.into()); + let conn = ctx_data.get::().expect("failed to get db"); + let conn = conn.lock().await; + let guild_options = conn.fetch_settings(guild_id).ok(); if let Some(guild_options) = guild_options { - if let Some(mute_role) = guild_options.get_mute_role() { + if let Some(mute_role) = guild_options.get_mute_role_id() { let options = &data.options; let user = options diff --git a/rusty-bot/src/commands/music/utils.rs b/rusty-bot/src/commands/music/utils.rs index 56b8fdc..a39975e 100644 --- a/rusty-bot/src/commands/music/utils.rs +++ b/rusty-bot/src/commands/music/utils.rs @@ -1,3 +1,4 @@ +use rusty_bot_database::queries::RustyConnectionExt; use serenity::{ builder::CreateMessage, client::Context, @@ -5,15 +6,15 @@ use serenity::{ model::{ channel::Message, guild::{Member, PartialMember}, - id::GuildId, + id::{GuildId, RoleId}, Permissions, }, }; use songbird::{input::Metadata, Call}; use crate::{ - data::GuildOptionsKey, utils::{message::embed_author, permissions::has_permission}, + DatabaseKey, }; use super::error::UseVoiceError; @@ -24,13 +25,13 @@ pub(crate) async fn is_mute( guild_id: GuildId, ) -> CommandResult { let data = ctx.data.read().await; + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; - let data = data - .get::() - .expect("Failed to get guild cache"); + let role_id = conn.settings_fetch_mute_role(guild_id.0)?; - if let Some(mute_role) = data.get(&guild_id).and_then(|o| o.get_mute_role()) { - Ok(member.roles.contains(&mute_role)) + if let Some(mute_role) = role_id { + Ok(member.roles.contains(&RoleId(mute_role))) } else { Ok(false) } diff --git a/rusty-bot/src/commands/roulette.rs b/rusty-bot/src/commands/roulette.rs index 614306f..e201670 100644 --- a/rusty-bot/src/commands/roulette.rs +++ b/rusty-bot/src/commands/roulette.rs @@ -1,8 +1,6 @@ -use crate::{ - api, - data::{GuildOptions, GuildOptionsKey}, -}; +use crate::{api, DatabaseKey}; use rand::Rng; +use rusty_bot_database::{queries::RustyConnectionExt}; use serenity::{ framework::standard::{ macros::{command, group}, @@ -45,13 +43,13 @@ async fn shot(ctx: &Context, msg: &Message, args: Args) -> CommandResult { #[only_in(guilds)] async fn kick(ctx: &Context, msg: &Message) -> CommandResult { if let Some(guild_id) = &msg.guild_id { - let mut data = ctx.data.write().await; - let guilds_options = data - .get_mut::() - .expect("Expected NonKickGuildsContainer in TypeMap."); + let data = ctx.data.read().await; + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; + let guild_options = conn.fetch_settings(guild_id.0).ok(); - let guild_options = guilds_options.entry(*guild_id).or_default(); - if !guild_options.roulette_options.kick_enabled { + if !(guild_options.is_some() && guild_options.unwrap().roulette_kick_enabled == Some(true)) + { msg.channel_id .say( ctx, @@ -85,27 +83,23 @@ async fn kick(ctx: &Context, msg: &Message) -> CommandResult { #[required_permissions("ADMINISTRATOR")] #[owner_privilege] async fn disable_kick(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { - let disable = match args.len() { - 0 => true, - _ => args.single::()?, - }; - let mut data = ctx.data.write().await; - let guilds_options = data - .get_mut::() - .expect("Expected NonKickGuildsContainer in TypeMap."); - if let Some(guild_id) = msg.guild_id { - let entry = guilds_options - .entry(guild_id) - .or_insert_with(|| GuildOptions::default().set_guild_id(guild_id)); - entry.roulette_options.kick_enabled = !disable; + let disable = match args.len() { + 0 => true, + _ => args.single::()?, + }; + + let data = ctx.data.read().await; + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; + + conn.settings_edit_kick(guild_id.0, disable)?; + if disable { msg.channel_id.say(ctx, "No fun allowed").await?; } else { msg.channel_id.say(ctx, "Done").await?; } - - entry.save_async(guild_id.0).await?; } Ok(()) } diff --git a/rusty-bot/src/commands/settings.rs b/rusty-bot/src/commands/settings.rs index 820095f..83ee803 100644 --- a/rusty-bot/src/commands/settings.rs +++ b/rusty-bot/src/commands/settings.rs @@ -1,5 +1,6 @@ use std::str::FromStr; +use rusty_bot_database::queries::RustyConnectionExt; use serenity::{ client::Context, framework::standard::{Args, CommandResult}, @@ -11,7 +12,7 @@ use serenity::{ model::prelude::*, }; -use crate::data::{GuildOptions, GuildOptionsKey}; +use crate::DatabaseKey; #[group] #[prefix("settings")] @@ -28,55 +29,44 @@ async fn muterole(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult _ => Some(args.single::()?), }; - if let Some(id) = role_id { - let mut data = ctx.data.write().await; + if let Some(guild_id) = msg.guild_id { + if let Some(id) = role_id { + let data = ctx.data.read().await; + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; - let guilds_options = data - .get_mut::() - .expect("Expected NonKickGuildsContainer in TypeMap."); - - if let Some(guild_id) = msg.guild_id { - let entry = guilds_options - .entry(guild_id) - .or_insert_with(|| GuildOptions::default().set_guild_id(guild_id)); - - entry.mute_id = match id.as_str() { - "none" => None, - _ => Some(RoleId::from_str(&id)?), - }; - - entry.save_async(guild_id.0).await?; + conn.settings_edit_mute_role( + guild_id.0, + match id.as_str() { + "none" => None, + _ => Some(RoleId::from_str(&id)?.0), + }, + )?; msg.channel_id.say(&ctx.http, "Saved").await?; - } - } else { - let data = ctx.data.read().await; + } else { + let data = ctx.data.read().await; - let guilds_options = data - .get::() - .expect("Expected NonKickGuildsContainer in TypeMap."); + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; - if let Some(guild_id) = msg.guild_id { - if let Some(options) = guilds_options.get(&guild_id) { - msg.channel_id - .say( - &ctx.http, - if let Some(role_id) = options.mute_id { - format!( - "Mute role is @{}", - role_id.to_role_cached(&ctx).await.unwrap().name - ) - } else { - "Mute role is None".to_string() - }, - ) - .await?; - } else { - msg.channel_id.say(&ctx.http, "Mute role is None").await?; - } + let role_id = conn.settings_fetch_mute_role(guild_id.0)?; + + msg.channel_id + .say( + &ctx.http, + if let Some(role_id) = role_id { + format!( + "Mute role is @{}", + RoleId(role_id).to_role_cached(&ctx).await.unwrap().name + ) + } else { + "Mute role is None".to_string() + }, + ) + .await?; } } - Ok(()) } @@ -90,51 +80,38 @@ async fn logchannel(ctx: &Context, msg: &Message, mut args: Args) -> CommandResu _ => Some(args.single::()?), }; - if let Some(id) = channel_id { - let mut data = ctx.data.write().await; + if let Some(guild_id) = msg.guild_id { + if let Some(id) = channel_id { + let data = ctx.data.read().await; + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; - let guilds_options = data - .get_mut::() - .expect("Expected NonKickGuildsContainer in TypeMap."); - - if let Some(guild_id) = msg.guild_id { - let entry = guilds_options - .entry(guild_id) - .or_insert_with(|| GuildOptions::default().set_guild_id(guild_id)); - - entry.mention_log_channel = match id.as_str() { - "none" => None, - _ => Some(ChannelId::from_str(&id)?), - }; - - entry.save_async(guild_id.0).await?; + conn.settings_edit_log_channel( + guild_id.0, + match id.as_str() { + "none" => None, + _ => Some(ChannelId::from_str(&id)?.0), + }, + )?; msg.channel_id.say(&ctx.http, "Saved").await?; - } - } else { - let data = ctx.data.read().await; + } else { + let data = ctx.data.read().await; + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; - let guilds_options = data - .get::() - .expect("Expected NonKickGuildsContainer in TypeMap."); + let log_channel = conn.settings_fetch_log_channel(guild_id.0)?; - if let Some(guild_id) = msg.guild_id { - if let Some(options) = guilds_options.get(&guild_id) { - msg.channel_id - .say( - &ctx.http, - if let Some(channel_id) = options.mention_log_channel { - format!("Logging channel is {}", channel_id.mention()) - } else { - "Logging channel is None".to_string() - }, - ) - .await?; - } else { - msg.channel_id - .say(&ctx.http, "Logging channel is None") - .await?; - } + msg.channel_id + .say( + &ctx.http, + if let Some(channel_id) = log_channel { + format!("Logging channel is {}", ChannelId(channel_id).mention()) + } else { + "Logging channel is None".to_string() + }, + ) + .await?; } } diff --git a/rusty-bot/src/config.rs b/rusty-bot/src/config.rs index 3c59ab6..cfcbc37 100644 --- a/rusty-bot/src/config.rs +++ b/rusty-bot/src/config.rs @@ -10,4 +10,5 @@ pub(crate) struct Bot { pub(crate) token: String, pub(crate) application_id: u64, pub(crate) invite_url: Option, + pub(crate) database_url: String, } diff --git a/rusty-bot/src/data/guilds_options.rs b/rusty-bot/src/data/guilds_options.rs deleted file mode 100644 index a2e6d23..0000000 --- a/rusty-bot/src/data/guilds_options.rs +++ /dev/null @@ -1,128 +0,0 @@ -use log::error; -use serde::{Deserialize, Serialize}; -use serenity::{ - model::{ - id::ChannelId, - prelude::{GuildId, RoleId}, - }, - prelude::TypeMapKey, -}; -use std::{ - collections::HashMap, - fs, - io::{Result as IoResult, Write}, - path::{Path, PathBuf}, -}; -use tokio::io::AsyncWriteExt; - -#[derive(Debug, Serialize, Deserialize)] -pub(crate) struct GuildOptions { - #[serde(skip_serializing)] - pub(crate) guild_id: Option, - pub(crate) mute_id: Option, - pub(crate) mention_log_channel: Option, - pub(crate) roulette_options: RouletteOptions, -} - -impl GuildOptions { - pub async fn save_async(&mut self, guild_id: u64) -> tokio::io::Result<()> { - let path = PathBuf::from(format!("./data/guilds_options/{}.json", guild_id)); - - if !path.parent().unwrap().exists() { - tokio::fs::create_dir_all(path.parent().unwrap()).await?; - } - - let mut file = tokio::fs::File::create(path).await?; - let serialized = serde_json::to_string_pretty(self)?; - file.write_all(&serialized.as_bytes()).await?; - - Ok(()) - } - - pub fn save(&mut self, guild_id: u64) -> IoResult<()> { - let path = PathBuf::from(format!("./data/guilds_options/{}.json", guild_id)); - - if !path.parent().unwrap().exists() { - std::fs::create_dir_all(path.parent().unwrap())?; - } - - let mut file = std::fs::File::create(path)?; - let serialized = serde_json::to_string_pretty(self)?; - file.write_all(&serialized.as_bytes())?; - - Ok(()) - } - - pub(crate) fn set_guild_id(mut self, id: GuildId) -> Self { - self.guild_id = Some(id); - self - } - - pub fn load_from_dir>(path: P) -> IoResult> { - let mut res = HashMap::new(); - for entry in fs::read_dir(path)?.filter_map(|e| e.ok()) { - match serde_json::from_reader::(fs::File::open(entry.path())?) { - Ok(options) => { - let id = GuildId::from( - entry - .file_name() - .to_string_lossy() - .split('.') - .next() - .unwrap() - .parse::() - .unwrap(), - ); - res.insert(id, options.set_guild_id(id)); - } - Err(e) => { - error!("While parsing guild option {}", e); - } - } - } - Ok(res) - } - - pub(crate) fn get_mute_role(&self) -> Option { - self.mute_id - } -} - -impl Default for GuildOptions { - fn default() -> Self { - Self { - roulette_options: RouletteOptions::default(), - guild_id: None, - mute_id: None, - mention_log_channel: None, - } - } -} - -impl Drop for GuildOptions { - fn drop(&mut self) { - if let Some(id) = self.guild_id { - log::debug!("Saving {:?}", self); - if let Err(e) = self.save(id.0) { - log::error!("While saving {} : {}", id.0, e); - } - } - } -} - -#[derive(Debug, Serialize, Deserialize)] -pub(crate) struct RouletteOptions { - pub(crate) kick_enabled: bool, -} - -impl Default for RouletteOptions { - fn default() -> Self { - Self { kick_enabled: true } - } -} - -pub(crate) struct GuildOptionsKey; - -impl TypeMapKey for GuildOptionsKey { - type Value = HashMap; -} diff --git a/rusty-bot/src/data/mod.rs b/rusty-bot/src/data/mod.rs index d5ae97f..57bbac5 100644 --- a/rusty-bot/src/data/mod.rs +++ b/rusty-bot/src/data/mod.rs @@ -4,9 +4,6 @@ use serenity::{ }; use std::{collections::HashMap, sync::Arc}; -mod guilds_options; -pub(crate) use guilds_options::{GuildOptions, GuildOptionsKey}; - pub(crate) struct ShardManagerContainer; impl TypeMapKey for ShardManagerContainer { diff --git a/rusty-bot/src/main.rs b/rusty-bot/src/main.rs index 6f70f12..34c6721 100644 --- a/rusty-bot/src/main.rs +++ b/rusty-bot/src/main.rs @@ -1,13 +1,12 @@ -use crate::{ - commands::{ - admin::ADMIN_GROUP, general::GENERAL_GROUP, owner::OWNER_GROUP, roulette::ROULETTE_GROUP, - settings::SETTINGS_GROUP, - }, - data::{BulletsContainer, GuildOptions, GuildOptionsKey, ShardManagerContainer, Uptime}, +use std::{ + collections::{HashMap, HashSet}, + fs, + path::Path, + sync::Arc, + time::Duration, + time::Instant, }; -use async_trait::async_trait; -use commands::interaction; -use log::{debug, error, info}; + use serenity::{ framework::standard::{ help_commands, @@ -19,14 +18,17 @@ use serenity::{ model::prelude::*, prelude::*, }; -use std::{ - collections::{HashMap, HashSet}, - fs, - io::Result as IoResult, - path::Path, - sync::Arc, - time::Duration, - time::Instant, + +use log::{debug, error, info}; + +use rusty_bot_database::{diesel::PgConnection, establish_connection, queries::RustyConnectionExt}; + +use crate::{ + commands::{ + admin::ADMIN_GROUP, general::GENERAL_GROUP, interaction, owner::OWNER_GROUP, + roulette::ROULETTE_GROUP, settings::SETTINGS_GROUP, + }, + data::{BulletsContainer, ShardManagerContainer, Uptime}, }; #[cfg(feature = "music")] @@ -47,9 +49,15 @@ const MINIMUM_MENTIONS: usize = 20; const PREFIX: &str = "?"; pub(crate) static mut INVITE_URL: Option = None; +pub(crate) struct DatabaseKey; + +impl TypeMapKey for DatabaseKey { + type Value = Arc>; +} + // TODO CLAP FOR CLI #[tokio::main] -async fn main() -> IoResult<()> { +async fn main() -> anyhow::Result<()> { let now = Instant::now(); log4rs::init_file("log4rs.yaml", Default::default()).unwrap(); @@ -69,10 +77,12 @@ async fn main() -> IoResult<()> { fs::create_dir(dir)?; } - let data_dir = Path::new("data"); - if !data_dir.exists() { - fs::create_dir(data_dir)?; - } + // let data_dir = Path::new("data"); + // if !data_dir.exists() { + // fs::create_dir(data_dir)?; + // } + + let conn = Arc::new(Mutex::new(establish_connection(&conf.bot.database_url)?)); let http = Http::new_with_token(&token); @@ -143,17 +153,15 @@ async fn main() -> IoResult<()> { let mut data = client.data.write().await; data.insert::(HashMap::default()); data.insert::(Arc::clone(&client.shard_manager)); + /*#[cfg(feature = "music")] { data.insert::(std::sync::Arc::clone(&client.voice_manager.unwrap())); }*/ + data.insert::(now); - data.insert::({ - let options = GuildOptions::load_from_dir("./data/guilds_options").unwrap_or_default(); - log::debug!("Loaded {:?}", options); - options - }) + data.insert::(conn); } let current_runtime = tokio::runtime::Handle::current(); @@ -177,7 +185,7 @@ async fn main() -> IoResult<()> { struct Messages {} -#[async_trait] +#[async_trait::async_trait] impl EventHandler for Messages { async fn ready(&self, ctx: Context, ready: Ready) { info!("{} connected to discord", ready.user.name); @@ -239,10 +247,6 @@ async fn log_mentions(ctx: Context, new_message: &Message) -> CommandResult { let data = ctx.data.read().await; - let guilds_options = data - .get::() - .expect("Expected NonKickGuildsContainer in TypeMap."); - let mute = if new_message.mention_everyone { true } else { @@ -295,31 +299,35 @@ async fn log_mentions(ctx: Context, new_message: &Message) -> CommandResult { if mute { if let Some(guild_id) = new_message.guild_id { - if let Some(options) = guilds_options.get(&guild_id) { - if let Some(role_id) = options.mute_id { - let mut member = new_message.member(&ctx).await?; - member.add_role(&ctx.http, role_id).await?; - } + let conn = data.get::().expect("failed to get db"); + let conn = conn.lock().await; - if let Some(channel_id) = options.mention_log_channel { - channel_id - .send_message(&ctx.http, |m| { - m.embed(|e| { - e.title("New message with mentions") - .fields(vec![ - ("Sender", new_message.author.mention().to_string(), false), - ("Content", new_message.content.clone(), false), - ( - "Channel", - new_message.channel_id.mention().to_string(), - false, - ), - ]) - .colour((247, 76, 0)) - }) + let role_id = conn.settings_fetch_mute_role(guild_id.0)?; + let channel_id = conn.settings_fetch_log_channel(guild_id.0)?; + + if let Some(role_id) = role_id { + let mut member = new_message.member(&ctx).await?; + member.add_role(&ctx.http, role_id).await?; + } + + if let Some(channel_id) = channel_id { + ChannelId(channel_id) + .send_message(&ctx.http, |m| { + m.embed(|e| { + e.title("New message with mentions") + .fields(vec![ + ("Sender", new_message.author.mention().to_string(), false), + ("Content", new_message.content.clone(), false), + ( + "Channel", + new_message.channel_id.mention().to_string(), + false, + ), + ]) + .colour((247, 76, 0)) }) - .await?; - } + }) + .await?; } } }