Compare commits
4 Commits
jude/index
...
bec92177cb
Author | SHA1 | Date | |
---|---|---|---|
bec92177cb | |||
cee578eaf1 | |||
6615e05196 | |||
6cfdc10a6a |
116
Cargo.lock
generated
116
Cargo.lock
generated
@ -139,6 +139,55 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum"
|
||||||
|
version = "0.6.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"axum-core",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"itoa",
|
||||||
|
"matchit",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"percent-encoding",
|
||||||
|
"pin-project-lite",
|
||||||
|
"rustversion",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_path_to_error",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"sync_wrapper",
|
||||||
|
"tokio",
|
||||||
|
"tower",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-core"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"mime",
|
||||||
|
"rustversion",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.68"
|
version = "0.3.68"
|
||||||
@ -1080,6 +1129,12 @@ dependencies = [
|
|||||||
"regex-automata 0.1.10",
|
"regex-automata 0.1.10",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matchit"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
@ -1549,6 +1604,27 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "prometheus"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "449811d15fbdf5ceb5c1144416066429cf82316e2ec8ce0c1f6f8a02e7bbcf8c"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"fnv",
|
||||||
|
"lazy_static",
|
||||||
|
"memchr",
|
||||||
|
"parking_lot 0.12.1",
|
||||||
|
"protobuf",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "protobuf"
|
||||||
|
version = "2.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.29"
|
version = "1.0.29"
|
||||||
@ -1946,6 +2022,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_path_to_error"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4beec8bce849d58d06238cb50db2e1c417cfeafa4c63f692b15c82b7c80f8335"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_repr"
|
name = "serde_repr"
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
@ -2131,12 +2217,14 @@ dependencies = [
|
|||||||
name = "soundfx-rs"
|
name = "soundfx-rs"
|
||||||
version = "1.5.11"
|
version = "1.5.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"axum",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"poise",
|
"poise",
|
||||||
|
"prometheus",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2337,6 +2425,12 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sync_wrapper"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.6.0"
|
version = "3.6.0"
|
||||||
@ -2529,6 +2623,28 @@ dependencies = [
|
|||||||
"tracing",
|
"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",
|
||||||
|
"pin-project",
|
||||||
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
|
"tower-layer",
|
||||||
|
"tower-service",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tower-layer"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower-service"
|
name = "tower-service"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
|
@ -20,6 +20,11 @@ serde_json = "1.0"
|
|||||||
dashmap = "5.3"
|
dashmap = "5.3"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
prometheus = { version = "0.13.3", optional = true }
|
||||||
|
axum = { version = "0.6.20", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
metrics = ["dep:prometheus", "dep:axum"]
|
||||||
|
|
||||||
[patch."https://github.com/serenity-rs/serenity"]
|
[patch."https://github.com/serenity-rs/serenity"]
|
||||||
serenity = { version = "0.11.6" }
|
serenity = { version = "0.11.6" }
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use poise::serenity_prelude::{
|
use poise::serenity_prelude::{
|
||||||
builder::CreateActionRow, model::application::component::ButtonStyle, GuildChannel,
|
builder::CreateActionRow, model::application::component::ButtonStyle, GuildChannel,
|
||||||
ReactionType,
|
ReactionType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "metrics")]
|
||||||
|
use crate::metrics::PLAY_COUNTER;
|
||||||
use crate::{
|
use crate::{
|
||||||
cmds::autocomplete_sound,
|
cmds::autocomplete_sound,
|
||||||
models::{guild_data::CtxGuildData, sound::SoundCtx},
|
models::{guild_data::CtxGuildData, sound::SoundCtx},
|
||||||
utils::{join_channel, play_from_query, queue_audio},
|
utils::{join_channel, play_audio, play_from_query, queue_audio},
|
||||||
Context, Error,
|
Context, Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,6 +29,9 @@ pub async fn play(
|
|||||||
|
|
||||||
let guild = ctx.guild().unwrap();
|
let guild = ctx.guild().unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "metrics")]
|
||||||
|
PLAY_COUNTER.inc();
|
||||||
|
|
||||||
ctx.say(
|
ctx.say(
|
||||||
play_from_query(
|
play_from_query(
|
||||||
&ctx.serenity_context(),
|
&ctx.serenity_context(),
|
||||||
@ -42,6 +49,70 @@ pub async fn play(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Play a random sound from this server
|
||||||
|
#[poise::command(
|
||||||
|
slash_command,
|
||||||
|
rename = "random",
|
||||||
|
default_member_permissions = "SPEAK",
|
||||||
|
guild_only = true
|
||||||
|
)]
|
||||||
|
pub async fn play_random(
|
||||||
|
ctx: Context<'_>,
|
||||||
|
#[description = "Channel to play in (default: your current voice channel)"]
|
||||||
|
#[channel_types("Voice")]
|
||||||
|
channel: Option<GuildChannel>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
ctx.defer().await?;
|
||||||
|
|
||||||
|
let guild = ctx.guild().unwrap();
|
||||||
|
let channel_to_join = channel.map(|c| c.id).or_else(|| {
|
||||||
|
guild
|
||||||
|
.voice_states
|
||||||
|
.get(&ctx.author().id)
|
||||||
|
.and_then(|voice_state| voice_state.channel_id)
|
||||||
|
});
|
||||||
|
|
||||||
|
match channel_to_join {
|
||||||
|
Some(channel) => {
|
||||||
|
let (call_handler, _) =
|
||||||
|
join_channel(ctx.serenity_context(), guild.clone(), channel).await;
|
||||||
|
|
||||||
|
let sounds = ctx.data().guild_sounds(guild.id, None).await?;
|
||||||
|
|
||||||
|
let ts = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
|
||||||
|
|
||||||
|
// This is far cheaper and easier than using an RNG. No reason to use a full RNG here
|
||||||
|
// anyway.
|
||||||
|
match sounds.get(ts.subsec_micros() as usize % sounds.len()) {
|
||||||
|
Some(sound) => {
|
||||||
|
let guild_data = ctx.data().guild_data(guild.id).await.unwrap();
|
||||||
|
let mut lock = call_handler.lock().await;
|
||||||
|
|
||||||
|
play_audio(
|
||||||
|
sound,
|
||||||
|
guild_data.read().await.volume,
|
||||||
|
&mut lock,
|
||||||
|
&ctx.data().database,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
ctx.say("No sounds in this server!").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None => {
|
||||||
|
ctx.say("You are not in a voice chat!").await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Play up to 25 sounds on queue
|
/// Play up to 25 sounds on queue
|
||||||
#[poise::command(
|
#[poise::command(
|
||||||
slash_command,
|
slash_command,
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
use std::{collections::HashMap, env};
|
|
||||||
|
|
||||||
use poise::serenity_prelude::{
|
use poise::serenity_prelude::{
|
||||||
model::{
|
model::{
|
||||||
application::interaction::{Interaction, InteractionResponseType},
|
application::interaction::{Interaction, InteractionResponseType},
|
||||||
channel::Channel,
|
channel::Channel,
|
||||||
},
|
},
|
||||||
utils::shard_id,
|
|
||||||
ActionRowComponent, Activity, Context, CreateActionRow, CreateComponents,
|
ActionRowComponent, Activity, Context, CreateActionRow, CreateComponents,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -25,46 +22,6 @@ pub async fn listener(ctx: &Context, event: &poise::Event<'_>, data: &Data) -> R
|
|||||||
poise::Event::Ready { .. } => {
|
poise::Event::Ready { .. } => {
|
||||||
ctx.set_activity(Activity::watching("for /play")).await;
|
ctx.set_activity(Activity::watching("for /play")).await;
|
||||||
}
|
}
|
||||||
poise::Event::GuildCreate { guild, is_new, .. } => {
|
|
||||||
if *is_new {
|
|
||||||
if let Ok(token) = env::var("DISCORDBOTS_TOKEN") {
|
|
||||||
let shard_count = ctx.cache.shard_count();
|
|
||||||
let current_shard_id = shard_id(guild.id.as_u64().to_owned(), shard_count);
|
|
||||||
|
|
||||||
let guild_count = ctx
|
|
||||||
.cache
|
|
||||||
.guilds()
|
|
||||||
.iter()
|
|
||||||
.filter(|g| {
|
|
||||||
shard_id(g.as_u64().to_owned(), shard_count) == current_shard_id
|
|
||||||
})
|
|
||||||
.count() as u64;
|
|
||||||
|
|
||||||
let mut hm = HashMap::new();
|
|
||||||
hm.insert("server_count", guild_count);
|
|
||||||
hm.insert("shard_id", current_shard_id);
|
|
||||||
hm.insert("shard_count", shard_count);
|
|
||||||
|
|
||||||
let response = data
|
|
||||||
.http
|
|
||||||
.post(
|
|
||||||
format!(
|
|
||||||
"https://top.gg/api/bots/{}/stats",
|
|
||||||
ctx.cache.current_user_id().as_u64()
|
|
||||||
)
|
|
||||||
.as_str(),
|
|
||||||
)
|
|
||||||
.header("Authorization", token)
|
|
||||||
.json(&hm)
|
|
||||||
.send()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if let Err(res) = response {
|
|
||||||
println!("DiscordBots Response: {:?}", res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
poise::Event::VoiceStateUpdate { old, new, .. } => {
|
poise::Event::VoiceStateUpdate { old, new, .. } => {
|
||||||
if let Some(past_state) = old {
|
if let Some(past_state) = old {
|
||||||
if let (Some(guild_id), None) = (past_state.guild_id, new.channel_id) {
|
if let (Some(guild_id), None) = (past_state.guild_id, new.channel_id) {
|
||||||
@ -105,10 +62,9 @@ pub async fn listener(ctx: &Context, event: &poise::Event<'_>, data: &Data) -> R
|
|||||||
let mut sound = sqlx::query_as_unchecked!(
|
let mut sound = sqlx::query_as_unchecked!(
|
||||||
Sound,
|
Sound,
|
||||||
"
|
"
|
||||||
SELECT name, id, public, server_id, uploader_id
|
SELECT name, id, public, server_id, uploader_id
|
||||||
FROM sounds
|
FROM sounds
|
||||||
WHERE id = ?
|
WHERE id = ?",
|
||||||
",
|
|
||||||
join_id
|
join_id
|
||||||
)
|
)
|
||||||
.fetch_one(&data.database)
|
.fetch_one(&data.database)
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -5,6 +5,8 @@ mod cmds;
|
|||||||
mod consts;
|
mod consts;
|
||||||
mod error;
|
mod error;
|
||||||
mod event_handlers;
|
mod event_handlers;
|
||||||
|
#[cfg(feature = "metrics")]
|
||||||
|
mod metrics;
|
||||||
mod models;
|
mod models;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
@ -85,6 +87,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
cmds::manage::download_file(),
|
cmds::manage::download_file(),
|
||||||
cmds::manage::delete_sound(),
|
cmds::manage::delete_sound(),
|
||||||
cmds::play::play(),
|
cmds::play::play(),
|
||||||
|
cmds::play::play_random(),
|
||||||
cmds::play::queue_play(),
|
cmds::play::queue_play(),
|
||||||
cmds::play::loop_play(),
|
cmds::play::loop_play(),
|
||||||
cmds::play::soundboard(),
|
cmds::play::soundboard(),
|
||||||
@ -103,7 +106,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
],
|
],
|
||||||
..cmds::favorite::favorites()
|
..cmds::favorite::favorites()
|
||||||
},
|
},
|
||||||
cmds::search::show_random_sounds(),
|
|
||||||
cmds::search::search_sounds(),
|
cmds::search::search_sounds(),
|
||||||
cmds::stop::stop_playing(),
|
cmds::stop::stop_playing(),
|
||||||
cmds::stop::disconnect(),
|
cmds::stop::disconnect(),
|
||||||
@ -142,6 +144,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|||||||
|
|
||||||
sqlx::migrate!().run(&database).await?;
|
sqlx::migrate!().run(&database).await?;
|
||||||
|
|
||||||
|
#[cfg(feature = "metrics")]
|
||||||
|
{
|
||||||
|
metrics::init_metrics();
|
||||||
|
tokio::spawn(async { metrics::serve().await });
|
||||||
|
}
|
||||||
|
|
||||||
poise::Framework::builder()
|
poise::Framework::builder()
|
||||||
.token(discord_token)
|
.token(discord_token)
|
||||||
.setup(move |ctx, _bot, framework| {
|
.setup(move |ctx, _bot, framework| {
|
||||||
|
40
src/metrics.rs
Normal file
40
src/metrics.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use axum::{routing::get, Router};
|
||||||
|
use lazy_static;
|
||||||
|
use log::warn;
|
||||||
|
use prometheus::{register_int_counter, IntCounter, Registry};
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref REGISTRY: Registry = Registry::new();
|
||||||
|
pub static ref PLAY_COUNTER: IntCounter =
|
||||||
|
register_int_counter!("play", "Number of calls to /play").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_metrics() {
|
||||||
|
REGISTRY.register(Box::new(PLAY_COUNTER.clone())).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn serve() {
|
||||||
|
let app = Router::new().route("/", get(metrics));
|
||||||
|
let addr = SocketAddr::from(([127, 0, 0, 1], 31755));
|
||||||
|
|
||||||
|
axum::Server::bind(&addr)
|
||||||
|
.serve(app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn metrics() -> String {
|
||||||
|
let encoder = prometheus::TextEncoder::new();
|
||||||
|
let res_custom = encoder.encode_to_string(®ISTRY.gather());
|
||||||
|
|
||||||
|
match res_custom {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error encoding metrics: {:?}", e);
|
||||||
|
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -106,7 +106,7 @@ pub async fn play_from_query(
|
|||||||
user_id: UserId,
|
user_id: UserId,
|
||||||
channel: Option<ChannelId>,
|
channel: Option<ChannelId>,
|
||||||
query: &str,
|
query: &str,
|
||||||
loop_: bool,
|
r#loop: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
let guild_id = guild.id;
|
let guild_id = guild.id;
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ pub async fn play_from_query(
|
|||||||
guild_data.read().await.volume,
|
guild_data.read().await.volume,
|
||||||
&mut lock,
|
&mut lock,
|
||||||
&data.database,
|
&data.database,
|
||||||
loop_,
|
r#loop,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
Reference in New Issue
Block a user