Add context menu commands for showing and editing profiles

This commit is contained in:
2025-02-04 23:40:01 +00:00
parent c843fec323
commit aad93ad7d8
4 changed files with 63 additions and 32 deletions

View File

@@ -10,7 +10,7 @@ where
{ {
let roles: Vec<_> = match ctx.author_member().await { let roles: Vec<_> = match ctx.author_member().await {
Some(member) => member.roles.iter().map(|r| r.get()).collect(), Some(member) => member.roles.iter().map(|r| r.get()).collect(),
None => return Ok(false), None => return Err(AppError::StaffOnly { command_name: ctx.command().qualified_name.clone() }),
}; };
match ctx.data().repository().await?.staff_roles_contains(&roles).await { match ctx.data().repository().await?.staff_roles_contains(&roles).await {

View File

@@ -16,6 +16,8 @@ where
help::help(), help::help(),
pokeapi::pokeapi(), pokeapi::pokeapi(),
profile::profile(), profile::profile(),
profile::cmu_profile_show(),
profile::cmu_profile_edit(),
] ]
} }
@@ -34,7 +36,13 @@ where
R: RepositoryProvider, R: RepositoryProvider,
{ {
for command in commands { for command in commands {
if command.slash_action.is_none() {
// Skip context-menu-only commands
continue;
}
names.push(format!("{}{}", prefix, command.qualified_name.clone())); names.push(format!("{}{}", prefix, command.qualified_name.clone()));
if !command.subcommands.is_empty() { if !command.subcommands.is_empty() {
let old_len = prefix.len(); let old_len = prefix.len();
prefix.push_str(&command.qualified_name); prefix.push_str(&command.qualified_name);

View File

@@ -220,12 +220,11 @@ where
Ok(()) Ok(())
} }
/// Edit your friend codes. async fn edit_inner<R: RepositoryProvider + Send + Sync>(
#[poise::command(slash_command, guild_only)] ctx: AppContext<'_, R, R::BackendError>,
async fn edit<R: RepositoryProvider + Send + Sync>(ctx: AppContext<'_, R, R::BackendError>) -> Result<(), AppError<R::BackendError>> { user: &serenity::all::User,
let author_id = ctx.author().id.get(); ) -> Result<(), AppError<R::BackendError>> {
let embed = match execute_edit_codes_modal(ctx, user.id.get()).await {
let embed = match execute_edit_codes_modal(ctx, author_id).await {
Ok(()) => CreateEmbed::new() Ok(()) => CreateEmbed::new()
.title("Changes Saved") .title("Changes Saved")
.description("Your changes have been saved successfully.") .description("Your changes have been saved successfully.")
@@ -247,6 +246,24 @@ async fn edit<R: RepositoryProvider + Send + Sync>(ctx: AppContext<'_, R, R::Bac
Ok(()) Ok(())
} }
#[poise::command(context_menu_command = "Edit Friend Codes", guild_only)]
pub async fn cmu_profile_edit<R: RepositoryProvider + Send + Sync>(
ctx: AppContext<'_, R, R::BackendError>,
user: serenity::all::User,
) -> Result<(), AppError<R::BackendError>> {
if ctx.author().id != user.id && !crate::checks::is_staff(ctx.into()).await? {
return Err(AppError::StaffOnly { command_name: ctx.command().qualified_name.clone() });
}
edit_inner(ctx, &user).await
}
/// Edit your friend codes.
#[poise::command(slash_command, guild_only)]
async fn edit<R: RepositoryProvider + Send + Sync>(ctx: AppContext<'_, R, R::BackendError>) -> Result<(), AppError<R::BackendError>> {
edit_inner(ctx, ctx.author()).await
}
/// Edit any user's friend codes. /// Edit any user's friend codes.
#[poise::command( #[poise::command(
slash_command, slash_command,
@@ -259,26 +276,5 @@ async fn overwrite<R: RepositoryProvider + Send + Sync>(
#[description = "The profile to edit."] #[description = "The profile to edit."]
member: Member, member: Member,
) -> Result<(), AppError<R::BackendError>> { ) -> Result<(), AppError<R::BackendError>> {
let member_id = member.user.id.get(); edit_inner(ctx, &member.user).await
let embed = match execute_edit_codes_modal(ctx, member_id).await {
Ok(()) => CreateEmbed::new()
.title("Changes Saved")
.description("Your changes have been saved successfully.")
.color(crate::utils::bot_color(&ctx).await),
Err(EditError::ValidationError(errors)) => CreateEmbed::new()
.title("Validation Error")
.description(errors.join("\n"))
.color(Color::RED),
Err(EditError::SerenityError(err)) => return Err(AppError::from(err)),
Err(EditError::RepositoryError(err)) => return Err(AppError::from(err)),
};
let reply = CreateReply::default()
.embed(embed)
.ephemeral(true);
ctx.send(reply).await?;
Ok(())
} }

View File

@@ -2,12 +2,16 @@ use cipher_core::repository::user_repository::UserRepository;
use cipher_core::repository::RepositoryProvider; use cipher_core::repository::RepositoryProvider;
use poise::CreateReply; use poise::CreateReply;
use serenity::all::CreateEmbed; use serenity::all::CreateEmbed;
use serenity::all::Member;
use serenity::all::User;
use crate::app::AppContext; use crate::app::AppContext;
use crate::app::AppError; use crate::app::AppError;
mod codes; mod codes;
pub use codes::cmu_profile_edit;
/// Edit and show profiles. /// Edit and show profiles.
#[poise::command( #[poise::command(
slash_command, slash_command,
@@ -22,6 +26,21 @@ pub async fn profile<R: RepositoryProvider + Send + Sync>(
Ok(()) Ok(())
} }
#[poise::command(context_menu_command = "Show User Profile", guild_only)]
pub async fn cmu_profile_show<R: RepositoryProvider + Send + Sync>(
ctx: AppContext<'_, R, R::BackendError>,
user: User,
) -> Result<(), AppError<R::BackendError>> {
let guild = match ctx.guild_id() {
Some(guild) => guild,
None => return Ok(()),
};
let member = guild.member(ctx, user.id).await?;
show_inner(ctx, member, true).await
}
/// Show your profile or someone else's. /// Show your profile or someone else's.
#[poise::command(slash_command, guild_only)] #[poise::command(slash_command, guild_only)]
async fn show<R: RepositoryProvider + Send + Sync>( async fn show<R: RepositoryProvider + Send + Sync>(
@@ -32,13 +51,21 @@ async fn show<R: RepositoryProvider + Send + Sync>(
#[description = "Hide reply from other users. Defaults to True."] #[description = "Hide reply from other users. Defaults to True."]
ephemeral: Option<bool>, ephemeral: Option<bool>,
) -> Result<(), AppError<R::BackendError>> { ) -> Result<(), AppError<R::BackendError>> {
let mut repo = ctx.data.repository().await?;
let member = match option_member { let member = match option_member {
Some(member) => member, Some(member) => member,
None => ctx.author_member().await.ok_or(AppError::UnknownCacheOrHttpError)?.into_owned(), None => ctx.author_member().await.ok_or(AppError::UnknownCacheOrHttpError)?.into_owned(),
}; };
show_inner(ctx, member, ephemeral.unwrap_or(true)).await
}
async fn show_inner<R: RepositoryProvider + Send + Sync>(
ctx: AppContext<'_, R, R::BackendError>,
member: Member,
ephemeral: bool,
) -> Result<(), AppError<R::BackendError>> {
let mut repo = ctx.data.repository().await?;
let avatar_url = crate::utils::member_avatar_url(&member); let avatar_url = crate::utils::member_avatar_url(&member);
let embed_color = match member.colour(ctx) { let embed_color = match member.colour(ctx) {
@@ -76,7 +103,7 @@ async fn show<R: RepositoryProvider + Send + Sync>(
let reply = CreateReply::default() let reply = CreateReply::default()
.embed(embed) .embed(embed)
.ephemeral(ephemeral.unwrap_or(true)); .ephemeral(ephemeral);
ctx.send(reply).await?; ctx.send(reply).await?;