Skip to content

Commit

Permalink
Merge pull request #26 from Panini-Devs/0.0.1-staging-changes
Browse files Browse the repository at this point in the history
Pagination of warning command
  • Loading branch information
paninizer authored Feb 17, 2024
2 parents 99e6167 + c989d16 commit ddac02c
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- uses: hecrj/setup-rust-action@v2
with:
rust-version: ${{ matrix.rust }}
- name: Update APT Cache
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update
- name: Install 'libatk1.0-dev' on Ubuntu
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get install -y libatk1.0-dev
Expand Down
Binary file modified database.sqlite
Binary file not shown.
106 changes: 103 additions & 3 deletions src/commands/moderation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ use std::time::Duration;

use crate::{
utilities::{
messages, models,
modlog::{self, ModType},
self, embeds::warnings_command_embed, messages, models, modlog::{self, ModType}
},
Context, Error,
};

use chrono::{Days, Utc};
use chrono::{Days, NaiveDateTime, Utc};
use duration_str::parse;
use poise::serenity_prelude::UserId;
use serenity::model::Timestamp;
Expand Down Expand Up @@ -607,3 +606,104 @@ pub async fn warn(

Ok(())
}

#[poise::command(
prefix_command,
slash_command,
category = "Moderator",
required_permissions = "MODERATE_MEMBERS",
required_bot_permissions = "MODERATE_MEMBERS | SEND_MESSAGES",
guild_only
)]
pub async fn warnings(
context: Context<'_>,
#[description = "The user to get warnings for."]
#[rename = "user"]
user_id: UserId,
) -> Result<(), Error> {
let database = &context.data().sqlite;

let user = models::user(context, user_id).await?;

if user.system {
let reply = messages::error_reply("Cannot get warnings for a system user.", false);
context.send(reply).await?;
return Ok(());
}

let result = {
let (user_name, user_mention) = (&user.name, models::user_mention(context, user_id).await?);

let guild_id = context.guild_id().unwrap();

let user_mod_history =
match modlog::select_modlog(ModType::Warn, &user_id, &guild_id, database).await {
Ok(user_mod_history) => user_mod_history,
Err(why) => {
error!("Couldn't select warnings from infractions: {why:?}");
return Err(why.into());
}
};

let warning_count = user_mod_history.len();
if warning_count < 1 {
let reply =
messages::info_reply(format!("{user_mention} doesn't have any warnings."), true);
context.send(reply).await?;

return Ok(());
}

let (uuids, moderator_ids, reasons, created_ats) = (
user_mod_history
.iter()
.map(|(uuid, _, _, _, _, _, _)| uuid)
.collect::<Vec<&String>>(),
user_mod_history
.iter()
.map(|(_, _, _, moderator_id, _, _, _)| moderator_id)
.collect::<Vec<&i64>>(),
user_mod_history
.iter()
.map(|(_, _, _, _, reason, _, _)| reason)
.collect::<Vec<&String>>(),
user_mod_history
.iter()
.map(|(_, _, _, _, _, created_at, _)| created_at)
.collect::<Vec<&NaiveDateTime>>(),
);

// TODO: Add pagination
let uuids_iter = uuids.chunks(25);
let mod_ids_iter = moderator_ids.chunks(25);
let reasons_iter = reasons.chunks(25);
let created_ats_iter = created_ats.chunks(25);

// Cycle through the chunks of 25, creating pagination embeds
let mut embeds = Vec::new();
uuids_iter.zip(mod_ids_iter.zip(reasons_iter.zip(created_ats_iter))).for_each(
|(uuids, (moderator_ids, (reasons, created_ats)))| {
embeds.push(warnings_command_embed(&user, uuids, moderator_ids, reasons, created_ats));
}
);

match utilities::paginate::paginate(context, embeds).await {
Ok(_) => {
let author = context.author().id;
info!("@{author} requested @{user_name}'s warnings");
Ok(format!("{user_mention} has {warning_count} warning(s)."))
}
Err(why) => {
error!("Failed to paginate: {why:?}");
Err(why.to_string())
},
}
};

if let Err(why) = result {
let reply = messages::error_reply(&why, true);
context.send(reply).await?;
}

Ok(())
}
4 changes: 4 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{sync::Arc, time::Duration};
use tokio::time::sleep;
use tracing::{error, info};
use utilities::event_handler::event_handler;
use utilities::on_error::on_error;
use utilities::types::GuildSettings;

mod commands;
Expand All @@ -26,6 +27,7 @@ pub struct Data {
pub is_loop_running: AtomicBool,
} // User data, which is stored and accessible in all command invocations
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type FrameworkError<'a> = poise::FrameworkError<'a, Data, Error>;
pub type Context<'a> = poise::Context<'a, Data, Error>;
pub type PartialContext<'a> = poise::PartialContext<'a, Data, Error>;

Expand Down Expand Up @@ -192,6 +194,7 @@ async fn main() {
timeout(),
untimeout(),
warn(),
warnings(),
// Utility commands
help(),
ping(),
Expand All @@ -216,6 +219,7 @@ async fn main() {
commands_ran_global.fetch_add(1, Ordering::Relaxed);
})
},
on_error: |error| Box::pin(on_error(error)),
..Default::default()
})
.setup(|context, _ready, framework| {
Expand Down
10 changes: 5 additions & 5 deletions src/utilities/embeds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ use std::fmt::Write;
// Modified from wakalaka-rs
pub fn warnings_command_embed(
user: &User,
uuids: Vec<&String>,
moderator_ids: Vec<&i64>,
reasons: Vec<&String>,
dates: Vec<NaiveDateTime>,
uuids: &[&String],
moderator_ids: &[&i64],
reasons: &[&String],
dates: &[&NaiveDateTime],
) -> CreateEmbed {
// |(PFP) {user_name} |
// | ID | Moderator | Reason |
Expand Down Expand Up @@ -56,7 +56,7 @@ pub fn warnings_command_embed(
("Reason", reason_field, true),
("\u{200B}", "\u{200B}".to_owned(), false),
("ID", id_field, true),
("Date", date_field, true)
("Date", date_field, true),
];

CreateEmbed::default()
Expand Down
1 change: 1 addition & 0 deletions src/utilities/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ pub mod git;
pub mod messages;
pub mod models;
pub mod modlog;
pub mod on_error;
pub mod paginate;
pub mod types;
6 changes: 3 additions & 3 deletions src/utilities/modlog.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ impl ModType {
}
}

pub async fn select_mod_log(
pub async fn select_modlog(
modtype: ModType,
user_id: &UserId,
guild_id: &GuildId,
pool: &SqlitePool,
) -> Result<Vec<(String, i64, i64, i64, String, String, String)>, sqlx::Error> {
) -> Result<Vec<(String, i64, i64, i64, String, NaiveDateTime, String)>, sqlx::Error> {
let start_time = Instant::now();

let rows = sqlx::query(
Expand Down Expand Up @@ -67,7 +67,7 @@ pub async fn select_mod_log(
user_id,
moderator_id,
action_type,
created_at.to_string(),
created_at,
reason,
));
}
Expand Down
63 changes: 63 additions & 0 deletions src/utilities/on_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use tracing::error;

use crate::FrameworkError;

use super::messages;

pub async fn on_error(error: FrameworkError<'_>) {
match error {
FrameworkError::Setup { .. } | FrameworkError::EventHandler { .. } => {}
FrameworkError::Command { ctx, .. } => {
let reply =
messages::error_reply("Oh no! There's a problem in executing this command.", true);
if let Err(why) = ctx.send(reply).await {
if why.to_string().contains("40060") {
// Interaction has already been acknowledged.
return;
}

error!("Couldn't send reply: {why:?}");
}
}
FrameworkError::CommandPanic { ctx, .. } => {
let reply = messages::error_reply(
"Oh no! A panic occurred whilst executing this command.",
true,
);
if let Err(why) = ctx.send(reply).await {
error!("Couldn't send reply: {:?}", why);
}
}
FrameworkError::ArgumentParse { .. }
| poise::FrameworkError::CommandStructureMismatch { .. } => {}
FrameworkError::MissingBotPermissions {
missing_permissions,
ctx,
..
} => {
let permissions = missing_permissions
.iter()
.map(|permission| permission.to_string())
.collect::<Vec<String>>()
.join(", ");

let reply = messages::error_reply(
format!("Oh no! I'm missing the following permission(s): `{permissions}`"),
true,
);
if let Err(why) = ctx.send(reply).await {
error!("Couldn't send reply: {:?}", why);
}
}
FrameworkError::NsfwOnly { ctx, .. } => {
let reply = messages::error_reply(
"Sorry, but I can only execute this command in a NSFW channel.",
true,
);
if let Err(why) = ctx.send(reply).await {
error!("Couldn't send reply: {:?}", why);
}
}
_ => {}
}
}

0 comments on commit ddac02c

Please sign in to comment.