From 6f66564377486f7fd62bed3938710f806d11fb34 Mon Sep 17 00:00:00 2001 From: Benjamin Sherriff Date: Wed, 5 Jul 2023 16:44:14 -0400 Subject: [PATCH] Working on play command, unable to get source to work --- Cargo.lock | 288 +------------------------------------ Cargo.toml | 28 +++- Dockerfile | 24 +++- README.md | 2 + src/commands/audio/mod.rs | 101 +++++++++++-- src/commands/audio/play.rs | 116 +++++++-------- 6 files changed, 192 insertions(+), 367 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7f62e6c..123fed6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,38 +36,12 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "arrayvec" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" -[[package]] -name = "async-recursion" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.23", -] - [[package]] name = "async-trait" version = "0.1.69" @@ -205,19 +179,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "chrono" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits 0.2.15", - "serde", - "winapi", -] - [[package]] name = "cipher" version = "0.3.0" @@ -247,22 +208,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - [[package]] name = "cpufeatures" version = "0.2.8" @@ -403,15 +348,6 @@ dependencies = [ "libc", ] -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - [[package]] name = "flate2" version = "1.0.26" @@ -441,21 +377,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.0" @@ -705,42 +626,6 @@ dependencies = [ "tokio-rustls 0.24.1", ] -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "idna" version = "0.4.0" @@ -761,26 +646,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys", -] - [[package]] name = "ipnet" version = "2.8.0" @@ -794,7 +659,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24fddda5af7e54bf7da53067d6e802dbcc381d0a8eef629df528e3ebf68755cb" dependencies = [ "hermit-abi", - "rustix 0.38.2", + "rustix", "windows-sys", ] @@ -831,12 +696,6 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" -[[package]] -name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - [[package]] name = "linux-raw-sys" version = "0.4.3" @@ -934,24 +793,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1011,50 +852,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" -[[package]] -name = "openssl" -version = "0.10.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.23", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "ordered-float" version = "2.10.0" @@ -1286,13 +1083,11 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", - "hyper-tls", "ipnet", "js-sys", "log", "mime", "mime_guess", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -1302,7 +1097,6 @@ dependencies = [ "serde_json", "serde_urlencoded", "tokio", - "tokio-native-tls", "tokio-rustls 0.24.1", "tokio-util", "tower-service", @@ -1336,20 +1130,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustix" -version = "0.37.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8818fa822adcc98b18fedbb3632a6a33213c070556b5aa7c4c8cc21cff565c4c" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys", -] - [[package]] name = "rustix" version = "0.38.2" @@ -1359,7 +1139,7 @@ dependencies = [ "bitflags 2.3.3", "errno", "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys", "windows-sys", ] @@ -1428,15 +1208,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - [[package]] name = "scoped-tls" version = "1.0.1" @@ -1459,29 +1230,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "security-framework" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.166" @@ -1558,7 +1306,6 @@ dependencies = [ "bitflags 1.3.2", "bytes", "cfg-if", - "chrono", "command_attr", "dashmap", "flate2", @@ -1627,7 +1374,6 @@ dependencies = [ name = "siren" version = "0.2.0" dependencies = [ - "async-recursion", "dotenv", "env_logger", "log", @@ -1770,20 +1516,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" -dependencies = [ - "autocfg", - "cfg-if", - "fastrand", - "redox_syscall", - "rustix 0.37.22", - "windows-sys", -] - [[package]] name = "termcolor" version = "1.2.0" @@ -1895,16 +1627,6 @@ dependencies = [ "syn 2.0.23", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.23.4" @@ -2143,12 +1865,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index a96b0a0..e14b99a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,12 +7,28 @@ edition = "2021" [dependencies] dotenv = "0.15.0" -serenity = { version = "0.11", features = ["http"] } -tokio = { version = "1.21.2", features = ["macros", "rt-multi-thread"] } -serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -reqwest = { version = "0.11.18", features = ["json"] } -async-recursion = "1.0.4" log = "0.4.19" -songbird = "0.3.2" env_logger = "0.10.0" + +[dependencies.serenity] +version = "0.11.6" +default-features = false +features = ["client", "gateway", "rustls_backend", "model", "voice", "cache", "framework", "standard_framework"] + +[dependencies.songbird] +version = "0.3.2" +features = ["builtin-queue", "yt-dlp"] + +[dependencies.tokio] +version = "1.29.1" +features = ["macros", "rt-multi-thread"] + +[dependencies.serde] +version = "1.0" +features = ["derive"] + +[dependencies.reqwest] +version = "0.11.18" +default-features = false +features = ["json", "rustls-tls"] \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 751e8dd..3c57b30 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,20 @@ FROM rust:1.67 as builder -RUN apt-get update && apt-get install -y cmake && rm -rf /var/lib/apt/lists/* -WORKDIR /usr/src/siren +WORKDIR /siren +RUN apt-get update && apt-get install -y cmake && apt-get auto-remove -y COPY . . -RUN cargo install --path . +RUN cargo build --release --bin siren -FROM debian:bullseye-slim -RUN apt-get update && apt-get install -y libopus-dev ffmpeg youtube-dl && rm -rf /var/lib/apt/lists/* -COPY --from=builder /usr/local/cargo/bin/siren /usr/local/bin/siren -CMD ["siren"] +FROM debian:bullseye-slim as packages +WORKDIR /packages +RUN apt-get update && apt-get install -y curl tar xz-utils +RUN curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux > yt-dlp && \ + chmod +x yt-dlp +RUN curl -L https://github.com/yt-dlp/FFmpeg-Builds/releases/latest/download/ffmpeg-master-latest-linux64-gpl.tar.xz > ffmpeg.tar.xz && \ + tar -xJf ffmpeg.tar.xz --wildcards */bin/ffmpeg --transform='s/^.*\///' && rm ffmpeg.tar.xz + +FROM debian:bullseye-slim as runtime +WORKDIR /siren +RUN apt-get update && apt-get install -y libopus-dev ffmpeg youtube-dl +COPY --from=builder /siren/target/release/siren siren +COPY --from=packages /packages /usr/bin +CMD ["./siren"] diff --git a/README.md b/README.md index ad04f26..3f4b127 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ sudo apt install ffmpeg sudo apt apt install youtube-dl ``` +Requires [yt-dlp](https://github.com/yt-dlp/yt-dlp#installation) and potentially [yt-dlp FFmpeg Static Auto-Builds](https://github.com/yt-dlp/FFmpeg-Builds) + Copy `.env.TEMPLATE` to `.env` and fill out the fields Run `cargo run` to begin the application diff --git a/src/commands/audio/mod.rs b/src/commands/audio/mod.rs index 1d3c3f1..7ea6d14 100644 --- a/src/commands/audio/mod.rs +++ b/src/commands/audio/mod.rs @@ -1,10 +1,13 @@ +use std::sync::Arc; + use log::debug; use serenity::model::application::interaction::{InteractionResponseType, application_command::ApplicationCommandInteraction}; use serenity::model::prelude::{GuildId, ChannelId}; use serenity::model::user::User; use serenity::prelude::*; -use songbird::id::GuildId as SongbirdGuildId; +use songbird::Call; +use songbird::input::{Restartable, Input, Metadata, error::Error as SongbirdError}; pub mod pause; pub mod play; @@ -13,6 +16,15 @@ pub mod skip; pub mod stop; pub mod volume; +/// Joins a Discord voice channel. +/// +/// # Arguments +/// - ctx - The context of the command. +/// - guild_id_option - The guild ID of the guild to join. +/// - user - The user that is requesting to join the voice channel. +/// +/// # Returns +/// Result<(), String> - Ok if the bot successfully joined the voice channel, Err if there was an error. pub async fn join(ctx: &Context, guild_id_option: &Option, user: &User) -> Result<(), String> { let guild_id = match guild_id_option { Some(g) => g, @@ -35,6 +47,14 @@ pub async fn join(ctx: &Context, guild_id_option: &Option, user: &User) } } +/// Leaves a Discord voice channel. +/// +/// # Arguments +/// - ctx - The context of the command. +/// - guild_id_option - The guild ID of the guild to leave. +/// +/// # Returns +/// Result<(), String> - Ok if the bot successfully left the voice channel, Err if there was an error. pub async fn leave(ctx: &Context, guild_id_option: &Option) -> Result<(), String> { let guild_id = match guild_id_option { Some(g) => g, @@ -43,20 +63,25 @@ pub async fn leave(ctx: &Context, guild_id_option: &Option) -> Result<( } }; - let songbird_guild_id = SongbirdGuildId { - 0: guild_id.0 - }; - let manager = songbird::get(ctx).await.expect("Songbird Voice client placed in at initialization").clone(); - if manager.get(songbird_guild_id).is_some() { + if manager.get(*guild_id).is_some() { debug!("<{}> Disconnecting from channel", guild_id.0); - if let Err(e) = manager.remove(songbird_guild_id).await { + if let Err(e) = manager.remove(*guild_id).await { return Err(format!("{}", e)) } } Ok(()) } +/// Finds the voice channel that the user is in. +/// +/// # Arguments +/// - ctx - The context of the command. +/// - guild_id - The guild ID of the guild to search. +/// - user - The user to search for. +/// +/// # Returns +/// Result - Ok if the user is in a voice channel, Err if the user is not in a voice channel. fn find_voice_channel(ctx: &Context, guild_id: &GuildId, user: &User) -> Result { let guild = match guild_id.to_guild_cached(ctx.cache.to_owned()) { Some(g) => g, @@ -69,16 +94,70 @@ fn find_voice_channel(ctx: &Context, guild_id: &GuildId, user: &User) -> Result< } } +/// Creates a response to an interaction. +/// +/// # Arguments +/// - ctx - The context of the command. +/// - command - The command that was sent. +/// - content - The content of the response. +/// +/// # Returns +/// Result<(), SerenityError> - Ok if the response was created successfully, Err if there was an error. 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 + response + .kind(InteractionResponseType::ChannelMessageWithSource) + .interaction_response_data(|message: &mut serenity::builder::CreateInteractionResponseData<'_>| message.content(content)) + }).await } +/// Edits a response to an interaction. +/// +/// # Arguments +/// - ctx - The context of the command. +/// - command - The command that was sent. +/// - content - The content of the response. +/// +/// # Returns +/// Result - Ok if the response was edited successfully, Err if there was an error. pub async fn edit_response(ctx: &Context, command: &ApplicationCommandInteraction, content: String) -> Result { command.edit_original_interaction_response(&ctx.http, |response: &mut serenity::builder::EditInteractionResponse| { response.content(content) }).await } + +/// Adds a song to the queue. +/// +/// # Arguments +/// - call - The call to add the song to. +/// - url - The URL of the song to add. +/// - lazy - Whether or not to lazy load the song. +/// +/// # Returns +/// Result - Ok if the song was added successfully, Err if there was an error. +pub async fn add_song(call: Arc>, url: &str, lazy: bool) -> Result { + let source = if is_valid_url(url) { + Restartable::ytdl(url.to_owned(), lazy).await? + } else { + Restartable::ytdl_search(url, lazy).await? + }; + let mut handler = call.lock().await; + let track: Input = source.into(); + let metadata = *track.metadata.clone(); + handler.enqueue_source(track); + Ok(metadata) +} + +/// Checks if a string is a valid URL. +/// +/// # Arguments +/// - url - The string to check. +/// +/// # Returns +/// bool - True if the string is a valid URL, false if it is not. +fn is_valid_url(url: &str) -> bool { + match url.parse::() { + Ok(_) => return true, + Err(_) => return false + } +} diff --git a/src/commands/audio/play.rs b/src/commands/audio/play.rs index 7838b17..ad144f7 100644 --- a/src/commands/audio/play.rs +++ b/src/commands/audio/play.rs @@ -1,89 +1,91 @@ use log::{debug, warn, error}; use serenity::prelude::*; -use serenity::builder::{CreateApplicationCommand}; +use serenity::builder::CreateApplicationCommand; use serenity::model::application::interaction::application_command::ApplicationCommandInteraction; -use songbird::id::GuildId as SongbirdGuildId; -use songbird::input; -use crate::commands::audio::{join, leave}; +use crate::commands::audio::{join, leave, add_song}; use super::{create_response, edit_response}; pub async fn run(ctx: &Context, command: &ApplicationCommandInteraction) { - let track_option = match command.data.options.get(0) { + // Get the track url + let track_url = match command.data.options.get(0) { Some(t) => match &t.value { Some(v) => match v.as_str() { Some(s) => s.to_owned(), None => { warn!("Missing track option"); - return + if let Err(why) = create_response(&ctx, &command, format!("Track option is missing")).await { + error!("Failed to create response message: {}", why); + } + return; } } None => { warn!("Missing track option"); - return + if let Err(why) = create_response(&ctx, &command, format!("Track option is missing")).await { + error!("Failed to create response message: {}", why); + } + return; } } None => { warn!("Missing track option"); - return + if let Err(why) = create_response(&ctx, &command, format!("Track option is missing")).await { + error!("Failed to create response message: {}", why); + } + return; } }; - match create_response(&ctx, &command, format!("Playing track \"{}\"", track_option)).await { + // Create the initial response + if let Err(why) = create_response(&ctx, &command, format!("Processing command...")).await { + error!("Failed to create response message: {}", why); + return; + } + + match join(&ctx, &command.guild_id, &command.user).await { Ok(_) => { - match join(&ctx, &command.guild_id, &command.user).await { - Ok(_) => { - let guild_id = match command.guild_id { - Some(g) => g, - None => { - let _ = edit_response(&ctx, &command, "Unable to join voice channel".to_string()); - return; - } - }; - debug!("Play command executed with track: {:?}", track_option); - - let songbird_guild_id = SongbirdGuildId { - 0: guild_id.0 - }; - let manager = songbird::get(ctx).await.expect("Songbird Voice client placed in at initialization").clone(); - if let Some(handler_lock) = manager.get(songbird_guild_id) { - let mut handler = handler_lock.lock().await; - let source: input::Input; - if track_option.starts_with("http") { - // Play remote track - source = match input::ytdl(&track_option).await { - Ok(source) => source, - Err(why) => { - warn!("Unable to get source: {}", why); - let _ = leave(&ctx, &command.guild_id).await; - return; - } - }; - } else if track_option.starts_with("#") { - // Play tracks based on tag - let _ = leave(&ctx, &command.guild_id).await; - return; - } else { - // Play local track - let _ = leave(&ctx, &command.guild_id).await; - return; - }; - - let _song = handler.play_source(source); + let guild_id = match command.guild_id { + Some(g) => g, + None => { + if let Err(why) = edit_response(&ctx, &command, "Unable to join voice channel".to_string()).await { + error!("Failed to edit response message: {}", why); } - }, - Err(err) => { - warn!("{}", err); - let _ = edit_response(&ctx, &command, "Unable to join voice channel".to_string()); + return; } + }; + debug!("Play command executed with track: {:?}", track_url); + + let manager = songbird::get(ctx).await.expect("Songbird Voice client placed in at initialization").clone(); + if let Some(handler_lock) = manager.get(guild_id) { + match add_song(handler_lock, &track_url, true).await { + Ok(added_song) => { + let track_title = added_song.title.unwrap(); + debug!("Added song: {}", track_title); + if let Err(why) = edit_response(&ctx, &command, format!("Added song to queue: {}", track_title)).await { + error!("Failed to edit response message: {}", why); + } + } + Err(why) => { + warn!("Failed to add song: {}", why); + if let Err(why) = edit_response(&ctx, &command, format!("Failed to add song: {}", why)).await { + error!("Failed to edit response message: {}", why); + } + if let Err(why) = leave(&ctx, &command.guild_id).await { + error!("Failed to leave voice channel: {}", why); + } + return; + } + }; + } + }, + Err(err) => { + warn!("{}", err); + if let Err(why) = edit_response(&ctx, &command, "Unable to join voice channel".to_string()).await { + error!("Failed to edit response message: {}", why); } - } - Err(why) => { - error!("Failed to create a response message: {}", why); - let _ = edit_response(&ctx, &command, "Unable to play track".to_string()); - return; } } }