Add a command to view your owned license

This commit is contained in:
Braydon 2023-06-02 05:10:41 -04:00
parent 5133ab688f
commit 68591c90c9
3 changed files with 110 additions and 2 deletions

@ -94,6 +94,18 @@ public class License {
*/ */
@NonNull private Date created; @NonNull private Date created;
/**
* Check if the Discord user
* with the given snowflake
* owns this license.
*
* @param snowflake the snowflake
* @return true if owns, otherwise false
*/
public boolean isOwner(long snowflake) {
return ownerSnowflake == snowflake;
}
/** /**
* Check if this license has expired. * Check if this license has expired.
* <p> * <p>

@ -1,37 +1,62 @@
package me.braydon.license.service; package me.braydon.license.service;
import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.braydon.license.common.MiscUtils;
import me.braydon.license.common.TimeUtils; import me.braydon.license.common.TimeUtils;
import me.braydon.license.model.License; import me.braydon.license.model.License;
import me.braydon.license.repository.LicenseRepository;
import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.OnlineStatus; import net.dv8tion.jda.api.OnlineStatus;
import net.dv8tion.jda.api.entities.Activity; import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.MessageEmbed; 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.channel.concrete.TextChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.exceptions.ErrorResponseException; 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.requests.ErrorResponse; import net.dv8tion.jda.api.requests.ErrorResponse;
import net.dv8tion.jda.api.requests.GatewayIntent; import net.dv8tion.jda.api.requests.GatewayIntent;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.info.BuildProperties; import org.springframework.boot.info.BuildProperties;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.awt.*;
import java.util.Objects;
import java.util.Optional;
/** /**
* @author Braydon * @author Braydon
*/ */
@Service @Service
@Slf4j(topic = "Discord") @Slf4j(topic = "Discord")
public final class DiscordService { public final class DiscordService {
/**
* The {@link LicenseRepository} to use.
*/
@Nonnull private final LicenseRepository licenseRepository;
/** /**
* The version of this Springboot application. * The version of this Springboot application.
*/ */
@NonNull private final String applicationVersion; @NonNull private final String applicationVersion;
/**
* The salt to use for hashing license keys.
*/
@Value("${salts.licenses}")
@NonNull private String licensesSalt;
/** /**
* The name of this Springboot application. * The name of this Springboot application.
*/ */
@ -92,7 +117,9 @@ public final class DiscordService {
private JDA jda; private JDA jda;
@Autowired @Autowired
public DiscordService(@NonNull BuildProperties buildProperties) { public DiscordService(@NonNull LicenseRepository licenseRepository, @NonNull BuildProperties buildProperties) {
this.licenseRepository = licenseRepository;
;
this.applicationVersion = buildProperties.getVersion(); this.applicationVersion = buildProperties.getVersion();
} }
@ -111,6 +138,7 @@ public final class DiscordService {
GatewayIntent.GUILD_MEMBERS GatewayIntent.GUILD_MEMBERS
).setStatus(OnlineStatus.DO_NOT_DISTURB) ).setStatus(OnlineStatus.DO_NOT_DISTURB)
.setActivity(Activity.watching("your licenses")) .setActivity(Activity.watching("your licenses"))
.addEventListeners(new EventHandler())
.build(); .build();
jda.awaitReady(); // Await JDA to be ready jda.awaitReady(); // Await JDA to be ready
@ -118,6 +146,13 @@ public final class DiscordService {
log.info("Logged into {} in {}ms", log.info("Logged into {} in {}ms",
jda.getSelfUser().getAsTag(), System.currentTimeMillis() - before jda.getSelfUser().getAsTag(), System.currentTimeMillis() - before
); );
// Registering slash commands
jda.updateCommands().addCommands(
Commands.slash("license", "Manage one of your licenses")
.addOption(OptionType.STRING, "key", "The license key", true)
.addOption(OptionType.STRING, "product", "The product the license is for", true)
).queue();
} }
/** /**
@ -205,4 +240,65 @@ public final class DiscordService {
applicationName, applicationVersion, TimeUtils.dateTime() applicationName, applicationVersion, TimeUtils.dateTime()
)).build(); )).build();
} }
/**
* The event handler for the bot.
*/
public class EventHandler extends ListenerAdapter {
@Override
public void onSlashCommandInteraction(@NonNull SlashCommandInteractionEvent event) {
User user = event.getUser(); // The command executor
// Handle the license command
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...
// License lookup
Optional<License> 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" : "<t:" + expires + ":R>",
true
)
.addField("Uses", String.valueOf(license.getUses()), true)
.addField("Last Used",
lastUsed == -1L ? "Never" : "<t:" + lastUsed + ":R>",
true
)
.addField("IPs",
license.getIps().size() + "/" + license.getIpLimit(),
true
)
.addField("HWIDs",
license.getHwids().size() + "/" + license.getHwidLimit(),
true
)
.addField("Created",
"<t:" + (license.getCreated().getTime() / 1000L) + ":R>",
true
)
)).queue();
}
}
}
} }

@ -143,7 +143,7 @@ public final class LicenseService {
license.getOwnerName() == null ? "N/A" : license.getOwnerName(), license.getOwnerName() == null ? "N/A" : license.getOwnerName(),
true true
) )
.addField("Expires", .addField("Expiration",
expires == -1L ? "Never" : "<t:" + expires + ":R>", expires == -1L ? "Never" : "<t:" + expires + ":R>",
true true
) )