From d5c355d7c1f2274c9153e89e29060209d69f7697 Mon Sep 17 00:00:00 2001 From: Kas-tle <26531652+Kas-tle@users.noreply.github.com> Date: Thu, 18 Apr 2024 07:49:35 -0700 Subject: [PATCH] Add Previews Channel (#310) * Previews channel support Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Order settings command list Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> * Address review Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --------- Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com> --- README.md | 36 +++---- build.gradle | 6 ++ .../org/geysermc/discordbot/GeyserBot.java | 2 +- .../administration/SettingsCommand.java | 2 + .../discordbot/listeners/PreviewHandler.java | 96 +++++++++++++++++++ .../discordbot/storage/ServerSettings.java | 33 +++++++ 6 files changed, 157 insertions(+), 18 deletions(-) create mode 100644 src/main/java/org/geysermc/discordbot/listeners/PreviewHandler.java diff --git a/README.md b/README.md index 94855d11..93dab7ca 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,22 @@ A bot for the GeyserMC Discord server These allow customisation of the bot on a per-guild basis. These can be changed using `!settings set preference value` and checked using `!settings get preference`. Most of these are lists which values are seperated by `,` and a few are seperated by a `|` aswell which gives them a more key-value like structure. -| Preference | Description | Example value | -|----------------------|--------------------------------------------------------------------------------------|-------------------------------------------------------------| -| `convert-extensions` | File extensions to auto upload to paste.gg | `txt,log,yml,log.0` | -| `log-channel` | Channel ID to output moderation logs to | `614877230811709474` | -| `update-channel` | Channel ID to notify with minecraft updates | `618189671259832320` | -| `roles` | Roles that are available to join with the `role`/`rank` command | `GeyserNews\|613169070367309834,Tester\|775083359101517884` | -| `voice-role` | The role to grant users when in a voice channel | `856161072507518986` | -| `dont-log` | Channels IDs that are excluded from the moderation log | `613168850925649981,613168974645035016` | -| `check-domains` | Domains to check against to filter phishing and malware domains | `steamcommunity.com` | -| `health-checks` | Channel IDs to log web health checks to along with the url to check | `808759445827223572\|https://api.geysermc.org/health` | -| `dont-level` | Channel IDs to ignore messages for the level system, set to 0 to disable server wide | `754458074818805811` | -| `punishment-message` | A message to add to the end of a punishment DM | `If you wish to appeal your punishment, contact an admin.` | -| `banned-domains` | Domains that if found in a message, the message will be removed | `dlscord-nitro.click,discord-airdrop.com` | -| `banned-ips` | IPs that if a domain found in a message resolves to, the message will be removed | `95.181.163.44,95.181.163.46` | -| `rss-feeds` | Channel IDs to log RSS posts to along with the RSS feed url | `916482484122824704\|https://blog.geysermc.org/feed.xml` | -| `allowed-invites` | Guild IDs for allowed invites | `613163671870242838,393465748535640064` | -| `forum-channel` | Channel ID for forum management | `645877230811709456` | \ No newline at end of file +| Preference | Description | Example value | +|-------------------------|--------------------------------------------------------------------------------------|-------------------------------------------------------------| +| `convert-extensions` | File extensions to auto upload to paste.gg | `txt,log,yml,log.0` | +| `log-channel` | Channel ID to output moderation logs to | `614877230811709474` | +| `update-channel` | Channel ID to notify with minecraft updates | `618189671259832320` | +| `roles` | Roles that are available to join with the `role`/`rank` command | `GeyserNews\|613169070367309834,Tester\|775083359101517884` | +| `voice-role` | The role to grant users when in a voice channel | `856161072507518986` | +| `dont-log` | Channels IDs that are excluded from the moderation log | `613168850925649981,613168974645035016` | +| `check-domains` | Domains to check against to filter phishing and malware domains | `steamcommunity.com` | +| `health-checks` | Channel IDs to log web health checks to along with the url to check | `808759445827223572\|https://api.geysermc.org/health` | +| `dont-level` | Channel IDs to ignore messages for the level system, set to 0 to disable server wide | `754458074818805811` | +| `punishment-message` | A message to add to the end of a punishment DM | `If you wish to appeal your punishment, contact an admin.` | +| `banned-domains` | Domains that if found in a message, the message will be removed | `dlscord-nitro.click,discord-airdrop.com` | +| `banned-ips` | IPs that if a domain found in a message resolves to, the message will be removed | `95.181.163.44,95.181.163.46` | +| `rss-feeds` | Channel IDs to log RSS posts to along with the RSS feed url | `916482484122824704\|https://blog.geysermc.org/feed.xml` | +| `allowed-invites` | Guild IDs for allowed invites | `613163671870242838,393465748535640064` | +| `forum-channel` | Channel ID for forum management | `645877230811709456` | +| `preview-feeds-channel` | News channel ID for preview feeds | `614877230811709474` | +| `preview-feeds` | Forum channel ID for preview feeds | `614877230811709474` | \ No newline at end of file diff --git a/build.gradle b/build.gradle index 67699c2e..2e8f3be0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java-library' + id 'application' id 'io.sentry.jvm.gradle' version '3.13.0' } @@ -104,6 +105,11 @@ jar { } } +// Allow running with `./gradlew run` task +application { + mainClass = 'org.geysermc.discordbot.GeyserBot' +} + test { useJUnitPlatform() } diff --git a/src/main/java/org/geysermc/discordbot/GeyserBot.java b/src/main/java/org/geysermc/discordbot/GeyserBot.java index 76a72e9b..b1156cbf 100644 --- a/src/main/java/org/geysermc/discordbot/GeyserBot.java +++ b/src/main/java/org/geysermc/discordbot/GeyserBot.java @@ -62,7 +62,6 @@ import org.slf4j.LoggerFactory; import pw.chew.chewbotcca.util.RestClient; -import javax.security.auth.login.LoginException; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationTargetException; @@ -240,6 +239,7 @@ public static void main(String[] args) throws IOException { new HelpHandler(), new SupportHandler(), new DeleteHandler(), + new PreviewHandler(), client.build(), tagClient.build()) .build(); diff --git a/src/main/java/org/geysermc/discordbot/commands/administration/SettingsCommand.java b/src/main/java/org/geysermc/discordbot/commands/administration/SettingsCommand.java index e47c649c..aab32cb8 100644 --- a/src/main/java/org/geysermc/discordbot/commands/administration/SettingsCommand.java +++ b/src/main/java/org/geysermc/discordbot/commands/administration/SettingsCommand.java @@ -69,6 +69,8 @@ public SettingsCommand() { .addChoice("Forum Channel", "forum-channel") .addChoice("Health Checks", "health-checks") .addChoice("Log channel", "log-channel") + .addChoice("Preview Channel", "preview-channel") + .addChoice("Preview Feeds Channel", "preview-feeds-channel") .addChoice("Punishment Message", "punishment-message") .addChoice("Roles", "roles") .addChoice("RSS Feeds", "rss-feeds") diff --git a/src/main/java/org/geysermc/discordbot/listeners/PreviewHandler.java b/src/main/java/org/geysermc/discordbot/listeners/PreviewHandler.java new file mode 100644 index 00000000..5b8799cb --- /dev/null +++ b/src/main/java/org/geysermc/discordbot/listeners/PreviewHandler.java @@ -0,0 +1,96 @@ +package org.geysermc.discordbot.listeners; + +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.NewsChannel; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; +import org.geysermc.discordbot.storage.ServerSettings; +import org.geysermc.discordbot.util.BotColors; +import org.json.JSONObject; +import pw.chew.chewbotcca.util.RestClient; + +import javax.annotation.Nonnull; + +import java.time.Instant; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PreviewHandler extends ListenerAdapter { + private static final Pattern GH_PR_PATTERN = Pattern.compile("https://github\\.com/GeyserMC/(.+)/pull/(\\d+)"); + + @Override + public void onMessageReceived(@Nonnull MessageReceivedEvent event) { + if (event.getAuthor().isBot()) + return; + + NewsChannel previewFeedsChannel = ServerSettings.getPreviewFeedsChannel(event.getGuild()); + if (previewFeedsChannel == null) + return; + if (event.getChannel().getIdLong() != previewFeedsChannel.getIdLong()) + return; + + // Create a post in the preview forum channel with this content + ForumChannel previewChannel = ServerSettings.getPreviewChannel(event.getGuild()); + if (previewChannel == null) + return; + + // Check if the message contains a GitHub PR link and extract the repo and PR + // number via matcher + String content = event.getMessage().getContentRaw(); + + Matcher matcher = GH_PR_PATTERN.matcher(content); + if (!matcher.find()) { + return; + } + + String repo = matcher.group(1); + String pr = matcher.group(2); + + RestClient.RestResponse restResponse = RestClient + .getJsonObject("https://api.github.com/repos/GeyserMC/" + repo + "/pulls/" + pr); + JSONObject serverResponse = restResponse.body(); + + if (serverResponse.has("message")) { + // The linked PR does not exist + return; + } + + previewChannel.createForumPost( + serverResponse.getString("title"), + MessageCreateData.fromEmbeds(new EmbedBuilder() + .setTitle(serverResponse.getString("title")) + .setColor(BotColors.SUCCESS.getColor()) + .setDescription(event.getMessage().getContentRaw()) + .setAuthor(event.getAuthor().getEffectiveName(), null, + event.getAuthor().getEffectiveAvatarUrl()) + .setImage("https://opengraph.githubassets.com/1/GeyserMC/" + repo + "/pull/" + pr) + .setTimestamp(Instant.now()) + .build())) + .addActionRow( + Button.link(serverResponse.getString("html_url"), "Discuss on GitHub") + .withEmoji(Emoji.fromFormatted("💬")), + Button.link(serverResponse.getString("html_url") + "/files", "View Changes") + .withEmoji(Emoji.fromFormatted("📝")), + Button.link(serverResponse.getString("html_url") + "/checks", "Download Artifacts") + .withEmoji(Emoji.fromFormatted("📦"))) + .queue(forumPost -> { + // Reply to the original message with the link to the forum post + event.getMessage().replyEmbeds(new EmbedBuilder() + .setColor(BotColors.SUCCESS.getColor()) + .setDescription("The above preview can be discussed in:\n### <#" + + forumPost.getMessage().getId() + ">") + .setTimestamp(Instant.now()) + .build()).queue(); + }); + + // Remove embeds from the original message + event.getMessage().suppressEmbeds(true).queue(); + + // Publish message + event.getMessage().crosspost().queue(); + } +} diff --git a/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java b/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java index a0f78feb..e5761f4a 100644 --- a/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java +++ b/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java @@ -28,6 +28,7 @@ import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel; +import net.dv8tion.jda.api.entities.channel.concrete.NewsChannel; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; @@ -222,4 +223,36 @@ public static ForumChannel getForumChannel(@NotNull Guild guild) { } return guild.getForumChannelById(channel); } + + /** + * Get the preview feeds channel for the selected guild + * + * @param guild ID of the guild to get the channel for + * @return The preview feeds channel for the guild + */ + public static NewsChannel getPreviewFeedsChannel(@NotNull Guild guild) { + String channel = GeyserBot.storageManager.getServerPreference(guild.getIdLong(), "preview-feeds-channel"); + + if (channel == null) { + return null; + } + + return guild.getNewsChannelById(channel); + } + + /** + * Get the preview channel for the selected guild + * + * @param guild ID of the guild to get the channel for + * @return The preview channel for the guild + */ + public static ForumChannel getPreviewChannel(@NotNull Guild guild) { + String channel = GeyserBot.storageManager.getServerPreference(guild.getIdLong(), "preview-channel"); + + if (channel == null) { + return null; + } + + return guild.getForumChannelById(channel); + } }