components) {
replyEphemeral(MessageCreateData.fromContent(message), components);
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/BuiltInTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/BuiltInTask.java
index a64fa2d..ceb185c 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/BuiltInTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/BuiltInTask.java
@@ -5,7 +5,7 @@ package me.bobert.netneutrality.core.tasks;
* https://discord.gg/qUa6zNFZsd
*/
-import net.dv8tion.jda.api.interactions.components.buttons.Button;
+import me.bobert.netneutrality.core.context.TaskContext;
/**
* Represents a task that has a known ID and is loaded at runtime.
@@ -13,13 +13,5 @@ import net.dv8tion.jda.api.interactions.components.buttons.Button;
*/
public interface BuiltInTask extends Task {
- static Button createButton(Class extends BuiltInTask> clazz) {
- BuiltInTask builtInTask = TaskManager.getInstance().findBuiltInTaskByClass(clazz);
-
- if (builtInTask == null) {
- throw new IllegalArgumentException("Task class " + clazz.getName() + " was not registered by the task manager.");
- }
-
- return builtInTask.createButton();
- }
+ void executeTask(TaskContext context);
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/ScheduledTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/ScheduledTask.java
new file mode 100644
index 0000000..d7a3c3e
--- /dev/null
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/ScheduledTask.java
@@ -0,0 +1,64 @@
+package me.bobert.netneutrality.core.tasks;
+
+/*
+ * Copyright (c) anthorob
+ *
+ * https://discord.gg/qUa6zNFZsd
+ */
+
+import java.util.concurrent.TimeUnit;
+
+public interface ScheduledTask {
+
+ class TimeFrame {
+ private final int interval;
+ private final TimeUnit unit;
+
+ private TimeFrame(int interval, TimeUnit unit) {
+ this.interval = interval;
+ this.unit = unit;
+ }
+
+ public static TimeFrame minutely() {
+ return new TimeFrame(1, TimeUnit.MINUTES);
+ }
+
+ public static TimeFrame minutely(int interval) {
+ return new TimeFrame(interval, TimeUnit.MINUTES);
+ }
+
+ public static TimeFrame hourly() {
+ return new TimeFrame(1, TimeUnit.HOURS);
+ }
+
+ public static TimeFrame hourly(int interval) {
+ return new TimeFrame(interval, TimeUnit.HOURS);
+ }
+
+ public static TimeFrame daily() {
+ return new TimeFrame(1, TimeUnit.DAYS);
+ }
+
+ public static TimeFrame daily(int interval) {
+ return new TimeFrame(interval, TimeUnit.DAYS);
+ }
+
+ public static TimeFrame custom(int interval, TimeUnit unit) {
+ return new TimeFrame(interval, unit);
+ }
+
+ public int getInterval() {
+ return interval;
+ }
+
+ public TimeUnit getUnit() {
+ return unit;
+ }
+ }
+
+ String getId();
+
+ TimeFrame getTimeFrame();
+
+ void run();
+}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/IStepTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/StepTask.java
similarity index 99%
rename from src/main/java/me/bobert/netneutrality/core/tasks/IStepTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/StepTask.java
index fee1dbb..d14bc51 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/IStepTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/StepTask.java
@@ -6,7 +6,7 @@ package me.bobert.netneutrality.core.tasks;
* https://discord.gg/qUa6zNFZsd
*/
-public interface IStepTask {
+public interface StepTask {
/**
* Defines the type of input expected for a step.
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/Task.java b/src/main/java/me/bobert/netneutrality/core/tasks/Task.java
index 3b337bb..7db15f0 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/Task.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/Task.java
@@ -7,6 +7,7 @@ package me.bobert.netneutrality.core.tasks;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
+import me.bobert.netneutrality.core.tasks.trait.WithButton;
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
@@ -64,8 +65,6 @@ public interface Task {
OffsetDateTime getCreationTime();
- Button createButton();
-
/**
* This method is used to reply to the interaction that triggered this task.
* If the task is implementing {@link WithChildren}, the child tasks must be instanciated in this method.
@@ -80,4 +79,14 @@ public interface Task {
Lifecycle execute(TaskContext context);
void insert(CommandContext context);
+
+ static Button createButton(Class extends WithButton> clazz) {
+ WithButton withButton = TaskManager.getInstance().findButtonTaskByClass(clazz);
+
+ if (withButton == null) {
+ throw new IllegalArgumentException("Task " + clazz.getName() + " was not registered by the task manager.");
+ }
+
+ return withButton.createButton();
+ }
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/TaskManager.java b/src/main/java/me/bobert/netneutrality/core/tasks/TaskManager.java
index 99e2b54..6d4cf93 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/TaskManager.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/TaskManager.java
@@ -11,28 +11,24 @@ import me.bobert.netneutrality.core.context.InteractionContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.trait.Deletable;
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
+import me.bobert.netneutrality.core.tasks.trait.WithButton;
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
import me.bobert.netneutrality.core.tasks.trait.WithParent;
-import me.bobert.netneutrality.core.thread.ThreadManager;
+import me.bobert.netneutrality.core.thread.ScheduledTaskManager;
import me.bobert.netneutrality.db.service.TaskService;
import me.bobert.netneutrality.exceptions.GracefulContextError;
import me.bobert.netneutrality.exceptions.TaskExecutionException;
-import me.bobert.netneutrality.thread.CleanupTaskThread;
+import me.bobert.netneutrality.utils.ReflectionHelper;
import net.dv8tion.jda.api.interactions.InteractionHook;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.reflections.Reflections;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
@@ -63,12 +59,11 @@ public class TaskManager {
public void init() {
loadBuiltInTasks();
+ loadScheduledTasks();
taskService.loadTasks();
-
- ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
}
- protected TaskService getTaskService() {
+ public TaskService getTaskService() {
return taskService;
}
@@ -101,7 +96,7 @@ public class TaskManager {
taskService.persistTask(persistableTask);
}
- if (task instanceof IStepTask) {
+ if (task instanceof StepTask) {
pendingTasks.add(task);
} else {
tasks.put(id, task);
@@ -176,7 +171,7 @@ public class TaskManager {
}
}
- logger.debug("Executing task: {}", taskId);
+ logger.info("Executing task: {}", taskId);
Task.Lifecycle status = task.execute(context);
handleTaskLifecycle(task, status);
@@ -205,7 +200,7 @@ public class TaskManager {
public Optional findTaskByPartialId(Class clazz, String partialId) {
List tasks = findTasksByPartialId(clazz, partialId);
- return Optional.ofNullable(tasks.isEmpty() ? null : tasks.get(0));
+ return Optional.ofNullable(tasks.isEmpty() ? null : tasks.getFirst());
}
public List findTasksByPartialId(Class clazz, String partialId) {
@@ -216,10 +211,10 @@ public class TaskManager {
.collect(Collectors.toList());
}
- public BuiltInTask findBuiltInTaskByClass(Class extends BuiltInTask> clazz) {
+ public WithButton findButtonTaskByClass(Class extends WithButton> clazz) {
return tasks.values().stream()
.filter(clazz::isInstance)
- .map(task -> (BuiltInTask) task)
+ .map(task -> (WithButton) task)
.findFirst()
.orElse(null);
}
@@ -237,22 +232,27 @@ public class TaskManager {
private void loadBuiltInTasks() {
logger.info("Loading built-in tasks...");
- Set> classes = new Reflections("me.bobert.netneutrality.tasks").getSubTypesOf(BuiltInTask.class);
- for (Class extends BuiltInTask> s : classes) {
- try {
- if (Modifier.isAbstract(s.getModifiers())) {
- continue;
- }
-
- BuiltInTask task = s.getConstructor().newInstance();
-
- logger.info("Loaded task: {}", task);
- tasks.put(task.getId(), task);
- } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
- | InvocationTargetException | NoSuchMethodException | SecurityException e) {
- logger.error("Failed to instantiate built-in task", e);
+ ReflectionHelper.loadTasks(BuiltInTask.class, builtInTask -> {
+ if (builtInTask.isInsertable()) {
+ logger.info("Loaded task: {}", builtInTask.getClass().getName());
+ tasks.put(builtInTask.getId(), builtInTask);
}
- }
+ });
+ }
+
+ private void loadScheduledTasks() {
+ logger.info("Loading scheduled tasks...");
+
+ ReflectionHelper.loadTasks(ScheduledTask.class, scheduledTask -> {
+ String taskId = scheduledTask.getId();
+
+ if (taskId == null || taskId.trim().isEmpty()) {
+ logger.error("Cannot load scheduled task {}: task ID is null or empty", scheduledTask.getClass().getName());
+ return;
+ }
+
+ ScheduledTaskManager.schedule(scheduledTask);
+ });
}
private void deleteInteraction(Task task) {
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/AbstractBuiltInTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractBuiltInTask.java
similarity index 88%
rename from src/main/java/me/bobert/netneutrality/core/tasks/AbstractBuiltInTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractBuiltInTask.java
index f334bf4..61dbbbd 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/AbstractBuiltInTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractBuiltInTask.java
@@ -1,4 +1,4 @@
-package me.bobert.netneutrality.core.tasks;
+package me.bobert.netneutrality.core.tasks.impl;
/*
* Copyright (c) anthorob
*
@@ -7,6 +7,7 @@ package me.bobert.netneutrality.core.tasks;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
+import me.bobert.netneutrality.core.tasks.BuiltInTask;
public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask {
@@ -35,6 +36,4 @@ public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltI
executeTask(context);
return Lifecycle.BUILTIN;
}
-
- public abstract void executeTask(TaskContext context);
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/AbstractPersistableTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractPersistableTask.java
similarity index 89%
rename from src/main/java/me/bobert/netneutrality/core/tasks/AbstractPersistableTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractPersistableTask.java
index 4b03f13..820aad8 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/AbstractPersistableTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractPersistableTask.java
@@ -1,4 +1,4 @@
-package me.bobert.netneutrality.core.tasks;
+package me.bobert.netneutrality.core.tasks.impl;
/*
* Copyright (c) anthorob
*
@@ -6,6 +6,8 @@ package me.bobert.netneutrality.core.tasks;
*/
import me.bobert.netneutrality.core.context.CommandContext;
+import me.bobert.netneutrality.core.tasks.PersistableTask;
+import me.bobert.netneutrality.core.tasks.TaskManager;
import org.json.JSONObject;
public abstract class AbstractPersistableTask extends AbstractTask implements PersistableTask {
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/impl/StepTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractStepTask.java
similarity index 93%
rename from src/main/java/me/bobert/netneutrality/core/tasks/impl/StepTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractStepTask.java
index f0466ab..3876037 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/impl/StepTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractStepTask.java
@@ -9,13 +9,11 @@ package me.bobert.netneutrality.core.tasks.impl;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.MessageContext;
import me.bobert.netneutrality.core.context.TaskContext;
-import me.bobert.netneutrality.core.tasks.AbstractTask;
-import me.bobert.netneutrality.core.tasks.IStepTask;
+import me.bobert.netneutrality.core.tasks.StepTask;
import me.bobert.netneutrality.core.tasks.TaskManager;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
-import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.fellbaum.jemoji.Emoji;
import net.fellbaum.jemoji.EmojiManager;
@@ -27,7 +25,7 @@ import java.util.UUID;
* Represents a task that consists of multiple sequential steps. The task starts with an initial message (question)
* and expects a user response. Each step can lead to another step, forming a workflow.
*/
-public abstract class StepTask extends AbstractTask implements IStepTask {
+public abstract class AbstractStepTask extends AbstractTask implements StepTask {
private final Object stepLock = new Object();
private CommandContext commandContext;
private Guild guild;
@@ -110,11 +108,6 @@ public abstract class StepTask extends AbstractTask implements IStepTask {
return getCreationTime().plusMinutes(60).isBefore(OffsetDateTime.now());
}
- @Override
- public Button createButton() {
- return null;
- }
-
/**
* Validates the user input against the expected step type.
*
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/AbstractTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractTask.java
similarity index 88%
rename from src/main/java/me/bobert/netneutrality/core/tasks/AbstractTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractTask.java
index a50c620..c972192 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/AbstractTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/AbstractTask.java
@@ -1,4 +1,4 @@
-package me.bobert.netneutrality.core.tasks;
+package me.bobert.netneutrality.core.tasks.impl;
/*
* Copyright (c) anthorob
*
@@ -6,6 +6,8 @@ package me.bobert.netneutrality.core.tasks;
*/
import me.bobert.netneutrality.core.context.CommandContext;
+import me.bobert.netneutrality.core.tasks.Task;
+import me.bobert.netneutrality.core.tasks.TaskManager;
import java.time.OffsetDateTime;
import java.util.Objects;
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/impl/ButtonTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/ButtonTask.java
index f18d76e..27dc14d 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/impl/ButtonTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/ButtonTask.java
@@ -5,49 +5,40 @@ package me.bobert.netneutrality.core.tasks.impl;
* https://discord.gg/qUa6zNFZsd
*/
-import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
import me.bobert.netneutrality.core.tasks.Task;
-import me.bobert.netneutrality.core.tasks.trait.WithChildren;
-import net.dv8tion.jda.api.interactions.components.ActionRow;
+import me.bobert.netneutrality.core.tasks.trait.WithButton;
+import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
import java.time.OffsetDateTime;
-import java.util.List;
-public abstract class ButtonTask extends GenericRandomIdTask {
+public abstract class ButtonTask extends GenericRandomIdTask implements WithButton {
private final ButtonStyle buttonStyle;
private final String buttonLabel;
+ private Emoji emoji;
public ButtonTask(ButtonStyle buttonStyle, String buttonLabel) {
this.buttonStyle = buttonStyle;
this.buttonLabel = buttonLabel;
}
+ public Task setEmoji(Emoji emoji) {
+ this.emoji = emoji;
+ return this;
+ }
+
@Override
public Button createButton() {
if (buttonStyle == null || buttonLabel == null) {
return null;
}
- return Button.of(buttonStyle, getId(), buttonLabel);
+ return Button.of(buttonStyle, getId(), buttonLabel, emoji);
}
@Override
public boolean isExpired() {
return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now());
}
-
- public ActionRow createActionRow(List tasks) {
- ActionRow row = ActionRow.of(tasks.stream().map(Task::createButton).toList());
- if (this instanceof WithChildren parent) {
- parent.addChild(tasks);
- }
-
- return row;
- }
-
- public ActionRow createActionRow(Task... tasks) {
- return createActionRow(List.of(tasks));
- }
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/impl/EmojiTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/EmojiTask.java
index dae4604..cfdb734 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/impl/EmojiTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/EmojiTask.java
@@ -6,8 +6,6 @@ package me.bobert.netneutrality.core.tasks.impl;
*/
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
-import me.bobert.netneutrality.core.tasks.AbstractPersistableTask;
-import net.dv8tion.jda.api.interactions.components.buttons.Button;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -54,9 +52,4 @@ public abstract class EmojiTask extends AbstractPersistableTask {
public String getId() {
return String.format(EmojiTaskContext.ID, getGuildId(), getMessageId());
}
-
- @Override
- public Button createButton() {
- return null;
- }
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/GenericCustomIdTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/GenericCustomIdTask.java
similarity index 63%
rename from src/main/java/me/bobert/netneutrality/core/tasks/GenericCustomIdTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/impl/GenericCustomIdTask.java
index 8ab28a2..573905b 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/GenericCustomIdTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/GenericCustomIdTask.java
@@ -1,4 +1,4 @@
-package me.bobert.netneutrality.core.tasks;
+package me.bobert.netneutrality.core.tasks.impl;
/*
* Copyright (c) anthorob
@@ -6,8 +6,6 @@ package me.bobert.netneutrality.core.tasks;
* https://discord.gg/qUa6zNFZsd
*/
-import net.dv8tion.jda.api.interactions.components.buttons.Button;
-
public abstract class GenericCustomIdTask extends AbstractTask {
private final String customId;
@@ -19,9 +17,4 @@ public abstract class GenericCustomIdTask extends AbstractTask {
public String getId() {
return customId;
}
-
- @Override
- public Button createButton() {
- return null;
- }
}
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/GenericRandomIdTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/GenericRandomIdTask.java
similarity index 83%
rename from src/main/java/me/bobert/netneutrality/core/tasks/GenericRandomIdTask.java
rename to src/main/java/me/bobert/netneutrality/core/tasks/impl/GenericRandomIdTask.java
index 23cde6e..e7a0e66 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/GenericRandomIdTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/GenericRandomIdTask.java
@@ -1,4 +1,4 @@
-package me.bobert.netneutrality.core.tasks;
+package me.bobert.netneutrality.core.tasks.impl;
/*
* Copyright (c) anthorob
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/impl/ModalTask.java b/src/main/java/me/bobert/netneutrality/core/tasks/impl/ModalTask.java
index beb5a01..c16a680 100644
--- a/src/main/java/me/bobert/netneutrality/core/tasks/impl/ModalTask.java
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/impl/ModalTask.java
@@ -6,7 +6,6 @@ package me.bobert.netneutrality.core.tasks.impl;
*/
import me.bobert.netneutrality.core.context.TaskContext;
-import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
import net.dv8tion.jda.api.interactions.modals.ModalMapping;
import java.time.OffsetDateTime;
diff --git a/src/main/java/me/bobert/netneutrality/core/tasks/trait/WithButton.java b/src/main/java/me/bobert/netneutrality/core/tasks/trait/WithButton.java
new file mode 100644
index 0000000..2603df3
--- /dev/null
+++ b/src/main/java/me/bobert/netneutrality/core/tasks/trait/WithButton.java
@@ -0,0 +1,31 @@
+package me.bobert.netneutrality.core.tasks.trait;
+
+/*
+ * Copyright (c) anthorob
+ *
+ * https://discord.gg/qUa6zNFZsd
+ */
+
+import me.bobert.netneutrality.core.tasks.Task;
+import net.dv8tion.jda.api.interactions.components.ActionRow;
+import net.dv8tion.jda.api.interactions.components.buttons.Button;
+
+import java.util.List;
+
+public interface WithButton {
+
+ Button createButton();
+
+ default ActionRow createActionRow(List tasks) {
+ ActionRow row = ActionRow.of(tasks.stream().filter(task -> task instanceof WithButton).map(task -> (WithButton) task).map(WithButton::createButton).toList());
+ if (this instanceof WithChildren parent) {
+ parent.addChild(tasks);
+ }
+
+ return row;
+ }
+
+ default ActionRow createActionRow(Task... tasks) {
+ return createActionRow(List.of(tasks));
+ }
+}
diff --git a/src/main/java/me/bobert/netneutrality/core/thread/ScheduledTaskManager.java b/src/main/java/me/bobert/netneutrality/core/thread/ScheduledTaskManager.java
new file mode 100644
index 0000000..bc33c35
--- /dev/null
+++ b/src/main/java/me/bobert/netneutrality/core/thread/ScheduledTaskManager.java
@@ -0,0 +1,64 @@
+package me.bobert.netneutrality.core.thread;
+
+/*
+ * Copyright (c) anthorob
+ *
+ * https://discord.gg/qUa6zNFZsd
+ */
+
+import me.bobert.netneutrality.core.LogHandler;
+import me.bobert.netneutrality.core.tasks.ScheduledTask;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+
+public class ScheduledTaskManager {
+ private static final Logger logger = LogManager.getLogger(ScheduledTaskManager.class);
+ private static final ScheduledExecutorService executorService = ThreadManager.getScheduledExecutor();
+ private static final Map> scheduledTasks = new ConcurrentHashMap<>();
+
+ private ScheduledTaskManager() {}
+
+ public static void schedule(ScheduledTask task) {
+ String taskId = task.getId();
+
+ if (scheduledTasks.containsKey(taskId)) {
+ logger.warn("Task {} is already scheduled.", taskId);
+ return;
+ }
+
+ ScheduledTask.TimeFrame timeFrame = task.getTimeFrame();
+
+ Runnable runnable = () -> {
+ logger.info("Executing scheduled task: {}", taskId);
+
+ try {
+ task.run();
+ } catch (Exception e) {
+ logger.error("An exception was thrown while executing scheduled task {}", taskId);
+ LogHandler.log(e);
+ }
+ };
+
+ ScheduledFuture> future = executorService.scheduleWithFixedDelay(
+ runnable,
+ 1,
+ timeFrame.getInterval(),
+ timeFrame.getUnit()
+ );
+
+ scheduledTasks.put(taskId, future);
+ logger.info("Successfully scheduled task: {} with interval {} {}",
+ taskId, timeFrame.getInterval(), timeFrame.getUnit());
+ }
+
+ public static void cancelAll() {
+ logger.info("Cancelling all {} scheduled tasks", scheduledTasks.size());
+ scheduledTasks.forEach((id, future) -> future.cancel(false));
+ scheduledTasks.clear();
+ }
+}
diff --git a/src/main/java/me/bobert/netneutrality/music/MusicManager.java b/src/main/java/me/bobert/netneutrality/music/MusicManager.java
index 1af9447..870c85f 100644
--- a/src/main/java/me/bobert/netneutrality/music/MusicManager.java
+++ b/src/main/java/me/bobert/netneutrality/music/MusicManager.java
@@ -15,12 +15,9 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import dev.lavalink.youtube.YoutubeAudioSourceManager;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.CommandContext;
-import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.db.service.GuildService;
import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
-import me.bobert.netneutrality.thread.MusicManagerCleanupThread;
-import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Guild;
@@ -31,7 +28,6 @@ import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
public class MusicManager {
@@ -72,9 +68,6 @@ public class MusicManager {
String po = Settings.getInstance().get("NETNEUTRALITY_PO_TOKEN");
String visitor = Settings.getInstance().get("NETNEUTRALITY_VISITOR_DATA");
dev.lavalink.youtube.clients.Web.setPoTokenAndVisitorData(po, visitor);
-
- ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new MusicPlayerAutoDisconnectThread(), 15, 15, TimeUnit.MINUTES);
- ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new MusicManagerCleanupThread(), 1, 1, TimeUnit.HOURS);
}
public static void disconnectPlayers() {
diff --git a/src/main/java/me/bobert/netneutrality/tasks/help/BotInfo.java b/src/main/java/me/bobert/netneutrality/tasks/help/BotInfo.java
index 8c02b71..ccb717d 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/help/BotInfo.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/help/BotInfo.java
@@ -6,10 +6,12 @@ package me.bobert.netneutrality.tasks.help;
*/
import me.bobert.netneutrality.core.context.TaskContext;
-import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
+import me.bobert.netneutrality.core.tasks.impl.AbstractBuiltInTask;
+import me.bobert.netneutrality.core.tasks.trait.WithButton;
+import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
-public class BotInfo extends AbstractBuiltInTask {
+public class BotInfo extends AbstractBuiltInTask implements WithButton {
@Override
public String getId() {
@@ -23,7 +25,7 @@ public class BotInfo extends AbstractBuiltInTask {
@Override
public void executeTask(TaskContext context) {
- context.sendMessageInPrivate("For information about the bot and to follow the development, " +
- "you can join the discord here: https://discord.gg/udVJwTgV");
+ context.replyEphemeral("For information about the bot and to follow the development, " +
+ "you can join the discord here: " + Settings.getDiscordInvite());
}
}
diff --git a/src/main/java/me/bobert/netneutrality/tasks/help/CommandHelp.java b/src/main/java/me/bobert/netneutrality/tasks/help/CommandHelp.java
index 37eeb5b..88089d3 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/help/CommandHelp.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/help/CommandHelp.java
@@ -6,12 +6,13 @@ package me.bobert.netneutrality.tasks.help;
*/
import me.bobert.netneutrality.core.context.TaskContext;
-import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
+import me.bobert.netneutrality.core.tasks.impl.AbstractBuiltInTask;
+import me.bobert.netneutrality.core.tasks.trait.WithButton;
import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
-public class CommandHelp extends AbstractBuiltInTask {
+public class CommandHelp extends AbstractBuiltInTask implements WithButton {
@Override
public String getId() {
diff --git a/src/main/java/me/bobert/netneutrality/tasks/help/ReportBug.java b/src/main/java/me/bobert/netneutrality/tasks/help/ReportBug.java
index 70ec080..830b965 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/help/ReportBug.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/help/ReportBug.java
@@ -6,10 +6,12 @@ package me.bobert.netneutrality.tasks.help;
*/
import me.bobert.netneutrality.core.context.TaskContext;
-import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
+import me.bobert.netneutrality.core.tasks.impl.AbstractBuiltInTask;
+import me.bobert.netneutrality.core.tasks.trait.WithButton;
+import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
-public class ReportBug extends AbstractBuiltInTask {
+public class ReportBug extends AbstractBuiltInTask implements WithButton {
@Override
public String getId() {
@@ -23,7 +25,7 @@ public class ReportBug extends AbstractBuiltInTask {
@Override
public void executeTask(TaskContext context) {
- context.sendMessageInPrivate("To report a bug with the bot, you can either use the /report-bug command " +
- "or join the discord here: https://discord.gg/udVJwTgV");
+ context.replyEphemeral("To report a bug with the bot, you can either use the /report-bug command " +
+ "or join the discord here: " + Settings.getDiscordInvite());
}
}
diff --git a/src/main/java/me/bobert/netneutrality/tasks/music/LoadPlaylistTask.java b/src/main/java/me/bobert/netneutrality/tasks/music/LoadPlaylistTask.java
index 18d4df3..089c90d 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/music/LoadPlaylistTask.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/music/LoadPlaylistTask.java
@@ -109,15 +109,12 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
}
return Lifecycle.KEEP;
- });
+ }).setEmoji(Emoji.fromFormatted("U+1F60E"));
- Task cancel = CancelButton.create(this, cancelText, context -> Lifecycle.REMOVE_SELF_AND_CHILDREN);
- addChild(accept, cancel);
+ Task cancel = CancelButton.create(this, cancelText, context -> Lifecycle.REMOVE_SELF_AND_CHILDREN)
+ .setEmoji(Emoji.fromFormatted("U+1F92E"));
- return ActionRow.of(
- accept.createButton().withEmoji(Emoji.fromFormatted("U+1F60E")),
- cancel.createButton().withEmoji(Emoji.fromFormatted("U+1F92E"))
- );
+ return createActionRow(accept, cancel);
}
@Override
diff --git a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleFirstStep.java b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleFirstStep.java
index 83aeaa8..e03a5fe 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleFirstStep.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleFirstStep.java
@@ -6,11 +6,11 @@ package me.bobert.netneutrality.tasks.reaction;
* https://discord.gg/qUa6zNFZsd
*/
-import me.bobert.netneutrality.core.tasks.impl.StepTask;
+import me.bobert.netneutrality.core.tasks.impl.AbstractStepTask;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
-public class EmojiRoleFirstStep extends StepTask {
+public class EmojiRoleFirstStep extends AbstractStepTask {
private final Message target;
public EmojiRoleFirstStep(Message target) {
diff --git a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleModify.java b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleModify.java
index 40bf58e..ca442fd 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleModify.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleModify.java
@@ -74,7 +74,7 @@ public class EmojiRoleModify extends ButtonTask implements WithChildren {
});
ActionRow row = createActionRow(addEmojiTask, removeEmojiTask, deleteMappingTask);
- context.replyEphemeral("Select what you want to do with this message", List.of(row));
+ context.replyEphemeral("Select what you want to do with this message", row);
}
@Override
diff --git a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleRemoveStep.java b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleRemoveStep.java
index 0d59a89..0de6bc9 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleRemoveStep.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleRemoveStep.java
@@ -6,14 +6,14 @@ package me.bobert.netneutrality.tasks.reaction;
* https://discord.gg/qUa6zNFZsd
*/
-import me.bobert.netneutrality.core.tasks.impl.StepTask;
+import me.bobert.netneutrality.core.tasks.impl.AbstractStepTask;
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
import net.dv8tion.jda.api.entities.Message;
import java.util.Set;
import java.util.StringJoiner;
-public class EmojiRoleRemoveStep extends StepTask {
+public class EmojiRoleRemoveStep extends AbstractStepTask {
private final Message target;
private final EmojiReactionRoleTask task;
@@ -22,6 +22,11 @@ public class EmojiRoleRemoveStep extends StepTask {
this.task = task;
}
+ @Override
+ public String getName() {
+ return "emoji-role-remove";
+ }
+
@Override
public StepType getStepType() {
return StepType.EMOJI;
@@ -54,9 +59,4 @@ public class EmojiRoleRemoveStep extends StepTask {
task.removeEmoji(emoji);
return StepResult.success("Removed emoji successfully!");
}
-
- @Override
- public String getName() {
- return "emoji-role-remove";
- }
}
diff --git a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleSecondStep.java b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleSecondStep.java
index 92c3a60..f305909 100644
--- a/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleSecondStep.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/reaction/EmojiRoleSecondStep.java
@@ -8,14 +8,14 @@ package me.bobert.netneutrality.tasks.reaction;
import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager;
-import me.bobert.netneutrality.core.tasks.impl.StepTask;
+import me.bobert.netneutrality.core.tasks.impl.AbstractStepTask;
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.Role;
import java.util.Map;
-public class EmojiRoleSecondStep extends StepTask {
+public class EmojiRoleSecondStep extends AbstractStepTask {
private final Message target;
private final Role role;
diff --git a/src/main/java/me/bobert/netneutrality/thread/MusicManagerCleanupThread.java b/src/main/java/me/bobert/netneutrality/tasks/scheduled/MusicManagerCleanup.java
similarity index 74%
rename from src/main/java/me/bobert/netneutrality/thread/MusicManagerCleanupThread.java
rename to src/main/java/me/bobert/netneutrality/tasks/scheduled/MusicManagerCleanup.java
index d52d064..140386d 100644
--- a/src/main/java/me/bobert/netneutrality/thread/MusicManagerCleanupThread.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/scheduled/MusicManagerCleanup.java
@@ -1,27 +1,35 @@
-package me.bobert.netneutrality.thread;
+package me.bobert.netneutrality.tasks.scheduled;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
+import me.bobert.netneutrality.core.tasks.ScheduledTask;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.music.TrackScheduler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-public class MusicManagerCleanupThread implements Runnable {
- private static final Logger logger = LogManager.getLogger(MusicManagerCleanupThread.class);
+public class MusicManagerCleanup implements ScheduledTask {
+ private static final Logger logger = LogManager.getLogger(MusicManagerCleanup.class);
+
+ @Override
+ public String getId() {
+ return "MusicManagerCleanup";
+ }
+
+ @Override
+ public TimeFrame getTimeFrame() {
+ return TimeFrame.hourly();
+ }
@Override
public void run() {
-
try {
-
for (MusicManager manager : MusicManager.getManagers()) {
processManager(manager);
}
-
} catch (InterruptedException e) {
logger.error("Interrupted while running MusicManagerCleanupThread", e);
}
diff --git a/src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java b/src/main/java/me/bobert/netneutrality/tasks/scheduled/MusicPlayerAutoDisconnect.java
similarity index 84%
rename from src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java
rename to src/main/java/me/bobert/netneutrality/tasks/scheduled/MusicPlayerAutoDisconnect.java
index 2efb483..802c195 100644
--- a/src/main/java/me/bobert/netneutrality/thread/MusicPlayerAutoDisconnectThread.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/scheduled/MusicPlayerAutoDisconnect.java
@@ -1,10 +1,12 @@
-package me.bobert.netneutrality.thread;
+package me.bobert.netneutrality.tasks.scheduled;
+
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
+import me.bobert.netneutrality.core.tasks.ScheduledTask;
import me.bobert.netneutrality.music.MusicManager;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel;
@@ -13,17 +15,25 @@ import org.apache.logging.log4j.Logger;
import java.util.List;
-public class MusicPlayerAutoDisconnectThread implements Runnable {
- private static final Logger logger = LogManager.getLogger(MusicPlayerAutoDisconnectThread.class);
+public class MusicPlayerAutoDisconnect implements ScheduledTask {
+ private static final Logger logger = LogManager.getLogger(MusicPlayerAutoDisconnect.class);
+
+ @Override
+ public String getId() {
+ return "MusicPlayerAutoDisconnect";
+ }
+
+ @Override
+ public TimeFrame getTimeFrame() {
+ return TimeFrame.minutely(15);
+ }
@Override
public void run() {
try {
-
for (MusicManager manager : MusicManager.getManagers()) {
processManager(manager);
}
-
} catch (InterruptedException e) {
logger.error("Interrupted while running MusicPlayerAutoDisconnectThread", e);
}
diff --git a/src/main/java/me/bobert/netneutrality/thread/CleanupTaskThread.java b/src/main/java/me/bobert/netneutrality/tasks/scheduled/TaskCleanup.java
similarity index 69%
rename from src/main/java/me/bobert/netneutrality/thread/CleanupTaskThread.java
rename to src/main/java/me/bobert/netneutrality/tasks/scheduled/TaskCleanup.java
index 6022b37..00260e2 100644
--- a/src/main/java/me/bobert/netneutrality/thread/CleanupTaskThread.java
+++ b/src/main/java/me/bobert/netneutrality/tasks/scheduled/TaskCleanup.java
@@ -1,4 +1,4 @@
-package me.bobert.netneutrality.thread;/*
+package me.bobert.netneutrality.tasks.scheduled;/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.thread;/*
import me.bobert.netneutrality.core.tasks.BuiltInTask;
import me.bobert.netneutrality.core.tasks.PersistableTask;
+import me.bobert.netneutrality.core.tasks.ScheduledTask;
import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager;
import org.apache.logging.log4j.LogManager;
@@ -13,8 +14,18 @@ import org.apache.logging.log4j.Logger;
import java.util.List;
-public class CleanupTaskThread implements Runnable {
- private static final Logger logger = LogManager.getLogger(CleanupTaskThread.class);
+public class TaskCleanup implements ScheduledTask {
+ private static final Logger logger = LogManager.getLogger(TaskCleanup.class);
+
+ @Override
+ public String getId() {
+ return "TaskCleanup";
+ }
+
+ @Override
+ public TimeFrame getTimeFrame() {
+ return TimeFrame.minutely(5);
+ }
@Override
public void run() {
diff --git a/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java b/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java
deleted file mode 100644
index cb19a71..0000000
--- a/src/main/java/me/bobert/netneutrality/thread/ShutdownThread.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package me.bobert.netneutrality.thread;
-
-/*
- * Copyright (c) anthorob
- *
- * https://discord.gg/qUa6zNFZsd
- */
-
-import me.bobert.netneutrality.NetNeutrality;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.core.LoggerContext;
-import org.apache.logging.log4j.core.config.Configurator;
-
-public class ShutdownThread extends Thread {
- private final NetNeutrality main;
-
- public ShutdownThread(NetNeutrality main) {
- this.main = main;
- }
-
- @Override
- public void run() {
- if (LogManager.getContext() instanceof LoggerContext) {
- System.out.println("Shutting down log4j2");
- Configurator.shutdown((LoggerContext) LogManager.getContext());
- }
-
- if (main.getJDA() != null) {
- main.shutdown();
- }
-
- }
-}
diff --git a/src/main/java/me/bobert/netneutrality/utils/ReflectionHelper.java b/src/main/java/me/bobert/netneutrality/utils/ReflectionHelper.java
new file mode 100644
index 0000000..24e7d5f
--- /dev/null
+++ b/src/main/java/me/bobert/netneutrality/utils/ReflectionHelper.java
@@ -0,0 +1,55 @@
+package me.bobert.netneutrality.utils;
+
+/*
+ * Copyright (c) anthorob
+ *
+ * https://discord.gg/qUa6zNFZsd
+ */
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.reflections.Reflections;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Modifier;
+import java.util.Set;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+public class ReflectionHelper {
+ private static final Logger logger = LogManager.getLogger(ReflectionHelper.class);
+ private static final BiConsumer, Exception> DEFAULT_FAILURE = (aClass, e) -> logger.error("Failed to load class: {}", aClass.getName(), e);
+
+ public static void loadClassesFromPackage(String packageName, Class clazz, Consumer onInstanciated, BiConsumer, Exception> onFailed) {
+ Set> classes = new Reflections(packageName).getSubTypesOf(clazz);
+ for (Class extends E> s : classes) {
+ try {
+ if (Modifier.isAbstract(s.getModifiers())) {
+ continue;
+ }
+
+ E instance = s.getConstructor().newInstance();
+ onInstanciated.accept(instance);
+ } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
+ | InvocationTargetException | NoSuchMethodException | SecurityException e) {
+ onFailed.accept(s, e);
+ }
+ }
+ }
+
+ public static void loadCommands(Class clazz, Consumer onInstanciated, BiConsumer, Exception> onFailed) {
+ loadClassesFromPackage("me.bobert.netneutrality.command", clazz, onInstanciated, onFailed);
+ }
+
+ public static void loadCommands(Class clazz, Consumer onInstanciated) {
+ loadClassesFromPackage("me.bobert.netneutrality.command", clazz, onInstanciated, DEFAULT_FAILURE);
+ }
+
+ public static void loadTasks(Class clazz, Consumer onInstanciated, BiConsumer, Exception> onFailed) {
+ loadClassesFromPackage("me.bobert.netneutrality.tasks", clazz, onInstanciated, onFailed);
+ }
+
+ public static void loadTasks(Class clazz, Consumer onInstanciated) {
+ loadClassesFromPackage("me.bobert.netneutrality.tasks", clazz, onInstanciated, DEFAULT_FAILURE);
+ }
+}
diff --git a/src/main/java/me/bobert/netneutrality/utils/Settings.java b/src/main/java/me/bobert/netneutrality/utils/Settings.java
index fce7c92..ec0c689 100644
--- a/src/main/java/me/bobert/netneutrality/utils/Settings.java
+++ b/src/main/java/me/bobert/netneutrality/utils/Settings.java
@@ -54,6 +54,10 @@ public class Settings {
return INSTANCE;
}
+ public static String getDiscordInvite() {
+ return Settings.getInstance().get("NETNEUTRALITY_BOT_INVITE", "https://discord.gg/PyCpd7JGMp");
+ }
+
public static String getVersion() {
return Settings.getInstance().getMavenVersion();
}