Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transfer Feature reopen #905

Merged
merged 23 commits into from
Oct 20, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public static Collection<Feature> createFeatures(JDA jda, Database database, Con
features.add(new HelpThreadCreatedListener(helpSystemHelper));

// Message context commands
features.add(new TransferQuestionCommand(config));

// User context commands

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package org.togetherjava.tjbot.features.moderation;

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
import net.dv8tion.jda.api.entities.channel.forums.ForumPost;
import net.dv8tion.jda.api.entities.channel.forums.ForumTag;
import net.dv8tion.jda.api.entities.channel.forums.ForumTagSnowflake;
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.MessageContextInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.interactions.components.Modal;
import net.dv8tion.jda.api.interactions.components.text.TextInput;
import net.dv8tion.jda.api.interactions.components.text.TextInputStyle;
import net.dv8tion.jda.api.requests.RestAction;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.togetherjava.tjbot.config.Config;
import org.togetherjava.tjbot.features.BotCommandAdapter;
import org.togetherjava.tjbot.features.CommandVisibility;
import org.togetherjava.tjbot.features.MessageContextCommand;
import org.togetherjava.tjbot.features.utils.StringDistances;

import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Pattern;

public final class TransferQuestionCommand extends BotCommandAdapter
implements MessageContextCommand {
private static final Logger logger = LoggerFactory.getLogger(TransferQuestionCommand.class);
private static final String COMMAND_NAME = "transfer-question";
private static final String TRANSFER_QUESTION_TITLE_ID = "transferID";
private static final String TRANSFER_QUESTION_INPUT_ID = "transferQuestion";
private static final String TRANSFER_QUESTION_TAG = "tags";
private static final int TITLE_MAX_LENGTH = 50;
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
private static final Pattern TITLE_COMPACT_REMOVAL_PATTERN = Pattern.compile("\\W");
private static final int TITLE_COMPACT_LENGTH_MIN = 2;
private static final int TITLE_COMPACT_LENGTH_MAX = 30;
private final Predicate<String> isHelpForumName;

private final List<String> defaultTags;
Taz03 marked this conversation as resolved.
Show resolved Hide resolved


/**
* Creates a new instance.
*/
public TransferQuestionCommand(Config config) {
Taz03 marked this conversation as resolved.
Show resolved Hide resolved
super(Commands.message(COMMAND_NAME), CommandVisibility.GUILD);

isHelpForumName =
Pattern.compile(config.getHelpSystem().getHelpForumPattern()).asMatchPredicate();

defaultTags = config.getHelpSystem().getCategories();
}

@Override
public void onMessageContext(MessageContextInteractionEvent event) {
String originalMessage = event.getTarget().getContentRaw();
String originalMessageId = event.getTarget().getId();
String originalChannelId = event.getChannel().getId();
String authorId = event.getTarget().getAuthor().getId();

TextInput transferQuestionTitle =
TextInput.create(TRANSFER_QUESTION_TITLE_ID, "Title", TextInputStyle.SHORT)
.setMaxLength(70)
.setMinLength(8)
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
.setValue(createTitle(originalMessage))
.build();

TextInput transferQuestionInput = TextInput
.create(TRANSFER_QUESTION_INPUT_ID, "Transfer question menu", TextInputStyle.PARAGRAPH)
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
.setValue(originalMessage)
.setRequiredRange(3, 2000)
.build();

TextInput transferQuestionTag = TextInput
.create(TRANSFER_QUESTION_TAG, "Transfer question tags", TextInputStyle.SHORT)
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
.setValue("Java")
Taz03 marked this conversation as resolved.
Show resolved Hide resolved
.build();

String transferQuestionModalComponentID =
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
generateComponentId(authorId, originalMessageId, originalChannelId);
Modal transferModal =
Modal.create(transferQuestionModalComponentID, "transfer question menu")
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
.addActionRow(transferQuestionTitle)
.addActionRow(transferQuestionInput)
.addActionRow(transferQuestionTag)
.build();

event.replyModal(transferModal)
.queue(success -> logger.info(
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
"{} with id: {} triggered the transfer action on message with id: {}",
event.getUser().getName(), event.getUser().getId(), originalMessageId),
failed -> {
});
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
}

@Override
public void onModalSubmitted(ModalInteractionEvent event, List<String> args) {
event.deferReply().queue();

event.getJDA()
.retrieveUserById(args.get(0))
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
.flatMap(fetchedUser -> createForumPost(event, fetchedUser))
.flatMap(user -> dmUser(event.getChannel(), user, event.getGuild()))
.flatMap(dmSent -> deleteOriginalMessage(event.getJDA(), args.get(2), args.get(1)))
.flatMap(deletedOriginalMessage -> event.getHook().sendMessage("Question Transferred"))
.queue();
}

private static String createTitle(String message) {
if (message.length() >= TITLE_MAX_LENGTH) {
int lastWordEnd = message.lastIndexOf(' ', TITLE_MAX_LENGTH);

if (lastWordEnd == -1) {
lastWordEnd = TITLE_MAX_LENGTH;
}

message = message.substring(0, lastWordEnd);
}

return isTitleValid(message) ? message : "Untitled";
}

private static boolean isTitleValid(CharSequence title) {
String titleCompact = TITLE_COMPACT_REMOVAL_PATTERN.matcher(title).replaceAll("");

return titleCompact.length() >= TITLE_COMPACT_LENGTH_MIN
&& titleCompact.length() <= TITLE_COMPACT_LENGTH_MAX;
}

private RestAction<User> createForumPost(ModalInteractionEvent event, User originalUser) {
MessageCreateData forumMessage = MessageCreateData
.fromContent(event.getValue(TRANSFER_QUESTION_INPUT_ID).getAsString());
String forumTitle = event.getValue(TRANSFER_QUESTION_TITLE_ID).getAsString();
ForumChannel questionsForum = getHelperForum(event.getJDA());

String transferQuestionTag = event.getValue(TRANSFER_QUESTION_TAG).getAsString();

String queryTag = StringDistances.closestMatch(transferQuestionTag, defaultTags)
.orElse(defaultTags.get(0));

ForumTag defaultTag = getDefaultTagOr(questionsForum.getAvailableTagsByName(queryTag, true),
Taz03 marked this conversation as resolved.
Show resolved Hide resolved
() -> questionsForum.getAvailableTagsByName(defaultTags.get(0), true).get(0));
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved

return questionsForum.createForumPost(forumTitle, forumMessage)
.setTags(ForumTagSnowflake.fromId(defaultTag.getId()))
.map(ForumPost::getMessage)
.flatMap(message -> message.reply("Original Post from " + originalUser.getAsMention()))
.map(sent -> originalUser);
Taz03 marked this conversation as resolved.
Show resolved Hide resolved
}

private RestAction<Message> dmUser(MessageChannelUnion sourceChannel, User originalUser,
Guild guild) {

return originalUser.openPrivateChannel()
.flatMap(channel -> channel.sendMessage(
"Hi, TJ here. You are getting this message because you tried to ask a question in wrong chat, please read rules - %s"
.formatted(guild.getName())))
.onErrorFlatMap(error -> sourceChannel.sendMessage(
"Due to failed dm interaction, you are getting this message here. Please read rules about asking questions -%s%s"
.formatted(guild.getName(), originalUser.getAsMention())));

}

private RestAction<Void> deleteOriginalMessage(JDA jda, String channelId, String messageId) {
return jda.getTextChannelById(channelId).deleteMessageById(messageId);
}

private ForumChannel getHelperForum(JDA jda) {
Optional<ForumChannel> forumChannelOptional = jda.getForumChannels()
.stream()
.filter(forumChannel -> isHelpForumName.test(forumChannel.getName()))
.findFirst();

return forumChannelOptional
.orElseThrow(() -> new RuntimeException("Helper Forum Not found"));
Zabuzard marked this conversation as resolved.
Show resolved Hide resolved
}

private static ForumTag getDefaultTagOr(List<ForumTag> tagsFoundOnForum,
Supplier<ForumTag> defaultTag) {
return tagsFoundOnForum.isEmpty() ? defaultTag.get() : tagsFoundOnForum.get(0);
}
}
Loading