From b2059a76c65b694b7f95568f6a419539287e234d Mon Sep 17 00:00:00 2001 From: brotoskyj Date: Mon, 12 Jan 2026 12:36:15 -0500 Subject: [PATCH] Closes #1 --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 111 ++++++++++++++++++++++++----------------- src/modules/updator.rs | 20 +++++--- 4 files changed, 79 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 685bd25..7ab2db9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,7 +4,7 @@ version = 4 [[package]] name = "RhythmWorks" -version = "0.2.0" +version = "0.3.0" dependencies = [ "reqwest", "semver", diff --git a/Cargo.toml b/Cargo.toml index 90da1cd..1e75e8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "RhythmWorks" -version = "0.2.0" +version = "0.3.0" edition = "2024" [dependencies] diff --git a/src/main.rs b/src/main.rs index f460121..c1f7019 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,18 +8,12 @@ use songbird::SerenityInit; use songbird::get; use songbird::input::HlsRequest; use songbird::input::Input; -use songbird::tracks::TrackHandle; -use std::collections::HashMap; use std::io; use std::process::Command; use std::process::Stdio; -use std::sync::Arc; pub mod modules; -type TrackMap = Arc>>; -struct Handler { - track_map: TrackMap, -} +struct Handler; fn get_user_voice_channel(ctx: &Context, msg: &Message) -> Option { let guild_id = msg.guild_id?; let guild = ctx.cache.guild(guild_id)?; @@ -77,61 +71,78 @@ impl EventHandler for Handler { } } if msg.content.starts_with("!play") { - let mut url = msg + let mut yt_url = msg .content .strip_prefix("!play ") .unwrap_or("") .trim() .to_string(); - let separator = "&"; - if let Some(offset) = url.find(separator) { - url.truncate(offset); + if let Some(offset) = yt_url.find('&') { + yt_url.truncate(offset); } - if url.is_empty() { + if yt_url.is_empty() { let _ = msg.reply(&ctx, "Please provide a YouTube URL.").await; return; } let guild_id = msg.guild_id.unwrap(); - let manager = songbird::get(&ctx) - .await - .expect("Songbird Voice client placed in at initialisation.") - .clone(); - let handler_lock = if let Some(handler) = manager.get(guild_id) { - handler + let manager = songbird::get(&ctx).await.unwrap().clone(); + let handler_lock = if let Some(call) = manager.get(guild_id) { + call } else { - let channel_id = get_user_voice_channel(&ctx, &msg); - match channel_id { - Some(c) => manager.join(guild_id, c).await.unwrap(), + let channel_id = match get_user_voice_channel(&ctx, &msg) { + Some(c) => c, None => { let _ = msg.reply(&ctx, "Join a voice channel first!").await; return; } - } + }; + manager.join(guild_id, channel_id).await.unwrap() }; let output = Command::new("./yt-dlp") - .args(["-f", "bestaudio", "-g", &url]) + .args(["-f", "bestaudio", "-g", &yt_url]) .stdout(Stdio::piped()) .output() - .expect("Failed"); - let url_bytes = output.stdout; - let url = String::from_utf8(url_bytes).expect("Failed"); - let mut handler = handler_lock.lock().await; - let input = Input::from(HlsRequest::new(reqwest::Client::new(), url.to_string())); - let track_handle = handler.play_input(input); - let _ = track_handle.play(); - self.track_map - .lock() - .await - .insert(guild_id.into(), track_handle); - let _ = msg.channel_id.say(&ctx.http, "🎶 Playing...").await; + .expect("yt-dlp failed"); + if !output.status.success() { + let _ = msg.reply(&ctx, "Failed to fetch audio stream.").await; + return; + } + let stream_url = String::from_utf8(output.stdout) + .expect("Invalid UTF-8") + .trim() + .to_string(); + let mut call = handler_lock.lock().await; + let input = Input::from(HlsRequest::new(reqwest::Client::new(), stream_url)); + let _ = call.enqueue_input(input).await; + let position = call.queue().len(); + let response = if position == 1 { + "🎶 Now playing!".to_string() + } else { + format!("🎶 Added Song to Queue: #{}", position) + }; + let _ = msg.channel_id.say(&ctx.http, response).await; + } + if msg.content == "!skip" { + let guild_id = match msg.guild_id { + Some(g) => g, + None => return, + }; + let manager = songbird::get(&ctx).await.unwrap(); + if let Some(call) = manager.get(guild_id) { + let call = call.lock().await; + let _ = call.queue().skip(); + let _ = msg.channel_id.say(&ctx.http, "Skipped").await; + } } if msg.content == "!pause" { let guild_id = match msg.guild_id { Some(g) => g, None => return, }; - if let Some(handle) = self.track_map.lock().await.get(&guild_id.into()) { - let _ = handle.pause(); + let manager = songbird::get(&ctx).await.unwrap(); + if let Some(call) = manager.get(guild_id) { + let call = call.lock().await; + let _ = call.queue().pause(); } } if msg.content == "!resume" { @@ -139,8 +150,10 @@ impl EventHandler for Handler { Some(g) => g, None => return, }; - if let Some(handle) = self.track_map.lock().await.get(&guild_id.into()) { - let _ = handle.play(); + let manager = songbird::get(&ctx).await.unwrap(); + if let Some(call) = manager.get(guild_id) { + let call = call.lock().await; + let _ = call.queue().resume(); } } if msg.content == "!stop" { @@ -148,8 +161,10 @@ impl EventHandler for Handler { Some(g) => g, None => return, }; - if let Some(handle) = self.track_map.lock().await.get(&guild_id.into()) { - let _ = handle.stop(); + let manager = songbird::get(&ctx).await.unwrap(); + if let Some(call) = manager.get(guild_id) { + let call = call.lock().await; + let _ = call.queue().stop(); } } } @@ -157,24 +172,28 @@ impl EventHandler for Handler { println!("{} is connected!", ready.user.name); } } + #[tokio::main] async fn main() { - println!("RhythmWorks Copyright (C) 2025 James Brotosky\n + println!( + "RhythmWorks Copyright (C) 2025 James Brotosky\n This program comes with ABSOLUTELY NO WARRANTY\n -This is free software, and you are welcome to redistribute it under certain conditions."); +This is free software, and you are welcome to redistribute it under certain conditions." + ); updator::update().await; println!("Please paste in your bot token"); let mut input = String::new(); - io::stdin().read_line(&mut input).expect("Failed to read line"); + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); let token = input.trim(); let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::GUILDS | GatewayIntents::DIRECT_MESSAGES | GatewayIntents::MESSAGE_CONTENT | GatewayIntents::GUILD_VOICE_STATES; - let track_map: TrackMap = Arc::new(Mutex::new(HashMap::new())); let mut client = Client::builder(&token, intents) - .event_handler(Handler { track_map }) + .event_handler(Handler) .register_songbird() .await .expect("Err creating client"); diff --git a/src/modules/updator.rs b/src/modules/updator.rs index ac293c2..4ebc644 100644 --- a/src/modules/updator.rs +++ b/src/modules/updator.rs @@ -1,15 +1,22 @@ use std::thread; -use serde::Deserialize; -use semver::Version; use reqwest; +use semver::Version; +use serde::Deserialize; #[derive(Deserialize, Debug)] struct Release { tag_name: String, } pub async fn update() { - let body = reqwest::get("https://git.racooncity.org/api/v1/repos/brotoskyj/RhythmWorks/releases/latest").await.unwrap().text().await.unwrap(); + let body = reqwest::get( + "https://git.racooncity.org/api/v1/repos/brotoskyj/RhythmWorks/releases/latest", + ) + .await + .unwrap() + .text() + .await + .unwrap(); let release_info: Release = serde_json::from_str(&body).unwrap(); let latest_version = Version::parse(&release_info.tag_name).unwrap(); let current_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); @@ -18,9 +25,6 @@ pub async fn update() { println!("URL: https://git.racooncity.org/brotoskyj/RhythmWorks/releases"); thread::sleep(std::time::Duration::from_secs(10)); std::process::exit(1); - + } else { } - else { - - } -} \ No newline at end of file +} -- 2.34.1