Working on database

This commit is contained in:
oupson 2022-03-15 21:56:23 +01:00
parent dd33bc8f81
commit 90dadc92d6
31 changed files with 1100 additions and 317 deletions

3
.gitignore vendored
View File

@ -4,4 +4,5 @@
Conf.toml
/data
/log
join_audio.mp3
*.mp3
.env

140
Cargo.lock generated
View File

@ -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"

View File

@ -1,5 +1,6 @@
[workspace]
members = [
"rusty-bot",
"rusty-bot-database"
"rusty-bot-database",
"rusty-bot-convert-data"
]

View File

@ -11,4 +11,6 @@ root:
- file-debug
loggers:
rusty_bot:
level: trace
rusty_bot_database:
level: trace

View File

@ -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"] }

View File

@ -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, models::GuildOptions>(std::fs::File::open(
entry.path(),
)?) {
Ok(options) => {
let id = entry
.file_name()
.to_string_lossy()
.split('.')
.next()
.unwrap()
.parse::<u64>()
.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(())
}

View File

@ -0,0 +1,13 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct GuildOptions {
pub(crate) mute_id: Option<u64>,
pub(crate) mention_log_channel: Option<u64>,
pub(crate) roulette_options: RouletteOptions,
}
#[derive(Debug, Serialize, Deserialize)]
pub(crate) struct RouletteOptions {
pub(crate) kick_enabled: bool,
}

View File

@ -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"

View File

@ -0,0 +1,5 @@
# RustyBot Database
## TODO
![Screenshot](schema/database.svg)

View File

@ -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"

View File

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -0,0 +1,244 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="911" height="346" view_box="0 0 911 346" xmlns="http://www.w3.org/2000/svg" xmlns:link="http://www.w3.org/1999/xlink">
\n\n
<desc>Généré par Mocodo 2.3.7 le Tue, 15 Mar 2022 13:53:31</desc>
<rect id="frame" x="0" y="0" width="911" height="346" fill="#f5f5f5" stroke="none" stroke-width="0" />
<!-- Association PLAYING_ON_MUSIC_SESSION -->
<line x1="280" y1="127" x2="280" y2="43" stroke="#bf812d" stroke-width="2" />
<text x="285.0" y="85" fill="#01665e" font-family="Verdana" font-size="12">0,N</text>
<line x1="481" y1="43" x2="280" y2="43" stroke="#bf812d" stroke-width="2" />
<text x="389" y="60.0" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<path d="M 415.0 43.0 L 403.0 49.0 L 407.0 43.0 L 403.0 37.0 Z" fill="#bf812d" stroke-width="0" />
<g id="association-PLAYING_ON_MUSIC_SESSION">
<path d="M 369 18 a 14 14 90 0 1 14 14 V 43 h -206 V 32 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 383 43.0 v 11 a 14 14 90 0 1 -14 14 H 191 a 14 14 90 0 1 -14 -14 V 43.0 H 206" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="177" y="18" width="206" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="177" y1="43" x2="383" y2="43" stroke="#bf812d" stroke-width="1" />
<text x="184" y="35.7" fill="#000000" font-family="Verdana" font-size="12">PLAYING_ON_MUSIC_SESSION</text>
</g>
<!-- Association LOG_CHANNEL -->
<line x1="836" y1="43" x2="836" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="841.0" y="85" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<path d="M 836.0 68.0 L 842.0 80.0 L 836.0 76.0 L 830.0 80.0 Z" fill="#bf812d" stroke-width="0" />
<line x1="836" y1="228" x2="836" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="841.0" y="186" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<g id="association-LOG_CHANNEL">
<path d="M 875 102 a 14 14 90 0 1 14 14 V 127 h -106 V 116 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 889 127.0 v 11 a 14 14 90 0 1 -14 14 H 797 a 14 14 90 0 1 -14 -14 V 127.0 H 106" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="783" y="102" width="106" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="783" y1="127" x2="889" y2="127" stroke="#bf812d" stroke-width="1" />
<text x="790" y="119.7" fill="#000000" font-family="Verdana" font-size="12">LOG_CHANNEL</text>
</g>
<!-- Association TEXT_CHANNEL -->
<line x1="836" y1="43" x2="664" y2="43" stroke="#bf812d" stroke-width="2" />
<text x="774" y="35.0" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<path d="M 800.0 43.0 L 788.0 49.0 L 792.0 43.0 L 788.0 37.0 Z" fill="#bf812d" stroke-width="0" />
<line x1="481" y1="43" x2="664" y2="43" stroke="#bf812d" stroke-width="2" />
<text x="552" y="35.0" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<g id="association-TEXT_CHANNEL">
<path d="M 706 18 a 14 14 90 0 1 14 14 V 43 h -112 V 32 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 720 43.0 v 11 a 14 14 90 0 1 -14 14 H 622 a 14 14 90 0 1 -14 -14 V 43.0 H 112" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="608" y="18" width="112" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="608" y1="43" x2="720" y2="43" stroke="#bf812d" stroke-width="1" />
<text x="615" y="35.7" fill="#000000" font-family="Verdana" font-size="12">TEXT_CHANNEL</text>
</g>
<!-- Association MUSIC_METADATAS -->
<line x1="90" y1="228" x2="280" y2="228" stroke="#bf812d" stroke-width="2" />
<text x="162" y="245.0" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<path d="M 157.0 228.0 L 169.0 222.0 L 165.0 228.0 L 169.0 234.0 Z" fill="#bf812d" stroke-width="0" />
<line x1="280" y1="127" x2="280" y2="228" stroke="#bf812d" stroke-width="2" />
<text x="285.0" y="178" fill="#01665e" font-family="Verdana" font-size="12">0,N</text>
<g id="association-MUSIC_METADATAS">
<path d="M 335 203 a 14 14 90 0 1 14 14 V 228 h -138 V 217 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 349 228.0 v 11 a 14 14 90 0 1 -14 14 H 225 a 14 14 90 0 1 -14 -14 V 228.0 H 138" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="211" y="203" width="138" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="211" y1="228" x2="349" y2="228" stroke="#bf812d" stroke-width="1" />
<text x="218" y="220.7" fill="#000000" font-family="Verdana" font-size="12">MUSIC_METADATAS</text>
</g>
<!-- Association MUSIC_REQUESTED_BY -->
<line x1="90" y1="43" x2="90" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="95.0" y="85" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<path d="M 90.0 68.0 L 96.0 80.0 L 90.0 76.0 L 84.0 80.0 Z" fill="#bf812d" stroke-width="0" />
<line x1="280" y1="127" x2="90" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="197" y="144.0" fill="#01665e" font-family="Verdana" font-size="12">0,N</text>
<g id="association-MUSIC_REQUESTED_BY">
<path d="M 157 102 a 14 14 90 0 1 14 14 V 127 h -162 V 116 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 171 127.0 v 11 a 14 14 90 0 1 -14 14 H 23 a 14 14 90 0 1 -14 -14 V 127.0 H 162" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="9" y="102" width="162" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="9" y1="127" x2="171" y2="127" stroke="#bf812d" stroke-width="1" />
<text x="16" y="119.7" fill="#000000" font-family="Verdana" font-size="12">MUSIC_REQUESTED_BY</text>
</g>
<!-- Association SETTINGS_OF_GUILD -->
<line x1="836" y1="228" x2="664" y2="228" stroke="#bf812d" stroke-width="2" />
<text x="744" y="245.0" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<line x1="481" y1="228" x2="664" y2="228" stroke="#bf812d" stroke-width="2" />
<text x="513" y="245.0" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<path d="M 508.0 228.0 L 520.0 222.0 L 516.0 228.0 L 520.0 234.0 Z" fill="#bf812d" stroke-width="0" />
<g id="association-SETTINGS_OF_GUILD">
<path d="M 724 203 a 14 14 90 0 1 14 14 V 228 h -148 V 217 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 738 228.0 v 11 a 14 14 90 0 1 -14 14 H 604 a 14 14 90 0 1 -14 -14 V 228.0 H 148" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="590" y="203" width="148" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="590" y1="228" x2="738" y2="228" stroke="#bf812d" stroke-width="1" />
<text x="597" y="220.7" fill="#000000" font-family="Verdana" font-size="12">SETTINGS_OF_GUILD</text>
</g>
<!-- Association PLAYING_ON_GUILD -->
<line x1="481" y1="43" x2="481" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="455.0" y="94" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<line x1="481" y1="228" x2="481" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="486.0" y="195" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<path d="M 481.0 203.0 L 475.0 191.0 L 481.0 195.0 L 487.0 191.0 Z" fill="#bf812d" stroke-width="0" />
<g id="association-PLAYING_ON_GUILD">
<path d="M 538 102 a 14 14 90 0 1 14 14 V 127 h -142 V 116 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 552 127.0 v 11 a 14 14 90 0 1 -14 14 H 424 a 14 14 90 0 1 -14 -14 V 127.0 H 142" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="410" y="102" width="142" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="410" y1="127" x2="552" y2="127" stroke="#bf812d" stroke-width="1" />
<text x="417" y="119.7" fill="#000000" font-family="Verdana" font-size="12">PLAYING_ON_GUILD</text>
</g>
<!-- Association PLAYING_ON_CHANNEL -->
<line x1="481" y1="43" x2="664" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="552" y="68.8895407756" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<line x1="836" y1="43" x2="664" y2="127" stroke="#bf812d" stroke-width="2" />
<text x="774" y="56.401628839" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<path d="M 800.0 60.5813953488 L 791.850201599 71.2388240268 L 792.811459872 64.0920777369 L 786.584178017 60.456013835 Z" fill="#bf812d" stroke-width="0" />
<g id="association-PLAYING_ON_CHANNEL">
<path d="M 730 102 a 14 14 90 0 1 14 14 V 127 h -160 V 116 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 744 127.0 v 11 a 14 14 90 0 1 -14 14 H 598 a 14 14 90 0 1 -14 -14 V 127.0 H 160" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="584" y="102" width="160" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="584" y1="127" x2="744" y2="127" stroke="#bf812d" stroke-width="1" />
<text x="591" y="119.7" fill="#000000" font-family="Verdana" font-size="12">PLAYING_ON_CHANNEL</text>
</g>
<!-- Association MUTE_ROLE -->
<line x1="836" y1="228" x2="836" y2="312" stroke="#bf812d" stroke-width="2" />
<text x="841.0" y="279" fill="#01665e" font-family="Verdana" font-size="12">1,1</text>
<line x1="664" y1="312" x2="836" y2="312" stroke="#bf812d" stroke-width="2" />
<text x="693" y="329.0" fill="#01665e" font-family="Verdana" font-size="12">0,1</text>
<path d="M 688.0 312.0 L 700.0 306.0 L 696.0 312.0 L 700.0 318.0 Z" fill="#bf812d" stroke-width="0" />
<g id="association-MUTE_ROLE">
<path d="M 867 287 a 14 14 90 0 1 14 14 V 312 h -90 V 301 a 14 14 90 0 1 14 -14" fill="#dfc27d" stroke="#dfc27d" stroke-width="0" />
<path d="M 881 312.0 v 11 a 14 14 90 0 1 -14 14 H 805 a 14 14 90 0 1 -14 -14 V 312.0 H 90" fill="#f6e8c3" stroke="#f6e8c3" stroke-width="0" />
<rect x="791" y="287" width="90" height="50" fill="none" rx="14" stroke="#bf812d" stroke-width="2" />
<line x1="791" y1="312" x2="881" y2="312" stroke="#bf812d" stroke-width="1" />
<text x="798" y="304.7" fill="#000000" font-family="Verdana" font-size="12">MUTE_ROLE</text>
</g>
<!-- Entity GUILD -->
<g id="entity-GUILD">
<g id="frame-GUILD">
<rect x="454" y="203" width="54" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="454" y="228.0" width="54" height="25" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="454" y="203" width="54" height="50" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="454" y1="228" x2="508" y2="228" stroke="#35978f" stroke-width="1" />
</g>
<text x="461" y="220.7" fill="#000000" font-family="Verdana" font-size="12">GUILD</text>
<text x="459" y="245.8" fill="#000000" font-family="Verdana" font-size="12">guildID</text>
<line x1="459" y1="248" x2="503" y2="248" stroke="#000000" stroke-width="1" />
</g>
<!-- Entity SETTINGS -->
<g id="entity-SETTINGS">
<g id="frame-SETTINGS">
<rect x="770" y="194" width="132" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="770" y="219.0" width="132" height="43" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="770" y="194" width="132" height="68" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="770" y1="219" x2="902" y2="219" stroke="#35978f" stroke-width="1" />
</g>
<text x="804" y="211.7" fill="#000000" font-family="Verdana" font-size="12">SETTINGS</text>
<text x="775" y="236.8" fill="#000000" font-family="Verdana" font-size="12">settingsID</text>
<line x1="775" y1="239" x2="838" y2="239" stroke="#000000" stroke-width="1" />
<text x="775" y="253.8" fill="#000000" font-family="Verdana" font-size="12">rouletteKickEnabled</text>
</g>
<!-- Entity DISCORD_USER -->
<g id="entity-DISCORD_USER">
<g id="frame-DISCORD_USER">
<rect x="35" y="18" width="110" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="35" y="43.0" width="110" height="25" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="35" y="18" width="110" height="50" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="35" y1="43" x2="145" y2="43" stroke="#35978f" stroke-width="1" />
</g>
<text x="40" y="35.7" fill="#000000" font-family="Verdana" font-size="12">DISCORD_USER</text>
<text x="40" y="60.8" fill="#000000" font-family="Verdana" font-size="12">userId</text>
<line x1="40" y1="63" x2="80" y2="63" stroke="#000000" stroke-width="1" />
</g>
<!-- Entity MUSIC_INFO -->
<g id="entity-MUSIC_INFO">
<g id="frame-MUSIC_INFO">
<rect x="23" y="177" width="134" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="23" y="202.0" width="134" height="77" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="23" y="177" width="134" height="102" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="23" y1="202" x2="157" y2="202" stroke="#35978f" stroke-width="1" />
</g>
<text x="50" y="194.7" fill="#000000" font-family="Verdana" font-size="12">MUSIC_INFO</text>
<text x="28" y="219.7" fill="#000000" font-family="Verdana" font-size="12">musicSourceURL</text>
<line x1="28" y1="222" x2="129" y2="222" stroke="#000000" stroke-width="1" />
<text x="28" y="236.8" fill="#000000" font-family="Verdana" font-size="12">musicTitle</text>
<text x="28" y="253.8" fill="#000000" font-family="Verdana" font-size="12">musicThumbnailURL</text>
<text x="28" y="270.8" fill="#000000" font-family="Verdana" font-size="12">musicLength</text>
</g>
<!-- Entity MUSIC -->
<g id="entity-MUSIC">
<g id="frame-MUSIC">
<rect x="224" y="93" width="112" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="224" y="118.0" width="112" height="43" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="224" y="93" width="112" height="68" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="224" y1="118" x2="336" y2="118" stroke="#35978f" stroke-width="1" />
</g>
<text x="259" y="110.7" fill="#000000" font-family="Verdana" font-size="12">MUSIC</text>
<text x="229" y="135.8" fill="#000000" font-family="Verdana" font-size="12">musicID</text>
<line x1="229" y1="138" x2="279" y2="138" stroke="#000000" stroke-width="1" />
<text x="229" y="152.8" fill="#000000" font-family="Verdana" font-size="12">musicSourceURL</text>
</g>
<!-- Entity ROLE -->
<g id="entity-ROLE">
<g id="frame-ROLE">
<rect x="640" y="287" width="48" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="640" y="312.0" width="48" height="25" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="640" y="287" width="48" height="50" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="640" y1="312" x2="688" y2="312" stroke="#35978f" stroke-width="1" />
</g>
<text x="647" y="304.7" fill="#000000" font-family="Verdana" font-size="12">ROLE</text>
<text x="645" y="329.8" fill="#000000" font-family="Verdana" font-size="12">roleID</text>
<line x1="645" y1="332" x2="683" y2="332" stroke="#000000" stroke-width="1" />
</g>
<!-- Entity MUSIC_SESSION -->
<g id="entity-MUSIC_SESSION">
<g id="frame-MUSIC_SESSION">
<rect x="415" y="9" width="132" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="415" y="34.0" width="132" height="43" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="415" y="9" width="132" height="68" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="415" y1="34" x2="547" y2="34" stroke="#35978f" stroke-width="1" />
</g>
<text x="428" y="26.7" fill="#000000" font-family="Verdana" font-size="12">MUSIC_SESSION</text>
<text x="420" y="51.8" fill="#000000" font-family="Verdana" font-size="12">musicSessionID</text>
<line x1="420" y1="54" x2="516" y2="54" stroke="#000000" stroke-width="1" />
<text x="420" y="68.8" fill="#000000" font-family="Verdana" font-size="12">currentPlayingIndex</text>
</g>
<!-- Entity CHANNEL -->
<g id="entity-CHANNEL">
<g id="frame-CHANNEL">
<rect x="800" y="18" width="72" height="25" fill="#80cdc1" stroke="#80cdc1" stroke-width="0" />
<rect x="800" y="43.0" width="72" height="25" fill="#c7eae5" stroke="#c7eae5" stroke-width="0" />
<rect x="800" y="18" width="72" height="50" fill="none" stroke="#35978f" stroke-width="2" />
<line x1="800" y1="43" x2="872" y2="43" stroke="#35978f" stroke-width="1" />
</g>
<text x="806" y="35.7" fill="#000000" font-family="Verdana" font-size="12">CHANNEL</text>
<text x="805" y="60.8" fill="#000000" font-family="Verdana" font-size="12">channelID</text>
<line x1="805" y1="63" x2="867" y2="63" stroke="#000000" stroke-width="1" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -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

View File

@ -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<T> = std::result::Result<T, Error>;

View File

@ -1 +1,24 @@
// KEEP
#[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<PgConnection> {
let conn = PgConnection::establish(&database_url)?;
embedded_migrations::run(&conn)?;
Ok(conn)
}

View File

@ -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<String>,
pub thumbnail_url: Option<String>,
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<i16>,
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<i64>,
pub roulette_kick_enabled: Option<bool>,
pub log_channel_id: Option<i64>,
}
impl Settings {
pub fn get_mute_role_id(&self) -> Option<u64> {
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<i64>,
#[column_name = "settingsroulettekickenabled"]
pub roulette_kick_enabled: Option<bool>,
#[column_name = "settingslogchannelid"]
pub log_channel_id: Option<i64>,
}
impl NewSettings {
pub fn new(
guild_id: u64,
mute_role_id: Option<u64>,
roulette_kick_enabled: Option<bool>,
log_channel_id: Option<u64>,
) -> 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) }),
}
}
}

View File

@ -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<Settings>;
fn settings_edit_kick(&self, guild_id: u64, disable: bool) -> Result<()>;
fn settings_fetch_mute_role(&self, guild_id: u64) -> Result<Option<u64>>;
fn settings_edit_mute_role(&self, guild_id: u64, mute_role: Option<u64>) -> Result<()>;
fn settings_fetch_log_channel(&self, guild_id: u64) -> Result<Option<u64>>;
fn settings_edit_log_channel(&self, guild_id: u64, channel: Option<u64>) -> Result<()>;
fn insert_settings(&self, settings: NewSettings) -> Result<()>;
}
impl RustyConnectionExt for PgConnection {
fn fetch_settings(&self, guild_id: u64) -> Result<Settings> {
use schema::settings::dsl::*;
let id = unsafe { std::mem::transmute::<u64, i64>(guild_id) };
let res = settings
.filter(settingsguildid.eq(id))
.first::<Settings>(self)?;
Ok(res)
}
fn settings_edit_kick(&self, guild_id: u64, disable: bool) -> Result<()> {
use schema::settings::dsl::*;
let id = unsafe { std::mem::transmute::<u64, i64>(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<u64>) -> Result<()> {
use schema::settings::dsl::*;
let id = unsafe { std::mem::transmute::<u64, i64>(guild_id) };
let mute_role = mute_role.map(|id| unsafe { std::mem::transmute::<u64, i64>(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<Option<u64>> {
use schema::settings::dsl::*;
let id: QueryResult<Option<i64>> = settings
.select(settingsmuteroleid)
.filter(settingsguildid.eq(unsafe { std::mem::transmute::<u64, i64>(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<Option<u64>> {
use schema::settings::dsl::*;
let id: QueryResult<Option<i64>> = settings
.select(settingslogchannelid)
.filter(settingsguildid.eq(unsafe { std::mem::transmute::<u64, i64>(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<u64>) -> Result<()> {
use schema::settings::dsl::*;
let id = unsafe { std::mem::transmute::<u64, i64>(guild_id) };
let channel = channel.map(|id| unsafe { std::mem::transmute::<u64, i64>(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(())
}
}

View File

@ -0,0 +1,56 @@
table! {
music (musicid) {
musicid -> Int4,
musicsourceurl -> Text,
musicuserrequesterid -> Int8,
}
}
table! {
music_info (musicinfosourceurl) {
musicinfosourceurl -> Text,
musicinfotitle -> Text,
musicinfothumbnailurl -> Nullable<Text>,
musicinfolength -> Int4,
musicinfomusicid -> Int4,
}
}
table! {
music_session (musicsessionid) {
musicsessionid -> Int4,
musicsessioncurrentplayingindex -> Nullable<Int2>,
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<Int8>,
settingsroulettekickenabled -> Nullable<Bool>,
settingslogchannelid -> Nullable<Int8>,
}
}
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,
);

View File

@ -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"

View File

@ -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::<GuildOptionsKey>()
.expect("Failed to get guild cache");
let guild_options = ctx_data.get(&guild_id.into());
let conn = ctx_data.get::<DatabaseKey>().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

View File

@ -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<bool> {
let data = ctx.data.read().await;
let conn = data.get::<DatabaseKey>().expect("failed to get db");
let conn = conn.lock().await;
let data = data
.get::<GuildOptionsKey>()
.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)
}

View File

@ -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::<GuildOptionsKey>()
.expect("Expected NonKickGuildsContainer in TypeMap.");
let data = ctx.data.read().await;
let conn = data.get::<DatabaseKey>().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::<bool>()?,
};
let mut data = ctx.data.write().await;
let guilds_options = data
.get_mut::<GuildOptionsKey>()
.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::<bool>()?,
};
let data = ctx.data.read().await;
let conn = data.get::<DatabaseKey>().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(())
}

View File

@ -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::<String>()?),
};
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::<DatabaseKey>().expect("failed to get db");
let conn = conn.lock().await;
let guilds_options = data
.get_mut::<GuildOptionsKey>()
.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::<GuildOptionsKey>()
.expect("Expected NonKickGuildsContainer in TypeMap.");
let conn = data.get::<DatabaseKey>().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::<String>()?),
};
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::<DatabaseKey>().expect("failed to get db");
let conn = conn.lock().await;
let guilds_options = data
.get_mut::<GuildOptionsKey>()
.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::<DatabaseKey>().expect("failed to get db");
let conn = conn.lock().await;
let guilds_options = data
.get::<GuildOptionsKey>()
.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?;
}
}

View File

@ -10,4 +10,5 @@ pub(crate) struct Bot {
pub(crate) token: String,
pub(crate) application_id: u64,
pub(crate) invite_url: Option<String>,
pub(crate) database_url: String,
}

View File

@ -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<GuildId>,
pub(crate) mute_id: Option<RoleId>,
pub(crate) mention_log_channel: Option<ChannelId>,
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<P: AsRef<Path>>(path: P) -> IoResult<HashMap<GuildId, GuildOptions>> {
let mut res = HashMap::new();
for entry in fs::read_dir(path)?.filter_map(|e| e.ok()) {
match serde_json::from_reader::<fs::File, GuildOptions>(fs::File::open(entry.path())?) {
Ok(options) => {
let id = GuildId::from(
entry
.file_name()
.to_string_lossy()
.split('.')
.next()
.unwrap()
.parse::<u64>()
.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<RoleId> {
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<GuildId, GuildOptions>;
}

View File

@ -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 {

View File

@ -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<String> = None;
pub(crate) struct DatabaseKey;
impl TypeMapKey for DatabaseKey {
type Value = Arc<Mutex<PgConnection>>;
}
// 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::<BulletsContainer>(HashMap::default());
data.insert::<ShardManagerContainer>(Arc::clone(&client.shard_manager));
/*#[cfg(feature = "music")]
{
data.insert::<VoiceManager>(std::sync::Arc::clone(&client.voice_manager.unwrap()));
}*/
data.insert::<Uptime>(now);
data.insert::<GuildOptionsKey>({
let options = GuildOptions::load_from_dir("./data/guilds_options").unwrap_or_default();
log::debug!("Loaded {:?}", options);
options
})
data.insert::<DatabaseKey>(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::<GuildOptionsKey>()
.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::<DatabaseKey>().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?;
}
}
}