diff --git a/pom.xml b/pom.xml index 3c8cee5..26d7494 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 net.pcsx2 HifumiBot - 4.10.0 + 4.10.1 21 21 diff --git a/src/main/java/net/pcsx2/hifumi/async/AntiBotRunnable.java b/src/main/java/net/pcsx2/hifumi/async/AntiBotRunnable.java index f18458f..4888019 100644 --- a/src/main/java/net/pcsx2/hifumi/async/AntiBotRunnable.java +++ b/src/main/java/net/pcsx2/hifumi/async/AntiBotRunnable.java @@ -1,9 +1,20 @@ package net.pcsx2.hifumi.async; import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.net.URI; +import java.net.URL; import java.time.OffsetDateTime; import java.util.ArrayList; +import javax.imageio.ImageIO; + +import org.apache.commons.lang3.StringUtils; + import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.components.actionrow.ActionRow; import net.dv8tion.jda.api.components.buttons.Button; @@ -12,6 +23,7 @@ import net.dv8tion.jda.api.entities.Message.Attachment; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.utils.FileUpload; import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; import net.pcsx2.hifumi.HifumiBot; import net.pcsx2.hifumi.database.Database; @@ -57,8 +69,40 @@ private boolean isImageScam() { User user = this.message.getAuthor(); if (timeoutRes) { - // Since our timeout succeeded, now sweep up any other messages the bot might have - // blasted out while this runnable was going. + // Since our timeout succeeded, grab some thumbnails of the images so we have something to present for review before deleting stuff. + // If we delete the message first then attachments go too. + ArrayList files = new ArrayList(); + + for (Attachment attachment : this.message.getAttachments()) { + // For now, just do one image... If we have problems later and need them all, yank out this if. + if (!files.isEmpty()) { + break; + } + + try { + URL url = URL.of(URI.create(attachment.getProxyUrl()), null); + BufferedImage img = ImageIO.read(url); + int width = img.getWidth() / 2, height = img.getHeight() / 2; + Image scaled = img.getScaledInstance(width, height, Image.SCALE_FAST); + BufferedImage bufImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics2D graph = bufImg.createGraphics(); + graph.drawImage(scaled, 0, 0, null); + graph.dispose(); + + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { + ImageIO.write(bufImg, "png", os); + + try (ByteArrayInputStream imgStream = new ByteArrayInputStream(os.toByteArray())) { + FileUpload file = FileUpload.fromData(imgStream, attachment.getFileName()).asSpoiler(); + files.add(file); + } + } + } catch (Exception e) { + // Squelch + } + } + + // Sweep up any other messages the bot might have blasted out while this runnable was going. OffsetDateTime timeToRemoveMessagesSince = OffsetDateTime.now().minusMinutes(AGE_MINUTES_TO_REMOVE_MESSAGES); ArrayList otherMessages = Database.getAllMessagesSinceTime(this.message.getAuthor().getIdLong(), timeToRemoveMessagesSince.toEpochSecond()); @@ -75,24 +119,36 @@ private boolean isImageScam() { eb.addField("Username", user.getName(), true); eb.addField("Display Name (as mention)", user.getAsMention(), true); eb.setColor(Color.YELLOW); - eb.appendDescription("Links in body:\n"); + + // Body content preview + eb.addField("Body Content (raw, first 100 chars)", StringUtils.abbreviate(this.message.getContentRaw(), 100), false); + + // Links + StringBuilder sb = new StringBuilder(); for (String link : links) { - eb.appendDescription(link + "\n"); + sb.append(link + "\n"); } - - eb.appendDescription("Attachments:\n"); + + eb.addField("Links in Body", sb.toString(), false); + + // Attachments + sb = new StringBuilder(); for (Attachment attachment : this.message.getAttachments()) { - eb.appendDescription(attachment.getProxyUrl() + "\n"); + sb.append(attachment.getProxyUrl() + "\n"); } + + eb.addField("Attachments", sb.toString(), false); MessageCreateBuilder mb = new MessageCreateBuilder(); mb.addEmbeds(eb.build()); + mb.addFiles(files); mb.addComponents(ActionRow.of( Button.of(ButtonStyle.DANGER, "imagescam:dospamkick:" + authorIdLong, "Looks like a bot scam, kick user"), Button.of(ButtonStyle.SUCCESS, "imagescam:clear:" + authorIdLong, "Looks innocent, remove timeout") )); + Messaging.sendMessage(HifumiBot.getSelf().getConfig().channels.systemOutputChannelId, mb.build()); return true; } else { diff --git a/src/main/java/net/pcsx2/hifumi/event/ButtonEventListener.java b/src/main/java/net/pcsx2/hifumi/event/ButtonEventListener.java index 68dce6c..1596d2b 100644 --- a/src/main/java/net/pcsx2/hifumi/event/ButtonEventListener.java +++ b/src/main/java/net/pcsx2/hifumi/event/ButtonEventListener.java @@ -3,19 +3,10 @@ import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Optional; import java.util.concurrent.TimeUnit; -import net.pcsx2.hifumi.HifumiBot; -import net.pcsx2.hifumi.command.AbstractSlashCommand; -import net.pcsx2.hifumi.command.slash.CommandEmulog; -import net.pcsx2.hifumi.command.slash.CommandServerMetadata; -import net.pcsx2.hifumi.command.slash.CommandWhois; -import net.pcsx2.hifumi.moderation.ModActions; -import net.pcsx2.hifumi.util.MemberUtils; -import net.pcsx2.hifumi.util.Messaging; -import net.pcsx2.hifumi.util.UserUtils; - import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.components.MessageTopLevelComponent; import net.dv8tion.jda.api.components.actionrow.ActionRow; @@ -25,6 +16,15 @@ import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.pcsx2.hifumi.HifumiBot; +import net.pcsx2.hifumi.command.AbstractSlashCommand; +import net.pcsx2.hifumi.command.slash.CommandEmulog; +import net.pcsx2.hifumi.command.slash.CommandServerMetadata; +import net.pcsx2.hifumi.command.slash.CommandWhois; +import net.pcsx2.hifumi.moderation.ModActions; +import net.pcsx2.hifumi.util.MemberUtils; +import net.pcsx2.hifumi.util.Messaging; +import net.pcsx2.hifumi.util.UserUtils; public class ButtonEventListener extends ListenerAdapter { @@ -132,6 +132,7 @@ public void onButtonInteraction(ButtonInteractionEvent event) { Button button = Button.of(ButtonStyle.PRIMARY, "imagescam:resolved:" + userIdLong, "Resolved by " + event.getUser().getEffectiveName() + " (kicked user)"); ActionRow actionRow = ActionRow.of(button); event.getHook().editMessageComponentsById(event.getMessageId(), actionRow).queue(); + event.getMessage().editMessageAttachments(List.of()).queue(); } case "clear" -> { Optional memberOpt = MemberUtils.getOrRetrieveMember(event.getGuild(), userIdLong); @@ -140,10 +141,14 @@ public void onButtonInteraction(ButtonInteractionEvent event) { Member member = memberOpt.get(); member.removeTimeout().queue(); event.reply("Timeout removed from user").setEphemeral(true).queue(); - Button button = Button.of(ButtonStyle.PRIMARY, "imagescam:resolved:" + userIdLong, "Resolved by " + event.getUser().getEffectiveName() + " (removed timeout)"); - ActionRow actionRow = ActionRow.of(button); - event.getHook().editMessageComponentsById(event.getMessageId(), actionRow).queue(); + } else { + event.reply("User could not be retrieved, did they already leave?").setEphemeral(true).queue(); } + + Button button = Button.of(ButtonStyle.PRIMARY, "imagescam:resolved:" + userIdLong, "Resolved by " + event.getUser().getEffectiveName() + " (removed timeout)"); + ActionRow actionRow = ActionRow.of(button); + event.getHook().editMessageComponentsById(event.getMessageId(), actionRow).queue(); + event.getMessage().editMessageAttachments(List.of()).queue(); } case "resolved" -> { event.reply("This event has already been resolved.").setEphemeral(true).queue();