|
| 1 | +package com.diamondfire.helpbot.sys.report; |
| 2 | + |
| 3 | +import club.minnced.discord.webhook.WebhookClient; |
| 4 | +import club.minnced.discord.webhook.external.JDAWebhookClient; |
| 5 | +import club.minnced.discord.webhook.send.*; |
| 6 | +import com.diamondfire.helpbot.bot.HelpBotInstance; |
| 7 | +import com.diamondfire.helpbot.util.*; |
| 8 | +import net.dv8tion.jda.api.EmbedBuilder; |
| 9 | +import net.dv8tion.jda.api.components.actionrow.ActionRow; |
| 10 | +import net.dv8tion.jda.api.components.attachmentupload.AttachmentUpload; |
| 11 | +import net.dv8tion.jda.api.components.buttons.Button; |
| 12 | +import net.dv8tion.jda.api.components.label.Label; |
| 13 | +import net.dv8tion.jda.api.components.textdisplay.TextDisplay; |
| 14 | +import net.dv8tion.jda.api.components.textinput.*; |
| 15 | +import net.dv8tion.jda.api.entities.*; |
| 16 | +import net.dv8tion.jda.api.entities.emoji.Emoji; |
| 17 | +import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent; |
| 18 | +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; |
| 19 | +import net.dv8tion.jda.api.hooks.ListenerAdapter; |
| 20 | +import net.dv8tion.jda.api.interactions.modals.*; |
| 21 | +import net.dv8tion.jda.api.modals.Modal; |
| 22 | +import org.jetbrains.annotations.NotNull; |
| 23 | + |
| 24 | +import java.awt.*; |
| 25 | +import java.net.URL; |
| 26 | +import java.util.*; |
| 27 | +import java.util.List; |
| 28 | +import java.util.concurrent.*; |
| 29 | + |
| 30 | +public class ReportListener extends ListenerAdapter { |
| 31 | + |
| 32 | + private static final ExecutorService SERVICE = Executors.newCachedThreadPool(); |
| 33 | + |
| 34 | + private static final long MESSAGE_ID = 1433051440410136659L; |
| 35 | + private static final long CHANNEL_ID = 849769323166040124L; |
| 36 | + private static final long NO_REPORTS_ROLE_ID = 345024090211483648L; |
| 37 | + |
| 38 | + private static final String BUTTON_ID = "create-report"; |
| 39 | + private static final String USERNAME_FIELD_ID = "username"; |
| 40 | + private static final String RULE_BROKEN_FIELD_ID = "rule-broken"; |
| 41 | + private static final String PROOF_FIELD_ID = "proof"; |
| 42 | + private static final String REPORT_MODAL_FIELD_ID = "report"; |
| 43 | + |
| 44 | + public ReportListener() { |
| 45 | + if (HelpBotInstance.getConfig().isDevBot()) { |
| 46 | + return; |
| 47 | + } |
| 48 | + |
| 49 | + HelpBotInstance.getJda().getTextChannelById(CHANNEL_ID).retrieveMessageById(MESSAGE_ID).queue((msg) -> { |
| 50 | + |
| 51 | + Button button = Button.secondary(BUTTON_ID, "Create Report") |
| 52 | + .withEmoji(Emoji.fromUnicode("\uD83D\uDEA9")); // 🚩 |
| 53 | + |
| 54 | + EmbedBuilder embed = new EmbedBuilder(); |
| 55 | + embed.setTitle("Reporting"); |
| 56 | + embed.setDescription(""" |
| 57 | + Welcome to the reports channel! This is the place where you report rulebreakers. Before you get to that, let's lay down the rules. |
| 58 | + |
| 59 | + 1. Use your common sense - don't break server rules. |
| 60 | + 2. Only report a player if they have legitimately broken a rule. |
| 61 | + 3. Do not make joke reports. |
| 62 | + |
| 63 | + Breaking these rules may result in the inability to post reports to the reports channel, restricted permissions on Discord, or a Discord server mute. |
| 64 | + """); |
| 65 | + embed.setColor(0x05E076); |
| 66 | + |
| 67 | + msg.editMessage("") |
| 68 | + .setEmbeds(List.of( |
| 69 | + embed.build() |
| 70 | + )) |
| 71 | + .setComponents(ActionRow.of(button)) |
| 72 | + .queue(); |
| 73 | + }); |
| 74 | + } |
| 75 | + |
| 76 | + |
| 77 | + @Override |
| 78 | + public void onButtonInteraction(@NotNull ButtonInteractionEvent event) { |
| 79 | + if (!BUTTON_ID.equals(event.getComponentId())) { |
| 80 | + return; |
| 81 | + } |
| 82 | + |
| 83 | + Member member = event.getMember(); |
| 84 | + if (member.getRoles().contains(event.getGuild().getRoleById(NO_REPORTS_ROLE_ID))) { |
| 85 | + event.reply(":x: You're currently blocked from making reports").setEphemeral(true).queue(); |
| 86 | + return; |
| 87 | + } |
| 88 | + |
| 89 | + /* |
| 90 | + Username of rule breaker: |
| 91 | + Rule broken: |
| 92 | + Proof (uncropped screenshot): |
| 93 | + */ |
| 94 | + |
| 95 | + TextDisplay body = TextDisplay.of(""" |
| 96 | + **Thank you for helping keep DiamondFire safe!** |
| 97 | + * Multiple proofs are allowed. |
| 98 | + * Proofs can be both **videos and screenshots**. |
| 99 | + * Uploaded screenshots should be **uncropped** |
| 100 | + > Meaning, take a __full screenshot__ of your screen. |
| 101 | + * Make sure reports are made in **good faith with valid proof**. |
| 102 | + """); |
| 103 | + |
| 104 | + TextInput username = TextInput.create(USERNAME_FIELD_ID, TextInputStyle.SHORT) |
| 105 | + .setPlaceholder("Minecraft Username") |
| 106 | + .setRequiredRange(3, 16) |
| 107 | + .setRequired(true) |
| 108 | + .build(); |
| 109 | + |
| 110 | + TextInput ruleBroken = TextInput.create(RULE_BROKEN_FIELD_ID, TextInputStyle.PARAGRAPH) |
| 111 | + .setPlaceholder("Which rule did they break?") |
| 112 | + .setRequired(true) |
| 113 | + .build(); |
| 114 | + |
| 115 | + AttachmentUpload proof = AttachmentUpload.create(PROOF_FIELD_ID) |
| 116 | + .setMinValues(1) |
| 117 | + .setMaxValues(10) |
| 118 | + .setRequired(true) |
| 119 | + .build(); |
| 120 | + |
| 121 | + Modal modal = Modal.create(REPORT_MODAL_FIELD_ID, "Report") |
| 122 | + .addComponents(body, Label.of("Username of Rule Breaker", username), Label.of("Rule Broken", ruleBroken), Label.of("Proof (uncropped)", proof)) |
| 123 | + .build(); |
| 124 | + |
| 125 | + event.replyModal(modal).queue(); |
| 126 | + } |
| 127 | + |
| 128 | + |
| 129 | + @Override |
| 130 | + public void onModalInteraction(ModalInteractionEvent event) { |
| 131 | + if (!REPORT_MODAL_FIELD_ID.equals(event.getModalId())) { |
| 132 | + return; |
| 133 | + } |
| 134 | + |
| 135 | + String username = getValue(event, USERNAME_FIELD_ID).getAsString(); |
| 136 | + String message = getValue(event, RULE_BROKEN_FIELD_ID).getAsString(); |
| 137 | + List<Message.Attachment> proofs = getValue(event, PROOF_FIELD_ID).getAsAttachmentList(); |
| 138 | + |
| 139 | + final var webhookUrl = HelpBotInstance.getConfig().getForwardingChannels().get("" + CHANNEL_ID); |
| 140 | + if (webhookUrl == null) { |
| 141 | + event.reply(":x: Internal error").queue(); |
| 142 | + } |
| 143 | + |
| 144 | + SERVICE.submit(() -> { |
| 145 | + try (WebhookClient client = JDAWebhookClient.withUrl(webhookUrl.getAsString())) { |
| 146 | + boolean tooLong = message.length() > 2000; |
| 147 | + String content = tooLong ? "See content.txt for message (too long)" : message; |
| 148 | + |
| 149 | + WebhookMessageBuilder builder = new WebhookMessageBuilder() |
| 150 | + .setContent(String.format(""" |
| 151 | + **Offender**: %s |
| 152 | + **Rule Broken**: |
| 153 | + %s |
| 154 | + """, username, content)) |
| 155 | + .setAllowedMentions(AllowedMentions.none()) |
| 156 | + .setUsername(event.getMember().getEffectiveName()) |
| 157 | + .setAvatarUrl(event.getUser().getEffectiveAvatarUrl()); |
| 158 | + |
| 159 | + for (Message.Attachment attachment : proofs) { |
| 160 | + URL url = new URL(attachment.getProxyUrl()); |
| 161 | + builder.addFile(attachment.getFileName(), url.openStream().readAllBytes()); |
| 162 | + } |
| 163 | + if (tooLong) { |
| 164 | + builder.addFile("content.txt", message.getBytes()); |
| 165 | + } |
| 166 | + |
| 167 | + System.out.println("Sending to webhook"); |
| 168 | + client.send(builder.build()).whenComplete((msg, exception) -> { |
| 169 | + if (exception != null) exception.printStackTrace(); |
| 170 | + if (exception != null) { |
| 171 | + event.reply(":x: Uh oh! An error occurred while submitting your report. Please try resending it later.").setEphemeral(true).queue(); |
| 172 | + } else { |
| 173 | + event.reply(":mega: Your report has been successfully submitted!").setEphemeral(true).queue(); |
| 174 | + } |
| 175 | + }); |
| 176 | + } catch (Throwable ignored) { |
| 177 | + ignored.printStackTrace(); |
| 178 | + } |
| 179 | + }); |
| 180 | + |
| 181 | + } |
| 182 | + |
| 183 | + private ModalMapping getValue(ModalInteraction event, String customId) { |
| 184 | + // Instead of event#getValue since it returns null when not found, |
| 185 | + // we just assume the components always exist as we already confirm |
| 186 | + // it's the report modal. |
| 187 | + return event.getValues().stream() |
| 188 | + .filter(mapping -> mapping.getCustomId().equals(customId)) |
| 189 | + .findFirst().orElseThrow(); |
| 190 | + } |
| 191 | + |
| 192 | +} |
0 commit comments