Skip to content

Commit d2f25e6

Browse files
committed
add javadocs, and fixed requested changes.
1 parent 7679c4b commit d2f25e6

6 files changed

Lines changed: 179 additions & 181 deletions

File tree

src/main/java/net/discordjug/javabot/systems/user_commands/format_code/Code.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,34 @@ public class Code {
1818
private Language language;
1919
private final String content;
2020

21+
/**
22+
* @param language the language the code is written in, used for syntax highlighting
23+
* @param content the raw, already-sanitized code to format
24+
*/
2125
public Code(Language language, String content) {
2226
this.language = language;
2327
this.content = content;
2428
}
2529

30+
/**
31+
* @return the raw code content
32+
*/
2633
public String getContent() {
2734
return content;
2835
}
2936

37+
/**
38+
* @return the {@link Language} this code is formatted as
39+
*/
3040
public Language getLanguage() {
3141
return language;
3242
}
3343

34-
public void setLanguage(Language language) {
35-
this.language = language;
36-
}
37-
3844
/**
3945
* Splits {@link #content} into pieces that each fit within {@link #MAX_SIZE},
4046
* breaking on newlines where possible so lines are not cut in half.
4147
*/
42-
public List<String> toDiscordChunks() {
48+
private List<String> toDiscordChunks() {
4349
List<String> chunks = new ArrayList<>();
4450
String remaining = content;
4551

@@ -59,7 +65,12 @@ public List<String> toDiscordChunks() {
5965
return chunks;
6066
}
6167

62-
/** Wraps each chunk in a language-tagged Discord code block. */
68+
/**
69+
* Splits the content into chunks that each fit within Discord's character limit and wraps
70+
* every chunk in a language-tagged code block.
71+
*
72+
* @return the formatted code-block messages, one per Discord message
73+
*/
6374
public List<String> toDiscordMessages() {
6475
return toDiscordChunks()
6576
.stream()
Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,13 @@
11
package net.discordjug.javabot.systems.user_commands.format_code;
22

3-
import net.discordjug.javabot.util.ExceptionLogger;
43
import net.discordjug.javabot.util.IndentationHelper;
5-
import net.discordjug.javabot.util.Responses;
64
import net.discordjug.javabot.util.StringUtils;
7-
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
85
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
96
import net.dv8tion.jda.api.interactions.InteractionContextType;
107
import net.dv8tion.jda.api.interactions.commands.build.Commands;
11-
import net.dv8tion.jda.api.utils.FileUpload;
128
import org.jetbrains.annotations.NotNull;
139
import xyz.dynxsty.dih4jda.interactions.commands.application.ContextCommand;
1410

15-
import javax.annotation.Nonnull;
16-
import java.nio.charset.StandardCharsets;
17-
import java.util.List;
1811

1912
/**
2013
* <h3>This class represents the "Format and Indent Code" Message Context command.</h3>
@@ -35,44 +28,8 @@ public void execute(@NotNull MessageContextInteractionEvent event) {
3528
StringUtils.standardSanitizer().compute(event.getTarget().getContentRaw()),
3629
IndentationHelper.IndentationType.TABS);
3730

38-
if (indented.isBlank()) {
39-
event.reply("There is no code to format in that message.").setEphemeral(true).queue();
40-
return;
41-
}
42-
4331
Code code = new Code(Language.JAVA, indented);
44-
List<String> messages = code.toDiscordMessages();
45-
46-
// Reply with the full code as a file (acknowledges the interaction), then post
47-
// the readable code-block chunks in order.
48-
FileUpload file = FileUpload.fromData(indented.getBytes(StandardCharsets.UTF_8),
49-
"code." + code.getLanguage().getDiscordName());
50-
MessageChannel channel = event.getChannel();
51-
event.replyFiles(file)
52-
.setAllowedMentions(List.of())
53-
.queue(
54-
success -> sendChunksInOrder(channel, messages, 0, event),
55-
error -> {
56-
ExceptionLogger.capture(error, getClass().getSimpleName());
57-
Responses.error(event.getHook(), "The message could not be converted into a formatted code block.")
58-
.queue();
59-
}
60-
);
61-
}
6232

63-
private void sendChunksInOrder(MessageChannel channel, List<String> messages, int index, @Nonnull MessageContextInteractionEvent event) {
64-
if (index >= messages.size()) {
65-
return;
66-
}
67-
channel.sendMessage(messages.get(index))
68-
.setAllowedMentions(List.of())
69-
.queue(
70-
success -> sendChunksInOrder(channel, messages, index + 1, event),
71-
error -> {
72-
ExceptionLogger.capture(error, getClass().getSimpleName());
73-
Responses.error(event.getHook(), "The message could not be converted into a formatted code block.")
74-
.queue();
75-
}
76-
);
33+
FormatCodeDispatcher.sendCode(code, event, event.getTarget());
7734
}
7835
}
Lines changed: 32 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package net.discordjug.javabot.systems.user_commands.format_code;
22

3-
import net.dv8tion.jda.api.interactions.InteractionHook;
43
import xyz.dynxsty.dih4jda.interactions.commands.application.SlashCommand;
54
import net.discordjug.javabot.util.*;
65
import net.dv8tion.jda.api.components.actionrow.ActionRow;
@@ -16,7 +15,6 @@
1615
import org.jetbrains.annotations.NotNull;
1716

1817
import java.util.Collections;
19-
import java.util.List;
2018

2119
/**
2220
* <h3>This class represents the /format-code command.</h3>
@@ -30,25 +28,7 @@ public FormatCodeCommand() {
3028
.setContexts(InteractionContextType.GUILD)
3129
.addOptions(
3230
new OptionData(OptionType.STRING, "message-id", "Message to be formatted, last message used if left blank.", false),
33-
new OptionData(OptionType.STRING, "format", "The language used to format the code, defaults to Java if left blank.", false)
34-
.addChoice("C", "c")
35-
.addChoice("C#", "csharp")
36-
.addChoice("C++", "cpp")
37-
.addChoice("CSS", "css")
38-
.addChoice("D", "d")
39-
.addChoice("Go", "go")
40-
.addChoice("HTML", "html")
41-
.addChoice("Java", "java")
42-
.addChoice("JavaScript", "js")
43-
.addChoice("Kotlin", "kotlin")
44-
.addChoice("PHP", "php")
45-
.addChoice("Python", "python")
46-
.addChoice("Ruby", "ruby")
47-
.addChoice("Rust", "rust")
48-
.addChoice("SQL", "sql")
49-
.addChoice("Swift", "swift")
50-
.addChoice("TypeScript", "typescript")
51-
.addChoice("XML", "xml"),
31+
formatOption(),
5232
new OptionData(OptionType.STRING,"auto-indent","The type of indentation applied to the message, does not automatically indent if left blank.",false)
5333
.addChoice("Four Spaces","FOUR_SPACES")
5434
.addChoice("Two Spaces","TWO_SPACES")
@@ -57,6 +37,29 @@ public FormatCodeCommand() {
5737
);
5838
}
5939

40+
/**
41+
* Builds the {@code format} option, generating one choice per {@link Language} (excluding
42+
* {@link Language#UNKNOWN}) so the enum stays the single source of truth for the language list.
43+
*
44+
* @return the configured {@code format} option
45+
*/
46+
private static OptionData formatOption() {
47+
OptionData option = new OptionData(OptionType.STRING, "format", "The language used to format the code, defaults to Java if left blank.", false);
48+
for (Language language : Language.values()) {
49+
if (language != Language.UNKNOWN) { // UNKNOWN is the fallback, not a real choice
50+
option.addChoice(language.getDisplayName(), language.name()); // value = enum name so valueOf() reverses it
51+
}
52+
}
53+
return option;
54+
}
55+
56+
/**
57+
* Builds the action row placed on the file-upload message: a delete button and a "View Original" link.
58+
*
59+
* @param target the original message linked by the "View Original" button
60+
* @param requesterId the id of the user permitted to delete the message
61+
* @return an action row containing the delete and "View Original" buttons
62+
*/
6063
@Contract("_ -> new")
6164
static @NotNull ActionRow buildActionRow(@NotNull Message target, long requesterId) {
6265
return ActionRow.of(InteractionUtils.createDeleteButton(requesterId),
@@ -66,9 +69,9 @@ public FormatCodeCommand() {
6669
@Override
6770
public void execute(@NotNull SlashCommandInteractionEvent event) {
6871
OptionMapping idOption = event.getOption("message-id");
69-
String format = event.getOption("format", "java", OptionMapping::getAsString);
72+
Language language = event.getOption("format", Language.JAVA, o -> Language.fromString(o.getAsString()));
7073
String indentation = event.getOption("auto-indent","NULL",OptionMapping::getAsString);
71-
event.deferReply().queue();
74+
7275
if (idOption == null) {
7376
event.getChannel().getHistory()
7477
.retrievePast(10)
@@ -78,7 +81,7 @@ public void execute(@NotNull SlashCommandInteractionEvent event) {
7881
.filter(m -> !m.getAuthor().isBot()).findFirst()
7982
.orElse(null);
8083
if (target != null) {
81-
sendFormattedCode(event, target, format, indentation);
84+
sendFormattedCode(event, target, language, indentation);
8285
} else {
8386
Responses.error(event.getHook(), "Could not find message; please specify a message id.").queue();
8487
}
@@ -90,38 +93,18 @@ public void execute(@NotNull SlashCommandInteractionEvent event) {
9093
}
9194
long messageId = idOption.getAsLong();
9295
event.getChannel().retrieveMessageById(messageId).queue(
93-
target -> sendFormattedCode(event, target, format, indentation),
96+
target -> sendFormattedCode(event, target, language, indentation),
9497
e -> Responses.error(event.getHook(), "Could not retrieve message with id: " + messageId).queue());
9598
}
9699
}
97100

98-
private void sendFormattedCode(SlashCommandInteractionEvent event, Message target, String format, String indentation) {
101+
private void sendFormattedCode(SlashCommandInteractionEvent event, Message target, Language language, String indentation) {
99102
String content = IndentationHelper.formatIndentation(
100103
StringUtils.standardSanitizer().compute(target.getContentRaw()),
101104
IndentationHelper.IndentationType.valueOf(indentation));
102105

103-
if (content.isBlank()) {
104-
Responses.error(event.getHook(), "There is no code to format in that message.").queue();
105-
return;
106-
}
107-
108-
Code code = new Code(Language.fromString(format), content);
109-
sendChunksInOrder(event.getHook(), code.toDiscordMessages(), 0);
110-
}
106+
Code code = new Code(language,content);
111107

112-
private void sendChunksInOrder(InteractionHook hook, List<String> messages, int index) {
113-
if (index >= messages.size()) {
114-
return;
115-
}
116-
var action = hook.sendMessage(messages.get(index)).setAllowedMentions(List.of());
117-
118-
action.queue(
119-
success -> sendChunksInOrder(hook, messages, index + 1),
120-
error -> {
121-
ExceptionLogger.capture(error, getClass().getSimpleName());
122-
Responses.error(hook, "The message could not be converted into a formatted code block.")
123-
.queue();
124-
}
125-
);
108+
FormatCodeDispatcher.sendCode(code, event, target);
126109
}
127-
}
110+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package net.discordjug.javabot.systems.user_commands.format_code;
2+
3+
import net.discordjug.javabot.util.*;
4+
import net.dv8tion.jda.api.components.actionrow.ActionRow;
5+
import net.dv8tion.jda.api.components.buttons.Button;
6+
import net.dv8tion.jda.api.entities.Message;
7+
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
8+
import net.dv8tion.jda.api.interactions.commands.CommandInteraction;
9+
import net.dv8tion.jda.api.utils.FileUpload;
10+
import org.jetbrains.annotations.Contract;
11+
import org.jetbrains.annotations.NotNull;
12+
13+
import javax.annotation.Nonnull;
14+
import java.nio.charset.StandardCharsets;
15+
import java.util.List;
16+
17+
/**
18+
* Shared sending logic for the code-formatting commands. Replies with the full code as a
19+
* downloadable file, then posts it as one or more ordered code-block messages that each respect
20+
* Discord's 2000-character limit.
21+
*/
22+
public class FormatCodeDispatcher {
23+
24+
/**
25+
* Acknowledges the interaction by replying with the full code as a file, then posts the code as
26+
* ordered code-block messages. Replies with an error instead if there is nothing to format.
27+
*
28+
* @param code the code to send
29+
* @param event the interaction to reply to
30+
* @param target the original message the code came from, used for the channel and the
31+
* "View Original" / delete buttons
32+
*/
33+
public static void sendCode(Code code, @Nonnull CommandInteraction event, Message target){
34+
if (code.getContent().isBlank()) {
35+
Responses.error(event.getHook(), "There is no code to format in that message.").queue();
36+
return;
37+
}
38+
// Currently we always format as Java. A language dropdown will be added in the future.
39+
List<String> messages = code.toDiscordMessages();
40+
41+
// The reply both acknowledges the interaction and hands users the full,
42+
// un-split code as a downloadable file (so chunking never loses anything).
43+
FileUpload file = FileUpload.fromData(
44+
code.getContent().getBytes(StandardCharsets.UTF_8),
45+
"code." + code.getLanguage().getDiscordName()
46+
);
47+
48+
MessageChannel channel = target.getChannel();
49+
50+
event.replyFiles(file)
51+
.setAllowedMentions(List.of())
52+
.setComponents(FormatCodeCommand.buildActionRow(target, event.getUser().getIdLong()))
53+
.queue(success -> sendChunksInOrder(channel, messages, 0, target,event));
54+
}
55+
56+
57+
private static void sendChunksInOrder(MessageChannel channel, List<String> messages, int index, Message target, @Nonnull CommandInteraction event) {
58+
if (index >= messages.size()) {
59+
return;
60+
}
61+
var action = channel.sendMessage(messages.get(index))
62+
.setAllowedMentions(List.of());
63+
64+
if (index == messages.size() - 1) {
65+
action.setComponents(buildActionRow(target, event.getUser().getIdLong()));
66+
}
67+
68+
action.queue(success ->
69+
sendChunksInOrder(channel, messages, index + 1, target, event));
70+
}
71+
72+
/**
73+
* Builds the action row placed on the last code-block message.
74+
*
75+
* @param target the original message linked by the "View Original" button
76+
* @param requesterId the id of the requesting user
77+
* @return an action row containing the "View Original" link button
78+
*/
79+
@Contract("_ -> new")
80+
static @NotNull ActionRow buildActionRow(@NotNull Message target, long requesterId) {
81+
return ActionRow.of(Button.link(target.getJumpUrl(), "View Original"));
82+
}
83+
}

0 commit comments

Comments
 (0)