From 5a73154fe92d2a7a1eec1e6613039d360291573d Mon Sep 17 00:00:00 2001 From: toast Date: Tue, 30 Jan 2024 23:17:59 +1100 Subject: [PATCH] Database integration and test command --- Cargo.lock | 128 +++++++++++++++++++++++++++++++++++- Cargo.toml | 3 +- docker-compose.yml | 12 ++++ run.sh | 2 +- src/commands/status.rs | 25 ++++++- src/controllers/database.rs | 36 ++++++++++ src/controllers/mod.rs | 1 + src/main.rs | 26 ++++++-- src/models/mod.rs | 1 + src/models/mpservers.rs | 39 +++++++++++ 10 files changed, 262 insertions(+), 11 deletions(-) create mode 100644 src/controllers/database.rs create mode 100644 src/controllers/mod.rs create mode 100644 src/models/mod.rs create mode 100644 src/models/mpservers.rs diff --git a/Cargo.lock b/Cargo.lock index 0734514..779120b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -392,6 +392,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -434,12 +435,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "finl_unicode" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fcfdc7a0362c9f4444381a9e697c79d435fe65b52a37466fc2c1184cee9edc6" + [[package]] name = "flate2" version = "1.0.28" @@ -643,6 +656,15 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "0.2.11" @@ -800,7 +822,7 @@ dependencies = [ [[package]] name = "kon" -version = "0.1.10" +version = "0.1.11" dependencies = [ "cargo_toml", "gamedig", @@ -811,6 +833,7 @@ dependencies = [ "serenity", "sysinfo", "tokio", + "tokio-postgres", "uptime_lib", ] @@ -854,6 +877,16 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "md-5" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] + [[package]] name = "memchr" version = "2.6.4" @@ -1134,6 +1167,35 @@ dependencies = [ "syn 2.0.46", ] +[[package]] +name = "postgres-protocol" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49b6c5ef183cd3ab4ba005f1ca64c21e8bd97ce4699cfea9e8d9a2c4958ca520" +dependencies = [ + "base64", + "byteorder", + "bytes", + "fallible-iterator", + "hmac", + "md-5", + "memchr", + "rand", + "sha2", + "stringprep", +] + +[[package]] +name = "postgres-types" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d2234cdee9408b523530a9b6d2d6b373d1db34f6a8e51dc03ded1828d7fb67c" +dependencies = [ + "bytes", + "fallible-iterator", + "postgres-protocol", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1554,6 +1616,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1631,12 +1704,29 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stringprep" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb41d74e231a107a1b4ee36bd1214b11285b77768d2e3824aedafa988fd36ee6" +dependencies = [ + "finl_unicode", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "strsim" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "1.0.109" @@ -1817,6 +1907,32 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-postgres" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d340244b32d920260ae7448cb72b6e238bddc3d4f7603394e7dd46ed8e48f5b8" +dependencies = [ + "async-trait", + "byteorder", + "bytes", + "fallible-iterator", + "futures-channel", + "futures-util", + "log", + "parking_lot", + "percent-encoding", + "phf", + "pin-project-lite", + "postgres-protocol", + "postgres-types", + "rand", + "socket2 0.5.5", + "tokio", + "tokio-util", + "whoami", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -2204,6 +2320,16 @@ version = "0.25.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10" +[[package]] +name = "whoami" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index 725557a..dc4a773 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kon" -version = "0.1.10" +version = "0.1.11" rust-version = "1.74" edition = "2021" @@ -16,6 +16,7 @@ serde_json = "1.0.113" serenity = "0.12.0" sysinfo = "0.30.5" tokio = { version = "1.35.1", features = ["macros", "signal", "rt-multi-thread"] } +tokio-postgres = "0.7.10" uptime_lib = "0.3.0" [[bin]] diff --git a/docker-compose.yml b/docker-compose.yml index 0883617..55e8fb5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,3 +7,15 @@ services: env_file: - .env restart: unless-stopped + db: + container_name: kon-database + image: postgres:16.1-alpine3.19 + restart: unless-stopped + ports: + - 37930:5432/tcp + volumes: + - /var/lib/docker/volumes/kon-database:/var/lib/postgresql/data:rw + environment: + POSTGRES_USER: ${POSTGRES_USER} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB} diff --git a/run.sh b/run.sh index 9151687..97f9808 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,3 @@ #!/bin/bash -export $(cat .env | xargs) && cargo run +export $(grep -v '^#' .env | xargs) && cargo run diff --git a/src/commands/status.rs b/src/commands/status.rs index 1d8055d..db98d71 100644 --- a/src/commands/status.rs +++ b/src/commands/status.rs @@ -1,11 +1,15 @@ -use crate::{Error, EMBED_COLOR}; +use crate::{ + models::mpservers::MPServers, + EMBED_COLOR, + Error +}; use gamedig::protocols::{ valve::{ Engine, GatheringSettings, Response }, types::TimeoutSettings, - valve, + valve }; use std::{ str::FromStr, @@ -73,7 +77,7 @@ async fn pms_serverstatus(url: &str) -> Result, Error> { } /// Query the server statuses -#[poise::command(slash_command, subcommands("ats", "wg"), subcommand_required)] +#[poise::command(slash_command, subcommands("ats", "wg", "fs"), subcommand_required)] pub async fn status(_: poise::Context<'_, (), Error>) -> Result<(), Error> { Ok(()) } @@ -133,3 +137,18 @@ pub async fn wg(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> { Ok(()) } + +/// Retrieve the data from Farming Simulator 22 server +#[poise::command(slash_command, guild_only)] +pub async fn fs(ctx: poise::Context<'_, (), Error>) -> Result<(), Error> { + // let embed = CreateEmbed::new().color(EMBED_COLOR); + let server = MPServers::get_server_ip(ctx.guild_id().unwrap().into(), "testserver").await?; + let ip = server.0; + let md5 = server.1; + + ctx.send(CreateReply::default().content(format!("IP: {}\nMD5: {}", ip, md5))).await?; + + // ctx.send(CreateReply::default().content("This command is not yet implemented")).await?; + + Ok(()) +} diff --git a/src/controllers/database.rs b/src/controllers/database.rs new file mode 100644 index 0000000..28631ad --- /dev/null +++ b/src/controllers/database.rs @@ -0,0 +1,36 @@ +use serenity::prelude::TypeMapKey; +use tokio_postgres::{Client, NoTls, Error}; + +pub struct DatabaseController { + pub client: Client +} + +impl TypeMapKey for DatabaseController { + type Value = DatabaseController; +} + +impl DatabaseController { + pub async fn new() -> Result { + let db_uri = std::env::var("DATABASE_URI").expect("Expected a \"DATABASE_URI\" in the envvar but none was found"); + let (client, connection) = tokio_postgres::connect(&db_uri, NoTls).await?; + + tokio::spawn(async move { + if let Err(e) = connection.await { + eprintln!("Connection error: {}", e); + } + }); + + // MPServers + client.batch_execute(" + CREATE TABLE IF NOT EXISTS mpservers ( + server_name VARCHAR(255) NOT NULL PRIMARY KEY, + guild_owner BIGINT NOT NULL, + is_active BOOLEAN NOT NULL, + ip_address VARCHAR(255) NOT NULL, + md5_code VARCHAR(255) NOT NULL + ); + ").await?; + + Ok(DatabaseController { client }) + } +} diff --git a/src/controllers/mod.rs b/src/controllers/mod.rs new file mode 100644 index 0000000..8fd0a6b --- /dev/null +++ b/src/controllers/mod.rs @@ -0,0 +1 @@ +pub mod database; diff --git a/src/main.rs b/src/main.rs index 48ab27c..c02bde6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,25 +1,32 @@ mod commands; +mod controllers; +mod models; -use std::env::var; use poise::serenity_prelude::{self as serenity}; +use std::{ + env::var, + error +}; use serenity::{ builder::{ CreateMessage, CreateEmbed, CreateEmbedAuthor }, + Context, + Ready, ClientBuilder, GatewayIntents }; -type Error = Box; +type Error = Box; pub static EMBED_COLOR: i32 = 0x5a99c7; static BOT_READY_NOTIFY: u64 = 865673694184996888; async fn on_ready( - ctx: &serenity::Context, - ready: &serenity::Ready, + ctx: &Context, + ready: &Ready, framework: &poise::Framework<(), Error> ) -> Result<(), Error> { println!("Connected to API as {}", ready.user.name); @@ -52,6 +59,7 @@ async fn on_ready( #[tokio::main] async fn main() { let token = var("DISCORD_TOKEN").expect("Expected a \"DISCORD_TOKEN\" in the envvar but none was found"); + let db = controllers::database::DatabaseController::new().await.expect("Failed to connect to database"); let framework = poise::Framework::builder() .options(poise::FrameworkOptions { @@ -67,12 +75,20 @@ async fn main() { }; println!("[{}] {} ran /{}", get_guild_name, ctx.author().name, ctx.command().qualified_name) }), + initialize_owners: true, ..Default::default() }) .setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework))) .build(); - let mut client = ClientBuilder::new(token, GatewayIntents::GUILDS).framework(framework).await.expect("Error creating client"); + let mut client = ClientBuilder::new(token, GatewayIntents::GUILDS) + .framework(framework) + .await.expect("Error creating client"); + + { + let mut data = client.data.write().await; + data.insert::(db); + } if let Err(why) = client.start().await { println!("Client error: {:?}", why); diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..5cf24a3 --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1 @@ +pub mod mpservers; diff --git a/src/models/mpservers.rs b/src/models/mpservers.rs new file mode 100644 index 0000000..0a7ca1f --- /dev/null +++ b/src/models/mpservers.rs @@ -0,0 +1,39 @@ +use crate::controllers::database::DatabaseController; + +// #[derive(Debug)] +pub struct MPServers { + pub server_name: String, + pub guild_owner: i64, + pub is_active: bool, + pub ip_address: String, + pub md5_code: String +} + +#[allow(dead_code)] +impl MPServers { + pub async fn get_servers(guild_id: u64) -> Result { + let client = DatabaseController::new().await?.client; + let row = client.query_one(" + SELECT * FROM mpservers + WHERE guild_owner = $1 + ", &[&(guild_id as i64)]).await?; + + Ok(Self { + server_name: row.get("server_name"), + guild_owner: row.get("guild_owner"), + is_active: row.get("is_active"), + ip_address: row.get("ip_address"), + md5_code: row.get("md5_code") + }) + } + + pub async fn get_server_ip(guild_id: u64, server_name: &str) -> Result<(String, String), tokio_postgres::Error> { + let client = DatabaseController::new().await?.client; + let row = client.query_one(" + SELECT ip_address, md5_code FROM mpservers + WHERE guild_owner = $1 AND server_name = $2 + ", &[&(guild_id as i64), &server_name]).await?; + + Ok((row.get("ip_address"), row.get("md5_code"))) + } +}