From fb0ad5be13c6c1007e99a842ceb320573191c60c Mon Sep 17 00:00:00 2001 From: Lucien Cartier-Tilet Date: Thu, 23 Nov 2023 22:15:47 +0100 Subject: [PATCH] feat: add listing logger channels in a guild This commit also allows in the database to hold more than one channel per guild and introduces clippy linting. Closes #2 BREAKING CHANGES: The database schema changed from its root. All databases should be dropped and recreated before running this new version. --- .../20231122220824_guild_log_channels.sql | 3 +- src/db/mod.rs | 17 ++++++-- src/discord/commands.rs | 39 ++++++++++++++++--- src/discord/mod.rs | 8 ++-- src/main.rs | 4 +- 5 files changed, 56 insertions(+), 15 deletions(-) diff --git a/migrations/20231122220824_guild_log_channels.sql b/migrations/20231122220824_guild_log_channels.sql index 192e410..90085f8 100644 --- a/migrations/20231122220824_guild_log_channels.sql +++ b/migrations/20231122220824_guild_log_channels.sql @@ -7,7 +7,8 @@ -- them from the database. This operation is noop in Rust and should -- therefore not cost a single CPU cycle. CREATE TABLE IF NOT EXISTS guild_log_channels ( - guild_id INTEGER PRIMARY KEY, + guild_id INTEGER NOT NULL, channel_id INTEGER NOT NULL, UNIQUE(guild_id, channel_id) ); +CREATE INDEX IF NOT EXISTS guild_log_channels_guild_id ON guild_log_channels(guild_id); diff --git a/src/db/mod.rs b/src/db/mod.rs index 3720f66..e8eb31d 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -1,7 +1,10 @@ +#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)] + use std::env; use poise::serenity_prelude::{ChannelId, GuildId}; use sqlx::SqlitePool; +use tracing::error; pub type Result = ::std::result::Result; @@ -22,9 +25,9 @@ impl Database { pub async fn get_logging_channel( &self, - guild_id: u64, + guild_id: GuildId, ) -> Result> { - let guild_id = guild_id as i64; + let guild_id = guild_id.0 as i64; let channels = sqlx::query!( r#" SELECT channel_id @@ -34,7 +37,11 @@ WHERE guild_id = ?1 guild_id ) .fetch_all(&self.pool) - .await?; + .await + .map_err(|e| { + error!("Error getting logging channels for guild {guild_id}: {e:?}"); + e + })?; Ok(channels.iter().map(|id| id.channel_id as u64).collect()) } @@ -57,6 +64,10 @@ VALUES ( ?1, ?2 ) ) .execute(&mut *conn) .await + .map_err(|e| { + error!("Error setting channel {channel_id} as logger for guild {guild_id}: {e:?}"); + e + }) .map(|_| ()) } } diff --git a/src/discord/commands.rs b/src/discord/commands.rs index 93ee94f..cbc2162 100644 --- a/src/discord/commands.rs +++ b/src/discord/commands.rs @@ -2,12 +2,13 @@ use super::{Context, Error}; use super::utils::serenity; +#[allow(clippy::unused_async)] #[poise::command( slash_command, - subcommands("add_channel"), + subcommands("add_channel", "list_channels"), required_permissions = "ADMINISTRATOR" )] -pub async fn logging(_ctx: Context<'_>, _arg: String) -> Result<(), Error> { +pub async fn logging(_ctx: Context<'_>) -> Result<(), Error> { Ok(()) } @@ -18,7 +19,7 @@ pub async fn add_channel( ) -> Result<(), Error> { let channel_id = channel.id(); let response = match ctx.guild_id() { - None => "Error: Could not determine the guild's ID.".to_owned(), + None => "Error: Could not determine the guild's ID".to_owned(), Some(guild_id) => { match ctx .data() @@ -26,7 +27,7 @@ pub async fn add_channel( .set_logging_channel(guild_id, channel_id) .await { - Ok(_) => format!( + Ok(()) => format!( "Added channel <#{channel_id}> as a logging channel" ), Err(e) => { @@ -34,7 +35,7 @@ pub async fn add_channel( if db_error.is_unique_violation() { format!("Channel <#{channel_id}> is already a logging channel") } else { - format!("Error: {:?}", e) + format!("Error: {e:?}") } } else { format!( @@ -48,3 +49,31 @@ pub async fn add_channel( ctx.say(response).await?; Ok(()) } + +#[poise::command(slash_command, aliases("list-channels"))] +pub async fn list_channels(ctx: Context<'_>) -> Result<(), Error> { + let response = match ctx.guild_id() { + None => "Error: Could not determine the guild's ID".to_owned(), + Some(guild_id) => { + match ctx.data().database.get_logging_channel(guild_id).await { + Ok(channels) => { + if channels.is_empty() { + "No channels registered as loggers".to_owned() + } else { + format!( + "Here are the channels currently set as loggers:\n{}", + channels + .iter() + .map(|channel| format!("- <#{channel}>")) + .collect::>() + .join("\n") + ) + } + } + Err(e) => format!("Could not retrieve loggers: {e:?}"), + } + } + }; + ctx.say(response).await?; + Ok(()) +} diff --git a/src/discord/mod.rs b/src/discord/mod.rs index 2818095..daea786 100644 --- a/src/discord/mod.rs +++ b/src/discord/mod.rs @@ -8,9 +8,8 @@ use utils::serenity; use commands::logging; use utils::{BotData, Context, Error}; -pub async fn make_bot() -> color_eyre::Result> -{ - let framework = poise::Framework::builder() +pub fn make_bot() -> FrameworkBuilder { + poise::Framework::builder() .options(poise::FrameworkOptions { commands: vec![logging()], ..Default::default() @@ -26,6 +25,5 @@ pub async fn make_bot() -> color_eyre::Result> .await?; Ok(BotData::new().await?) }) - }); - Ok(framework) + }) } diff --git a/src/main.rs b/src/main.rs index 200e01a..4d8c8eb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![warn(clippy::style, clippy::pedantic)] + mod utils; mod db; mod discord; @@ -10,7 +12,7 @@ async fn main() -> Result<(), Box> { color_eyre::install()?; utils::setup_logging(); - let bot = discord::make_bot().await?; + let bot = discord::make_bot(); bot.run().await?; Ok(())