This commit is contained in:
2026-03-12 01:03:44 +01:00
parent 2f6f8414d0
commit 2f6b57031c
15 changed files with 179 additions and 15 deletions
Generated
+9
View File
@@ -2252,6 +2252,7 @@ dependencies = [
"serenity", "serenity",
"tokio", "tokio",
"types", "types",
"url",
"uuid", "uuid",
] ]
@@ -2353,6 +2354,13 @@ dependencies = [
"getrandom 0.2.17", "getrandom 0.2.17",
] ]
[[package]]
name = "nats"
version = "0.1.0"
dependencies = [
"async-nats",
]
[[package]] [[package]]
name = "nkeys" name = "nkeys"
version = "0.4.5" version = "0.4.5"
@@ -4908,6 +4916,7 @@ name = "types"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"serde", "serde",
"url",
"uuid", "uuid",
] ]
+1
View File
@@ -15,4 +15,5 @@ serenity = { version = "0.12.5", default-features = false, features = [
] } ] }
tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.50.0", features = ["macros", "rt-multi-thread"] }
types = { path = "../../libs/types" } types = { path = "../../libs/types" }
url = { version = "2.5.8", features = ["serde"] }
uuid = { version = "1.22.0" } uuid = { version = "1.22.0" }
+1
View File
@@ -1,2 +1,3 @@
pub mod ping; pub mod ping;
pub mod play;
pub mod testnats; pub mod testnats;
+65
View File
@@ -0,0 +1,65 @@
use std::path::PathBuf;
use postcard::{from_bytes, to_stdvec};
use serenity::{
all::{
CommandInteraction, Context, CreateCommandOption, CreateInteractionResponse,
CreateInteractionResponseMessage, EditInteractionResponse,
},
builder::CreateCommand,
model::application::{CommandOptionType, ResolvedOption, ResolvedValue},
};
use types::{
jobs::{DownloadJob, JobResponse, Jobs, PlayJob, SearchJob},
misc::{new_uuid_v4, parse_url_or_default},
};
use url::Url;
pub async fn run(
ctx: &Context,
interaction: &CommandInteraction,
nats_client: &async_nats::Client,
) -> Result<(), serenity::Error> {
let options = interaction.data.options();
if let Some(ResolvedOption {
value: ResolvedValue::String(value),
..
}) = options.first()
{
let url: Url;
let is_url = value.starts_with("https://") || value.starts_with("http://");
if !is_url {
let response = match nats_client
.request(
"corro-dj.search",
to_stdvec(&SearchJob {
uuid: new_uuid_v4(),
query: value.to_string(),
})
.unwrap()
.into(),
)
.await
{
Ok(resp) => resp,
Err(_why) => return Err(serenity::Error::Other("send error")),
};
}
}
Ok(())
}
pub fn register() -> CreateCommand {
CreateCommand::new("play")
.description("Play a song")
.add_option(
CreateCommandOption::new(
CommandOptionType::String,
"song",
"Name or url of the song to play",
)
.required(false),
)
}
+7 -5
View File
@@ -15,8 +15,9 @@ use serenity::{
}; };
use types::{ use types::{
jobs::{DownloadJob, JobResponse, Jobs, PlayJob, SearchJob}, jobs::{DownloadJob, JobResponse, Jobs, PlayJob, SearchJob},
misc::new_uuid_v4, misc::{new_uuid_v4, parse_url_or_default},
}; };
use url::Url;
pub async fn run( pub async fn run(
ctx: &Context, ctx: &Context,
@@ -40,8 +41,9 @@ pub async fn run(
) )
.await?; .await?;
let url: String; let url: Url;
let is_url = value.starts_with("https://"); let is_url = value.starts_with("https://");
if !is_url { if !is_url {
let search_job = SearchJob { let search_job = SearchJob {
uuid: new_uuid_v4(), uuid: new_uuid_v4(),
@@ -64,7 +66,7 @@ pub async fn run(
.await?; .await?;
return Err(serenity::Error::Other("Search error")); return Err(serenity::Error::Other("Search error"));
} else if let Some(Jobs::Search(content)) = search_response.content { } else if let Some(Jobs::Search(content)) = search_response.content {
url = content.url; url = content.song.url;
} else { } else {
interaction interaction
.edit_response(ctx, EditInteractionResponse::new().content("unknown error")) .edit_response(ctx, EditInteractionResponse::new().content("unknown error"))
@@ -72,7 +74,7 @@ pub async fn run(
return Err(serenity::Error::Other("unknown error")); return Err(serenity::Error::Other("unknown error"));
} }
} else { } else {
url = value.to_string(); url = parse_url_or_default(value.to_string());
} }
let download_job = DownloadJob { let download_job = DownloadJob {
@@ -154,7 +156,7 @@ pub async fn run(
} }
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
CreateCommand::new("play") CreateCommand::new("testnats")
.description("Play a song") .description("Play a song")
.add_option( .add_option(
CreateCommandOption::new( CreateCommandOption::new(
+17 -2
View File
@@ -2,6 +2,7 @@ mod commands;
use std::env; use std::env;
use async_nats::Client;
use serenity::{ use serenity::{
Client, Client,
all::{Context, EventHandler, GatewayIntents}, all::{Context, EventHandler, GatewayIntents},
@@ -25,12 +26,18 @@ impl EventHandler for Handler {
let content = match command.data.name.as_str() { let content = match command.data.name.as_str() {
"play" => { "play" => {
commands::testnats::run(&ctx, &command, &self.nats_client) commands::play::run(&ctx, &command, &self.nats_client)
.await .await
.unwrap(); .unwrap();
None None
} }
"ping" => Some(commands::ping::run(&command.data.options())), "ping" => Some(commands::ping::run(&command.data.options())),
"testnats" => {
commands::testnats::run(&ctx, &command, &self.nats_client)
.await
.unwrap();
None
}
_ => Some("not implemented :(".to_string()), _ => Some("not implemented :(".to_string()),
}; };
@@ -49,7 +56,11 @@ impl EventHandler for Handler {
if let Err(why) = Command::set_global_commands( if let Err(why) = Command::set_global_commands(
&ctx.http, &ctx.http,
vec![commands::ping::register(), commands::testnats::register()], vec![
commands::ping::register(),
commands::play::register(),
commands::testnats::register(),
],
) )
.await .await
{ {
@@ -58,6 +69,10 @@ impl EventHandler for Handler {
} }
} }
impl A for Client {
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
// Configure the client with your Discord bot token in the environment. // Configure the client with your Discord bot token in the environment.
+16 -3
View File
@@ -1,4 +1,8 @@
use types::jobs::{JobResponse, SearchJob, SearchResponse}; use types::{
jobs::{JobResponse, SearchJob, SearchResponse},
misc::parse_url_or_default,
queue::YoutubeSong,
};
use yt_dlp::Downloader; use yt_dlp::Downloader;
pub async fn search( pub async fn search(
@@ -13,8 +17,17 @@ pub async fn search(
.await .await
{ {
Ok(result) => match result.webpage_url { Ok(result) => match result.webpage_url {
Some(url) => JobResponse { Some(webpage_url) => JobResponse {
content: Some(SearchResponse { url }), content: Some(SearchResponse {
song: YoutubeSong {
title: result.title,
artist: result.channel.unwrap_or(String::new()),
url: parse_url_or_default(webpage_url),
thumbnail_url: parse_url_or_default(
result.thumbnail.unwrap_or(String::new()),
),
},
}),
error: None, error: None,
}, },
None => JobResponse { None => JobResponse {
+7
View File
@@ -0,0 +1,7 @@
[package]
name = "nats"
version = "0.1.0"
edition = "2024"
[dependencies]
async-nats = { version = "0.46.0" }
View File
+1
View File
@@ -0,0 +1 @@
pub mod functions;
+1
View File
@@ -5,4 +5,5 @@ edition = "2024"
[dependencies] [dependencies]
serde = { version = "1.0.228" } serde = { version = "1.0.228" }
url = { version = "2.5.8", features = ["serde"] }
uuid = { version = "1.22.0", features = ["serde", "v4"] } uuid = { version = "1.22.0", features = ["serde", "v4"] }
+21 -5
View File
@@ -1,18 +1,34 @@
use std::{num::NonZeroU64, path::PathBuf}; use std::{num::NonZeroU64, path::PathBuf};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid; use uuid::Uuid;
use crate::queue::YoutubeSong;
pub enum JobsBody {
}
impl Jobs {
fn as_str(&self) -> &'static str {
match self {
Self::Search(self) => "Hello",
Self::Download => "World",
}
}
}
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum Jobs { pub enum Jobs {
Search(SearchResponse), Search(SearchResponse),
Download(DownloadResponse), Download(DownloadResponse),
Play(PlayResponse), Play(PlayResponse),
// Error(String),
} }
pub type JobResult = Result<Jobs, String>;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct JobResponse<T = Jobs> { pub struct JobResponse<T = Jobs> {
pub content: Option<T>, pub content: Option<T>,
@@ -22,7 +38,7 @@ pub struct JobResponse<T = Jobs> {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct DownloadJob { pub struct DownloadJob {
pub uuid: Uuid, pub uuid: Uuid,
pub url: String, pub url: Url,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@@ -49,5 +65,5 @@ pub struct SearchJob {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct SearchResponse { pub struct SearchResponse {
pub url: String, pub song: YoutubeSong,
} }
+1
View File
@@ -1,2 +1,3 @@
pub mod jobs; pub mod jobs;
pub mod misc; pub mod misc;
pub mod queue;
+5
View File
@@ -1,5 +1,10 @@
use url::Url;
use uuid::Uuid; use uuid::Uuid;
pub fn new_uuid_v4() -> Uuid { pub fn new_uuid_v4() -> Uuid {
Uuid::new_v4() Uuid::new_v4()
} }
pub fn parse_url_or_default(url_string: String) -> Url {
Url::parse(url_string.as_str()).unwrap_or(Url::parse("https://example.com").unwrap())
}
+27
View File
@@ -0,0 +1,27 @@
use std::num::NonZeroU64;
use serde::{Deserialize, Serialize};
use url::Url;
use uuid::Uuid;
#[derive(Serialize, Deserialize, Debug)]
pub struct YoutubeSong {
pub title: String,
pub artist: String,
pub url: Url,
pub thumbnail_url: Url,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct YoutubePlaylist {
pub title: String,
pub songs: Vec<YoutubeSong>,
pub url: Url,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Queue {
pub uuid: Uuid,
pub guild_id: NonZeroU64,
pub songs: Vec<YoutubeSong>,
}