4 Commits

7 changed files with 1025 additions and 33 deletions
+1
View File
@@ -1 +1,2 @@
/target
cliff.toml
Generated
+902 -7
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -1,16 +1,21 @@
[package]
name = "RhythmWorks"
version = "0.5.1"
version = "0.7.0"
edition = "2024"
[dependencies]
color-eyre = "0.6.5"
crossterm = "0.29.0"
dialoguer = "0.12.0"
keyring = { version = "3.6.3", features = ["linux-native", "windows-native"] }
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-appender-log = "0.27.0"
opentelemetry-appender-tracing = "0.27.0"
opentelemetry-otlp = { version = "0.27.0", features = ["trace", "metrics", "grpc-tonic", "http-proto", "tls", "reqwest-client", "reqwest-rustls", "logs"] }
opentelemetry-proto = "0.27.0"
opentelemetry-semantic-conventions = "0.27.0"
opentelemetry_sdk = { version = "0.27.0", features = ["rt-tokio", "trace"] }
opentelemetry_sdk = { version = "0.27.0", features = ["logs", "rt-tokio", "trace"] }
ratatui = { version = "0.30.0", features = ["all-widgets"] }
reqwest = { version = "0.12.28", features = ["blocking", "json"] }
semver = "1.0.27"
serde = "1.0.228"
@@ -19,6 +24,7 @@ serenity = { version = "0.12.5", features = ["client", "gateway", "voice"] }
songbird = { version = "0.5.0", features = ["builtin-queue", "driver", "serenity"] }
symphonia = { version = "0.5.5", features = ["aac", "alac", "isomp4", "mp3"] }
tokio = "1.48.0"
tokio-util = "0.7.18"
tonic = { version = "0.12.3", features = ["tls-roots"] }
tracing = "0.1.41"
tracing-opentelemetry = "0.32.0"
+1
View File
@@ -0,0 +1 @@
## [0.5.2] - 2026-01-21
+87 -16
View File
@@ -18,11 +18,23 @@ use songbird::events::EventHandler as VoiceEventHandler;
use songbird::get;
use songbird::input::HlsRequest;
use songbird::input::Input;
use tracing::instrument;
use std::env;
use std::process::Command;
use std::process::Stdio;
pub mod modules;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::Layer;
use tracing_subscriber::filter::Targets;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
pub mod modules;
enum BotState {
Stopped,
Running,
}
struct TrackTraceHandler {
otel_ctx: opentelemetry::Context,
guild_id: String,
@@ -83,6 +95,7 @@ impl EventHandler for Handler {
let cx = opentelemetry::Context::current_with_span(span);
let guild_id = match msg.guild_id {
Some(g) => {
tracing::info!("Received Guild ID");
cx.span().add_event(
"Received Guild ID",
vec![KeyValue::new("Server ID", g.to_string())],
@@ -349,6 +362,30 @@ impl EventHandler for Handler {
println!("{} is connected!", ready.user.name);
}
}
async fn run_bot(token: String, shutdown: CancellationToken) {
let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::GUILDS
| GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT
| GatewayIntents::GUILD_VOICE_STATES;
let mut client = Client::builder(&token, intents)
.event_handler(Handler)
.register_songbird()
.await
.expect("Err creating client");
tokio::select! {
result = client.start() => {
if let Err(why) = result {
println!("Client error: {why:?}");
}
}
_ = shutdown.cancelled() => {
println!("Shutting down bot...");
}
}
}
#[tokio::main]
async fn main() {
@@ -357,17 +394,33 @@ async fn main() {
This program comes with ABSOLUTELY NO WARRANTY\n
This is free software, and you are welcome to redistribute it under certain conditions."
);
let tracer_provider = telemetry::init_telemetry();
let (tracer_provider, logger_provider) = telemetry::init_telemetry();
global::set_tracer_provider(tracer_provider.clone());
let filter = Targets::new()
.with_target("RhythmWorks", LevelFilter::INFO)
.with_default(LevelFilter::OFF);
let otel_log_layer =
opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge::new(&logger_provider)
.with_filter(filter);
tracing_subscriber::registry().with(otel_log_layer).init();
let tracer: global::BoxedTracer = global::tracer("tracer");
tracer
.in_span("Checking for Updates", |_cx| updator::update())
.await;
let service_name = "RhythmWorks";
let username = env::var("USER").or_else(|_| env::var("USERNAME")).unwrap();
let mut bot_task: Option<JoinHandle<()>> = None;
let mut shutdown_token: Option<CancellationToken> = None;
let mut bot_state = BotState::Stopped;
loop {
let token = credentialmanager::get_token(service_name, &username);
let options: Vec<&'static str> = vec!["- Start Bot", "- Change Token", "- Delete Token"];
let options: Vec<&'static str> = vec![
"- Start Bot",
"- Stop Bot",
"- Change Token",
"- Delete Token",
"- Exit",
];
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Choose an Option")
.default(0)
@@ -376,26 +429,44 @@ This is free software, and you are welcome to redistribute it under certain cond
.unwrap();
match selection {
0 => {
let intents = GatewayIntents::GUILD_MESSAGES
| GatewayIntents::GUILDS
| GatewayIntents::DIRECT_MESSAGES
| GatewayIntents::MESSAGE_CONTENT
| GatewayIntents::GUILD_VOICE_STATES;
let mut client = Client::builder(&token, intents)
.event_handler(Handler)
.register_songbird()
.await
.expect("Err creating client");
if let Err(why) = client.start().await {
println!("Client error: {why:?}");
if matches!(bot_state, BotState::Running) {
println!("[x] Bot is already running.");
continue;
}
let token = credentialmanager::get_token(service_name, &username);
let shutdown = CancellationToken::new();
let shutdown_clone = shutdown.clone();
let task = tokio::spawn(run_bot(token, shutdown_clone));
bot_task = Some(task);
shutdown_token = Some(shutdown);
bot_state = BotState::Running;
println!("✅ Bot started.");
}
1 => {
credentialmanager::change_token(service_name, &username);
if let Some(token) = shutdown_token.take() {
token.cancel();
}
if let Some(task) = bot_task.take() {
let _ = task.await;
}
println!("Bot stopped.");
}
2 => {
credentialmanager::change_token(service_name, &username);
}
3 => {
credentialmanager::remove_token(service_name, &username);
}
4 => {
if let Some(token) = shutdown_token {
token.cancel();
}
break;
}
_ => println!("Invalid Option"),
}
}
+23 -5
View File
@@ -1,9 +1,13 @@
use opentelemetry::KeyValue;
use opentelemetry::global;
use opentelemetry_otlp::{WithExportConfig, WithTonicConfig};
use opentelemetry_sdk::{Resource, runtime};
use opentelemetry_sdk::{Resource, logs, runtime};
use tonic::transport::{Channel, ClientTlsConfig};
pub fn init_telemetry() -> opentelemetry_sdk::trace::TracerProvider {
pub fn init_telemetry() -> (
opentelemetry_sdk::trace::TracerProvider,
opentelemetry_sdk::logs::LoggerProvider,
) {
let endpoint = "https://signoz.racooncity.org".to_string();
let channel = Channel::from_shared(endpoint.clone())
.unwrap()
@@ -13,14 +17,28 @@ pub fn init_telemetry() -> opentelemetry_sdk::trace::TracerProvider {
let exporter = opentelemetry_otlp::SpanExporter::builder()
.with_tonic()
.with_endpoint(endpoint.clone())
.with_channel(channel)
.with_channel(channel.clone())
.build()
.expect("Failed to Build Exporter");
opentelemetry_sdk::trace::TracerProvider::builder()
let log_exporter = opentelemetry_otlp::LogExporter::builder()
.with_tonic()
.with_endpoint(endpoint.clone())
.with_channel(channel.clone())
.build()
.expect("Failed to build log exporter");
let logger_provider = logs::LoggerProvider::builder()
.with_resource(Resource::new(vec![KeyValue::new(
"service.name",
"RhythmWorks",
)]))
.with_batch_exporter(log_exporter, runtime::Tokio)
.build();
let tracer_provider = opentelemetry_sdk::trace::TracerProvider::builder()
.with_batch_exporter(exporter, runtime::Tokio)
.with_resource(Resource::new(vec![KeyValue::new(
"service.name",
"RhythmWorks",
)]))
.build()
.build();
(tracer_provider, logger_provider)
}
+2 -2
View File
@@ -10,7 +10,7 @@ struct Release {
pub async fn update() {
let body = reqwest::get(
"https://git.racooncity.org/api/v1/repos/brotoskyj/RhythmWorks/releases/latest",
"https://git.racooncity.org/api/v1/repos/mysticmomba/RhythmWorks/releases/latest",
)
.await
.unwrap()
@@ -22,7 +22,7 @@ pub async fn update() {
let current_version = Version::parse(env!("CARGO_PKG_VERSION")).unwrap();
if latest_version > current_version {
println!("Update Available! Please download the latest version");
println!("URL: https://git.racooncity.org/brotoskyj/RhythmWorks/releases");
println!("URL: https://git.racooncity.org/mysticmomba/RhythmWorks/releases");
thread::sleep(std::time::Duration::from_secs(10));
std::process::exit(1);
}