Initial Commit

This commit is contained in:
brotoskyj
2025-12-29 14:16:52 -05:00
commit 2b62bd3683
9 changed files with 5940 additions and 0 deletions
+178
View File
@@ -0,0 +1,178 @@
use modules::*;
use serenity::all::ChannelId;
use serenity::async_trait;
use serenity::model::channel::Message;
use serenity::model::gateway::Ready;
use serenity::prelude::*;
use songbird::SerenityInit;
use songbird::get;
use songbird::input::HlsRequest;
use songbird::input::Input;
use songbird::tracks::TrackHandle;
use std::collections::HashMap;
use std::env;
use std::process::Command;
use std::process::Stdio;
use std::sync::Arc;
pub mod modules;
type TrackMap = Arc<Mutex<HashMap<u64, TrackHandle>>>;
struct Handler {
track_map: TrackMap,
}
fn get_user_voice_channel(ctx: &Context, msg: &Message) -> Option<ChannelId> {
let guild_id = msg.guild_id?;
let guild = ctx.cache.guild(guild_id)?;
guild
.voice_states
.get(&msg.author.id)
.and_then(|vs| vs.channel_id)
}
#[async_trait]
impl EventHandler for Handler {
async fn message(&self, ctx: Context, msg: Message) {
if msg.content == "!ping"
&& let Err(why) = msg.channel_id.say(&ctx.http, "Pong!").await
{
println!("Error sending message: {why:?}");
}
if msg.content == "!join" {
let guild_id = match msg.guild_id {
Some(g) => g,
None => return,
};
let channel_id = match get_user_voice_channel(&ctx, &msg) {
Some(c) => c,
None => {
let _ = msg
.channel_id
.say(&ctx.http, "You must be in a voice channel")
.await;
return;
}
};
let manager = get(&ctx).await.expect("Songbird not initialized").clone();
let _ = manager.join(guild_id, channel_id).await;
let _ = msg
.channel_id
.say(&ctx.http, "Joined your voice channel!")
.await;
}
if msg.content == "!leave" {
let guild_id = match msg.guild_id {
Some(g) => g,
None => return,
};
let manager = songbird::get(&ctx).await.unwrap();
if manager.get(guild_id).is_some() {
let _ = manager.remove(guild_id).await;
let _ = msg
.channel_id
.say(&ctx.http, "Left the voice channel")
.await;
}
}
if msg.content.starts_with("!play") {
let url = msg
.content
.strip_prefix("!play ")
.unwrap_or("")
.trim()
.to_string();
if 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
} else {
let channel_id = get_user_voice_channel(&ctx, &msg);
match channel_id {
Some(c) => manager.join(guild_id, c).await.unwrap(),
None => {
let _ = msg.reply(&ctx, "Join a voice channel first!").await;
return;
}
}
};
let output = Command::new("yt-dlp")
.args(["-f", "bestaudio", "-g", &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;
}
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();
}
}
if msg.content == "!resume" {
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.play();
}
}
if msg.content == "!stop" {
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.stop();
}
}
}
async fn ready(&self, _: Context, ready: Ready) {
println!("{} is connected!", ready.user.name);
}
}
#[tokio::main]
async fn main() {
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.");
updator::update().await;
let token = env::var("DISCORD_TOKEN").expect("Expected a token in the environment");
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 })
.register_songbird()
.await
.expect("Err creating client");
if let Err(why) = client.start().await {
println!("Client error: {why:?}");
}
}
View File
+2
View File
@@ -0,0 +1,2 @@
pub mod updator;
pub mod downloadyt;
+20
View File
@@ -0,0 +1,20 @@
use serde::Deserialize;
use semver::Version;
use reqwest;
#[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 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();
if latest_version > current_version {
println!("Update Available")
}
else {
println!("Already on current version");
}
}