3 Commits

Author SHA1 Message Date
brotoskyj 6169e6065d Added Telemetry
closes #8
2026-01-15 14:32:28 -05:00
James Brotosky 0ae38b0e01 Merge pull request 'Closes #1' (#10) from feature/queue into master
Reviewed-on: brotoskyj/RhythmWorks#10
2026-01-12 12:37:26 -05:00
brotoskyj b2059a76c6 Closes #1 2026-01-12 12:36:15 -05:00
7 changed files with 567 additions and 71 deletions
Generated
+328 -3
View File
@@ -4,8 +4,13 @@ version = 4
[[package]] [[package]]
name = "RhythmWorks" name = "RhythmWorks"
version = "0.2.0" version = "0.4.0"
dependencies = [ dependencies = [
"opentelemetry 0.27.1",
"opentelemetry-otlp",
"opentelemetry-proto",
"opentelemetry-semantic-conventions",
"opentelemetry_sdk",
"reqwest", "reqwest",
"semver", "semver",
"serde", "serde",
@@ -14,6 +19,10 @@ dependencies = [
"songbird", "songbird",
"symphonia", "symphonia",
"tokio", "tokio",
"tonic",
"tracing",
"tracing-opentelemetry",
"tracing-subscriber",
"yt-dlp", "yt-dlp",
] ]
@@ -82,6 +91,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "anyhow"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]] [[package]]
name = "arbitrary" name = "arbitrary"
version = "1.4.2" version = "1.4.2"
@@ -100,6 +115,28 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "async-stream"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]] [[package]]
name = "async-trait" name = "async-trait"
version = "0.1.89" version = "0.1.89"
@@ -152,6 +189,53 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "axum"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
dependencies = [
"async-trait",
"axum-core",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"rustversion",
"serde",
"sync_wrapper",
"tower 0.5.2",
"tower-layer",
"tower-service",
]
[[package]]
name = "axum-core"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
dependencies = [
"async-trait",
"bytes",
"futures-util",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"rustversion",
"sync_wrapper",
"tower-layer",
"tower-service",
]
[[package]] [[package]]
name = "base64" name = "base64"
version = "0.22.1" version = "0.22.1"
@@ -1243,6 +1327,12 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.8.1" version = "1.8.1"
@@ -1257,6 +1347,7 @@ dependencies = [
"http", "http",
"http-body", "http-body",
"httparse", "httparse",
"httpdate",
"itoa", "itoa",
"pin-project-lite", "pin-project-lite",
"pin-utils", "pin-utils",
@@ -1275,6 +1366,7 @@ dependencies = [
"hyper", "hyper",
"hyper-util", "hyper-util",
"rustls 0.23.35", "rustls 0.23.35",
"rustls-native-certs",
"rustls-pki-types", "rustls-pki-types",
"tokio", "tokio",
"tokio-rustls 0.26.4", "tokio-rustls 0.26.4",
@@ -1282,6 +1374,19 @@ dependencies = [
"webpki-roots 1.0.4", "webpki-roots 1.0.4",
] ]
[[package]]
name = "hyper-timeout"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [
"hyper",
"hyper-util",
"pin-project-lite",
"tokio",
"tower-service",
]
[[package]] [[package]]
name = "hyper-tls" name = "hyper-tls"
version = "0.6.0" version = "0.6.0"
@@ -1515,6 +1620,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "itertools"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.16" version = "1.0.16"
@@ -1683,6 +1797,12 @@ dependencies = [
"regex-automata", "regex-automata",
] ]
[[package]]
name = "matchit"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
[[package]] [[package]]
name = "md-5" name = "md-5"
version = "0.10.6" version = "0.10.6"
@@ -1937,6 +2057,108 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "opentelemetry"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab70038c28ed37b97d8ed414b6429d343a8bbf44c9f79ec854f3a643029ba6d7"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite",
"thiserror 1.0.69",
"tracing",
]
[[package]]
name = "opentelemetry"
version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0"
dependencies = [
"futures-core",
"futures-sink",
"js-sys",
"pin-project-lite",
"thiserror 2.0.17",
]
[[package]]
name = "opentelemetry-http"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a8a7f5f6ba7c1b286c2fbca0454eaba116f63bbe69ed250b642d36fbb04d80"
dependencies = [
"async-trait",
"bytes",
"http",
"opentelemetry 0.27.1",
"reqwest",
]
[[package]]
name = "opentelemetry-otlp"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91cf61a1868dacc576bf2b2a1c3e9ab150af7272909e80085c3173384fe11f76"
dependencies = [
"async-trait",
"futures-core",
"http",
"opentelemetry 0.27.1",
"opentelemetry-http",
"opentelemetry-proto",
"opentelemetry_sdk",
"prost",
"reqwest",
"thiserror 1.0.69",
"tokio",
"tonic",
"tracing",
]
[[package]]
name = "opentelemetry-proto"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6e05acbfada5ec79023c85368af14abd0b307c015e9064d249b2a950ef459a6"
dependencies = [
"hex",
"opentelemetry 0.27.1",
"opentelemetry_sdk",
"prost",
"serde",
"tonic",
]
[[package]]
name = "opentelemetry-semantic-conventions"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc1b6902ff63b32ef6c489e8048c5e253e2e4a803ea3ea7e783914536eb15c52"
[[package]]
name = "opentelemetry_sdk"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "231e9d6ceef9b0b2546ddf52335785ce41252bc7474ee8ba05bfad277be13ab8"
dependencies = [
"async-trait",
"futures-channel",
"futures-executor",
"futures-util",
"glob",
"opentelemetry 0.27.1",
"percent-encoding",
"rand 0.8.5",
"serde_json",
"thiserror 1.0.69",
"tokio",
"tokio-stream",
"tracing",
]
[[package]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "2.10.1" version = "2.10.1"
@@ -2195,6 +2417,29 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "prost"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5"
dependencies = [
"bytes",
"prost-derive",
]
[[package]]
name = "prost-derive"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.111",
]
[[package]] [[package]]
name = "pulldown-cmark" name = "pulldown-cmark"
version = "0.9.6" version = "0.9.6"
@@ -2442,6 +2687,7 @@ dependencies = [
"pin-project-lite", "pin-project-lite",
"quinn", "quinn",
"rustls 0.23.35", "rustls 0.23.35",
"rustls-native-certs",
"rustls-pki-types", "rustls-pki-types",
"serde", "serde",
"serde_json", "serde_json",
@@ -2451,7 +2697,7 @@ dependencies = [
"tokio-native-tls", "tokio-native-tls",
"tokio-rustls 0.26.4", "tokio-rustls 0.26.4",
"tokio-util", "tokio-util",
"tower", "tower 0.5.2",
"tower-http", "tower-http",
"tower-service", "tower-service",
"url", "url",
@@ -2581,6 +2827,7 @@ version = "0.23.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
dependencies = [ dependencies = [
"log",
"once_cell", "once_cell",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
@@ -2601,6 +2848,15 @@ dependencies = [
"security-framework 3.5.1", "security-framework 3.5.1",
] ]
[[package]]
name = "rustls-pemfile"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "rustls-pki-types" name = "rustls-pki-types"
version = "1.13.2" version = "1.13.2"
@@ -3963,6 +4219,59 @@ dependencies = [
"tokio-util", "tokio-util",
] ]
[[package]]
name = "tonic"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
dependencies = [
"async-stream",
"async-trait",
"axum",
"base64",
"bytes",
"h2",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-timeout",
"hyper-util",
"percent-encoding",
"pin-project",
"prost",
"rustls-native-certs",
"rustls-pemfile",
"socket2 0.5.10",
"tokio",
"tokio-rustls 0.26.4",
"tokio-stream",
"tower 0.4.13",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
dependencies = [
"futures-core",
"futures-util",
"indexmap 1.9.3",
"pin-project",
"pin-project-lite",
"rand 0.8.5",
"slab",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]] [[package]]
name = "tower" name = "tower"
version = "0.5.2" version = "0.5.2"
@@ -3991,7 +4300,7 @@ dependencies = [
"http-body", "http-body",
"iri-string", "iri-string",
"pin-project-lite", "pin-project-lite",
"tower", "tower 0.5.2",
"tower-layer", "tower-layer",
"tower-service", "tower-service",
] ]
@@ -4062,6 +4371,22 @@ dependencies = [
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-opentelemetry"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc"
dependencies = [
"js-sys",
"opentelemetry 0.31.0",
"smallvec",
"tracing",
"tracing-core",
"tracing-log",
"tracing-subscriber",
"web-time",
]
[[package]] [[package]]
name = "tracing-subscriber" name = "tracing-subscriber"
version = "0.3.22" version = "0.3.22"
+10 -1
View File
@@ -1,9 +1,14 @@
[package] [package]
name = "RhythmWorks" name = "RhythmWorks"
version = "0.2.0" version = "0.4.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
opentelemetry = { version = "0.27.0", features = ["logs", "metrics", "trace"] }
opentelemetry-otlp = { version = "0.27.0", features = ["trace", "metrics", "grpc-tonic", "http-proto", "tls", "reqwest-client", "reqwest-rustls"] }
opentelemetry-proto = "0.27.0"
opentelemetry-semantic-conventions = "0.27.0"
opentelemetry_sdk = { version = "0.27.0", features = ["rt-tokio", "trace"] }
reqwest = { version = "0.12.28", features = ["blocking", "json"] } reqwest = { version = "0.12.28", features = ["blocking", "json"] }
semver = "1.0.27" semver = "1.0.27"
serde = "1.0.228" serde = "1.0.228"
@@ -12,6 +17,10 @@ serenity = { version = "0.12.5", features = ["client", "gateway", "voice"] }
songbird = { version = "0.5.0", features = ["builtin-queue", "driver", "serenity"] } songbird = { version = "0.5.0", features = ["builtin-queue", "driver", "serenity"] }
symphonia = { version = "0.5.5", features = ["aac", "alac", "isomp4", "mp3"] } symphonia = { version = "0.5.5", features = ["aac", "alac", "isomp4", "mp3"] }
tokio = "1.48.0" tokio = "1.48.0"
tonic = { version = "0.12.3", features = ["tls-roots"] }
tracing = "0.1.41"
tracing-opentelemetry = "0.32.0"
tracing-subscriber = "0.3.20"
yt-dlp = "1.4.7" yt-dlp = "1.4.7"
+6
View File
@@ -0,0 +1,6 @@
#!/bin/bash
cargo clean
cargo fmt
cargo clippy --fix --all-targets --allow-dirty
cargo build --release
cargo build --release --target x86_64-pc-windows-gnu
+181 -56
View File
@@ -1,6 +1,14 @@
use modules::*; use modules::*;
use opentelemetry::KeyValue;
use opentelemetry::global;
use opentelemetry::global::ObjectSafeSpan;
use opentelemetry::trace::SpanKind;
use opentelemetry::trace::Status;
use opentelemetry::trace::TraceContextExt;
use opentelemetry::trace::Tracer;
use serenity::all::ChannelId; use serenity::all::ChannelId;
use serenity::async_trait; use serenity::async_trait;
use serenity::futures::channel::oneshot::channel;
use serenity::model::channel::Message; use serenity::model::channel::Message;
use serenity::model::gateway::Ready; use serenity::model::gateway::Ready;
use serenity::prelude::*; use serenity::prelude::*;
@@ -8,18 +16,12 @@ use songbird::SerenityInit;
use songbird::get; use songbird::get;
use songbird::input::HlsRequest; use songbird::input::HlsRequest;
use songbird::input::Input; use songbird::input::Input;
use songbird::tracks::TrackHandle;
use std::collections::HashMap;
use std::io; use std::io;
use std::process::Command; use std::process::Command;
use std::process::Stdio; use std::process::Stdio;
use std::sync::Arc;
pub mod modules; pub mod modules;
type TrackMap = Arc<Mutex<HashMap<u64, TrackHandle>>>; struct Handler;
struct Handler {
track_map: TrackMap,
}
fn get_user_voice_channel(ctx: &Context, msg: &Message) -> Option<ChannelId> { fn get_user_voice_channel(ctx: &Context, msg: &Message) -> Option<ChannelId> {
let guild_id = msg.guild_id?; let guild_id = msg.guild_id?;
let guild = ctx.cache.guild(guild_id)?; let guild = ctx.cache.guild(guild_id)?;
@@ -38,13 +40,50 @@ impl EventHandler for Handler {
println!("Error sending message: {why:?}"); println!("Error sending message: {why:?}");
} }
if msg.content == "!join" { if msg.content == "!join" {
let tracer = global::tracer("RhythmWorks");
let span = tracer.start("Join Message Received");
let cx = opentelemetry::Context::current_with_span(span);
let guild_id = match msg.guild_id { let guild_id = match msg.guild_id {
Some(g) => g, Some(g) => {
None => return, cx.span().add_event(
}; "Received Guild ID",
let channel_id = match get_user_voice_channel(&ctx, &msg) { vec![KeyValue::new("Server ID", g.to_string())],
Some(c) => c, );
cx.span().set_status(opentelemetry::trace::Status::Ok);
cx.span()
.set_attribute(KeyValue::new("Guild_ID", g.to_string()));
g
}
None => { None => {
cx.span().add_event(
"Failed to Retreive Guild ID",
vec![KeyValue::new("Server ID", "None")],
);
cx.span()
.set_status(opentelemetry::trace::Status::error("Failed"));
return;
}
};
let channel_id = match get_user_voice_channel(&ctx, &msg) {
Some(c) => {
cx.span().add_event(
"Received Channel ID",
vec![KeyValue::new("Channel ID", c.to_string())],
);
cx.span().set_status(Status::Ok);
c
}
None => {
cx.span().add_event(
"Failed to Retreive Channel ID",
vec![KeyValue::new("Channel ID", "None")],
);
cx.span()
.set_status(opentelemetry::trace::Status::error(format!(
"{} not in Voice Channel",
msg.author.to_string()
)));
let _ = msg let _ = msg
.channel_id .channel_id
.say(&ctx.http, "You must be in a voice channel") .say(&ctx.http, "You must be in a voice channel")
@@ -52,7 +91,6 @@ impl EventHandler for Handler {
return; return;
} }
}; };
let manager = get(&ctx).await.expect("Songbird not initialized").clone(); let manager = get(&ctx).await.expect("Songbird not initialized").clone();
let _ = manager.join(guild_id, channel_id).await; let _ = manager.join(guild_id, channel_id).await;
@@ -61,6 +99,11 @@ impl EventHandler for Handler {
.channel_id .channel_id
.say(&ctx.http, "Joined your voice channel!") .say(&ctx.http, "Joined your voice channel!")
.await; .await;
cx.span().add_event(
"Joined Voice Channel",
vec![KeyValue::new("Voice Channel", msg.channel_id.to_string())],
);
cx.span().set_status(Status::Ok);
} }
if msg.content == "!leave" { if msg.content == "!leave" {
let guild_id = match msg.guild_id { let guild_id = match msg.guild_id {
@@ -77,61 +120,130 @@ impl EventHandler for Handler {
} }
} }
if msg.content.starts_with("!play") { if msg.content.starts_with("!play") {
let mut url = msg let tracer = global::tracer("RhythmWorks");
let span = tracer.start("Play Message Received");
let mut yt_url = msg
.content .content
.strip_prefix("!play ") .strip_prefix("!play ")
.unwrap_or("") .unwrap_or("")
.trim() .trim()
.to_string(); .to_string();
let separator = "&"; if let Some(offset) = yt_url.find('&') {
if let Some(offset) = url.find(separator) { yt_url.truncate(offset);
url.truncate(offset);
} }
if url.is_empty() { if yt_url.is_empty() {
let _ = msg.reply(&ctx, "Please provide a YouTube URL.").await; let _ = msg.reply(&ctx, "Please provide a YouTube URL.").await;
return; return;
} }
let guild_id = msg.guild_id.unwrap(); let cx = opentelemetry::Context::current_with_span(span);
let manager = songbird::get(&ctx) cx.span()
.await .set_attribute(KeyValue::new("Youtube_URL", yt_url.to_string()));
.expect("Songbird Voice client placed in at initialisation.") let guild_id = match msg.guild_id {
.clone(); Some(g) => {
let handler_lock = if let Some(handler) = manager.get(guild_id) { cx.span().add_event(
handler "Received Guild ID",
} else { vec![KeyValue::new("Server ID", g.to_string())],
let channel_id = get_user_voice_channel(&ctx, &msg); );
match channel_id { cx.span().set_status(opentelemetry::trace::Status::Ok);
Some(c) => manager.join(guild_id, c).await.unwrap(), cx.span()
.set_attribute(KeyValue::new("Guild_ID", g.to_string()));
g
}
None => { None => {
let _ = msg.reply(&ctx, "Join a voice channel first!").await; cx.span().add_event(
"Failed to Retreive Guild ID",
vec![KeyValue::new("Server ID", "None")],
);
cx.span()
.set_status(opentelemetry::trace::Status::error("Failed"));
return; return;
} }
};
let manager = songbird::get(&ctx).await.unwrap().clone();
let handler_lock = if let Some(call) = manager.get(guild_id) {
call
} else {
let channel_id = match get_user_voice_channel(&ctx, &msg) {
Some(c) => c,
None => {
cx.span().add_event(
"Failed to Retreive Channel ID",
vec![KeyValue::new("Channel ID", "None")],
);
cx.span()
.set_status(opentelemetry::trace::Status::error(format!(
"{} not in Voice Channel",
msg.author.to_string()
)));
let _ = msg
.channel_id
.say(&ctx.http, "You must be in a voice channel")
.await;
return;
} }
}; };
let output = Command::new("./yt-dlp") manager.join(guild_id, channel_id).await.unwrap()
.args(["-f", "bestaudio", "-g", &url]) };
let output = match Command::new("./yt-dlp")
.args(["-f", "bestaudio", "-g", &yt_url])
.stdout(Stdio::piped()) .stdout(Stdio::piped())
.output() .output()
.expect("Failed"); {
let url_bytes = output.stdout; Ok(output) => {
let url = String::from_utf8(url_bytes).expect("Failed"); cx.span().add_event(
let mut handler = handler_lock.lock().await; "Retreiving Youtube Information",
let input = Input::from(HlsRequest::new(reqwest::Client::new(), url.to_string())); vec![KeyValue::new("Success", yt_url.to_string())],
let track_handle = handler.play_input(input); );
let _ = track_handle.play(); output
self.track_map }
.lock() Err(e) => {
.await cx.span().add_event(
.insert(guild_id.into(), track_handle); "Retreiving Youtube Information",
let _ = msg.channel_id.say(&ctx.http, "🎶 Playing...").await; vec![KeyValue::new("Failed", e.to_string())],
);
cx.span().set_status(Status::error("Failed"));
eprintln!("{}", e);
return;
}
};
let stream_url = String::from_utf8(output.stdout)
.expect("Invalid UTF-8")
.trim()
.to_string();
let mut call = handler_lock.lock().await;
let input = Input::from(HlsRequest::new(reqwest::Client::new(), stream_url));
let _ = call.enqueue_input(input).await;
let position = call.queue().len();
let response = if position == 1 {
cx.span().add_event("Starting Playback", vec![KeyValue::new("Status", "Playing".to_string())]);
"🎶 Now playing!".to_string()
} else {
cx.span().add_event("Starting Playback", vec![KeyValue::new("Status", "Added to Queue".to_string())]);
format!("🎶 Added Song to Queue: #{}", position)
};
let _ = msg.channel_id.say(&ctx.http, response).await;
}
if msg.content == "!skip" {
let guild_id = match msg.guild_id {
Some(g) => g,
None => return,
};
let manager = songbird::get(&ctx).await.unwrap();
if let Some(call) = manager.get(guild_id) {
let call = call.lock().await;
let _ = call.queue().skip();
let _ = msg.channel_id.say(&ctx.http, "Skipped").await;
}
} }
if msg.content == "!pause" { if msg.content == "!pause" {
let guild_id = match msg.guild_id { let guild_id = match msg.guild_id {
Some(g) => g, Some(g) => g,
None => return, None => return,
}; };
if let Some(handle) = self.track_map.lock().await.get(&guild_id.into()) { let manager = songbird::get(&ctx).await.unwrap();
let _ = handle.pause(); if let Some(call) = manager.get(guild_id) {
let call = call.lock().await;
let _ = call.queue().pause();
} }
} }
if msg.content == "!resume" { if msg.content == "!resume" {
@@ -139,8 +251,10 @@ impl EventHandler for Handler {
Some(g) => g, Some(g) => g,
None => return, None => return,
}; };
if let Some(handle) = self.track_map.lock().await.get(&guild_id.into()) { let manager = songbird::get(&ctx).await.unwrap();
let _ = handle.play(); if let Some(call) = manager.get(guild_id) {
let call = call.lock().await;
let _ = call.queue().resume();
} }
} }
if msg.content == "!stop" { if msg.content == "!stop" {
@@ -148,8 +262,10 @@ impl EventHandler for Handler {
Some(g) => g, Some(g) => g,
None => return, None => return,
}; };
if let Some(handle) = self.track_map.lock().await.get(&guild_id.into()) { let manager = songbird::get(&ctx).await.unwrap();
let _ = handle.stop(); if let Some(call) = manager.get(guild_id) {
let call = call.lock().await;
let _ = call.queue().stop();
} }
} }
} }
@@ -157,24 +273,33 @@ impl EventHandler for Handler {
println!("{} is connected!", ready.user.name); println!("{} is connected!", ready.user.name);
} }
} }
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
println!("RhythmWorks Copyright (C) 2025 James Brotosky\n println!(
"RhythmWorks Copyright (C) 2025 James Brotosky\n
This program comes with ABSOLUTELY NO WARRANTY\n This program comes with ABSOLUTELY NO WARRANTY\n
This is free software, and you are welcome to redistribute it under certain conditions."); This is free software, and you are welcome to redistribute it under certain conditions."
updator::update().await; );
let tracer_provider = telemetry::init_telemetry();
global::set_tracer_provider(tracer_provider.clone());
let tracer: global::BoxedTracer = global::tracer("tracer");
tracer
.in_span("Checking for Updates", |cx| updator::update())
.await;
println!("Please paste in your bot token"); println!("Please paste in your bot token");
let mut input = String::new(); let mut input = String::new();
io::stdin().read_line(&mut input).expect("Failed to read line"); io::stdin()
.read_line(&mut input)
.expect("Failed to read line");
let token = input.trim(); let token = input.trim();
let intents = GatewayIntents::GUILD_MESSAGES let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::GUILDS | GatewayIntents::GUILDS
| GatewayIntents::DIRECT_MESSAGES | GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT | GatewayIntents::MESSAGE_CONTENT
| GatewayIntents::GUILD_VOICE_STATES; | GatewayIntents::GUILD_VOICE_STATES;
let track_map: TrackMap = Arc::new(Mutex::new(HashMap::new()));
let mut client = Client::builder(&token, intents) let mut client = Client::builder(&token, intents)
.event_handler(Handler { track_map }) .event_handler(Handler)
.register_songbird() .register_songbird()
.await .await
.expect("Err creating client"); .expect("Err creating client");
+1
View File
@@ -1 +1,2 @@
pub mod updator; pub mod updator;
pub mod telemetry;
+26
View File
@@ -0,0 +1,26 @@
use opentelemetry::KeyValue;
use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::{Resource, runtime};
use tonic::transport::{Channel, ClientTlsConfig};
pub fn init_telemetry() -> opentelemetry_sdk::trace::TracerProvider {
let endpoint = "https://signoz.racooncity.org".to_string();
let channel = Channel::from_shared(endpoint.clone())
.unwrap()
.tls_config(ClientTlsConfig::new().with_native_roots())
.unwrap()
.connect_lazy();
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint(endpoint.clone())
.with_channel(channel)
.build()
.expect("Failed to Build Exporter");
opentelemetry_sdk::trace::TracerProvider::builder()
.with_batch_exporter(exporter, runtime::Tokio)
.with_resource(Resource::new(vec![KeyValue::new(
"service.name",
"RhythmWorks",
)]))
.build()
}
+11 -7
View File
@@ -1,15 +1,22 @@
use std::thread; use std::thread;
use serde::Deserialize;
use semver::Version;
use reqwest; use reqwest;
use semver::Version;
use serde::Deserialize;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct Release { struct Release {
tag_name: String, tag_name: String,
} }
pub async fn update() { pub async fn update() {
let body = reqwest::get("https://git.racooncity.org/api/v1/repos/brotoskyj/RhythmWorks/releases/latest").await.unwrap().text().await.unwrap(); let body = reqwest::get(
"https://git.racooncity.org/api/v1/repos/brotoskyj/RhythmWorks/releases/latest",
)
.await
.unwrap()
.text()
.await
.unwrap();
let release_info: Release = serde_json::from_str(&body).unwrap(); let release_info: Release = serde_json::from_str(&body).unwrap();
let latest_version = Version::parse(&release_info.tag_name).unwrap(); let latest_version = Version::parse(&release_info.tag_name).unwrap();
let current_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); let current_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
@@ -18,9 +25,6 @@ pub async fn update() {
println!("URL: https://git.racooncity.org/brotoskyj/RhythmWorks/releases"); println!("URL: https://git.racooncity.org/brotoskyj/RhythmWorks/releases");
thread::sleep(std::time::Duration::from_secs(10)); thread::sleep(std::time::Duration::from_secs(10));
std::process::exit(1); std::process::exit(1);
} else {
}
else {
} }
} }