diff --git a/Cargo.lock b/Cargo.lock index 2f5519e..308622c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1809,7 +1809,7 @@ dependencies = [ [[package]] name = "songbird" version = "0.1.5" -source = "git+https://github.com/serenity-rs/songbird?branch=current#df64ad92e5dc150790be2216c4e79b6fefa4964a" +source = "git+https://github.com/FelixMcFelix/songbird?branch=fix-driver-leave-join#a5c4f915bc24c09460c36156a96b38e2957bceb4" dependencies = [ "async-trait", "async-tungstenite 0.13.1", @@ -1838,7 +1838,7 @@ dependencies = [ [[package]] name = "soundfx-rs" -version = "1.2.0" +version = "1.2.1" dependencies = [ "dashmap", "dotenv", diff --git a/Cargo.toml b/Cargo.toml index 50e4a66..704e869 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "soundfx-rs" -version = "1.2.0" +version = "1.2.2" authors = ["jellywx "] edition = "2018" @@ -12,7 +12,7 @@ tokio = { version = "1.0", features = ["fs", "process", "io-util"] } lazy_static = "1.4" reqwest = "0.11" env_logger = "0.8" -songbird = { git = "https://github.com/serenity-rs/songbird", branch = "current" } +songbird = { git = "https://github.com/FelixMcFelix/songbird", branch = "fix-driver-leave-join" } regex = "1.4" log = "0.4" serde_json = "1.0" diff --git a/src/event_handlers.rs b/src/event_handlers.rs new file mode 100644 index 0000000..92c17cb --- /dev/null +++ b/src/event_handlers.rs @@ -0,0 +1,40 @@ +use serenity::async_trait; +use serenity::model::id::GuildId; +use songbird::Event; +use songbird::EventContext; +use songbird::EventHandler as SongbirdEventHandler; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::RwLock; + +pub struct RestartTrack; + +#[async_trait] +impl SongbirdEventHandler for RestartTrack { + async fn act(&self, ctx: &EventContext<'_>) -> Option { + if let EventContext::Track(&[(_state, track)]) = ctx { + let _ = track.seek_time(Default::default()); + } + + None + } +} + +pub struct UpdateTrackCount { + pub guild_id: GuildId, + pub track_count: Arc>>, +} + +#[async_trait] +impl SongbirdEventHandler for UpdateTrackCount { + async fn act(&self, _ctx: &EventContext<'_>) -> Option { + { + let mut write_lock = self.track_count.write().await; + + let current = write_lock.get(&self.guild_id).cloned(); + write_lock.insert(self.guild_id, current.unwrap_or(1) - 1); + } + + None + } +} diff --git a/src/main.rs b/src/main.rs index b424cc5..ff6377b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,21 +4,26 @@ extern crate lazy_static; extern crate reqwest; mod error; +mod event_handlers; mod framework; mod guild_data; mod sound; -use guild_data::GuildData; -use sound::Sound; +use crate::{ + event_handlers::{RestartTrack, UpdateTrackCount}, + framework::RegexFramework, + guild_data::{CtxGuildData, GuildData}, + sound::Sound, +}; use regex_command_attr::command; use serenity::{ - async_trait, client::{bridge::gateway::GatewayIntents, Client, Context}, framework::standard::{Args, CommandResult}, http::Http, model::{ + channel::Channel, channel::Message, guild::Guild, id::{ChannelId, GuildId, RoleId}, @@ -31,19 +36,17 @@ use serenity::{ use songbird::{ create_player, error::JoinResult, - events::EventHandler as SongbirdEventHandler, ffmpeg, input::{cached::Memory, Input}, - Call, Event, EventContext, SerenityInit, + tracks::TrackHandle, + Call, Event, SerenityInit, TrackEvent, }; use sqlx::mysql::MySqlPool; use dotenv::dotenv; -use crate::{framework::RegexFramework, guild_data::CtxGuildData}; use dashmap::DashMap; -use serenity::model::channel::Channel; use std::{collections::HashMap, convert::TryFrom, env, sync::Arc, time::Duration}; use tokio::sync::{MutexGuard, RwLock}; @@ -71,6 +74,12 @@ impl TypeMapKey for GuildDataCache { type Value = Arc>>>; } +struct GuildTrackCount; + +impl TypeMapKey for GuildTrackCount { + type Value = Arc>>; +} + const THEME_COLOR: u32 = 0x00e0f3; lazy_static! { @@ -150,7 +159,20 @@ impl EventHandler for Handler { if channel.members(&ctx).await.map(|m| m.len()).unwrap_or(0) <= 1 { let songbird = songbird::get(&ctx).await.unwrap(); - let _ = songbird.remove(guild_id).await; + { + let track_count = ctx + .data + .read() + .await + .get::() + .cloned() + .unwrap(); + + let mut write_lock = track_count.write().await; + write_lock.insert(guild_id, 0); + } + + let _ = songbird.leave(guild_id).await; } } } @@ -230,26 +252,24 @@ async fn play_audio( call_handler: &mut MutexGuard<'_, Call>, mysql_pool: MySqlPool, loop_: bool, -) -> Result<(), Box> { - { - let (track, track_handler) = - create_player(sound.store_sound_source(mysql_pool.clone()).await?.into()); +) -> Result> { + let (track, track_handler) = + create_player(sound.store_sound_source(mysql_pool.clone()).await?.into()); - let _ = track_handler.set_volume(volume as f32 / 100.0); + let _ = track_handler.set_volume(volume as f32 / 100.0); - if loop_ { - let _ = track_handler.enable_loop(); - } else { - let _ = track_handler.disable_loop(); - } - - call_handler.play(track); + if loop_ { + let _ = track_handler.enable_loop(); + } else { + let _ = track_handler.disable_loop(); } + call_handler.play(track); + sound.plays += 1; sound.commit(mysql_pool).await?; - Ok(()) + Ok(track_handler) } async fn join_channel( @@ -384,10 +404,12 @@ async fn main() -> Result<(), Box> { .unwrap(); let guild_data_cache = Arc::new(DashMap::new()); + let guild_track_count = Arc::new(RwLock::new(HashMap::new())); let mut data = client.data.write().await; data.insert::(guild_data_cache); + data.insert::(guild_track_count); data.insert::(mysql_pool); data.insert::(Arc::new(reqwest::Client::new())); @@ -581,11 +603,11 @@ async fn play_cmd(ctx: &Context, msg: &Message, args: Args, loop_: bool) -> Comm let (call_handler, _) = join_channel(ctx, guild.clone(), user_channel).await; - let guild_data = ctx.guild_data(guild).await.unwrap(); + let guild_data = ctx.guild_data(guild_id).await.unwrap(); let mut lock = call_handler.lock().await; - play_audio( + let track_handle = play_audio( sound, guild_data.read().await.volume, &mut lock, @@ -593,6 +615,29 @@ async fn play_cmd(ctx: &Context, msg: &Message, args: Args, loop_: bool) -> Comm loop_, ) .await?; + + let track_count = ctx + .data + .read() + .await + .get::() + .cloned() + .unwrap(); + + { + let mut write_lock = track_count.write().await; + + let current = write_lock.get(&guild_id).cloned(); + write_lock.insert(guild_id, current.unwrap_or(0) + 1); + } + + let _ = track_handle.add_event( + Event::Track(TrackEvent::End), + UpdateTrackCount { + guild_id, + track_count, + }, + ); } msg.channel_id @@ -621,19 +666,6 @@ async fn play_cmd(ctx: &Context, msg: &Message, args: Args, loop_: bool) -> Comm Ok(()) } -struct RestartTrack; - -#[async_trait] -impl SongbirdEventHandler for RestartTrack { - async fn act(&self, ctx: &EventContext<'_>) -> Option { - if let EventContext::Track(&[(_state, track)]) = ctx { - let _ = track.seek_time(Default::default()); - } - - None - } -} - #[command] #[permission_level(Managed)] async fn play_ambience(ctx: &Context, msg: &Message, args: Args) -> CommandResult { @@ -656,36 +688,46 @@ async fn play_ambience(ctx: &Context, msg: &Message, args: Args) -> CommandResul let audio_index = ctx.data.read().await.get::().cloned().unwrap(); if let Some(filename) = audio_index.get(&search_name) { + let (track, track_handler) = create_player( + Input::try_from( + Memory::new(ffmpeg(format!("audio/{}", filename)).await.unwrap()).unwrap(), + ) + .unwrap(), + ); + + let (call_handler, _) = join_channel(ctx, guild.clone(), user_channel).await; + let guild_data = ctx.guild_data(guild).await.unwrap(); + { - let (call_handler, _) = join_channel(ctx, guild.clone(), user_channel).await; - - let guild_data = ctx.guild_data(guild).await.unwrap(); - let mut lock = call_handler.lock().await; - // stop anything currently playing - lock.stop(); - - let (track, track_handler) = create_player( - Input::try_from( - Memory::new(ffmpeg(format!("audio/{}", filename)).await.unwrap()) - .unwrap(), - ) - .unwrap(), - ); - - let _ = track_handler.set_volume(guild_data.read().await.volume as f32 / 100.0); - let _ = track_handler.add_event( - Event::Periodic( - track_handler.metadata().duration.unwrap() - Duration::from_millis(500), - None, - ), - RestartTrack {}, - ); - lock.play(track); } + let _ = track_handler.set_volume(guild_data.read().await.volume as f32 / 100.0); + let _ = track_handler.add_event( + Event::Periodic( + track_handler.metadata().duration.unwrap() - Duration::from_millis(200), + None, + ), + RestartTrack {}, + ); + + { + let track_count = ctx + .data + .read() + .await + .get::() + .cloned() + .unwrap(); + + let mut write_lock = track_count.write().await; + + let current = write_lock.get(&msg.guild_id.unwrap()).cloned(); + write_lock.insert(msg.guild_id.unwrap(), current.unwrap_or(0) + 1); + } + msg.channel_id .say(&ctx, format!("Playing ambience **{}**", search_name)) .await?; @@ -725,9 +767,24 @@ __Available ambience sounds:__ #[command] #[permission_level(Managed)] async fn stop_playing(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { + { + let guild_id = msg.guild_id.unwrap(); + + let track_count = ctx + .data + .read() + .await + .get::() + .cloned() + .unwrap(); + + let mut write_lock = track_count.write().await; + write_lock.insert(guild_id, 0); + } + let voice_manager = songbird::get(ctx).await.unwrap(); - let _ = voice_manager.remove(msg.guild_id.unwrap()).await; + let _ = voice_manager.leave(msg.guild_id.unwrap()).await; Ok(()) }