WIP: started to impl. songbird

This commit is contained in:
moonleay 2024-02-23 19:05:45 +01:00
parent d6a2cb5c8b
commit d48de636f9
Signed by: moonleay
GPG key ID: 82667543CCD715FB
8 changed files with 1064 additions and 268 deletions

1174
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -13,5 +13,11 @@ tokio = { version = "1.36", features = ["macros", "rt-multi-thread"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
confy = "0.6.0"
lavalink-rs = "0.10"
songbird = "0.4"
chrono = "0.4"
reqwest = "0.11"
symphonia = "0.5"
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
tracing-futures = "0.2.5"
futures = "0.3.1"

View file

@ -1,9 +1,8 @@
use chrono::Local;
use serenity::all::{CommandInteraction, Context};
use serenity::builder::{CreateCommand, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
use serenity::model::application::ResolvedOption;
pub fn run(ctx: &Context, command: &CommandInteraction, _options: &[ResolvedOption]) -> CreateEmbed {
pub async fn run(_ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
let username = command.user.name.as_str();
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
CreateEmbed::new()

View file

@ -1,16 +1,16 @@
use chrono::Local;
use serenity::all::{CommandInteraction, Context, ResolvedOption, ResolvedValue};
use serenity::all::{CommandDataOption, CommandDataOptionValue, CommandInteraction, Context, ResolvedOption, ResolvedValue};
use serenity::builder::{CreateCommand, CreateCommandOption, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
use serenity::model::application::CommandOptionType;
pub fn run(ctx: &Context, command: &CommandInteraction, options: &[ResolvedOption]) -> CreateEmbed {
pub async fn run(ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
let username = command.user.name.as_str();
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
let options = &command.data.options;
let query = if let Some(ResolvedOption {
value: ResolvedValue::String(query), ..
}) = options.first()
let query = if let Some(CommandDataOption {
value: CommandDataOptionValue::String(query), ..
}) = &options.first()
{
query
} else {
@ -21,6 +21,45 @@ pub fn run(ctx: &Context, command: &CommandInteraction, options: &[ResolvedOptio
.footer(CreateEmbedFooter::new(format!(">{} | {}", current_time, username)))
};
let guild_id = match &command.guild_id {
Some(guild_id) => guild_id,
None => {
return CreateEmbed::new()
.author(CreateEmbedAuthor::new("Rustendo"))
.title("guildid not found")
.description("Could not find guild id.")
.footer(CreateEmbedFooter::new(format!("> {} | {}", current_time, username)));
}
};
let (guild_id, channel_id) = {
let guild = &ctx.cache.guild(guild_id).unwrap();
// This may be unsafe, idk not sure yet
let channel_id = guild
.voice_states
.get(&command.user.id)
.and_then(|voice_state| voice_state.channel_id);
(guild.id, channel_id)
};
let connect_to = match channel_id {
Some(channel) => channel,
None => {
return CreateEmbed::new()
.author(CreateEmbedAuthor::new("Rustendo"))
.title("You are not in a VC.")
.description("Join one to start playing music.")
.footer(CreateEmbedFooter::new(format!("> {} | {}", current_time, username)));
},
};
let manager = &songbird::get(ctx)
.await
.expect("")
.clone();
manager.join(guild_id, connect_to).await.expect("Cannot connect>...");
CreateEmbed::new()
.author(CreateEmbedAuthor::new("Rustendo"))
.title(format!("Searching for {}", query))
@ -35,4 +74,4 @@ pub fn register() -> CreateCommand {
CreateCommandOption::new(CommandOptionType::String, "query", "Link or search term")
.required(true)
)
}
}

View file

@ -1,9 +1,8 @@
use chrono::Local;
use serenity::all::{CommandInteraction, Context};
use serenity::builder::{CreateCommand, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
use serenity::model::application::ResolvedOption;
pub fn run(ctx: &Context, command: &CommandInteraction, _options: &[ResolvedOption]) -> CreateEmbed {
pub async fn run(_ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
let username = command.user.name.as_str();
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
CreateEmbed::new()

View file

@ -1,8 +0,0 @@
use lavalink_rs::{client::LavalinkClient, model::events};
use serenity::all::standard::macros::hook;
#[hook]
pub async fn ready_event(client: LavalinkClient, _session_id: String, event: &events::Ready) {
client.delete_all_player_contexts().await.unwrap();
println!("Lavalink is ready for Freddy:: {:?}", event);
}

View file

@ -1 +0,0 @@
pub mod lavalink_event_handler;

View file

@ -2,13 +2,9 @@ mod commands;
mod util;
mod handler;
use std::error::Error;
use lavalink_rs::client::LavalinkClient;
use lavalink_rs::model::events::Events;
use lavalink_rs::model::UserId;
use lavalink_rs::node::NodeBuilder;
use serenity::all::OnlineStatus;
use std::thread::current;
use chrono::Local;
use serenity::all::{CommandInteraction, OnlineStatus};
use serenity::async_trait;
use serenity::builder::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseMessage};
use serenity::gateway::ActivityData;
@ -17,27 +13,39 @@ use serenity::model::gateway::Ready;
use serenity::prelude::*;
use util::config;
use crate::handler::lavalink_event_handler;
// This trait adds the `register_songbird` and `register_songbird_with` methods
// to the client builder below, making it easy to install this voice client.
// The voice client can be retrieved in any command using `songbird::get(ctx).await`.
use songbird::SerenityInit;
// Event related imports to detect track creation failures.
use songbird::events::{Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent};
// To turn user URLs into playable audio, we'll use yt-dlp.
use songbird::input::YoutubeDl;
// YtDl requests need an HTTP client to operate -- we'll create and store our own.
use reqwest::Client as HttpClient;
// Import the `Context` to handle commands.
use serenity::client::Context;
struct HttpKey;
impl TypeMapKey for HttpKey {
type Value = HttpClient;
}
struct Handler;
pub struct Data {
pub lavalink: LavalinkClient,
}
#[async_trait]
impl EventHandler for Handler {
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
if let Interaction::Command(command) = interaction {
let content = match command.data.name.as_str() {
"info" => Some(commands::info::run(&ctx, &command, &command.data.options())),
"play" => Some(commands::play::run(&ctx, &command, &command.data.options())),
"stop" => Some(commands::stop::run(&ctx, &command, &command.data.options())),
_ => Some(CreateEmbed::new()
.author(CreateEmbedAuthor::new("Rustendo"))
.description("Cannot find command")
.footer(CreateEmbedFooter::new("Error during execution"))),
"info" => Some(commands::info::run(&ctx, &command)),
"play" => Some(commands::play::run(&ctx, &command)),
"stop" => Some(commands::stop::run(&ctx, &command)),
_ => Some(respond_with_error(&ctx, &command)),
};
if let Some(embed) = content {
@ -61,6 +69,17 @@ impl EventHandler for Handler {
}
}
pub async fn respond_with_error(_ctx: &Context, command: &CommandInteraction) -> CreateEmbed {
let username = &command.user.name.as_str();
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
CreateEmbed::new()
.author(CreateEmbedAuthor::new("Rustendo"))
.title("Command not found")
.description("Cannot find the executed command")
.footer(CreateEmbedFooter::new(format!("> {} | {}", current_time, username)))
}
#[tokio::main]
async fn main() {
println!(r"__________ __ .___
@ -78,30 +97,13 @@ async fn main() {
let status = OnlineStatus::DoNotDisturb;
let activity = ActivityData::streaming("music", "https://twitch.tv/moonleaytv").unwrap();
let node_builder = NodeBuilder {
hostname: config.lavalink_address,
password: config.lavalink_password,
user_id: UserId(config.user_id),
..Default::default()
};
// let event = Events::default();
let event = Events {
ready: Some(lavalink_event_handler::ready_event),
..Default::default()
};
// Connect to lavalink
let lavalink_client = LavalinkClient::new(event, vec![node_builder]);
tokio::spawn(async move {
lavalink_client.start().await;
});
// Build the client
let mut client = Client::builder(config.discord_token, GatewayIntents::empty())
.event_handler(Handler)
.status(status)
.activity(activity)
.register_songbird()
.type_map_insert::<HttpKey>(HttpClient::new())
.await
.expect("Error creating client");
@ -109,7 +111,9 @@ async fn main() {
//
// Shards will automatically attempt to reconnect, and will perform exponential backoff until
// it reconnects.
if let Err(why) = client.start().await {
if let Err(why) = client
.start()
.await {
println!("Client error: {why:?}");
}
}