diff --git a/.gitignore b/.gitignore
index 054870a..3c7663d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,26 +1,128 @@
+# ---> Java
+# Compiled class file
+*.class
+# Log file
+*.log
+# BlueJ files
+*.ctxt
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+# ---> Windows
+# Windows thumbnail cache files
+Thumbs.db
+Thumbs.db:encryptable
+ehthumbs.db
+ehthumbs_vista.db
+# Dump file
+*.stackdump
+# Folder config file
+[Dd]esktop.ini
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+# Windows Installer files
+*.cab
+*.msi
+*.msix
+*.msm
+*.msp
+# Windows shortcuts
+*.lnk
+# ---> JetBrains
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+# AWS User-specific
+.idea/**/aws.xml
+# Generated files
+.idea/**/contentModel.xml
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+# CMake
+cmake-build-*/
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+# File-based project format
+*.iws
+# IntelliJ
+out/
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+# JIRA plugin
+atlassian-ide-plugin.xml
+# Cursive Clojure plugin
+.idea/replstate.xml
+# SonarLint plugin
+.idea/sonarlint/
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+# Editor-based Rest Client
+.idea/httpRequests
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+# ---> Gradle
.gradle
-build/
-!gradle/wrapper/gradle-wrapper.jar
-!**/src/main/**/build/
-!**/src/test/**/build/
+**/build/
+!src/**/build/
+# Ignore Gradle GUI config
+gradle-app.setting
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+# Cache of project
+.gradletasknamecache
+# Eclipse Gradle plugin generated files
+# Eclipse Core
+.project
+# JDT-specific (Eclipse Java Development Tools)
+.classpath
+
### IntelliJ IDEA ###
-.idea/modules.xml
-.idea/jarRepositories.xml
-.idea/compiler.xml
-.idea/libraries/
-*.iws
-*.iml
-*.ipr
-out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
-.classpath
.factorypath
-.project
.settings
.springBeans
.sts4-cache
@@ -40,4 +142,9 @@ bin/
### Mac OS ###
.DS_Store
-*-config.yml
\ No newline at end of file
+
+# Ignore Gradle build output directory
+build
+
+*-config.yml
+modules/
diff --git a/.idea/.name b/.idea/.name
new file mode 100644
index 0000000..f23a249
--- /dev/null
+++ b/.idea/.name
@@ -0,0 +1 @@
+ModularBot
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..b589d56
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
deleted file mode 100644
index e578f4a..0000000
--- a/.idea/gradle.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..faf1c28
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index 00be228..0000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1660073510005
-
-
- 1660073510005
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/src/main/java/tech/nevets/modbot/CoreBot.java b/src/main/java/tech/nevets/modbot/CoreBot.java
index 88b1808..4feeeaf 100644
--- a/src/main/java/tech/nevets/modbot/CoreBot.java
+++ b/src/main/java/tech/nevets/modbot/CoreBot.java
@@ -8,7 +8,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import tech.nevets.modbot.api.BotModule;
import tech.nevets.modbot.api.Config;
-import tech.nevets.modbot.util.commands.CommandListener;
+import tech.nevets.modbot.util.commands.CoreListener;
import javax.security.auth.login.LoginException;
import java.util.*;
@@ -24,10 +24,11 @@ public class CoreBot {
CORE_CONFIG.addDefaults(getDefaults());
CORE_CONFIG.loadConfig();
modules = ModuleLoader.loadModules();
+ CoreListener coreListener = new CoreListener();
JDABuilder builder = JDABuilder.createDefault(CORE_CONFIG.getConfig().getString("bot.token"))
.enableCache(CacheFlag.VOICE_STATE)
- .addEventListeners(new CommandListener());
+ .addEventListeners(coreListener);
for (ListenerAdapter listener : PLUGIN_LISTENERS) {
builder.addEventListeners(listener);
@@ -39,8 +40,9 @@ public class CoreBot {
e.printStackTrace();
}
- //CommandManager.registerSlashCommands();
+ coreListener.getCommandRegistry().registerSlashCommands();
for (BotModule module : modules) {
+ module.loadCommandRegistry().registerSlashCommands();
module.onEnable(jda);
}
@@ -50,6 +52,8 @@ public class CoreBot {
PLUGIN_LISTENERS.add(listener);
}
+
+
private static Map getDefaults() {
Map defaults = new HashMap<>();
defaults.put("bot.token", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
diff --git a/src/main/java/tech/nevets/modbot/api/BotModule.java b/src/main/java/tech/nevets/modbot/api/BotModule.java
index d9fdaa8..a9a815e 100644
--- a/src/main/java/tech/nevets/modbot/api/BotModule.java
+++ b/src/main/java/tech/nevets/modbot/api/BotModule.java
@@ -1,11 +1,14 @@
package tech.nevets.modbot.api;
import net.dv8tion.jda.api.JDA;
+import tech.nevets.modbot.api.commands.CommandRegistry;
public interface BotModule {
default void onPreEnable() {}
+ CommandRegistry loadCommandRegistry();
+
void onEnable(JDA jda);
void onDisable();
diff --git a/src/main/java/tech/nevets/modbot/api/commands/CommandRegistry.java b/src/main/java/tech/nevets/modbot/api/commands/CommandRegistry.java
new file mode 100644
index 0000000..5fb595a
--- /dev/null
+++ b/src/main/java/tech/nevets/modbot/api/commands/CommandRegistry.java
@@ -0,0 +1,154 @@
+package tech.nevets.modbot.api.commands;
+
+import net.dv8tion.jda.api.entities.Member;
+import net.dv8tion.jda.api.entities.Role;
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.nevets.modbot.CoreBot;
+import tech.nevets.modbot.util.commands.CommandContext;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class CommandRegistry {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CommandRegistry.class);
+ private final List commands = new ArrayList<>();
+ private final List prefixCommands = new ArrayList<>();
+ private final List slashCommands = new ArrayList<>();
+
+ public CommandRegistry() {}
+
+ public void addCommand(ICommand cmd) {
+ boolean nameFound = commands.stream().anyMatch(it -> it.getName().equalsIgnoreCase(cmd.getName()));
+ if (nameFound) {
+ throw new IllegalArgumentException("A command with that name is already present!");
+ }
+
+ commands.add(cmd);
+
+ if (cmd instanceof IPrefixCommand) {
+ prefixCommands.add((IPrefixCommand) cmd);
+ }
+
+ if (cmd instanceof ISlashCommand) {
+ slashCommands.add((ISlashCommand) cmd);
+ }
+ }
+
+ public void registerSlashCommands() {
+ if (slashCommands.size() > 0) {
+ List cmdDataList = new ArrayList<>();
+ for (ISlashCommand slashCmd : slashCommands) {
+ cmdDataList.add(slashCmd.getCommandData());
+ }
+ CoreBot.jda.updateCommands().addCommands(cmdDataList).queue();
+ }
+ }
+
+ @Nullable
+ public IPrefixCommand getPrefixCommand(String search) {
+ String searchLower = search.toLowerCase();
+
+ for (IPrefixCommand cmd : prefixCommands) {
+ if (cmd.getName().equals(searchLower) || cmd.getAliases().contains(searchLower)) {
+ return cmd;
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ public ISlashCommand getSlashCommand(String search) {
+ String searchLower = search.toLowerCase();
+
+ for (ISlashCommand cmd : slashCommands) {
+ if (cmd.getName().equals(searchLower) || cmd.getAliases().contains(searchLower)) {
+ return cmd;
+ }
+ }
+ return null;
+ }
+
+ public List getCommands() {
+ return commands;
+ }
+
+ public List getPrefixCommands() {
+ return prefixCommands;
+ }
+
+ public List getSlashCommands() {
+ return slashCommands;
+ }
+
+ public boolean hasPermission(Member member, ICommand cmd) {
+ List memberRoles = member.getRoles();
+ int permLevel = 0;
+
+ for (Role memberRole : memberRoles) {
+ switch (memberRole.getName().toLowerCase()) {
+ case "helper", "1":
+ if (permLevel < 1) permLevel = 1;
+ continue;
+ case "moderator", "mod", "2":
+ if (permLevel < 2) permLevel = 2;
+ continue;
+ case "administrator", "admin", "3":
+ if (permLevel < 3) permLevel = 3;
+ continue;
+ case "owner", "4":
+ if (permLevel < 4) permLevel = 4;
+ }
+ }
+ return permLevel >= cmd.getPermissionLevel();
+ }
+
+ public void handle(GuildMessageReceivedEvent event) {
+ String[] split = event.getMessage().getContentRaw()
+ .replaceFirst("(?i)" + Pattern.quote(CoreBot.CORE_CONFIG.getConfig().getString("bot.prefix")), "")
+ .split("\\s+");
+
+ String invoke = split[0].toLowerCase();
+ IPrefixCommand cmd = getPrefixCommand(invoke);
+
+ assert event.getMember() != null;
+ if (!hasPermission(event.getMember(), cmd)) {
+ event.getMessage().reply("You do not have permission to use this command.").queue();
+ return;
+ }
+
+ if (cmd != null) {
+ event.getChannel().sendTyping().queue();
+ List args = Arrays.asList(split).subList(1, split.length);
+
+ CommandContext ctx = new CommandContext(event, args);
+
+ cmd.handle(ctx);
+ }
+ }
+
+ public void handle(SlashCommandEvent event) {
+ String invoke = event.getName();
+ ISlashCommand cmd = getSlashCommand(invoke);
+
+ if (cmd != null) {
+ event.deferReply().queue();
+
+ assert event.getMember() != null;
+ if (!hasPermission(event.getMember(), cmd)) {
+ event.getHook().sendMessage("You do not have permission to use this command.").queue();
+ return;
+ }
+
+ CommandContext ctx = new CommandContext(event);
+
+ cmd.handleSlash(ctx);
+ }
+ }
+}
diff --git a/src/main/java/tech/nevets/modbot/api/commands/ICommand.java b/src/main/java/tech/nevets/modbot/api/commands/ICommand.java
index fdbe68d..1220c4e 100644
--- a/src/main/java/tech/nevets/modbot/api/commands/ICommand.java
+++ b/src/main/java/tech/nevets/modbot/api/commands/ICommand.java
@@ -1,4 +1,22 @@
package tech.nevets.modbot.api.commands;
+import java.util.List;
+
public interface ICommand {
+
+ String getName();
+
+ /**
+ * @apiNote 0 = default, 1 = helper, 2 = moderator, 3 = admin, 4 = owner
+ * @return int
+ */
+ default int getPermissionLevel() {
+ return 0;
+ }
+
+ String getHelp();
+
+ default List getAliases(){
+ return List.of();
+ }
}
diff --git a/src/main/java/tech/nevets/modbot/api/commands/IPrefixCommand.java b/src/main/java/tech/nevets/modbot/api/commands/IPrefixCommand.java
index 3f01d0a..31db4d2 100644
--- a/src/main/java/tech/nevets/modbot/api/commands/IPrefixCommand.java
+++ b/src/main/java/tech/nevets/modbot/api/commands/IPrefixCommand.java
@@ -1,4 +1,9 @@
package tech.nevets.modbot.api.commands;
+import tech.nevets.modbot.util.commands.CommandContext;
+
public interface IPrefixCommand extends ICommand {
+
+ void handle(CommandContext ctx);
+
}
diff --git a/src/main/java/tech/nevets/modbot/api/commands/ISlashCommand.java b/src/main/java/tech/nevets/modbot/api/commands/ISlashCommand.java
index 6f45f7a..c0f692e 100644
--- a/src/main/java/tech/nevets/modbot/api/commands/ISlashCommand.java
+++ b/src/main/java/tech/nevets/modbot/api/commands/ISlashCommand.java
@@ -1,4 +1,14 @@
package tech.nevets.modbot.api.commands;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import tech.nevets.modbot.util.commands.CommandContext;
+
public interface ISlashCommand extends ICommand {
+
+ void handleSlash(CommandContext ctx);
+
+ String getDescription();
+
+ CommandData getCommandData();
+
}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/CommandContext.java b/src/main/java/tech/nevets/modbot/util/commands/CommandContext.java
new file mode 100644
index 0000000..ccb17d4
--- /dev/null
+++ b/src/main/java/tech/nevets/modbot/util/commands/CommandContext.java
@@ -0,0 +1,36 @@
+package tech.nevets.modbot.util.commands;
+
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+
+import java.util.List;
+
+public class CommandContext implements ICommandContext {
+ private GuildMessageReceivedEvent event;
+ private SlashCommandEvent slashEvent;
+ private List args;
+
+ public CommandContext(GuildMessageReceivedEvent event, List args) {
+ this.event = event;
+ this.args = args;
+ }
+
+ public CommandContext(SlashCommandEvent slashEvent) {
+ this.slashEvent = slashEvent;
+ this.args = null;
+ }
+
+ @Override
+ public GuildMessageReceivedEvent getEvent() {
+ return this.event;
+ }
+
+ @Override
+ public SlashCommandEvent getSlashEvent() {
+ return this.slashEvent;
+ }
+
+ public List getArgs() {
+ return this.args;
+ }
+}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/CommandListener.java b/src/main/java/tech/nevets/modbot/util/commands/CommandListener.java
deleted file mode 100644
index d6c9bea..0000000
--- a/src/main/java/tech/nevets/modbot/util/commands/CommandListener.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package tech.nevets.modbot.util.commands;
-
-import net.dv8tion.jda.api.hooks.ListenerAdapter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class CommandListener extends ListenerAdapter {
- private static final Logger LOGGER = LoggerFactory.getLogger(CommandListener.class);
- private final CommandManager commandManager = new CommandManager();
-}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/CommandManager.java b/src/main/java/tech/nevets/modbot/util/commands/CommandManager.java
index dbaa129..3e5f4dc 100644
--- a/src/main/java/tech/nevets/modbot/util/commands/CommandManager.java
+++ b/src/main/java/tech/nevets/modbot/util/commands/CommandManager.java
@@ -1,9 +1,71 @@
package tech.nevets.modbot.util.commands;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import tech.nevets.modbot.api.commands.CommandRegistry;
+import tech.nevets.modbot.api.commands.ICommand;
+import tech.nevets.modbot.api.commands.IPrefixCommand;
+import tech.nevets.modbot.api.commands.ISlashCommand;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
public class CommandManager {
- private static final Logger LOGGER = LoggerFactory.getLogger(CommandManager.class);
+ public static final List ALL_COMMANDS = new ArrayList<>();
+ public static final List ALL_PREFIX_COMMANDS = new ArrayList<>();
+ public static final List ALL_SLASH_COMMANDS = new ArrayList<>();
+ public static void addFromRegistry(CommandRegistry registry) {
+ ALL_COMMANDS.addAll(registry.getCommands());
+ ALL_PREFIX_COMMANDS.addAll(registry.getPrefixCommands());
+ ALL_SLASH_COMMANDS.addAll(registry.getSlashCommands());
+ }
+
+ @Nullable
+ public static ICommand getCommand(String search) {
+ String searchLower = search.toLowerCase();
+
+ for (ICommand cmd : ALL_COMMANDS) {
+ if (cmd.getName().equals(searchLower) || cmd.getAliases().contains(searchLower)) {
+ return cmd;
+ }
+ }
+ return null;
+ }
+
+ public static List getAllCommands() {
+ return ALL_COMMANDS;
+ }
+
+
+ @Nullable
+ public static IPrefixCommand getPrefixCommand(String search) {
+ String searchLower = search.toLowerCase();
+
+ for (IPrefixCommand cmd : ALL_PREFIX_COMMANDS) {
+ if (cmd.getName().equals(searchLower) || cmd.getAliases().contains(searchLower)) {
+ return cmd;
+ }
+ }
+ return null;
+ }
+
+ public static List getAllPrefixCommands() {
+ return ALL_PREFIX_COMMANDS;
+ }
+
+ @Nullable
+ public static ISlashCommand getSlashCommand(String search) {
+ String searchLower = search.toLowerCase();
+
+ for (ISlashCommand cmd : ALL_SLASH_COMMANDS) {
+ if (cmd.getName().equals(searchLower) || cmd.getAliases().contains(searchLower)) {
+ return cmd;
+ }
+ }
+ return null;
+ }
+
+ public static List getAllSlashCommands() {
+ return ALL_SLASH_COMMANDS;
+ }
}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/CoreListener.java b/src/main/java/tech/nevets/modbot/util/commands/CoreListener.java
new file mode 100644
index 0000000..7bbc7e4
--- /dev/null
+++ b/src/main/java/tech/nevets/modbot/util/commands/CoreListener.java
@@ -0,0 +1,52 @@
+package tech.nevets.modbot.util.commands;
+
+import net.dv8tion.jda.api.entities.User;
+import net.dv8tion.jda.api.events.ReadyEvent;
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tech.nevets.modbot.CoreBot;
+import tech.nevets.modbot.api.commands.CommandRegistry;
+
+import javax.annotation.Nonnull;
+
+public class CoreListener extends ListenerAdapter {
+ private static final Logger LOGGER = LoggerFactory.getLogger(CoreListener.class);
+ private final CommandRegistry registry = new CommandRegistry();
+
+ {
+ registry.addCommand(new HelpCmd());
+ registry.addCommand(new TestCmd());
+ CommandManager.addFromRegistry(registry);
+ }
+
+ public CommandRegistry getCommandRegistry() {
+ return registry;
+ }
+
+ @Override
+ public void onReady(@Nonnull ReadyEvent event) {
+ LOGGER.info("BuzzBot is ready: " + event.getJDA().getSelfUser().getAsTag());
+ }
+
+ @Override
+ public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
+ User user = event.getAuthor();
+ String raw = event.getMessage().getContentRaw();
+
+ if (user.isBot() || event.isWebhookMessage()) {
+ return;
+ }
+
+ if (raw.startsWith(CoreBot.CORE_CONFIG.getConfig().getString("bot.prefix"))) {
+ registry.handle(event);
+ }
+ }
+
+ @Override
+ public void onSlashCommand(@Nonnull SlashCommandEvent event) {
+ registry.handle(event);
+ }
+}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/HelpCmd.java b/src/main/java/tech/nevets/modbot/util/commands/HelpCmd.java
new file mode 100644
index 0000000..32ededb
--- /dev/null
+++ b/src/main/java/tech/nevets/modbot/util/commands/HelpCmd.java
@@ -0,0 +1,98 @@
+package tech.nevets.modbot.util.commands;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.interactions.commands.Command;
+import net.dv8tion.jda.api.interactions.commands.OptionMapping;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import net.dv8tion.jda.api.interactions.commands.build.OptionData;
+import org.jetbrains.annotations.NotNull;
+import tech.nevets.modbot.CoreBot;
+import tech.nevets.modbot.api.commands.ICommand;
+import tech.nevets.modbot.api.commands.IPrefixCommand;
+import tech.nevets.modbot.api.commands.ISlashCommand;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class HelpCmd implements ISlashCommand {
+
+ @Override
+ public void handleSlash(CommandContext ctx) {
+
+ StringBuilder sb = new StringBuilder();
+ EmbedBuilder eb = new EmbedBuilder();
+
+ OptionMapping option = ctx.getSlashEvent().getOption("command");
+ String cmd = null;
+ if (option != null) {
+ cmd = option.getAsString();
+ }
+
+ if (cmd == null){
+ IPrefixCommand prefixCommand = CommandManager.getPrefixCommand(ctx.getSlashEvent().getName());
+ ISlashCommand slashCommand = CommandManager.getSlashCommand(ctx.getSlashEvent().getName());
+
+ if (prefixCommand == null && slashCommand == null) {
+ ctx.getSlashEvent().getHook().sendMessage("Nothing found for " + ctx.getSlashEvent()).queue();
+ return;
+ }
+
+ eb.setTitle("Commands");
+
+ CommandManager.getAllCommands().stream().map(ICommand::getName).forEach(
+ (it) -> sb.append('`').append(CoreBot.CORE_CONFIG.getConfig().getString("bot.prefix")).append(it).append("`\n")
+ );
+
+ eb.addField("", sb.toString(), false);
+ } else {
+ ICommand command = CommandManager.getCommand(cmd);
+
+ if (command != null) {
+ eb.setTitle(command.getName());
+ eb.addField("", command.getHelp(), false);
+ } else {
+ eb.setTitle("Error");
+ eb.addField("", "Unknown command, run `/help` to see a list of available commands", false);
+ }
+ }
+
+ ctx.getSlashEvent().getHook().sendMessageEmbeds(eb.build()).queue();
+ }
+
+ @Override
+ public String getName() {
+ return "help";
+ }
+
+ @Override
+ public String getHelp() {
+ return "Shows the list of bot commands\n" +
+ "Usage: `" + CoreBot.CORE_CONFIG.getConfig().getString("bot.prefix") + "help [command]`";
+ }
+
+
+ @Override
+ public String getDescription() {
+ return "Lists bot commands";
+ }
+
+ @Override
+ public @NotNull CommandData getCommandData() {
+ List choices = new ArrayList<>();
+
+ for (IPrefixCommand cmd : CommandManager.getAllPrefixCommands()) {
+ choices.add(new Command.Choice(cmd.getName(), cmd.getName()));
+ }
+
+ for (ISlashCommand cmd : CommandManager.getAllSlashCommands()) {
+ choices.add(new Command.Choice(cmd.getName(), cmd.getName()));
+ }
+
+ OptionData option = new OptionData(OptionType.STRING, "command", "Gives info on specified command", false);
+ option.addChoices(choices);
+
+ return new CommandData(this.getName(), this.getDescription())
+ .addOptions(option);
+ }
+}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/ICommandContext.java b/src/main/java/tech/nevets/modbot/util/commands/ICommandContext.java
new file mode 100644
index 0000000..6aa18ec
--- /dev/null
+++ b/src/main/java/tech/nevets/modbot/util/commands/ICommandContext.java
@@ -0,0 +1,150 @@
+package tech.nevets.modbot.util.commands;
+
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.*;
+import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
+import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
+import net.dv8tion.jda.api.interactions.InteractionHook;
+import net.dv8tion.jda.api.sharding.ShardManager;
+
+public interface ICommandContext {
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.Guild} for the current command/event
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.Guild} for this command/event
+ */
+ default Guild getGuild() {
+ if (this.getEvent() == null) {
+ return this.getSlashEvent().getGuild();
+ } else if (this.getSlashEvent() == null) {
+ return this.getEvent().getGuild();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent message event} that was received for this instance
+ *
+ * @return the {@link net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent message event} that was received for this instance
+ */
+ GuildMessageReceivedEvent getEvent();
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.events.interaction.SlashCommandEvent interaction event} that was received for this instance
+ *
+ * @returns the {@link net.dv8tion.jda.api.events.interaction.SlashCommandEvent interaction event} that was received for this instance
+ */
+ SlashCommandEvent getSlashEvent();
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.TextChannel channel} that the message for this event was send in
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.TextChannel channel} that the message for this event was send in
+ */
+ default TextChannel getChannel() {
+ if (this.getEvent() == null) {
+ return this.getSlashEvent().getTextChannel();
+ } else if (this.getSlashEvent() == null) {
+ return this.getEvent().getChannel();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.Message message} that triggered this event
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.Message message} that triggered this event
+ */
+ default Message getMessage() {
+ if (this.getEvent() == null) {
+ return null;
+ } else if (this.getSlashEvent() == null) {
+ return this.getEvent().getMessage();
+ }
+ return null;
+ }
+
+ default String getCommandString() {
+ if (this.getEvent() == null) {
+ return this.getSlashEvent().getCommandString();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.User author} of the message as user
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.User author} of the message as user
+ */
+ default User getAuthor() {
+ if (this.getEvent() == null) {
+ return this.getSlashEvent().getUser();
+ } else if (this.getSlashEvent() == null) {
+ return this.getEvent().getAuthor();
+ }
+ return null;
+ }
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.Member author} of the message as member
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.Member author} of the message as member
+ */
+ default Member getMember() {
+ if (this.getEvent() == null) {
+ return this.getSlashEvent().getMember();
+ } else if (this.getSlashEvent() == null) {
+ return this.getEvent().getMember();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.interactions.InteractionHook interaction hook} of the slash command
+ *
+ * @return the {@link net.dv8tion.jda.api.interactions.InteractionHook interaction hook} of the slash command
+ */
+ default InteractionHook getHook() {
+ return this.getSlashEvent().getHook();
+ }
+
+ /**
+ * Returns the current {@link net.dv8tion.jda.api.JDA jda} instance
+ *
+ * @return the current {@link net.dv8tion.jda.api.JDA jda} instance
+ */
+ default JDA getJDA() {
+ if (this.getEvent() == null) {
+ return this.getSlashEvent().getJDA();
+ } else if (this.getSlashEvent() == null) {
+ return this.getEvent().getJDA();
+ }
+ return null;
+ }
+
+ /**
+ * Returns the current {@link net.dv8tion.jda.api.sharding.ShardManager} instance
+ *
+ * @return the current {@link net.dv8tion.jda.api.sharding.ShardManager} instance
+ */
+ default ShardManager getShardManager() {
+ return this.getJDA().getShardManager();
+ }
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.User user} for the currently logged in account
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.User user} for the currently logged in account
+ */
+ default User getSelfUser() {
+ return this.getJDA().getSelfUser();
+ }
+
+ /**
+ * Returns the {@link net.dv8tion.jda.api.entities.Member member} in the guild for the currently logged in account
+ *
+ * @return the {@link net.dv8tion.jda.api.entities.Member member} in the guild for the currently logged in account
+ */
+ default Member getSelfMember() {
+ return this.getGuild().getSelfMember();
+ }
+}
diff --git a/src/main/java/tech/nevets/modbot/util/commands/TestCmd.java b/src/main/java/tech/nevets/modbot/util/commands/TestCmd.java
new file mode 100644
index 0000000..8094cdc
--- /dev/null
+++ b/src/main/java/tech/nevets/modbot/util/commands/TestCmd.java
@@ -0,0 +1,44 @@
+package tech.nevets.modbot.util.commands;
+
+import net.dv8tion.jda.api.entities.IMentionable;
+import net.dv8tion.jda.api.interactions.commands.OptionType;
+import net.dv8tion.jda.api.interactions.commands.build.CommandData;
+import tech.nevets.modbot.api.commands.ISlashCommand;
+
+public class TestCmd implements ISlashCommand {
+ @Override
+ public void handleSlash(CommandContext ctx) {
+ long number = ctx.getSlashEvent().getOption("number").getAsLong();
+ IMentionable member = ctx.getSlashEvent().getOption("mention").getAsMentionable();
+ boolean testBoolean = ctx.getSlashEvent().getOption("boolean").getAsBoolean();
+ String commandString = ctx.getSlashEvent().getCommandString();
+
+ String content = "number: " + number + ", mention: " + member.getAsMention() + ", boolean: " + testBoolean + ", command string: " + commandString;
+
+ ctx.getHook().sendMessage(content).queue();
+ }
+
+ @Override
+ public String getDescription() {
+ return "Checks the ping between bot and API";
+ }
+
+ @Override
+ public CommandData getCommandData() {
+ return new CommandData(this.getName(), this.getDescription())
+ .addOption(OptionType.INTEGER, "number", "test number", true)
+ .addOption(OptionType.USER, "mention", "mentioned member or role", true)
+ .addOption(OptionType.BOOLEAN, "boolean", "test boolean", true);
+ }
+
+ @Override
+ public String getName() {
+ return "test";
+ }
+
+
+ @Override
+ public String getHelp() {
+ return "tests api ping";
+ }
+}