Discord player lookups

This commit is contained in:
Braydon 2024-04-24 18:01:08 -04:00
parent e6a12c3675
commit 0ba927b7d1
5 changed files with 260 additions and 2 deletions

@ -11,10 +11,11 @@
<!-- Properties -->
<properties>
<java.version>8</java.version>
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<slf4j.version>2.1.0-alpha1</slf4j.version>
</properties>
<build>
@ -100,6 +101,8 @@
<version>1.18.32</version>
<scope>provided</scope>
</dependency>
<!-- Libraries -->
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
@ -117,5 +120,19 @@
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

@ -1,8 +1,62 @@
package cc.restfulmc.bot;
import cc.restfulmc.bot.command.CommandManager;
import cc.restfulmc.sdk.client.ClientConfig;
import cc.restfulmc.sdk.client.RESTfulMCClient;
import lombok.Getter;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.SelfUser;
/**
* @author Braydon
*/
@Slf4j(topic = "RESTfulMC Bot") @Getter
public final class DiscordBot {
public static void main(String[] args) { }
/**
* The JDA bot instance.
*/
private JDA jda;
@SneakyThrows
public DiscordBot() {
String token = System.getenv("BOT_TOKEN");
if (token == null) { // Missing BOT_TOKEN
throw new NullPointerException("Missing BOT_TOKEN environment variable");
}
jda = JDABuilder.createLight(token)
.setActivity(Activity.watching("Minecraft servers"))
.build();
jda.awaitReady(); // Wait for JDA to become ready
// Setup the API SDK
RESTfulMCClient apiClient = new RESTfulMCClient(ClientConfig.defaultConfig());
// Commands
new CommandManager(this, apiClient);
SelfUser self = jda.getSelfUser();
log.info("Logged in as bot {} ({})", self.getAsTag(), self.getId());
// Add a cleanup hook to cleanup the bot when the JVM shuts down
Runtime.getRuntime().addShutdownHook(new Thread(this::cleanup));
}
/**
* Cleanup the bot.
*/
public void cleanup() {
log.info("Cleaning up...");
jda.shutdown();
jda = null;
log.info("Goodbye!");
}
public static void main(@NonNull String[] args) {
new DiscordBot();
}
}

@ -0,0 +1,55 @@
package cc.restfulmc.bot.command;
import cc.restfulmc.bot.DiscordBot;
import cc.restfulmc.bot.command.impl.PlayerCommand;
import cc.restfulmc.sdk.client.RESTfulMCClient;
import lombok.NonNull;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author Braydon
*/
public final class CommandManager extends ListenerAdapter {
private final List<SlashCommand> commands = Collections.synchronizedList(new ArrayList<>());
public CommandManager(@NonNull DiscordBot bot, @NonNull RESTfulMCClient apiClient) {
registerCommand(new PlayerCommand(apiClient));
// Update the commands on Discord
CommandListUpdateAction updateCommands = bot.getJda().updateCommands();
for (SlashCommand command : commands) {
updateCommands.addCommands(Commands.slash(command.getName(), command.getDescription()).addOptions(command.getOptions()));
}
updateCommands.queue();
// Handle registered events
bot.getJda().addEventListener(this);
}
@Override
public void onSlashCommandInteraction(@NonNull SlashCommandInteractionEvent event) {
for (SlashCommand command : commands) {
if (command.getName().equals(event.getName())) {
event.deferReply().queue(); // Inform Discord we received the command
command.onExecute(event.getUser(), event.getMember(), event); // Invoke the command
break;
}
}
}
/**
* Register a slash command.
*
* @param command the command to register
*/
public void registerCommand(@NonNull SlashCommand command) {
commands.add(command);
}
}

@ -0,0 +1,62 @@
package cc.restfulmc.bot.command;
import cc.restfulmc.sdk.exception.RESTfulMCAPIException;
import lombok.Getter;
import lombok.NonNull;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
/**
* A wrapper for slash commands.
*
* @author Braydon
*/
@Getter
public abstract class SlashCommand {
/**
* The name of this command.
*/
@NonNull private final String name;
/**
* The description of this command.
*/
@NonNull private final String description;
/**
* Optional options for this command.
*/
private final OptionData[] options;
public SlashCommand(@NonNull String name, @NonNull String description, OptionData... options) {
this.name = name;
this.description = description;
this.options = options;
}
/**
* Invoked when this command is executed.
*
* @param user the executing user
* @param member the executing member, null if in dms
* @param event the event that triggered this command
*/
public abstract void onExecute(@NonNull User user, Member member, @NonNull SlashCommandInteractionEvent event);
/**
* Reply to an interaction with an API error.
*
* @param event the event to reply to
* @param apiError the api error to reply with
*/
protected final void replyWithApiError(@NonNull SlashCommandInteractionEvent event, @NonNull RESTfulMCAPIException apiError) {
event.getHook().sendMessageEmbeds(new EmbedBuilder()
.setColor(0xAA0000)
.setTitle(apiError.getCode() + " | API Error")
.setDescription(apiError.getMessage())
.build()).queue();
}
}

@ -0,0 +1,70 @@
package cc.restfulmc.bot.command.impl;
import cc.restfulmc.bot.command.SlashCommand;
import cc.restfulmc.sdk.client.RESTfulMCClient;
import cc.restfulmc.sdk.exception.RESTfulMCAPIException;
import cc.restfulmc.sdk.response.Player;
import lombok.NonNull;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
/**
* @author Braydon
*/
public final class PlayerCommand extends SlashCommand {
/**
* The API client to use for lookups.
*/
@NonNull private final RESTfulMCClient apiClient;
public PlayerCommand(@NonNull RESTfulMCClient apiClient) {
super("player", "Lookup a player by their username or UUID",
new OptionData(OptionType.STRING, "query", "The player username or UUID").setRequired(true)
);
this.apiClient = apiClient;
}
/**
* Invoked when this command is executed.
*
* @param user the executing user
* @param member the executing member, null if in dms
* @param event the event that triggered this command
*/
@Override
public void onExecute(@NonNull User user, Member member, @NonNull SlashCommandInteractionEvent event) {
OptionMapping query = event.getOption("query"); // Get the query
assert query != null;
String queryValue = query.getAsString(); // Get the query value
// Lookup the requested player by the given query
apiClient.async().getPlayer(queryValue).whenComplete((player, ex) -> {
// Failed to lookup the player, handle the error
if (ex != null) {
if (ex.getCause() instanceof RESTfulMCAPIException apiError) {
replyWithApiError(event, apiError);
} else { // Only print real errors
ex.printStackTrace();
}
return;
}
// Respond with the player
long cached = player.getCached(); // The timestamp the player was cached
event.getHook().sendMessageEmbeds(new EmbedBuilder()
.setColor(0x55FF55)
.setTitle("<:grass_block:1232798337300828181> Player Response", "https://api.restfulmc.cc/player/" + queryValue)
.addField("Unique ID", player.getUniqueId().toString(), true)
.addField("Username", player.getUsername(), true)
.addField("Legacy", player.isLegacy() ? "Yes" : "No", true)
.addField("Cached", cached == -1L ? "No" : "Yes, <t:" + (cached / 1000L) + ":R>", true)
.setThumbnail(player.getSkin().getParts().get(Player.SkinPart.HEAD))
.setFooter("Requested by " + user.getName() + " | " + user.getId(), user.getEffectiveAvatarUrl())
.build()).queue();
});
}
}