From 69e7a2a18f1bedb18c1dd738a40f3d7289cb3794 Mon Sep 17 00:00:00 2001 From: trivernis Date: Fri, 9 Apr 2021 18:34:18 +0200 Subject: [PATCH] Add autodelete setting and sticky np embed Signed-off-by: trivernis --- src/client.rs | 7 ++++-- src/commands/common.rs | 19 +++++++++++++++ src/commands/minecraft/enchantment.rs | 2 ++ src/commands/minecraft/item.rs | 2 ++ src/commands/misc/help.rs | 3 ++- src/commands/misc/shutdown.rs | 2 ++ src/commands/misc/stats.rs | 2 ++ src/commands/mod.rs | 1 + src/commands/music/clear_queue.rs | 2 ++ src/commands/music/current.rs | 2 ++ src/commands/music/join.rs | 2 ++ src/commands/music/leave.rs | 2 ++ src/commands/music/lyrics.rs | 2 ++ src/commands/music/mod.rs | 2 +- src/commands/music/pause.rs | 2 ++ src/commands/music/play.rs | 11 +++++---- src/commands/music/play_next.rs | 2 ++ src/commands/music/playlists.rs | 2 ++ src/commands/music/queue.rs | 2 ++ src/commands/music/shuffle.rs | 2 ++ src/commands/music/skip.rs | 2 ++ src/commands/settings/get.rs | 5 ++-- src/commands/settings/mod.rs | 3 --- src/commands/settings/set.rs | 6 +++-- src/messages/music.rs | 29 +++++++++++++++++++--- src/providers/mod.rs | 1 + src/providers/settings.rs | 35 +++++++++++++++++++++++++++ src/utils/messages.rs | 22 ++++++++++++++--- 28 files changed, 151 insertions(+), 23 deletions(-) create mode 100644 src/commands/common.rs create mode 100644 src/providers/settings.rs diff --git a/src/client.rs b/src/client.rs index 29a5d86..6170afd 100644 --- a/src/client.rs +++ b/src/client.rs @@ -8,11 +8,13 @@ use songbird::SerenityInit; use crate::commands::*; use crate::handler::Handler; -use crate::utils::context_data::{DatabaseContainer, Store, StoreData}; +use crate::utils::context_data::{ + DatabaseContainer, EventDrivenMessageContainer, Store, StoreData, +}; use crate::utils::error::{BotError, BotResult}; use database::get_database; use serenity::model::id::UserId; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; pub async fn get_client() -> BotResult { let token = dotenv::var("BOT_TOKEN").map_err(|_| BotError::MissingToken)?; @@ -27,6 +29,7 @@ pub async fn get_client() -> BotResult { let mut data = client.data.write().await; data.insert::(StoreData::new()); data.insert::(database); + data.insert::(HashMap::new()); } Ok(client) diff --git a/src/commands/common.rs b/src/commands/common.rs new file mode 100644 index 0000000..b946de0 --- /dev/null +++ b/src/commands/common.rs @@ -0,0 +1,19 @@ +use crate::providers::settings::{get_setting, Setting}; +use crate::utils::error::BotResult; +use serenity::model::channel::Message; +use serenity::prelude::*; + +/// Deletes a message automatically if configured that way +pub async fn handle_autodelete(ctx: &Context, msg: &Message) -> BotResult<()> { + if let Some(guild_id) = msg.guild_id { + let autodelete = get_setting(ctx, guild_id, Setting::BotAutoDelete) + .await? + .unwrap_or(true); + + if autodelete { + let _ = msg.delete(ctx).await; + } + } + + Ok(()) +} diff --git a/src/commands/minecraft/enchantment.rs b/src/commands/minecraft/enchantment.rs index 163765f..d25f8ff 100644 --- a/src/commands/minecraft/enchantment.rs +++ b/src/commands/minecraft/enchantment.rs @@ -2,6 +2,7 @@ use serenity::client::Context; use serenity::framework::standard::{macros::command, Args, CommandError, CommandResult}; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::utils::context_data::Store; #[command] @@ -64,6 +65,7 @@ pub(crate) async fn enchantment(ctx: &Context, msg: &Message, args: Args) -> Com }) }) .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/minecraft/item.rs b/src/commands/minecraft/item.rs index 489b461..df4bd46 100644 --- a/src/commands/minecraft/item.rs +++ b/src/commands/minecraft/item.rs @@ -2,6 +2,7 @@ use serenity::client::Context; use serenity::framework::standard::{macros::command, Args, CommandError, CommandResult}; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::utils::context_data::Store; #[command] @@ -60,6 +61,7 @@ pub(crate) async fn item(ctx: &Context, msg: &Message, args: Args) -> CommandRes }) }) .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/misc/help.rs b/src/commands/misc/help.rs index 5483532..d7ba4ea 100644 --- a/src/commands/misc/help.rs +++ b/src/commands/misc/help.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use crate::commands::common::handle_autodelete; use serenity::client::Context; use serenity::framework::standard::macros::help; use serenity::framework::standard::{help_commands, Args}; @@ -18,6 +19,6 @@ pub async fn help( owners: HashSet, ) -> CommandResult { let _ = help_commands::with_embeds(ctx, msg, args, help_options, groups, owners).await; - + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/misc/shutdown.rs b/src/commands/misc/shutdown.rs index 7fe3da1..1c2c6ec 100644 --- a/src/commands/misc/shutdown.rs +++ b/src/commands/misc/shutdown.rs @@ -1,3 +1,4 @@ +use crate::commands::common::handle_autodelete; use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; @@ -13,5 +14,6 @@ async fn shutdown(ctx: &Context, msg: &Message) -> CommandResult { msg.channel_id .say(ctx, ":night_with_stars: Good night ....") .await?; + handle_autodelete(ctx, msg).await?; process::exit(0); } diff --git a/src/commands/misc/stats.rs b/src/commands/misc/stats.rs index 2e15e7a..add521f 100644 --- a/src/commands/misc/stats.rs +++ b/src/commands/misc/stats.rs @@ -1,3 +1,4 @@ +use crate::commands::common::handle_autodelete; use chrono::Duration as ChronoDuration; use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; @@ -64,6 +65,7 @@ async fn stats(ctx: &Context, msg: &Message) -> CommandResult { }) }) .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index f378f65..84ae7ed 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -8,3 +8,4 @@ pub(crate) mod minecraft; pub(crate) mod misc; pub(crate) mod music; pub(crate) mod settings; +mod common; diff --git a/src/commands/music/clear_queue.rs b/src/commands/music/clear_queue.rs index 8d83456..b20c10f 100644 --- a/src/commands/music/clear_queue.rs +++ b/src/commands/music/clear_queue.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; #[command] @@ -24,6 +25,7 @@ async fn clear_queue(ctx: &Context, msg: &Message) -> CommandResult { msg.channel_id .say(ctx, "The queue has been cleared") .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/current.rs b/src/commands/music/current.rs index 535790b..afc7e95 100644 --- a/src/commands/music/current.rs +++ b/src/commands/music/current.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; use crate::messages::music::NowPlayingMessage; use std::mem; @@ -29,6 +30,7 @@ async fn current(ctx: &Context, msg: &Message) -> CommandResult { let _ = old_np.inner().delete().await; } } + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/join.rs b/src/commands/music/join.rs index e6d695c..65543d7 100644 --- a/src/commands/music/join.rs +++ b/src/commands/music/join.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::{get_channel_for_author, join_channel}; #[command] @@ -14,6 +15,7 @@ async fn join(ctx: &Context, msg: &Message) -> CommandResult { let channel_id = get_channel_for_author(&msg.author.id, &guild)?; log::debug!("Joining channel {} for guild {}", channel_id, guild.id); join_channel(ctx, channel_id, guild.id).await; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/leave.rs b/src/commands/music/leave.rs index cea6277..4471f49 100644 --- a/src/commands/music/leave.rs +++ b/src/commands/music/leave.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::{get_queue_for_guild, get_voice_manager}; #[command] @@ -35,6 +36,7 @@ async fn leave(ctx: &Context, msg: &Message) -> CommandResult { msg.channel_id.say(ctx, "Not in a voice channel").await?; log::debug!("Not in a voice channel"); } + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/lyrics.rs b/src/commands/music/lyrics.rs index 84ec030..82416ae 100644 --- a/src/commands/music/lyrics.rs +++ b/src/commands/music/lyrics.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; use crate::providers::music::lyrics::get_lyrics; @@ -44,6 +45,7 @@ async fn lyrics(ctx: &Context, msg: &Message) -> CommandResult { .say(ctx, "I'm not playing music right now") .await?; } + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/mod.rs b/src/commands/music/mod.rs index 4db3e0b..f26f94c 100644 --- a/src/commands/music/mod.rs +++ b/src/commands/music/mod.rs @@ -249,7 +249,7 @@ async fn play_next_in_queue( let track = handler_lock.play_only_source(source); log::trace!("Track is {:?}", track); - if let Some(np) = &queue_lock.now_playing_msg { + if let Some(np) = &mut queue_lock.now_playing_msg { let _ = np.refresh(track.metadata()).await; } queue_lock.set_current(track); diff --git a/src/commands/music/pause.rs b/src/commands/music/pause.rs index 3d49174..6704f28 100644 --- a/src/commands/music/pause.rs +++ b/src/commands/music/pause.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; use serenity::prelude::*; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; #[command] @@ -28,6 +29,7 @@ async fn pause(ctx: &Context, msg: &Message) -> CommandResult { } else { msg.channel_id.say(ctx, "Nothing to pause").await?; } + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/play.rs b/src/commands/music/play.rs index fa2562a..c3a5ce0 100644 --- a/src/commands/music/play.rs +++ b/src/commands/music/play.rs @@ -8,8 +8,8 @@ use crate::commands::music::{ join_channel, play_next_in_queue, }; -use crate::commands::settings::SETTING_AUTOSHUFFLE; -use crate::utils::context_data::get_database_from_context; +use crate::commands::common::handle_autodelete; +use crate::providers::settings::{get_setting, Setting}; #[command] #[only_in(guilds)] @@ -45,10 +45,10 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult { for song in songs { queue_lock.add(song); } - let database = get_database_from_context(ctx).await; - let autoshuffle = database - .get_guild_setting(guild.id.0, SETTING_AUTOSHUFFLE)? + let autoshuffle = get_setting(ctx, guild.id, Setting::MusicAutoShuffle) + .await? .unwrap_or(false); + if autoshuffle { log::debug!("Autoshuffeling"); queue_lock.shuffle(); @@ -60,6 +60,7 @@ async fn play(ctx: &Context, msg: &Message, args: Args) -> CommandResult { log::debug!("Playing first song in queue"); while !play_next_in_queue(&ctx.http, &msg.channel_id, &queue, &handler_lock).await {} } + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/play_next.rs b/src/commands/music/play_next.rs index a775c5d..ce90f82 100644 --- a/src/commands/music/play_next.rs +++ b/src/commands/music/play_next.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::{Args, CommandError, CommandResult}; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::{ get_channel_for_author, get_queue_for_guild, get_songs_for_query, get_voice_manager, join_channel, play_next_in_queue, @@ -50,6 +51,7 @@ async fn play_next(ctx: &Context, msg: &Message, args: Args) -> CommandResult { if play_first { while !play_next_in_queue(&ctx.http, &msg.channel_id, &queue, &handler).await {} } + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/playlists.rs b/src/commands/music/playlists.rs index 03ae07a..ab7761b 100644 --- a/src/commands/music/playlists.rs +++ b/src/commands/music/playlists.rs @@ -1,3 +1,4 @@ +use crate::commands::common::handle_autodelete; use crate::utils::context_data::get_database_from_context; use serenity::client::Context; use serenity::framework::standard::macros::command; @@ -27,6 +28,7 @@ async fn playlists(ctx: &Context, msg: &Message) -> CommandResult { }) }) .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/queue.rs b/src/commands/music/queue.rs index c397d18..cd984ec 100644 --- a/src/commands/music/queue.rs +++ b/src/commands/music/queue.rs @@ -5,6 +5,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; #[command] @@ -55,6 +56,7 @@ async fn queue(ctx: &Context, msg: &Message) -> CommandResult { }) }) .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/shuffle.rs b/src/commands/music/shuffle.rs index e252652..3475e4a 100644 --- a/src/commands/music/shuffle.rs +++ b/src/commands/music/shuffle.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; #[command] @@ -24,6 +25,7 @@ async fn shuffle(ctx: &Context, msg: &Message) -> CommandResult { msg.channel_id .say(ctx, "The queue has been shuffled") .await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/music/skip.rs b/src/commands/music/skip.rs index f914ab8..44118a9 100644 --- a/src/commands/music/skip.rs +++ b/src/commands/music/skip.rs @@ -3,6 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::CommandResult; use serenity::model::channel::Message; +use crate::commands::common::handle_autodelete; use crate::commands::music::get_queue_for_guild; #[command] @@ -23,6 +24,7 @@ async fn skip(ctx: &Context, msg: &Message) -> CommandResult { } msg.channel_id.say(ctx, "Skipped to the next song").await?; + handle_autodelete(ctx, msg).await?; Ok(()) } diff --git a/src/commands/settings/get.rs b/src/commands/settings/get.rs index b077278..f8b3c8d 100644 --- a/src/commands/settings/get.rs +++ b/src/commands/settings/get.rs @@ -3,7 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::{Args, CommandResult}; use serenity::model::channel::Message; -use crate::commands::settings::GUILD_SETTINGS; +use crate::providers::settings::ALL_SETTINGS; use crate::utils::context_data::get_database_from_context; #[command] @@ -37,7 +37,8 @@ async fn get(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { } } else { log::debug!("Displaying all guild settings"); - for key in GUILD_SETTINGS { + for key in ALL_SETTINGS { + let key = key.to_string(); let mut kv_pairs = Vec::new(); { match database.get_guild_setting::(guild.id.0, &key)? { diff --git a/src/commands/settings/mod.rs b/src/commands/settings/mod.rs index 7215deb..fc894df 100644 --- a/src/commands/settings/mod.rs +++ b/src/commands/settings/mod.rs @@ -10,6 +10,3 @@ mod set; #[commands(set, get)] #[prefix("settings")] pub struct Settings; - -pub const SETTING_AUTOSHUFFLE: &str = "music.autoshuffle"; -pub const GUILD_SETTINGS: &[&str] = &[SETTING_AUTOSHUFFLE]; diff --git a/src/commands/settings/set.rs b/src/commands/settings/set.rs index 436e96c..1698cc7 100644 --- a/src/commands/settings/set.rs +++ b/src/commands/settings/set.rs @@ -3,7 +3,7 @@ use serenity::framework::standard::macros::command; use serenity::framework::standard::{Args, CommandResult}; use serenity::model::channel::Message; -use crate::commands::settings::GUILD_SETTINGS; +use crate::providers::settings::ALL_SETTINGS; use crate::utils::context_data::get_database_from_context; #[command] @@ -16,7 +16,9 @@ use crate::utils::context_data::get_database_from_context; #[required_permissions("MANAGE_GUILD")] async fn set(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult { let key = args.single::().unwrap().to_lowercase(); - if !GUILD_SETTINGS.contains(&&*key) { + let all_settings: Vec = ALL_SETTINGS.iter().map(|s| s.to_string()).collect(); + + if !all_settings.contains(&key) { msg.channel_id .say(ctx, format!("Invalid setting `{}`", key)) .await?; diff --git a/src/messages/music.rs b/src/messages/music.rs index 12e8967..d008830 100644 --- a/src/messages/music.rs +++ b/src/messages/music.rs @@ -32,11 +32,34 @@ impl NowPlayingMessage { } /// Refreshes the now playing message - pub async fn refresh(&self, meta: &Metadata) -> BotResult<()> { - self.inner - .edit(|m| m.embed(|e| Self::create_embed(meta, e))) + pub async fn refresh(&mut self, meta: &Metadata) -> BotResult<()> { + let channel_id = self.inner.channel_id(); + let messages = channel_id + .messages(self.inner.http(), |p| p.limit(1)) .await?; + let needs_recreate = messages + .first() + .map(|m| m.id != self.inner.message_id()) + .unwrap_or(true); + + // recreates the message if needed + if needs_recreate { + log::debug!("Song info message will be recreated"); + let http = self.inner.http().clone(); + let _ = self.inner.delete().await; + + self.inner = ShareableMessage::create(http, &channel_id, |f| { + f.embed(|e| Self::create_embed(meta, e)) + }) + .await?; + } else { + log::debug!("Reusing old song info"); + self.inner + .edit(|m| m.embed(|e| Self::create_embed(meta, e))) + .await?; + } + Ok(()) } diff --git a/src/providers/mod.rs b/src/providers/mod.rs index bfb8134..26428cb 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -1 +1,2 @@ pub(crate) mod music; +pub(crate) mod settings; diff --git a/src/providers/settings.rs b/src/providers/settings.rs new file mode 100644 index 0000000..3084aa4 --- /dev/null +++ b/src/providers/settings.rs @@ -0,0 +1,35 @@ +use crate::utils::context_data::DatabaseContainer; +use crate::utils::error::{BotError, BotResult}; +use serenity::client::Context; +use serenity::model::prelude::GuildId; +use std::str::FromStr; + +pub static ALL_SETTINGS: &[Setting] = &[Setting::MusicAutoShuffle, Setting::BotAutoDelete]; + +#[derive(Clone, Debug)] +pub enum Setting { + MusicAutoShuffle, + BotAutoDelete, +} + +impl ToString for Setting { + fn to_string(&self) -> String { + match self { + Self::MusicAutoShuffle => "music.autoshuffle".to_string(), + Self::BotAutoDelete => "bot.autodelete".to_string(), + } + } +} + +/// Returns a specific guild setting +pub async fn get_setting( + ctx: &Context, + guild_id: GuildId, + setting: Setting, +) -> BotResult> { + let data = ctx.data.read().await; + let database = data.get::().unwrap(); + database + .get_guild_setting::(guild_id.0, &setting.to_string()) + .map_err(BotError::from) +} diff --git a/src/utils/messages.rs b/src/utils/messages.rs index d3f1b34..15e1c80 100644 --- a/src/utils/messages.rs +++ b/src/utils/messages.rs @@ -42,7 +42,7 @@ impl ShareableMessage { /// Deletes the underlying message pub async fn delete(&self) -> BotResult<()> { - let msg = self.get_message().await?; + let msg = self.get_discord_message().await?; msg.delete(&self.http).await?; Ok(()) @@ -53,20 +53,34 @@ impl ShareableMessage { where F: FnOnce(&mut EditMessage) -> &mut EditMessage, { - let mut message = self.get_message().await?; + let mut message = self.get_discord_message().await?; message.edit(&self.http, f).await?; Ok(()) } /// Returns the underlying message - async fn get_message(&self) -> BotResult { + pub async fn get_discord_message(&self) -> BotResult { let message = self .http - .http() .get_message(self.channel_id, self.message_id) .await?; Ok(message) } + + /// Returns the channel of the message + pub fn channel_id(&self) -> ChannelId { + ChannelId(self.channel_id) + } + + /// Returns the message id of the message + pub fn message_id(&self) -> MessageId { + MessageId(self.message_id) + } + + /// Returns the reference to the http object + pub fn http(&self) -> &Arc { + &self.http + } }