Skip to content
This repository has been archived by the owner on Nov 25, 2022. It is now read-only.

Commit

Permalink
Add ModuleDefine, refactor ModuleHelp (#20, #22)
Browse files Browse the repository at this point in the history
- #20: Add ModuleDefine
- #22: Completely refactor ModuleHelp
- Full help output is now alphabetized by name
- Removed redundant help embed fields
- Rearranged CommandType entries in a sensible order
- Removed references to default prefix in default help replies
  • Loading branch information
Mikaël Francoeur authored and zzzowoey committed Jul 26, 2019
1 parent f9593c5 commit 60552b2
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 80 deletions.
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ dependencies {
}
implementation group: 'org.hibernate', name: 'hibernate-core', version: '5.4.3.Final'

// Spring Web for consuming REST APIs
// Logging framework is, again, excluded to avoid conflict
implementation (group: 'org.springframework.data', name: 'spring-data-rest-webmvc', version: '2.1.6.RELEASE') {
exclude group: 'org.slf4j', module: 'jcl-over-slf4j'
}

// Other libraries & APIs
implementation group: 'com.discord4j', name: 'discord4j-core', version: '3.0.7'
implementation group: 'org.aeonbits.owner', name: 'owner', version: '1.0.10'
Expand All @@ -35,6 +41,7 @@ dependencies {
implementation group: 'com.h2database', name: 'h2', version: '1.4.199'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.9'
implementation group: 'javax.validation', name: 'validation-api', version: '2.0.1.Final'
implementation group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'

// JUnit
testCompile group: 'org.junit.jupiter', name: 'junit-jupiter', version: '5.5.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Scans the {@link com.github.coleb1911.ghost2.commands commands} package for valid command {@link Module}s
Expand All @@ -41,9 +42,9 @@ public final class CommandRegistry implements ApplicationListener<ContextRefresh
private AutowireCapableBeanFactory factory;

@ReflectiveAccess
CommandRegistry() {
public CommandRegistry() {
// Instantiate lists
instances = new LinkedList<>();
instances = new ArrayList<>();
invalidModules = new LinkedHashSet<>();

// Find all Modules
Expand Down Expand Up @@ -101,16 +102,16 @@ public ModuleInfo getInfo(String name) {
}

/**
* Get the {@link ModuleInfo} for every {@link Module} found on the classpath
* Get the {@link ModuleInfo} for every {@link Module} found on the classpath.
* The {@code ModuleInfo} objects are sorted alphabetically by name.
*
* @return Associated CommandInfo for all available Modules
* @return Associated ModuleInfo for all available Modules
*/
public List<ModuleInfo> getAllInfo() {
List<ModuleInfo> ret = new ArrayList<>();
for (Module module : instances) {
ret.add(module.getInfo());
}
return ret;
return instances.stream()
.map(Module::getInfo)
.sorted(Comparator.comparing(ModuleInfo::getName))
.collect(Collectors.toUnmodifiableList());
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package com.github.coleb1911.ghost2.commands.meta;

public enum CommandType {
OPERATOR("\u26D4"),
CONFIG("\u2699"),
MUSIC("\uD83C\uDFB5"),
FUN("\uD83C\uDF89"),
INFO("\u2139"),
MODERATION("\uD83D\uDEE1"),
MUSIC("\uD83C\uDFB5"),
UTILITY("\uD83D\uDD27");
CONFIG("\u2699"),
UTILITY("\uD83D\uDD27"),
OPERATOR("\u26D4");

private final String icon;

Expand All @@ -22,4 +22,4 @@ public String getIcon() {
public String getFormattedName() {
return name().charAt(0) + name().substring(1).toLowerCase();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
public abstract class Module {
public static final String REPLY_INSUFFICIENT_PERMISSIONS_USER = "You don't have permission to run that command.";
public static final String REPLY_INSUFFICIENT_PERMISSIONS_BOT = "I don't have sufficient permissions to run that command.";
public static final String REPLY_COMMAND_INVALID = "That command doesn't exist. See \'g!help\' for a list of valid commands and their arguments.";
public static final String REPLY_ARGUMENT_INVALID = "Invalid argument. See \'g!help\' for a list of valid commands and their arguments.";
public static final String REPLY_COMMAND_INVALID = "That command doesn't exist. See `help` for a list of valid commands and their arguments.";
public static final String REPLY_ARGUMENT_INVALID = "Invalid argument. See `help` for a list of valid commands and their arguments.";
public static final String REPLY_GENERAL_ERROR = "Whoops! An error occurred somewhere along the line. My operator has been notified.";

private final ModuleInfo info;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
package com.github.coleb1911.ghost2.commands.modules.info;

import com.github.coleb1911.ghost2.Ghost2Application;
import com.github.coleb1911.ghost2.commands.CommandRegistry;
import com.github.coleb1911.ghost2.commands.meta.CommandContext;
import com.github.coleb1911.ghost2.commands.meta.CommandType;
import com.github.coleb1911.ghost2.commands.meta.Module;
import com.github.coleb1911.ghost2.commands.meta.ModuleInfo;
import com.github.coleb1911.ghost2.commands.meta.ReflectiveAccess;
import org.springframework.beans.factory.annotation.Autowired;

import javax.validation.constraints.NotNull;
import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
* @author cbryant02
* @author LeMikaelF
*/
public final class ModuleHelp extends Module {

@Autowired private CommandRegistry registry;

@ReflectiveAccess
public ModuleHelp() {
super(new ModuleInfo.Builder(ModuleHelp.class)
Expand All @@ -26,72 +34,102 @@ public ModuleHelp() {

@Override
public void invoke(@NotNull final CommandContext ctx) {
CommandRegistry registry = Ghost2Application.getApplicationInstance().getDispatcher().getRegistry();

// Single-command help
if (ctx.getArgs().size() > 0) {
// Fetch & null-check CommandInfo
ModuleInfo info = registry.getInfo(ctx.getArgs().get(0));
if (null == info) {
ctx.reply(Module.REPLY_COMMAND_INVALID);
return;
}
singleCommandHelp(ctx);
} else {
fullCommandList(ctx);
}
}

/**
* Sends a message detailing a single command, or an error message if the command is invalid.
*
* @param ctx The {@code CommandContext} to publish on.
*/
private void singleCommandHelp(@NotNull CommandContext ctx) {
// Fetch & null-check CommandInfo
ModuleInfo info = registry.getInfo(ctx.getArgs().get(0));
if (null == info) {
ctx.reply(Module.REPLY_COMMAND_INVALID);
return;
}

// Build and send embed
ctx.getChannel().createMessage(messageSpec -> messageSpec.setEmbed(embedSpec -> {
String aliasesString = getFormattedListString("", ", ", "", info.getAliases())
.orElse("n/a");

embedSpec.setTitle("Command help");
embedSpec.addField("Name", info.getName(), false);
embedSpec.addField("Description", info.getDescription(), false);
embedSpec.addField("Aliases", aliasesString, false);
embedSpec.addField("Category", info.getType().getIcon() + info.getType().getFormattedName(), false);
})).block();
}

// Build and send embed
ctx.getChannel().createMessage(messageSpec -> messageSpec.setEmbed(embedSpec -> {
String aliasList;
if (info.getAliases().size() == 0) {
aliasList = "n/a";
} else {
StringJoiner joiner = new StringJoiner(", ");
for (String alias : info.getAliases()) {
joiner.add(alias);
}
aliasList = joiner.toString();
}

embedSpec.setTitle("Command help");
embedSpec.addField("Name", info.getName(), false);
embedSpec.addField("Description", info.getDescription(), false);
embedSpec.addField("Aliases", aliasList, false);
embedSpec.addField("Category", info.getType().getIcon() + info.getType().getFormattedName(), false);
})).block();
} else { // Full command list
// Categorize all available modules
Map<CommandType, List<ModuleInfo>> modules = new LinkedHashMap<>();

for (CommandType type : CommandType.values()) {
modules.put(type, new ArrayList<>());
}

for (ModuleInfo info : registry.getAllInfo()) {
modules.get(info.getType()).add(info);
/**
* Sends a message with a formatted embed enumerating all the commands in their respective {@code CommandType}.
*
* @param ctx The {@code CommandContext} to publish on.
*/
private void fullCommandList(@NotNull CommandContext ctx) {
ctx.getChannel().createMessage(messageSpec -> messageSpec.setEmbed(embedSpec -> {
embedSpec.setTitle("Help");
embedSpec.setFooter("See help <command> for help with specific commands", null);

for (Map.Entry<CommandType, List<ModuleInfo>> module : categorizeModules().entrySet()) {
List<String> names = module.getValue().stream()
.map(ModuleInfo::getName)
.collect(Collectors.toList());

CommandType type = module.getKey();
String commandList = getFormattedListString("`", "`, `", "`", names)
.orElse("No commands (...yet)");

embedSpec.addField(type.getIcon() + " " + type.getFormattedName(), commandList, false);
}
})).block();
}


/**
* Takes a list of {@link String Strings}, and if it's empty, returns an empty {@code String}. If not, returns a
* {@code String} beginning with {@code before}, delimits the values with {@code between}, and appends
* {@code after} at the end.
*
* @param prefix A {@code String} to prepend to the result.
* @param delimiter A {@code String} to insert between the list elements (ex.: ", ").
* @param suffix A {@code String} to append to the result.
* @param values The values to be displayed.
* @return A formatted string
*/
private Optional<String> getFormattedListString(String prefix, String delimiter, String suffix, List<String> values) {
if (values.isEmpty()) return Optional.empty();

// Build and send embed
ctx.getChannel().createMessage(messageSpec -> messageSpec.setEmbed(embedSpec -> {
embedSpec.setTitle("Help");
embedSpec.setAuthor(ctx.getSelf().getUsername(), null, ctx.getSelf().getAvatarUrl());
embedSpec.setFooter("See g!help <command> for help with specific commands", null);
embedSpec.setTimestamp(Instant.now());

for (Map.Entry<CommandType, List<ModuleInfo>> module : modules.entrySet()) {
String commandList;
if (module.getValue().isEmpty()) {
commandList = "No commands (...yet)";
} else {
StringJoiner joiner = new StringJoiner(", ");
for (ModuleInfo info : module.getValue()) {
joiner.add("`" + info.getName() + "`");
}
commandList = joiner.toString();
}

CommandType type = module.getKey();
embedSpec.addField(type.getIcon() + " " + type.getFormattedName(), commandList, false);
}
})).block();
StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);
for (String value : values) {
joiner.add(value);
}

return Optional.of(joiner.toString());
}

/**
* Categorizes all available modules.
*
* @return A map of all available sorts of {@link CommandType} and their corresponding {@link ModuleInfo}.
*/
private Map<CommandType, List<ModuleInfo>> categorizeModules() {
Map<CommandType, List<ModuleInfo>> modules = new LinkedHashMap<>();

for (CommandType type : CommandType.values()) {
modules.put(type, new ArrayList<>());
}
for (ModuleInfo info : registry.getAllInfo()) {
modules.get(info.getType()).add(info);
}

return modules;
}
}
Loading

0 comments on commit 60552b2

Please sign in to comment.