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.
This commit is contained in:
Lucien Cartier-Tilet 2023-11-23 22:15:47 +01:00
parent ee2b2c17d0
commit fb0ad5be13
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA
5 changed files with 56 additions and 15 deletions

View File

@ -7,7 +7,8 @@
-- them from the database. This operation is noop in Rust and should -- them from the database. This operation is noop in Rust and should
-- therefore not cost a single CPU cycle. -- therefore not cost a single CPU cycle.
CREATE TABLE IF NOT EXISTS guild_log_channels ( CREATE TABLE IF NOT EXISTS guild_log_channels (
guild_id INTEGER PRIMARY KEY, guild_id INTEGER NOT NULL,
channel_id INTEGER NOT NULL, channel_id INTEGER NOT NULL,
UNIQUE(guild_id, channel_id) UNIQUE(guild_id, channel_id)
); );
CREATE INDEX IF NOT EXISTS guild_log_channels_guild_id ON guild_log_channels(guild_id);

View File

@ -1,7 +1,10 @@
#![allow(clippy::cast_possible_wrap, clippy::cast_sign_loss)]
use std::env; use std::env;
use poise::serenity_prelude::{ChannelId, GuildId}; use poise::serenity_prelude::{ChannelId, GuildId};
use sqlx::SqlitePool; use sqlx::SqlitePool;
use tracing::error;
pub type Result<T> = ::std::result::Result<T, sqlx::Error>; pub type Result<T> = ::std::result::Result<T, sqlx::Error>;
@ -22,9 +25,9 @@ impl Database {
pub async fn get_logging_channel( pub async fn get_logging_channel(
&self, &self,
guild_id: u64, guild_id: GuildId,
) -> Result<Vec<u64>> { ) -> Result<Vec<u64>> {
let guild_id = guild_id as i64; let guild_id = guild_id.0 as i64;
let channels = sqlx::query!( let channels = sqlx::query!(
r#" r#"
SELECT channel_id SELECT channel_id
@ -34,7 +37,11 @@ WHERE guild_id = ?1
guild_id guild_id
) )
.fetch_all(&self.pool) .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()) Ok(channels.iter().map(|id| id.channel_id as u64).collect())
} }
@ -57,6 +64,10 @@ VALUES ( ?1, ?2 )
) )
.execute(&mut *conn) .execute(&mut *conn)
.await .await
.map_err(|e| {
error!("Error setting channel {channel_id} as logger for guild {guild_id}: {e:?}");
e
})
.map(|_| ()) .map(|_| ())
} }
} }

View File

@ -2,12 +2,13 @@ use super::{Context, Error};
use super::utils::serenity; use super::utils::serenity;
#[allow(clippy::unused_async)]
#[poise::command( #[poise::command(
slash_command, slash_command,
subcommands("add_channel"), subcommands("add_channel", "list_channels"),
required_permissions = "ADMINISTRATOR" required_permissions = "ADMINISTRATOR"
)] )]
pub async fn logging(_ctx: Context<'_>, _arg: String) -> Result<(), Error> { pub async fn logging(_ctx: Context<'_>) -> Result<(), Error> {
Ok(()) Ok(())
} }
@ -18,7 +19,7 @@ pub async fn add_channel(
) -> Result<(), Error> { ) -> Result<(), Error> {
let channel_id = channel.id(); let channel_id = channel.id();
let response = match ctx.guild_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) => { Some(guild_id) => {
match ctx match ctx
.data() .data()
@ -26,7 +27,7 @@ pub async fn add_channel(
.set_logging_channel(guild_id, channel_id) .set_logging_channel(guild_id, channel_id)
.await .await
{ {
Ok(_) => format!( Ok(()) => format!(
"Added channel <#{channel_id}> as a logging channel" "Added channel <#{channel_id}> as a logging channel"
), ),
Err(e) => { Err(e) => {
@ -34,7 +35,7 @@ pub async fn add_channel(
if db_error.is_unique_violation() { if db_error.is_unique_violation() {
format!("Channel <#{channel_id}> is already a logging channel") format!("Channel <#{channel_id}> is already a logging channel")
} else { } else {
format!("Error: {:?}", e) format!("Error: {e:?}")
} }
} else { } else {
format!( format!(
@ -48,3 +49,31 @@ pub async fn add_channel(
ctx.say(response).await?; ctx.say(response).await?;
Ok(()) 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::<Vec<String>>()
.join("\n")
)
}
}
Err(e) => format!("Could not retrieve loggers: {e:?}"),
}
}
};
ctx.say(response).await?;
Ok(())
}

View File

@ -8,9 +8,8 @@ use utils::serenity;
use commands::logging; use commands::logging;
use utils::{BotData, Context, Error}; use utils::{BotData, Context, Error};
pub async fn make_bot() -> color_eyre::Result<FrameworkBuilder<BotData, Error>> pub fn make_bot() -> FrameworkBuilder<BotData, Error> {
{ poise::Framework::builder()
let framework = poise::Framework::builder()
.options(poise::FrameworkOptions { .options(poise::FrameworkOptions {
commands: vec![logging()], commands: vec![logging()],
..Default::default() ..Default::default()
@ -26,6 +25,5 @@ pub async fn make_bot() -> color_eyre::Result<FrameworkBuilder<BotData, Error>>
.await?; .await?;
Ok(BotData::new().await?) Ok(BotData::new().await?)
}) })
}); })
Ok(framework)
} }

View File

@ -1,3 +1,5 @@
#![warn(clippy::style, clippy::pedantic)]
mod utils; mod utils;
mod db; mod db;
mod discord; mod discord;
@ -10,7 +12,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
color_eyre::install()?; color_eyre::install()?;
utils::setup_logging(); utils::setup_logging();
let bot = discord::make_bot().await?; let bot = discord::make_bot();
bot.run().await?; bot.run().await?;
Ok(()) Ok(())