From f145ab6aa3cca7d06eca22fd58131e979e7797e2 Mon Sep 17 00:00:00 2001 From: Tim203 Date: Thu, 4 Jun 2026 20:31:09 +0200 Subject: [PATCH] Automate applying security actions --- .../org/geysermc/discordbot/GeyserBot.java | 4 ++ .../background/SecurityActionsApplier.java | 70 +++++++++++++++++++ .../storage/AbstractStorageManager.java | 10 ++- .../storage/MySQLStorageManager.java | 21 ++++++ .../discordbot/storage/ServerSettings.java | 23 ++++++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/geysermc/discordbot/background/SecurityActionsApplier.java diff --git a/src/main/java/org/geysermc/discordbot/GeyserBot.java b/src/main/java/org/geysermc/discordbot/GeyserBot.java index 3158f2c2..e022992c 100644 --- a/src/main/java/org/geysermc/discordbot/GeyserBot.java +++ b/src/main/java/org/geysermc/discordbot/GeyserBot.java @@ -42,6 +42,7 @@ import net.dv8tion.jda.api.utils.MemberCachePolicy; import net.dv8tion.jda.api.utils.cache.CacheFlag; import net.dv8tion.jda.api.utils.messages.MessageRequest; +import org.geysermc.discordbot.background.SecurityActionsApplier; import org.geysermc.discordbot.health_checker.HealthCheckerManager; import org.geysermc.discordbot.http.Server; import org.geysermc.discordbot.listeners.*; @@ -301,6 +302,9 @@ public static void main(String[] args) throws IOException { int playerCount = players.getJSONArray(players.length() - 1).getInt(1); jda.getPresence().setActivity(Activity.playing(BotHelpers.coolFormat(serverCount) + " servers, " + BotHelpers.coolFormat(playerCount) + " players")); }, 5, 60 * 5, TimeUnit.SECONDS); + + // Start the Security Actions applier + generalThreadPool.scheduleAtFixedRate(new SecurityActionsApplier(), 5, 60 * 60, TimeUnit.SECONDS); } public static JDA getJDA() { diff --git a/src/main/java/org/geysermc/discordbot/background/SecurityActionsApplier.java b/src/main/java/org/geysermc/discordbot/background/SecurityActionsApplier.java new file mode 100644 index 00000000..b8fdb4d7 --- /dev/null +++ b/src/main/java/org/geysermc/discordbot/background/SecurityActionsApplier.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2026 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/GeyserDiscordBot + */ + +package org.geysermc.discordbot.background; + +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import java.time.OffsetDateTime; +import java.util.List; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.guild.SecurityIncidentActions; +import org.geysermc.discordbot.GeyserBot; +import org.geysermc.discordbot.storage.ServerSettings; + +public class SecurityActionsApplier implements Runnable { + @Override + public void run() { + Long2ObjectMap> enabledActionsPerGuild = ServerSettings.getListForAllServers("security-actions"); + if (enabledActionsPerGuild.isEmpty()) { + return; + } + + // The max is 24h, so just set it to 12h to be sure. + // It runs every hour anyway. + OffsetDateTime later = OffsetDateTime.now().plusHours(12); + + for (Long2ObjectMap.Entry> entry : enabledActionsPerGuild.long2ObjectEntrySet()) { + Guild guild = GeyserBot.getJDA().getGuildById(entry.getLongKey()); + if (guild == null || !guild.getSelfMember().hasPermission(Permission.MANAGE_SERVER)) continue; + + boolean invitesDisabled = entry.getValue().contains("invites"); + boolean dmsDisabled = entry.getValue().contains("dms"); + + if (invitesDisabled || dmsDisabled) { + guild.modifySecurityIncidents(SecurityIncidentActions.enabled( + invitesDisabled ? later : null, + dmsDisabled ? later : null + )).queue(); + continue; + } + + SecurityIncidentActions actions = guild.getSecurityIncidentActions(); + if (actions.getDirectMessagesDisabledUntil() != null || actions.getInvitesDisabledUntil() != null) { + guild.modifySecurityIncidents(SecurityIncidentActions.disabled()).queue(); + } + } + } +} diff --git a/src/main/java/org/geysermc/discordbot/storage/AbstractStorageManager.java b/src/main/java/org/geysermc/discordbot/storage/AbstractStorageManager.java index 7f3c3bc0..813c8e31 100644 --- a/src/main/java/org/geysermc/discordbot/storage/AbstractStorageManager.java +++ b/src/main/java/org/geysermc/discordbot/storage/AbstractStorageManager.java @@ -25,10 +25,10 @@ package org.geysermc.discordbot.storage; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; -import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.UserSnowflake; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; @@ -55,6 +55,14 @@ public abstract class AbstractStorageManager { */ public abstract String getServerPreference(long serverID, String preference); + /** + * Get a preference from the database, for all servers + * + * @param preference Key of the requested preference + * @return The value for each server that has configured this preference. + */ + public abstract Long2ObjectMap getPreferenceForAllServers(String preference); + /** * Set a preference in the database * diff --git a/src/main/java/org/geysermc/discordbot/storage/MySQLStorageManager.java b/src/main/java/org/geysermc/discordbot/storage/MySQLStorageManager.java index 0fd26f57..db4f18c9 100644 --- a/src/main/java/org/geysermc/discordbot/storage/MySQLStorageManager.java +++ b/src/main/java/org/geysermc/discordbot/storage/MySQLStorageManager.java @@ -26,6 +26,9 @@ package org.geysermc.discordbot.storage; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import java.sql.PreparedStatement; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; @@ -99,6 +102,24 @@ public String getServerPreference(long serverID, String preference) { return null; } + @Override + public Long2ObjectMap getPreferenceForAllServers(String preference) { + checkConnection(); + try (PreparedStatement statement = connection.prepareStatement("SELECT server, value FROM preferences WHERE key = ?")) { + statement.setString(1, preference); + + Long2ObjectMap map = new Long2ObjectOpenHashMap<>(); + try (ResultSet result = statement.executeQuery()) { + while (result.next()) { + map.put(result.getLong(1), result.getString(2)); + } + } + return map; + } catch (SQLException ignored) { + return null; + } + } + @Override public void setServerPreference(long serverID, String preference, String value) { checkConnection(); diff --git a/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java b/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java index d154bb96..3f49d6f7 100644 --- a/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java +++ b/src/main/java/org/geysermc/discordbot/storage/ServerSettings.java @@ -25,6 +25,9 @@ package org.geysermc.discordbot.storage; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMaps; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.channel.Channel; @@ -69,6 +72,26 @@ public static List getList(long serverID, String key) { return new ArrayList<>(Arrays.asList(listData.split(",").clone())); } + /** + * Get a preference as a list of strings delimited by `,`, for each server with this preference + * + * @param key The preference key to get + * @return Per guild the preference value as a list + */ + @NotNull + public static Long2ObjectMap> getListForAllServers(String key) { + Long2ObjectMap listData = GeyserBot.storageManager.getPreferenceForAllServers(key); + if (listData == null) { + return Long2ObjectMaps.emptyMap(); + } + + Long2ObjectMap> map = new Long2ObjectOpenHashMap<>(); + listData.long2ObjectEntrySet().forEach(entry -> + map.put(entry.getLongKey(), new ArrayList<>(Arrays.asList(entry.getValue().split(",").clone()))) + ); + return map; + } + public static void setList(long serverID, String key, List data) { GeyserBot.storageManager.setServerPreference(serverID, key, StringUtils.join(data, ",")); }