add custom role management

This commit is contained in:
2024-12-08 01:08:33 -05:00
parent b0ec64171f
commit f4a06a7b17
11 changed files with 250 additions and 3 deletions

View File

@@ -3,7 +3,6 @@ use crate::common::{Context, Error};
use poise::serenity_prelude as serenity; use poise::serenity_prelude as serenity;
use serenity::Colour; use serenity::Colour;
// this code uses Member::permissions, which while it is a deprecated function, it doesn't actually matter // this code uses Member::permissions, which while it is a deprecated function, it doesn't actually matter
// since it is only used to display information to the user. // since it is only used to display information to the user.
#[allow(deprecated)] #[allow(deprecated)]

View File

@@ -30,8 +30,8 @@ pub async fn daily(ctx: Context<'_>) -> Result<(), Error> {
super::add_balance(id, 50, db).await?; super::add_balance(id, 50, db).await?;
ctx.reply("Added **50** credits to your account!").await?; ctx.reply("Added **50** credits to your account!").await?;
} else { } else {
let until_next_daily = Duration::from_secs(10) - daily.elapsed(); let until_next_daily = Duration::from_secs(24 * 60 * 60) - daily.elapsed();
ctx.reply(format!("Your daily will be available in {:?}.", format_duration(until_next_daily))).await?; ctx.reply(format!("Your next daily will be available in **{}**.", format_duration(until_next_daily))).await?;
} }
}, },
None => { None => {

View File

@@ -18,5 +18,6 @@ pub fn commands() -> Vec<Command<Data, Error>> {
gambling::wager::wager(), gambling::wager::wager(),
gambling::daily::daily(), gambling::daily::daily(),
eval::eval(), eval::eval(),
self_roles::role(),
] ]
} }

View File

@@ -0,0 +1,54 @@
use crate::common::{Context, Error};
use hex_color::HexColor;
use poise::serenity_prelude::{Color, EditRole};
/// Change the color of your personal role
#[poise::command(slash_command, prefix_command)]
pub async fn color(ctx: Context<'_>, color: String) -> Result<(), Error> {
let data = ctx.data();
let mut db = data.database.lock().await;
let db = db.as_mut();
let color = match HexColor::parse_rgb(&color) {
Ok(color) => color,
Err(e) => {
ctx.reply(format!("Couldn't parse color: {e}")).await?;
return Ok(());
}
};
if let Some(guild) = ctx.guild_id() {
let role = match super::get_user_role(ctx, ctx.author().id, guild, db).await? {
Some(role) => role,
None => {
let role = guild.create_role(ctx,
EditRole::new()
.name(format!("{}", color.display_rgb()))
.colour(Color::from_rgb(color.r, color.g, color.b))).await?;
sqlx::query("INSERT INTO selfroles (userid, roleid, guildid) VALUES ($1, $2, $3)")
.bind(ctx.author().id.get() as i64)
.bind(role.id.get() as i64)
.bind(guild.get() as i64)
.execute(db).await?;
let member = guild.member(ctx, ctx.author().id).await?;
member.add_role(ctx, role.clone()).await?;
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

@@ -0,0 +1,30 @@
use crate::common::{Context, Error};
/// Remove and delete your personal role
#[poise::command(slash_command, prefix_command)]
pub async fn disown(ctx: Context<'_>) -> 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() {
if let Some(role) = super::get_user_role(ctx, ctx.author().id, guild, db).await? {
guild.delete_role(ctx, role).await?;
sqlx::query("DELETE FROM selfroles WHERE roleid = $1")
.bind(role.get() as i64)
.execute(db).await?;
ctx.reply("Your role has been successfully removed.").await?;
Ok(())
} else {
ctx.reply("You do not currently have a personal role to remove.").await?;
Ok(())
}
} else {
ctx.reply("This command must be called within a server.").await?;
Ok(())
}
}

View File

@@ -0,0 +1,37 @@
use crate::common::{Context, Error};
use sqlx::{PgConnection, Row};
use poise::serenity_prelude::{RoleId, UserId, GuildId};
mod register;
mod whois;
mod color;
mod name;
mod disown;
#[poise::command(
prefix_command,
slash_command,
subcommands(
"register::register",
"whois::whois",
"color::color",
"name::name",
"disown::disown",
)
)]
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> {
match sqlx::query("SELECT roleid FROM selfroles WHERE userid = $1 AND guildid = $2")
.bind(user.get() as i64)
.bind(guild.get() as i64)
.fetch_one(db).await
{
Ok(row) => Ok(Some(RoleId::new(row.try_get::<i64, usize>(0)? as u64))),
Err(sqlx::Error::RowNotFound) => Ok(None),
Err(e) => return Err(Box::new(e)),
}
}

View File

@@ -0,0 +1,43 @@
use crate::common::{Context, Error};
use poise::serenity_prelude::EditRole;
/// Change the name of your personal role
#[poise::command(slash_command, prefix_command)]
pub async fn name(ctx: Context<'_>, name: String) -> 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() {
let role = match super::get_user_role(ctx, ctx.author().id, guild, db).await? {
Some(role) => role,
None => {
let role = guild.create_role(ctx, EditRole::new().name(name)).await?;
sqlx::query("INSERT INTO selfroles (userid, roleid, guildid) VALUES ($1, $2, $3)")
.bind(ctx.author().id.get() as i64)
.bind(role.id.get() as i64)
.bind(guild.get() as i64)
.execute(db).await?;
let member = guild.member(ctx, ctx.author().id).await?;
member.add_role(ctx, role.clone()).await?;
ctx.reply(format!("You've been given the {} role!", role)).await?;
return Ok(());
}
};
guild.edit_role(ctx, role, EditRole::new().name(name)).await?;
ctx.reply("Your custom role's name has been updated!").await?;
Ok(())
} else {
ctx.reply("This command must be run within a server.").await?;
Ok(())
}
}

View File

@@ -0,0 +1,29 @@
use crate::common::{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> {
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().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?;
ctx.reply(format!("**{}** now has **{}** set as their personal role.", user.display_name(), role.name)).await?;
Ok(())
} else {
ctx.reply("This command can only be run in a guild!").await?;
Ok(())
}
}

View File

@@ -0,0 +1,40 @@
use crate::common::{Context, Error};
use poise::serenity_prelude as serenity;
use serenity::UserId;
use sqlx::Row;
/// Let you know who is the owner of a role.
#[poise::command(slash_command, prefix_command)]
pub async fn whois(ctx: Context<'_>, role: serenity::Role) -> 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() {
let row = match sqlx::query("SELECT userid FROM selfroles WHERE roleid = $1")
.bind(role.id.get() as i64)
.fetch_one(db).await
{
Ok(row) => row,
Err(sqlx::Error::RowNotFound) => {
ctx.reply("This role is not owned by anyone.").await?;
return Ok(());
}
Err(e) => return Err(Box::new(e)),
};
let user: i64 = row.try_get(0)?;
let user = UserId::new(user as u64);
let member = guild.member(ctx, user).await?;
ctx.reply(format!("{} owns this role.", member.display_name())).await?;
} else {
ctx.reply("This command must be used within a server!").await?;
}
Ok(())
}

View File

@@ -7,6 +7,9 @@ use sqlx::PgConnection;
pub struct Data { pub struct Data {
pub database: Arc<Mutex<PgConnection>>, pub database: Arc<Mutex<PgConnection>>,
pub mentions: Arc<Mutex<HashMap<UserId, std::time::Instant>>>, pub mentions: Arc<Mutex<HashMap<UserId, std::time::Instant>>>,
/// last time the user redeemed a daily
pub dailies: Arc<Mutex<HashMap<UserId, std::time::Instant>>>,
} }
pub type Error = Box<dyn std::error::Error + Send + Sync>; pub type Error = Box<dyn std::error::Error + Send + Sync>;

View File

@@ -79,11 +79,22 @@ async fn main() -> Result<(), Error> {
"#, "#,
).execute(&mut database).await?; ).execute(&mut database).await?;
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS selfroles (
userid BIGINT PRIMARY KEY,
roleid BIGINT,
guildid BIGINT
)
"#,
).execute(&mut database).await?;
println!("Bot is ready!"); println!("Bot is ready!");
Ok(Data { Ok(Data {
database: Arc::new(Mutex::new(database)), database: Arc::new(Mutex::new(database)),
mentions: Arc::new(Mutex::new(HashMap::new())), mentions: Arc::new(Mutex::new(HashMap::new())),
dailies: Arc::new(Mutex::new(HashMap::new())),
}) })
}) })
}) })