Skip to content

Commit

Permalink
Fix Help displaying Groups and their Commands (#577)
Browse files Browse the repository at this point in the history
  • Loading branch information
Lakelezz authored and arqunis committed May 25, 2019
1 parent 86ec810 commit eca204a
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 112 deletions.
255 changes: 154 additions & 101 deletions src/framework/standard/help_commands.rs
Expand Up @@ -34,6 +34,7 @@ use super::{
use crate::{
cache::CacheRwLock,
client::Context,
framework::standard::CommonOptions,
model::channel::Message,
Error,
http::Http,
Expand Down Expand Up @@ -249,13 +250,15 @@ pub fn has_all_requirements(
let guild = guild.read();

if let Some(member) = guild.members.get(&msg.author.id) {

if let Ok(permissions) = member.permissions(&cache) {

return if cmd.allowed_roles.is_empty() {
permissions.administrator() || has_correct_permissions(&cache, cmd, msg)
permissions.administrator() || has_correct_permissions(&cache, &cmd, msg)
} else {
permissions.administrator()
|| (has_correct_roles(cmd, &guild, member)
&& has_correct_permissions(&cache, cmd, msg))
|| (has_correct_roles(&cmd, &guild, member)
&& has_correct_permissions(&cache, &cmd, msg))
};
}
}
Expand All @@ -264,52 +267,6 @@ pub fn has_all_requirements(
cmd.only_in != OnlyIn::Guild
}

/// Checks whether a command would be visible, takes permissions, channel sent in,
/// and roles into consideration.
///
/// **Note**: A command is visible when it is either normally displayed or
/// strikethrough upon requested help by a user.
#[cfg(feature = "cache")]
pub fn is_command_visible(
cache: impl AsRef<CacheRwLock>,
command_options: &CommandOptions,
msg: &Message,
help_options: &HelpOptions,
) -> bool {
if command_options.only_in == OnlyIn::None
|| command_options.only_in == OnlyIn::Dm && msg.is_private()
|| command_options.only_in == OnlyIn::Guild && !msg.is_private()
{
if let Some(guild) = msg.guild(&cache) {
let guild = guild.read();

if let Some(member) = guild.members.get(&msg.author.id) {
if command_options.help_available {
return if has_correct_permissions(&cache, command_options, msg) {
if has_correct_roles(command_options, &guild, &member) {
true
} else {
help_options.lacking_role != HelpBehaviour::Hide
}
} else {
help_options.lacking_permissions != HelpBehaviour::Hide
};
}
}
} else if command_options.help_available {
return if has_correct_permissions(&cache, command_options, msg) {
true
} else {
help_options.lacking_permissions != HelpBehaviour::Hide
};
}
} else {
return help_options.wrong_channel != HelpBehaviour::Hide;
}

false
}

/// Checks if `search_on` starts with `word` and is then cleanly followed by a
/// `" "`.
#[inline]
Expand Down Expand Up @@ -347,6 +304,51 @@ fn find_any_command_matches(
}).cloned()
}

#[cfg(all(feature = "cache", feature = "http"))]
fn check_command_behaviour(
msg: &Message,
options: &impl CommonOptions,
owners: &HashSet<UserId, impl BuildHasher>,
help_options: &HelpOptions,
cache: &CacheRwLock,
) -> HelpBehaviour {
if !&options.help_available() {
return HelpBehaviour::Hide;
}

if options.only_in() == OnlyIn::None
|| options.only_in() == OnlyIn::Dm && msg.is_private()
|| options.only_in() == OnlyIn::Guild && !msg.is_private()
{
if options.owners_only() && !owners.contains(&msg.author.id) {
return help_options.lacking_ownership.clone();
}

if has_correct_permissions(&cache, options, msg) {

if let Some(guild) = msg.guild(&cache) {
let guild = guild.read();

if let Some(member) = guild.members.get(&msg.author.id) {
if has_correct_roles(options, &guild, &member) {
return HelpBehaviour::Nothing;
} else {
return help_options.lacking_role.clone();
}
}
} else {
return HelpBehaviour::Nothing;
}
} else {
return help_options.lacking_permissions.clone();
}
} else {
return help_options.wrong_channel.clone();
}

HelpBehaviour::Nothing
}

#[cfg(all(feature = "cache", feature = "http"))]
fn nested_group_command_search<'a>(
cache: &CacheRwLock,
Expand All @@ -355,11 +357,27 @@ fn nested_group_command_search<'a>(
help_options: &'a HelpOptions,
msg: &Message,
similar_commands: &mut Vec<SuggestedCommandName>,
owners: &HashSet<UserId, impl BuildHasher>,
) -> Result<CustomisedHelpData<'a>, ()> {
for group in groups {
let group = *group;
let mut found: Option<&'static InternalCommand> = None;

let group_behaviour = check_command_behaviour(
&msg,
&group.options,
&owners,
&help_options,
&cache,
);

match &group_behaviour {
HelpBehaviour::Nothing => (),
_ => {
continue;
}
}

for command in group.commands {
let command = *command;

Expand All @@ -377,7 +395,13 @@ fn nested_group_command_search<'a>(
if let Some(n) = search_command_name_matched {

if n == command.options.names[0] {
if is_command_visible(&cache, &command.options, msg, help_options) {
if HelpBehaviour::Nothing == check_command_behaviour(
&msg,
&command.options,
&owners,
&help_options,
&cache,
) {
found = Some(command);
} else {
break;
Expand All @@ -400,7 +424,13 @@ fn nested_group_command_search<'a>(
let levenshtein_distance = levenshtein_distance(&command_name, &name);

if levenshtein_distance <= help_options.max_levenshtein_distance
&& is_command_visible(&cache, &command.options, &msg, &help_options)
&& HelpBehaviour::Nothing == check_command_behaviour(
&msg,
&command.options,
&owners,
&help_options,
&cache,
)
{
similar_commands.push(SuggestedCommandName {
name: command_name,
Expand Down Expand Up @@ -460,12 +490,13 @@ fn nested_group_command_search<'a>(
}

match nested_group_command_search(
&cache,
cache,
&group.sub_groups,
name,
&help_options,
&msg,
help_options,
msg,
similar_commands,
owners,
) {
Ok(found) => return Ok(found),
Err(()) => (),
Expand All @@ -485,6 +516,7 @@ fn fetch_single_command<'a>(
name: &str,
help_options: &'a HelpOptions,
msg: &Message,
owners: &HashSet<UserId, impl BuildHasher>,
) -> Result<CustomisedHelpData<'a>, Vec<SuggestedCommandName>> {
let mut similar_commands: Vec<SuggestedCommandName> = Vec::new();
let cache = cache.as_ref();
Expand All @@ -497,6 +529,7 @@ fn fetch_single_command<'a>(
&help_options,
&msg,
&mut similar_commands,
&owners,
) {
Ok(found) => Ok(found),
Err(()) => Err(similar_commands),
Expand All @@ -512,53 +545,55 @@ fn fill_eligible_commands<'a>(
group: &'a CommandGroup,
msg: &Message,
to_fill: &mut GroupCommandsPair,
highest_formatter: &mut HelpBehaviour,
) {
to_fill.name = group.name;
to_fill.prefixes = group.options.prefixes.to_vec();

let group_behaviour = {
if let HelpBehaviour::Hide = highest_formatter {
HelpBehaviour::Hide
} else {
std::cmp::max(
*highest_formatter,
check_command_behaviour(
msg,
&group.options,
owners,
help_options,
&context.cache,
)
)
}
};

*highest_formatter = group_behaviour;

for command in commands {
let command = *command;
let options = &command.options;
let name = &options.names[0];

if !options.help_available {
continue;
}

if options.only_in == OnlyIn::None
|| options.only_in == OnlyIn::Dm && msg.is_private()
|| options.only_in == OnlyIn::Guild && !msg.is_private()
{
if options.owners_only && !owners.contains(&msg.author.id) {
let name = format_command_name!(&help_options.lacking_ownership, &name);
match &group_behaviour {
HelpBehaviour::Nothing => (),
_ => {
let name = format_command_name!(&group_behaviour, &name);
to_fill.command_names.push(name);

continue;
}
}

if has_correct_permissions(&context.cache, &options, msg) {
if let Some(guild) = msg.guild(&context.cache) {
let guild = guild.read();
let command_behaviour = check_command_behaviour(
msg,
&command.options,
owners,
help_options,
&context.cache,
);

if let Some(member) = guild.members.get(&msg.author.id) {
if has_correct_roles(&options, &guild, &member) {
to_fill.command_names.push(format!("`{}`", &name));
} else {
let name = format_command_name!(&help_options.lacking_role, &name);
to_fill.command_names.push(name);
}
}
} else {
to_fill.command_names.push(format!("`{}`", &name));
}
} else {
let name = format_command_name!(&help_options.lacking_permissions, &name);
to_fill.command_names.push(name);
}
} else {
let name = format_command_name!(&help_options.wrong_channel, &name);
to_fill.command_names.push(name);
}
let name = format_command_name!(command_behaviour, &name);
to_fill.command_names.push(name);
}
}

Expand All @@ -571,17 +606,31 @@ fn fetch_all_eligible_commands_in_group<'a>(
help_options: &'a HelpOptions,
group: &'a CommandGroup,
msg: &Message,
highest_formatter: HelpBehaviour,
) -> GroupCommandsPair {
let mut group_with_cmds = GroupCommandsPair::default();
let mut highest_formatter = highest_formatter;

fill_eligible_commands(&context, &commands, &owners,
&help_options, &group, &msg,
&mut group_with_cmds);
fill_eligible_commands(
&context,
&commands,
&owners,
&help_options,
&group,
&msg,
&mut group_with_cmds,
&mut highest_formatter,
);

for sub_group in group.sub_groups {
let grouped_cmd = fetch_all_eligible_commands_in_group(
&context, &sub_group.commands, &owners,
&help_options, &sub_group, &msg
&context,
&sub_group.commands,
&owners,
&help_options,
&sub_group,
&msg,
highest_formatter,
);

group_with_cmds.sub_groups.push(grouped_cmd);
Expand Down Expand Up @@ -631,6 +680,7 @@ fn create_single_group(
&help_options,
&group,
&msg,
HelpBehaviour::Nothing,
);

group_with_cmds.name = group.name;
Expand All @@ -655,7 +705,7 @@ pub fn create_customised_help_data<'a>(
if !args.is_empty() {
let name = args.message();

return match fetch_single_command(&cache, &groups, &name, &help_options, &msg) {
return match fetch_single_command(&cache, &groups, &name, &help_options, &msg, owners) {
Ok(single_command) => single_command,
Err(suggestions) => {
let searched_named_lowercase = name.to_lowercase();
Expand Down Expand Up @@ -776,16 +826,19 @@ fn flatten_group_to_string(
let _ = writeln!(group_text, "{}", joined_commands);

for sub_group in &group.sub_groups {
let mut sub_group_text = String::default();

flatten_group_to_string(
&mut sub_group_text,
&sub_group,
nest_level + 1,
&help_options,
);

if !sub_group.command_names.is_empty() {
let mut sub_group_text = String::default();

flatten_group_to_string(
&mut sub_group_text,
&sub_group,
nest_level + 1,
&help_options,
);

let _ = write!(group_text, "{}", sub_group_text);
let _ = write!(group_text, "{}", sub_group_text);
}
}
}

Expand Down

0 comments on commit eca204a

Please sign in to comment.