Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
a114e30
Forms system
Defective4 Sep 7, 2025
bdf0b1a
Store form fields in a separate table
Defective4 Sep 17, 2025
b62574d
Use default values for all `NOT NULL` fields
Defective4 Sep 17, 2025
2c97172
Store form's message channel and id as longs instead of Strings
Defective4 Sep 17, 2025
7db911a
Changed submissions table structure to use unique ids
Defective4 Sep 17, 2025
e2c6372
Correct `FormsRepository` javadoc
Defective4 Sep 17, 2025
7cb72ed
Adjustments to the `FormsRepository` class
Defective4 Sep 17, 2025
61d7e36
Fix javadoc spelling mistakes
Defective4 Sep 17, 2025
21d9a42
Make the `FormField` into a record
Defective4 Sep 17, 2025
3425fa0
Convert `FormData` into a record and use `Instants` for expiration
Defective4 Sep 18, 2025
fdd328e
Form field javadoc
Defective4 Sep 20, 2025
123282b
Convert `FormUser` to a `record`
Defective4 Sep 20, 2025
7fccf8e
`FormCommand` javadoc
Defective4 Sep 20, 2025
385820d
Fix checkstyle
Defective4 Sep 20, 2025
2763a57
Merge branch 'Java-Discord:main' into forms-feature
Defective4 Mar 15, 2026
f6f2149
Migrate forms feature to new JDA and Java version
Defective4 Mar 15, 2026
71aa7fc
Fix indentation in forms-related classes
Defective4 Mar 15, 2026
6cc7b9e
Properly document form commands
Defective4 Mar 15, 2026
f13bae7
Add a staff role check to all form commands
Defective4 Mar 15, 2026
b421eb0
Rename `checkNotClosed` to `isOpen` in the form interaction manager
Defective4 Mar 15, 2026
85b8d9e
Add a user ID to form submission embeds
Defective4 Mar 15, 2026
2a78356
Replace codeblocks in user submissions
Defective4 Mar 15, 2026
7c2b156
Extract some of the common form logic to `FormSubcommand`
Defective4 Mar 15, 2026
598a95c
Update detach and attach command descriptions
Defective4 Mar 15, 2026
763af6d
Log the exception in form detach subcommand
Defective4 Mar 15, 2026
0741915
Remove useless timestamp field from form details embed
Defective4 Mar 15, 2026
be9b2c5
Improve form details subcommand
Defective4 Mar 15, 2026
8d35fc7
Change form modify subcommand javadoc
Defective4 Mar 15, 2026
1614da9
Allow for disabling form expiration time
Defective4 Mar 15, 2026
c5bdea8
Remove leftover `EXPIRATION_PERMANENT` field from `FormData`
Defective4 Mar 15, 2026
4cb7103
Disallow form deleting if it's attached to a message
Defective4 Mar 15, 2026
3b24656
Fix option to modify form expiration date
Defective4 Mar 15, 2026
135108f
Include info that field indexes are 0-indexed in form field remove
Defective4 Mar 15, 2026
3a5367d
Fix javadocs
Defective4 Mar 15, 2026
462138d
Fix message not being able to be detached if the og channel was deleted
Defective4 Mar 15, 2026
78624bb
Field removal subcommand improvements
Defective4 Mar 15, 2026
e4a7c0b
Use `filterChoices` in forms commands where possible
Defective4 Mar 16, 2026
05ebe4f
Update reopen command description
Defective4 Mar 16, 2026
af0c407
Fix checkstyle errors
Defective4 Mar 16, 2026
1e2e788
Update form export subcommand javadoc
Defective4 Mar 16, 2026
2ddea7d
Use `Map#forEach` in form export subcommand
Defective4 Mar 16, 2026
e2a54ab
Correct javadoc in forms repository
Defective4 Mar 16, 2026
fd0b13f
Change components array in `FormData` to an unmodifiable list
Defective4 Mar 16, 2026
f03aa15
Use optional for form attachment info
Defective4 Mar 16, 2026
037061d
Fix form submissions delete subcommand description
Defective4 Mar 16, 2026
cc20300
Add missing javadoc comments
Defective4 Mar 16, 2026
40728f5
Resolve TODO comments in forms feature classes
Defective4 Mar 16, 2026
f43f7df
Extract subcommand field ids to constants
Defective4 Mar 16, 2026
4ef2c89
Fix an issue with form fields not being added to the modal
Defective4 Mar 16, 2026
d03f90d
Improve form commands descriptions and add missing javadocs
Defective4 Mar 16, 2026
15dee0b
Make changes requested in the review
Defective4 Mar 16, 2026
ed3b3d3
Use `DateTimeFormatter` in forms
Defective4 Mar 16, 2026
1786843
Change time format in forms
Defective4 Mar 16, 2026
ac9e2ff
Change the button "mapping" method name
Defective4 Mar 16, 2026
90e8a37
Log failed form submission attempts
Defective4 Mar 16, 2026
00102a1
Log form submission errors
Defective4 Mar 16, 2026
23a38e8
Fix javadoc type in form interaction manager
Defective4 Mar 16, 2026
fa3c43e
Rename `createFormModal`
Defective4 Mar 16, 2026
0a0c877
Fix checkstyle errors
Defective4 Mar 16, 2026
694c2b9
Use `Responses#error` where possible in forms commands
Defective4 Mar 17, 2026
4dc6fe3
Remove "created at" field from form details embed
Defective4 Mar 17, 2026
7d657ce
Merge branch 'Java-Discord:main' into forms-feature
Defective4 Mar 17, 2026
b7c6552
Address the remaining issues
Defective4 Mar 17, 2026
d8304fd
Merge branch 'forms-feature' of https://github.com/Defective4/JavaBot…
Defective4 Mar 17, 2026
0556dda
Disallow attaching forms to messages not sent by the bot
Defective4 Mar 17, 2026
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 @@ -76,9 +76,9 @@ public class FormInteractionManager implements ButtonHandler, ModalHandler {
public void closeForm(Guild guild, FormData form) {
formsRepo.closeForm(form);

if (form.getMessageChannel() != null && form.getMessageId() != null) {
TextChannel formChannel = guild.getTextChannelById(form.getMessageChannel());
formChannel.retrieveMessageById(form.getMessageId()).queue(msg -> {
if (form.isAttached()) {
TextChannel formChannel = guild.getTextChannelById(form.getMessageChannel().get());
formChannel.retrieveMessageById(form.getMessageId().get()).queue(msg -> {
mapFormMessageButtons(msg, btn -> {
String cptId = btn.getId();
String[] split = ComponentIdBuilder.split(cptId);
Expand Down Expand Up @@ -149,8 +149,9 @@ public void handleModal(ModalInteractionEvent event, List<ModalMapping> values)
return;
}

channel.sendMessageEmbeds(createSubmissionEmbed(form, values, event.getMember())).queue();
formsRepo.logSubmission(event.getUser(), form);
channel.sendMessageEmbeds(createSubmissionEmbed(form, values, event.getMember())).queue(msg -> {
formsRepo.addSubmission(event.getUser(), form, msg);
});

event.getHook()
Comment thread
danthe1st marked this conversation as resolved.
Outdated
.sendMessage(
Expand Down Expand Up @@ -190,9 +191,9 @@ public void mapFormMessageButtons(Message msg, Function<Button, Button> mapper)
public void reopenForm(Guild guild, FormData form) {
formsRepo.reopenForm(form);

if (form.getMessageChannel() != null && form.getMessageId() != null) {
TextChannel formChannel = guild.getTextChannelById(form.getMessageChannel());
formChannel.retrieveMessageById(form.getMessageId()).queue(msg -> {
if (form.isAttached()) {
TextChannel formChannel = guild.getTextChannelById(form.getMessageChannel().get());
formChannel.retrieveMessageById(form.getMessageId().get()).queue(msg -> {
mapFormMessageButtons(msg, btn -> {
String cptId = btn.getId();
String[] split = ComponentIdBuilder.split(cptId);
Expand Down Expand Up @@ -267,7 +268,7 @@ private static MessageEmbed createSubmissionEmbed(FormData form, List<ModalMappi
ModalMapping mapping = values.get(i);
FormField field = form.getFields().get(i);
String value = mapping.getAsString();
Comment thread
danthe1st marked this conversation as resolved.
builder.addField(field.getLabel(), value == null ? "*Empty*" : "```\n" + value + "\n```", false);
builder.addField(field.label(), value == null ? "*Empty*" : "```\n" + value + "\n```", false);
}

return builder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ public AddFieldFormSubcommand(FormsRepository formsRepo) {
.addOption(OptionType.BOOLEAN, "required",
"Whether or not the user has to input data in this field. Default: false")
.addOption(OptionType.STRING, "style", "Input style. Default: SHORT", false, true)
Comment thread
danthe1st marked this conversation as resolved.
Outdated
.addOption(OptionType.STRING, "value", "Initial field value")
.addOption(OptionType.INTEGER, "index", "Index to insert the field at"));
.addOption(OptionType.STRING, "value", "Initial field value"));
}

@Override
Expand All @@ -60,13 +59,7 @@ public void execute(SlashCommandInteractionEvent event) {
return;
}

int index = event.getOption("index", -1, OptionMapping::getAsInt);
if (index < -1 || index >= form.getFields().size()) {
event.getHook().sendMessage("Field index out of bounds").queue();
return;
}

formsRepo.addField(form, createFormFieldFromEvent(event), index);
formsRepo.addField(form, createFormFieldFromEvent(event));
event.getHook().sendMessage("Added a new field to the form.").queue();
}

Expand Down Expand Up @@ -98,6 +91,6 @@ private static FormField createFormFieldFromEvent(SlashCommandInteractionEvent e
});
String value = e.getOption("value", OptionMapping::getAsString);

return new FormField(label, max, min, placeholder, required, style.name(), value);
return new FormField(label, max, min, placeholder, required, style, value, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void execute(SlashCommandInteractionEvent event) {
}
FormData form = formOpt.get();

if (form.getMessageChannel() != null && form.getMessageId() != null) {
if (form.isAttached()) {
event.getHook()
.sendMessage("The form seems to already be attached to a message. Detach it before continuing.")
.queue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void execute(SlashCommandInteractionEvent event) {

long formId = System.currentTimeMillis();
FormData form = new FormData(formId, List.of(), event.getOption("title", OptionMapping::getAsString),
event.getOption("submit-channel", OptionMapping::getAsChannel).getId(),
event.getOption("submit-channel", OptionMapping::getAsChannel).getIdLong(),
event.getOption("submit-message", null, OptionMapping::getAsString), null, null, expiration, false,
event.getOption("onetime", false, OptionMapping::getAsBoolean));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ public void execute(SlashCommandInteractionEvent event) {
FormData form = formOpt.get();
formsRepo.deleteForm(form);

if (form.getMessageChannel() != null && form.getMessageId() != null) {
if (form.isAttached()) {
DetachFormSubcommand.detachFromMessage(form, event.getGuild());
Comment thread
danthe1st marked this conversation as resolved.
Outdated
// TODO send a warning
Comment thread
danthe1st marked this conversation as resolved.
Outdated
}

event.getHook().sendMessage("Form deleted!").queue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void execute(SlashCommandInteractionEvent event) {
}
FormData form = formOpt.get();

if (form.getMessageChannel() == null && form.getMessageId() == null) {
if (!form.isAttached()) {
event.getHook().sendMessage("This form doesn't seem to be attached to a message").queue();
return;
}
Expand All @@ -80,8 +80,9 @@ public void handleAutoComplete(CommandAutoCompleteInteractionEvent event, AutoCo
* @param guild guild this form is contained in
*/
public static void detachFromMessage(FormData form, Guild guild) {
TextChannel formChannel = guild.getTextChannelById(form.getMessageChannel());
formChannel.retrieveMessageById(form.getMessageId()).queue(msg -> {
if(!form.isAttached()) return;
TextChannel formChannel = guild.getTextChannelById(form.getMessageChannel().get());
formChannel.retrieveMessageById(form.getMessageId().get()).queue(msg -> {
List<ActionRow> components = msg.getActionRows().stream().map(row -> {
ItemComponent[] cpts = row.getComponents().stream().filter(cpt -> {
Comment thread
danthe1st marked this conversation as resolved.
Outdated
if (cpt instanceof Button btn) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,10 @@ private EmbedBuilder createFormDetailsEmbed(FormData form, Guild guild) {
addCodeblockField(builder, "State", form.isClosed() ? "Closed" : form.hasExpired() ? "Expired" : "Open", false);

builder.addField("Attached in",
form.getMessageChannel() == null ? "*Not attached*" : "<#" + form.getMessageChannel() + ">", true);
form.isAttached() ? "<#" + form.getMessageChannel().get() + ">" : "*Not attached*", true);
builder.addField("Attached to",
form.getMessageChannel() == null || form.getMessageId() == null ? "*Not attached*"
: String.format("[Link](https://discord.com/channels/%s/%s/%s)", guild.getId(),
form.getMessageChannel(), form.getMessageId()),
form.isAttached() ? String.format("[Link](https://discord.com/channels/%s/%s/%s)", guild.getId(),
form.getMessageChannel().get(), form.getMessageId().get()) : "*Not attached*",
true);

builder.addField("Submissions channel", "<#" + form.getSubmitChannel() + ">", true);
Comment thread
danthe1st marked this conversation as resolved.
Outdated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ public void execute(SlashCommandInteractionEvent event) {
FormData oldForm = formOpt.get();

String title = event.getOption("title", oldForm.getTitle(), OptionMapping::getAsString);
String submitChannel = event.getOption("submit-channel", oldForm.getSubmitChannel(),
OptionMapping::getAsString);
long submitChannel = event.getOption("submit-channel", oldForm.getSubmitChannel(), OptionMapping::getAsLong);
String submitMessage = event.getOption("submit-message", oldForm.getSubmitMessage(),
OptionMapping::getAsString);
long expiration;
Expand All @@ -72,7 +71,8 @@ public void execute(SlashCommandInteractionEvent event) {
boolean onetime = event.getOption("onetime", oldForm.isOnetime(), OptionMapping::getAsBoolean);

FormData newForm = new FormData(oldForm.getId(), oldForm.getFields(), title, submitChannel, submitMessage,
oldForm.getMessageId(), oldForm.getMessageChannel(), expiration, oldForm.isClosed(), onetime);
oldForm.getMessageId().orElse(null), oldForm.getMessageChannel().orElse(null), expiration,
oldForm.isClosed(), onetime);

formsRepo.updateForm(newForm);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public void execute(SlashCommandInteractionEvent event) {
return;
}

if (form.getMessageChannel() != null && form.getMessageId() != null && form.getFields().size() <= 1) {
if (form.isAttached() && form.getFields().size() <= 1) {
event.getHook().sendMessage(
"Can't remove the last field from an attached form. Detach the form before removing the field")
.queue();
Expand All @@ -61,7 +61,7 @@ public void execute(SlashCommandInteractionEvent event) {

formsRepo.removeField(form, index);

event.getHook().sendMessage("Removed field `" + form.getFields().get(index).getLabel() + "` from the form.")
event.getHook().sendMessage("Removed field `" + form.getFields().get(index).label() + "` from the form.")
.queue();
}

Expand All @@ -79,7 +79,7 @@ public void handleAutoComplete(CommandAutoCompleteInteractionEvent event, AutoCo
List<Choice> choices = new ArrayList<>();
List<FormField> fields = form.get().getFields();
for (int i = 0; i < fields.size(); i++) {
choices.add(new Choice(fields.get(i).getLabel(), i));
choices.add(new Choice(fields.get(i).label(), i));
}
event.replyChoices(choices).queue();
Comment thread
danthe1st marked this conversation as resolved.
Outdated
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import net.discordjug.javabot.systems.staff_commands.forms.dao.FormsRepository;
import net.discordjug.javabot.systems.staff_commands.forms.model.FormData;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.AutoCompleteQuery;
Expand All @@ -29,11 +30,9 @@ public class SubmissionsDeleteFormSubcommand extends Subcommand implements AutoC
*/
public SubmissionsDeleteFormSubcommand(FormsRepository formsRepo) {
this.formsRepo = formsRepo;
setCommandData(
new SubcommandData("submissions-delete", "Deletes submissions of an user in the form").addOptions(
new OptionData(OptionType.INTEGER, "form-id", "The ID of a form to get submissions for", true,
true),
new OptionData(OptionType.STRING, "user-id", "User to delete submissions of", true, true)));
setCommandData(new SubcommandData("submissions-delete", "Deletes submissions of a user in the form")
.addOptions(new OptionData(OptionType.INTEGER, "form-id", "The ID of a form to get submissions for",
Comment thread
danthe1st marked this conversation as resolved.
Outdated
true, true), new OptionData(OptionType.USER, "user", "User to delete submissions of", true)));
}

@Override
Expand All @@ -45,7 +44,7 @@ public void execute(SlashCommandInteractionEvent event) {
return;
}

String user = event.getOption("user-id", OptionMapping::getAsString);
User user = event.getOption("user", OptionMapping::getAsUser);
FormData form = formOpt.get();

int count = formsRepo.deleteSubmissions(form, user);
Expand All @@ -54,24 +53,8 @@ public void execute(SlashCommandInteractionEvent event) {

@Override
public void handleAutoComplete(CommandAutoCompleteInteractionEvent event, AutoCompleteQuery target) {
switch (target.getName()) {
case "user-id" -> {
Long formId = event.getOption("form-id", OptionMapping::getAsLong);
if (formId != null) {
Optional<FormData> form = formsRepo.getForm(formId);
if (form.isPresent()) {
event.replyChoices(formsRepo.getAllSubmissions(form.get()).keySet().stream()
.map(user -> new Choice(user.getUsername(), Long.toString(user.getId()))).toList())
.queue();
return;
}
}
event.replyChoices().queue();
}
case "form-id" -> event.replyChoices(
formsRepo.getAllForms().stream().map(form -> new Choice(form.toString(), form.getId())).toList())
.queue();
default -> {}
}
event.replyChoices(
formsRepo.getAllForms().stream().map(form -> new Choice(form.toString(), form.getId())).toList())
.queue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public void execute(SlashCommandInteractionEvent event) {
}

FormData form = formOpt.get();
Map<FormUser, Integer> submissions = formsRepo.getAllSubmissions(form);
Map<FormUser, Integer> submissions = formsRepo.getSubmissionsCountPerUser(form);
JsonObject root = new JsonObject();
JsonObject details = new JsonObject();
JsonArray users = new JsonArray();
Expand Down
Loading
Loading