finish abstraction for StepTask
This commit is contained in:
1 parent
f01c48c61c
commit
3466fb536d
29 files changed
+719
-127
No files matched your search
@@ -12,6 +12,8 @@ import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
|||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
@@ -19,6 +21,8 @@ import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ReactionCommand extends AbstractAdminCommand {
|
public class ReactionCommand extends AbstractAdminCommand {
|
||||||
|
|
||||||
@@ -74,7 +78,7 @@ public class ReactionCommand extends AbstractAdminCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(CommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
context.ack();
|
context.ephemeral();
|
||||||
|
|
||||||
String guildId = context.getGuild().getId();
|
String guildId = context.getGuild().getId();
|
||||||
String messageId = context.getOption("messageid", CommandOption::getAsString);
|
String messageId = context.getOption("messageid", CommandOption::getAsString);
|
||||||
@@ -102,25 +106,31 @@ public class ReactionCommand extends AbstractAdminCommand {
|
|||||||
builder.append("I have found the following tasks for this guild:");
|
builder.append("I have found the following tasks for this guild:");
|
||||||
|
|
||||||
for (EmojiReactionRoleTask item : tasks) {
|
for (EmojiReactionRoleTask item : tasks) {
|
||||||
|
Map<String, String> mapping = item.getEmojiMapping().entrySet().stream()
|
||||||
|
.map(entry -> {
|
||||||
|
Role role = context.getGuild().getRoleById(entry.getValue());
|
||||||
|
return role != null ? Map.entry(entry.getKey(), role.getAsMention()) : null;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
|
||||||
builder.append("\n- Task ID: ").append(item.getId()).append("\n")
|
builder.append("\n- Task ID: ").append(item.getId()).append("\n")
|
||||||
.append(" Message ID: ").append(item.getMessageId()).append("\n")
|
.append(" Message: ")
|
||||||
.append(" Emoji Mapping: ").append(item.getEmojiMapping()).append("\n");
|
.append(String.format(Message.JUMP_URL, item.getGuildId(), item.getChannelId(), item.getMessageId()))
|
||||||
|
.append("\n")
|
||||||
|
.append(" Emoji Mappings:\n");
|
||||||
|
|
||||||
|
if (mapping.isEmpty()) {
|
||||||
|
builder.append(" No emoji mappings found\n");
|
||||||
|
} else {
|
||||||
|
mapping.forEach((emoji, roleMention) ->
|
||||||
|
builder.append(" - ").append(emoji).append(" → ").append(roleMention).append("\n")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.success(builder.toString());
|
return CommandExecutionResult.success(builder.toString());
|
||||||
}
|
}
|
||||||
case "setup" -> {
|
|
||||||
if (task != null) {
|
|
||||||
return CommandExecutionResult.error("The message ID is already registered, use the add subcommand instead.");
|
|
||||||
}
|
|
||||||
|
|
||||||
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
|
||||||
String role = context.getOption("role", CommandOption::getAsString);
|
|
||||||
task = new EmojiReactionRoleTask(guildId, messageId, Map.of(emoji, role));
|
|
||||||
task.insert(context);
|
|
||||||
|
|
||||||
return CommandExecutionResult.success("Added emoji to message successfully!");
|
|
||||||
}
|
|
||||||
case "add" -> {
|
case "add" -> {
|
||||||
if (task == null) {
|
if (task == null) {
|
||||||
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
@@ -129,7 +139,6 @@ public class ReactionCommand extends AbstractAdminCommand {
|
|||||||
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
||||||
String role = context.getOption("role", CommandOption::getAsString);
|
String role = context.getOption("role", CommandOption::getAsString);
|
||||||
task.addEmoji(emoji, role);
|
task.addEmoji(emoji, role);
|
||||||
task.save();
|
|
||||||
return CommandExecutionResult.success("Added emoji to message successfully!");
|
return CommandExecutionResult.success("Added emoji to message successfully!");
|
||||||
}
|
}
|
||||||
case "remove" -> {
|
case "remove" -> {
|
||||||
@@ -140,7 +149,6 @@ public class ReactionCommand extends AbstractAdminCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task.removeEmoji(emoji);
|
task.removeEmoji(emoji);
|
||||||
task.save();
|
|
||||||
return CommandExecutionResult.success("Removed emoji from message successfully!");
|
return CommandExecutionResult.success("Removed emoji from message successfully!");
|
||||||
}
|
}
|
||||||
case "delete" -> {
|
case "delete" -> {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import me.bobert.netneutrality.music.MusicManager;
|
|||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import me.bobert.netneutrality.utils.MusicUtils;
|
import me.bobert.netneutrality.utils.MusicUtils;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
@@ -26,6 +25,11 @@ public class QueueCommand extends AbstractMusicCommand {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCommandName() {
|
public String getCommandName() {
|
||||||
return "queue";
|
return "queue";
|
||||||
@@ -52,11 +56,6 @@ public class QueueCommand extends AbstractMusicCommand {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
|
||||||
return DefaultMemberPermissions.DISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(CommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package me.bobert.netneutrality.command.react;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.AbstractMessageMenuCommand;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import me.bobert.netneutrality.tasks.reaction.EmojiRoleFirstStep;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
public class CreateEmojiReaction extends AbstractMessageMenuCommand {
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(Message target, CommandContext context) {
|
||||||
|
EmojiReactionRoleTask task = TaskManager.getInstance().findTaskByPartialId(EmojiReactionRoleTask.class, target.getId()).orElse(null);
|
||||||
|
|
||||||
|
if (task != null) {
|
||||||
|
context.replyEphemeral("This message is already registered with the bot, please use the modify command instead");
|
||||||
|
return CommandExecutionResult.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.task(new EmojiRoleFirstStep(target));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "Create emoji role mapping";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package me.bobert.netneutrality.command.react;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.AbstractMessageMenuCommand;
|
||||||
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import me.bobert.netneutrality.tasks.reaction.EmojiRoleModify;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
|
||||||
|
public class ModifyEmojiReaction extends AbstractMessageMenuCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandExecutionResult execute(Message target, CommandContext context) {
|
||||||
|
EmojiReactionRoleTask task = TaskManager.getInstance().findTaskByPartialId(EmojiReactionRoleTask.class, target.getId()).orElse(null);
|
||||||
|
if (task == null) {
|
||||||
|
context.replyEphemeral("This message is not registered with the bot, please use the create command instead");
|
||||||
|
return CommandExecutionResult.none();
|
||||||
|
}
|
||||||
|
|
||||||
|
return CommandExecutionResult.task(new EmojiRoleModify(target, task));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needAdmin() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCommandName() {
|
||||||
|
return "Modify emoji role mapping";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -42,6 +42,7 @@ public class MessageTaskContext extends TaskContext implements MessageContext {
|
|||||||
@Override
|
@Override
|
||||||
public void replyEphemeral(MessageCreateData messageCreateData) {
|
public void replyEphemeral(MessageCreateData messageCreateData) {
|
||||||
if (getType() == ChannelType.PRIVATE) {
|
if (getType() == ChannelType.PRIVATE) {
|
||||||
|
reply(messageCreateData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ package me.bobert.netneutrality.core.command;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
|
||||||
|
|
||||||
public abstract class AbstractAdminCommand extends AbstractCommand {
|
public abstract class AbstractAdminCommand extends AbstractCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -14,11 +12,6 @@ public abstract class AbstractAdminCommand extends AbstractCommand {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
|
||||||
return DefaultMemberPermissions.DISABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommandCategory getCategory() {
|
public CommandCategory getCategory() {
|
||||||
return CommandCategory.ADMINISTRATION;
|
return CommandCategory.ADMINISTRATION;
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ public abstract class AbstractCommand implements Command {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||||
|
if (needAdmin()) {
|
||||||
|
return DefaultMemberPermissions.DISABLED;
|
||||||
|
}
|
||||||
|
|
||||||
return DefaultMemberPermissions.ENABLED;
|
return DefaultMemberPermissions.ENABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,13 @@ public class CommandExecutionResult {
|
|||||||
|
|
||||||
private final Result result;
|
private final Result result;
|
||||||
private final String message;
|
private final String message;
|
||||||
private final Object data;
|
private final Task task;
|
||||||
private final Exception exception;
|
private final Exception exception;
|
||||||
|
|
||||||
private CommandExecutionResult(Result result, String message, Object data, Exception exception) {
|
private CommandExecutionResult(Result result, String message, Task task, Exception exception) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.data = data;
|
this.task = task;
|
||||||
this.exception = exception;
|
this.exception = exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,10 +33,6 @@ public class CommandExecutionResult {
|
|||||||
this(result, message, null);
|
this(result, message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private CommandExecutionResult(Result result, Object data) {
|
|
||||||
this(result, null, data, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result getResult() {
|
public Result getResult() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -45,8 +41,8 @@ public class CommandExecutionResult {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object getData() {
|
public Task getTask() {
|
||||||
return data;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Exception getException() {
|
public Exception getException() {
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ public class CommandProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (result == TASK) {
|
if (result == TASK) {
|
||||||
TaskManager.getInstance().insert(context, (Task) commandExecutionResult.getData());
|
TaskManager.getInstance().insert(context, commandExecutionResult.getTask());
|
||||||
}
|
}
|
||||||
|
|
||||||
String message = commandExecutionResult.getMessage();
|
String message = commandExecutionResult.getMessage();
|
||||||
|
|||||||
@@ -17,11 +17,6 @@ public abstract class AbstractTask implements Task {
|
|||||||
creationTime = OffsetDateTime.now();
|
creationTime = OffsetDateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInsertable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OffsetDateTime getCreationTime() {
|
public OffsetDateTime getCreationTime() {
|
||||||
return creationTime;
|
return creationTime;
|
||||||
|
|||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface IStepTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the type of input expected for a step.
|
||||||
|
*/
|
||||||
|
enum StepType {
|
||||||
|
/**
|
||||||
|
* Expects a string input from the user.
|
||||||
|
*/
|
||||||
|
STRING,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expects a valid role ID for the current guild.
|
||||||
|
* The role must exist in the guild where the command was executed.
|
||||||
|
*/
|
||||||
|
ROLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expects a valid emoji.
|
||||||
|
*/
|
||||||
|
EMOJI
|
||||||
|
}
|
||||||
|
|
||||||
|
class StepResult {
|
||||||
|
private enum ResultType {
|
||||||
|
SUCCESS, FAILURE
|
||||||
|
}
|
||||||
|
|
||||||
|
private final ResultType type;
|
||||||
|
private final String message;
|
||||||
|
private final Task nextStep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the result of a step execution. Each step can either succeed, fail, or transition to another step.
|
||||||
|
*/
|
||||||
|
private StepResult(ResultType type, String message, Task nextStep) {
|
||||||
|
this.type = type;
|
||||||
|
this.message = message;
|
||||||
|
this.nextStep = nextStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the step was completed successfully, with an optional follow-up step.
|
||||||
|
*
|
||||||
|
* @param message the success message
|
||||||
|
* @param nextStep the next step in the workflow, or null if this is the final step
|
||||||
|
* @return a successful result
|
||||||
|
*/
|
||||||
|
public static StepResult success(String message, Task nextStep) {
|
||||||
|
return new StepResult(ResultType.SUCCESS, message, nextStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the step was completed successfully.
|
||||||
|
*
|
||||||
|
* @param message the success message
|
||||||
|
* @return a successful result without a next step
|
||||||
|
*/
|
||||||
|
public static StepResult success(String message) {
|
||||||
|
return new StepResult(ResultType.SUCCESS, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the step has failed.
|
||||||
|
*
|
||||||
|
* @param message the failure message
|
||||||
|
* @return a failed result
|
||||||
|
*/
|
||||||
|
public static StepResult failure(String message) {
|
||||||
|
return new StepResult(ResultType.FAILURE, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves directly to the next step without displaying a message.
|
||||||
|
*
|
||||||
|
* @param nextTask the next task in the workflow
|
||||||
|
* @return a successful result that advances to the next step
|
||||||
|
*/
|
||||||
|
public static StepResult next(Task nextTask) {
|
||||||
|
return new StepResult(ResultType.SUCCESS, null, nextTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the message associated with the step result.
|
||||||
|
*
|
||||||
|
* @return the result message, or null if there is no message
|
||||||
|
*/
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the next step in the task workflow.
|
||||||
|
*
|
||||||
|
* @return the next step, or null if this is the last step
|
||||||
|
*/
|
||||||
|
public Task getNextStep() {
|
||||||
|
return nextStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the step execution was successful.
|
||||||
|
*
|
||||||
|
* @return true if the step was successful, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return type == ResultType.SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the type of step input expected.
|
||||||
|
*
|
||||||
|
* @return the step type
|
||||||
|
*/
|
||||||
|
StepType getStepType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a general description of the step. This will be displayed before asking the question.
|
||||||
|
*
|
||||||
|
* @return the step description
|
||||||
|
*/
|
||||||
|
String getStepDescription();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the specific question for this step.
|
||||||
|
*
|
||||||
|
* @return the step question
|
||||||
|
*/
|
||||||
|
String getStepQuestion();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the step using the provided answer.
|
||||||
|
*
|
||||||
|
* @param answer the user's input, validated against the expected step type
|
||||||
|
* @return the result of executing this step
|
||||||
|
*/
|
||||||
|
StepResult executeStep(Object answer);
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import me.bobert.netneutrality.core.LogHandler;
|
|||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.InteractionContext;
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.impl.StepTask;
|
|
||||||
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
@@ -81,10 +80,6 @@ public class TaskManager {
|
|||||||
return tasks.values();
|
return tasks.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized Collection<Task> getPendingTasks() {
|
|
||||||
return pendingTasks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void insert(CommandContext context, Task task) {
|
public void insert(CommandContext context, Task task) {
|
||||||
String id = task.getId();
|
String id = task.getId();
|
||||||
|
|
||||||
@@ -102,7 +97,7 @@ public class TaskManager {
|
|||||||
taskService.persistTask(persistableTask);
|
taskService.persistTask(persistableTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task instanceof StepTask) {
|
if (task instanceof IStepTask) {
|
||||||
pendingTasks.add(task);
|
pendingTasks.add(task);
|
||||||
} else {
|
} else {
|
||||||
tasks.put(id, task);
|
tasks.put(id, task);
|
||||||
@@ -116,7 +111,7 @@ public class TaskManager {
|
|||||||
if (task instanceof WithChildren taskWithChildren) {
|
if (task instanceof WithChildren taskWithChildren) {
|
||||||
List<Task> childrens = taskWithChildren.getChildTasks();
|
List<Task> childrens = taskWithChildren.getChildTasks();
|
||||||
if (childrens == null || childrens.isEmpty()) {
|
if (childrens == null || childrens.isEmpty()) {
|
||||||
logger.warn("Task [{}]: parent task doesn't create any childs", task.getId());
|
logger.warn("Parent task '{}' doesn't create any childs", task.getName());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,22 +121,25 @@ public class TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void activateTask(Task task) {
|
public void activateTask(Task task, String oldId) {
|
||||||
if (pendingTasks.remove(task)) {
|
if (pendingTasks.remove(task)) {
|
||||||
tasks.put(task.getId(), task);
|
tasks.put(task.getId(), task);
|
||||||
logger.info("Updating task {} with new id: {}", task.getName(), task.getId());
|
logger.info("Updating task {} with new id: {} (was: {})",
|
||||||
|
task.getName(), task.getId(), oldId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeTask(Task task) {
|
public void removeTask(Task task) {
|
||||||
String id = task.getId();
|
String id = task.getId();
|
||||||
|
|
||||||
if (tasks.containsKey(id)) {
|
if (tasks.remove(id) != null) {
|
||||||
logger.debug("Removing task: {}", task);
|
logger.debug("Removing task: {}", id);
|
||||||
tasks.remove(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
tasksLock.remove(task.getLockId());
|
ReentrantLock lock = tasksLock.remove(task.getLockId());
|
||||||
|
if (lock != null) {
|
||||||
|
logger.debug("Cleaning up lock for removed task: {}", task.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(TaskContext context) {
|
public void handle(TaskContext context) {
|
||||||
@@ -166,15 +164,15 @@ public class TaskManager {
|
|||||||
if (lock != null) {
|
if (lock != null) {
|
||||||
logger.debug("Aquiring lock for task: {}", taskId);
|
logger.debug("Aquiring lock for task: {}", taskId);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
logger.debug("Aquired lock for: {}", lockId);
|
|
||||||
|
|
||||||
if (!tasks.containsKey(taskId)) {
|
if (!tasks.containsKey(taskId)) {
|
||||||
logger.debug("Task {} is no longer available after aquiring lock", taskId);
|
logger.debug("Task {} is no longer available after aquiring lock", taskId);
|
||||||
|
tasksLock.remove(lockId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("Executing task: {}", task);
|
logger.debug("Executing task: {}", taskId);
|
||||||
Task.Lifecycle status = task.execute(context);
|
Task.Lifecycle status = task.execute(context);
|
||||||
handleTaskLifecycle(task, status);
|
handleTaskLifecycle(task, status);
|
||||||
|
|
||||||
@@ -201,6 +199,11 @@ public class TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <E extends Task> Optional<E> findTaskByPartialId(Class<E> clazz, String partialId) {
|
||||||
|
List<E> tasks = findTasksByPartialId(clazz, partialId);
|
||||||
|
return Optional.ofNullable(tasks.isEmpty() ? null : tasks.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
public <E extends Task> List<E> findTasksByPartialId(Class<E> clazz, String partialId) {
|
public <E extends Task> List<E> findTasksByPartialId(Class<E> clazz, String partialId) {
|
||||||
return tasks.values().stream()
|
return tasks.values().stream()
|
||||||
.filter(clazz::isInstance)
|
.filter(clazz::isInstance)
|
||||||
|
|||||||
@@ -6,10 +6,14 @@ package me.bobert.netneutrality.core.tasks.impl;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
|
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 net.dv8tion.jda.api.interactions.components.buttons.Button;
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
|
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class ButtonTask extends GenericRandomIdTask {
|
public abstract class ButtonTask extends GenericRandomIdTask {
|
||||||
private final ButtonStyle buttonStyle;
|
private final ButtonStyle buttonStyle;
|
||||||
@@ -33,4 +37,17 @@ public abstract class ButtonTask extends GenericRandomIdTask {
|
|||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now());
|
return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ActionRow createActionRow(List<Task> 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -15,10 +15,12 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
public abstract class EmojiTask extends AbstractPersistableTask {
|
public abstract class EmojiTask extends AbstractPersistableTask {
|
||||||
private final Map<String, String> emojiMapping = new ConcurrentHashMap<>();
|
private final Map<String, String> emojiMapping = new ConcurrentHashMap<>();
|
||||||
private final String guildId;
|
private final String guildId;
|
||||||
|
private final String channelId;
|
||||||
private final String messageId;
|
private final String messageId;
|
||||||
|
|
||||||
public EmojiTask(String guildId, String messageId) {
|
public EmojiTask(String guildId, String channelId, String messageId) {
|
||||||
this.guildId = guildId;
|
this.guildId = guildId;
|
||||||
|
this.channelId = channelId;
|
||||||
this.messageId = messageId;
|
this.messageId = messageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,16 +28,22 @@ public abstract class EmojiTask extends AbstractPersistableTask {
|
|||||||
return guildId;
|
return guildId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getChannelId() {
|
||||||
|
return channelId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getMessageId() {
|
public String getMessageId() {
|
||||||
return messageId;
|
return messageId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addEmoji(String emoji, String snowflake) {
|
public void addEmoji(String emoji, String snowflake) {
|
||||||
emojiMapping.put(emoji, snowflake);
|
emojiMapping.put(emoji, snowflake);
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeEmoji(String emoji) {
|
public void removeEmoji(String emoji) {
|
||||||
emojiMapping.remove(emoji);
|
emojiMapping.remove(emoji);
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getEmojiMapping() {
|
public Map<String, String> getEmojiMapping() {
|
||||||
|
|||||||
@@ -10,23 +10,26 @@ import me.bobert.netneutrality.core.context.CommandContext;
|
|||||||
import me.bobert.netneutrality.core.context.MessageContext;
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.AbstractTask;
|
import me.bobert.netneutrality.core.tasks.AbstractTask;
|
||||||
|
import me.bobert.netneutrality.core.tasks.IStepTask;
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
import net.dv8tion.jda.api.entities.Message;
|
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.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
import org.slf4j.Logger;
|
import net.fellbaum.jemoji.Emoji;
|
||||||
import org.slf4j.LoggerFactory;
|
import net.fellbaum.jemoji.EmojiManager;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public abstract class StepTask extends AbstractTask {
|
/**
|
||||||
private static final Logger logger = LoggerFactory.getLogger(StepTask.class);
|
* 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 enum StepType {
|
*/
|
||||||
STRING, ROLE, EMOJI
|
public abstract class StepTask extends AbstractTask implements IStepTask {
|
||||||
}
|
private final Object stepLock = new Object();
|
||||||
|
private CommandContext commandContext;
|
||||||
private Guild guild;
|
private Guild guild;
|
||||||
private String id = UUID.randomUUID().toString();
|
private String id = UUID.randomUUID().toString();
|
||||||
|
|
||||||
@@ -35,45 +38,76 @@ public abstract class StepTask extends AbstractTask {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guild getGuild() {
|
||||||
|
return guild;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void replyToInteraction(CommandContext context) {
|
public void replyToInteraction(CommandContext context) {
|
||||||
|
synchronized (stepLock) {
|
||||||
|
this.commandContext = context;
|
||||||
|
}
|
||||||
|
|
||||||
if (context.isFromGuild()) {
|
if (context.isFromGuild()) {
|
||||||
guild = context.getGuild();
|
guild = context.getGuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (guild == null && getStepType() == StepType.ROLE) {
|
if (guild == null && getStepType() == StepType.ROLE) {
|
||||||
throw new IllegalArgumentException("Step task if of type role but it was inserted in a non-guild context");
|
throw new IllegalArgumentException("Step task is of type role but it was inserted in a non-guild context");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inDM()) {
|
|
||||||
context.replyEphemeral("Please follow the instructions sent in DM.");
|
context.replyEphemeral("Please follow the instructions sent in DM.");
|
||||||
context.sendMessageInPrivate(getStepDescription() + "\n" + getStepQuestion(), asyncContext -> assignId(asyncContext.getMessageId()));
|
context.sendMessageInPrivate(getStepDescription() + "\n\n" + getStepQuestion(),
|
||||||
} else {
|
asyncContext -> assignId(asyncContext.getMessageId()));
|
||||||
context.reply(getStepQuestion(), asyncContext -> id = asyncContext.getMessageId());
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("step id : " + id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Lifecycle execute(TaskContext context) {
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
synchronized (stepLock) {
|
||||||
|
if (!context.getDiscordUser().equals(commandContext.getDiscordUser())) {
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (context instanceof MessageContext messageContext) {
|
if (context instanceof MessageContext messageContext) {
|
||||||
Message message = messageContext.getMessage();
|
Message message = messageContext.getMessage();
|
||||||
String answer = message.getContentRaw();
|
String input = message.getContentRaw();
|
||||||
|
|
||||||
if (validateAnswer(answer)) {
|
|
||||||
StepTask nextStep = executeStep(answer);
|
|
||||||
|
|
||||||
//TODO: to finish
|
Object answer = validateInput(context, input);
|
||||||
|
if (answer == null) {
|
||||||
|
return Lifecycle.KEEP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StepResult result = executeStep(answer);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
context.replyEphemeral(result.getMessage());
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getMessage() != null) {
|
||||||
|
context.replyEphemeral(result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.getNextStep() != null) {
|
||||||
|
result.getNextStep().insert(commandContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Lifecycle.REMOVE_SELF;
|
return Lifecycle.REMOVE_SELF;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Step task is not of type MessageContext");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return getCreationTime().plusMinutes(30).isBefore(OffsetDateTime.now());
|
// is it really needed to expire this type of task?
|
||||||
|
return getCreationTime().plusMinutes(60).isBefore(OffsetDateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -81,8 +115,44 @@ public abstract class StepTask extends AbstractTask {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validateAnswer(String answer) {
|
/**
|
||||||
return false;
|
* Validates the user input against the expected step type.
|
||||||
|
*
|
||||||
|
* @param context the task execution context
|
||||||
|
* @param input the user-provided input
|
||||||
|
* @return the parsed input if valid, or null if invalid
|
||||||
|
*/
|
||||||
|
private Object validateInput(TaskContext context, String input) {
|
||||||
|
switch (getStepType()) {
|
||||||
|
case ROLE -> {
|
||||||
|
try {
|
||||||
|
Role role = guild.getRoleById(input);
|
||||||
|
if (role == null) {
|
||||||
|
context.replyEphemeral("The role doesn't exist");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return role;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
context.replyEphemeral("Not a valid role ID");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case EMOJI -> {
|
||||||
|
Optional<Emoji> parsed = EmojiManager.getEmoji(input);
|
||||||
|
|
||||||
|
if (parsed.isPresent()) {
|
||||||
|
return parsed.get().getEmoji();
|
||||||
|
}
|
||||||
|
|
||||||
|
context.replyEphemeral("Not a valid emoji");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case STRING -> {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
default -> throw new IllegalStateException("Unknown step type: " + getStepType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assignId(String newId) {
|
private void assignId(String newId) {
|
||||||
@@ -90,21 +160,10 @@ public abstract class StepTask extends AbstractTask {
|
|||||||
throw new IllegalArgumentException("Task ID cannot be null or empty.");
|
throw new IllegalArgumentException("Task ID cannot be null or empty.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String oldId = id;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
this.id = newId;
|
this.id = newId;
|
||||||
TaskManager.getInstance().activateTask(this);
|
TaskManager.getInstance().activateTask(this, oldId);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.id = newId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean inDM();
|
|
||||||
|
|
||||||
public abstract StepType getStepType();
|
|
||||||
|
|
||||||
public abstract String getStepDescription();
|
|
||||||
|
|
||||||
public abstract String getStepQuestion();
|
|
||||||
|
|
||||||
public abstract StepTask executeStep(String answer);
|
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ public class TaskFactory {
|
|||||||
|
|
||||||
public static PersistableTask createTaskInstance(String className, JSONArray params, JSONArray types, JSONObject payload) throws Exception {
|
public static PersistableTask createTaskInstance(String className, JSONArray params, JSONArray types, JSONObject payload) throws Exception {
|
||||||
if (params.length() != types.length()) {
|
if (params.length() != types.length()) {
|
||||||
throw new RuntimeException("Wrong number of parameters and types.");
|
throw new RuntimeException("Wrong number of parameters and types found in task mappings");
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> clazz = Class.forName(className);
|
Class<?> clazz = Class.forName(className);
|
||||||
|
|||||||
@@ -19,7 +19,11 @@ public interface WithChildren extends Task {
|
|||||||
|
|
||||||
List<Task> getChildTasks();
|
List<Task> getChildTasks();
|
||||||
|
|
||||||
|
default void addChild(List<Task> tasks) {
|
||||||
|
getChildTasks().addAll(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
default void addChild(Task... tasks) {
|
default void addChild(Task... tasks) {
|
||||||
getChildTasks().addAll(List.of(tasks));
|
addChild(List.of(tasks));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ public class TaskService {
|
|||||||
for (OTask oTask : tasks) {
|
for (OTask oTask : tasks) {
|
||||||
String taskId = oTask.getTaskId();
|
String taskId = oTask.getTaskId();
|
||||||
String taskClass = oTask.getTaskClass();
|
String taskClass = oTask.getTaskClass();
|
||||||
logger.debug("Trying to load task with class: {}", taskClass);
|
logger.debug("Trying to load task with ID: {} and class: {}", taskId, taskClass);
|
||||||
|
|
||||||
String payload = oTask.getPayload();
|
String payload = oTask.getPayload();
|
||||||
|
|
||||||
@@ -95,11 +95,12 @@ public class TaskService {
|
|||||||
|
|
||||||
oTask.setPayload(task.toPayload());
|
oTask.setPayload(task.toPayload());
|
||||||
|
|
||||||
|
logger.info("Persisting task: {}", oTask.getTaskId());
|
||||||
taskRepository.save(oTask);
|
taskRepository.save(oTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unpersistTask(PersistableTask task) {
|
public void unpersistTask(PersistableTask task) {
|
||||||
logger.debug("Unpersisting task: {}", task);
|
logger.debug("Unpersisting task: {}", task.getId());
|
||||||
TaskRepository taskRepository = new TaskRepository();
|
TaskRepository taskRepository = new TaskRepository();
|
||||||
|
|
||||||
taskRepository.deleteBy(task.getId());
|
taskRepository.deleteBy(task.getId());
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadFailed(FriendlyException e) {
|
public void loadFailed(FriendlyException e) {
|
||||||
logger.debug("Guild {}: load failed for identifier {}", guild.getId(), identifier, e);
|
logger.debug("Guild {}: load failed for identifier {}", guild.getId(), identifier);
|
||||||
|
|
||||||
//TODO: better error messages
|
//TODO: better error messages
|
||||||
RuntimeException exception = new RuntimeException("Failed to load track: " + e.getMessage(), e);
|
RuntimeException exception = new RuntimeException("Failed to load track: " + e.getMessage(), e);
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ import java.util.Map;
|
|||||||
|
|
||||||
public class EmojiReactionRoleTask extends EmojiTask {
|
public class EmojiReactionRoleTask extends EmojiTask {
|
||||||
public EmojiReactionRoleTask(String guildId, String channelId, String messageId) {
|
public EmojiReactionRoleTask(String guildId, String channelId, String messageId) {
|
||||||
super(guildId, messageId);
|
super(guildId, channelId, messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// constructor for auto loading
|
// constructor for auto loading
|
||||||
public EmojiReactionRoleTask(String guildId, String messageId, Map<String, String> emojiMapping) {
|
public EmojiReactionRoleTask(String guildId, String channelId, String messageId, Map<String, String> emojiMapping) {
|
||||||
super(guildId, messageId);
|
super(guildId, channelId, messageId);
|
||||||
getEmojiMapping().putAll(emojiMapping);
|
getEmojiMapping().putAll(emojiMapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package me.bobert.netneutrality.tasks.generic;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
||||||
import me.bobert.netneutrality.core.tasks.impl.ButtonTask;
|
import me.bobert.netneutrality.core.tasks.impl.ButtonTask;
|
||||||
@@ -18,7 +19,7 @@ import java.util.function.Function;
|
|||||||
* A generic button task that represents a button to accept a choice.
|
* A generic button task that represents a button to accept a choice.
|
||||||
* @param <P> the type of the parent task, which must implement WithChildren
|
* @param <P> the type of the parent task, which must implement WithChildren
|
||||||
*/
|
*/
|
||||||
public class AcceptButton<P extends WithChildren> extends ButtonTask implements WithParent<P> {
|
public class AcceptButton<P extends WithChildren> extends ButtonTask implements WithParent<P>, NotAcknowledgeable {
|
||||||
private final P parentTask;
|
private final P parentTask;
|
||||||
private final Function<TaskContext, Lifecycle> function;
|
private final Function<TaskContext, Lifecycle> function;
|
||||||
|
|
||||||
@@ -32,10 +33,18 @@ public class AcceptButton<P extends WithChildren> extends ButtonTask implements
|
|||||||
return new AcceptButton<>(parentTask, ButtonStyle.PRIMARY, buttonLabel, function);
|
return new AcceptButton<>(parentTask, ButtonStyle.PRIMARY, buttonLabel, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <P extends WithChildren> AcceptButton<P> createSecondaryButton(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
return new AcceptButton<>(parentTask, ButtonStyle.SECONDARY, buttonLabel, function);
|
||||||
|
}
|
||||||
|
|
||||||
public static <P extends WithChildren> AcceptButton<P> createSuccessButton(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
public static <P extends WithChildren> AcceptButton<P> createSuccessButton(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
return new AcceptButton<>(parentTask, ButtonStyle.SUCCESS, buttonLabel, function);
|
return new AcceptButton<>(parentTask, ButtonStyle.SUCCESS, buttonLabel, function);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <P extends WithChildren> AcceptButton<P> createDangerButton(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
return new AcceptButton<>(parentTask, ButtonStyle.DANGER, buttonLabel, function);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public P getParentTask() {
|
public P getParentTask() {
|
||||||
return parentTask;
|
return parentTask;
|
||||||
@@ -46,6 +55,11 @@ public class AcceptButton<P extends WithChildren> extends ButtonTask implements
|
|||||||
return "accept-" + parentTask.getName();
|
return "accept-" + parentTask.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Lifecycle execute(TaskContext context) {
|
public Lifecycle execute(TaskContext context) {
|
||||||
return function.apply(context);
|
return function.apply(context);
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ public class CancelButton<P extends WithChildren> extends ButtonTask implements
|
|||||||
return "cancel-" + parentTask.getName();
|
return "cancel-" + parentTask.getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Lifecycle execute(TaskContext context) {
|
public Lifecycle execute(TaskContext context) {
|
||||||
return function.apply(context);
|
return function.apply(context);
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import me.bobert.netneutrality.utils.MusicUtils;
|
|||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -40,10 +39,6 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
this.sourceType = sourceType;
|
this.sourceType = sourceType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioPlaylist getPlaylist() {
|
|
||||||
return playlist;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "load-playlist";
|
return "load-playlist";
|
||||||
@@ -69,17 +64,14 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
// Search results
|
// Search results
|
||||||
if (playlist.isSearchResult()) {
|
if (playlist.isSearchResult()) {
|
||||||
List<AudioTrack> searchResults = playlist.getTracks().stream().limit(searchResultsLimit).toList();
|
List<AudioTrack> searchResults = playlist.getTracks().stream().limit(searchResultsLimit).toList();
|
||||||
List<ItemComponent> buttonList = new ArrayList<>();
|
List<Task> loadTasks = new ArrayList<>();
|
||||||
|
|
||||||
for (int i = 0; i < searchResults.size(); i++) {
|
for (int i = 0; i < searchResults.size(); i++) {
|
||||||
AudioTrack track = searchResults.get(i);
|
AudioTrack track = searchResults.get(i);
|
||||||
Task buttonTask = new LoadTrackFromPlaylistTask(this, String.valueOf(i + 1), track);
|
loadTasks.add(new LoadTrackFromPlaylistTask(this, String.valueOf(i + 1), track));
|
||||||
|
|
||||||
buttonList.add(buttonTask.createButton());
|
|
||||||
addChild(buttonTask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionRow firstRow = ActionRow.of(buttonList);
|
ActionRow firstRow = createActionRow(loadTasks);
|
||||||
ActionRow secondRow = commonButtons(searchResults, "Add all tracks", "Not for me honey");
|
ActionRow secondRow = commonButtons(searchResults, "Add all tracks", "Not for me honey");
|
||||||
|
|
||||||
context.edit(MessageFunction.buildSearchResultsMessage(searchResults, context.getGuildUser()), List.of(firstRow, secondRow));
|
context.edit(MessageFunction.buildSearchResultsMessage(searchResults, context.getGuildUser()), List.of(firstRow, secondRow));
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package me.bobert.netneutrality.tasks.reaction;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.tasks.impl.StepTask;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
|
|
||||||
|
public class EmojiRoleFirstStep extends StepTask {
|
||||||
|
private final Message target;
|
||||||
|
|
||||||
|
public EmojiRoleFirstStep(Message target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "role-first-step";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StepType getStepType() {
|
||||||
|
return StepType.ROLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStepDescription() {
|
||||||
|
return String.format("Creating a new emoji role mapping in guild %s for message %s",
|
||||||
|
getGuild().getId(),
|
||||||
|
target.getJumpUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStepQuestion() {
|
||||||
|
return "Reply to this message with the role ID you want to configure";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StepResult executeStep(Object answer) {
|
||||||
|
return StepResult.next(new EmojiRoleSecondStep(target, (Role) answer));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
package me.bobert.netneutrality.tasks.reaction;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.core.tasks.impl.ButtonTask;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
|
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
|
||||||
|
import me.bobert.netneutrality.tasks.generic.AcceptButton;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EmojiRoleModify extends ButtonTask implements WithChildren {
|
||||||
|
private final List<Task> childrens = new ArrayList<>();
|
||||||
|
private final Message target;
|
||||||
|
private final EmojiReactionRoleTask task;
|
||||||
|
|
||||||
|
public EmojiRoleModify(Message target, EmojiReactionRoleTask task) {
|
||||||
|
super(null, null);
|
||||||
|
this.target = target;
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "modify emoji role";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInsertable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyToInteraction(CommandContext context) {
|
||||||
|
Task addEmojiTask = AcceptButton.createSuccessButton(this, "Add emoji role mapping", taskContext -> {
|
||||||
|
if (task.getEmojiMapping().size() >= 10) {
|
||||||
|
taskContext.replyEphemeral("You cannot add more than 10 emoji roles on a message");
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
new EmojiRoleFirstStep(target).insert(context);
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
});
|
||||||
|
|
||||||
|
Task removeEmojiTask = AcceptButton.createSecondaryButton(this, "Remove emoji role mapping", taskContext -> {
|
||||||
|
if (task.getEmojiMapping().isEmpty()) {
|
||||||
|
taskContext.replyEphemeral("No emoji role mapping found for the message");
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
new EmojiRoleRemoveStep(target, task).insert(context);
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
});
|
||||||
|
|
||||||
|
Task deleteMappingTask = AcceptButton.createDangerButton(this, "Unregister task", taskContext -> {
|
||||||
|
if (!TaskManager.getInstance().hasTask(task.getId())) {
|
||||||
|
taskContext.replyEphemeral("This task no longer exists");
|
||||||
|
return Lifecycle.REMOVE_SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
task.delete();
|
||||||
|
context.replyEphemeral("Task deleted successfully!");
|
||||||
|
return Lifecycle.REMOVE_SELF;
|
||||||
|
});
|
||||||
|
|
||||||
|
ActionRow row = createActionRow(addEmojiTask, removeEmojiTask, deleteMappingTask);
|
||||||
|
context.replyEphemeral("Select what you want to do with this message", List.of(row));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Task> getChildTasks() {
|
||||||
|
return childrens;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
package me.bobert.netneutrality.tasks.reaction;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.tasks.impl.StepTask;
|
||||||
|
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 {
|
||||||
|
private final Message target;
|
||||||
|
private final EmojiReactionRoleTask task;
|
||||||
|
|
||||||
|
public EmojiRoleRemoveStep(Message target, EmojiReactionRoleTask task) {
|
||||||
|
this.target = target;
|
||||||
|
this.task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StepType getStepType() {
|
||||||
|
return StepType.EMOJI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStepDescription() {
|
||||||
|
return String.format("Select which emoji you want to remove from the message %s", target.getJumpUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStepQuestion() {
|
||||||
|
StringJoiner joiner = new StringJoiner(" - ");
|
||||||
|
Set<String> emojis = task.getEmojiMapping().keySet();
|
||||||
|
for (String emoji : emojis) {
|
||||||
|
joiner.add(emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
return joiner.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StepResult executeStep(Object answer) {
|
||||||
|
String emoji = (String) answer;
|
||||||
|
|
||||||
|
if (!task.getEmojiMapping().containsKey(emoji)) {
|
||||||
|
return StepResult.failure("The emoji doesn't exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
task.removeEmoji(emoji);
|
||||||
|
return StepResult.success("Removed emoji successfully!");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "emoji-role-remove";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package me.bobert.netneutrality.tasks.reaction;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.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 {
|
||||||
|
private final Message target;
|
||||||
|
private final Role role;
|
||||||
|
|
||||||
|
public EmojiRoleSecondStep(Message target, Role role) {
|
||||||
|
this.target = target;
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StepType getStepType() {
|
||||||
|
return StepType.EMOJI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStepDescription() {
|
||||||
|
return String.format("Creating a new emoji role mapping in guild %s for message %s and role %s",
|
||||||
|
getGuild().getId(),
|
||||||
|
target.getJumpUrl(),
|
||||||
|
role.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStepQuestion() {
|
||||||
|
return "Reply with the emoji you want to add to the role";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StepResult executeStep(Object answer) {
|
||||||
|
String emoji = (String) answer;
|
||||||
|
Task emojiTask = new EmojiReactionRoleTask(target.getGuildId(),
|
||||||
|
target.getChannelId(),
|
||||||
|
target.getId(),
|
||||||
|
Map.of(emoji, role.getId()));
|
||||||
|
|
||||||
|
if (TaskManager.getInstance().hasTask(emojiTask.getId())) {
|
||||||
|
return StepResult.failure("The message ID is already registered with the bot, please use the modify mapping command instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
return StepResult.success("Mapping registered successfully for role " + role.getName() + " with emoji " + emoji, emojiTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "role-second-step";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,19 +18,18 @@ public class CleanupTaskThread implements Runnable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
List<Task> tasks = TaskManager.getInstance().getTasks()
|
TaskManager manager = TaskManager.getInstance();
|
||||||
|
|
||||||
|
List<Task> expiredTasks = manager.getTasks()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(task -> !(task instanceof PersistableTask))
|
.filter(task -> !(task instanceof PersistableTask))
|
||||||
.filter(task -> !(task instanceof BuiltInTask))
|
.filter(task -> !(task instanceof BuiltInTask))
|
||||||
.filter(Task::isExpired)
|
.filter(Task::isExpired)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (!tasks.isEmpty()) {
|
if (!expiredTasks.isEmpty()) {
|
||||||
logger.info("Cleaning up {} expired tasks..", tasks.size());
|
logger.info("Cleaning up {} expired tasks..", expiredTasks.size());
|
||||||
|
expiredTasks.forEach(manager::removeTask);
|
||||||
for (Task task : tasks) {
|
|
||||||
TaskManager.getInstance().removeTask(task);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,10 +4,12 @@
|
|||||||
"class": "me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask",
|
"class": "me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask",
|
||||||
"params": [
|
"params": [
|
||||||
"guildId",
|
"guildId",
|
||||||
|
"channelId",
|
||||||
"messageId",
|
"messageId",
|
||||||
"emojiMapping"
|
"emojiMapping"
|
||||||
],
|
],
|
||||||
"types": [
|
"types": [
|
||||||
|
"string",
|
||||||
"string",
|
"string",
|
||||||
"string",
|
"string",
|
||||||
"map"
|
"map"
|
||||||
|
|||||||
Reference in new issue
Block a user