Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8fefbddffd | |||
| 7dea731cbf | |||
| ff9a46c05c |
Generated
+69
-1
@@ -4,8 +4,10 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "RhythmWorks"
|
name = "RhythmWorks"
|
||||||
version = "0.4.0"
|
version = "0.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"dialoguer",
|
||||||
|
"keyring",
|
||||||
"opentelemetry 0.27.1",
|
"opentelemetry 0.27.1",
|
||||||
"opentelemetry-otlp",
|
"opentelemetry-otlp",
|
||||||
"opentelemetry-proto",
|
"opentelemetry-proto",
|
||||||
@@ -444,6 +446,19 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "console"
|
||||||
|
version = "0.16.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4"
|
||||||
|
dependencies = [
|
||||||
|
"encode_unicode",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"unicode-width",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.9.6"
|
version = "0.9.6"
|
||||||
@@ -777,6 +792,18 @@ dependencies = [
|
|||||||
"syn 2.0.111",
|
"syn 2.0.111",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dialoguer"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96"
|
||||||
|
dependencies = [
|
||||||
|
"console",
|
||||||
|
"shell-words",
|
||||||
|
"tempfile",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -831,6 +858,12 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encode_unicode"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding_rs"
|
name = "encoding_rs"
|
||||||
version = "0.8.33"
|
version = "0.8.33"
|
||||||
@@ -1655,6 +1688,19 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keyring"
|
||||||
|
version = "3.6.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"linux-keyutils",
|
||||||
|
"log",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1719,6 +1765,16 @@ dependencies = [
|
|||||||
"zlib-rs",
|
"zlib-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-keyutils"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.10.0",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -3239,6 +3295,12 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "shell-words"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@@ -4590,6 +4652,12 @@ version = "1.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "universal-hash"
|
name = "universal-hash"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
|||||||
+3
-1
@@ -1,9 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "RhythmWorks"
|
name = "RhythmWorks"
|
||||||
version = "0.4.0"
|
version = "0.5.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
dialoguer = "0.12.0"
|
||||||
|
keyring = { version = "3.6.3", features = ["linux-native", "windows-native"] }
|
||||||
opentelemetry = { version = "0.27.0", features = ["logs", "metrics", "trace"] }
|
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-otlp = { version = "0.27.0", features = ["trace", "metrics", "grpc-tonic", "http-proto", "tls", "reqwest-client", "reqwest-rustls"] }
|
||||||
opentelemetry-proto = "0.27.0"
|
opentelemetry-proto = "0.27.0"
|
||||||
|
|||||||
+122
-29
@@ -1,26 +1,64 @@
|
|||||||
|
use dialoguer::Select;
|
||||||
|
use dialoguer::theme::ColorfulTheme;
|
||||||
use modules::*;
|
use modules::*;
|
||||||
use opentelemetry::KeyValue;
|
use opentelemetry::KeyValue;
|
||||||
use opentelemetry::global;
|
use opentelemetry::global;
|
||||||
use opentelemetry::global::ObjectSafeSpan;
|
|
||||||
use opentelemetry::trace::SpanKind;
|
|
||||||
use opentelemetry::trace::Status;
|
use opentelemetry::trace::Status;
|
||||||
use opentelemetry::trace::TraceContextExt;
|
use opentelemetry::trace::TraceContextExt;
|
||||||
use opentelemetry::trace::Tracer;
|
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::*;
|
||||||
|
use songbird::Event;
|
||||||
|
use songbird::EventContext;
|
||||||
use songbird::SerenityInit;
|
use songbird::SerenityInit;
|
||||||
|
use songbird::events::EventHandler as VoiceEventHandler;
|
||||||
use songbird::get;
|
use songbird::get;
|
||||||
use songbird::input::HlsRequest;
|
use songbird::input::HlsRequest;
|
||||||
use songbird::input::Input;
|
use songbird::input::Input;
|
||||||
use std::io;
|
use std::env;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
pub mod modules;
|
pub mod modules;
|
||||||
|
|
||||||
|
struct TrackTraceHandler {
|
||||||
|
otel_ctx: opentelemetry::Context,
|
||||||
|
guild_id: String,
|
||||||
|
track_url: String,
|
||||||
|
hls_url: String,
|
||||||
|
event_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl VoiceEventHandler for TrackTraceHandler {
|
||||||
|
async fn act(&self, ctx: &EventContext<'_>) -> Option<Event> {
|
||||||
|
let tracer = global::tracer("RhythmWorks");
|
||||||
|
let track_status = if let EventContext::Track(track_list) = ctx {
|
||||||
|
track_list.first().map(|state| format!("{:?}", state.0))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
tracer.in_span(self.event_name, |cx| {
|
||||||
|
cx.span()
|
||||||
|
.set_attribute(KeyValue::new("Guild_ID", self.guild_id.clone()));
|
||||||
|
cx.span()
|
||||||
|
.set_attribute(KeyValue::new("Youtube_URL", self.track_url.clone()));
|
||||||
|
cx.span()
|
||||||
|
.set_attribute(KeyValue::new("Stream_URL", self.hls_url.clone()));
|
||||||
|
cx.span().add_event(
|
||||||
|
self.event_name,
|
||||||
|
vec![KeyValue::new(
|
||||||
|
"track_status",
|
||||||
|
track_status.unwrap().to_string(),
|
||||||
|
)],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
struct Handler;
|
struct Handler;
|
||||||
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?;
|
||||||
@@ -82,7 +120,7 @@ impl EventHandler for Handler {
|
|||||||
cx.span()
|
cx.span()
|
||||||
.set_status(opentelemetry::trace::Status::error(format!(
|
.set_status(opentelemetry::trace::Status::error(format!(
|
||||||
"{} not in Voice Channel",
|
"{} not in Voice Channel",
|
||||||
msg.author.to_string()
|
msg.author
|
||||||
)));
|
)));
|
||||||
let _ = msg
|
let _ = msg
|
||||||
.channel_id
|
.channel_id
|
||||||
@@ -173,7 +211,7 @@ impl EventHandler for Handler {
|
|||||||
cx.span()
|
cx.span()
|
||||||
.set_status(opentelemetry::trace::Status::error(format!(
|
.set_status(opentelemetry::trace::Status::error(format!(
|
||||||
"{} not in Voice Channel",
|
"{} not in Voice Channel",
|
||||||
msg.author.to_string()
|
msg.author
|
||||||
)));
|
)));
|
||||||
let _ = msg
|
let _ = msg
|
||||||
.channel_id
|
.channel_id
|
||||||
@@ -185,7 +223,7 @@ impl EventHandler for Handler {
|
|||||||
manager.join(guild_id, channel_id).await.unwrap()
|
manager.join(guild_id, channel_id).await.unwrap()
|
||||||
};
|
};
|
||||||
let output = match Command::new("./yt-dlp")
|
let output = match Command::new("./yt-dlp")
|
||||||
.args(["-f", "bestaudio", "-g", &yt_url])
|
.args(["-f", "bestaudio", "-g", "--no-playlist", &yt_url])
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.output()
|
.output()
|
||||||
{
|
{
|
||||||
@@ -210,18 +248,56 @@ impl EventHandler for Handler {
|
|||||||
.expect("Invalid UTF-8")
|
.expect("Invalid UTF-8")
|
||||||
.trim()
|
.trim()
|
||||||
.to_string();
|
.to_string();
|
||||||
|
let telemetry_stream_url = stream_url.clone();
|
||||||
let mut call = handler_lock.lock().await;
|
let mut call = handler_lock.lock().await;
|
||||||
let input = Input::from(HlsRequest::new(reqwest::Client::new(), stream_url));
|
let input = Input::from(HlsRequest::new(reqwest::Client::new(), stream_url));
|
||||||
let _ = call.enqueue_input(input).await;
|
let track_handle = call.enqueue_input(input).await;
|
||||||
|
let otel_ctx = opentelemetry::Context::current();
|
||||||
let position = call.queue().len();
|
let position = call.queue().len();
|
||||||
let response = if position == 1 {
|
let response = if position == 1 {
|
||||||
cx.span().add_event("Starting Playback", vec![KeyValue::new("Status", "Playing".to_string())]);
|
cx.span().add_event(
|
||||||
|
"Starting Playback",
|
||||||
|
vec![KeyValue::new("Status", "Playing".to_string())],
|
||||||
|
);
|
||||||
"🎶 Now playing!".to_string()
|
"🎶 Now playing!".to_string()
|
||||||
} else {
|
} else {
|
||||||
cx.span().add_event("Starting Playback", vec![KeyValue::new("Status", "Added to Queue".to_string())]);
|
cx.span().add_event(
|
||||||
|
"Starting Playback",
|
||||||
|
vec![KeyValue::new("Status", "Added to Queue".to_string())],
|
||||||
|
);
|
||||||
format!("🎶 Added Song to Queue: #{}", position)
|
format!("🎶 Added Song to Queue: #{}", position)
|
||||||
};
|
};
|
||||||
let _ = msg.channel_id.say(&ctx.http, response).await;
|
let _ = msg.channel_id.say(&ctx.http, response).await;
|
||||||
|
let _ = track_handle.add_event(
|
||||||
|
Event::Track(songbird::events::TrackEvent::Play),
|
||||||
|
TrackTraceHandler {
|
||||||
|
otel_ctx: otel_ctx.clone(),
|
||||||
|
guild_id: guild_id.to_string().clone(),
|
||||||
|
track_url: yt_url.clone(),
|
||||||
|
hls_url: telemetry_stream_url.clone(),
|
||||||
|
event_name: "Track Started Playing",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _ = track_handle.add_event(
|
||||||
|
Event::Track(songbird::events::TrackEvent::End),
|
||||||
|
TrackTraceHandler {
|
||||||
|
otel_ctx: otel_ctx.clone(),
|
||||||
|
guild_id: guild_id.to_string().clone(),
|
||||||
|
track_url: yt_url.clone(),
|
||||||
|
hls_url: telemetry_stream_url.clone(),
|
||||||
|
event_name: "Track Finished",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
let _ = track_handle.add_event(
|
||||||
|
Event::Track(songbird::events::TrackEvent::Error),
|
||||||
|
TrackTraceHandler {
|
||||||
|
otel_ctx: otel_ctx.clone(),
|
||||||
|
guild_id: guild_id.to_string().clone(),
|
||||||
|
track_url: yt_url.clone(),
|
||||||
|
hls_url: telemetry_stream_url.clone(),
|
||||||
|
event_name: "Playback had an error",
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if msg.content == "!skip" {
|
if msg.content == "!skip" {
|
||||||
let guild_id = match msg.guild_id {
|
let guild_id = match msg.guild_id {
|
||||||
@@ -285,25 +361,42 @@ This is free software, and you are welcome to redistribute it under certain cond
|
|||||||
global::set_tracer_provider(tracer_provider.clone());
|
global::set_tracer_provider(tracer_provider.clone());
|
||||||
let tracer: global::BoxedTracer = global::tracer("tracer");
|
let tracer: global::BoxedTracer = global::tracer("tracer");
|
||||||
tracer
|
tracer
|
||||||
.in_span("Checking for Updates", |cx| updator::update())
|
.in_span("Checking for Updates", |_cx| updator::update())
|
||||||
.await;
|
.await;
|
||||||
println!("Please paste in your bot token");
|
let service_name = "RhythmWorks";
|
||||||
let mut input = String::new();
|
let username = env::var("USER").or_else(|_| env::var("USERNAME")).unwrap();
|
||||||
io::stdin()
|
loop {
|
||||||
.read_line(&mut input)
|
let token = credentialmanager::get_token(service_name, &username);
|
||||||
.expect("Failed to read line");
|
let options: Vec<&'static str> = vec!["- Start Bot", "- Change Token", "- Delete Token"];
|
||||||
let token = input.trim();
|
let selection = Select::with_theme(&ColorfulTheme::default())
|
||||||
let intents = GatewayIntents::GUILD_MESSAGES
|
.with_prompt("Choose an Option")
|
||||||
| GatewayIntents::GUILDS
|
.default(0)
|
||||||
| GatewayIntents::DIRECT_MESSAGES
|
.items(&options)
|
||||||
| GatewayIntents::MESSAGE_CONTENT
|
.interact()
|
||||||
| GatewayIntents::GUILD_VOICE_STATES;
|
.unwrap();
|
||||||
let mut client = Client::builder(&token, intents)
|
match selection {
|
||||||
.event_handler(Handler)
|
0 => {
|
||||||
.register_songbird()
|
let intents = GatewayIntents::GUILD_MESSAGES
|
||||||
.await
|
| GatewayIntents::GUILDS
|
||||||
.expect("Err creating client");
|
| GatewayIntents::DIRECT_MESSAGES
|
||||||
if let Err(why) = client.start().await {
|
| GatewayIntents::MESSAGE_CONTENT
|
||||||
println!("Client error: {why:?}");
|
| 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:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
credentialmanager::change_token(service_name, &username);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
credentialmanager::remove_token(service_name, &username);
|
||||||
|
}
|
||||||
|
_ => println!("Invalid Option"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
use dialoguer::{Input, theme::ColorfulTheme};
|
||||||
|
use keyring::Entry;
|
||||||
|
|
||||||
|
pub fn get_token<'a>(service: &str, username: &str) -> String {
|
||||||
|
let entry = Entry::new(service, username).expect("Failed to get keyring entry");
|
||||||
|
match entry.get_password() {
|
||||||
|
Ok(token) => token,
|
||||||
|
Err(_) => {
|
||||||
|
let token: String = Input::with_theme(&ColorfulTheme::default())
|
||||||
|
.with_prompt("Enter your bot token")
|
||||||
|
.interact_text()
|
||||||
|
.unwrap();
|
||||||
|
match entry.set_password(&token) {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to set token: {}", e);
|
||||||
|
std::process::abort()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn change_token(service: &str, username: &str) {
|
||||||
|
let entry = Entry::new(service, username).expect("Failed to get keyring entry");
|
||||||
|
let new_token: String = Input::with_theme(&ColorfulTheme::default())
|
||||||
|
.with_prompt("New Token")
|
||||||
|
.interact_text()
|
||||||
|
.unwrap();
|
||||||
|
let _ = entry.set_password(&new_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_token(service: &str, username: &str) {
|
||||||
|
let entry = Entry::new(service, username).expect("Failed to get keyring entry");
|
||||||
|
let _ = entry.delete_credential();
|
||||||
|
}
|
||||||
+2
-1
@@ -1,2 +1,3 @@
|
|||||||
pub mod updator;
|
pub mod credentialmanager;
|
||||||
pub mod telemetry;
|
pub mod telemetry;
|
||||||
|
pub mod updator;
|
||||||
|
|||||||
@@ -25,6 +25,5 @@ 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 {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user