lot of refactor

This commit is contained in:
2026-03-14 19:21:56 +01:00
parent b63982714b
commit 8d4a002b73
22 changed files with 256 additions and 313 deletions
Generated
+12 -6
View File
@@ -2248,13 +2248,12 @@ name = "master"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-nats", "async-nats",
"nats", "nats-libs",
"postcard",
"serenity", "serenity",
"serenity-libs",
"tokio", "tokio",
"types", "types",
"url", "url",
"uuid",
] ]
[[package]] [[package]]
@@ -2356,7 +2355,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "nats" name = "nats-libs"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"async-nats", "async-nats",
@@ -3847,6 +3846,15 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "serenity-libs"
version = "0.1.0"
dependencies = [
"async-trait",
"serenity",
"types",
]
[[package]] [[package]]
name = "serenity-voice-model" name = "serenity-voice-model"
version = "0.2.1" version = "0.2.1"
@@ -5714,8 +5722,6 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-nats", "async-nats",
"futures", "futures",
"futures-executor",
"nats",
"postcard", "postcard",
"rustls 0.23.37", "rustls 0.23.37",
"serenity", "serenity",
+4 -2
View File
@@ -1,11 +1,12 @@
FROM rust:1.93.1-alpine3.23 AS builder FROM rust:1.94.0-alpine3.23 AS builder
RUN --mount=type=cache,target=/root/.cargo \ RUN --mount=type=cache,target=/root/.cargo \
rustup component add clippy && \ rustup component add clippy && \
rustup component add rustfmt --toolchain nightly rustup component add rustfmt --toolchain nightly
RUN \ RUN \
cargo install cargo-sort cargo install cargo-sort && \
cargo install machete
WORKDIR /build WORKDIR /build
@@ -13,6 +14,7 @@ COPY . .
RUN --mount=type=cache,target=/build/target \ RUN --mount=type=cache,target=/build/target \
cargo sort ./**/Cargo.toml --check && \ cargo sort ./**/Cargo.toml --check && \
cargo machete --with-metadata && \
cargo clippy && \ cargo clippy && \
cargo +nightly fmt --check cargo +nightly fmt --check
+2 -3
View File
@@ -5,8 +5,7 @@ edition = "2024"
[dependencies] [dependencies]
async-nats = { version = "0.46.0" } async-nats = { version = "0.46.0" }
nats = { path = "../../libs/nats" } nats-libs = { path = "../../libs/nats-libs" }
postcard = { version = "1.1.3", features = ["use-std"] }
serenity = { version = "0.12.5", default-features = false, features = [ serenity = { version = "0.12.5", default-features = false, features = [
"cache", "cache",
"client", "client",
@@ -14,7 +13,7 @@ serenity = { version = "0.12.5", default-features = false, features = [
"gateway", "gateway",
"rustls_backend", "rustls_backend",
] } ] }
serenity-libs = { path = "../../libs/serenity-libs" }
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"] } url = { version = "2.5.8", features = ["serde"] }
uuid = { version = "1.22.0" }
-1
View File
@@ -1,3 +1,2 @@
pub mod ping; pub mod ping;
pub mod play; pub mod play;
pub mod testnats;
+11 -3
View File
@@ -1,7 +1,15 @@
use serenity::{builder::CreateCommand, model::application::ResolvedOption}; use serenity::{
all::{CommandInteraction, Context},
builder::CreateCommand,
};
use serenity_libs::functions::CustomInteraction;
use types::error::CorroError;
pub fn run(_options: &[ResolvedOption]) -> String { pub async fn run(ctx: &Context, interaction: &CommandInteraction) -> Result<(), CorroError> {
"Pong!".to_string() let _ = interaction
.create_text_response(ctx, "Pong!".to_string())
.await;
Ok(())
} }
pub fn register() -> CreateCommand { pub fn register() -> CreateCommand {
+42 -33
View File
@@ -1,17 +1,12 @@
use nats::functions::JobClient; use nats_libs::functions::JobClient;
use serenity::{ use serenity::{
all::{ all::{CommandInteraction, Context, CreateCommandOption},
CommandInteraction,
Context,
CreateCommandOption,
CreateInteractionResponse,
CreateInteractionResponseMessage,
EditInteractionResponse,
},
builder::CreateCommand, builder::CreateCommand,
model::application::{CommandOptionType, ResolvedOption, ResolvedValue}, model::application::{CommandOptionType, ResolvedOption, ResolvedValue},
}; };
use serenity_libs::functions::CustomInteraction;
use types::{ use types::{
error::{CorroError, CorroErrorType},
jobs::{DownloadJob, JobsMap, JobsResponseMap, PlayJob, SearchJob}, jobs::{DownloadJob, JobsMap, JobsResponseMap, PlayJob, SearchJob},
misc::{new_uuid_v4, parse_url_or_default}, misc::{new_uuid_v4, parse_url_or_default},
}; };
@@ -21,7 +16,7 @@ pub async fn run(
ctx: &Context, ctx: &Context,
interaction: &CommandInteraction, interaction: &CommandInteraction,
nats_client: &async_nats::Client, nats_client: &async_nats::Client,
) -> Result<(), serenity::Error> { ) -> Result<(), CorroError> {
let options = interaction.data.options(); let options = interaction.data.options();
if let Some(ResolvedOption { if let Some(ResolvedOption {
@@ -29,15 +24,13 @@ pub async fn run(
.. ..
}) = options.first() }) = options.first()
{ {
interaction match interaction
.create_response( .create_text_response(ctx, format!("Searching: {value}..."))
ctx, .await
CreateInteractionResponse::Message( {
CreateInteractionResponseMessage::new() Ok(_) => {}
.content(format!("Searching: {value}...")), Err(why) => return Err(why),
), };
)
.await?;
let url: Url; let url: Url;
let is_url = value.starts_with("https://") || value.starts_with("http://"); let is_url = value.starts_with("https://") || value.starts_with("http://");
@@ -52,13 +45,16 @@ pub async fn run(
{ {
Ok(resp) => match resp { Ok(resp) => match resp {
JobsResponseMap::Search(resp) => resp, JobsResponseMap::Search(resp) => resp,
_ => return Err(serenity::Error::Other("Unexpected return type")), _ => {
return Err(CorroError {
error_type: CorroErrorType::JobError,
message: "Unexpected return type".to_string(),
});
}
}, },
Err(_why) => return Err(serenity::Error::Other("send error")), Err(why) => return Err(why),
}; };
println!("{:?}", &search_response);
url = search_response.song.url; url = search_response.song.url;
} else { } else {
url = parse_url_or_default(value.to_string()); url = parse_url_or_default(value.to_string());
@@ -73,13 +69,16 @@ pub async fn run(
{ {
Ok(resp) => match resp { Ok(resp) => match resp {
JobsResponseMap::Download(resp) => resp, JobsResponseMap::Download(resp) => resp,
_ => return Err(serenity::Error::Other("Unexpected return type")), _ => {
return Err(CorroError {
error_type: CorroErrorType::JobError,
message: "Unexpected return type".to_string(),
});
}
}, },
Err(_why) => return Err(serenity::Error::Other("send error")), Err(why) => return Err(why),
}; };
println!("{:?}", &download_response);
let guild_id = interaction.guild_id.unwrap(); let guild_id = interaction.guild_id.unwrap();
let channel_id = guild_id let channel_id = guild_id
@@ -89,7 +88,7 @@ pub async fn run(
.channel_id .channel_id
.unwrap(); .unwrap();
let _ = match nats_client match nats_client
.send_job(JobsMap::Play(PlayJob { .send_job(JobsMap::Play(PlayJob {
uuid: new_uuid_v4(), uuid: new_uuid_v4(),
path: download_response.path, path: download_response.path,
@@ -100,15 +99,25 @@ pub async fn run(
{ {
Ok(resp) => match resp { Ok(resp) => match resp {
JobsResponseMap::Play(resp) => resp, JobsResponseMap::Play(resp) => resp,
_ => return Err(serenity::Error::Other("Unexpected return type")), _ => {
return Err(CorroError {
error_type: CorroErrorType::JobError,
message: "Unexpected return type".to_string(),
});
}
}, },
Err(_why) => return Err(serenity::Error::Other("send error")), Err(why) => return Err(why),
}; };
interaction match interaction
.edit_response(ctx, EditInteractionResponse::new().content("Playing...")) .edit_text_response(ctx, "Playing...".to_string())
.await?; .await
{
Ok(_) => {}
Err(why) => return Err(why),
};
} }
Ok(()) Ok(())
} }
-169
View File
@@ -1,169 +0,0 @@
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, JobsResponseMap, 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()
{
interaction
.create_response(
ctx,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content(format!("Searching: {value}...")),
),
)
.await?;
let url: Url;
let is_url = value.starts_with("https://");
if !is_url {
let search_job = SearchJob {
uuid: new_uuid_v4(),
query: value.to_string(),
};
let response = match nats_client
.request("corro-dj.search", to_stdvec(&search_job).unwrap().into())
.await
{
Ok(resp) => resp,
Err(_why) => return Err(serenity::Error::Other("send error")),
};
let search_response: JobResponse = from_bytes(&response.payload).unwrap();
if let Some(error) = search_response.error {
interaction
.edit_response(ctx, EditInteractionResponse::new().content(error))
.await?;
return Err(serenity::Error::Other("Search error"));
} else if let Some(JobsResponseMap::Search(content)) = search_response.content {
url = content.song.url;
} else {
interaction
.edit_response(ctx, EditInteractionResponse::new().content("unknown error"))
.await?;
return Err(serenity::Error::Other("unknown error"));
}
} else {
url = parse_url_or_default(value.to_string());
}
let download_job = DownloadJob {
uuid: new_uuid_v4(),
url,
};
println!("job {:?}", download_job);
let response = match nats_client
.request(
"corro-dj.download",
to_stdvec(&download_job).unwrap().into(),
)
.await
{
Ok(resp) => resp,
Err(_why) => return Err(serenity::Error::Other("send error")),
};
let job_response: JobResponse = from_bytes(&response.payload).unwrap();
println!("response: {:?}", job_response);
let text_response: String;
if let Some(error) = job_response.error {
text_response = error;
} else if let Some(JobsResponseMap::Download(content)) = job_response.content {
text_response = content.path.display().to_string();
} else {
text_response = "unkown".to_string();
}
interaction
.edit_response(ctx, EditInteractionResponse::new().content(&text_response))
.await?;
let guild_id = interaction.guild_id.unwrap();
let channel_id = guild_id
.get_user_voice_state(&ctx.http, interaction.user.id)
.await
.unwrap()
.channel_id
.unwrap();
let play_job = PlayJob {
uuid: new_uuid_v4(),
path: PathBuf::from(&text_response),
guild_id: guild_id.into(),
channel_id: channel_id.into(),
};
println!("job {:?}", play_job);
let _ = match nats_client
.request("corro-dj.play", to_stdvec(&play_job).unwrap().into())
.await
{
Ok(resp) => resp,
Err(_why) => return Err(serenity::Error::Other("send error")),
};
interaction
.edit_response(ctx, EditInteractionResponse::new().content("playing..."))
.await?;
} else {
interaction
.create_response(
ctx,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new()
.content("Please provide a valid string"),
),
)
.await?;
}
Ok(())
}
pub fn register() -> CreateCommand {
CreateCommand::new("testnats")
.description("Play a song")
.add_option(
CreateCommandOption::new(
CommandOptionType::String,
"song",
"Name or url of the song to play",
)
.required(false),
)
}
+25 -30
View File
@@ -6,12 +6,13 @@ use serenity::{
Client, Client,
all::{Context, EventHandler, GatewayIntents}, all::{Context, EventHandler, GatewayIntents},
async_trait, async_trait,
builder::{CreateInteractionResponse, CreateInteractionResponseMessage},
model::{ model::{
application::{Command, Interaction}, application::{Command, Interaction},
gateway::Ready, gateway::Ready,
}, },
}; };
use serenity_libs::functions::CustomInteraction;
use types::error::{CorroError, CorroErrorType};
struct Handler { struct Handler {
nats_client: async_nats::Client, nats_client: async_nats::Client,
@@ -21,30 +22,28 @@ struct Handler {
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 {
println!("Received command interaction: {command:#?}"); println!("Received command interaction: {command:?}");
let content = match command.data.name.as_str() { if let Err(why) = match command.data.name.as_str() {
"play" => { "play" => commands::play::run(&ctx, &command, &self.nats_client).await,
commands::play::run(&ctx, &command, &self.nats_client) "ping" => commands::ping::run(&ctx, &command).await,
.await _ => Err(CorroError {
.unwrap(); error_type: CorroErrorType::CommandError,
None message: format!("Command {} not implemented", &command.data.name),
} }),
"ping" => Some(commands::ping::run(&command.data.options())), } {
"testnats" => { println!("{:?}: {}", why.error_type, why.message);
commands::testnats::run(&ctx, &command, &self.nats_client)
.await
.unwrap();
None
}
_ => Some("not implemented :(".to_string()),
};
if let Some(content) = content { match why.error_type {
let data = CreateInteractionResponseMessage::new().content(content); CorroErrorType::CommandError | CorroErrorType::SerenityError => {}
let builder = CreateInteractionResponse::Message(data); _ => {
if let Err(why) = command.create_response(&ctx.http, builder).await { let _ = command
println!("Cannot respond to slash command: {why}"); .edit_text_response(
&ctx,
format!("{:?}: {}", why.error_type, why.message),
)
.await;
}
} }
} }
} }
@@ -55,15 +54,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![ vec![commands::ping::register(), commands::play::register()],
commands::ping::register(),
commands::play::register(),
commands::testnats::register(),
],
) )
.await .await
{ {
println!("Client error: {why:?}"); println!("Set commands error: {why:?}");
} }
} }
} }
@@ -90,6 +85,6 @@ 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) = discord_client.start().await { if let Err(why) = discord_client.start().await {
println!("Client error: {why:?}"); println!("Client start error: {why:?}");
} }
} }
+3 -2
View File
@@ -3,11 +3,12 @@ name = "worker"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[package.metadata.cargo-machete]
ignored = ["symphonia"]
[dependencies] [dependencies]
async-nats = { version = "0.46.0" } async-nats = { version = "0.46.0" }
futures = { version = "0.3.32" } futures = { version = "0.3.32" }
futures-executor = { version = "0.3.32" }
nats = { path = "../../libs/nats" }
postcard = { version = "1.1.3", features = ["use-std"] } postcard = { version = "1.1.3", features = ["use-std"] }
rustls = { version = "0.23.37", default-features = false, features = [ rustls = { version = "0.23.37", default-features = false, features = [
"aws-lc-rs", "aws-lc-rs",
+6 -4
View File
@@ -24,7 +24,9 @@ impl EventHandler for Handler {
async fn ready(&self, ctx: Context, ready: Ready) { async fn ready(&self, ctx: Context, ready: Ready) {
println!("{} is connected!", ready.user.name); println!("{} is connected!", ready.user.name);
let voice_manager = songbird::get(&ctx).await; let voice_manager = songbird::get(&ctx)
.await
.expect("Cannot init voice manager");
let mut subscriber = self let mut subscriber = self
.nats_client .nats_client
@@ -65,9 +67,9 @@ impl EventHandler for Handler {
let response = match result { let response = match result {
Ok(response) => response, Ok(response) => response,
Err(err) => JobResponse { Err(why) => JobResponse {
content: None, content: None,
error: Some(err), error: Some(why),
}, },
}; };
@@ -115,6 +117,6 @@ async fn main() {
.expect("Error creating discord client"); .expect("Error creating discord client");
if let Err(why) = discord_client.start().await { if let Err(why) = discord_client.start().await {
println!("Client error: {why:?}"); println!("Client start error: {why:?}");
} }
} }
+17 -8
View File
@@ -1,4 +1,7 @@
use types::jobs::{DownloadJob, DownloadResponse, JobResponse}; use types::{
error::{CorroError, CorroErrorType},
jobs::{DownloadJob, DownloadResponse, JobResponse},
};
use yt_dlp::{ use yt_dlp::{
Downloader, Downloader,
model::{AudioCodecPreference, AudioQuality}, model::{AudioCodecPreference, AudioQuality},
@@ -7,12 +10,15 @@ use yt_dlp::{
pub async fn download( pub async fn download(
downloader: &Downloader, downloader: &Downloader,
job: DownloadJob, job: DownloadJob,
) -> Result<JobResponse<DownloadResponse>, String> { ) -> Result<JobResponse<DownloadResponse>, CorroError> {
println!("job: {:?}", job);
let video = match downloader.fetch_video_infos(job.url).await { let video = match downloader.fetch_video_infos(job.url).await {
Ok(video) => video, Ok(video) => video,
Err(err) => return Err(err.to_string()), Err(why) => {
return Err(CorroError {
error_type: CorroErrorType::YtdlpError,
message: why.to_string(),
});
}
}; };
let audio_path = match downloader let audio_path = match downloader
@@ -25,11 +31,14 @@ pub async fn download(
.await .await
{ {
Ok(path) => path, Ok(path) => path,
Err(err) => return Err(err.to_string()), Err(why) => {
return Err(CorroError {
error_type: CorroErrorType::YtdlpError,
message: why.to_string(),
});
}
}; };
println!("audio_path: {:?}", &audio_path);
Ok(JobResponse { Ok(JobResponse {
content: Some(DownloadResponse { content: Some(DownloadResponse {
path: audio_path.clone(), path: audio_path.clone(),
+30 -35
View File
@@ -6,46 +6,41 @@ use songbird::{
events::{Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent}, events::{Event, EventContext, EventHandler as VoiceEventHandler, TrackEvent},
input::File, input::File,
}; };
use types::jobs::{JobResponse, PlayJob, PlayResponse}; use types::{
error::CorroError,
jobs::{JobResponse, PlayJob, PlayResponse},
};
pub async fn play( pub async fn play(
voice_manager: &Option<Arc<Songbird>>, voice_manager: &Arc<Songbird>,
job: PlayJob, job: PlayJob,
) -> Result<JobResponse<PlayResponse>, String> { ) -> Result<JobResponse<PlayResponse>, CorroError> {
println!("job: {:?}", job); if let Ok(handler_lock) = voice_manager.join(job.guild_id, job.channel_id).await {
// Attach an event handler to see notifications of all track errors.
if let Some(voice_manager) = voice_manager {
if let Ok(handler_lock) = voice_manager.join(job.guild_id, job.channel_id).await {
// Attach an event handler to see notifications of all track errors.
let mut handler = handler_lock.lock().await;
handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier);
}
let handler_lock = match voice_manager.get(job.guild_id) {
Some(handler) => handler,
None => {
return Ok(JobResponse {
content: Some(PlayResponse {}),
error: None,
});
}
};
let mut handler = handler_lock.lock().await; let mut handler = handler_lock.lock().await;
handler.add_global_event(TrackEvent::Error.into(), TrackErrorNotifier);
let src = File::new(job.path).clone();
let track_handle = handler.play_input(src.into());
println!("{:?}", track_handle.get_info().await);
Ok(JobResponse {
content: Some(PlayResponse {}),
error: None,
})
} else {
Err("No voice_manager defined".to_string())
} }
let handler_lock = match voice_manager.get(job.guild_id) {
Some(handler) => handler,
None => {
return Ok(JobResponse {
content: Some(PlayResponse {}),
error: None,
});
}
};
let mut handler = handler_lock.lock().await;
let src = File::new(job.path).clone();
let _ = handler.play_input(src.into());
Ok(JobResponse {
content: Some(PlayResponse {}),
error: None,
})
} }
struct TrackErrorNotifier; struct TrackErrorNotifier;
+10 -7
View File
@@ -1,4 +1,5 @@
use types::{ use types::{
error::{CorroError, CorroErrorType},
jobs::{JobResponse, SearchJob, SearchResponse}, jobs::{JobResponse, SearchJob, SearchResponse},
misc::parse_url_or_default, misc::parse_url_or_default,
queue::YoutubeSong, queue::YoutubeSong,
@@ -8,9 +9,7 @@ use yt_dlp::Downloader;
pub async fn search( pub async fn search(
downloader: &Downloader, downloader: &Downloader,
job: SearchJob, job: SearchJob,
) -> Result<JobResponse<SearchResponse>, String> { ) -> Result<JobResponse<SearchResponse>, CorroError> {
println!("job: {:?}", job);
let result = match downloader let result = match downloader
.youtube_extractor() .youtube_extractor()
.search_first(&job.query) .search_first(&job.query)
@@ -32,16 +31,20 @@ pub async fn search(
}, },
None => JobResponse { None => JobResponse {
content: None, content: None,
error: Some("url is not defined".to_string()), error: Some(CorroError {
error_type: CorroErrorType::JobError,
message: "webpage_url is not defined".to_string(),
}),
}, },
}, },
Err(why) => JobResponse { Err(why) => JobResponse {
content: None, content: None,
error: Some(format!("{why}")), error: Some(CorroError {
error_type: CorroErrorType::JobError,
message: why.to_string(),
}),
}, },
}; };
println!("reply: {:?}", result.content);
Ok(result) Ok(result)
} }
@@ -1,5 +1,5 @@
[package] [package]
name = "nats" name = "nats-libs"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
@@ -1,16 +1,19 @@
use async_nats::Client; use async_nats::Client;
use async_trait::async_trait; use async_trait::async_trait;
use postcard::{from_bytes, to_stdvec}; use postcard::{from_bytes, to_stdvec};
use types::jobs::{JobResponse, JobsMap, JobsResponseMap}; use types::{
error::{CorroError, CorroErrorType},
jobs::{JobResponse, JobsMap, JobsResponseMap},
};
#[async_trait] #[async_trait]
pub trait JobClient { pub trait JobClient {
async fn send_job(&self, job: JobsMap) -> Result<JobsResponseMap, String>; async fn send_job(&self, job: JobsMap) -> Result<JobsResponseMap, CorroError>;
} }
#[async_trait] #[async_trait]
impl JobClient for Client { impl JobClient for Client {
async fn send_job(&self, job: JobsMap) -> Result<JobsResponseMap, String> { async fn send_job(&self, job: JobsMap) -> Result<JobsResponseMap, CorroError> {
let subject = match &job { let subject = match &job {
JobsMap::Download(_job) => "download", JobsMap::Download(_job) => "download",
JobsMap::Play(_job) => "play", JobsMap::Play(_job) => "play",
@@ -25,8 +28,6 @@ impl JobClient for Client {
.await .await
{ {
Ok(response) => { Ok(response) => {
println!("{:?}", &response.payload);
let parsed: JobResponse = from_bytes(&response.payload).unwrap(); let parsed: JobResponse = from_bytes(&response.payload).unwrap();
if let Some(error) = parsed.error { if let Some(error) = parsed.error {
@@ -34,10 +35,16 @@ impl JobClient for Client {
} else if let Some(content) = parsed.content { } else if let Some(content) = parsed.content {
Ok(content) Ok(content)
} else { } else {
Err("Unknown content".to_string()) Err(CorroError {
error_type: CorroErrorType::ParseError,
message: "Unknown payload".to_owned(),
})
} }
} }
Err(why) => Err(why.to_string()), Err(why) => Err(CorroError {
error_type: CorroErrorType::NatsError,
message: why.to_string(),
}),
} }
} }
} }
+9
View File
@@ -0,0 +1,9 @@
[package]
name = "serenity-libs"
version = "0.1.0"
edition = "2024"
[dependencies]
async-trait = { version = "0.1.89" }
serenity = { version = "0.12.5", default-features = false }
types = { path = "../../libs/types" }
+49
View File
@@ -0,0 +1,49 @@
use async_trait::async_trait;
use serenity::all::{
CommandInteraction,
Context,
CreateInteractionResponse,
CreateInteractionResponseMessage,
EditInteractionResponse,
};
use types::error::{CorroError, CorroErrorType};
#[async_trait]
pub trait CustomInteraction {
async fn create_text_response(&self, ctx: &Context, content: String) -> Result<(), CorroError>;
async fn edit_text_response(&self, ctx: &Context, content: String) -> Result<(), CorroError>;
}
#[async_trait]
impl CustomInteraction for CommandInteraction {
async fn create_text_response(&self, ctx: &Context, content: String) -> Result<(), CorroError> {
match self
.create_response(
&ctx,
CreateInteractionResponse::Message(
CreateInteractionResponseMessage::new().content(&content),
),
)
.await
{
Ok(_) => Ok(()),
Err(why) => Err(CorroError {
error_type: CorroErrorType::SerenityError,
message: why.to_string(),
}),
}
}
async fn edit_text_response(&self, ctx: &Context, content: String) -> Result<(), CorroError> {
match self
.edit_response(&ctx, EditInteractionResponse::new().content(&content))
.await
{
Ok(_) => Ok(()),
Err(why) => Err(CorroError {
error_type: CorroErrorType::SerenityError,
message: why.to_string(),
}),
}
}
}
+1
View File
@@ -0,0 +1 @@
pub mod functions;
+17
View File
@@ -0,0 +1,17 @@
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug)]
pub enum CorroErrorType {
JobError,
ParseError,
NatsError,
YtdlpError,
CommandError,
SerenityError,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct CorroError {
pub error_type: CorroErrorType,
pub message: String,
}
+2 -2
View File
@@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
use uuid::Uuid; use uuid::Uuid;
use crate::queue::YoutubeSong; use crate::{error::CorroError, queue::YoutubeSong};
#[derive(Debug, Deserialize, Serialize)] #[derive(Debug, Deserialize, Serialize)]
pub enum JobsResponseMap { pub enum JobsResponseMap {
@@ -23,7 +23,7 @@ pub enum JobsMap {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct JobResponse<T = JobsResponseMap> { pub struct JobResponse<T = JobsResponseMap> {
pub content: Option<T>, pub content: Option<T>,
pub error: Option<String>, pub error: Option<CorroError>,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
+1
View File
@@ -1,3 +1,4 @@
pub mod error;
pub mod jobs; pub mod jobs;
pub mod misc; pub mod misc;
pub mod queue; pub mod queue;