From 815600709de31d6d715e1d90d9005033679f61a2 Mon Sep 17 00:00:00 2001 From: jude-lafitteIII Date: Wed, 20 May 2020 01:46:41 +0100 Subject: [PATCH] split guilddata, errortypes and sound into separate files --- src/error.rs | 14 ++ src/guilddata.rs | 67 +++++++++ src/main.rs | 375 ++--------------------------------------------- src/sound.rs | 293 ++++++++++++++++++++++++++++++++++++ 4 files changed, 385 insertions(+), 364 deletions(-) create mode 100644 src/error.rs create mode 100644 src/guilddata.rs create mode 100644 src/sound.rs diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..97ec01d --- /dev/null +++ b/src/error.rs @@ -0,0 +1,14 @@ +use std::fmt::Formatter; + +#[derive(Debug)] +pub enum ErrorTypes { + InvalidFile, + NotEnoughRoles, +} + +impl std::error::Error for ErrorTypes {} +impl std::fmt::Display for ErrorTypes { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ErrorTypes") + } +} diff --git a/src/guilddata.rs b/src/guilddata.rs new file mode 100644 index 0000000..513d248 --- /dev/null +++ b/src/guilddata.rs @@ -0,0 +1,67 @@ +use serenity::model::guild::Guild; +use sqlx::mysql::MySqlPool; + +pub struct GuildData { + pub id: u64, + pub name: Option, + pub prefix: String, + pub volume: u8, +} + +impl GuildData { + pub async fn get_from_id(guild_id: u64, db_pool: MySqlPool) -> Option { + let guild = sqlx::query_as!( + GuildData, + " +SELECT * + FROM servers + WHERE id = ? + ", guild_id + ) + .fetch_one(&db_pool) + .await; + + match guild { + Ok(guild) => Some(guild), + + Err(_) => None, + } + } + + pub async fn create_from_guild(guild: Guild, db_pool: MySqlPool) -> Result> { + sqlx::query!( + " +INSERT INTO servers (id, name) + VALUES (?, ?) + ", guild.id.as_u64(), guild.name + ) + .execute(&db_pool) + .await?; + + Ok(GuildData { + id: *guild.id.as_u64(), + name: Some(guild.name.clone()), + prefix: String::from("?"), + volume: 100, + }) + } + + pub async fn commit(&self, db_pool: MySqlPool) -> Result<(), Box> { + sqlx::query!( + " +UPDATE servers +SET + name = ?, + prefix = ?, + volume = ? +WHERE + id = ? + ", + self.name, self.prefix, self.volume, self.id + ) + .execute(&db_pool) + .await?; + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index ea08cb5..54e07ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,14 @@ #[macro_use] extern crate lazy_static; +mod guilddata; +mod sound; +mod error; + +use sound::Sound; +use guilddata::GuildData; +use error::ErrorTypes; + use serenity::{ client::{ bridge::{ @@ -17,7 +25,6 @@ use serenity::{ }, model::{ channel::Message, - guild::Guild, id::{ GuildId, RoleId, @@ -28,11 +35,7 @@ use serenity::{ Mutex as SerenityMutex, * }, - voice::{ - AudioSource, - ffmpeg, - Handler as VoiceHandler, - }, + voice::Handler as VoiceHandler, }; use sqlx::{ @@ -46,8 +49,6 @@ use sqlx::{ use dotenv::dotenv; use tokio::{ - fs::File, - process::Command, sync::{ Mutex, MutexGuard @@ -61,11 +62,9 @@ use std::{ HashSet, }, env, - path::Path, sync::Arc, time::Duration, }; -use std::fmt::Formatter; struct SQLPool; @@ -106,7 +105,7 @@ lazy_static! { static ref DISCONNECT_CYCLES: u8 = { dotenv().unwrap(); - env::var("DISCONNECT_CYCLES").unwrap_or("2").parse::().unwrap() + env::var("DISCONNECT_CYCLES").unwrap_or("2".to_string()).parse::().unwrap() }; } @@ -115,7 +114,7 @@ lazy_static! { struct AllUsers; #[group] -#[commands (play, upload_new_sound, change_volume, delete_sound)] +#[commands(play, upload_new_sound, change_volume, delete_sound)] #[checks(role_check)] struct RoleManagedUsers; @@ -195,358 +194,6 @@ async fn perform_permission_check(ctx: &Context, msg: &&Message) -> CheckResult CheckResult::Failure(Reason::User(String::from("User needs `Manage Guild` permission"))) } -struct Sound { - name: String, - id: u32, - plays: u32, - public: bool, - server_id: u64, - uploader_id: u64, - src: Vec, -} - -#[derive(Debug)] -enum ErrorTypes { - InvalidFile, - NotEnoughRoles, -} - -impl std::error::Error for ErrorTypes {} -impl std::fmt::Display for ErrorTypes { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "ErrorTypes") - } -} - -impl Sound { - async fn search_for_sound(query: &str, guild_id: u64, user_id: u64, db_pool: MySqlPool, strict: bool) -> Result, sqlx::Error> { - - fn extract_id(s: &str) -> Option { - if s.len() > 3 && s.to_lowercase().starts_with("id:") { - match s[3..].parse::() { - Ok(id) => Some(id), - - Err(_) => None - } - } - else { - None - } - } - - if let Some(id) = extract_id(&query) { - let sound = sqlx::query_as_unchecked!( - Self, - " -SELECT * - FROM sounds - WHERE id = ? AND ( - public = 1 OR - uploader_id = ? OR - server_id = ? - ) - ", - id, user_id, guild_id - ) - .fetch_all(&db_pool) - .await?; - - Ok(sound) - } - else { - let name = query; - let sound; - - if strict { - sound = sqlx::query_as_unchecked!( - Self, - " -SELECT * - FROM sounds - WHERE name = ? AND ( - public = 1 OR - uploader_id = ? OR - server_id = ? - ) - ORDER BY rand(), public = 1, server_id = ?, uploader_id = ? - ", - name, user_id, guild_id, guild_id, user_id - ) - .fetch_all(&db_pool) - .await?; - - } - else { - sound = sqlx::query_as_unchecked!( - Self, - " -SELECT * - FROM sounds - WHERE name LIKE CONCAT('%', ?, '%') AND ( - public = 1 OR - uploader_id = ? OR - server_id = ? - ) - ORDER BY rand(), public = 1, server_id = ?, uploader_id = ? - ", - name, user_id, guild_id, guild_id, user_id - ) - .fetch_all(&db_pool) - .await?; - } - - Ok(sound) - } - } - - async fn store_sound_source(&self) -> Result, Box> { - let caching_location = env::var("CACHING_LOCATION").unwrap_or(String::from("/tmp")); - - let path_name = format!("{}/sound-{}", caching_location, self.id); - let path = Path::new(&path_name); - - if !path.exists() { - use tokio::prelude::*; - - let mut file = File::create(&path).await?; - - file.write_all(self.src.as_ref()).await?; - } - - Ok(ffmpeg(path_name).await?) - } - - async fn count_user_sounds(user_id: u64, db_pool: MySqlPool) -> Result { - let c = sqlx::query!( - " -SELECT COUNT(1) as count - FROM sounds - WHERE uploader_id = ? - ", - user_id - ) - .fetch_one(&db_pool) - .await?.count; - - Ok(c as u32) - } - - async fn count_named_user_sounds(user_id: u64, name: &String, db_pool: MySqlPool) -> Result { - let c = sqlx::query!( - " -SELECT COUNT(1) as count - FROM sounds - WHERE - uploader_id = ? AND - name = ? - ", - user_id, name - ) - .fetch_one(&db_pool) - .await?.count; - - Ok(c as u32) - } - - async fn set_as_greet(&self, user_id: u64, db_pool: MySqlPool) -> Result<(), Box> { - sqlx::query!( - " -UPDATE users -SET - join_sound_id = ? -WHERE - user = ? - ", - self.id, user_id - ) - .execute(&db_pool) - .await?; - - Ok(()) - } - - async fn commit(&self, db_pool: MySqlPool) -> Result<(), Box> { - sqlx::query!( - " -UPDATE sounds -SET - plays = ?, - public = ? -WHERE - id = ? - ", - self.plays, self.public, self.id - ) - .execute(&db_pool) - .await?; - - Ok(()) - } - - async fn delete(&self, db_pool: MySqlPool) -> Result<(), Box> { - sqlx::query!( - " -DELETE - FROM sounds - WHERE id = ? - ", - self.id - ) - .execute(&db_pool) - .await?; - - Ok(()) - } - - async fn create_anon(name: &str, src_url: &str, server_id: u64, user_id: u64, db_pool: MySqlPool) -> Result> { - async fn process_src(src_url: &str) -> Option> { - let future = Command::new("ffmpeg") - .arg("-i") - .arg(src_url) - .arg("-loglevel") - .arg("error") - .arg("-b:a") - .arg("28000") - .arg("-f") - .arg("opus") - .arg("-fs") - .arg("1048576") - .arg("pipe:1") - .output(); - - let output = future.await; - - match output { - Ok(out) => { - if out.status.success() { - Some(out.stdout) - } - else { - None - } - } - - Err(_) => None, - } - } - - let source = process_src(src_url).await; - - match source { - Some(data) => { - match sqlx::query!( - " -INSERT INTO sounds (name, server_id, uploader_id, public, src) - VALUES (?, ?, ?, 1, ?) - ", - name, server_id, user_id, data - ) - .execute(&db_pool) - .await { - Ok(u) => Ok(u), - - Err(e) => Err(Box::new(e)) - } - } - - None => Err(Box::new(ErrorTypes::InvalidFile)) - } - } - - async fn get_user_sounds(user_id: u64, db_pool: MySqlPool) -> Result, Box> { - let sounds = sqlx::query_as_unchecked!( - Sound, - " -SELECT * - FROM sounds - WHERE uploader_id = ? - ", - user_id - ).fetch_all(&db_pool).await?; - - Ok(sounds) - } - - async fn get_guild_sounds(guild_id: u64, db_pool: MySqlPool) -> Result, Box> { - let sounds = sqlx::query_as_unchecked!( - Sound, - " -SELECT * - FROM sounds - WHERE server_id = ? - ", - guild_id - ).fetch_all(&db_pool).await?; - - Ok(sounds) - } -} - -struct GuildData { - pub id: u64, - pub name: Option, - pub prefix: String, - pub volume: u8, -} - -impl GuildData { - async fn get_from_id(guild_id: u64, db_pool: MySqlPool) -> Option { - let guild = sqlx::query_as!( - GuildData, - " -SELECT * - FROM servers - WHERE id = ? - ", guild_id - ) - .fetch_one(&db_pool) - .await; - - match guild { - Ok(guild) => Some(guild), - - Err(_) => None, - } - } - - async fn create_from_guild(guild: Guild, db_pool: MySqlPool) -> Result> { - sqlx::query!( - " -INSERT INTO servers (id, name) - VALUES (?, ?) - ", guild.id.as_u64(), guild.name - ) - .execute(&db_pool) - .await?; - - Ok(GuildData { - id: *guild.id.as_u64(), - name: Some(guild.name.clone()), - prefix: String::from("?"), - volume: 100, - }) - } - - async fn commit(&self, db_pool: MySqlPool) -> Result<(), Box> { - sqlx::query!( - " -UPDATE servers -SET - name = ?, - prefix = ?, - volume = ? -WHERE - id = ? - ", - self.name, self.prefix, self.volume, self.id - ) - .execute(&db_pool) - .await?; - - Ok(()) - } -} - // create event handler for bot struct Handler; diff --git a/src/sound.rs b/src/sound.rs new file mode 100644 index 0000000..180f070 --- /dev/null +++ b/src/sound.rs @@ -0,0 +1,293 @@ +use super::error::ErrorTypes; + +use sqlx::mysql::MySqlPool; + +use serenity::voice::{ + AudioSource, + ffmpeg, +}; + +use tokio::{ + fs::File, + process::Command, +}; + +use std::{ + env, + path::Path, +}; + + +pub struct Sound { + pub name: String, + pub id: u32, + pub plays: u32, + pub public: bool, + pub server_id: u64, + pub uploader_id: u64, + pub src: Vec, +} + +impl Sound { + pub async fn search_for_sound(query: &str, guild_id: u64, user_id: u64, db_pool: MySqlPool, strict: bool) -> Result, sqlx::Error> { + + fn extract_id(s: &str) -> Option { + if s.len() > 3 && s.to_lowercase().starts_with("id:") { + match s[3..].parse::() { + Ok(id) => Some(id), + + Err(_) => None + } + } + else { + None + } + } + + if let Some(id) = extract_id(&query) { + let sound = sqlx::query_as_unchecked!( + Self, + " +SELECT * + FROM sounds + WHERE id = ? AND ( + public = 1 OR + uploader_id = ? OR + server_id = ? + ) + ", + id, user_id, guild_id + ) + .fetch_all(&db_pool) + .await?; + + Ok(sound) + } + else { + let name = query; + let sound; + + if strict { + sound = sqlx::query_as_unchecked!( + Self, + " +SELECT * + FROM sounds + WHERE name = ? AND ( + public = 1 OR + uploader_id = ? OR + server_id = ? + ) + ORDER BY rand(), public = 1, server_id = ?, uploader_id = ? + ", + name, user_id, guild_id, guild_id, user_id + ) + .fetch_all(&db_pool) + .await?; + + } + else { + sound = sqlx::query_as_unchecked!( + Self, + " +SELECT * + FROM sounds + WHERE name LIKE CONCAT('%', ?, '%') AND ( + public = 1 OR + uploader_id = ? OR + server_id = ? + ) + ORDER BY rand(), public = 1, server_id = ?, uploader_id = ? + ", + name, user_id, guild_id, guild_id, user_id + ) + .fetch_all(&db_pool) + .await?; + } + + Ok(sound) + } + } + + pub async fn store_sound_source(&self) -> Result, Box> { + let caching_location = env::var("CACHING_LOCATION").unwrap_or(String::from("/tmp")); + + let path_name = format!("{}/sound-{}", caching_location, self.id); + let path = Path::new(&path_name); + + if !path.exists() { + use tokio::prelude::*; + + let mut file = File::create(&path).await?; + + file.write_all(self.src.as_ref()).await?; + } + + Ok(ffmpeg(path_name).await?) + } + + pub async fn count_user_sounds(user_id: u64, db_pool: MySqlPool) -> Result { + let c = sqlx::query!( + " +SELECT COUNT(1) as count + FROM sounds + WHERE uploader_id = ? + ", + user_id + ) + .fetch_one(&db_pool) + .await?.count; + + Ok(c as u32) + } + + pub async fn count_named_user_sounds(user_id: u64, name: &String, db_pool: MySqlPool) -> Result { + let c = sqlx::query!( + " +SELECT COUNT(1) as count + FROM sounds + WHERE + uploader_id = ? AND + name = ? + ", + user_id, name + ) + .fetch_one(&db_pool) + .await?.count; + + Ok(c as u32) + } + + pub async fn set_as_greet(&self, user_id: u64, db_pool: MySqlPool) -> Result<(), Box> { + sqlx::query!( + " +UPDATE users +SET + join_sound_id = ? +WHERE + user = ? + ", + self.id, user_id + ) + .execute(&db_pool) + .await?; + + Ok(()) + } + + pub async fn commit(&self, db_pool: MySqlPool) -> Result<(), Box> { + sqlx::query!( + " +UPDATE sounds +SET + plays = ?, + public = ? +WHERE + id = ? + ", + self.plays, self.public, self.id + ) + .execute(&db_pool) + .await?; + + Ok(()) + } + + pub async fn delete(&self, db_pool: MySqlPool) -> Result<(), Box> { + sqlx::query!( + " +DELETE + FROM sounds + WHERE id = ? + ", + self.id + ) + .execute(&db_pool) + .await?; + + Ok(()) + } + + pub async fn create_anon(name: &str, src_url: &str, server_id: u64, user_id: u64, db_pool: MySqlPool) -> Result> { + async fn process_src(src_url: &str) -> Option> { + let future = Command::new("ffmpeg") + .arg("-i") + .arg(src_url) + .arg("-loglevel") + .arg("error") + .arg("-b:a") + .arg("28000") + .arg("-f") + .arg("opus") + .arg("-fs") + .arg("1048576") + .arg("pipe:1") + .output(); + + let output = future.await; + + match output { + Ok(out) => { + if out.status.success() { + Some(out.stdout) + } + else { + None + } + } + + Err(_) => None, + } + } + + let source = process_src(src_url).await; + + match source { + Some(data) => { + match sqlx::query!( + " +INSERT INTO sounds (name, server_id, uploader_id, public, src) + VALUES (?, ?, ?, 1, ?) + ", + name, server_id, user_id, data + ) + .execute(&db_pool) + .await { + Ok(u) => Ok(u), + + Err(e) => Err(Box::new(e)) + } + } + + None => Err(Box::new(ErrorTypes::InvalidFile)) + } + } + + pub async fn get_user_sounds(user_id: u64, db_pool: MySqlPool) -> Result, Box> { + let sounds = sqlx::query_as_unchecked!( + Sound, + " +SELECT * + FROM sounds + WHERE uploader_id = ? + ", + user_id + ).fetch_all(&db_pool).await?; + + Ok(sounds) + } + + pub async fn get_guild_sounds(guild_id: u64, db_pool: MySqlPool) -> Result, Box> { + let sounds = sqlx::query_as_unchecked!( + Sound, + " +SELECT * + FROM sounds + WHERE server_id = ? + ", + guild_id + ).fetch_all(&db_pool).await?; + + Ok(sounds) + } +}