Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions src/main/java/net/discordjug/javabot/Bot.java
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,12 @@ private void registerComponentHandlers(@NotNull ApplicationContext ctx) {
List<IdMapping<StringSelectMenuHandler>> stringSelectMappings = new ArrayList<>();
for (Object handler : interactionHandlers.values()) {
AutoDetectableComponentHandler annotation = handler.getClass().getAnnotation(AutoDetectableComponentHandler.class);
String[] keys = annotation.value();
addComponentHandler(buttonMappings, keys, handler, ButtonHandler.class);
addComponentHandler(modalMappings, keys, handler, ModalHandler.class);
addComponentHandler(stringSelectMappings, keys, handler, StringSelectMenuHandler.class);
if (annotation != null) {//superclasses are annotated, ignore
String[] keys = annotation.value();
addComponentHandler(buttonMappings, keys, handler, ButtonHandler.class);
addComponentHandler(modalMappings, keys, handler, ModalHandler.class);
addComponentHandler(stringSelectMappings, keys, handler, StringSelectMenuHandler.class);
}
}
dih4jda.addButtonMappings(buttonMappings.toArray(IdMapping[]::new));
dih4jda.addModalMappings(modalMappings.toArray(IdMapping[]::new));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,6 @@ private boolean isInvalidHelpForumChannel(@NotNull ForumChannel forum) {

private void handleHelpThanksInteraction(@NotNull ButtonInteractionEvent event, @NotNull HelpManager manager, String @NotNull [] id) {
ThreadChannel post = manager.getPostThread();
HelpConfig config = botConfig.get(event.getGuild()).getHelpConfig();
if (event.getUser().getIdLong() != post.getOwnerIdLong()) {
Responses.warning(
event,
Expand Down Expand Up @@ -301,16 +300,14 @@ private void handleReplyGuidelines(@NotNull IReplyCallback callback, @NotNull Fo
}

private void handlePostClose(ButtonInteractionEvent event, @NotNull HelpManager manager) {
if (manager.isForumEligibleToBeUnreserved(event)) {
manager.close(event, event.getUser().getIdLong() == manager.getPostThread()
.getOwnerIdLong(), null);
} else {
if (event.getUser().getIdLong() != manager.getPostThread().getOwnerIdLong()) {
Responses.warning(
event,
"Could not close this post",
"You're not allowed to close this post."
"Sorry, but only the original poster can close this post using these buttons."
)
.queue();
return;
}
manager.close(event, true, null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,23 @@ public void close(IReplyCallback callback, boolean withHelpers, @Nullable String
.queue(s -> postThread.getManager().setLocked(true).setArchived(true).queue());
if (callback.getMember().getIdLong() != postThread.getOwnerIdLong() &&
Boolean.parseBoolean(preferenceService.getOrCreate(postThread.getOwnerIdLong(), Preference.PRIVATE_CLOSE_NOTIFICATIONS).getState())) {

postThread.getOwner().getUser().openPrivateChannel()
.flatMap(c -> createDMCloseInfoEmbed(callback.getMember(), postThread, reason, c))
.queue(success -> {}, failure -> {});

botConfig.get(callback.getGuild())
.getModerationConfig()
.getLogChannel()
.sendMessageEmbeds(new EmbedBuilder()
.setTitle("Post closed by non-original poster")
.setDescription("The post " + postThread.getAsMention() +
" has been closed by " + callback.getMember().getAsMention() + ".\n\n" +
"[Post link](" + postThread.getJumpUrl() + ")")
.addField("Reason", reason, false)
.setAuthor(callback.getMember().getEffectiveName(), null, callback.getMember().getEffectiveAvatarUrl())
.build())
.queue();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
package net.discordjug.javabot.systems.help.commands;

import java.util.List;

import org.jetbrains.annotations.NotNull;

import net.discordjug.javabot.annotations.AutoDetectableComponentHandler;
import net.discordjug.javabot.data.config.BotConfig;
import net.discordjug.javabot.data.h2db.DbActions;
import net.discordjug.javabot.systems.help.HelpManager;
import net.discordjug.javabot.systems.help.dao.HelpAccountRepository;
import net.discordjug.javabot.systems.help.dao.HelpTransactionRepository;
import net.discordjug.javabot.systems.user_preferences.UserPreferenceService;
import net.discordjug.javabot.util.ExceptionLogger;
import net.discordjug.javabot.util.Responses;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.entities.channel.unions.MessageChannelUnion;
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.CommandInteraction;
import net.dv8tion.jda.api.interactions.Interaction;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.Commands;

import org.jetbrains.annotations.NotNull;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.text.TextInput;
import net.dv8tion.jda.api.interactions.components.text.TextInputStyle;
import net.dv8tion.jda.api.interactions.modals.Modal;
import net.dv8tion.jda.api.interactions.modals.ModalMapping;
import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand;
import xyz.dynxsty.dih4jda.interactions.components.ModalHandler;
import xyz.dynxsty.dih4jda.util.ComponentIdBuilder;

/**
* A simple command that can be used inside reserved help channels to
* immediately unreserve them, instead of waiting for a timeout.
*/
public class UnreserveCommand extends SlashCommand {
@AutoDetectableComponentHandler(UnreserveCommand.UNRESERVE_ID)
public class UnreserveCommand extends SlashCommand implements ModalHandler {
static final String UNRESERVE_ID = "unreserve";
private static final int MINIMUM_REASON_LENGTH = 11;
private static final String REASON_ID = "reason";
private final BotConfig botConfig;
private final DbActions dbActions;
private final HelpAccountRepository helpAccountRepository;
Expand All @@ -43,35 +61,80 @@ public UnreserveCommand(BotConfig botConfig, DbActions dbActions, HelpTransactio
this.helpAccountRepository = helpAccountRepository;
this.helpTransactionRepository = helpTransactionRepository;
this.preferenceService = preferenceService;
setCommandData(Commands.slash("unreserve", "Unreserves this post marking your question/issue as resolved.")
setCommandData(Commands.slash(UNRESERVE_ID, "Unreserves this post marking your question/issue as resolved.")
.setGuildOnly(true)
.addOption(OptionType.STRING, "reason", "The reason why you're unreserving this channel", false)
.addOption(OptionType.STRING, REASON_ID, "The reason why you're unreserving this channel", false)
);
}

@Override
public void execute(@NotNull SlashCommandInteractionEvent event) {
String reason = event.getOption(REASON_ID, null, OptionMapping::getAsString);
onCloseRequest(event, event, event.getChannel(), reason, ()->{
TextInput reasonInput = TextInput
.create(REASON_ID, "Reason", TextInputStyle.SHORT)
.setRequiredRange(MINIMUM_REASON_LENGTH, 100)
.setRequired(true)
.setPlaceholder(reason == null ? "Please enter the reason you are closing this post here" : reason)
.build();
Modal modal = Modal
.create(ComponentIdBuilder.build(UNRESERVE_ID), "Close post")
.addComponents(ActionRow.of(
reasonInput))
.build();
event.replyModal(modal).queue();
});
}

@Override
public void handleModal(ModalInteractionEvent event, List<ModalMapping> values) {
values
.stream()
.filter(mapping -> REASON_ID.equals(mapping.getId()))
.map(mapping -> mapping.getAsString())
.filter(reason -> !isReasonInvalid(reason))
.findAny()
.ifPresentOrElse(reason -> {
onCloseRequest(event, event, event.getChannel(), reason, ()->{
Responses.error(event, "The provided reason is missing or not valid").queue();
ExceptionLogger.capture(new IllegalStateException("A reason was expected but not present"), getClass().getName());
});
}, () -> Responses.warning(event, "A valid reason must be provided").queue());

}

private void onCloseRequest(Interaction interaction, IReplyCallback replyCallback, MessageChannelUnion channel, String reason, Runnable noReasonHandler) {
ChannelType channelType = channel.getType();
// check whether the channel type is either text or thread (possible forum post?)
if (event.getChannelType() != ChannelType.TEXT && event.getChannelType() != ChannelType.GUILD_PUBLIC_THREAD) {
replyInvalidChannel(event);
if (channelType != ChannelType.TEXT && channelType != ChannelType.GUILD_PUBLIC_THREAD) {
replyInvalidChannel(replyCallback);
return;
}
ThreadChannel postThread = event.getChannel().asThreadChannel();
ThreadChannel postThread = channel.asThreadChannel();
if (postThread.getParentChannel().getType() != ChannelType.FORUM) {
replyInvalidChannel(event);
replyInvalidChannel(replyCallback);
return;
}
HelpManager manager = new HelpManager(postThread, dbActions, botConfig, helpAccountRepository, helpTransactionRepository, preferenceService);
if (manager.isForumEligibleToBeUnreserved(event.getInteraction())) {
manager.close(event,
event.getUser().getIdLong() == manager.getPostThread().getOwnerIdLong(),
event.getOption("reason", null, OptionMapping::getAsString));
if (manager.isForumEligibleToBeUnreserved(interaction)) {
if (replyCallback.getUser().getIdLong() != postThread.getOwnerIdLong() && isReasonInvalid(reason)) {
noReasonHandler.run();
return;
}
manager.close(replyCallback,
replyCallback.getUser().getIdLong() == manager.getPostThread().getOwnerIdLong(),
reason);
} else {
Responses.warning(event, "Could not close this post", "You're not allowed to close this post.").queue();
Responses.warning(replyCallback, "Could not close this post", "You're not allowed to close this post.").queue();
}
}

private void replyInvalidChannel(CommandInteraction interaction) {
Responses.warning(interaction, "Invalid Channel",
private boolean isReasonInvalid(String reason) {
return reason == null || reason.length() < MINIMUM_REASON_LENGTH;
}

private void replyInvalidChannel(IReplyCallback replyCallback) {
Responses.warning(replyCallback, "Invalid Channel",
"This command may only be used in either the text-channel-based help system, or in our new forum help system.")
.queue();
}
Expand Down