updated everything

This commit is contained in:
jellywx 2021-01-22 17:19:57 +00:00
parent f534d0f788
commit 7466c3d75b
4 changed files with 977 additions and 491 deletions

1293
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,14 @@
[package] [package]
name = "soundfx-rs" name = "soundfx-rs"
version = "1.0.1-rc.6" version = "1.1.0"
authors = ["jude-lafitteIII <judewrs@gmail.com>"] authors = ["jellywx <judesouthworth@pm.me>"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
serenity = { git = "https://github.com/jellywx/serenity", branch = "jellywx-member_permissions", features = ["voice", "collector"] } serenity = { version = "0.10", features = ["voice", "collector"] }
sqlx = { version = "0.3.5", default-features = false, features = ["runtime-tokio", "macros", "mysql", "bigdecimal"] } sqlx = { git = "https://github.com/ant32/sqlx.git", branch = "tokio-1.0", default-features = false, features = ["runtime-tokio-rustls", "macros", "mysql", "bigdecimal"] }
dotenv = "0.15" dotenv = "0.15"
tokio = { version = "0.2", features = ["fs", "process", "io-util"] } tokio = { version = "1.0", features = ["fs", "process", "io-util"] }
lazy_static = "1.4" lazy_static = "1.4"
reqwest = "0.10" reqwest = "0.11"
songbird = "0.1"

View File

@ -11,13 +11,10 @@ use guilddata::GuildData;
use sound::Sound; use sound::Sound;
use serenity::{ use serenity::{
client::{ client::{bridge::gateway::GatewayIntents, Client, Context},
bridge::{gateway::GatewayIntents, voice::ClientVoiceManager},
Client, Context,
},
framework::standard::{ framework::standard::{
macros::{check, command, group, hook}, macros::{check, command, group, hook},
Args, CheckResult, CommandError, CommandResult, DispatchError, Reason, StandardFramework, Args, CommandError, CommandResult, DispatchError, Reason, StandardFramework,
}, },
http::Http, http::Http,
model::{ model::{
@ -26,16 +23,20 @@ use serenity::{
id::{GuildId, RoleId}, id::{GuildId, RoleId},
voice::VoiceState, voice::VoiceState,
}, },
prelude::{Mutex as SerenityMutex, *}, prelude::*,
utils::shard_id, utils::shard_id,
voice::Handler as VoiceHandler,
}; };
use songbird::{Call, SerenityInit};
type CheckResult = Result<(), Reason>;
use sqlx::mysql::MySqlPool; use sqlx::mysql::MySqlPool;
use dotenv::dotenv; use dotenv::dotenv;
use std::{collections::HashMap, env, sync::Arc, time::Duration}; use std::{collections::HashMap, env, sync::Arc, time::Duration};
use tokio::sync::MutexGuard;
struct MySQL; struct MySQL;
@ -43,12 +44,6 @@ impl TypeMapKey for MySQL {
type Value = MySqlPool; type Value = MySqlPool;
} }
struct VoiceManager;
impl TypeMapKey for VoiceManager {
type Value = Arc<SerenityMutex<ClientVoiceManager>>;
}
struct ReqwestClient; struct ReqwestClient;
impl TypeMapKey for ReqwestClient { impl TypeMapKey for ReqwestClient {
@ -109,20 +104,20 @@ async fn self_perm_check(ctx: &Context, msg: &Message, _args: &mut Args) -> Chec
if let Ok(permissions) = permissions_r { if let Ok(permissions) = permissions_r {
if permissions.send_messages() && permissions.embed_links() { if permissions.send_messages() && permissions.embed_links() {
CheckResult::Success Ok(())
} else { } else {
CheckResult::Failure(Reason::Log( Err(Reason::Log(
"Bot does not have enough permissions".to_string(), "Bot does not have enough permissions".to_string(),
)) ))
} }
} else { } else {
CheckResult::Failure(Reason::Log("No perms found".to_string())) Err(Reason::Log("No perms found".to_string()))
} }
} else { } else {
CheckResult::Failure(Reason::Log("No DM commands".to_string())) Err(Reason::Log("No DM commands".to_string()))
} }
} else { } else {
CheckResult::Failure(Reason::Log("Channel not available".to_string())) Err(Reason::Log("Channel not available".to_string()))
} }
} }
@ -173,33 +168,33 @@ SELECT COUNT(1) as count
match role_res { match role_res {
Ok(role_count) => { Ok(role_count) => {
if role_count.count > 0 { if role_count.count > 0 {
CheckResult::Success Ok(())
} }
else { else {
CheckResult::Failure(Reason::User("User has not got a sufficient role. Use `?roles` to set up role restrictions".to_string())) Err(Reason::User("User has not got a sufficient role. Use `?roles` to set up role restrictions".to_string()))
} }
} }
Err(_) => { Err(_) => {
CheckResult::Failure(Reason::User("User has not got a sufficient role. Use `?roles` to set up role restrictions".to_string())) Err(Reason::User("User has not got a sufficient role. Use `?roles` to set up role restrictions".to_string()))
} }
} }
} }
Err(_) => CheckResult::Failure(Reason::User( Err(_) => Err(Reason::User(
"Unexpected error looking up user roles".to_string(), "Unexpected error looking up user roles".to_string(),
)), )),
} }
} }
None => CheckResult::Failure(Reason::User( None => Err(Reason::User(
"Unexpected error looking up guild".to_string(), "Unexpected error looking up guild".to_string(),
)), )),
} }
} }
if perform_permission_check(ctx, &msg).await.is_success() { if perform_permission_check(ctx, &msg).await.is_ok() {
CheckResult::Success Ok(())
} else { } else {
check_for_roles(&ctx, &msg).await check_for_roles(&ctx, &msg).await
} }
@ -219,14 +214,14 @@ async fn perform_permission_check(ctx: &Context, msg: &&Message) -> CheckResult
.unwrap() .unwrap()
.manage_guild() .manage_guild()
{ {
CheckResult::Success Ok(())
} else { } else {
CheckResult::Failure(Reason::User(String::from( Err(Reason::User(String::from(
"User needs `Manage Guild` permission", "User needs `Manage Guild` permission",
))) )))
} }
} else { } else {
CheckResult::Failure(Reason::User(String::from("Guild not cached"))) Err(Reason::User(String::from("Guild not cached")))
} }
} }
@ -295,17 +290,9 @@ impl EventHandler for Handler {
if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx).await if let Some(Channel::Guild(channel)) = channel_id.to_channel_cached(&ctx).await
{ {
if channel.members(&ctx).await.map(|m| m.len()).unwrap_or(0) <= 1 { if channel.members(&ctx).await.map(|m| m.len()).unwrap_or(0) <= 1 {
let voice_manager_lock = ctx let voice_manager = songbird::get(&ctx).await.unwrap();
.data
.read()
.await
.get::<VoiceManager>()
.cloned()
.expect("Could not get VoiceManager from data");
let mut voice_manager = voice_manager_lock.lock().await; let _ = voice_manager.leave(guild_id).await;
voice_manager.leave(guild_id);
} }
} }
} }
@ -351,20 +338,13 @@ SELECT name, id, plays, public, server_id, uploader_id
.await .await
.unwrap(); .unwrap();
let voice_manager_lock = ctx let voice_manager = songbird::get(&ctx).await.unwrap();
.data
.read()
.await
.get::<VoiceManager>()
.cloned()
.expect("Could not get VoiceManager from data");
let mut voice_manager = voice_manager_lock.lock().await; let (handler, _) = voice_manager.join(guild_id, user_channel).await;
if let Some(handler) = voice_manager.join(guild_id, user_channel) { let _ =
let _audio = play_audio(&mut sound, guild_data, &mut handler.lock().await, pool)
play_audio(&mut sound, guild_data, handler, pool).await; .await;
}
} }
} }
} }
@ -376,16 +356,12 @@ SELECT name, id, plays, public, server_id, uploader_id
async fn play_audio( async fn play_audio(
sound: &mut Sound, sound: &mut Sound,
guild: GuildData, guild: GuildData,
handler: &mut VoiceHandler, handler: &mut MutexGuard<'_, Call>,
mysql_pool: MySqlPool, mysql_pool: MySqlPool,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let audio = handler.play_only(sound.store_sound_source(mysql_pool.clone()).await?); let audio = handler.play_source(sound.store_sound_source(mysql_pool.clone()).await?);
{ let _ = audio.set_volume(guild.volume as f32 / 100.0);
let mut locked = audio.lock().await;
locked.volume(guild.volume as f32 / 100.0);
}
sound.plays += 1; sound.plays += 1;
sound.commit(mysql_pool).await?; sound.commit(mysql_pool).await?;
@ -484,12 +460,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
) )
.framework(framework) .framework(framework)
.event_handler(Handler) .event_handler(Handler)
.register_songbird()
.await .await
.expect("Error occurred creating client"); .expect("Error occurred creating client");
{ {
let mysql_pool = let mysql_pool =
MySqlPool::new(&env::var("DATABASE_URL").expect("No database URL provided")) MySqlPool::connect(&env::var("DATABASE_URL").expect("No database URL provided"))
.await .await
.unwrap(); .unwrap();
@ -497,8 +474,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
data.insert::<MySQL>(mysql_pool); data.insert::<MySQL>(mysql_pool);
data.insert::<VoiceManager>(Arc::clone(&client.voice_manager));
data.insert::<ReqwestClient>(Arc::new(reqwest::Client::new())); data.insert::<ReqwestClient>(Arc::new(reqwest::Client::new()));
} }
@ -550,22 +525,13 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
match sound_res { match sound_res {
Some(sound) => { Some(sound) => {
let voice_manager_lock = ctx let voice_manager = songbird::get(ctx).await.unwrap();
.data
.read()
.await
.get::<VoiceManager>()
.cloned()
.expect("Could not get VoiceManager from data");
let mut voice_manager = voice_manager_lock.lock().await; let (call_handler, _) = voice_manager.join(guild_id, user_channel).await;
match voice_manager.join(guild_id, user_channel) { let guild_data = GuildData::get_from_id(guild, pool.clone()).await.unwrap();
Some(handler) => {
let guild_data =
GuildData::get_from_id(guild, pool.clone()).await.unwrap();
play_audio(sound, guild_data, handler, pool).await?; play_audio(sound, guild_data, &mut call_handler.lock().await, pool).await?;
msg.channel_id msg.channel_id
.say( .say(
@ -575,12 +541,6 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult {
.await?; .await?;
} }
None => {
msg.channel_id.say(&ctx, "Failed to join channel").await?;
}
};
}
None => { None => {
msg.channel_id msg.channel_id
.say(&ctx, "Couldn't find sound by term provided") .say(&ctx, "Couldn't find sound by term provided")
@ -1300,21 +1260,9 @@ WHERE
#[command("stop")] #[command("stop")]
async fn stop_playing(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { async fn stop_playing(ctx: &Context, msg: &Message, _args: Args) -> CommandResult {
let voice_manager_lock = ctx let voice_manager = songbird::get(ctx).await.unwrap();
.data
.read()
.await
.get::<VoiceManager>()
.cloned()
.expect("Could not get VoiceManager from data");
let mut voice_manager = voice_manager_lock.lock().await; let _ = voice_manager.leave(msg.guild_id.unwrap()).await;
let manager_opt = voice_manager.get_mut(msg.guild_id.unwrap());
if let Some(manager) = manager_opt {
manager.leave();
}
Ok(()) Ok(())
} }

View File

@ -2,10 +2,10 @@ use super::error::ErrorTypes;
use sqlx::mysql::MySqlPool; use sqlx::mysql::MySqlPool;
use serenity::voice::{ffmpeg, AudioSource}; use tokio::{fs::File, io::AsyncWriteExt, process::Command};
use tokio::{fs::File, process::Command};
use songbird::ffmpeg;
use songbird::input::Input;
use std::{env, path::Path}; use std::{env, path::Path};
pub struct Sound { pub struct Sound {
@ -136,21 +136,19 @@ SELECT src
pub async fn store_sound_source( pub async fn store_sound_source(
&self, &self,
db_pool: MySqlPool, db_pool: MySqlPool,
) -> Result<Box<dyn AudioSource>, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<Input, Box<dyn std::error::Error + Send + Sync>> {
let caching_location = env::var("CACHING_LOCATION").unwrap_or(String::from("/tmp")); let caching_location = env::var("CACHING_LOCATION").unwrap_or(String::from("/tmp"));
let path_name = format!("{}/sound-{}", caching_location, self.id); let path_name = format!("{}/sound-{}", caching_location, self.id);
let path = Path::new(&path_name); let path = Path::new(&path_name);
if !path.exists() { if !path.exists() {
use tokio::prelude::*;
let mut file = File::create(&path).await?; let mut file = File::create(&path).await?;
file.write_all(&self.get_self_src(db_pool).await).await?; file.write_all(&self.get_self_src(db_pool).await).await?;
} }
Ok(ffmpeg(path_name).await?) Ok(ffmpeg(path_name).await.expect("FFMPEG ERROR!"))
} }
pub async fn count_user_sounds( pub async fn count_user_sounds(
@ -264,7 +262,7 @@ DELETE
server_id: u64, server_id: u64,
user_id: u64, user_id: u64,
db_pool: MySqlPool, db_pool: MySqlPool,
) -> Result<u64, Box<dyn std::error::Error + Send + Sync + Send>> { ) -> Result<(), Box<dyn std::error::Error + Send + Sync + Send>> {
async fn process_src(src_url: &str) -> Option<Vec<u8>> { async fn process_src(src_url: &str) -> Option<Vec<u8>> {
let output = Command::new("ffmpeg") let output = Command::new("ffmpeg")
.kill_on_drop(true) .kill_on_drop(true)
@ -312,7 +310,7 @@ INSERT INTO sounds (name, server_id, uploader_id, public, src)
.execute(&db_pool) .execute(&db_pool)
.await .await
{ {
Ok(u) => Ok(u), Ok(_) => Ok(()),
Err(e) => Err(Box::new(e)), Err(e) => Err(Box::new(e)),
} }