This commit is contained in:
parent
c87562b36a
commit
5483ef3595
5 changed files with 2977 additions and 421 deletions
34
src/main.rs
34
src/main.rs
|
|
@ -2,19 +2,49 @@ mod emergency_report;
|
|||
mod error_codes;
|
||||
mod fest_fax;
|
||||
mod miiverse_mod_application;
|
||||
mod music;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use regex::Regex;
|
||||
use serenity::async_trait;
|
||||
use serenity::prelude::*;
|
||||
use songbird::SerenityInit;
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn main() {
|
||||
match Command::new("ffmpeg").arg("-version").output() {
|
||||
Ok(output) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let first_line = stdout.lines().next().unwrap_or("");
|
||||
println!("detected ffmpeg ver {}", first_line);
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!("'ffmpeg' was not found in your system PATH");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
match Command::new("yt-dlp").arg("--version").output() {
|
||||
Ok(output) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let version = stdout.trim();
|
||||
println!("detected yt-dlp ver {}", version);
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!("'yt-dlp' was not found in your system PATH");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
let token = env::var("SPACEBOT_TOKEN").expect("Token not specified");
|
||||
let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
|
||||
let intents = GatewayIntents::GUILD_MESSAGES
|
||||
| GatewayIntents::MESSAGE_CONTENT
|
||||
| GatewayIntents::GUILDS
|
||||
| GatewayIntents::GUILD_VOICE_STATES;
|
||||
|
||||
let mut client = Client::builder(&token, intents)
|
||||
.event_handler(error_codes::ErrorCodeHandler)
|
||||
|
|
@ -24,6 +54,8 @@ async fn main() {
|
|||
.event_handler(emergency_report::EmergencyReportHandler::default())
|
||||
.event_handler(miiverse_mod_application::MiiverseModApplicationHandler::default())
|
||||
//.event_handler(fest_fax::FestFaxHandler::default())
|
||||
.event_handler(music::MusicHandler::default())
|
||||
.register_songbird()
|
||||
.await
|
||||
.expect("unable to create client");
|
||||
|
||||
|
|
|
|||
271
src/music.rs
Normal file
271
src/music.rs
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
//note to anyone reading my commits lol: not exactly efficient but this probs will only be used internally so who gaf!! meow meow
|
||||
use serenity::async_trait;
|
||||
use serenity::model::channel::Message;
|
||||
use serenity::prelude::*;
|
||||
use serenity::builder::{CreateMessage, CreateEmbed, CreateEmbedAuthor};
|
||||
use serenity::model::id::{GuildId, ChannelId};
|
||||
use songbird::input::{YoutubeDl, Input, Compose};
|
||||
use dashmap::DashMap;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
use std::env;
|
||||
|
||||
static PLAYLISTS: Lazy<DashMap<u64, Vec<String>>> = Lazy::new(DashMap::new);
|
||||
|
||||
pub struct MusicHandler {
|
||||
prefix: String,
|
||||
}
|
||||
|
||||
impl Default for MusicHandler {
|
||||
fn default() -> Self {
|
||||
// account for lowercase & uppercase... cuz that was pissing me off for no reason that it didnt??
|
||||
let prefix = env::var("PREFIX")
|
||||
.or_else(|_| env::var("prefix"))
|
||||
.unwrap_or_else(|_| "space!".to_string()) // fallback to space!, ngl binder probably wont check so hopefully this will stay :3
|
||||
.to_lowercase();
|
||||
|
||||
Self { prefix }
|
||||
}
|
||||
}
|
||||
|
||||
async fn join_channel(
|
||||
ctx: &Context,
|
||||
msg: &Message,
|
||||
guild_id: GuildId,
|
||||
channel_id: ChannelId
|
||||
) -> Result<Arc<Mutex<songbird::Call>>, &'static str> {
|
||||
let manager = songbird::get(ctx).await.expect("Songbird client not initialized").clone();
|
||||
|
||||
if manager.get(guild_id).is_some() {
|
||||
return Err("bubs im already in another channel...");
|
||||
}
|
||||
|
||||
if manager.join(guild_id, channel_id).await.is_ok() {
|
||||
PLAYLISTS.insert(guild_id.get(), Vec::new());
|
||||
if let Some(handler) = manager.get(guild_id) {
|
||||
return Ok(handler);
|
||||
}
|
||||
}
|
||||
|
||||
Err("Failed to join the voice channel.")
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl EventHandler for MusicHandler {
|
||||
async fn message(&self, ctx: Context, msg: Message) {
|
||||
if msg.author.bot { return; }
|
||||
|
||||
let content_lower = msg.content.trim_start().to_lowercase();
|
||||
|
||||
if !content_lower.starts_with(&self.prefix) { return; }
|
||||
|
||||
let original_content = msg.content.trim_start();
|
||||
let stripped_content = &original_content[self.prefix.len()..];
|
||||
let args: Vec<&str> = stripped_content.split_whitespace().collect();
|
||||
if args.is_empty() { return; }
|
||||
|
||||
match args[0].to_lowercase().as_str() {
|
||||
"join" => {
|
||||
let guild = msg.guild(&ctx.cache).unwrap().clone();
|
||||
let guild_id = guild.id;
|
||||
|
||||
let voice_channel_id = guild
|
||||
.voice_states
|
||||
.get(&msg.author.id)
|
||||
.and_then(|state| state.channel_id);
|
||||
|
||||
match voice_channel_id {
|
||||
Some(channel_id) => {
|
||||
if let Err(err_msg) = join_channel(&ctx, &msg, guild_id, channel_id).await {
|
||||
let builder = CreateMessage::new().content(err_msg);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
}
|
||||
}
|
||||
None => {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("You must be in a voice channel to use ts!!!!!")
|
||||
.color(16728123);
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"leave" => {
|
||||
let guild_id = msg.guild_id.unwrap();
|
||||
let manager = songbird::get(&ctx).await.expect("Songbird client not initialized").clone();
|
||||
|
||||
if manager.get(guild_id).is_some() {
|
||||
let _ = manager.leave(guild_id).await;
|
||||
let _ = manager.remove(guild_id).await;
|
||||
PLAYLISTS.remove(&guild_id.get());
|
||||
} else {
|
||||
let builder = CreateMessage::new().content("uuuuhhhhhhhh i think i have dementia...");
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
}
|
||||
}
|
||||
|
||||
"play" => {
|
||||
let _ = msg.delete(&ctx.http).await;
|
||||
|
||||
let guild = msg.guild(&ctx.cache).unwrap().clone();
|
||||
let guild_id = guild.id;
|
||||
let manager = songbird::get(&ctx).await.expect("Songbird client not initialized").clone();
|
||||
|
||||
let voice_channel_id = guild
|
||||
.voice_states
|
||||
.get(&msg.author.id)
|
||||
.and_then(|state| state.channel_id);
|
||||
|
||||
let handler_lock = match manager.get(guild_id) {
|
||||
Some(handler) => handler,
|
||||
None => match voice_channel_id {
|
||||
Some(channel_id) => match join_channel(&ctx, &msg, guild_id, channel_id).await {
|
||||
Ok(handler) => handler,
|
||||
Err(err_msg) => {
|
||||
let builder = CreateMessage::new().content(err_msg);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
return;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("IM NOT IN A VOICE CHANNEL THOUGH...")
|
||||
.color(16728123)
|
||||
.description("what kinda drugs you on?? in fact.. lemme have some of those.")
|
||||
.author(CreateEmbedAuthor::new("help: use join first"));
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if args.len() < 2 {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("You must be in a voice channel to use ts!!!!!")
|
||||
.color(16728123);
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let url = args[1].to_string();
|
||||
|
||||
if !url.contains("youtube.com") && !url.contains("youtu.be") {
|
||||
let embed = CreateEmbed::new()
|
||||
.title("You must be in a voice channel to use ts!!!!!")
|
||||
.color(16728123);
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let is_playing = {
|
||||
let handler = handler_lock.lock().await;
|
||||
!handler.queue().is_empty()
|
||||
};
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
let mut src = YoutubeDl::new(client, url.clone());
|
||||
|
||||
match src.aux_metadata().await {
|
||||
Ok(metadata) => {
|
||||
let mut handler = handler_lock.lock().await;
|
||||
let input: Input = src.into();
|
||||
|
||||
let queue = handler.queue().clone();
|
||||
let _ = queue.add_source(input, &mut *handler).await;
|
||||
|
||||
let mut queue_list = PLAYLISTS.entry(guild_id.get()).or_insert_with(Vec::new);
|
||||
queue_list.push(url);
|
||||
|
||||
let current_position = queue_list.len();
|
||||
|
||||
let title = metadata.title.unwrap_or_else(|| "Unknown Track".to_string());
|
||||
let artist = metadata.artist.or(metadata.channel).unwrap_or_else(|| "Unknown Artist".to_string());
|
||||
let thumbnail = metadata.thumbnail.unwrap_or_default();
|
||||
|
||||
let display_name = msg.author.global_name.as_ref().unwrap_or(&msg.author.name);
|
||||
let user_avatar = msg.author.avatar_url().unwrap_or_default();
|
||||
|
||||
let mut author_node = CreateEmbedAuthor::new(format!("Requested by {}", display_name));
|
||||
if !user_avatar.is_empty() {
|
||||
author_node = author_node.icon_url(user_avatar);
|
||||
}
|
||||
|
||||
let mut embed = CreateEmbed::new()
|
||||
.color(12543992)
|
||||
.author(author_node)
|
||||
.field("Title", title, true)
|
||||
.field("Artist", format!("by\n{}", artist), true)
|
||||
.field(" ", "Meowwww :3", false)
|
||||
.field("Position in Queue", format!("#{}", current_position), false);
|
||||
|
||||
if !thumbnail.is_empty() {
|
||||
embed = embed.thumbnail(thumbnail);
|
||||
}
|
||||
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
|
||||
if !is_playing {
|
||||
let start_embed = CreateEmbed::new()
|
||||
.title(":white_check_mark: | Playback started successfully!")
|
||||
.color(3066993);
|
||||
let start_builder = CreateMessage::new().embed(start_embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, start_builder).await;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("YTDL extraction error: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"skip" => {
|
||||
let guild_id = msg.guild_id.unwrap();
|
||||
let manager = songbird::get(&ctx).await.expect("Songbird client not initialized").clone();
|
||||
|
||||
if let Some(handler_lock) = manager.get(guild_id) {
|
||||
let handler = handler_lock.lock().await;
|
||||
|
||||
let queue = handler.queue();
|
||||
if queue.is_empty() {
|
||||
let builder = CreateMessage::new().content("uuuuhhhhhhhh i think i have dementia...");
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let skip_count = args.get(1).and_then(|s| s.parse::<usize>().ok()).unwrap_or(1);
|
||||
|
||||
if skip_count > 1 {
|
||||
if let Some(mut local_queue) = PLAYLISTS.get_mut(&guild_id.get()) {
|
||||
let len = local_queue.len();
|
||||
let skip_limit = std::cmp::min(len, skip_count - 1);
|
||||
local_queue.drain(0..skip_limit);
|
||||
|
||||
let embed = CreateEmbed::new()
|
||||
.title(format!(":wastebasket: | Trashed **{}** songs from queue.", skip_limit));
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
}
|
||||
}
|
||||
|
||||
let _ = queue.skip();
|
||||
if skip_count <= 1 {
|
||||
let embed = CreateEmbed::new()
|
||||
.title(":fast_forward: | Skipped that mf!! Playing next song in queue..");
|
||||
let builder = CreateMessage::new().embed(embed);
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
}
|
||||
} else {
|
||||
let builder = CreateMessage::new().content("IM NOT IN A VOICE CHANNEL U CHUD");
|
||||
let _ = msg.channel_id.send_message(&ctx.http, builder).await;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue