trim the fat, hostname matching will bee too hard to set up for average user anyway

This commit is contained in:
2025-07-17 20:21:08 +02:00
parent bc04c511ca
commit 7d1a7a2b87
10 changed files with 24 additions and 230 deletions
@@ -5,7 +5,6 @@ public record DiscordInvite(
Long guildId,
Long expiresAt
) {
public DiscordInvite {
if (code == null) {
throw new IllegalArgumentException("Code cannot be null");
@@ -15,14 +14,4 @@ public record DiscordInvite(
}
}
/**
* Checks if the invite is expired.
* The invite is considered expired if the current time is more than 5 seconds before the expiration time.
*
* @return true if the invite is expired, false otherwise
*/
public boolean isExpired() {
return System.currentTimeMillis() + 5000 > expiresAt;
}
}
@@ -14,7 +14,7 @@ import java.time.temporal.ChronoUnit;
public class DiscordInviteManager {
private final Cache<String, DiscordInvite> cache = Caffeine.newBuilder()
.expireAfterWrite(Duration.of(60, ChronoUnit.SECONDS))
.expireAfterWrite(Duration.of(55, ChronoUnit.SECONDS))
.build();
private final JDA jda;
@@ -26,7 +26,7 @@ public class DiscordInviteManager {
@Nullable
public DiscordInvite getInvite(long guildId) {
DiscordInvite discordInvite = cache.getIfPresent(String.valueOf(guildId));
if (discordInvite == null || discordInvite.isExpired()) {
if (discordInvite == null) {
Guild guild = jda.getGuildById(guildId);
if (guild == null) {
return null; // Guild not found
@@ -48,4 +48,8 @@ public class DiscordInviteManager {
return discordInvite;
}
public void removeFromCache(long guildId) {
cache.invalidate(String.valueOf(guildId));
}
}
@@ -1,11 +1,8 @@
package me.youhavetrouble.inviter.discord;
import org.jetbrains.annotations.Nullable;
public record GuildSettings(
long guildId,
boolean apiEnabled,
@Nullable String apiHostname
boolean apiEnabled
) {
}
@@ -19,14 +19,14 @@ public class ApiStatusChangeCommand extends Command {
@Nullable
public String getName() {
return "api";
return "invites";
}
public void register(JDA jda, String name) {
jda.upsertCommand(Commands.slash("api", "Change if Inviter API should allow to retrieve this server's invites.")
jda.upsertCommand(Commands.slash("invites", "Change or see if Inviter should create invites for this guild.")
.setIntegrationTypes(IntegrationType.GUILD_INSTALL)
.addOptions(
new OptionData(OptionType.BOOLEAN, "status", "Status of the api availability for this guild", false)
new OptionData(OptionType.BOOLEAN, "status", "Enable or disable Inviter to work for this guild", false)
)
.setContexts(InteractionContextType.GUILD)
.setDefaultPermissions(DefaultMemberPermissions.enabledFor(Permission.MANAGE_SERVER))
@@ -49,8 +49,8 @@ public class ApiStatusChangeCommand extends Command {
GuildSettings setings = Main.getStorage().getGuildSettings(guild.getIdLong());
String message = setings.apiEnabled() ?
"Inviter API is currently __**enabled**__ for this server." :
"Inviter API is currently __**disabled**__ for this server.";
"Inviter is currently __**enabled**__ for this server." :
"Inviter is currently __**disabled**__ for this server.";
event.getHook().editOriginal(message).queue();
return;
@@ -59,8 +59,8 @@ public class ApiStatusChangeCommand extends Command {
boolean status = statusMapping.getAsBoolean();
long guildId = guild.getIdLong();
Main.getStorage().updateDiscordApiEnabled(guildId, status);
String message = status ? "Inviter API is now __**enabled**__ for this server." : "Inviter API is now __**disabled**__ for this server.";
Main.getStorage().updateInvitesEnabled(guildId, status);
String message = status ? "Inviter is now __**enabled**__ for this server." : "Inviter API is now __**disabled**__ for this server.";
event.getHook().editOriginal(message).queue();
}
@@ -19,9 +19,9 @@ public class GuildJoinAndLeaveListener extends ListenerAdapter {
@Override
public void onGuildLeave(@NotNull GuildLeaveEvent event) {
Storage storage = Main.getStorage();
long guildId = event.getGuild().getIdLong();
storage.removeGuildSettings(guildId);
Main.getDiscordInviteMenager().removeFromCache(guildId);
Main.getStorage().removeGuildSettings(guildId);
}
}
@@ -1,42 +0,0 @@
package me.youhavetrouble.inviter.dns;
import me.youhavetrouble.inviter.Main;
import org.jetbrains.annotations.NotNull;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;
public class DNSLookup {
public static Set<String> getTxtRecords(@NotNull String hostname) throws Exception {
Hashtable<String, String> env = new Hashtable<>();
env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
env.put("java.naming.provider.url", "dns://1.1.1.1");
env.put("com.sun.jndi.dns.cache.ttl", "0");
Set<String> txtRecords = new HashSet<>();
DirContext ctx = new InitialDirContext(env);
Attributes attrs = ctx.getAttributes(hostname, new String[]{"TXT"});
Attribute txtAttr = attrs.get("TXT");
if (txtAttr != null) {
for (int i = 0; i < txtAttr.size(); i++) {
Object record = txtAttr.get(i);
if (record instanceof String recordString) {
txtRecords.add(recordString);
} else if (record instanceof byte[]) {
txtRecords.add(new String((byte[]) record));
} else {
txtRecords.add(record.toString());
}
}
}
return txtRecords;
}
}
@@ -11,7 +11,7 @@ import java.util.regex.Pattern;
public class GetDiscordInviteByGuildId implements EndpointHandler {
private final Pattern pathPattern = Pattern.compile("^/api/v1/discord/\\d{10,18}$");
private final Pattern pathPattern = Pattern.compile("^/invite/\\d{10,18}$");
@NotNull
@Override
@@ -1,49 +0,0 @@
package me.youhavetrouble.inviter.http.endpoints;
import com.sun.net.httpserver.HttpExchange;
import me.youhavetrouble.inviter.Main;
import me.youhavetrouble.inviter.discord.DiscordInvite;
import me.youhavetrouble.inviter.discord.DiscordInviteManager;
import me.youhavetrouble.inviter.discord.GuildSettings;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
import java.util.regex.Pattern;
public class MainEndpoint implements EndpointHandler {
private final Pattern pathPattern = Pattern.compile("^/$");
@NotNull
@Override
public Pattern pathPattern() {
return pathPattern;
}
@Override
public void handle(@NotNull HttpExchange exchange) throws IOException {
if (!exchange.getRequestMethod().equals("GET")) {
exchange.sendResponseHeaders(405, -1); // Method Not Allowed
return;
}
String host = exchange.getRemoteAddress().getHostName();
GuildSettings guildSettings = Main.getStorage().getGuildSettings(host);
if (guildSettings == null) {
exchange.sendResponseHeaders(404, -1); // Not Found
return;
}
DiscordInviteManager inviteManager = Main.getDiscordInviteMenager();
DiscordInvite invite = inviteManager.getInvite(guildSettings.guildId());
if (invite == null) {
exchange.sendResponseHeaders(404, -1); // Not Found
return;
}
exchange.getResponseHeaders().set("Location", "https://discord.gg/" + invite.code());
exchange.sendResponseHeaders(307, -1);
}
}
@@ -4,7 +4,6 @@ import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import me.youhavetrouble.inviter.discord.GuildSettings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.sql.DataSource;
import java.io.File;
@@ -12,7 +11,6 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class SqliteStorage implements Storage {
@@ -39,18 +37,10 @@ public class SqliteStorage implements Storage {
connection.createStatement().execute("""
CREATE TABLE IF NOT EXISTS guild_settings (
guild_id LONG PRIMARY KEY,
api_enabled BOOLEAN NOT NULL DEFAULT FALSE
invites_enabled BOOLEAN NOT NULL DEFAULT FALSE
);
"""
);
connection.createStatement().execute("""
CREATE TABLE IF NOT EXISTS hostnames (
hostname VARCHAR(256) PRIMARY KEY,
guild_id LONG NOT NULL,
failed_checks INTEGER NOT NULL DEFAULT 0,
FOREIGN KEY (guild_id) REFERENCES guild_settings(guild_id) ON DELETE CASCADE
)
""");
} catch (SQLException e) {
throw new RuntimeException("Failed to initialize database", e);
}
@@ -69,39 +59,16 @@ public class SqliteStorage implements Storage {
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
boolean apiEnabled = resultSet.getBoolean("api_enabled");
String apiHostname = resultSet.getString("hostname");
return new GuildSettings(guildId, apiEnabled, apiHostname);
boolean invitesEnabled = resultSet.getBoolean("invites_enabled");
return new GuildSettings(guildId, invitesEnabled);
}
return new GuildSettings(guildId,false, null);
return new GuildSettings(guildId,false);
} catch (SQLException e) {
throw new RuntimeException("Failed to retrieve guild settings", e);
}
}
@Nullable
@Override
public GuildSettings getGuildSettings(@NotNull String hostname) {
try (Connection connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement(
"SELECT * FROM guild_settings WHERE hostname = ?"
);
statement.setString(1, hostname);
ResultSet resultSet = statement.executeQuery();
if (resultSet.next()) {
long guildId = resultSet.getLong("guild_id");
boolean apiEnabled = resultSet.getBoolean("api_enabled");
return new GuildSettings(guildId, apiEnabled, hostname);
}
return null; // No settings found for the given hostname
} catch (SQLException e) {
throw new RuntimeException("Failed to retrieve guild settings by hostname", e);
}
}
@Override
public void saveDefaultGuildSettings(long guildId) {
try (Connection connection = dataSource.getConnection()) {
@@ -129,10 +96,10 @@ public class SqliteStorage implements Storage {
}
@Override
public void updateDiscordApiEnabled(long guildId, boolean enabled) {
public void updateInvitesEnabled(long guildId, boolean enabled) {
try (Connection connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement(
"UPDATE guild_settings SET api_enabled = ? WHERE guild_id = ?"
"UPDATE guild_settings SET invites_enabled = ? WHERE guild_id = ?"
);
statement.setBoolean(1, enabled);
statement.setLong(2, guildId);
@@ -143,63 +110,4 @@ public class SqliteStorage implements Storage {
}
@Override
public void addHostname(long guildId, @Nullable String hostname) {
try (Connection connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement(
"INSERT OR IGNORE INTO hostnames (hostname, guild_id) VALUES (?, ?)"
);
statement.setString(1, hostname);
statement.setLong(2, guildId);
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("Failed to add hostname", e);
}
}
@Override
public void removeHostname(@NotNull String hostname) {
try (Connection connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement(
"DELETE FROM hostnames WHERE hostname = ?"
);
statement.setString(1, hostname);
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("Failed to remove hostname", e);
}
}
@Override
public List<String> listHostnames(long guildId) {
try (Connection connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement(
"SELECT hostname FROM hostnames WHERE guild_id = ?"
);
statement.setLong(1, guildId);
ResultSet resultSet = statement.executeQuery();
List<String> hostnames = new java.util.ArrayList<>();
while (resultSet.next()) {
hostnames.add(resultSet.getString("hostname"));
}
return hostnames;
} catch (SQLException e) {
throw new RuntimeException("Failed to list hostnames", e);
}
}
@Override
public void cleanUpHostnames() {
try (Connection connection = dataSource.getConnection()) {
PreparedStatement statement = connection.prepareStatement(
"DELETE FROM hostnames WHERE failed_checks >= 3"
);
statement.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("Failed to clean up hostnames", e);
}
}
}
@@ -3,28 +3,15 @@ package me.youhavetrouble.inviter.storage;
import me.youhavetrouble.inviter.discord.GuildSettings;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public interface Storage {
@NotNull GuildSettings getGuildSettings(long guildId);
@Nullable GuildSettings getGuildSettings(@NotNull String hostname);
void saveDefaultGuildSettings(long guildId);
void removeGuildSettings(long guildId);
void updateDiscordApiEnabled(long guildId, boolean enabled);
void addHostname(long guildId, @Nullable String hostname);
void removeHostname(@NotNull String hostname);
List<String> listHostnames(long guildId);
void cleanUpHostnames();
void updateInvitesEnabled(long guildId, boolean enabled);
}