Stripped out ui/api
This commit is contained in:
212
src/bot/commands/audio/mod.rs
Normal file
212
src/bot/commands/audio/mod.rs
Normal file
@@ -0,0 +1,212 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use log::{debug, warn};
|
||||
|
||||
use reqwest::Url;
|
||||
use serenity::client::Cache;
|
||||
use serenity::model::application::interaction::{
|
||||
InteractionResponseType, application_command::ApplicationCommandInteraction,
|
||||
};
|
||||
use serenity::model::prelude::{GuildId, ChannelId};
|
||||
use serenity::model::user::User;
|
||||
use serenity::prelude::*;
|
||||
use siren::ServiceError;
|
||||
use songbird::{Call, Songbird};
|
||||
use songbird::input::{Restartable, Input, Metadata, error::Error as SongbirdError};
|
||||
|
||||
use crate::bot::ytdlp::{PlaylistItem, YtDlp};
|
||||
|
||||
pub mod pause;
|
||||
pub mod play;
|
||||
pub mod resume;
|
||||
pub mod skip;
|
||||
pub mod stop;
|
||||
pub mod volume;
|
||||
|
||||
pub async fn join_by_user(
|
||||
cache: &Arc<Cache>,
|
||||
manager: Arc<Songbird>,
|
||||
guild_id_option: &Option<GuildId>,
|
||||
user: &User,
|
||||
) -> Result<(), ServiceError> {
|
||||
let guild_id = match guild_id_option {
|
||||
Some(g) => g,
|
||||
None => {
|
||||
return Err(ServiceError {
|
||||
status: 422,
|
||||
message: format!("{}", "No guild ID set"),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
let channel_id = match find_voice_channel(cache, &guild_id, &user) {
|
||||
Ok(channel) => channel,
|
||||
Err(err) => {
|
||||
return Err(ServiceError {
|
||||
status: 500,
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
join(manager, guild_id, &channel_id).await
|
||||
}
|
||||
|
||||
pub async fn join(
|
||||
manager: Arc<Songbird>,
|
||||
guild_id: &GuildId,
|
||||
channel_id: &ChannelId,
|
||||
) -> Result<(), ServiceError> {
|
||||
debug!("<{}> Joining channel {}", guild_id.0, channel_id.0);
|
||||
let (_handle_lock, success) = manager
|
||||
.join(guild_id.to_owned(), channel_id.to_owned())
|
||||
.await;
|
||||
match success {
|
||||
Ok(s) => Ok(s),
|
||||
Err(err) => {
|
||||
warn!("Failed to join channel: {:?}", err);
|
||||
Err(ServiceError {
|
||||
status: 500,
|
||||
message: err.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn leave(
|
||||
manager: Arc<Songbird>,
|
||||
guild_id_option: &Option<GuildId>,
|
||||
) -> Result<(), String> {
|
||||
let guild_id = match guild_id_option {
|
||||
Some(g) => g,
|
||||
None => {
|
||||
return Err(format!("{}", "No guild ID set"));
|
||||
}
|
||||
};
|
||||
|
||||
if manager.get(*guild_id).is_some() {
|
||||
debug!("<{}> Disconnecting from channel", guild_id.0);
|
||||
if let Err(e) = manager.remove(*guild_id).await {
|
||||
return Err(format!("{}", e));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_voice_channel(
|
||||
cache: &Arc<Cache>,
|
||||
guild_id: &GuildId,
|
||||
user: &User,
|
||||
) -> Result<ChannelId, String> {
|
||||
let guild = match guild_id.to_guild_cached(cache.to_owned()) {
|
||||
Some(g) => g,
|
||||
None => return Err(format!("Guild not found")),
|
||||
};
|
||||
|
||||
match guild
|
||||
.voice_states
|
||||
.get(&user.id)
|
||||
.and_then(|voice_state| voice_state.channel_id)
|
||||
{
|
||||
Some(channel) => Ok(channel),
|
||||
None => return Err(format!("User is not in a voice channel")),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_response(
|
||||
ctx: &Context,
|
||||
command: &ApplicationCommandInteraction,
|
||||
content: String,
|
||||
) -> Result<(), SerenityError> {
|
||||
command
|
||||
.create_interaction_response(
|
||||
&ctx.http,
|
||||
|response: &mut serenity::builder::CreateInteractionResponse<'_>| {
|
||||
response
|
||||
.kind(InteractionResponseType::ChannelMessageWithSource)
|
||||
.interaction_response_data(
|
||||
|message: &mut serenity::builder::CreateInteractionResponseData<'_>| {
|
||||
message.content(content)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn edit_response(
|
||||
ctx: &Context,
|
||||
command: &ApplicationCommandInteraction,
|
||||
content: String,
|
||||
) -> Result<serenity::model::channel::Message, SerenityError> {
|
||||
command
|
||||
.edit_original_interaction_response(
|
||||
&ctx.http,
|
||||
|response: &mut serenity::builder::EditInteractionResponse| response.content(content),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn add_song(
|
||||
call: Arc<Mutex<Call>>,
|
||||
url: &str,
|
||||
lazy: bool,
|
||||
volume: Option<f32>,
|
||||
) -> Result<Metadata, SongbirdError> {
|
||||
let source = Restartable::ytdl(url.to_owned(), lazy).await?;
|
||||
let mut handler = call.lock().await;
|
||||
let track: Input = source.into();
|
||||
let metadata = *track.metadata.clone();
|
||||
let track_handle = handler.enqueue_source(track);
|
||||
if let Some(volume) = volume {
|
||||
let _ = track_handle.set_volume(volume);
|
||||
}
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
pub fn get_playlist_urls(url: &str) -> Result<Vec<PlaylistItem>, ServiceError> {
|
||||
let output = YtDlp::new()
|
||||
.arg("--flat-playlist")
|
||||
.arg("--dump-json")
|
||||
.arg(url)
|
||||
.execute()?;
|
||||
let items: Vec<PlaylistItem> = String::from_utf8(output.stdout)?
|
||||
.split('\n')
|
||||
.filter_map(|line| {
|
||||
if line.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
serde_json::from_slice::<PlaylistItem>(line.as_bytes()).map_err(|err| ServiceError {
|
||||
status: 500,
|
||||
message: err.to_string(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
})
|
||||
.filter_map(|parsed| match parsed {
|
||||
Ok(item) => Some(item),
|
||||
Err(err) => {
|
||||
warn!("Failed to parse playlist item: {}", err);
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn is_valid_url(url: &str) -> (bool, bool) {
|
||||
Url::parse(url).ok().map_or((false, false), |valid_url| {
|
||||
let is_playlist: bool = valid_url
|
||||
.query_pairs()
|
||||
.find(|(key, _)| key == "list")
|
||||
.map_or(false, |_| true);
|
||||
(true, is_playlist)
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_songbird(ctx: &Context) -> Arc<Songbird> {
|
||||
songbird::get(ctx)
|
||||
.await
|
||||
.expect("Songbird Voice client placed in at initialization")
|
||||
}
|
||||
Reference in New Issue
Block a user