aboutsummaryrefslogtreecommitdiff
path: root/src/commands
diff options
context:
space:
mode:
Diffstat (limited to 'src/commands')
-rw-r--r--src/commands/game.rs25
-rw-r--r--src/commands/meme/create.rs2
-rw-r--r--src/commands/meme/delete.rs2
-rw-r--r--src/commands/meme/history.rs178
-rw-r--r--src/commands/meme/invoke.rs77
-rw-r--r--src/commands/meme/mod.rs25
-rw-r--r--src/commands/playback.rs61
-rw-r--r--src/commands/today/mod.rs9
8 files changed, 268 insertions, 111 deletions
diff --git a/src/commands/game.rs b/src/commands/game.rs
index 5c51da6..78c08ee 100644
--- a/src/commands/game.rs
+++ b/src/commands/game.rs
@@ -288,19 +288,20 @@ async fn _game(
.filter_map(|(uid, voice)| voice.channel_id.map(|cid| (*uid, cid)))
.collect::<FnvHashMap<_, _>>();
- let channel =
- pairs.get(&ctx.author().id).cloned().unwrap_or(CONFIG.discord.voice_channel());
+ if let Some(channel) = pairs.get(&ctx.author().id).cloned() {
+ tracing::debug!("identified user channel");
- users = pairs
- .iter()
- .filter_map(|(uid, cid)| {
- if *cid == channel {
- DISCORD_MAP.get(uid).map(|s| s.to_lowercase())
- } else {
- None
- }
- })
- .collect::<FnvHashSet<_>>();
+ users = pairs
+ .iter()
+ .filter_map(|(uid, cid)| {
+ if *cid == channel {
+ DISCORD_MAP.get(uid).map(|s| s.to_lowercase())
+ } else {
+ None
+ }
+ })
+ .collect::<FnvHashSet<_>>();
+ }
}
users
diff --git a/src/commands/meme/create.rs b/src/commands/meme/create.rs
index c6ed9c6..2d0ee9a 100644
--- a/src/commands/meme/create.rs
+++ b/src/commands/meme/create.rs
@@ -52,6 +52,7 @@ pub async fn addmeme(
let save_result = NewMeme {
title,
+ guild: util::guild_id(ctx)?.get() as _,
content: text,
image_id,
audio_id: None,
@@ -177,6 +178,7 @@ pub async fn addaudiomeme(
let save_result = NewMeme {
title,
content: text,
+ guild: util::guild_id(ctx)?.get() as _,
image_id,
audio_id: Some(audio_id),
metadata_id: 0,
diff --git a/src/commands/meme/delete.rs b/src/commands/meme/delete.rs
index 8adaa6c..96598d0 100644
--- a/src/commands/meme/delete.rs
+++ b/src/commands/meme/delete.rs
@@ -19,7 +19,7 @@ use crate::{
pub async fn delmeme(ctx: PoiseContext<'_>, title: String) -> anyhow::Result<()> {
let mut conn = connection().await?;
- match delete_meme(&mut conn, &title, ctx.author().id.get()).await {
+ match delete_meme(&mut conn, &title, ctx.author().id.get(), util::guild_id(ctx)?.get()).await {
Ok(_) => {
util::react(ctx, ReactionType::Unicode("💀".to_owned())).await?;
Ok(())
diff --git a/src/commands/meme/history.rs b/src/commands/meme/history.rs
index d6ccdcf..1acb019 100644
--- a/src/commands/meme/history.rs
+++ b/src/commands/meme/history.rs
@@ -1,3 +1,16 @@
+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,
@@ -7,31 +20,21 @@ use grate::tracing;
use itertools::Itertools;
use lazy_static::lazy_static;
use serenity::{
+ all::{
+ GuildId,
+ Mentionable,
+ },
futures::{
StreamExt,
TryStreamExt,
},
- prelude::*,
};
use tap::Pipe;
use timeago::{
Formatter,
TimeUnit,
};
-
-use crate::{
- commands::game::get_user_id,
- db::{
- self,
- connection,
- InvocationRecord,
- Meme,
- Metadata,
- },
- util,
- PoiseContext,
- CONFIG,
-};
+use windows::core::s;
lazy_static! {
static ref TIME_FORMATTER: Formatter = {
@@ -48,9 +51,11 @@ static CLEAN_DATE_FORMAT: &str = "%b %-e %Y";
/// Print info about the last meme.
#[poise::command(prefix_command, guild_only, category = "memes", aliases("what", "hwaet", "hwæt"))]
pub async fn wat(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
+ let guild_id = util::guild_id(ctx)?;
+
let mut conn = connection().await?;
- let record = match InvocationRecord::last(&mut conn).await {
+ let record = match InvocationRecord::last(&mut conn, guild_id.get()).await {
Ok(x) => x,
Err(e) => {
if let Some(NotFound) = e.downcast_ref::<DieselError>() {
@@ -70,7 +75,7 @@ pub async fn wat(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
match meme {
Ok(ref meme) => {
let metadata = Metadata::find(&mut conn, meme.metadata_id).await?;
- let author = CONFIG.discord.guild().member(&ctx, metadata.created_by as u64).await?;
+ let author = guild_id.member(&ctx, metadata.created_by as u64).await?;
util::reply(
ctx,
@@ -116,9 +121,11 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option<usize>) -> anyhow::Result<
let n = n.min(CONFIG.max_hist);
+ let guild_id = util::guild_id(ctx)?;
+
let records = {
let mut conn = connection().await?;
- InvocationRecord::last_n(&mut conn, n).await?
+ InvocationRecord::last_n(&mut conn, n, guild_id.get()).await?
};
if records.is_empty() {
@@ -150,9 +157,7 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option<usize>) -> anyhow::Result<
Err(e) => Err(e),
};
- let invoker_name = CONFIG
- .discord
- .guild()
+ let invoker_name = guild_id
.member(&ctx, rec.user_id as u64)
.await
.map(|m| m.display_name().to_owned())
@@ -160,9 +165,7 @@ pub async fn history(ctx: PoiseContext<'_>, n: Option<usize>) -> anyhow::Result<
let result = match meme {
Ok((metadata, meme)) => {
- let author_name = CONFIG
- .discord
- .guild()
+ let author_name = guild_id
.member(&ctx, metadata.created_by as u64)
.await
.map(|m| m.display_name().to_owned())
@@ -210,17 +213,89 @@ pub async fn stats(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
user::User,
};
+ let guild_id = util::guild_id(ctx)?;
+
let mut conn = connection().await?;
- let stats = db::stats(&mut conn).await?;
+ let stats = db::stats(&mut conn, guild_id.get()).await?;
tracing::debug!("reporting stats");
- let rand_user: User = UserId::new(stats.most_random_meme_user).to_user(&ctx).await?;
- let direct_user: User = UserId::new(stats.most_directly_named_meme_user).to_user(&ctx).await?;
+ async fn username(ctx: PoiseContext<'_>, guild_id: GuildId, id: u64) -> anyhow::Result<String> {
+ let user: User = UserId::new(id).to_user(&ctx).await?;
+ Ok(user.nick_in(&ctx, guild_id).await.unwrap_or(user.name))
+ }
+
+ let most_active_day = if let Some(ref most_active_day) = stats.most_active_day {
+ let fmt = most_active_day.format(CLEAN_DATE_FORMAT);
+ let count = stats.most_active_day_count;
+
+ format!("the most active day was *{fmt}* with **{count}** memes\n")
+ } else {
+ String::new()
+ };
+ let loudest_day = if let Some(ref loudest_day) = stats.most_audio_active_day {
+ let fmt = loudest_day.format(CLEAN_DATE_FORMAT);
+ let count = stats.most_audio_active_count;
+ format!("and the loudest day was *{fmt}* with **{count}** audio memes\n")
+ } else {
+ String::new()
+ };
+
+ let rand_user = if let Some(rand_user) = stats.most_random_meme_user {
+ let rand_user = username(ctx, guild_id, rand_user).await?;
+ format!(
+ "**{rand_user}** has invoked the most random memes ({})\n",
+ stats.most_random_meme_user_count
+ )
+ } else {
+ String::new()
+ };
+
+ let direct_user = if let Some(direct_user) = stats.most_directly_named_meme_user {
+ let direct_user = username(ctx, guild_id, direct_user).await?;
+ format!(
+ "**{direct_user}** has invoked the most memes by name ({})\n",
+ stats.most_directly_named_meme_count
+ )
+ } else {
+ String::new()
+ };
+
+ let started_recording = if let Some(ref started_recording) = stats.started_recording {
+ let fst = started_recording.naive_local().date().format(CLEAN_DATE_FORMAT);
+ let snd = TIME_FORMATTER.convert((chrono::Utc::now() - started_recording).to_std()?);
- let rand_user = rand_user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(rand_user.name);
- let direct_user =
- direct_user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(direct_user.name);
+ format!("started recording meme invocations on *{fst}* ({snd})\n")
+ } else {
+ String::new()
+ };
+
+ let most_requested = if let Some(ref most_requested) = stats.most_popular_named_meme {
+ format!(
+ "*{most_requested}* was the meme specifically requested the most ({})\n",
+ stats.most_popular_named_meme_count
+ )
+ } else {
+ String::new()
+ };
+
+ let most_random = if let Some(ref most_random) = stats.most_popular_random_meme {
+ format!(
+ "*{most_random}* was the meme specifically requested the most ({})\n",
+ stats.most_popular_random_meme_count
+ )
+ } else {
+ String::new()
+ };
+
+ let most_overall = if let Some(ref most_overall) = stats.most_popular_meme_overall {
+ format!(
+ "*{most_overall}* was the most-memed overall({})\n",
+ stats.most_popular_meme_overall_count
+ )
+ } else {
+ String::new()
+ };
let s = format!(
r#"
@@ -228,46 +303,23 @@ pub async fn stats(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
**{}** memes with audio ({:0.1}%)
**{}** memes with images ({:0.1}%)
-started recording meme invocations on *{}* ({})
-**{}** total meme invocations recorded
+{started_recording}**{}** total meme invocations recorded
**{}** of which were random ({:0.1}%)
and **{}** were audio ({:0.1}%)
-the most active day was *{}* with **{}** memes
-and the loudest day was *{}* with **{}** audio memes
-
-**{}** has invoked the most random memes ({})
-**{}** has invoked the most memes by name ({})
-
-*{}* was the meme specifically requested the most ({})
-*{}* was the meme randomly invoked the most ({})
-and *{}* was the most-memed overall ({})"#,
+{most_active_day}{loudest_day}
+{rand_user}{direct_user}
+{most_requested}{most_random}{most_overall}"#,
stats.memes_overall,
stats.audio_memes,
(stats.audio_memes as f64) / (stats.memes_overall as f64) * 100.,
stats.image_memes,
(stats.image_memes as f64) / (stats.memes_overall as f64) * 100.,
- stats.started_recording.naive_local().date().format(CLEAN_DATE_FORMAT),
- TIME_FORMATTER.convert((chrono::Utc::now() - stats.started_recording).to_std().unwrap()),
stats.total_meme_invocations,
stats.random_meme_invocations,
(stats.random_meme_invocations as f64) / (stats.total_meme_invocations as f64) * 100.,
stats.audio_meme_invocations,
(stats.audio_meme_invocations as f64) / (stats.total_meme_invocations as f64) * 100.,
- stats.most_active_day.format(CLEAN_DATE_FORMAT),
- stats.most_active_day_count,
- stats.most_audio_active_day.format(CLEAN_DATE_FORMAT),
- stats.most_audio_active_count,
- rand_user,
- stats.most_random_meme_user_count,
- direct_user,
- stats.most_directly_named_meme_count,
- stats.most_popular_named_meme,
- stats.most_popular_named_meme_count,
- stats.most_popular_random_meme,
- stats.most_popular_random_meme_count,
- stats.most_popular_meme_overall,
- stats.most_popular_meme_overall_count,
);
util::reply(ctx, s).await?;
@@ -279,13 +331,15 @@ and *{}* was the most-memed overall ({})"#,
pub async fn memers(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
use serenity::model::id::UserId;
- let s = db::memers()
+ let guild_id = util::guild_id(ctx)?;
+
+ let s = db::memers(guild_id.get())
.await?
.into_iter()
.pipe(serenity::futures::stream::iter)
.then(|info| async move {
let user = UserId::new(info.user_id).to_user(&ctx).await?;
- let username = user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(user.name);
+ let username = user.nick_in(&ctx, guild_id).await.unwrap_or(user.name);
let res = format!(
"**{}**: {} total, {} random, {} specific. favorite meme: *{}* ({})",
@@ -349,6 +403,8 @@ pub async fn query(ctx: PoiseContext<'_>, rest: util::RestVec) -> anyhow::Result
static ref AGE_REGEX: Regex = Regex::new(r"(?i)(?:age|order)=(.*)").unwrap();
}
+ let guild_id = util::guild_id(ctx)?;
+
let mut rest = rest.into_inner();
let creator: Option<u64> = try {
@@ -373,13 +429,13 @@ pub async fn query(ctx: PoiseContext<'_>, rest: util::RestVec) -> anyhow::Result
let order = order.is_some_and(|o| o.contains("new"));
- let iter = db::query_meme(rest.join(" "), creator, order).await?.into_iter();
+ let iter = db::query_meme(rest.join(" "), creator, order, guild_id.get()).await?.into_iter();
let result = iter
.pipe(serenity::futures::stream::iter)
.then(|(meme, metadata)| async move {
let user = UserId::new(metadata.created_by as u64).to_user(&ctx).await?;
- let username = user.nick_in(&ctx, CONFIG.discord.guild()).await.unwrap_or(user.name);
+ let username = user.nick_in(&ctx, guild_id).await.unwrap_or(user.name);
Ok(format!(
"*{}* by **{}** ({}). text length: **{}**, image: **{}**, audio: **{}**",
diff --git a/src/commands/meme/invoke.rs b/src/commands/meme/invoke.rs
index 37a78f5..aff5c23 100644
--- a/src/commands/meme/invoke.rs
+++ b/src/commands/meme/invoke.rs
@@ -1,5 +1,6 @@
use diesel::{
result::Error as DieselError,
+ row::NamedRow,
NotFound,
};
use grate::tracing;
@@ -79,14 +80,23 @@ pub(crate) async fn _meme(
return rand_meme(ctx, audio_playback).await;
}
+ let guild_id = util::guild_id(ctx)?;
+
let mut conn = connection().await?;
- let mem = match find_meme(&mut conn, args).await {
+ let mem = match find_meme(&mut conn, args, guild_id.get()).await {
Ok(x) => {
- InvocationRecord::create(&mut conn, ctx.author().id.get(), ctx.id(), x.id, false)
+ InvocationRecord::create(
+ &mut conn,
+ ctx.author().id.get(),
+ ctx.id(),
+ guild_id.get(),
+ x.id,
+ false,
+ )
.await?;
x
- },
+ }
Err(e) => {
return if let Some(NotFound) = e.downcast_ref::<DieselError>() {
tracing::info!("requested meme not found in database");
@@ -97,30 +107,43 @@ pub(crate) async fn _meme(
util::reply(ctx, "what in ryan's name").await?;
Err(e.into())
};
- },
+ }
};
send_meme(ctx, &mem, &mut conn).await
}
async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyhow::Result<()> {
- let mut conn = connection().await?;
+ let should_audio = util::users_listening(ctx).await?;
+ let guild_id = util::guild_id(ctx)?;
- let should_audio = util::users_listening(ctx.serenity_context()).await?;
+ let mut conn = connection().await?;
let mem = match audio_playback {
- AudioPlayback::Required => db::rand_audio_meme(&mut conn).await,
- AudioPlayback::Optional => db::rand_meme(&mut conn, should_audio).await,
- AudioPlayback::Prohibited => db::rand_silent_meme(&mut conn).await,
+ AudioPlayback::Required => db::rand_audio_meme(&mut conn, guild_id.get()).await,
+ AudioPlayback::Optional => db::rand_meme(&mut conn, should_audio, guild_id.get()).await,
+ AudioPlayback::Prohibited => db::rand_silent_meme(&mut conn, guild_id.get()).await,
};
match mem {
- Ok(mem) => {
- InvocationRecord::create(&mut conn, ctx.author().id.get(), ctx.id(), mem.id, true)
+ Ok(Some(mem)) => {
+ InvocationRecord::create(
+ &mut conn,
+ ctx.author().id.get(),
+ util::guild_id(ctx)?.get(),
+ ctx.id(),
+ mem.id,
+ true,
+ )
.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");
@@ -131,24 +154,40 @@ async fn rand_meme(ctx: PoiseContext<'_>, audio_playback: AudioPlayback) -> anyh
util::reply(ctx, "HELP").await?;
Err(e.into())
- },
+ }
}
}
/// Post a rare meme.
#[poise::command(prefix_command, guild_only, category = "memes", aliases("raremem", "rarememe"))]
pub async fn rare_meme(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
- let should_audio = util::users_listening(ctx.serenity_context()).await?;
+ let guild = util::guild_id(ctx)?;
+ let should_audio = util::users_listening(ctx).await?;
let mut conn = connection().await?;
- let meme = db::rare_meme(&mut conn, should_audio).await;
+ let meme = db::rare_meme(&mut conn, should_audio, guild.get()).await;
match meme {
- Ok(meme) => {
- InvocationRecord::create(&mut conn, ctx.author().id.get(), ctx.id(), meme.id, true)
+ Ok(Some(meme)) => {
+ InvocationRecord::create(
+ &mut conn,
+ ctx.author().id.get(),
+ util::guild_id(ctx)?.get(),
+ ctx.id(),
+ meme.id,
+ true,
+ )
.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>() {
tracing::info!("rare meme not found");
@@ -160,6 +199,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 7bc8b2e..eb6aa1d 100644
--- a/src/commands/meme/mod.rs
+++ b/src/commands/meme/mod.rs
@@ -19,6 +19,10 @@ use songbird::input::{
Compose,
Input,
};
+use std::{
+ borrow::ToOwned,
+ default::Default,
+};
pub use self::{
create::*,
@@ -102,14 +106,23 @@ async fn send_meme(
if let Some(audio) = audio {
let audio = audio?;
- let (_sb, call) = songbird(ctx).await?;
- let mut call = call.lock().await;
+ let Some(voice_channel) = util::best_voice_channel(ctx) else {
+ tracing::error!(?ctx, "couldn't find a relevant voice channel");
+ util::react(ctx, '🔇').await?;
- if call.current_channel().is_none() {
- call.join(CONFIG.discord.voice_channel()).await?;
- }
+ return Ok(());
+ };
+
+ {
+ let (_sb, call) = songbird(ctx).await?;
+ let mut call = call.lock().await;
- call.enqueue_input(Input::Lazy(Box::new(audio))).await;
+ if call.current_channel().is_none() {
+ call.join(voice_channel).await?;
+ }
+
+ call.enqueue_input(Input::Lazy(Box::new(audio))).await;
+ }
util::react(ctx, ReactionType::Unicode("📣".to_owned())).await?;
}
diff --git a/src/commands/playback.rs b/src/commands/playback.rs
index 50ac8bb..8121136 100644
--- a/src/commands/playback.rs
+++ b/src/commands/playback.rs
@@ -1,3 +1,5 @@
+use std::sync::Arc;
+
use grate::tracing;
use serenity::prelude::*;
use songbird::{
@@ -5,7 +7,7 @@ use songbird::{
Call,
Songbird,
};
-use std::sync::Arc;
+use tap::Conv;
use crate::{
bot::HttpKey,
@@ -16,7 +18,7 @@ use crate::{
};
pub fn commands() -> impl IntoIterator<Item = poise::Command<PoiseData, anyhow::Error>> {
- vec![play(), pause(), resume(), die(), list(), skip()]
+ vec![play(), pause(), resume(), die(), list(), skip(), move_()]
}
pub async fn songbird(ctx: PoiseContext<'_>) -> anyhow::Result<(Arc<Songbird>, Arc<Mutex<Call>>)> {
@@ -58,23 +60,36 @@ pub async fn _play(ctx: PoiseContext<'_>, url: &url::Url) -> anyhow::Result<()>
return Ok(());
}
- util::react(ctx, '🔃').await?;
+ let Some(voice_channel) = util::best_voice_channel(ctx) else {
+ tracing::error!(?ctx, "couldn't find a relevant voice channel");
+ util::react(ctx, '🔇').await?;
- let (_sb, call) = songbird(ctx).await?;
- let mut call = call.lock().await;
+ return Ok(());
+ };
- if call.current_channel().is_none() {
- call.join(CONFIG.discord.voice_channel()).await?;
- }
+ util::react(ctx, '🔃').await?;
let client = {
let data = ctx.serenity_context().data.read().await;
data.get::<HttpKey>().unwrap().clone()
};
- let input =
- YoutubeDl::new_ytdl_like(&crate::config::YTDL_COMMAND, client.clone(), url.to_string());
- call.enqueue_input(input.into()).await;
+ {
+ let (_sb, call) = songbird(ctx).await?;
+ let mut call = call.lock().await;
+
+ if call.current_channel().is_none() {
+ call.join(voice_channel).await?;
+ }
+
+ let input =
+ 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;
+ }
util::react(ctx, '📣').await?;
util::unreact(ctx, '🔃').await?;
@@ -82,6 +97,30 @@ pub async fn _play(ctx: PoiseContext<'_>, url: &url::Url) -> anyhow::Result<()>
Ok(())
}
+/// Move audio to the caller's voice channel.
+#[poise::command(rename = "move", prefix_command, guild_only, category = "playback")]
+pub async fn move_(ctx: PoiseContext<'_>) -> anyhow::Result<()> {
+ let (_sb, call) = songbird(ctx).await?;
+ let mut call = call.lock().await;
+
+ if call.current_channel().is_none() {
+ tracing::debug!("no current channel");
+ util::reply(ctx, "let's get yer head screwed on straight").await?;
+
+ return Ok(());
+ }
+
+ let Some(voice_channel) = util::best_voice_channel(ctx) else {
+ tracing::error!(?ctx, "couldn't find a relevant voice channel");
+ util::react(ctx, '🔇').await?;
+
+ return Ok(());
+ };
+
+ call.join(voice_channel).await?;
+ Ok(())
+}
+
/// Play a link.
#[poise::command(prefix_command, guild_only, category = "playback")]
pub async fn play(
diff --git a/src/commands/today/mod.rs b/src/commands/today/mod.rs
index 35059e5..e48983c 100644
--- a/src/commands/today/mod.rs
+++ b/src/commands/today/mod.rs
@@ -96,11 +96,18 @@ pub async fn today(ctx: PoiseContext<'_>, #[rest] _rest: Option<String>) -> anyh
let play_args = options.choose(&mut thread_rng());
if let Some(play_args) = play_args {
+ let Some(voice_channel) = util::best_voice_channel(ctx) else {
+ tracing::error!(?ctx, "couldn't find a relevant voice channel");
+ util::react(ctx, '🔇').await?;
+
+ return Ok(());
+ };
+
let (_sb, call) = songbird(ctx).await?;
let mut call = call.lock().await;
if call.current_channel().is_none() {
- call.join(CONFIG.discord.voice_channel()).await?;
+ call.join(voice_channel).await?;
}
let client = {