mirror of
https://github.com/supleed2/nanobot.git
synced 2024-12-22 14:15:51 +00:00
Split cmds.rs
into submodules
This commit is contained in:
parent
8e40216174
commit
497b4576bc
803
src/cmds.rs
803
src/cmds.rs
|
@ -1,803 +0,0 @@
|
|||
use crate::{db, ACtx, Data, Error, ManualMember, Member, PendingMember};
|
||||
use poise::serenity_prelude as serenity;
|
||||
use poise::Modal;
|
||||
|
||||
/// Buttons to (de-)register application commands globally or by guild
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(prefix_command, owners_only)]
|
||||
pub(crate) async fn cmds(ctx: poise::Context<'_, Data, Error>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send (customisable) verification introduction message in specified channel
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn setup(
|
||||
ctx: ACtx<'_>,
|
||||
#[description = "Channel to send verification introduction message in"]
|
||||
#[channel_types("Text", "News")]
|
||||
channel: serenity::GuildChannel,
|
||||
) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct Setup {
|
||||
#[name = "Contents of the verification intro message"]
|
||||
#[placeholder = "Welcome... Click the \"Begin\" button below to start verification"]
|
||||
#[paragraph]
|
||||
#[max_length = 500]
|
||||
message: String,
|
||||
#[name = "Emoji for the start button"]
|
||||
#[placeholder = "🚀"]
|
||||
#[max_length = 4]
|
||||
emoji: Option<String>,
|
||||
#[name = "Label for the start button"]
|
||||
#[placeholder = "Begin"]
|
||||
#[max_length = 80]
|
||||
text: Option<String>,
|
||||
}
|
||||
|
||||
tracing::info!("{} {}", ctx.author().name, channel.name());
|
||||
|
||||
if let Some(Setup {
|
||||
message,
|
||||
emoji,
|
||||
text,
|
||||
}) = Setup::execute(ctx).await?
|
||||
{
|
||||
ctx.say(format!("Sending intro message in {channel}"))
|
||||
.await?;
|
||||
let emoji = emoji.unwrap_or_default().chars().next().unwrap_or('🚀');
|
||||
channel
|
||||
.send_message(ctx.http(), |m| {
|
||||
m.content(message).components(|c| {
|
||||
c.create_action_row(|a| {
|
||||
a.create_button(|b| {
|
||||
b.style(serenity::ButtonStyle::Secondary)
|
||||
.emoji('📖')
|
||||
.label("More info")
|
||||
.custom_id("info")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(serenity::ButtonStyle::Primary)
|
||||
.emoji(emoji)
|
||||
.label(text.unwrap_or("Begin".to_string()))
|
||||
.custom_id("start")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say("Modal timed out, try again...").await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the number of members in the members table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn count_members(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let count = db::count_members(&ctx.data().db).await?;
|
||||
ctx.say(format!("There are {count} entries in the members table"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_member(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
remove_roles: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
if db::delete_member_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
if remove_roles.unwrap_or(true) {
|
||||
let mut m = id.clone();
|
||||
crate::verify::remove_role(ctx.serenity_context(), &mut m, ctx.data().member).await?;
|
||||
crate::verify::remove_role(ctx.serenity_context(), &mut m, ctx.data().fresher).await?;
|
||||
}
|
||||
ctx.say(format!("Successfully deleted member info for {id}"))
|
||||
.await?
|
||||
} else {
|
||||
ctx.say(format!("Failed to delete member info for {id}"))
|
||||
.await?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all members in members table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_all_members(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct Confirm {
|
||||
#[name = "This will output the members db as text"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(Confirm { confirm }) = Confirm::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let members = db::get_all_members(&ctx.data().db).await?;
|
||||
match tokio::fs::write("members.rs", format!("{members:#?}")).await {
|
||||
Ok(()) => {
|
||||
ctx.say("Sending members db data in followup message")
|
||||
.await?;
|
||||
ctx.channel_id()
|
||||
.send_files(&ctx.http(), vec!["members.rs"], |cm| {
|
||||
cm.content("File: members db")
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
ctx.say("Failed to create members db file").await?;
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file("members.rs").await;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping members db output").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Unreachable, used to create get_member command folder
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands(
|
||||
"get_member_by_id",
|
||||
"get_member_by_shortcode",
|
||||
"get_member_by_nickname",
|
||||
"get_member_by_realname",
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn get_member(_ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// Get member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "id")]
|
||||
pub(crate) async fn get_member_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
match db::get_member_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!("Member info for {id}:\n```rust\n{m:#?}\n```"))
|
||||
.await?
|
||||
}
|
||||
None => ctx.say(format!("No member entry found for {id}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get member info by Shortcode
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "shortcode")]
|
||||
pub(crate) async fn get_member_by_shortcode(ctx: ACtx<'_>, shortcode: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {shortcode}", ctx.author().name);
|
||||
match db::get_member_by_shortcode(&ctx.data().db, &shortcode).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!(
|
||||
"Member info for shortcode {shortcode}:\n```rust\n{m:#?}\n```"
|
||||
))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.say(format!("No entry found for shortcode {shortcode}"))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get member info by Nickname
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "nick")]
|
||||
pub(crate) async fn get_member_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {nickname}", ctx.author().name);
|
||||
match db::get_member_by_nickname(&ctx.data().db, &nickname).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!(
|
||||
"Member info for nickname {nickname}:\n```rust\n{m:#?}\n```"
|
||||
))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.say(format!("No entry found for nickname {nickname}",))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get member info by Real Name
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "name")]
|
||||
pub(crate) async fn get_member_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {realname}", ctx.author().name);
|
||||
match db::get_member_by_realname(&ctx.data().db, &realname).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!(
|
||||
"Member info for realname {realname}:\n```rust\n{m:#?}\n```"
|
||||
))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.say(format!("No entry found for realname {realname}",))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a member to the members table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn add_member(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
nickname: String,
|
||||
realname: String,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
"{} {}, {shortcode}, {realname}, {nickname}",
|
||||
ctx.author().name,
|
||||
id.user.name,
|
||||
);
|
||||
db::insert_member(
|
||||
&ctx.data().db,
|
||||
Member {
|
||||
discord_id: id.user.id.into(),
|
||||
shortcode,
|
||||
nickname,
|
||||
realname,
|
||||
fresher,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let mut m = id.clone();
|
||||
crate::verify::apply_role(ctx.serenity_context(), &mut m, ctx.data().member).await?;
|
||||
if fresher {
|
||||
crate::verify::apply_role(ctx.serenity_context(), &mut m, ctx.data().fresher).await?;
|
||||
}
|
||||
ctx.say(format!("Member added: {id}")).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add member to members table from pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn insert_member_from_pending(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
nickname: String,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
match db::insert_member_from_pending(&ctx.data().db, id.user.id.into(), &nickname, fresher)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
ctx.say(format!("Member moved from pending to members table: {id}"))
|
||||
.await?
|
||||
}
|
||||
Err(e) => ctx.say(format!("Error: {e}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add member to members table from manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn insert_member_from_manual(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
match db::insert_member_from_manual(&ctx.data().db, id.user.id.into()).await {
|
||||
Ok(()) => {
|
||||
ctx.say(format!("Member moved from manual to members table: {id}"))
|
||||
.await?
|
||||
}
|
||||
Err(e) => ctx.say(format!("Error: {e}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unreachable, used to create edit_member command folder
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands(
|
||||
"edit_member_shortcode",
|
||||
"edit_member_nickname",
|
||||
"edit_member_realname",
|
||||
"edit_member_fresher",
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn edit_member(_ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// Edit member Shortcode
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "shortcode")]
|
||||
pub(crate) async fn edit_member_shortcode(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {shortcode}", ctx.author().name);
|
||||
if db::edit_member_shortcode(&ctx.data().db, id.user.id.into(), &shortcode).await? {
|
||||
ctx.say(format!("{id} Shortcode updated to {shortcode}"))
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Shortcode for {id}"))
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Edit member Nickname
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "nick")]
|
||||
pub(crate) async fn edit_member_nickname(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
nickname: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {nickname}", ctx.author().name);
|
||||
if db::edit_member_nickname(&ctx.data().db, id.user.id.into(), &nickname).await? {
|
||||
ctx.say(format!("{id} Nick updated to {nickname}")).await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Nick for {id}")).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Edit member Real Name
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "name")]
|
||||
pub(crate) async fn edit_member_realname(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
realname: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {realname}", ctx.author().name);
|
||||
if db::edit_member_realname(&ctx.data().db, id.user.id.into(), &realname).await? {
|
||||
ctx.say(format!("{id} Name updated to {realname}")).await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Name for {id}")).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Edit member fresher status
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "fresher")]
|
||||
pub(crate) async fn edit_member_fresher(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {} {fresher}", ctx.author().name, id.user.name,);
|
||||
if db::edit_member_fresher(&ctx.data().db, id.user.id.into(), fresher).await? {
|
||||
ctx.say(format!("{id} Fresher status updated to {fresher}"))
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Fresher status for {id}"))
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set all members to non-freshers
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn set_members_non_fresher(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let updated = db::set_members_non_fresher(&ctx.data().db).await?;
|
||||
ctx.say(format!("{updated} updated to non-fresher, removing roles"))
|
||||
.await?;
|
||||
for mut m in ctx.data().server.members(ctx.http(), None, None).await? {
|
||||
let _ = m.remove_role(ctx.http(), ctx.data().fresher).await;
|
||||
}
|
||||
ctx.say("Roles removed").await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the number of pending members in the pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn count_pending(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let count = db::count_pending(&ctx.data().db).await?;
|
||||
ctx.say(format!("There are {count} entries in the pending table"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete pending member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_pending(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
if db::delete_pending_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
ctx.say(format!("Successfully deleted pending member info for {id}"))
|
||||
.await?
|
||||
} else {
|
||||
ctx.say(format!("Failed to delete pending member info for {id}"))
|
||||
.await?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all pending members in pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_all_pending(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmPending {
|
||||
#[name = "This will output the pending db as text"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmPending { confirm }) = ConfirmPending::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let pending = db::get_all_pending(&ctx.data().db).await?;
|
||||
match tokio::fs::write("pending.rs", format!("{pending:#?}")).await {
|
||||
Ok(()) => {
|
||||
ctx.say("Sending pending db data in followup message")
|
||||
.await?;
|
||||
ctx.channel_id()
|
||||
.send_files(&ctx.http(), vec!["pending.rs"], |cm| {
|
||||
cm.content("File: pending db")
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
ctx.say("Failed to create pending db file").await?;
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file("pending.rs").await;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping pending db output").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get pending member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_pending(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name);
|
||||
match db::get_pending_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(p) => {
|
||||
ctx.say(format!("Pending info for {id}:\n```rust\n{p:#?}\n```"))
|
||||
.await?
|
||||
}
|
||||
None => ctx.say(format!("No pending entry found for {id}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add pending member to pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn add_pending(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
realname: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
"{} {}, {shortcode}, {realname}",
|
||||
ctx.author().name,
|
||||
id.user.name,
|
||||
);
|
||||
db::insert_pending(
|
||||
&ctx.data().db,
|
||||
PendingMember {
|
||||
discord_id: id.user.id.into(),
|
||||
shortcode,
|
||||
realname,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
ctx.say(format!("Pending member added: {id}")).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all pending members in pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_all_pending(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmPurgePending {
|
||||
#[name = "This will wipe the pending db"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmPurgePending { confirm }) = ConfirmPurgePending::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let deleted = db::delete_all_pending(&ctx.data().db).await?;
|
||||
ctx.say(format!("Deleted {deleted} entries from the pending db"))
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping pending db purge").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the number of manual members in the manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn count_manual(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let count = db::count_manual(&ctx.data().db).await?;
|
||||
ctx.say(format!("There are {count} entries in the manual table"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete manual member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_manual(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
if db::delete_manual_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
ctx.say(format!("Successfully deleted manual member info for {id}"))
|
||||
.await?
|
||||
} else {
|
||||
ctx.say(format!("Failed to delete manual member info for {id}"))
|
||||
.await?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all manual members in manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_all_manual(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmManual {
|
||||
#[name = "This will output the manual db as text"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmManual { confirm }) = ConfirmManual::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let manual = db::get_all_manual(&ctx.data().db).await?;
|
||||
match tokio::fs::write("manual.rs", format!("{manual:#?}")).await {
|
||||
Ok(()) => {
|
||||
ctx.say("Sending manual db data in followup message")
|
||||
.await?;
|
||||
ctx.channel_id()
|
||||
.send_files(&ctx.http(), vec!["manual.rs"], |cm| {
|
||||
cm.content("File: manual db")
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
ctx.say("Failed to create manual db file").await?;
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file("manual.rs").await;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping manual db output").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get manual member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_manual(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name);
|
||||
match db::get_manual_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!("Manual info for {id}:\n```rust\n{m:#?}\n```"))
|
||||
.await?
|
||||
}
|
||||
None => ctx.say(format!("No manual entry found for {id}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add manual member to manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn add_manual(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
nickname: String,
|
||||
realname: String,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
"{} {}, {shortcode}, {realname}, {nickname}",
|
||||
ctx.author().name,
|
||||
id.user.name,
|
||||
);
|
||||
db::insert_manual(
|
||||
&ctx.data().db,
|
||||
ManualMember {
|
||||
discord_id: id.user.id.into(),
|
||||
shortcode,
|
||||
nickname,
|
||||
realname,
|
||||
fresher,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
ctx.say(format!("Manual member added: {id}")).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all manual members in manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_all_manual(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmPurgeManual {
|
||||
#[name = "This will wipe the manual db"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmPurgeManual { confirm }) = ConfirmPurgeManual::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let deleted = db::delete_all_manual(&ctx.data().db).await?;
|
||||
ctx.say(format!("Deleted {deleted} entries from the manual db"))
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping manual db purge").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Unreachable, used to create whois command folder
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands("whois_by_id", "whois_by_nickname", "whois_by_realname")
|
||||
)]
|
||||
pub(crate) async fn whois(_ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// (Public) Get member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "id")]
|
||||
pub(crate) async fn whois_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name);
|
||||
match db::get_member_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(m) => {
|
||||
ctx.send(|c| c.content(format!("{id}: {}", m.nickname)).ephemeral(true))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("No member entry found for {id}"))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// (Public) Get member info by Nickname (Exact)
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "nick")]
|
||||
pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {nickname}", ctx.author().name);
|
||||
if let Some(m) = db::get_member_by_nickname(&ctx.data().db, &nickname).await? {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("{nickname}: <@{}>", m.discord_id))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
let members = db::get_member_by_nickname_fuzzy(&ctx.data().db, &nickname, 3).await?;
|
||||
if members.is_empty() {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("No member entry found for nickname {nickname}"))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.send(|c| {
|
||||
c.ephemeral(true).content(format!(
|
||||
"Possible matches for {nickname}: {}",
|
||||
members
|
||||
.iter()
|
||||
.map(|m| format!(" <@{}>", m.discord_id))
|
||||
.collect::<String>()
|
||||
))
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// (Public) Get member info by Real Name (Exact)
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "name")]
|
||||
pub(crate) async fn whois_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {realname}", ctx.author().name);
|
||||
if let Some(m) = db::get_member_by_realname(&ctx.data().db, &realname).await? {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("{realname}: <@{}>", m.discord_id))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
let members = db::get_member_by_realname_fuzzy(&ctx.data().db, &realname, 3).await?;
|
||||
if members.is_empty() {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("No member entry found for realname {realname}"))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.send(|c| {
|
||||
c.ephemeral(true).content(format!(
|
||||
"Possible matches for {realname}: {}",
|
||||
members
|
||||
.iter()
|
||||
.map(|m| format!(" <@{}>", m.discord_id))
|
||||
.collect::<String>()
|
||||
))
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
103
src/cmds/edit_members.rs
Normal file
103
src/cmds/edit_members.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use crate::{db, ACtx, Error};
|
||||
use poise::serenity_prelude as serenity;
|
||||
|
||||
/// Unreachable, used to create edit_member command folder
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands(
|
||||
"edit_member_shortcode",
|
||||
"edit_member_nickname",
|
||||
"edit_member_realname",
|
||||
"edit_member_fresher",
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn edit_member(_ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// Edit member Shortcode
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "shortcode")]
|
||||
pub(crate) async fn edit_member_shortcode(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {shortcode}", ctx.author().name);
|
||||
if db::edit_member_shortcode(&ctx.data().db, id.user.id.into(), &shortcode).await? {
|
||||
ctx.say(format!("{id} Shortcode updated to {shortcode}"))
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Shortcode for {id}"))
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Edit member Nickname
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "nick")]
|
||||
pub(crate) async fn edit_member_nickname(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
nickname: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {nickname}", ctx.author().name);
|
||||
if db::edit_member_nickname(&ctx.data().db, id.user.id.into(), &nickname).await? {
|
||||
ctx.say(format!("{id} Nick updated to {nickname}")).await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Nick for {id}")).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Edit member Real Name
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "name")]
|
||||
pub(crate) async fn edit_member_realname(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
realname: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {realname}", ctx.author().name);
|
||||
if db::edit_member_realname(&ctx.data().db, id.user.id.into(), &realname).await? {
|
||||
ctx.say(format!("{id} Name updated to {realname}")).await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Name for {id}")).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Edit member fresher status
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "fresher")]
|
||||
pub(crate) async fn edit_member_fresher(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {} {fresher}", ctx.author().name, id.user.name,);
|
||||
if db::edit_member_fresher(&ctx.data().db, id.user.id.into(), fresher).await? {
|
||||
ctx.say(format!("{id} Fresher status updated to {fresher}"))
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say(format!("Failed to update Fresher status for {id}"))
|
||||
.await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set all members to non-freshers
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn set_members_non_fresher(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let updated = db::set_members_non_fresher(&ctx.data().db).await?;
|
||||
ctx.say(format!("{updated} updated to non-fresher, removing roles"))
|
||||
.await?;
|
||||
for mut m in ctx.data().server.members(ctx.http(), None, None).await? {
|
||||
let _ = m.remove_role(ctx.http(), ctx.data().fresher).await;
|
||||
}
|
||||
ctx.say("Roles removed").await?;
|
||||
Ok(())
|
||||
}
|
147
src/cmds/manual.rs
Normal file
147
src/cmds/manual.rs
Normal file
|
@ -0,0 +1,147 @@
|
|||
use crate::{db, ACtx, Error, ManualMember};
|
||||
use poise::serenity_prelude as serenity;
|
||||
use poise::Modal;
|
||||
|
||||
/// Get the number of manual members in the manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn count_manual(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let count = db::count_manual(&ctx.data().db).await?;
|
||||
ctx.say(format!("There are {count} entries in the manual table"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete manual member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_manual(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
if db::delete_manual_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
ctx.say(format!("Successfully deleted manual member info for {id}"))
|
||||
.await?
|
||||
} else {
|
||||
ctx.say(format!("Failed to delete manual member info for {id}"))
|
||||
.await?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all manual members in manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_all_manual(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmManual {
|
||||
#[name = "This will output the manual db as text"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmManual { confirm }) = ConfirmManual::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let manual = db::get_all_manual(&ctx.data().db).await?;
|
||||
match tokio::fs::write("manual.rs", format!("{manual:#?}")).await {
|
||||
Ok(()) => {
|
||||
ctx.say("Sending manual db data in followup message")
|
||||
.await?;
|
||||
ctx.channel_id()
|
||||
.send_files(&ctx.http(), vec!["manual.rs"], |cm| {
|
||||
cm.content("File: manual db")
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
ctx.say("Failed to create manual db file").await?;
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file("manual.rs").await;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping manual db output").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get manual member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_manual(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name);
|
||||
match db::get_manual_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!("Manual info for {id}:\n```rust\n{m:#?}\n```"))
|
||||
.await?
|
||||
}
|
||||
None => ctx.say(format!("No manual entry found for {id}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add manual member to manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn add_manual(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
nickname: String,
|
||||
realname: String,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
"{} {}, {shortcode}, {realname}, {nickname}",
|
||||
ctx.author().name,
|
||||
id.user.name,
|
||||
);
|
||||
db::insert_manual(
|
||||
&ctx.data().db,
|
||||
ManualMember {
|
||||
discord_id: id.user.id.into(),
|
||||
shortcode,
|
||||
nickname,
|
||||
realname,
|
||||
fresher,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
ctx.say(format!("Manual member added: {id}")).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all manual members in manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_all_manual(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmPurgeManual {
|
||||
#[name = "This will wipe the manual db"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmPurgeManual { confirm }) = ConfirmPurgeManual::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let deleted = db::delete_all_manual(&ctx.data().db).await?;
|
||||
ctx.say(format!("Deleted {deleted} entries from the manual db"))
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping manual db purge").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
246
src/cmds/members.rs
Normal file
246
src/cmds/members.rs
Normal file
|
@ -0,0 +1,246 @@
|
|||
use crate::{db, ACtx, Error, Member};
|
||||
use poise::serenity_prelude as serenity;
|
||||
use poise::Modal;
|
||||
|
||||
/// Get the number of members in the members table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn count_members(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let count = db::count_members(&ctx.data().db).await?;
|
||||
ctx.say(format!("There are {count} entries in the members table"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_member(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
remove_roles: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
if db::delete_member_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
if remove_roles.unwrap_or(true) {
|
||||
let mut m = id.clone();
|
||||
crate::verify::remove_role(ctx.serenity_context(), &mut m, ctx.data().member).await?;
|
||||
crate::verify::remove_role(ctx.serenity_context(), &mut m, ctx.data().fresher).await?;
|
||||
}
|
||||
ctx.say(format!("Successfully deleted member info for {id}"))
|
||||
.await?
|
||||
} else {
|
||||
ctx.say(format!("Failed to delete member info for {id}"))
|
||||
.await?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all members in members table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_all_members(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct Confirm {
|
||||
#[name = "This will output the members db as text"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(Confirm { confirm }) = Confirm::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let members = db::get_all_members(&ctx.data().db).await?;
|
||||
match tokio::fs::write("members.rs", format!("{members:#?}")).await {
|
||||
Ok(()) => {
|
||||
ctx.say("Sending members db data in followup message")
|
||||
.await?;
|
||||
ctx.channel_id()
|
||||
.send_files(&ctx.http(), vec!["members.rs"], |cm| {
|
||||
cm.content("File: members db")
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
ctx.say("Failed to create members db file").await?;
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file("members.rs").await;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping members db output").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Unreachable, used to create get_member command folder
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands(
|
||||
"get_member_by_id",
|
||||
"get_member_by_shortcode",
|
||||
"get_member_by_nickname",
|
||||
"get_member_by_realname",
|
||||
)
|
||||
)]
|
||||
pub(crate) async fn get_member(_ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// Get member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "id")]
|
||||
pub(crate) async fn get_member_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
match db::get_member_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!("Member info for {id}:\n```rust\n{m:#?}\n```"))
|
||||
.await?
|
||||
}
|
||||
None => ctx.say(format!("No member entry found for {id}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get member info by Shortcode
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "shortcode")]
|
||||
pub(crate) async fn get_member_by_shortcode(ctx: ACtx<'_>, shortcode: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {shortcode}", ctx.author().name);
|
||||
match db::get_member_by_shortcode(&ctx.data().db, &shortcode).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!(
|
||||
"Member info for shortcode {shortcode}:\n```rust\n{m:#?}\n```"
|
||||
))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.say(format!("No entry found for shortcode {shortcode}"))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get member info by Nickname
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "nick")]
|
||||
pub(crate) async fn get_member_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {nickname}", ctx.author().name);
|
||||
match db::get_member_by_nickname(&ctx.data().db, &nickname).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!(
|
||||
"Member info for nickname {nickname}:\n```rust\n{m:#?}\n```"
|
||||
))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.say(format!("No entry found for nickname {nickname}",))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get member info by Real Name
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "name")]
|
||||
pub(crate) async fn get_member_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {realname}", ctx.author().name);
|
||||
match db::get_member_by_realname(&ctx.data().db, &realname).await? {
|
||||
Some(m) => {
|
||||
ctx.say(format!(
|
||||
"Member info for realname {realname}:\n```rust\n{m:#?}\n```"
|
||||
))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.say(format!("No entry found for realname {realname}",))
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add a member to the members table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn add_member(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
nickname: String,
|
||||
realname: String,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
"{} {}, {shortcode}, {realname}, {nickname}",
|
||||
ctx.author().name,
|
||||
id.user.name,
|
||||
);
|
||||
db::insert_member(
|
||||
&ctx.data().db,
|
||||
Member {
|
||||
discord_id: id.user.id.into(),
|
||||
shortcode,
|
||||
nickname,
|
||||
realname,
|
||||
fresher,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let mut m = id.clone();
|
||||
crate::verify::apply_role(ctx.serenity_context(), &mut m, ctx.data().member).await?;
|
||||
if fresher {
|
||||
crate::verify::apply_role(ctx.serenity_context(), &mut m, ctx.data().fresher).await?;
|
||||
}
|
||||
ctx.say(format!("Member added: {id}")).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add member to members table from pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn insert_member_from_pending(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
nickname: String,
|
||||
fresher: bool,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
match db::insert_member_from_pending(&ctx.data().db, id.user.id.into(), &nickname, fresher)
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
ctx.say(format!("Member moved from pending to members table: {id}"))
|
||||
.await?
|
||||
}
|
||||
Err(e) => ctx.say(format!("Error: {e}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add member to members table from manual table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn insert_member_from_manual(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
match db::insert_member_from_manual(&ctx.data().db, id.user.id.into()).await {
|
||||
Ok(()) => {
|
||||
ctx.say(format!("Member moved from manual to members table: {id}"))
|
||||
.await?
|
||||
}
|
||||
Err(e) => ctx.say(format!("Error: {e}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
90
src/cmds/mod.rs
Normal file
90
src/cmds/mod.rs
Normal file
|
@ -0,0 +1,90 @@
|
|||
use crate::{ACtx, Data, Error};
|
||||
use poise::serenity_prelude as serenity;
|
||||
use poise::Modal;
|
||||
|
||||
pub(crate) mod members;
|
||||
pub(crate) use members::*;
|
||||
|
||||
pub(crate) mod pending;
|
||||
pub(crate) use pending::*;
|
||||
|
||||
pub(crate) mod manual;
|
||||
pub(crate) use manual::*;
|
||||
|
||||
pub(crate) mod edit_members;
|
||||
pub(crate) use edit_members::*;
|
||||
|
||||
pub(crate) mod whois;
|
||||
pub(crate) use whois::*;
|
||||
|
||||
/// Buttons to (de-)register application commands globally or by guild
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(prefix_command, owners_only)]
|
||||
pub(crate) async fn cmds(ctx: poise::Context<'_, Data, Error>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send (customisable) verification introduction message in specified channel
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn setup(
|
||||
ctx: ACtx<'_>,
|
||||
#[description = "Channel to send verification introduction message in"]
|
||||
#[channel_types("Text", "News")]
|
||||
channel: serenity::GuildChannel,
|
||||
) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct Setup {
|
||||
#[name = "Contents of the verification intro message"]
|
||||
#[placeholder = "Welcome... Click the \"Begin\" button below to start verification"]
|
||||
#[paragraph]
|
||||
#[max_length = 500]
|
||||
message: String,
|
||||
#[name = "Emoji for the start button"]
|
||||
#[placeholder = "🚀"]
|
||||
#[max_length = 4]
|
||||
emoji: Option<String>,
|
||||
#[name = "Label for the start button"]
|
||||
#[placeholder = "Begin"]
|
||||
#[max_length = 80]
|
||||
text: Option<String>,
|
||||
}
|
||||
|
||||
tracing::info!("{} {}", ctx.author().name, channel.name());
|
||||
|
||||
if let Some(Setup {
|
||||
message,
|
||||
emoji,
|
||||
text,
|
||||
}) = Setup::execute(ctx).await?
|
||||
{
|
||||
ctx.say(format!("Sending intro message in {channel}"))
|
||||
.await?;
|
||||
let emoji = emoji.unwrap_or_default().chars().next().unwrap_or('🚀');
|
||||
channel
|
||||
.send_message(ctx.http(), |m| {
|
||||
m.content(message).components(|c| {
|
||||
c.create_action_row(|a| {
|
||||
a.create_button(|b| {
|
||||
b.style(serenity::ButtonStyle::Secondary)
|
||||
.emoji('📖')
|
||||
.label("More info")
|
||||
.custom_id("info")
|
||||
})
|
||||
.create_button(|b| {
|
||||
b.style(serenity::ButtonStyle::Primary)
|
||||
.emoji(emoji)
|
||||
.label(text.unwrap_or("Begin".to_string()))
|
||||
.custom_id("start")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say("Modal timed out, try again...").await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
143
src/cmds/pending.rs
Normal file
143
src/cmds/pending.rs
Normal file
|
@ -0,0 +1,143 @@
|
|||
use crate::{db, ACtx, Error, PendingMember};
|
||||
use poise::serenity_prelude as serenity;
|
||||
use poise::Modal;
|
||||
|
||||
/// Get the number of pending members in the pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn count_pending(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
let count = db::count_pending(&ctx.data().db).await?;
|
||||
ctx.say(format!("There are {count} entries in the pending table"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete pending member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_pending(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name,);
|
||||
if db::delete_pending_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
ctx.say(format!("Successfully deleted pending member info for {id}"))
|
||||
.await?
|
||||
} else {
|
||||
ctx.say(format!("Failed to delete pending member info for {id}"))
|
||||
.await?
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Print all pending members in pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_all_pending(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmPending {
|
||||
#[name = "This will output the pending db as text"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmPending { confirm }) = ConfirmPending::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let pending = db::get_all_pending(&ctx.data().db).await?;
|
||||
match tokio::fs::write("pending.rs", format!("{pending:#?}")).await {
|
||||
Ok(()) => {
|
||||
ctx.say("Sending pending db data in followup message")
|
||||
.await?;
|
||||
ctx.channel_id()
|
||||
.send_files(&ctx.http(), vec!["pending.rs"], |cm| {
|
||||
cm.content("File: pending db")
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("{e}");
|
||||
ctx.say("Failed to create pending db file").await?;
|
||||
}
|
||||
}
|
||||
let _ = tokio::fs::remove_file("pending.rs").await;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping pending db output").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get pending member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn get_pending(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name);
|
||||
match db::get_pending_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(p) => {
|
||||
ctx.say(format!("Pending info for {id}:\n```rust\n{p:#?}\n```"))
|
||||
.await?
|
||||
}
|
||||
None => ctx.say(format!("No pending entry found for {id}")).await?,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Manually add pending member to pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn add_pending(
|
||||
ctx: ACtx<'_>,
|
||||
id: serenity::Member,
|
||||
shortcode: String,
|
||||
realname: String,
|
||||
) -> Result<(), Error> {
|
||||
tracing::info!(
|
||||
"{} {}, {shortcode}, {realname}",
|
||||
ctx.author().name,
|
||||
id.user.name,
|
||||
);
|
||||
db::insert_pending(
|
||||
&ctx.data().db,
|
||||
PendingMember {
|
||||
discord_id: id.user.id.into(),
|
||||
shortcode,
|
||||
realname,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
ctx.say(format!("Pending member added: {id}")).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete all pending members in pending table
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command)]
|
||||
pub(crate) async fn delete_all_pending(ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
#[derive(Modal)]
|
||||
struct ConfirmPurgePending {
|
||||
#[name = "This will wipe the pending db"]
|
||||
#[placeholder = "yes"]
|
||||
confirm: String,
|
||||
}
|
||||
|
||||
tracing::info!("{}", ctx.author().name);
|
||||
|
||||
if let Some(ConfirmPurgePending { confirm }) = ConfirmPurgePending::execute(ctx).await? {
|
||||
if confirm.to_lowercase().contains("yes") {
|
||||
let deleted = db::delete_all_pending(&ctx.data().db).await?;
|
||||
ctx.say(format!("Deleted {deleted} entries from the pending db"))
|
||||
.await?;
|
||||
Ok(())
|
||||
} else {
|
||||
ctx.say("Skipping pending db purge").await?;
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
ctx.say("Timed out").await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
102
src/cmds/whois.rs
Normal file
102
src/cmds/whois.rs
Normal file
|
@ -0,0 +1,102 @@
|
|||
use crate::{db, ACtx, Error};
|
||||
use poise::serenity_prelude as serenity;
|
||||
|
||||
/// Unreachable, used to create whois command folder
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands("whois_by_id", "whois_by_nickname", "whois_by_realname")
|
||||
)]
|
||||
pub(crate) async fn whois(_ctx: ACtx<'_>) -> Result<(), Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
/// (Public) Get member info by Discord ID
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "id")]
|
||||
pub(crate) async fn whois_by_id(ctx: ACtx<'_>, id: serenity::Member) -> Result<(), Error> {
|
||||
tracing::info!("{} {}", ctx.author().name, id.user.name);
|
||||
match db::get_member_by_id(&ctx.data().db, id.user.id.into()).await? {
|
||||
Some(m) => {
|
||||
ctx.send(|c| c.content(format!("{id}: {}", m.nickname)).ephemeral(true))
|
||||
.await?
|
||||
}
|
||||
None => {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("No member entry found for {id}"))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// (Public) Get member info by Nickname (Exact)
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "nick")]
|
||||
pub(crate) async fn whois_by_nickname(ctx: ACtx<'_>, nickname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {nickname}", ctx.author().name);
|
||||
if let Some(m) = db::get_member_by_nickname(&ctx.data().db, &nickname).await? {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("{nickname}: <@{}>", m.discord_id))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
let members = db::get_member_by_nickname_fuzzy(&ctx.data().db, &nickname, 3).await?;
|
||||
if members.is_empty() {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("No member entry found for nickname {nickname}"))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.send(|c| {
|
||||
c.ephemeral(true).content(format!(
|
||||
"Possible matches for {nickname}: {}",
|
||||
members
|
||||
.iter()
|
||||
.map(|m| format!(" <@{}>", m.discord_id))
|
||||
.collect::<String>()
|
||||
))
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// (Public) Get member info by Real Name (Exact)
|
||||
#[tracing::instrument(skip_all)]
|
||||
#[poise::command(slash_command, rename = "name")]
|
||||
pub(crate) async fn whois_by_realname(ctx: ACtx<'_>, realname: String) -> Result<(), Error> {
|
||||
tracing::info!("{} {realname}", ctx.author().name);
|
||||
if let Some(m) = db::get_member_by_realname(&ctx.data().db, &realname).await? {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("{realname}: <@{}>", m.discord_id))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
let members = db::get_member_by_realname_fuzzy(&ctx.data().db, &realname, 3).await?;
|
||||
if members.is_empty() {
|
||||
ctx.send(|c| {
|
||||
c.content(format!("No member entry found for realname {realname}"))
|
||||
.ephemeral(true)
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.send(|c| {
|
||||
c.ephemeral(true).content(format!(
|
||||
"Possible matches for {realname}: {}",
|
||||
members
|
||||
.iter()
|
||||
.map(|m| format!(" <@{}>", m.discord_id))
|
||||
.collect::<String>()
|
||||
))
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in a new issue