Refactor command handling and permissions management

- Updated examples formatting in SetpermCommand and LeaveSettingsCommand for consistency.
- Consolidated the handling of guild targets into a new utility file (servertarget.rs).
- Moved the del command logic to a new utility file (del.rs) for better organization.
- Enhanced the set command functionalities by creating a dedicated set.rs file, improving readability and maintainability.
- Removed deprecated servertarget.rs and del.rs files from the commands directory.
- Improved the help command structure for better readability and maintainability.
- Added new utility files for permissions and server target handling to streamline command processing.
This commit is contained in:
Puechberty Arthur
2026-04-10 18:03:55 +02:00
parent 00ae9cda11
commit 313ecc7d0c
16 changed files with 91 additions and 98 deletions
+2 -1
View File
@@ -78,7 +78,8 @@ pub async fn handle_autopublishon(ctx: &Context, msg: &Message, args: &[&str]) {
.and_then(|value| parse_channel_id(value))
.unwrap_or(msg.channel_id);
let result = db::add_autopublish_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await;
let result =
db::add_autopublish_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await;
if result.is_err() {
send_embed(
+5 -1
View File
@@ -15,7 +15,11 @@ impl crate::commands::command_contract::CommandSpec for AutopublishoffCommand {
category: "automation",
params: "[#canal]",
description: "Desactive la publication automatique des annonces sur un salon.",
examples: &["+autopublishoff", "+autopublishoff #annonces", "+help autopublishoff"],
examples: &[
"+autopublishoff",
"+autopublishoff #annonces",
"+help autopublishoff",
],
default_aliases: &["apboff"],
allow_in_dm: false,
default_permission: 5,
+5 -1
View File
@@ -15,7 +15,11 @@ impl crate::commands::command_contract::CommandSpec for AutopublishonCommand {
category: "automation",
params: "[#canal]",
description: "Active la publication automatique des annonces sur un salon.",
examples: &["+autopublishon", "+autopublishon #annonces", "+help autopublishon"],
examples: &[
"+autopublishon",
"+autopublishon #annonces",
"+help autopublishon",
],
default_aliases: &["apbon"],
allow_in_dm: false,
default_permission: 5,
+4 -2
View File
@@ -164,7 +164,8 @@ pub async fn handle_piconlyadd(ctx: &Context, msg: &Message, args: &[&str]) {
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let result = db::add_piconly_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await;
let result =
db::add_piconly_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await;
if result.is_err() {
send_embed(
@@ -210,7 +211,8 @@ pub async fn handle_piconlydel(ctx: &Context, msg: &Message, args: &[&str]) {
.and_then(|raw| parse_channel_id(raw))
.unwrap_or(msg.channel_id);
let result = db::remove_piconly_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await;
let result =
db::remove_piconly_channel(&pool, bot_id, guild_id_i64, channel_id.get() as i64).await;
if result.is_err() {
send_embed(
-289
View File
@@ -1,289 +0,0 @@
use serenity::builder::{CreateAttachment, CreateEmbed, EditProfile};
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::send_embed;
use crate::commands::perms_helpers::{
ensure_owner, get_pool, normalize_command_name, parse_user_or_role,
};
use crate::db::{grant_command_access, grant_perm_level};
pub async fn handle_setperm(ctx: &Context, msg: &Message, args: &[&str]) {
if !ensure_owner(ctx, msg).await {
return;
}
if args.len() < 2 {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+setperm <permission/commande> <role/membre>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let Some((scope_type, scope_id)) = parse_user_or_role(args[1]) else {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Role/membre invalide.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
let bot_id = ctx.cache.current_user().id;
let Some(pool) = get_pool(ctx).await else {
let embed = CreateEmbed::new()
.title("Erreur")
.description("DB indisponible.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
if let Ok(level) = args[0].parse::<u8>() {
if level > 9 {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Permission invalide (0..9).")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let _ = grant_perm_level(&pool, bot_id, scope_type, scope_id, level).await;
let who = if scope_type == "role" {
format!("<@&{}>", scope_id)
} else {
format!("<@{}>", scope_id)
};
let embed = CreateEmbed::new()
.title("Permission attribuee")
.description(format!("{} recoit la permission `{}`", who, level))
.color(0x57F287);
send_embed(ctx, msg, embed).await;
return;
}
let command = normalize_command_name(args[0]);
let _ = grant_command_access(&pool, bot_id, scope_type, scope_id, &command).await;
let who = if scope_type == "role" {
format!("<@&{}>", scope_id)
} else {
format!("<@{}>", scope_id)
};
let embed = CreateEmbed::new()
.title("Acces commande attribue")
.description(format!("{} recoit l'acces direct a `{}`", who, command))
.color(0x57F287);
send_embed(ctx, msg, embed).await;
}
pub async fn handle_setname(ctx: &Context, msg: &Message, args: &[&str]) {
if args.is_empty() {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+setname <nom>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let name = args.join(" ");
let mut me = match ctx.http.get_current_user().await {
Ok(user) => user,
Err(err) => {
let embed = CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de charger le profil bot: {}", err))
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
};
let result = me
.edit(&ctx.http, EditProfile::new().username(name.clone()))
.await;
let embed = match result {
Ok(_) => CreateEmbed::new()
.title("Profil mis à jour")
.description(format!("Nom défini sur: {}", name))
.color(0x57F287),
Err(err) => CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de modifier le nom: {}", err))
.color(0xED4245),
};
send_embed(ctx, msg, embed).await;
}
pub async fn handle_setpic(ctx: &Context, msg: &Message, args: &[&str]) {
if args.is_empty() {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+setpic <lien_image>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let Ok(attachment) = CreateAttachment::url(&ctx.http, args[0]).await else {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Impossible de télécharger l'image.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
let mut me = match ctx.http.get_current_user().await {
Ok(user) => user,
Err(err) => {
let embed = CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de charger le profil bot: {}", err))
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
};
let result = me
.edit(&ctx.http, EditProfile::new().avatar(&attachment))
.await;
let embed = match result {
Ok(_) => CreateEmbed::new()
.title("Profil mis à jour")
.description("Photo de profil modifiée.")
.color(0x57F287),
Err(err) => CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de modifier la photo: {}", err))
.color(0xED4245),
};
send_embed(ctx, msg, embed).await;
}
pub async fn handle_setbanner(ctx: &Context, msg: &Message, args: &[&str]) {
if args.is_empty() {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+setbanner <lien_image>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let Ok(attachment) = CreateAttachment::url(&ctx.http, args[0]).await else {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Impossible de télécharger l'image.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
let mut me = match ctx.http.get_current_user().await {
Ok(user) => user,
Err(err) => {
let embed = CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de charger le profil bot: {}", err))
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
};
let result = me
.edit(&ctx.http, EditProfile::new().banner(&attachment))
.await;
let embed = match result {
Ok(_) => CreateEmbed::new()
.title("Profil mis à jour")
.description("Bannière modifiée.")
.color(0x57F287),
Err(err) => CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de modifier la bannière: {}", err))
.color(0xED4245),
};
send_embed(ctx, msg, embed).await;
}
pub async fn handle_setprofil(ctx: &Context, msg: &Message, args: &[&str]) {
let raw = args.join(" ");
let parts: Vec<&str> = raw.split(";;").map(|s| s.trim()).collect();
if parts.len() != 3 {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+setprofil <nom> ;; <lien_pic> ;; <lien_banner>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let mut builder = EditProfile::new();
if !parts[0].is_empty() {
builder = builder.username(parts[0].to_string());
}
if !parts[1].is_empty() {
match CreateAttachment::url(&ctx.http, parts[1]).await {
Ok(avatar) => builder = builder.avatar(&avatar),
Err(_) => {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Image avatar invalide dans `+setprofil`.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
}
}
if !parts[2].is_empty() {
match CreateAttachment::url(&ctx.http, parts[2]).await {
Ok(banner) => builder = builder.banner(&banner),
Err(_) => {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Image bannière invalide dans `+setprofil`.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
}
}
let mut me = match ctx.http.get_current_user().await {
Ok(user) => user,
Err(err) => {
let embed = CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de charger le profil bot: {}", err))
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
};
let result = me.edit(&ctx.http, builder).await;
let embed = match result {
Ok(_) => CreateEmbed::new()
.title("Profil mis à jour")
.description("Nom, avatar et bannière traités.")
.color(0x57F287),
Err(err) => CreateEmbed::new()
.title("Erreur")
.description(format!("Impossible de modifier le profil: {}", err))
.color(0xED4245),
};
send_embed(ctx, msg, embed).await;
}
+5 -1
View File
@@ -15,7 +15,11 @@ impl crate::commands::command_contract::CommandSpec for SetpermCommand {
category: "botconfig",
params: "<permission/commande> <role/membre>",
description: "Attribue un niveau ACL ou un acces commande a un role ou membre.",
examples: &["+setperm 6 @Moderateur", "+setperm mute @Role", "+help setperm"],
examples: &[
"+setperm 6 @Moderateur",
"+setperm mute @Role",
"+help setperm",
],
default_aliases: &["stp"],
allow_in_dm: false,
default_permission: 9,
+1 -4
View File
@@ -143,10 +143,7 @@ impl crate::commands::command_contract::CommandSpec for LeaveSettingsCommand {
category: "config",
params: "[on/off] [salon] [message]",
description: "Configure les actions a executer quand un membre quitte le serveur.",
examples: &[
"+leavesettings",
"+leavesettings on #logs {user} a quitte",
],
examples: &["+leavesettings", "+leavesettings on #logs {user} a quitte"],
default_aliases: &["lset"],
allow_in_dm: false,
default_permission: 5,
+1 -4
View File
@@ -103,10 +103,7 @@ impl crate::commands::command_contract::CommandSpec for SetBoostembedCommand {
category: "config",
params: "<title|description|color> <valeur>",
description: "Configure le titre, la description et la couleur de l embed boost.",
examples: &[
"+setboostembed title Merci",
"+setboostembed color #FF66CC",
],
examples: &["+setboostembed title Merci", "+setboostembed color #FF66CC"],
default_aliases: &["sboostembed"],
allow_in_dm: false,
default_permission: 6,
-55
View File
@@ -1,55 +0,0 @@
use serenity::http::GuildPagination;
use serenity::model::prelude::*;
use serenity::prelude::*;
pub(crate) async fn resolve_guild_target(ctx: &Context, input: &str) -> Option<GuildId> {
let guilds = guilds_sorted(ctx).await;
if let Ok(index) = input.parse::<usize>() {
if index >= 1 && index <= guilds.len() {
return Some(guilds[index - 1].0);
}
}
input
.parse::<u64>()
.ok()
.map(GuildId::new)
.filter(|id| guilds.iter().any(|(guild_id, _)| guild_id == id))
}
pub(crate) async fn guilds_sorted(ctx: &Context) -> Vec<(GuildId, String)> {
let mut all_guilds = Vec::new();
let mut after: Option<GuildId> = None;
loop {
let page = if let Some(after_id) = after {
ctx.http
.get_guilds(Some(GuildPagination::After(after_id)), Some(100))
.await
.unwrap_or_default()
} else {
ctx.http
.get_guilds(None, Some(100))
.await
.unwrap_or_default()
};
if page.is_empty() {
break;
}
after = page.last().map(|guild| guild.id);
all_guilds.extend(page.into_iter().map(|guild| (guild.id, guild.name)));
if all_guilds.len() % 100 != 0 {
break;
}
}
all_guilds.sort_by(|a, b| {
a.1.to_lowercase()
.cmp(&b.1.to_lowercase())
.then_with(|| a.0.get().cmp(&b.0.get()))
});
all_guilds
}
+36 -23
View File
@@ -2,13 +2,14 @@ use crate::commands::command_contract::{CommandMetadata, CommandSpec};
#[path = "roles/addrole.rs"]
pub mod addrole;
#[path = "../utils/admin_common.rs"]
pub mod admin_common;
#[path = "../utils/admin_service.rs"]
pub mod admin_service;
#[path = "../utils/advanced_tools.rs"]
pub mod advanced_tools;
#[path = "perms/alias.rs"]
pub mod alias;
#[path = "perms/unalias.rs"]
pub mod unalias;
#[path = "info/alladmins.rs"]
pub mod alladmins;
#[path = "info/allbots.rs"]
@@ -29,6 +30,7 @@ pub mod antispam;
pub mod autobackup;
#[path = "config/autoconfiglog.rs"]
pub mod autoconfiglog;
#[path = "../utils/automod_service.rs"]
pub mod automod_service;
#[path = "automation/autopublish.rs"]
pub mod autopublish;
@@ -60,7 +62,9 @@ pub mod boosters;
pub mod boostlog;
#[path = "owner/botadmins.rs"]
pub mod botadmins;
#[path = "../utils/botconfig_common.rs"]
pub mod botconfig_common;
#[path = "../utils/botconfig_service.rs"]
pub mod botconfig_service;
#[path = "channel/bringall.rs"]
pub mod bringall;
@@ -70,10 +74,10 @@ pub mod button;
pub mod calc;
#[path = "botconfig/change.rs"]
pub mod change;
#[path = "botconfig/changereset.rs"]
pub mod changereset;
#[path = "botconfig/changeall.rs"]
pub mod changeall;
#[path = "botconfig/changereset.rs"]
pub mod changereset;
#[path = "info/channel.rs"]
pub mod channel;
#[path = "fun/choose.rs"]
@@ -102,18 +106,20 @@ pub mod clear_sanctions;
pub mod close;
#[path = "mod/cmute.rs"]
pub mod cmute;
#[path = "../utils/command_contract.rs"]
pub mod command_contract;
#[path = "../utils/common.rs"]
pub mod common;
#[path = "botconfig/compet.rs"]
pub mod compet;
#[path = "automation/create.rs"]
pub mod create;
#[path = "perms/del.rs"]
#[path = "../utils/del.rs"]
pub mod del;
#[path = "perms/delperm.rs"]
pub mod delperm;
#[path = "mod/delsanction.rs"]
pub mod del_sanction;
#[path = "perms/delperm.rs"]
pub mod delperm;
#[path = "roles/delrole.rs"]
pub mod delrole;
#[path = "roles/derank.rs"]
@@ -164,7 +170,9 @@ pub mod loading;
pub mod lock;
#[path = "channel/lockall.rs"]
pub mod lockall;
#[path = "../utils/logs_command_helpers.rs"]
pub mod logs_command_helpers;
#[path = "../utils/logs_service.rs"]
pub mod logs_service;
#[path = "botconfig/mainprefix.rs"]
pub mod mainprefix;
@@ -174,8 +182,11 @@ pub mod massiverole;
pub mod member;
#[path = "config/messagelog.rs"]
pub mod messagelog;
#[path = "../utils/moderation_channel_helpers.rs"]
pub mod moderation_channel_helpers;
#[path = "../utils/moderation_sanction_helpers.rs"]
pub mod moderation_sanction_helpers;
#[path = "../utils/moderation_tools.rs"]
pub mod moderation_tools;
#[path = "config/modlog.rs"]
pub mod modlog;
@@ -209,7 +220,9 @@ pub mod online;
pub mod owner;
#[path = "perms/perms.rs"]
pub mod perms;
#[path = "../utils/perms_helpers.rs"]
pub mod perms_helpers;
#[path = "../utils/perms_service.rs"]
pub mod perms_service;
#[path = "info/pic.rs"]
pub mod pic;
@@ -261,16 +274,22 @@ pub mod sanctions;
pub mod say;
#[path = "info/serverbanner.rs"]
pub mod serverbanner;
#[path = "info/serverinfo.rs"]
pub mod serverinfo;
#[path = "info/serverlist.rs"]
pub mod serverlist;
#[path = "info/serverpic.rs"]
pub mod serverpic;
#[path = "info/servertarget.rs"]
#[path = "../utils/servertarget.rs"]
pub mod servertarget;
#[path = "info/serverinfo.rs"]
pub mod serverinfo;
#[path = "botconfig/set.rs"]
#[path = "../utils/set.rs"]
pub mod set;
#[path = "config/setboostembed.rs"]
pub mod set_boostembed;
#[path = "config/setmodlogs.rs"]
pub mod set_modlogs;
#[path = "mod/setmuterole.rs"]
pub mod set_muterole;
#[path = "botconfig/setbanner.rs"]
pub mod setbanner;
#[path = "botconfig/setname.rs"]
@@ -281,12 +300,6 @@ pub mod setperm;
pub mod setpic;
#[path = "botconfig/setprofil.rs"]
pub mod setprofil;
#[path = "config/setboostembed.rs"]
pub mod set_boostembed;
#[path = "config/setmodlogs.rs"]
pub mod set_modlogs;
#[path = "mod/setmuterole.rs"]
pub mod set_muterole;
#[path = "botconfig/shadowbot.rs"]
pub mod shadowbot;
#[path = "info/showpics.rs"]
@@ -329,6 +342,8 @@ pub mod ticket_member;
pub mod tickets;
#[path = "mod/timeout.rs"]
pub mod timeout;
#[path = "perms/unalias.rs"]
pub mod unalias;
#[path = "mod/unban.rs"]
pub mod unban;
#[path = "mod/unbanall.rs"]
@@ -556,12 +571,10 @@ pub fn command_metadata_by_key(key: &str) -> Option<CommandMetadata> {
let normalized = key.to_lowercase();
let compact = normalized.replace('_', "");
all_command_metadata()
.into_iter()
.find(|meta| {
meta.name.eq_ignore_ascii_case(&normalized)
|| meta.name.replace('_', "").eq_ignore_ascii_case(&compact)
})
all_command_metadata().into_iter().find(|meta| {
meta.name.eq_ignore_ascii_case(&normalized)
|| meta.name.replace('_', "").eq_ignore_ascii_case(&compact)
})
}
pub fn resolve_default_alias(alias: &str) -> Option<&'static str> {
+3 -1
View File
@@ -45,7 +45,9 @@ pub async fn handle_alias(ctx: &Context, msg: &Message, args: &[&str]) {
}
let command = args[0].trim_start_matches('+').to_lowercase();
let is_known = all_command_keys().iter().any(|candidate| candidate == &command)
let is_known = all_command_keys()
.iter()
.any(|candidate| candidate == &command)
|| crate::commands::command_metadata_by_key(&command).is_some();
if !is_known {
let embed = serenity::builder::CreateEmbed::new()
-52
View File
@@ -1,52 +0,0 @@
use serenity::builder::CreateEmbed;
use serenity::model::prelude::*;
use serenity::prelude::*;
use crate::commands::common::send_embed;
use crate::commands::perms_helpers::{ensure_owner, get_pool, parse_user_or_role};
use crate::db::remove_scope_permissions;
pub async fn handle_del(ctx: &Context, msg: &Message, args: &[&str]) {
if !ensure_owner(ctx, msg).await {
return;
}
if args.is_empty() {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Usage: `+delperm <role/membre>`")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
}
let Some((scope_type, scope_id)) = parse_user_or_role(args[0]) else {
let embed = CreateEmbed::new()
.title("Erreur")
.description("Role/membre invalide.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
let bot_id = ctx.cache.current_user().id;
let Some(pool) = get_pool(ctx).await else {
let embed = CreateEmbed::new()
.title("Erreur")
.description("DB indisponible.")
.color(0xED4245);
send_embed(ctx, msg, embed).await;
return;
};
let removed = remove_scope_permissions(&pool, bot_id, scope_type, scope_id)
.await
.unwrap_or(0);
let embed = CreateEmbed::new()
.title("Permissions supprimees")
.description(format!("{} entree(s) supprimee(s).", removed))
.color(0x57F287);
send_embed(ctx, msg, embed).await;
}
+12 -38
View File
@@ -135,28 +135,10 @@ fn help_page_for_command(
"modlog" | "messagelog" | "voicelog" | "boostlog" | "rolelog" | "raidlog"
| "autoconfiglog" | "nolog" | "joinsettings" | "boostembed" | "setmodlogs"
| "setboostembed" | "leavesettings" | "viewlogs" => "logs",
"warn"
| "mute"
| "tempmute"
| "unmute"
| "cmute"
| "tempcmute"
| "uncmute"
| "mutelist"
| "unmuteall"
| "kick"
| "ban"
| "tempban"
| "unban"
| "banlist"
| "unbanall"
| "sanctions"
| "delsanction"
| "clearsanctions"
| "clearallsanctions"
| "cleanup"
| "renew"
| "clearmessages" => "moderation",
"warn" | "mute" | "tempmute" | "unmute" | "cmute" | "tempcmute" | "uncmute"
| "mutelist" | "unmuteall" | "kick" | "ban" | "tempban" | "unban" | "banlist"
| "unbanall" | "sanctions" | "delsanction" | "clearsanctions" | "clearallsanctions"
| "cleanup" | "renew" | "clearmessages" => "moderation",
"addrole" | "delrole" | "derank" | "massiverole" | "unmassiverole" | "temprole"
| "untemprole" | "sync" => "roles",
"lock" | "unlock" | "lockall" | "unlockall" | "hide" | "unhide" | "hideall"
@@ -164,19 +146,13 @@ fn help_page_for_command(
"giveaway" | "end" | "reroll" | "choose" | "calc" | "emoji" | "embed" | "say"
| "create" | "newsticker" | "button" | "autoreact" | "snipe" | "loading" | "backup"
| "autobackup" => "outils",
"shadowbot" | "setname" | "setpic" | "setbanner" | "setprofil" | "setperm"
| "theme" | "playto" | "listen" | "watch" | "compet" | "stream"
| "removeactivity" | "online" | "idle" | "dnd" | "invisible" | "change"
| "changereset" | "changeall" => {
"bot"
}
"owner" | "unowner" | "clearowners" | "bl" | "unbl" | "blinfo" | "clearbl"
| "allbots" | "alladmins" | "botadmins" | "mainprefix" | "prefix" | "mp"
| "mpsettings" | "mpsent" | "mpdelete" | "invite" | "leave" | "discussion" => {
"administration"
}
"perms" | "delperm" | "clearperms" | "allperms" | "alias" | "help"
| "helpsetting" => {
"shadowbot" | "setname" | "setpic" | "setbanner" | "setprofil" | "setperm" | "theme"
| "playto" | "listen" | "watch" | "compet" | "stream" | "removeactivity" | "online"
| "idle" | "dnd" | "invisible" | "change" | "changereset" | "changeall" => "bot",
"owner" | "unowner" | "clearowners" | "bl" | "unbl" | "blinfo" | "clearbl" | "allbots"
| "alladmins" | "botadmins" | "mainprefix" | "prefix" | "mp" | "mpsettings" | "mpsent"
| "mpdelete" | "invite" | "leave" | "discussion" => "administration",
"perms" | "delperm" | "clearperms" | "allperms" | "alias" | "help" | "helpsetting" => {
"permissions"
}
_ => match meta.category {
@@ -410,9 +386,7 @@ fn help_lookup_to_key(input: &str) -> Option<&'static str> {
"newsticker" => Some("newsticker"),
"piconly" => Some("piconly"),
"piconly add" | "piconlyadd" => Some("piconlyadd"),
"piconly del" | "piconly remove" | "piconly delete" | "piconlydel" => {
Some("piconlydel")
}
"piconly del" | "piconly remove" | "piconly delete" | "piconlydel" => Some("piconlydel"),
"massiverole" => Some("massiverole"),
"unmassiverole" => Some("unmassiverole"),
"noderank" => Some("noderank"),