commit ecc9d54c3c1e924c7778df41576bd9955c2e9854 Author: YouHaveTrouble Date: Mon Jul 7 19:47:52 2025 +0200 initial bot and in-memory storage diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f3cac9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ +logs/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..7bc07ec --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Environment-dependent path to Maven home directory +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..ecfa09c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..4b36ea0 --- /dev/null +++ b/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + me.youhavetrouble.inviter + Inviter + 1.0-SNAPSHOT + + + 21 + 21 + UTF-8 + + + + + org.apache.logging.log4j + log4j-core + 2.23.1 + compile + + + org.apache.logging.log4j + log4j-api + 2.23.1 + compile + + + org.apache.logging.log4j + log4j-slf4j2-impl + 2.23.1 + compile + + + com.github.ben-manes.caffeine + caffeine + 3.1.8 + compile + + + org.xerial + sqlite-jdbc + 3.46.1.2 + compile + + + com.zaxxer + HikariCP + 5.1.0 + compile + + + net.dv8tion + JDA + 5.6.1 + + + club.minnced + opus-java + + + + + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..dde2278 --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# Inviter + +Discord bot that allows users to set up personalized invite links for their servers and have rotating invite codes \ No newline at end of file diff --git a/src/main/java/me/youhavetrouble/inviter/DiscordInvite.java b/src/main/java/me/youhavetrouble/inviter/DiscordInvite.java new file mode 100644 index 0000000..2c2e746 --- /dev/null +++ b/src/main/java/me/youhavetrouble/inviter/DiscordInvite.java @@ -0,0 +1,28 @@ +package me.youhavetrouble.inviter; + +public record DiscordInvite( + String code, + Long guildId, + Long expiresAt +) { + + public DiscordInvite { + if (code == null) { + throw new IllegalArgumentException("Code cannot be null"); + } + if (guildId == null || guildId <= 0) { + throw new IllegalArgumentException("Guild ID must not be null nor be a negative number"); + } + } + + /** + * 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; + } + +} diff --git a/src/main/java/me/youhavetrouble/inviter/Main.java b/src/main/java/me/youhavetrouble/inviter/Main.java new file mode 100644 index 0000000..5a5d878 --- /dev/null +++ b/src/main/java/me/youhavetrouble/inviter/Main.java @@ -0,0 +1,49 @@ +package me.youhavetrouble.inviter; + +import me.youhavetrouble.inviter.storage.MemoryStorage; +import me.youhavetrouble.inviter.storage.Storage; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.requests.GatewayIntent; +import net.dv8tion.jda.api.utils.cache.CacheFlag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +public class Main { + + private static final Logger LOGGER = LoggerFactory.getLogger("Inviter"); + + private static JDA jda; + + private static Storage storage; + + public static void main(String[] args) throws InterruptedException { + + String token = System.getenv("DISCORD_TOKEN"); + + jda = JDABuilder.create( + token, + Set.of(GatewayIntent.GUILD_INVITES) + ) + .disableCache( + CacheFlag.ACTIVITY, + CacheFlag.VOICE_STATE, + CacheFlag.EMOJI, + CacheFlag.STICKER, + CacheFlag.CLIENT_STATUS, + CacheFlag.ONLINE_STATUS, + CacheFlag.SCHEDULED_EVENTS + ) + .build(); + + jda.awaitReady(); + + storage = new MemoryStorage(jda); + + LOGGER.info("Welcome to the Inviter Application!"); + + } + +} diff --git a/src/main/java/me/youhavetrouble/inviter/storage/MemoryStorage.java b/src/main/java/me/youhavetrouble/inviter/storage/MemoryStorage.java new file mode 100644 index 0000000..8557ff5 --- /dev/null +++ b/src/main/java/me/youhavetrouble/inviter/storage/MemoryStorage.java @@ -0,0 +1,75 @@ +package me.youhavetrouble.inviter.storage; + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import me.youhavetrouble.inviter.DiscordInvite; +import net.dv8tion.jda.api.JDA; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Invite; +import net.dv8tion.jda.api.entities.channel.unions.DefaultGuildChannelUnion; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +public class MemoryStorage implements Storage { + + private final Cache cache = Caffeine.newBuilder() + .expireAfterWrite(Duration.of(60, ChronoUnit.SECONDS)) + .build(); + + private final JDA jda; + + public MemoryStorage(JDA jda) { + this.jda = jda; + } + + + @Nullable + @Override + public DiscordInvite getInvite(long guildId) { + DiscordInvite discordInvite = cache.getIfPresent(String.valueOf(guildId)); + if (discordInvite == null || discordInvite.isExpired()) { + Guild guild = jda.getGuildById(guildId); + if (guild == null) { + return null; // Guild not found + } + DefaultGuildChannelUnion defaultChannel = guild.getDefaultChannel(); + if (defaultChannel == null) { + return null; // No default channel found + } + Invite invite = defaultChannel.createInvite() + .setMaxAge(60) // Set the invite to expire after 60 seconds + .complete(); + if (invite == null) return null; // Failed to create invite + discordInvite = new DiscordInvite( + invite.getCode(), + guild.getIdLong(), + invite.getTimeCreated().toEpochSecond() + ); + } + return discordInvite; + } + + @NotNull + @Override + public DiscordInvite saveInvite(Invite invite) { + + if (invite == null) { + throw new IllegalArgumentException("Invite cannot be null"); + } + if (invite.getGuild() == null) { + throw new IllegalArgumentException("Invite must be associated with a guild"); + } + + DiscordInvite discordInvite = new DiscordInvite( + invite.getCode(), + invite.getGuild().getIdLong(), + invite.getTimeCreated().toEpochSecond() + ); + + cache.put(invite.getGuild().getId(), discordInvite); + return discordInvite; + } +} diff --git a/src/main/java/me/youhavetrouble/inviter/storage/Storage.java b/src/main/java/me/youhavetrouble/inviter/storage/Storage.java new file mode 100644 index 0000000..e6f74b9 --- /dev/null +++ b/src/main/java/me/youhavetrouble/inviter/storage/Storage.java @@ -0,0 +1,19 @@ +package me.youhavetrouble.inviter.storage; + +import me.youhavetrouble.inviter.DiscordInvite; +import net.dv8tion.jda.api.entities.Invite; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface Storage { + + @Nullable DiscordInvite getInvite(long guildId); + + /** + * Saves the invite to the storage and returns the saved invite. + * @param invite JDA invite object to save + * @return the saved DiscordInvite object + */ + @NotNull DiscordInvite saveInvite(Invite invite); + +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..d3e27fc --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,30 @@ + + + + + + [%d{yyyy-MM-dd HH:mm:ss}] [%t/%p]: %msg%n + + + + + + + + + + + + + + + + + + + + + + + +