new role management commands, bug fix, table restructuring, better output

This commit is contained in:
2024-12-08 12:15:54 -05:00
parent 8f764a2270
commit 0def108010
10 changed files with 97 additions and 35 deletions

3
Cargo.lock generated
View File

@@ -204,7 +204,6 @@ dependencies = [
name = "bot"
version = "0.1.0"
dependencies = [
"chrono",
"clap",
"dotenv",
"hex_color",
@@ -294,10 +293,8 @@ checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-targets 0.52.6",
]

View File

@@ -20,8 +20,15 @@ pub async fn color(ctx: Context<'_>, color: String) -> Result<(), Error> {
};
if let Some(guild) = ctx.guild_id() {
let role = match super::get_user_role(ctx, ctx.author().id, guild, db).await? {
Some(role) => role,
match super::get_user_role(ctx.author().id, guild, db).await? {
Some(role) => {
guild.edit_role(ctx, role, EditRole::new().colour(Color::from_rgb(color.r, color.g, color.b))).await?;
let role = guild.role(ctx, role).await?;
ctx.reply(format!("{}'s color has been updated!", role)).await?;
Ok(())
},
None => {
let role = guild.create_role(ctx,
EditRole::new()
@@ -40,13 +47,7 @@ pub async fn color(ctx: Context<'_>, color: String) -> Result<(), Error> {
ctx.reply(format!("You have been given the {} role!", role)).await?;
return Ok(());
}
};
guild.edit_role(ctx, role, EditRole::new().colour(Color::from_rgb(color.r, color.g, color.b))).await?;
ctx.reply("Your custom role's color has been updated!").await?;
Ok(())
}
} else {
ctx.reply("This command must be run within a server.").await?;
Ok(())

View File

@@ -9,7 +9,7 @@ pub async fn disown(ctx: Context<'_>) -> Result<(), Error> {
let db = db.as_mut();
if let Some(guild) = ctx.guild_id() {
if let Some(role) = super::get_user_role(ctx, ctx.author().id, guild, db).await? {
if let Some(role) = super::get_user_role(ctx.author().id, guild, db).await? {
guild.delete_role(ctx, role).await?;
sqlx::query("DELETE FROM selfroles WHERE roleid = $1")

View File

@@ -8,6 +8,7 @@ mod whois;
mod color;
mod name;
mod disown;
mod remove;
#[poise::command(
prefix_command,
@@ -18,13 +19,14 @@ mod disown;
"color::color",
"name::name",
"disown::disown",
"remove::remove",
)
)]
pub async fn role(_ctx: Context<'_>) -> Result<(), Error> {
Ok(())
}
pub async fn get_user_role(_ctx: Context<'_>, user: UserId, guild: GuildId, db: &mut PgConnection) -> Result<Option<RoleId>, Error> {
pub async fn get_user_role(user: UserId, guild: GuildId, db: &mut PgConnection) -> Result<Option<RoleId>, Error> {
match sqlx::query("SELECT roleid FROM selfroles WHERE userid = $1 AND guildid = $2")
.bind(user.get() as i64)
.bind(guild.get() as i64)

View File

@@ -11,7 +11,7 @@ pub async fn name(ctx: Context<'_>, name: String) -> Result<(), Error> {
let db = db.as_mut();
if let Some(guild) = ctx.guild_id() {
let role = match super::get_user_role(ctx, ctx.author().id, guild, db).await? {
let role = match super::get_user_role(ctx.author().id, guild, db).await? {
Some(role) => role,
None => {
let role = guild.create_role(ctx, EditRole::new().name(name)).await?;
@@ -32,8 +32,9 @@ pub async fn name(ctx: Context<'_>, name: String) -> Result<(), Error> {
};
guild.edit_role(ctx, role, EditRole::new().name(name)).await?;
let role = guild.role(ctx, role).await?;
ctx.reply("Your custom role's name has been updated!").await?;
ctx.reply(format!("Your custom role's name has been updated to {}.", role)).await?;
Ok(())
} else {

View File

@@ -1,27 +1,36 @@
use crate::common::{Context, Error};
use crate::common::{self, Context, Error};
use poise::serenity_prelude as serenity;
/// Register an existing role as a user's custom role
#[poise::command(slash_command, prefix_command, required_permissions = "MANAGE_ROLES")]
pub async fn register(ctx: Context<'_>, user: Option<serenity::User>, role: serenity::Role) -> Result<(), Error> {
pub async fn register(ctx: Context<'_>, user: serenity::User, role: serenity::Role) -> Result<(), Error> {
let data = ctx.data();
let mut db = data.database.lock().await;
let db = db.as_mut();
let user = user.as_ref().unwrap_or(ctx.author());
if let Some(guild) = ctx.guild_id() {
match super::get_user_role(user.id, guild, db).await? {
Some(role) => {
common::no_ping_reply(&ctx, format!("{} already has the role {} registered.", user, role)).await?;
Ok(())
},
None => {
sqlx::query("INSERT INTO selfroles (userid, guildid, roleid) VALUES ($1, $2, $3)")
.bind(user.id.get() as i64)
.bind(guild.get() as i64)
.bind(role.id.get() as i64)
.execute(db).await?;
if let Some(guild) = ctx.guild().map(|g| g.id) {
sqlx::query("INSERT INTO selfroles (userid, roleid, guildid) VALUES ($1, $2, $3) ON CONFLICT (userid) DO UPDATE SET roleid = EXCLUDED.roleid")
.bind(user.id.get() as i64)
.bind(role.id.get() as i64)
.bind(guild.get() as i64)
.execute(db).await?;
let member = guild.member(ctx, user.id).await?;
member.add_role(ctx, role.id).await?;
ctx.reply(format!("**{}** now has **{}** set as their personal role.", user.display_name(), role.name)).await?;
common::no_ping_reply(&ctx, format!("{} now has {} set as their personal role.", user, role)).await?;
Ok(())
Ok(())
}
}
} else {
ctx.reply("This command can only be run in a guild!").await?;
Ok(())

View File

@@ -0,0 +1,39 @@
use crate::common::{self, Context, Error};
use poise::serenity_prelude as serenity;
/// force remove someone's role (this does not delete the role)
#[poise::command(slash_command, prefix_command, required_permissions = "MANAGE_ROLES")]
pub async fn remove(ctx: Context<'_>, user: serenity::User) -> Result<(), Error> {
let data = ctx.data();
let mut db = data.database.lock().await;
let db = db.as_mut();
if let Some(guild) = ctx.guild_id() {
match super::get_user_role(user.id, guild, db).await? {
Some(role) => {
sqlx::query("DELETE FROM selfroles WHERE userid = $1 AND guildid = $2")
.bind(user.id.get() as i64)
.bind(guild.get() as i64)
.execute(db).await?;
let member = guild.member(ctx, user.id).await?;
member.remove_role(ctx, role).await?;
let role = guild.role(ctx, role).await?;
common::no_ping_reply(&ctx, format!("{} has had {} removed.", user, role)).await?;
Ok(())
},
None => {
common::no_ping_reply(&ctx, format!("{} does not have a personal role to remove.", user)).await?;
Ok(())
}
}
} else {
ctx.reply("This command can only be run in a guild!").await?;
Ok(())
}
}

View File

@@ -1,5 +1,5 @@
use crate::common::{Context, Error};
use crate::common::{self, Context, Error};
use poise::serenity_prelude as serenity;
use serenity::UserId;
@@ -31,7 +31,7 @@ pub async fn whois(ctx: Context<'_>, role: serenity::Role) -> Result<(), Error>
let member = guild.member(ctx, user).await?;
ctx.reply(format!("{} owns this role.", member.display_name())).await?;
common::no_ping_reply(&ctx, format!("{} owns this role.", member)).await?;
} else {
ctx.reply("This command must be used within a server!").await?;
}

View File

@@ -1,7 +1,7 @@
use std::sync::Arc;
use tokio::sync::Mutex;
use std::collections::HashMap;
use poise::serenity_prelude::UserId;
use poise::{serenity_prelude::UserId, ReplyHandle};
use sqlx::PgConnection;
pub struct Data {
@@ -14,3 +14,15 @@ pub struct Data {
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type Context<'a> = poise::Context<'a, Data, Error>;
use poise::serenity_prelude::builder::CreateAllowedMentions;
use poise::CreateReply;
pub async fn no_ping_reply<'a>(ctx: &'a Context<'_>, text: impl Into<String>) -> Result<ReplyHandle<'a>, Error> {
Ok(ctx.send(
CreateReply::default()
.content(text.into())
.reply(true)
.allowed_mentions(CreateAllowedMentions::new())
).await?)
}

View File

@@ -82,9 +82,10 @@ async fn main() -> Result<(), Error> {
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS selfroles (
userid BIGINT,
userid BIGINT NOT NULL,
guildid BIGINT NOT NULL,
roleid BIGINT,
guildid BIGINT
UNIQUE (userid, guildid)
)
"#,
).execute(&mut database).await?;