track how many sounds are playing in a channel

This commit is contained in:
jellywx 2021-04-10 20:06:10 +01:00
parent 909e8e351a
commit d70f790b3d
4 changed files with 162 additions and 65 deletions

4
Cargo.lock generated
View File

@ -1809,7 +1809,7 @@ dependencies = [
[[package]] [[package]]
name = "songbird" name = "songbird"
version = "0.1.5" 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 = [ dependencies = [
"async-trait", "async-trait",
"async-tungstenite 0.13.1", "async-tungstenite 0.13.1",
@ -1838,7 +1838,7 @@ dependencies = [
[[package]] [[package]]
name = "soundfx-rs" name = "soundfx-rs"
version = "1.2.0" version = "1.2.1"
dependencies = [ dependencies = [
"dashmap", "dashmap",
"dotenv", "dotenv",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "soundfx-rs" name = "soundfx-rs"
version = "1.2.0" version = "1.2.2"
authors = ["jellywx <judesouthworth@pm.me>"] authors = ["jellywx <judesouthworth@pm.me>"]
edition = "2018" edition = "2018"
@ -12,7 +12,7 @@ tokio = { version = "1.0", features = ["fs", "process", "io-util"] }
lazy_static = "1.4" lazy_static = "1.4"
reqwest = "0.11" reqwest = "0.11"
env_logger = "0.8" 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" regex = "1.4"
log = "0.4" log = "0.4"
serde_json = "1.0" serde_json = "1.0"

40
src/event_handlers.rs Normal file
View File

@ -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<Event> {
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<RwLock<HashMap<GuildId, u32>>>,
}
#[async_trait]
impl SongbirdEventHandler for UpdateTrackCount {
async fn act(&self, _ctx: &EventContext<'_>) -> Option<Event> {
{
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
}
}

View File

@ -4,21 +4,26 @@ extern crate lazy_static;
extern crate reqwest; extern crate reqwest;
mod error; mod error;
mod event_handlers;
mod framework; mod framework;
mod guild_data; mod guild_data;
mod sound; mod sound;
use guild_data::GuildData; use crate::{
use sound::Sound; event_handlers::{RestartTrack, UpdateTrackCount},
framework::RegexFramework,
guild_data::{CtxGuildData, GuildData},
sound::Sound,
};
use regex_command_attr::command; use regex_command_attr::command;
use serenity::{ use serenity::{
async_trait,
client::{bridge::gateway::GatewayIntents, Client, Context}, client::{bridge::gateway::GatewayIntents, Client, Context},
framework::standard::{Args, CommandResult}, framework::standard::{Args, CommandResult},
http::Http, http::Http,
model::{ model::{
channel::Channel,
channel::Message, channel::Message,
guild::Guild, guild::Guild,
id::{ChannelId, GuildId, RoleId}, id::{ChannelId, GuildId, RoleId},
@ -31,19 +36,17 @@ use serenity::{
use songbird::{ use songbird::{
create_player, create_player,
error::JoinResult, error::JoinResult,
events::EventHandler as SongbirdEventHandler,
ffmpeg, ffmpeg,
input::{cached::Memory, Input}, input::{cached::Memory, Input},
Call, Event, EventContext, SerenityInit, tracks::TrackHandle,
Call, Event, SerenityInit, TrackEvent,
}; };
use sqlx::mysql::MySqlPool; use sqlx::mysql::MySqlPool;
use dotenv::dotenv; use dotenv::dotenv;
use crate::{framework::RegexFramework, guild_data::CtxGuildData};
use dashmap::DashMap; use dashmap::DashMap;
use serenity::model::channel::Channel;
use std::{collections::HashMap, convert::TryFrom, env, sync::Arc, time::Duration}; use std::{collections::HashMap, convert::TryFrom, env, sync::Arc, time::Duration};
use tokio::sync::{MutexGuard, RwLock}; use tokio::sync::{MutexGuard, RwLock};
@ -71,6 +74,12 @@ impl TypeMapKey for GuildDataCache {
type Value = Arc<DashMap<GuildId, Arc<RwLock<GuildData>>>>; type Value = Arc<DashMap<GuildId, Arc<RwLock<GuildData>>>>;
} }
struct GuildTrackCount;
impl TypeMapKey for GuildTrackCount {
type Value = Arc<RwLock<HashMap<GuildId, u32>>>;
}
const THEME_COLOR: u32 = 0x00e0f3; const THEME_COLOR: u32 = 0x00e0f3;
lazy_static! { lazy_static! {
@ -150,7 +159,20 @@ impl EventHandler for Handler {
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 songbird = songbird::get(&ctx).await.unwrap(); let songbird = songbird::get(&ctx).await.unwrap();
let _ = songbird.remove(guild_id).await; {
let track_count = ctx
.data
.read()
.await
.get::<GuildTrackCount>()
.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>, call_handler: &mut MutexGuard<'_, Call>,
mysql_pool: MySqlPool, mysql_pool: MySqlPool,
loop_: bool, loop_: bool,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> { ) -> Result<TrackHandle, Box<dyn std::error::Error + Send + Sync>> {
{ let (track, track_handler) =
let (track, track_handler) = create_player(sound.store_sound_source(mysql_pool.clone()).await?.into());
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_ { if loop_ {
let _ = track_handler.enable_loop(); let _ = track_handler.enable_loop();
} else { } else {
let _ = track_handler.disable_loop(); let _ = track_handler.disable_loop();
}
call_handler.play(track);
} }
call_handler.play(track);
sound.plays += 1; sound.plays += 1;
sound.commit(mysql_pool).await?; sound.commit(mysql_pool).await?;
Ok(()) Ok(track_handler)
} }
async fn join_channel( async fn join_channel(
@ -384,10 +404,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
.unwrap(); .unwrap();
let guild_data_cache = Arc::new(DashMap::new()); 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; let mut data = client.data.write().await;
data.insert::<GuildDataCache>(guild_data_cache); data.insert::<GuildDataCache>(guild_data_cache);
data.insert::<GuildTrackCount>(guild_track_count);
data.insert::<MySQL>(mysql_pool); data.insert::<MySQL>(mysql_pool);
data.insert::<ReqwestClient>(Arc::new(reqwest::Client::new())); data.insert::<ReqwestClient>(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, _) = let (call_handler, _) =
join_channel(ctx, guild.clone(), user_channel).await; 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; let mut lock = call_handler.lock().await;
play_audio( let track_handle = play_audio(
sound, sound,
guild_data.read().await.volume, guild_data.read().await.volume,
&mut lock, &mut lock,
@ -593,6 +615,29 @@ async fn play_cmd(ctx: &Context, msg: &Message, args: Args, loop_: bool) -> Comm
loop_, loop_,
) )
.await?; .await?;
let track_count = ctx
.data
.read()
.await
.get::<GuildTrackCount>()
.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 msg.channel_id
@ -621,19 +666,6 @@ async fn play_cmd(ctx: &Context, msg: &Message, args: Args, loop_: bool) -> Comm
Ok(()) Ok(())
} }
struct RestartTrack;
#[async_trait]
impl SongbirdEventHandler for RestartTrack {
async fn act(&self, ctx: &EventContext<'_>) -> Option<Event> {
if let EventContext::Track(&[(_state, track)]) = ctx {
let _ = track.seek_time(Default::default());
}
None
}
}
#[command] #[command]
#[permission_level(Managed)] #[permission_level(Managed)]
async fn play_ambience(ctx: &Context, msg: &Message, args: Args) -> CommandResult { 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::<AudioIndex>().cloned().unwrap(); let audio_index = ctx.data.read().await.get::<AudioIndex>().cloned().unwrap();
if let Some(filename) = audio_index.get(&search_name) { 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; 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); 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::<GuildTrackCount>()
.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 msg.channel_id
.say(&ctx, format!("Playing ambience **{}**", search_name)) .say(&ctx, format!("Playing ambience **{}**", search_name))
.await?; .await?;
@ -725,9 +767,24 @@ __Available ambience sounds:__
#[command] #[command]
#[permission_level(Managed)] #[permission_level(Managed)]
async fn stop_playing(ctx: &Context, msg: &Message, _args: Args) -> CommandResult { 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::<GuildTrackCount>()
.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 = songbird::get(ctx).await.unwrap();
let _ = voice_manager.remove(msg.guild_id.unwrap()).await; let _ = voice_manager.leave(msg.guild_id.unwrap()).await;
Ok(()) Ok(())
} }