From 6d4ac31d79b8d01b9303a187e765abb5ff83af9f Mon Sep 17 00:00:00 2001 From: Pingex Date: Sat, 14 Jan 2017 18:14:34 +0100 Subject: [PATCH] Effective removal of the old permissions system. --- .../dcf/DiscordCommandableFramework.java | 2 - .../java/net/pingex/dcf/commands/Command.java | 34 +-- .../pingex/dcf/commands/CommandHandler.java | 2 - .../pingex/dcf/commands/InternalCommands.java | 12 +- .../dcf/permissions/DefaultPermission.java | 63 ---- .../ICommandPermissionsProvider.java | 2 +- .../dcf/permissions/PermissionsCommands.java | 269 ------------------ .../dcf/permissions/PermissionsHandler.java | 82 ------ 8 files changed, 7 insertions(+), 459 deletions(-) delete mode 100644 src/main/java/net/pingex/dcf/permissions/DefaultPermission.java delete mode 100644 src/main/java/net/pingex/dcf/permissions/PermissionsCommands.java delete mode 100644 src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java diff --git a/src/main/java/net/pingex/dcf/DiscordCommandableFramework.java b/src/main/java/net/pingex/dcf/DiscordCommandableFramework.java index db1a357..bb0fd64 100644 --- a/src/main/java/net/pingex/dcf/DiscordCommandableFramework.java +++ b/src/main/java/net/pingex/dcf/DiscordCommandableFramework.java @@ -5,7 +5,6 @@ import net.pingex.dcf.commands.InternalCommands; import net.pingex.dcf.core.Configuration; import net.pingex.dcf.core.GatewayConnectionManager; import net.pingex.dcf.modularity.PluginLoader; -import net.pingex.dcf.permissions.PermissionsCommands; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import sx.blah.discord.api.ClientBuilder; @@ -45,7 +44,6 @@ public class DiscordCommandableFramework // Register internal commands new InternalCommands().getCommands().forEach(CommandRegistry::registerCommand); - new PermissionsCommands().getCommands().forEach(CommandRegistry::registerCommand); // Run plugins PluginLoader.getInstance().bulkRunPlugins(); diff --git a/src/main/java/net/pingex/dcf/commands/Command.java b/src/main/java/net/pingex/dcf/commands/Command.java index 54d53d8..83b4825 100644 --- a/src/main/java/net/pingex/dcf/commands/Command.java +++ b/src/main/java/net/pingex/dcf/commands/Command.java @@ -1,7 +1,6 @@ package net.pingex.dcf.commands; import net.pingex.dcf.commands.options.ICommandOption; -import net.pingex.dcf.permissions.DefaultPermission; import sx.blah.discord.handle.impl.events.MessageReceivedEvent; import java.lang.reflect.Method; import java.util.Arrays; @@ -39,12 +38,6 @@ public abstract class Command implements ICommandExecutor */ private String usage; - /** - * Default permission, ie. when the permissions provider doesn't return anything. - */ - @Deprecated - private DefaultPermission defaultPermission; - /** * Contains all options for this command. */ @@ -57,17 +50,15 @@ public abstract class Command implements ICommandExecutor * @param description Description of the command * @param isEnabled Is the command enabled ? * @param usage Command usage help - * @param defaultPermission Default permission, ie. when the permissions provider doesn't return anything. * @param options Command options. */ - public Command(String name, List aliases, String description, boolean isEnabled, String usage, @Deprecated DefaultPermission defaultPermission, Set options) + public Command(String name, List aliases, String description, boolean isEnabled, String usage, Set options) { this.name = name; this.aliases = aliases; this.description = description; this.isEnabled = isEnabled; this.usage = usage; - this.defaultPermission = defaultPermission; this.commandOptions = options; } @@ -80,7 +71,6 @@ public abstract class Command implements ICommandExecutor public static final String DESCRIPTION = "No command description provided."; public static final boolean IS_ENABLED = true; public static final String USAGE = "No command usage provided."; - public static final DefaultPermission DEFAULT_PERMISSION = DefaultPermission.EVERYONE; public static final Set OPTIONS = Collections.emptySet(); } @@ -114,11 +104,6 @@ public abstract class Command implements ICommandExecutor */ private String usage = Defaults.USAGE; - /** - * Default permission, ie. when the permissions provider doesn't return anything. - */ - private DefaultPermission defaultPermission = Defaults.DEFAULT_PERMISSION; - /** * Command options */ @@ -163,13 +148,6 @@ public abstract class Command implements ICommandExecutor return this; } - @Deprecated - public Builder defaultPermission(DefaultPermission defaultPermission) - { - this.defaultPermission = defaultPermission; - return this; - } - public Builder options(Set options) { commandOptions = options; @@ -183,7 +161,7 @@ public abstract class Command implements ICommandExecutor */ public Command build(ICommandExecutor toExecute) { - return new Command(name, aliases, description, isEnabled, usage, defaultPermission, commandOptions) + return new Command(name, aliases, description, isEnabled, usage, commandOptions) { @Override public void execute(MessageReceivedEvent event, List arguments) throws Throwable @@ -201,7 +179,7 @@ public abstract class Command implements ICommandExecutor */ public Command build(Method target, Object invokable) { - return new Command(name, aliases, description, isEnabled, usage, defaultPermission, commandOptions) + return new Command(name, aliases, description, isEnabled, usage, commandOptions) { @Override public void execute(MessageReceivedEvent event, List arguments) throws Throwable @@ -247,12 +225,6 @@ public abstract class Command implements ICommandExecutor return usage; } - @Deprecated - public DefaultPermission getDefaultPermission() - { - return defaultPermission; - } - public Set getOptions() { return Collections.unmodifiableSet(commandOptions); diff --git a/src/main/java/net/pingex/dcf/commands/CommandHandler.java b/src/main/java/net/pingex/dcf/commands/CommandHandler.java index 3924a0d..1973de0 100644 --- a/src/main/java/net/pingex/dcf/commands/CommandHandler.java +++ b/src/main/java/net/pingex/dcf/commands/CommandHandler.java @@ -7,13 +7,11 @@ import net.pingex.dcf.commands.parser.ICommandParser; import net.pingex.dcf.commands.parser.ParserException; import net.pingex.dcf.core.Configuration; import net.pingex.dcf.modularity.PluginWrapper; -import net.pingex.dcf.permissions.PermissionsHandler; import net.pingex.dcf.util.DiscordInteractionsUtil; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import sx.blah.discord.handle.impl.events.MessageReceivedEvent; -import java.lang.reflect.Method; import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutorService; diff --git a/src/main/java/net/pingex/dcf/commands/InternalCommands.java b/src/main/java/net/pingex/dcf/commands/InternalCommands.java index 70fe68b..c9a7e1a 100644 --- a/src/main/java/net/pingex/dcf/commands/InternalCommands.java +++ b/src/main/java/net/pingex/dcf/commands/InternalCommands.java @@ -3,14 +3,11 @@ package net.pingex.dcf.commands; import net.pingex.dcf.commands.options.ICommandOption; import net.pingex.dcf.commands.options.ScopeOption; import net.pingex.dcf.core.Configuration; -import net.pingex.dcf.permissions.DefaultPermission; -import net.pingex.dcf.permissions.PermissionsHandler; import net.pingex.dcf.util.ArgumentParser; import net.pingex.dcf.util.DiscordInteractionsUtil; import org.apache.commons.lang3.StringUtils; import sx.blah.discord.handle.impl.events.MessageReceivedEvent; import java.util.*; -import java.util.stream.Collectors; /** * Internal commands of DCF. @@ -33,7 +30,6 @@ public class InternalCommands implements IWithCommands private static final String DESCRIPTION = "List all available commands."; private static final boolean IS_ENABLED = true; private static final String USAGE = "Page"; - private static final DefaultPermission DEFAULT_PERMISSION = DefaultPermission.EVERYONE; private static final Set OPTIONS = Collections.singleton(new ScopeOption(ScopeOption.CommandScope.ANYWHERE)); /** @@ -45,15 +41,14 @@ public class InternalCommands implements IWithCommands private ListCommand() { - super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, DEFAULT_PERMISSION, OPTIONS); + super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, OPTIONS); } @Override public void execute(MessageReceivedEvent event, List arguments) { // Parameters - Set bank = CommandRegistry.getRegistry() - .stream().filter(command -> PermissionsHandler.canRunCommand(event.getMessage(), command)).collect(Collectors.toSet()); + Set bank = CommandRegistry.getRegistry(); int amountPages = (int) Math.ceil(bank.size()/(double)COMMANDS_PER_PAGE); int requestedPage; @@ -115,14 +110,13 @@ public class InternalCommands implements IWithCommands private static final String DESCRIPTION = "Gives the usage of a command."; private static final boolean IS_ENABLED = true; private static final String USAGE = "Command"; - private static final DefaultPermission DEFAULT_PERMISSION = DefaultPermission.EVERYONE; private static final Set OPTIONS = Collections.singleton(new ScopeOption(ScopeOption.CommandScope.ANYWHERE)); static final UsageCommand INSTANCE = new UsageCommand(); private UsageCommand() { - super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, DEFAULT_PERMISSION, OPTIONS); + super(NAME, ALIASES, DESCRIPTION, IS_ENABLED, USAGE, OPTIONS); } @Override diff --git a/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java b/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java deleted file mode 100644 index fafc953..0000000 --- a/src/main/java/net/pingex/dcf/permissions/DefaultPermission.java +++ /dev/null @@ -1,63 +0,0 @@ -package net.pingex.dcf.permissions; - -import net.pingex.dcf.core.Configuration; -import sx.blah.discord.handle.obj.IGuild; -import sx.blah.discord.handle.obj.IUser; -import java.util.function.Predicate; - -/** - * Default behavior when a permissions provider doesn't return `true` or `false` - */ -@Deprecated -public enum DefaultPermission implements Predicate -{ - /** - * Everyone is allowed to run the command. - */ - EVERYONE(tuple -> true), - - /** - * Only the guild owner is allowed to run the command. - */ - GUILD_OWNER(tuple -> tuple.guild != null && tuple.user.getID().equals(tuple.guild.getOwnerID())), - - /** - * Only the bot owner is allowed to run the command. - */ - BOT_OWNER(tuple -> tuple.user.getID().equals(Configuration.BOT_OWNER)), - - /** - * Guild Owner x Bot Owner - */ - ANY_OWNER(tuple -> GUILD_OWNER.test(tuple) || BOT_OWNER.test(tuple)), - - /** - * Nobody is allowed to run the command - */ - NONE(tuple -> false); - - DefaultPermission(Predicate predicate) - { - this.predicate = predicate; - } - - private Predicate predicate; - - @Override - public boolean test(Tuple o) - { - return predicate.test(o); - } - - public static class Tuple - { - IUser user; - IGuild guild; - - public Tuple(IUser user, IGuild guild) - { - this.user = user; - this.guild = guild; - } - } -} diff --git a/src/main/java/net/pingex/dcf/permissions/ICommandPermissionsProvider.java b/src/main/java/net/pingex/dcf/permissions/ICommandPermissionsProvider.java index 59f8af4..fb959d3 100644 --- a/src/main/java/net/pingex/dcf/permissions/ICommandPermissionsProvider.java +++ b/src/main/java/net/pingex/dcf/permissions/ICommandPermissionsProvider.java @@ -6,7 +6,7 @@ import sx.blah.discord.handle.obj.IRole; import sx.blah.discord.handle.obj.IUser; /** - * Provider of permissions, ie. the proxy between {@code PermissionsHandler} and a data storage. + * Provider of permissions, ie. a database which stores permissions data. */ public interface ICommandPermissionsProvider { diff --git a/src/main/java/net/pingex/dcf/permissions/PermissionsCommands.java b/src/main/java/net/pingex/dcf/permissions/PermissionsCommands.java deleted file mode 100644 index e0cd221..0000000 --- a/src/main/java/net/pingex/dcf/permissions/PermissionsCommands.java +++ /dev/null @@ -1,269 +0,0 @@ -package net.pingex.dcf.permissions; - -import net.pingex.dcf.commands.Command; -import net.pingex.dcf.commands.CommandRegistry; -import net.pingex.dcf.commands.IWithCommands; -import net.pingex.dcf.util.ArgumentParser; -import net.pingex.dcf.util.DiscordInteractionsUtil; -import org.apache.commons.lang3.StringUtils; -import sx.blah.discord.handle.impl.events.MessageReceivedEvent; -import sx.blah.discord.handle.obj.IGuild; -import sx.blah.discord.handle.obj.IRole; -import sx.blah.discord.handle.obj.IUser; -import java.util.*; - -/** - * Commands for the permissions package. - */ -public class PermissionsCommands implements IWithCommands -{ - @Override - public Set getCommands() - { - return new HashSet<>(Arrays.asList( - isAllowedCommand, setUser - )); - } - - private static final Command isAllowedCommand = - new Command.Builder("perm:canRun") - .description("Tells whether target user is allowed to run the command.") - .enabled(true) - .usage("Command [verbose]") - .defaultPermission(DefaultPermission.BOT_OWNER) - .build(PermissionsCommands::isAllowedImpl); - - private static void isAllowedImpl(MessageReceivedEvent event, List arguments) - { - // Parameters - Command target = null; - IUser userToLookup = null; - boolean verbose = false; - IGuild targetGuild = null; - ICommandPermissionsProvider provider = PermissionsHandler.getProvider(); - - // Argchk size - if(arguments.size() != 3 && arguments.size() != 4) - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments."); - return; - } - - // Argchk#1 - valid command - Optional uncheckedTarget = CommandRegistry.getCommandOrAliasByName(arguments.get(0)); - if(!uncheckedTarget.isPresent()) // Command existence - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Target command not found."); - return; - } - else target = uncheckedTarget.get(); - - // Argchk#2 - uid OR username#1234 OR "me" - if(arguments.get(1).equalsIgnoreCase("me")) userToLookup = event.getMessage().getAuthor(); // me - else // Anything else - try - { - Optional uncheckedUser = ArgumentParser.checkParseUsernameOrID(arguments.get(1), event.getClient()); - - if(uncheckedUser.isPresent()) userToLookup = uncheckedUser.get(); - else - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "User not found."); - return; - } - } - catch(ArgumentParser.ParserException e) - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments."); - return; - } - - // Argchk#3 - Guild - if(arguments.get(2).equalsIgnoreCase("this")) - try - { - targetGuild = event.getMessage().getGuild(); // this - } - catch(UnsupportedOperationException ignored) // DM - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "`this` is not supported outside a guild."); - return; - } - else if(arguments.get(2).equals("*")) targetGuild = null; // Global - ~NOP - else // Parse Name/ID - { - Optional uncheckedGuild = ArgumentParser.checkParseGuildOrID(arguments.get(2), event.getClient()); - if(uncheckedGuild.isPresent()) targetGuild = uncheckedGuild.get(); - else - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Guild not found."); - return; - } - } - - // Argchk#4 - verbosity - if(arguments.size() == 4) verbose = true; - - // ======================================== - - if(verbose) - { - StringBuilder builder = new StringBuilder() - .append("**Permission walkthrough**\n") - .append("```\n"); - - builder.append("User: ").append(userToLookup.getName()).append("#").append(userToLookup.getDiscriminator()).append("\n"); - builder.append("UID: ").append(userToLookup.getID()).append("\n"); - if(targetGuild != null) - { - builder.append("Guild: ").append(targetGuild.getName()).append("\n"); - builder.append("Guild ID: ").append(targetGuild.getID()).append("\n"); - } - builder.append("Command: ").append(target.getName()).append("\n"); - - builder.append("\n"); - - if(targetGuild != null) - builder.append("UID Guild check: ").append(booleanToString(provider.validateUser(targetGuild, userToLookup, target))).append("\n"); - builder.append("UID Global check: ").append(booleanToString(provider.validateUser(null, userToLookup, target))).append("\n"); - - if(targetGuild != null) - { - builder.append("Role check:\n"); - for(IRole i : targetGuild.getRolesForUser(userToLookup)) - builder.append("> ").append(StringUtils.rightPad(i.getName() + ":", 16)) - .append(booleanToString(provider.validateGroup(i, target))).append("\n"); - } - - builder.append("Default behavior: ") - .append(booleanToString(target.getDefaultPermission().test(new DefaultPermission.Tuple(userToLookup, targetGuild)))) - .append(" (").append(target.getDefaultPermission().toString()).append(")").append("\n"); - - builder.append("```"); - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), builder.toString()); - } - else - { - StringBuilder builder = new StringBuilder() - .append("**Permission summary**\n") - .append("```\n"); - - builder.append("User: ").append(userToLookup.getName()).append("#").append(userToLookup.getDiscriminator()).append("\n"); - builder.append("UID: ").append(userToLookup.getID()).append("\n"); - if(targetGuild != null) - { - builder.append("Guild: ").append(targetGuild.getName()).append("\n"); - builder.append("Guild ID: ").append(targetGuild.getID()).append("\n"); - } - builder.append("Command: ").append(target.getName()).append("\n"); - builder.append("Has Access: ").append(PermissionsHandler.canRunCommand(userToLookup, targetGuild, target) ? "Yes" : "No").append("\n"); - - builder.append("```"); - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), builder.toString()); - } - } - - /** - * Quick helper function to convert a bool to a String. - * @param input Input boolean, can be `null` - * @return "Grant" => `true`, "Deny" => `false`, "N/A" => `null` - */ - private static String booleanToString(Boolean input) - { - return input != null ? (input ? "Grant" : "Deny") : "N/A"; - } - - private static final Command setUser = - new Command.Builder("perm:setUser") - .description("Overrides an user's permission to run a command.") - .enabled(true) - .usage("Command ") - .defaultPermission(DefaultPermission.BOT_OWNER) - .build(PermissionsCommands::setUserImpl); - - private static void setUserImpl(MessageReceivedEvent event, List arguments) - { - // Parameters - IUser targetUser; - IGuild targetGuild = null; - Command targetCommand; - Boolean action; - ICommandPermissionsProvider provider = PermissionsHandler.getProvider(); - - // Argchk size - if(arguments.size() != 3 && arguments.size() != 4) - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments."); - return; - } - - // Argchk#1 Command - Optional uncheckedCommand = CommandRegistry.getCommandOrAliasByName(arguments.get(0)); - if(!uncheckedCommand.isPresent()) // Command existence - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Target command not found."); - return; - } - else targetCommand = uncheckedCommand.get(); - - // Argchk#2 User#Disc/UID - try - { - Optional uncheckedUser = ArgumentParser.checkParseUsernameOrID(arguments.get(1), event.getClient()); - - if(uncheckedUser.isPresent()) targetUser = uncheckedUser.get(); - else - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "User not found."); - return; - } - } - catch(ArgumentParser.ParserException e) - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Invalid arguments."); - return; - } - - // Argchk#3 Guild - if(arguments.get(2).equalsIgnoreCase("this")) - try - { - targetGuild = event.getMessage().getGuild(); // this - } - catch(UnsupportedOperationException ignored) // DM - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "`this` is not supported outside a guild."); - return; - } - else if(arguments.get(2).equals("*")) targetGuild = null; // Global - ~NOP - else // Parse Name/ID - { - Optional uncheckedGuild = ArgumentParser.checkParseGuildOrID(arguments.get(2), event.getClient()); - if(uncheckedGuild.isPresent()) targetGuild = uncheckedGuild.get(); - else - { - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), "Guild not found."); - return; - } - } - - // Argchk#4 true/false/null - action = (Boolean) ArgumentParser.parse(Boolean.class, arguments.size() > 3 ? arguments.get(3) : null); - - // ======================================== - - StringBuilder builder = new StringBuilder("**Changes summary**\n"); - builder.append("```\n"); - builder.append("User: ").append(targetUser.getName()).append("#").append(targetUser.getDiscriminator()).append("\n"); - builder.append("UID: ").append(targetUser.getID()).append("\n"); - builder.append("Guild: ").append(targetGuild != null ? targetGuild.getName() : "[GLOBAL]").append("\n"); - builder.append("Guild ID: ").append(targetGuild != null ? targetGuild.getID() : "N/A").append("\n"); - builder.append("Command: ").append(targetCommand.getName()).append("\n"); - builder.append("New perm: ").append(booleanToString(action)).append("\n"); - builder.append("```\n"); - - provider.setUserPermission(targetGuild, targetUser, targetCommand, action); - builder.append("Changes OK"); - DiscordInteractionsUtil.sendMessage(event.getMessage().getChannel(), builder.toString()); - } -} diff --git a/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java b/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java deleted file mode 100644 index 1eaf080..0000000 --- a/src/main/java/net/pingex/dcf/permissions/PermissionsHandler.java +++ /dev/null @@ -1,82 +0,0 @@ -package net.pingex.dcf.permissions; - -import net.pingex.dcf.commands.Command; -import sx.blah.discord.handle.obj.IGuild; -import sx.blah.discord.handle.obj.IMessage; -import sx.blah.discord.handle.obj.IRole; -import sx.blah.discord.handle.obj.IUser; - -/** - * Landing class of this package. - */ -@Deprecated -public class PermissionsHandler -{ - /** - * Get provider used to check permissions. - */ - public static ICommandPermissionsProvider getProvider() - { - return PermissionCheck.getProvider(); - } - - /** - * Tells whether someone can run requested command. - * - * The validator will successively test the following until something other than `null` is returned. - * - Individual user permission on the guild using {@code ICommandPermissionsProvider.validateUser()} - * - Individual user permission globally using {@code ICommandPermissionsProvider.validateUser()} - * - The user's role list using {@code ICommandPermissionsProvider.validateGroup()} - * - Default behavior included in a Command object - * - * @param targetUser User to check against - * @param targetGuild Guild to check against - * @param command Requested command, as parsed by {@code CommandHandler} - * @return `true` if request is granted, `false` if denied. - */ - public static boolean canRunCommand(IUser targetUser, IGuild targetGuild, Command command) - { - // First check: user permission for guild, skipped if DM - if(targetGuild != null) - { - Boolean canUserGuildRun = getProvider().validateUser(targetGuild, targetUser, command); - if(canUserGuildRun != null) return canUserGuildRun; - } - - // Second check: user permission globally - Boolean canUserRun = getProvider().validateUser(null, targetUser, command); - if(canUserRun != null) return canUserRun; - - // Third check: user role permissions, skipped if DM - if(targetGuild != null && targetGuild.getRolesForUser(targetUser) != null) - for(IRole i : targetGuild.getRolesForUser(targetUser)) - { - Boolean canRoleRun = getProvider().validateGroup(i, command); - if(canRoleRun != null) return canRoleRun; - } - - // Fourth check: default behavior - return command.getDefaultPermission().test(new DefaultPermission.Tuple(targetUser, targetGuild)); - } - - /** - * Shortcut to the other {@code canRunCommand()} method. - * @param request Incoming message - * @param command Command to check against - * @return `true` if request is granted, `false` if denied. - */ - public static boolean canRunCommand(IMessage request, Command command) - { - IUser requestAuthor = request.getAuthor(); - IGuild originatedGuild; - try - { - originatedGuild = request.getGuild(); - } - catch(UnsupportedOperationException uoe) // DM - { - originatedGuild = null; - } - return canRunCommand(requestAuthor, originatedGuild, command); - } -}