org.springframework.boot
diff --git a/src/main/java/me/braydon/license/service/DiscordService.java b/src/main/java/me/braydon/license/service/DiscordService.java
index 611a0f5..b69104e 100644
--- a/src/main/java/me/braydon/license/service/DiscordService.java
+++ b/src/main/java/me/braydon/license/service/DiscordService.java
@@ -1,5 +1,7 @@
package me.braydon.license.service;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct;
import lombok.Getter;
@@ -18,11 +20,14 @@ import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
+import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
+import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
+import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.requests.ErrorResponse;
import net.dv8tion.jda.api.requests.GatewayIntent;
import org.mindrot.jbcrypt.BCrypt;
@@ -32,8 +37,10 @@ import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Service;
import java.awt.*;
+import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
+import java.util.concurrent.TimeUnit;
/**
* @author Braydon
@@ -41,6 +48,9 @@ import java.util.Optional;
@Service
@Slf4j(topic = "Discord")
public final class DiscordService {
+ private static final String CLEAR_IPS_BUTTON_ID = "clearIps";
+ private static final String CLEAR_HWIDS_BUTTON_ID = "clearHwids";
+
/**
* The {@link LicenseRepository} to use.
*/
@@ -116,10 +126,22 @@ public final class DiscordService {
*/
private JDA jda;
+ /**
+ * Cached licenses for messages.
+ *
+ * When a license is looked up by it's owner, the
+ * response message is cached (key is the message snowflake)
+ * for 5 minutes. This is so we're able to get the message
+ * an action was performed on, as well as action timeouts.
+ *
+ */
+ private final Cache cachedLicenses = CacheBuilder.newBuilder()
+ .expireAfterWrite(5L, TimeUnit.MINUTES)
+ .build();
+
@Autowired
public DiscordService(@NonNull LicenseRepository licenseRepository, @NonNull BuildProperties buildProperties) {
this.licenseRepository = licenseRepository;
- ;
this.applicationVersion = buildProperties.getVersion();
}
@@ -253,51 +275,109 @@ public final class DiscordService {
if (event.getName().equals("license")) {
String key = Objects.requireNonNull(event.getOption("key")).getAsString();
String product = Objects.requireNonNull(event.getOption("product")).getAsString();
- event.deferReply().queue(); // Send thinking...
+ event.deferReply(true).queue(); // Send thinking...
// License lookup
- Optional optionalLicense = licenseRepository.getLicense(BCrypt.hashpw(key, licensesSalt), product);
- if (optionalLicense.isEmpty() // License not found or owned by someone else
- || (!optionalLicense.get().isOwner(user.getIdLong()))) {
+ try {
+ Optional optionalLicense = licenseRepository.getLicense(BCrypt.hashpw(key, licensesSalt), product);
+ if (optionalLicense.isEmpty() // License not found or owned by someone else
+ || (!optionalLicense.get().isOwner(user.getIdLong()))) {
+ event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
+ .setColor(Color.RED)
+ .setTitle("License not found")
+ .setDescription("Could not locate the license you were looking for")
+ )).queue(); // Send the error message
+ return;
+ }
+ License license = optionalLicense.get(); // The found license
+ String obfuscateKey = MiscUtils.obfuscateKey(key); // Obfuscate the key
+ long expires = license.isPermanent() ? -1L : license.getExpires().getTime() / 1000L;
+ long lastUsed = license.getLastUsed() == null ? -1L : license.getExpires().getTime() / 1000L;
+ event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
+ .setColor(Color.BLUE)
+ .setTitle("Your License")
+ .addField("License", "`" + obfuscateKey + "`", true)
+ .addField("Product", license.getProduct(), true)
+ .addField("Description", license.getDescription(), true)
+ .addField("Expiration",
+ expires == -1L ? "Never" : "",
+ true
+ )
+ .addField("Uses", String.valueOf(license.getUses()), true)
+ .addField("Last Used",
+ lastUsed == -1L ? "Never" : "",
+ true
+ )
+ .addField("IPs",
+ license.getIps().size() + "/" + license.getIpLimit(),
+ true
+ )
+ .addField("HWIDs",
+ license.getHwids().size() + "/" + license.getHwidLimit(),
+ true
+ )
+ .addField("Created",
+ "",
+ true
+ )
+ )).addActionRow( // Buttons
+ Button.danger(CLEAR_IPS_BUTTON_ID, "Clear IPs")
+ .withEmoji(Emoji.fromUnicode("🗑️")),
+ Button.danger(CLEAR_HWIDS_BUTTON_ID, "Clear HWIDs")
+ .withEmoji(Emoji.fromUnicode("🗑️"))
+ ).queue(message -> cachedLicenses.put(message.getIdLong(), license)); // Cache the license for the message
+ } catch (Exception ex) {
event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
.setColor(Color.RED)
- .setTitle("License not found")
- .setDescription("Could not locate the license you were looking for")
+ .setTitle("Lookup Failed")
+ .setDescription("More information has been logged")
+ )).queue(); // Send the error message
+ ex.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public void onButtonInteraction(@NonNull ButtonInteractionEvent event) {
+ User user = event.getUser(); // The user who clicked the button
+ String componentId = event.getComponentId(); // The button id
+
+ // License Actions
+ boolean clearIps = componentId.equals(CLEAR_IPS_BUTTON_ID);
+ boolean clearHwids = componentId.equals(CLEAR_HWIDS_BUTTON_ID);
+ if (clearIps || clearHwids) {
+ event.deferReply(true).queue(); // Send thinking...
+ License license = cachedLicenses.getIfPresent(event.getMessageIdLong()); // Get the cached license
+ if (license == null || (!license.isOwner(user.getIdLong()))) { // License not found or owned by someone else
+ event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
+ .setColor(Color.RED)
+ .setTitle("License Action Failed")
+ .setDescription("The license couldn't be found or the action timed out")
)).queue(); // Send the error message
return;
}
- License license = optionalLicense.get(); // The found license
- String obfuscateKey = MiscUtils.obfuscateKey(key); // Obfuscate the key
- long expires = license.isPermanent() ? -1L : license.getExpires().getTime() / 1000L;
- long lastUsed = license.getLastUsed() == null ? -1L : license.getExpires().getTime() / 1000L;
- event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
- .setColor(Color.BLUE)
- .setTitle("Your License")
- .addField("License", "`" + obfuscateKey + "`", true)
- .addField("Product", license.getProduct(), true)
- .addField("Description", license.getDescription(), true)
- .addField("Expiration",
- expires == -1L ? "Never" : "",
- true
- )
- .addField("Uses", String.valueOf(license.getUses()), true)
- .addField("Last Used",
- lastUsed == -1L ? "Never" : "",
- true
- )
- .addField("IPs",
- license.getIps().size() + "/" + license.getIpLimit(),
- true
- )
- .addField("HWIDs",
- license.getHwids().size() + "/" + license.getHwidLimit(),
- true
- )
- .addField("Created",
- "",
- true
- )
- )).queue();
+ try {
+ // Clear IPs
+ if (clearIps) {
+ license.setIps(new HashSet<>());
+ }
+ // Clear HWIDs
+ if (clearHwids) {
+ license.setHwids(new HashSet<>());
+ }
+ licenseRepository.save(license); // Save the license
+ event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
+ .setColor(Color.GREEN)
+ .setTitle("Cleared " + (clearIps ? "IP" : "HWID") + "s")
+ )).queue(); // Inform action success
+ } catch (Exception ex) {
+ event.getHook().sendMessageEmbeds(buildEmbed(new EmbedBuilder()
+ .setColor(Color.RED)
+ .setTitle("License Action Failed")
+ .setDescription("More information has been logged")
+ )).queue(); // Send the error message
+ ex.printStackTrace();
+ }
}
}
}