Major refactor
This commit is contained in:
89
crates/siren-bot/src/commands/audio/mod.rs
Normal file
89
crates/siren-bot/src/commands/audio/mod.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use crate::error::{Error, Result};
|
||||
use reqwest::Url;
|
||||
use serenity::{
|
||||
all::UserId,
|
||||
client::Cache,
|
||||
model::prelude::{ChannelId, GuildId},
|
||||
};
|
||||
use songbird::Songbird;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub mod mute;
|
||||
pub mod pause;
|
||||
pub mod play;
|
||||
pub mod resume;
|
||||
pub mod skip;
|
||||
pub mod stop;
|
||||
pub mod volume;
|
||||
|
||||
/**
|
||||
* Finds a voice channel that the user is currently in, and attempts to join it.
|
||||
*/
|
||||
pub async fn join_voice_channel(
|
||||
cache: &Arc<Cache>,
|
||||
manager: &Arc<Songbird>,
|
||||
guild_id: &GuildId,
|
||||
user_id: &UserId,
|
||||
) -> Result<ChannelId> {
|
||||
let channel_id = find_voice_channel(cache, guild_id, user_id)?;
|
||||
log::debug!("<{}> Joining channel {}", guild_id.get(), channel_id.get());
|
||||
match manager
|
||||
.join(guild_id.to_owned(), channel_id.to_owned())
|
||||
.await
|
||||
{
|
||||
Ok(_) => Ok(channel_id),
|
||||
Err(e) => {
|
||||
if e.should_leave_server() || e.should_reconnect_driver() {
|
||||
log::debug!("<{}> Cleaning up failed voice connection", guild_id.get());
|
||||
let _ = manager.remove(*guild_id).await;
|
||||
}
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves a voice channel.
|
||||
*/
|
||||
pub async fn leave_voice_channel(manager: &Arc<Songbird>, guild_id: &GuildId) -> Result<()> {
|
||||
if manager.get(guild_id.to_owned()).is_some() {
|
||||
log::debug!("<{}> Disconnecting from channel", guild_id.get());
|
||||
manager.remove(*guild_id).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether the given string is a properly formatted URL.
|
||||
*
|
||||
* Returns `true` if the input string is a valid URL, otherwise `false`.
|
||||
*/
|
||||
fn is_valid_url(url: &str) -> bool {
|
||||
Url::parse(url).is_ok()
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a voice channel that the user is currently in.
|
||||
*/
|
||||
fn find_voice_channel(
|
||||
cache: &Arc<Cache>,
|
||||
guild_id: &GuildId,
|
||||
user_id: &UserId,
|
||||
) -> Result<ChannelId> {
|
||||
let guild = match guild_id.to_guild_cached(cache) {
|
||||
Some(g) => g,
|
||||
None => return Err(Error::new(404, "Guild not found".to_string())),
|
||||
};
|
||||
|
||||
match guild
|
||||
.voice_states
|
||||
.get(&user_id)
|
||||
.and_then(|voice_state| voice_state.channel_id)
|
||||
{
|
||||
Some(channel) => Ok(channel),
|
||||
None => Err(Error::new(
|
||||
400,
|
||||
"User is not in a voice channel".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
54
crates/siren-bot/src/commands/audio/mute.rs
Normal file
54
crates/siren-bot/src/commands/audio/mute.rs
Normal file
@@ -0,0 +1,54 @@
|
||||
use crate::{
|
||||
chat::{edit_response, process_message},
|
||||
handler::get_songbird,
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CreateCommand},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match &command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Mute the track
|
||||
if let Some(handler_lock) = manager.get(guild_id.to_owned()) {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
let is_muted = handler.is_mute();
|
||||
match handler.mute(!is_muted).await {
|
||||
Ok(_) => {
|
||||
if is_muted {
|
||||
log::debug!("<{guild_id}> Unmuted");
|
||||
edit_response(&ctx, &command, "Unmuted".to_string()).await;
|
||||
} else {
|
||||
log::debug!("<{guild_id}> Muted");
|
||||
edit_response(&ctx, &command, "Muted".to_string()).await;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
edit_response(&ctx, &command, format!("Failed to mute: {}", err)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("mute").description("Mute/unmute Siren")
|
||||
}
|
||||
63
crates/siren-bot/src/commands/audio/pause.rs
Normal file
63
crates/siren-bot/src/commands/audio/pause.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use crate::{
|
||||
chat::{edit_response, process_message},
|
||||
error::{Error, Result},
|
||||
handler::get_songbird,
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CreateCommand, GuildId},
|
||||
prelude::*,
|
||||
};
|
||||
use songbird::Songbird;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match &command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Pause the track
|
||||
match pause_track(manager, guild_id).await {
|
||||
Ok(_) => {
|
||||
log::debug!("<{guild_id}> Paused the track");
|
||||
edit_response(&ctx, &command, "Pausing the track".to_string()).await;
|
||||
}
|
||||
Err(err) => edit_response(&ctx, &command, format!("Failed to pause: {}", err)).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pause_track(manager: &Arc<Songbird>, guild_id: &GuildId) -> Result<()> {
|
||||
if let Some(handler_lock) = manager.get(guild_id.to_owned()) {
|
||||
let handler = handler_lock.lock().await;
|
||||
match handler.queue().current() {
|
||||
Some(track) => track.pause()?,
|
||||
None => {
|
||||
return Err(Error {
|
||||
status: 404,
|
||||
details: "No track is currently playing".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("pause").description("Pause the current track")
|
||||
}
|
||||
218
crates/siren-bot/src/commands/audio/play.rs
Normal file
218
crates/siren-bot/src/commands/audio/play.rs
Normal file
@@ -0,0 +1,218 @@
|
||||
use super::{is_valid_url, join_voice_channel, leave_voice_channel};
|
||||
use crate::{
|
||||
chat::{create_message_response, edit_response, process_message},
|
||||
error::{Error, Result},
|
||||
handler::{get_client, get_songbird},
|
||||
ytdlp::{YtDlp, YtDlpItem},
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption},
|
||||
async_trait,
|
||||
model::prelude::GuildId,
|
||||
prelude::*,
|
||||
};
|
||||
use siren_core::data::guilds::GuildCache;
|
||||
use songbird::{
|
||||
Event,
|
||||
EventHandler,
|
||||
Songbird,
|
||||
TrackEvent,
|
||||
input::{Input, YoutubeDl},
|
||||
tracks::TrackHandle,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Process the command options
|
||||
let track_url = match command.data.options.first() {
|
||||
Some(o) => o.value.as_str().unwrap(),
|
||||
None => {
|
||||
log::warn!(
|
||||
"<{}> {} attempted to play a track without a track option",
|
||||
command.guild_id.unwrap(),
|
||||
command.user.id.get()
|
||||
);
|
||||
create_message_response(&ctx, &command, "Track option is missing".to_string(), false).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match &command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Join the user's voice channel
|
||||
match join_voice_channel(&ctx.cache, &manager, guild_id, &command.user.id).await {
|
||||
Ok(channel_id) => {
|
||||
log::debug!(
|
||||
"<{guild_id}> Play command executed on channel {channel_id} with track: {track_url:?}"
|
||||
);
|
||||
// Handle the track url
|
||||
match enqueue_track(manager, guild_id.to_owned(), track_url).await {
|
||||
Ok(items) => {
|
||||
let mut message = format!("Added {} tracks", items.len());
|
||||
if items.len() == 0 {
|
||||
message = "No tracks were played".to_string();
|
||||
log::warn!("<{guild_id}> No tracks were played");
|
||||
if let Err(err) = leave_voice_channel(&manager, guild_id).await {
|
||||
log::error!("Failed to leave voice channel: {}", err);
|
||||
};
|
||||
} else if items.len() == 1 {
|
||||
message = format!("Added **{}**", items[0].get_title());
|
||||
}
|
||||
edit_response(&ctx, &command, message).await;
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to play track: {}", err);
|
||||
if let Err(err) = leave_voice_channel(&manager, guild_id).await {
|
||||
log::error!("Failed to leave voice channel: {}", err);
|
||||
}
|
||||
edit_response(&ctx, &command, format!("Failed to play track: {}", err)).await;
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("<{guild_id}> Failed to join voice channel: {}", err);
|
||||
edit_response(&ctx, &command, format!("{}", err)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn enqueue_track(
|
||||
manager: &Arc<Songbird>,
|
||||
guild_id: GuildId,
|
||||
track_url: &str,
|
||||
) -> Result<Vec<YtDlpItem>> {
|
||||
let mut playlist_items: Vec<YtDlpItem> = Vec::new();
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
let guild = GuildCache::find_by_id(guild_id.get() as i64).await.unwrap();
|
||||
let valid = is_valid_url(&track_url);
|
||||
|
||||
// Check if the URL is valid
|
||||
if !valid {
|
||||
log::warn!("<{guild_id}> Invalid track url: {}", track_url);
|
||||
return Err(Error::new(422, format!("Invalid track url: {}", track_url)));
|
||||
}
|
||||
|
||||
playlist_items = get_ytdlp_items(&track_url)?;
|
||||
|
||||
// Add each track to the queue
|
||||
for item in &playlist_items {
|
||||
let volume = guild.volume as f32 / 100.0;
|
||||
let http_client = get_client();
|
||||
|
||||
let source = YoutubeDl::new(http_client.to_owned(), item.get_url().to_owned());
|
||||
let input: Input = source.into();
|
||||
let track_title = item.get_title().to_owned();
|
||||
|
||||
let track_handle: TrackHandle;
|
||||
track_handle = handler.enqueue_input(input).await;
|
||||
|
||||
// Set the volume
|
||||
let _ = track_handle.set_volume(volume);
|
||||
|
||||
log::debug!("<{guild_id}> Added track: {}", track_title);
|
||||
handler.remove_all_global_events();
|
||||
handler.add_global_event(
|
||||
Event::Track(TrackEvent::End),
|
||||
TrackEndNotifier {
|
||||
guild_id,
|
||||
call: manager.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
if handler.queue().is_empty() {
|
||||
let _ = handler.queue().resume();
|
||||
}
|
||||
}
|
||||
Ok(playlist_items)
|
||||
}
|
||||
|
||||
pub fn get_ytdlp_items(url: &str) -> Result<Vec<YtDlpItem>> {
|
||||
let output = YtDlp::new()
|
||||
.arg("--flat-playlist")
|
||||
.arg("--dump-json")
|
||||
.arg("--no-check-formats")
|
||||
.arg(url)
|
||||
.execute()?;
|
||||
|
||||
// Check if yt-dlp exited successfully; log stderr if not
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
return Err(Error::new(
|
||||
500,
|
||||
format!("yt-dlp failed ({}): {}", output.status, stderr.trim()),
|
||||
));
|
||||
}
|
||||
|
||||
let stdout = String::from_utf8(output.stdout)?;
|
||||
|
||||
let items: Vec<YtDlpItem> = stdout
|
||||
.split('\n')
|
||||
.filter_map(|line| {
|
||||
if line.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
serde_json::from_slice::<YtDlpItem>(line.as_bytes())
|
||||
.map_err(|err| Error::new(500, err.to_string())),
|
||||
)
|
||||
}
|
||||
})
|
||||
.filter_map(|parsed| match parsed {
|
||||
Ok(item) => Some(item),
|
||||
Err(err) => {
|
||||
log::warn!("Failed to parse yt-dlp item: {}", err);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("play")
|
||||
.description("Plays the given track")
|
||||
.add_option(
|
||||
CreateCommandOption::new(CommandOptionType::String, "track", "The track to be played")
|
||||
.required(true),
|
||||
)
|
||||
}
|
||||
|
||||
struct TrackEndNotifier {
|
||||
pub call: Arc<Songbird>,
|
||||
pub guild_id: GuildId,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for TrackEndNotifier {
|
||||
async fn act(&self, ctx: &songbird::events::EventContext<'_>) -> Option<songbird::events::Event> {
|
||||
if let songbird::EventContext::Track(_track_list) = ctx {
|
||||
if let Some(call) = self.call.get(self.guild_id) {
|
||||
let mut handler = call.lock().await;
|
||||
if handler.queue().is_empty() {
|
||||
log::debug!("Queue is empty, leaving voice channel");
|
||||
handler.leave().await.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
63
crates/siren-bot/src/commands/audio/resume.rs
Normal file
63
crates/siren-bot/src/commands/audio/resume.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use crate::{
|
||||
chat::{edit_response, process_message},
|
||||
error::{Error, Result},
|
||||
handler::get_songbird,
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CreateCommand, GuildId},
|
||||
prelude::*,
|
||||
};
|
||||
use songbird::Songbird;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match &command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Resume the track
|
||||
match resume_track(manager, guild_id).await {
|
||||
Ok(_) => {
|
||||
log::debug!("<{guild_id}> Resumed the track");
|
||||
edit_response(&ctx, &command, "resuming the track".to_string()).await;
|
||||
}
|
||||
Err(err) => edit_response(&ctx, &command, format!("Failed to resume: {}", err)).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn resume_track(manager: &Arc<Songbird>, guild_id: &GuildId) -> Result<()> {
|
||||
if let Some(handler_lock) = manager.get(guild_id.to_owned()) {
|
||||
let handler = handler_lock.lock().await;
|
||||
match handler.queue().current() {
|
||||
Some(track) => track.play()?,
|
||||
None => {
|
||||
return Err(Error {
|
||||
status: 404,
|
||||
details: "No track is currently playing".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("resume").description("Resume the current track")
|
||||
}
|
||||
48
crates/siren-bot/src/commands/audio/skip.rs
Normal file
48
crates/siren-bot/src/commands/audio/skip.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use crate::{
|
||||
chat::{edit_response, process_message},
|
||||
handler::get_songbird,
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CreateCommand},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match &command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Skip the track
|
||||
if let Some(handler_lock) = manager.get(guild_id.to_owned()) {
|
||||
let handler = handler_lock.lock().await;
|
||||
match handler.queue().skip() {
|
||||
Ok(_) => {
|
||||
log::debug!("<{guild_id}> Skipped the track");
|
||||
edit_response(&ctx, &command, "Skipping the track".to_string()).await;
|
||||
}
|
||||
Err(err) => {
|
||||
edit_response(&ctx, &command, format!("Failed to skip: {}", err)).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("skip").description("Skip the current track")
|
||||
}
|
||||
42
crates/siren-bot/src/commands/audio/stop.rs
Normal file
42
crates/siren-bot/src/commands/audio/stop.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::{
|
||||
chat::{edit_response, process_message},
|
||||
handler::get_songbird,
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CreateCommand},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match command.guild_id {
|
||||
Some(g) => g,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Stop the track and clear the queue
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let handler = handler_lock.lock().await;
|
||||
handler.queue().stop();
|
||||
log::debug!("<{guild_id}> Stopped the track");
|
||||
edit_response(&ctx, &command, "Stopping the tracks".to_string()).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("stop").description("Stop the current track and clear the queue")
|
||||
}
|
||||
92
crates/siren-bot/src/commands/audio/volume.rs
Normal file
92
crates/siren-bot/src/commands/audio/volume.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
use crate::{
|
||||
chat::{create_message_response, edit_response, process_message},
|
||||
handler::get_songbird,
|
||||
};
|
||||
use serenity::{
|
||||
all::{CommandInteraction, CommandOptionType, CreateCommand, CreateCommandOption},
|
||||
model::prelude::GuildId,
|
||||
prelude::*,
|
||||
};
|
||||
use siren_core::data::guilds::GuildCache;
|
||||
use songbird::Songbird;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn run(ctx: &Context, command: &CommandInteraction) {
|
||||
// Process the command options
|
||||
let volume = match command.data.options.first() {
|
||||
Some(o) => o.value.as_i64().unwrap() as i32,
|
||||
None => {
|
||||
log::warn!(
|
||||
"{} attempted to change the volume without a volume option",
|
||||
command.user.id.get()
|
||||
);
|
||||
create_message_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Volume option is missing".to_string(),
|
||||
false,
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Create the initial response
|
||||
process_message(&ctx, &command, false).await;
|
||||
|
||||
// Get the songbird manager
|
||||
let manager = get_songbird();
|
||||
|
||||
// Extract the guild ID
|
||||
let guild_id = match &command.guild_id {
|
||||
Some(guild_id) => guild_id,
|
||||
None => {
|
||||
edit_response(
|
||||
&ctx,
|
||||
&command,
|
||||
"Unable to find the current server ID".to_string(),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Set the volume
|
||||
set_volume(&manager, guild_id, volume).await;
|
||||
log::debug!("<{guild_id}> Setting the volume to {}", volume);
|
||||
edit_response(&ctx, &command, format!("Setting the volume to {}", volume)).await;
|
||||
}
|
||||
|
||||
pub async fn set_volume(manager: &Arc<Songbird>, guild_id: &GuildId, volume: i32) {
|
||||
// Format volume to f32 bound between 0.0 and 1.0
|
||||
let volume = std::cmp::min(100, std::cmp::max(0, volume));
|
||||
let bound_volume = volume as f32 / 100.0;
|
||||
|
||||
// Update the guild cache
|
||||
let mut guild_cache = GuildCache::find_by_id(guild_id.get() as i64).await.unwrap();
|
||||
guild_cache.volume = volume;
|
||||
guild_cache.update().await.unwrap();
|
||||
|
||||
// Update the volume of the songbird handler
|
||||
if let Some(handler_lock) = manager.get(guild_id.to_owned()) {
|
||||
let handler = handler_lock.lock().await;
|
||||
for (_, track_handle) in handler.queue().current_queue().iter().enumerate() {
|
||||
if let Err(err) = track_handle.set_volume(bound_volume) {
|
||||
log::error!("Unable to set volume: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register() -> CreateCommand {
|
||||
CreateCommand::new("volume")
|
||||
.description("Set the audio player volume")
|
||||
.add_option(
|
||||
CreateCommandOption::new(
|
||||
CommandOptionType::Integer,
|
||||
"volume",
|
||||
"Volume between 0 and 100",
|
||||
)
|
||||
.required(true),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user