forked from DiscordBots/Rustendo
WIP: started to impl. songbird
This commit is contained in:
parent
d6a2cb5c8b
commit
d48de636f9
8 changed files with 1064 additions and 268 deletions
1174
Cargo.lock
generated
1174
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -13,5 +13,11 @@ tokio = { version = "1.36", features = ["macros", "rt-multi-thread"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
confy = "0.6.0"
|
confy = "0.6.0"
|
||||||
lavalink-rs = "0.10"
|
songbird = "0.4"
|
||||||
chrono = "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"
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use serenity::all::{CommandInteraction, Context};
|
use serenity::all::{CommandInteraction, Context};
|
||||||
use serenity::builder::{CreateCommand, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
|
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 username = command.user.name.as_str();
|
||||||
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
|
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
|
||||||
CreateEmbed::new()
|
CreateEmbed::new()
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
|
|
||||||
use chrono::Local;
|
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::builder::{CreateCommand, CreateCommandOption, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
|
||||||
use serenity::model::application::CommandOptionType;
|
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 username = command.user.name.as_str();
|
||||||
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
|
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
|
||||||
|
let options = &command.data.options;
|
||||||
|
|
||||||
let query = if let Some(ResolvedOption {
|
let query = if let Some(CommandDataOption {
|
||||||
value: ResolvedValue::String(query), ..
|
value: CommandDataOptionValue::String(query), ..
|
||||||
}) = options.first()
|
}) = &options.first()
|
||||||
{
|
{
|
||||||
query
|
query
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,6 +21,45 @@ pub fn run(ctx: &Context, command: &CommandInteraction, options: &[ResolvedOptio
|
||||||
.footer(CreateEmbedFooter::new(format!(">{} | {}", current_time, username)))
|
.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()
|
CreateEmbed::new()
|
||||||
.author(CreateEmbedAuthor::new("Rustendo"))
|
.author(CreateEmbedAuthor::new("Rustendo"))
|
||||||
.title(format!("Searching for {}", query))
|
.title(format!("Searching for {}", query))
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use serenity::all::{CommandInteraction, Context};
|
use serenity::all::{CommandInteraction, Context};
|
||||||
use serenity::builder::{CreateCommand, CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter};
|
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 username = command.user.name.as_str();
|
||||||
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
|
let current_time = Local::now().format("%Y-%m-%d @ %H:%M:%S");
|
||||||
CreateEmbed::new()
|
CreateEmbed::new()
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
pub mod lavalink_event_handler;
|
|
82
src/main.rs
82
src/main.rs
|
@ -2,13 +2,9 @@ mod commands;
|
||||||
mod util;
|
mod util;
|
||||||
mod handler;
|
mod handler;
|
||||||
|
|
||||||
use std::error::Error;
|
use std::thread::current;
|
||||||
|
use chrono::Local;
|
||||||
use lavalink_rs::client::LavalinkClient;
|
use serenity::all::{CommandInteraction, OnlineStatus};
|
||||||
use lavalink_rs::model::events::Events;
|
|
||||||
use lavalink_rs::model::UserId;
|
|
||||||
use lavalink_rs::node::NodeBuilder;
|
|
||||||
use serenity::all::OnlineStatus;
|
|
||||||
use serenity::async_trait;
|
use serenity::async_trait;
|
||||||
use serenity::builder::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseMessage};
|
use serenity::builder::{CreateEmbed, CreateEmbedAuthor, CreateEmbedFooter, CreateInteractionResponse, CreateInteractionResponseMessage};
|
||||||
use serenity::gateway::ActivityData;
|
use serenity::gateway::ActivityData;
|
||||||
|
@ -17,27 +13,39 @@ use serenity::model::gateway::Ready;
|
||||||
use serenity::prelude::*;
|
use serenity::prelude::*;
|
||||||
use util::config;
|
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;
|
struct Handler;
|
||||||
|
|
||||||
pub struct Data {
|
|
||||||
pub lavalink: LavalinkClient,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl EventHandler for Handler {
|
impl EventHandler for Handler {
|
||||||
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
async fn interaction_create(&self, ctx: Context, interaction: Interaction) {
|
||||||
if let Interaction::Command(command) = interaction {
|
if let Interaction::Command(command) = interaction {
|
||||||
let content = match command.data.name.as_str() {
|
let content = match command.data.name.as_str() {
|
||||||
"info" => Some(commands::info::run(&ctx, &command, &command.data.options())),
|
"info" => Some(commands::info::run(&ctx, &command)),
|
||||||
"play" => Some(commands::play::run(&ctx, &command, &command.data.options())),
|
"play" => Some(commands::play::run(&ctx, &command)),
|
||||||
"stop" => Some(commands::stop::run(&ctx, &command, &command.data.options())),
|
"stop" => Some(commands::stop::run(&ctx, &command)),
|
||||||
_ => Some(CreateEmbed::new()
|
_ => Some(respond_with_error(&ctx, &command)),
|
||||||
.author(CreateEmbedAuthor::new("Rustendo"))
|
|
||||||
.description("Cannot find command")
|
|
||||||
.footer(CreateEmbedFooter::new("Error during execution"))),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(embed) = content {
|
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]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
println!(r"__________ __ .___
|
println!(r"__________ __ .___
|
||||||
|
@ -78,30 +97,13 @@ async fn main() {
|
||||||
let status = OnlineStatus::DoNotDisturb;
|
let status = OnlineStatus::DoNotDisturb;
|
||||||
let activity = ActivityData::streaming("music", "https://twitch.tv/moonleaytv").unwrap();
|
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
|
// Build the client
|
||||||
let mut client = Client::builder(config.discord_token, GatewayIntents::empty())
|
let mut client = Client::builder(config.discord_token, GatewayIntents::empty())
|
||||||
.event_handler(Handler)
|
.event_handler(Handler)
|
||||||
.status(status)
|
.status(status)
|
||||||
.activity(activity)
|
.activity(activity)
|
||||||
|
.register_songbird()
|
||||||
|
.type_map_insert::<HttpKey>(HttpClient::new())
|
||||||
.await
|
.await
|
||||||
.expect("Error creating client");
|
.expect("Error creating client");
|
||||||
|
|
||||||
|
@ -109,7 +111,9 @@ async fn main() {
|
||||||
//
|
//
|
||||||
// Shards will automatically attempt to reconnect, and will perform exponential backoff until
|
// Shards will automatically attempt to reconnect, and will perform exponential backoff until
|
||||||
// it reconnects.
|
// it reconnects.
|
||||||
if let Err(why) = client.start().await {
|
if let Err(why) = client
|
||||||
|
.start()
|
||||||
|
.await {
|
||||||
println!("Client error: {why:?}");
|
println!("Client error: {why:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue