|  | 
|  | 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