finish abstraction for StepTask
This commit is contained in:
1 parent
5ab18ff5ce
commit
87f78ccbea
4 files changed
+250
-56
No files matched your search
@@ -5,8 +5,6 @@ package me.bobert.netneutrality.core.command;
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
|
||||
public abstract class AbstractAdminCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
@@ -14,11 +12,6 @@ public abstract class AbstractAdminCommand extends AbstractCommand {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||
return DefaultMemberPermissions.DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandCategory getCategory() {
|
||||
return CommandCategory.ADMINISTRATION;
|
||||
|
||||
@@ -17,11 +17,6 @@ public abstract class AbstractTask implements Task {
|
||||
creationTime = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInsertable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OffsetDateTime getCreationTime() {
|
||||
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);
|
||||
}
|
||||
@@ -10,23 +10,26 @@ 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.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 org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import net.fellbaum.jemoji.Emoji;
|
||||
import net.fellbaum.jemoji.EmojiManager;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public abstract class StepTask extends AbstractTask {
|
||||
private static final Logger logger = LoggerFactory.getLogger(StepTask.class);
|
||||
|
||||
public enum StepType {
|
||||
STRING, ROLE, EMOJI
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
private final Object stepLock = new Object();
|
||||
private CommandContext commandContext;
|
||||
private Guild guild;
|
||||
private String id = UUID.randomUUID().toString();
|
||||
|
||||
@@ -35,45 +38,76 @@ public abstract class StepTask extends AbstractTask {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInsertable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replyToInteraction(CommandContext context) {
|
||||
synchronized (stepLock) {
|
||||
this.commandContext = context;
|
||||
}
|
||||
|
||||
if (context.isFromGuild()) {
|
||||
guild = context.getGuild();
|
||||
}
|
||||
|
||||
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.sendMessageInPrivate(getStepDescription() + "\n" + getStepQuestion(), asyncContext -> assignId(asyncContext.getMessageId()));
|
||||
} else {
|
||||
context.reply(getStepQuestion(), asyncContext -> id = asyncContext.getMessageId());
|
||||
}
|
||||
|
||||
System.out.println("step id : " + id);
|
||||
context.replyEphemeral("Please follow the instructions sent in DM.");
|
||||
context.sendMessageInPrivate(getStepDescription() + "\n\n" + getStepQuestion(),
|
||||
asyncContext -> assignId(asyncContext.getMessageId()));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Lifecycle execute(TaskContext context) {
|
||||
if (context instanceof MessageContext messageContext) {
|
||||
Message message = messageContext.getMessage();
|
||||
String answer = message.getContentRaw();
|
||||
|
||||
if (validateAnswer(answer)) {
|
||||
StepTask nextStep = executeStep(answer);
|
||||
|
||||
//TODO: to finish
|
||||
synchronized (stepLock) {
|
||||
if (!context.getDiscordUser().equals(commandContext.getDiscordUser())) {
|
||||
return Lifecycle.KEEP;
|
||||
}
|
||||
}
|
||||
|
||||
return Lifecycle.REMOVE_SELF;
|
||||
if (context instanceof MessageContext messageContext) {
|
||||
Message message = messageContext.getMessage();
|
||||
String input = message.getContentRaw();
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Step task is not of type MessageContext");
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
@@ -81,8 +115,44 @@ public abstract class StepTask extends AbstractTask {
|
||||
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) {
|
||||
@@ -90,21 +160,10 @@ public abstract class StepTask extends AbstractTask {
|
||||
throw new IllegalArgumentException("Task ID cannot be null or empty.");
|
||||
}
|
||||
|
||||
String oldId = id;
|
||||
synchronized (this) {
|
||||
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);
|
||||
}
|
||||
Reference in new issue
Block a user