aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNathan Perry <np@nathanperry.dev>2024-08-16 22:35:46 -0400
committerNathan Perry <np@nathanperry.dev>2024-08-16 22:35:47 -0400
commite0a9b18f45858829f88cbe20611aaf696fc5bf6a (patch)
treebaef5abb258a7857fabb3f084627acd9a469677f
parentef056edf92b678265a4666e1f9405e3b0ce66a42 (diff)
restore support for volume commands
-rw-r--r--Cargo.lock25
-rw-r--r--Cargo.toml3
-rw-r--r--src/bot.rs11
-rw-r--r--src/commands/game.rs14
-rw-r--r--src/commands/meme/history.rs33
-rw-r--r--src/commands/meme/invoke.rs23
-rw-r--r--src/commands/meme/mod.rs16
-rw-r--r--src/commands/mod.rs10
-rw-r--r--src/commands/playback.rs12
-rw-r--r--src/commands/sound_levels.rs71
-rw-r--r--src/commands/today/mod.rs7
-rw-r--r--src/config.rs6
-rw-r--r--src/db/manual_migrate.rs14
-rw-r--r--src/db/mod.rs89
-rw-r--r--src/db/models.rs1
-rw-r--r--src/util/mod.rs31
16 files changed, 228 insertions, 138 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 48bce4a..0373af6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -505,6 +505,20 @@ dependencies = [
]
[[package]]
+name = "dashmap"
+version = "6.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28"
+dependencies = [
+ "cfg-if",
+ "crossbeam-utils",
+ "hashbrown",
+ "lock_api",
+ "once_cell",
+ "parking_lot_core",
+]
+
+[[package]]
name = "data-encoding"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1335,7 +1349,7 @@ checksum = "c325dfab65f261f386debee8b0969da215b3fa0037e74c8a1234db7ba986d803"
dependencies = [
"crossbeam-channel",
"crossbeam-utils",
- "dashmap",
+ "dashmap 5.5.3",
"skeptic",
"smallvec",
"tagptr",
@@ -2433,7 +2447,7 @@ dependencies = [
"bytes",
"chrono",
"command_attr",
- "dashmap",
+ "dashmap 5.5.3",
"flate2",
"futures",
"fxhash",
@@ -2579,7 +2593,7 @@ dependencies = [
"audiopus",
"byteorder",
"crypto_secretbox",
- "dashmap",
+ "dashmap 5.5.3",
"derivative",
"discortp",
"flume",
@@ -2987,12 +3001,13 @@ dependencies = [
[[package]]
name = "thulani"
-version = "0.3.1"
+version = "0.4.0"
dependencies = [
"anyhow",
"bytemuck",
"chrono",
"clap",
+ "dashmap 6.0.1",
"deadpool-postgres",
"diesel",
"diesel-async",
@@ -3446,7 +3461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb704842c709bc76f63e99e704cb208beeccca2abbabd0d9aec02e48ca1cee0f"
dependencies = [
"chrono",
- "dashmap",
+ "dashmap 5.5.3",
"hashbrown",
"mini-moka",
"parking_lot",
diff --git a/Cargo.toml b/Cargo.toml
index face9a2..5ee41b1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,7 @@ edition = "2021"
[package]
name = "thulani"
-version = "0.3.1"
+version = "0.4.0"
edition.workspace = true
authors.workspace = true
default-run = "thulani"
@@ -55,6 +55,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
timeago = "0.4"
fnv = "1.0"
+dashmap = "6.0"
clap = { version = "4.5", features = ["derive"] }
diff --git a/src/bot.rs b/src/bot.rs
index 7d85c89..d9047e6 100644
--- a/src/bot.rs
+++ b/src/bot.rs
@@ -9,6 +9,7 @@ use std::{
};
use chrono::Datelike;
+use dashmap::DashMap;
use fnv::{
FnvHashMap,
FnvHashSet,
@@ -24,7 +25,6 @@ use serenity::{
all::{
Guild,
GuildId,
- PartialGuild,
ReactionType,
},
builder::CreateMessage,
@@ -63,6 +63,12 @@ impl TypeMapKey for HttpKey {
type Value = reqwest::Client;
}
+pub struct VolumeKey;
+
+impl TypeMapKey for VolumeKey {
+ type Value = DashMap<GuildId, f64>;
+}
+
#[cfg(debug_assertions)]
const BOTNAME: &str = "thulani (dev)";
@@ -96,7 +102,7 @@ impl EventHandler for Handler {
}
async fn guild_create(&self, ctx: Context, guild: Guild, _is_new: Option<bool>) {
- tracing::info!(guild_id = %guild.id, guild_name = %guild.name, "received guild_create");
+ tracing::info!(disc_event = "guild_create", guild_id = %guild.id, guild_name = %guild.name);
ready_guild(&ctx, guild.id).await;
}
@@ -409,6 +415,7 @@ pub async fn run() -> anyhow::Result<()> {
.event_handler(Handler)
.register_songbird_from_config(sb_config)
.type_map_insert::<HttpKey>(reqwest::Client::new())
+ .type_map_insert::<VolumeKey>(DashMap::new())
.framework(framework().await)
.await?;
diff --git a/src/commands/game.rs b/src/commands/game.rs
index 78c08ee..3a47b32 100644
--- a/src/commands/game.rs
+++ b/src/commands/game.rs
@@ -307,14 +307,6 @@ async fn _game(
users
};
- let inferred = users.is_empty();
-
- if inferred && users.len() < 2 || !inferred && users.is_empty() {
- tracing::info!("too few known users to make game comparison");
- util::reply(ctx, "yer too lonely").await?;
- return Ok(());
- }
-
let client = {
let data = ctx.serenity_context().data.read().await;
data.get::<HttpKey>().unwrap().clone()
@@ -333,6 +325,12 @@ async fn _game(
})
.collect::<FnvHashMap<_, _>>();
+ if user_indexes.len() < 2 {
+ tracing::info!("too few known users to make game comparison");
+ util::reply(ctx, "yer too lonely").await?;
+ return Ok(());
+ }
+
let data_ref = &data;
let user_games = user_indexes
.iter()
diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs
index 1acb019..270ae9a 100644
--- a/src/commands/meme/history.rs
+++ b/src/commands/meme/history.rs
@@ -1,16 +1,3 @@
-use crate::{
- commands::game::get_user_id,
- db::{
- self,
- connection,
- InvocationRecord,
- Meme,
- Metadata,
- },
- util,
- PoiseContext,
- CONFIG,
-};
use chrono::TimeZone;
use diesel::{
result::Error as DieselError,
@@ -34,7 +21,20 @@ use timeago::{
Formatter,
TimeUnit,
};
-use windows::core::s;
+
+use crate::{
+ commands::game::get_user_id,
+ config::CONFIG,
+ db::{
+ self,
+ connection,
+ InvocationRecord,
+ Meme,
+ Metadata,
+ },
+ util,
+ PoiseContext,
+};
lazy_static! {
static ref TIME_FORMATTER: Formatter = {
@@ -393,10 +393,7 @@ pub async fn query(ctx: PoiseContext<'_>, rest: util::RestVec) -> anyhow::Result
use regex::Regex;
use serenity::model::id::UserId;
- use crate::{
- db,
- CONFIG,
- };
+ use crate::db;
lazy_static! {
static ref CREATOR_REGEX: Regex = Regex::new(r"(?i)(?:by|creator)=(.*)").unwrap();
diff --git a/src/commands/meme/invoke.rs b/src/commands/meme/invoke.rs
index aff5c23..4b361a3 100644
--- a/src/commands/meme/invoke.rs
+++ b/src/commands/meme/invoke.rs
@@ -1,6 +1,5 @@
use diesel::{
result::Error as DieselError,
- row::NamedRow,
NotFound,
};
use grate::tracing;
@@ -93,10 +92,10 @@ pub(crate) async fn _meme(
x.id,
false,
)
- .await?;
+ .await?;
x
- }
+ },
Err(e) => {
return if let Some(NotFound) = e.downcast_ref::<DieselError>() {
tracing::info!("requested meme not found in database");
@@ -107,7 +106,7 @@ pub(crate) async fn _meme(
util::reply(ctx, "what in ryan's name").await?;
Err(e.into())
};
- }
+ },
};
send_meme(ctx, &mem, &mut conn).await
@@ -135,15 +134,15 @@ async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyh
mem.id,
true,
)
- .await?;
+ .await?;
send_meme(ctx, &mem, &mut conn).await?;
Ok(())
- }
+ },
Ok(None) => {
tracing::info!("random meme not found");
util::reply(ctx, "i don't know any :(").await?;
Ok(())
- }
+ },
Err(e) => {
if let Some(NotFound) = e.downcast_ref::<DieselError>() {
tracing::info!("random meme not found");
@@ -154,7 +153,7 @@ async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyh
util::reply(ctx, "HELP").await?;
Err(e.into())
- }
+ },
}
}
@@ -177,16 +176,16 @@ pub async fn rare_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
meme.id,
true,
)
- .await?;
+ .await?;
send_meme(ctx, &meme, &mut conn).await
- }
+ },
Ok(None) => {
tracing::info!("rare meme not found");
util::reply(ctx, "i don't know any :(").await?;
Ok(())
- }
+ },
Err(e) => {
if let Some(NotFound) = e.downcast_ref::<DieselError>() {
@@ -199,6 +198,6 @@ pub async fn rare_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
util::reply(ctx, "THE MEME MARKET IS IN FREEFALL").await?;
Err(e.into())
- }
+ },
}
}
diff --git a/src/commands/meme/mod.rs b/src/commands/meme/mod.rs
index eb6aa1d..3883f3e 100644
--- a/src/commands/meme/mod.rs
+++ b/src/commands/meme/mod.rs
@@ -1,3 +1,8 @@
+use std::{
+ borrow::ToOwned,
+ default::Default,
+};
+
use diesel_async::AsyncPgConnection;
use grate::tracing;
use rand::random;
@@ -19,10 +24,6 @@ use songbird::input::{
Compose,
Input,
};
-use std::{
- borrow::ToOwned,
- default::Default,
-};
pub use self::{
create::*,
@@ -38,7 +39,6 @@ use crate::{
},
util,
PoiseContext,
- CONFIG,
};
mod create;
@@ -113,6 +113,9 @@ async fn send_meme(
return Ok(());
};
+ let volume = util::volume(ctx).await;
+ tracing::debug!(volume);
+
{
let (_sb, call) = songbird(ctx).await?;
let mut call = call.lock().await;
@@ -121,7 +124,8 @@ async fn send_meme(
call.join(voice_channel).await?;
}
- call.enqueue_input(Input::Lazy(Box::new(audio))).await;
+ let handle = call.enqueue_input(Input::Lazy(Box::new(audio))).await;
+ handle.set_volume(volume as _)?;
}
util::react(ctx, ReactionType::Unicode("📣".to_owned())).await?;
diff --git a/src/commands/mod.rs b/src/commands/mod.rs
index 2729580..b0ef83b 100644
--- a/src/commands/mod.rs
+++ b/src/commands/mod.rs
@@ -19,8 +19,14 @@ pub(crate) mod today;
pub use self::meme::*;
pub fn commands() -> Vec<poise::Command<crate::PoiseData, anyhow::Error>> {
- let mut commands =
- vec![sound_levels::mute(), sound_levels::unmute(), roll::roll(), today::today(), help()];
+ let mut commands = vec![
+ sound_levels::mute(),
+ sound_levels::unmute(),
+ sound_levels::volume(),
+ roll::roll(),
+ today::today(),
+ help(),
+ ];
commands.extend(playback::commands());
diff --git a/src/commands/playback.rs b/src/commands/playback.rs
index 8121136..4d6d0be 100644
--- a/src/commands/playback.rs
+++ b/src/commands/playback.rs
@@ -14,7 +14,6 @@ use crate::{
util,
PoiseContext,
PoiseData,
- CONFIG,
};
pub fn commands() -> impl IntoIterator<Item = poise::Command<PoiseData, anyhow::Error>> {
@@ -74,6 +73,9 @@ pub async fn _play(ctx: PoiseContext<'_>, url: &url::Url) -> anyhow::Result<()>
data.get::<HttpKey>().unwrap().clone()
};
+ let volume = util::volume(ctx).await;
+ tracing::debug!(volume);
+
{
let (_sb, call) = songbird(ctx).await?;
let mut call = call.lock().await;
@@ -86,9 +88,11 @@ pub async fn _play(ctx: PoiseContext<'_>, url: &url::Url) -> anyhow::Result<()>
YoutubeDl::new_ytdl_like(&crate::config::YTDL_COMMAND, client.clone(), url.to_string());
let track = input.conv::<songbird::tracks::Track>();
+
// TODO: store enqueueing channel so songbird handler can switch channels
- call.enqueue(track).await;
+ let queued = call.enqueue(track).await;
+ queued.set_volume(volume as _)?;
}
util::react(ctx, '📣').await?;
@@ -197,7 +201,9 @@ pub async fn list(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
let call = call.lock().await;
let queue = call.queue();
- util::reply(ctx, "(command fix work-in-progress)").await?;
+ if queue.current_queue().is_empty() {
+ util::reply(ctx, "nothing queued").await?;
+ }
for track in queue.current_queue().into_iter() {
let info = track.get_info().await?;
diff --git a/src/commands/sound_levels.rs b/src/commands/sound_levels.rs
index 4946f47..2b9048e 100644
--- a/src/commands/sound_levels.rs
+++ b/src/commands/sound_levels.rs
@@ -1,10 +1,18 @@
+use std::error::Error;
+
+use serenity::all::{
+ Context,
+ Message,
+};
+
use crate::{
commands::playback::songbird,
+ util,
PoiseContext,
};
/// Mute audio (don't pause).
-#[poise::command(prefix_command, guild_only)]
+#[poise::command(prefix_command, guild_only, category = "playback")]
pub async fn mute(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
let (_sb, call) = songbird(ctx).await?;
@@ -15,7 +23,7 @@ pub async fn mute(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
}
/// Unmute audio.
-#[poise::command(prefix_command, guild_only)]
+#[poise::command(prefix_command, guild_only, category = "playback")]
pub async fn unmute(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
let (_sb, call) = songbird(ctx).await?;
@@ -24,3 +32,62 @@ pub async fn unmute(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
Ok(())
}
+
+#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
+struct PercentArg(f64);
+
+lazy_static::lazy_static! {
+ static ref ARG_PCT: regex::Regex = regex::Regex::new(r#"(\d+(?:\.\d+)?)\s*%?(.*)"#).unwrap();
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, thiserror::Error)]
+#[error("expected a number with an optional trailing %")]
+struct NonPctError;
+
+#[poise::async_trait]
+impl<'a> poise::PopArgument<'a> for PercentArg {
+ async fn pop_from(
+ args: &'a str,
+ attachment_index: usize,
+ _ctx: &Context,
+ _msg: &Message,
+ ) -> Result<(&'a str, usize, Self), (Box<dyn Error + Send + Sync>, Option<String>)> {
+ let Some(mtch) = ARG_PCT.captures(args) else {
+ return Err((Box::new(NonPctError), None));
+ };
+
+ let pct = mtch.get(1).unwrap().as_str().parse::<f64>().unwrap();
+ let prop = pct / 100.;
+ let rest = mtch.get(2).unwrap().as_str();
+
+ Ok((rest, attachment_index, Self(prop)))
+ }
+}
+
+/// Set volume by percent.
+#[poise::command(prefix_command, guild_only, category = "playback")]
+pub async fn volume(ctx: PoiseContext<'_>, volume: Option<PercentArg>) -> anyhow::Result<()> {
+ let Some(volume) = volume else {
+ let cur_vol = util::volume(ctx).await * 100.;
+
+ util::reply(ctx, format!("{cur_vol:.0}%")).await?;
+ return Ok(());
+ };
+
+ {
+ let data = ctx.serenity_context().data.read().await;
+ let vol = data.get::<crate::bot::VolumeKey>().unwrap();
+ vol.insert(util::guild_id(ctx)?, volume.0);
+ }
+
+ let (_sb, call) = songbird(ctx).await?;
+ let call = call.lock().await;
+
+ call.queue().modify_queue(|q| {
+ for elt in q {
+ elt.set_volume(volume.0 as _)?;
+ }
+
+ anyhow::Ok(())
+ })
+}
diff --git a/src/commands/today/mod.rs b/src/commands/today/mod.rs
index e48983c..f8f38c7 100644
--- a/src/commands/today/mod.rs
+++ b/src/commands/today/mod.rs
@@ -13,7 +13,6 @@ use crate::{
commands::playback::songbird,
util,
PoiseContext,
- CONFIG,
};
mod prelude;
@@ -103,6 +102,9 @@ pub async fn today(ctx: PoiseContext<'_>, #[rest] _rest: Option<String>) -> anyh
return Ok(());
};
+ let volume = util::volume(ctx).await;
+ tracing::debug!(volume);
+
let (_sb, call) = songbird(ctx).await?;
let mut call = call.lock().await;
@@ -118,7 +120,8 @@ pub async fn today(ctx: PoiseContext<'_>, #[rest] _rest: Option<String>) -> anyh
let input =
YoutubeDl::new_ytdl_like("yt-dlp", client.clone(), play_args.url.conv::<String>());
- call.enqueue_input(input.into()).await;
+ let handle = call.enqueue_input(input.into()).await;
+ handle.set_volume(volume as _)?;
let q = call.queue();
q.pause()?;
diff --git a/src/config.rs b/src/config.rs
index fae5a23..ea75cef 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -4,11 +4,7 @@ use dotenv::dotenv;
use envconfig::Envconfig;
use grate::tracing;
use lazy_static::lazy_static;
-use serenity::model::id::{
- ChannelId,
- GuildId,
- UserId,
-};
+use serenity::model::id::UserId;
lazy_static! {
pub static ref CONFIG: Config = {
diff --git a/src/db/manual_migrate.rs b/src/db/manual_migrate.rs
index 851f0d8..b3e8a9c 100644
--- a/src/db/manual_migrate.rs
+++ b/src/db/manual_migrate.rs
@@ -1,17 +1,5 @@
-use crate::{
- db::{
- do_migrate,
- schema::*,
- POOL,
- },
- guild_id,
-};
-use anyhow::anyhow;
use diesel::{
- associations::HasTable,
- backend::Backend,
pg::Pg,
- query_builder::AsQuery,
ExpressionMethods,
};
use diesel_async::{
@@ -21,6 +9,8 @@ use diesel_async::{
RunQueryDsl,
};
+use crate::db::schema::*;
+
#[inline]
pub async fn connection_no_migrate()
-> anyhow::Result<diesel_async::pooled_connection::deadpool::Object<AsyncPgConnection>> {
diff --git a/src/db/mod.rs b/src/db/mod.rs
index 5f19d54..3362e33 100644
--- a/src/db/mod.rs
+++ b/src/db/mod.rs
@@ -1,6 +1,12 @@
-pub use self::models::*;
-use self::schema::*;
-use crate::db::schema::memes::title;
+use std::{
+ convert::{
+ AsRef,
+ From,
+ },
+ env,
+ str::FromStr,
+};
+
use anyhow::{
anyhow,
Error,
@@ -19,7 +25,6 @@ use deadpool_postgres::{
};
use diesel::{
prelude::*,
- row::NamedRow,
BoolExpressionMethods,
ExpressionMethods,
NotFound,
@@ -37,22 +42,18 @@ use diesel_async::{
RunQueryDsl,
};
use grate::tracing;
-use rand::Rng;
use serenity::FutureExt;
-use std::{
- convert::{
- AsRef,
- From,
- },
- env,
- str::FromStr,
-};
use tokio_postgres::types::FromSql;
+use self::schema::*;
+
pub mod manual_migrate;
+
mod models;
mod schema;
+pub use self::models::*;
+
static MIGRATIONS: diesel_async_migrations::EmbeddedMigrations =
diesel_async_migrations::embed_migrations!();
@@ -82,7 +83,7 @@ lazy_static::lazy_static! {
#[inline]
pub async fn connection()
- -> Result<diesel_async::pooled_connection::deadpool::Object<AsyncPgConnection>> {
+-> Result<diesel_async::pooled_connection::deadpool::Object<AsyncPgConnection>> {
POOL.get()
.then(|mut conn| async move {
if let Ok(ref mut conn) = conn {
@@ -194,8 +195,8 @@ pub async fn query_meme<T: AsRef<str>>(
};
let metadata = Metadata {
- id: row.get(5),
- created: row.get(6),
+ id: row.get(5),
+ created: row.get(6),
created_by: row.get(7),
};
@@ -294,19 +295,19 @@ pub async fn delete_meme<T: AsRef<str>>(
}
let tombstone = NewTombstone {
- guild: guild as i64,
- deleted_by: deleted_by as i64,
+ guild: guild as i64,
+ deleted_by: deleted_by as i64,
metadata_id: deleted.metadata_id,
- meme_id: deleted.id,
+ meme_id: deleted.id,
};
let _ = diesel::insert_into(tombstones::table).values(&tombstone).execute(tx).await?;
Ok(())
}
- .scope_boxed()
+ .scope_boxed()
})
- .await
+ .await
}
pub async fn rare_meme(
@@ -472,32 +473,32 @@ pub async fn rand_silent_meme(conn: &mut AsyncPgConnection, guild: u64) -> Resul
#[derive(Debug, Clone)]
pub struct Stats {
- pub memes_overall: usize,
- pub audio_memes: usize,
- pub image_memes: usize,
- pub started_recording: Option<DateTime<Utc>>,
- pub total_meme_invocations: usize,
- pub audio_meme_invocations: usize,
+ pub memes_overall: usize,
+ pub audio_memes: usize,
+ pub image_memes: usize,
+ pub started_recording: Option<DateTime<Utc>>,
+ pub total_meme_invocations: usize,
+ pub audio_meme_invocations: usize,
pub random_meme_invocations: usize,
- pub most_active_day: Option<NaiveDate>,
+ pub most_active_day: Option<NaiveDate>,
pub most_active_day_count: usize,
- pub most_audio_active_day: Option<NaiveDate>,
+ pub most_audio_active_day: Option<NaiveDate>,
pub most_audio_active_count: usize,
- pub most_random_meme_user: Option<u64>,
- pub most_random_meme_user_count: usize,
- pub most_directly_named_meme_user: Option<u64>,
+ pub most_random_meme_user: Option<u64>,
+ pub most_random_meme_user_count: usize,
+ pub most_directly_named_meme_user: Option<u64>,
pub most_directly_named_meme_count: usize,
- pub most_popular_named_meme: Option<String>,
+ pub most_popular_named_meme: Option<String>,
pub most_popular_named_meme_count: usize,
- pub most_popular_random_meme: Option<String>,
+ pub most_popular_random_meme: Option<String>,
pub most_popular_random_meme_count: usize,
- pub most_popular_meme_overall: Option<String>,
+ pub most_popular_meme_overall: Option<String>,
pub most_popular_meme_overall_count: usize,
}
@@ -579,7 +580,7 @@ pub async fn stats(conn: &mut AsyncPgConnection, guild: u64) -> Result<Stats> {
#[inline]
fn option_first_count<T>(rows: Vec<tokio_postgres::Row>) -> (Option<T>, i64)
where
- for<'a> T: FromSql<'a>,
+ for<'a> T: FromSql<'a>,
{
let Some(row) = <[_]>::first(&rows) else {
return (None, 0);
@@ -730,10 +731,10 @@ pub async fn stats(conn: &mut AsyncPgConnection, guild: u64) -> Result<Stats> {
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
pub struct MemerInfo {
- pub user_id: u64,
- pub random_memes: usize,
- pub specific_memes: usize,
- pub most_used_meme: String,
+ pub user_id: u64,
+ pub random_memes: usize,
+ pub specific_memes: usize,
+ pub most_used_meme: String,
pub most_used_meme_count: usize,
}
@@ -790,10 +791,10 @@ pub async fn memers(guild: u64) -> Result<Vec<MemerInfo>> {
let most_memed_count: i64 = row.get(4);
MemerInfo {
- user_id: user_id as u64,
- random_memes: random_count as usize,
- specific_memes: specific_count as usize,
- most_used_meme: most_memed_meme,
+ user_id: user_id as u64,
+ random_memes: random_count as usize,
+ specific_memes: specific_count as usize,
+ most_used_meme: most_memed_meme,
most_used_meme_count: most_memed_count as usize,
}
})
diff --git a/src/db/models.rs b/src/db/models.rs
index 33fa34e..e699630 100644
--- a/src/db/models.rs
+++ b/src/db/models.rs
@@ -4,7 +4,6 @@ use anyhow::{
};
use chrono::naive::NaiveDateTime;
use diesel::{
- associations::HasTable,
prelude::*,
Identifiable,
Insertable,
diff --git a/src/util/mod.rs b/src/util/mod.rs
index 38f4e55..474d36d 100644
--- a/src/util/mod.rs
+++ b/src/util/mod.rs
@@ -1,19 +1,9 @@
use std::{
- cmp::max_by,
collections::HashMap,
- process::{
- id,
- Stdio,
- },
+ process::Stdio,
};
-use anyhow::anyhow;
use chrono::Duration;
-use diesel::row::NamedRow;
-use fnv::{
- FnvHashMap,
- FnvHashSet,
-};
use grate::tracing;
use lazy_static::lazy_static;
use poise::{
@@ -26,7 +16,6 @@ use regex::{
};
use serenity::{
all::{
- ChannelType,
CreateMessage,
GuildId,
GuildRef,
@@ -83,9 +72,9 @@ pub async fn users_listening(ctx: PoiseContext<'_>) -> anyhow::Result<bool> {
pub fn msg<U, E>(ctx: poise::Context<'_, U, E>) -> Option<&Message> {
match ctx {
poise::Context::Prefix(poise::PrefixContext {
- msg,
- ..
- }) => Some(msg),
+ msg,
+ ..
+ }) => Some(msg),
_ => None,
}
}
@@ -126,6 +115,18 @@ pub fn unwrap_tts<U, E>(ctx: poise::Context<'_, U, E>) -> bool {
}
#[inline]
+pub async fn volume(ctx: PoiseContext<'_>) -> f64 {
+ let Some(guild_id) = ctx.guild_id() else {
+ return 1.;
+ };
+
+ let data = ctx.serenity_context().data.read().await;
+ let vol = data.get::<crate::bot::VolumeKey>().unwrap();
+
+ vol.get(&guild_id).map(|g| *g).unwrap_or(1.)
+}
+
+#[inline]
pub async fn send(
ctx: &Context,
channel: ChannelId,