Skip to content
This repository was archived by the owner on Dec 1, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions src/main/java/net/javadiscord/javabot2/Bot.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.IndexModel;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.Indexes;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import net.javadiscord.javabot2.command.SlashCommandListener;
import net.javadiscord.javabot2.config.BotConfig;
import org.bson.BsonString;
import org.bson.Document;
import org.javacord.api.DiscordApi;
import org.javacord.api.DiscordApiBuilder;
import org.javacord.api.entity.intent.Intent;

import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

Expand All @@ -25,7 +33,12 @@ public class Bot {
/**
* A thread-safe MongoDB client that can be used to interact with MongoDB.
*/
public static MongoClient mongo;
public static MongoClient mongoClient;

/**
* The single Mongo database where all bot data is stored.
*/
public static MongoDatabase mongoDb;

/**
* The bot's configuration.
Expand All @@ -47,11 +60,15 @@ private Bot() {}
public static void main(String[] args) {
initDataSources();
asyncPool = Executors.newScheduledThreadPool(config.getSystems().getAsyncPoolSize());
DiscordApi api = new DiscordApiBuilder().setToken(config.getSystems().getDiscordBotToken()).login().join();
DiscordApi api = new DiscordApiBuilder()
.setToken(config.getSystems().getDiscordBotToken())
.setAllIntentsExcept(Intent.GUILD_MESSAGE_TYPING, Intent.GUILD_PRESENCES, Intent.GUILD_VOICE_STATES)
.login().join();
config.loadGuilds(api.getServers()); // Once we've logged in, load all guild config files.
config.flush(); // Flush to save any new config files that are generated for new guilds.
SlashCommandListener commandListener = new SlashCommandListener(
api,
args.length > 0 && args[0].equalsIgnoreCase("--register-commands"),
"commands/moderation.yaml"
);
api.addSlashCommandCreateListener(commandListener);
Expand All @@ -75,6 +92,14 @@ private static void initDataSources() {
hikariConfig.setMaximumPoolSize(hikariConfigSource.getMaximumPoolSize());
hikariConfig.setConnectionInitSql(hikariConfigSource.getConnectionInitSql());
hikariDataSource = new HikariDataSource(hikariConfig);
mongo = new MongoClient(new MongoClientURI(config.getSystems().getMongoDatabaseUrl()));
mongoDb = initMongoDatabase();
}

private static MongoDatabase initMongoDatabase() {
mongoClient = new MongoClient(new MongoClientURI(config.getSystems().getMongoDatabaseUrl()));
var db = mongoClient.getDatabase("javabot");
var warnCollection = db.getCollection("warn");
warnCollection.createIndex(Indexes.ascending("userId"), new IndexOptions().unique(false));
return db;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package net.javadiscord.javabot2.command;

import lombok.Getter;
import org.javacord.api.interaction.callback.InteractionImmediateResponseBuilder;

import java.util.function.Supplier;

Expand All @@ -16,13 +15,14 @@ public class ResponseException extends Exception {
* of an exception.
*/
@Getter
private final InteractionImmediateResponseBuilder responseBuilder;
private final Responses.ResponseBuilder responseBuilder;

/**
* Constructs the exception.
* @param responseBuilder The response builder to use.
*/
public ResponseException(InteractionImmediateResponseBuilder responseBuilder) {
public ResponseException(Responses.ResponseBuilder responseBuilder) {
super(responseBuilder.getMessage());
this.responseBuilder = responseBuilder;
}

Expand All @@ -33,7 +33,7 @@ public ResponseException(InteractionImmediateResponseBuilder responseBuilder) {
* @return The exception supplier.
*/
public static Supplier<ResponseException> warning(String message) {
return () -> new ResponseException(Responses.deferredWarningBuilder().message(message).build());
return () -> new ResponseException(Responses.deferredWarningBuilder().message(message));
}

/**
Expand All @@ -43,6 +43,6 @@ public static Supplier<ResponseException> warning(String message) {
* @return The exception supplier.
*/
public static Supplier<ResponseException> error(String message) {
return () -> new ResponseException(Responses.deferredErrorBuilder().message(message).build());
return () -> new ResponseException(Responses.deferredErrorBuilder().message(message));
}
}
4 changes: 4 additions & 0 deletions src/main/java/net/javadiscord/javabot2/command/Responses.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ public ResponseBuilder message(String message) {
return this;
}

public String getMessage() {
return this.message;
}

/**
* Makes this response publicly visible, i.e. not ephemeral.
* @return The response builder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,30 +21,39 @@
@Slf4j
public final class SlashCommandListener implements SlashCommandCreateListener {
/**
* The set of all slash command handlers, mapped by their ids.
* The set of all slash command handlers, mapped by their names.
*/
private final Map<Long, SlashCommandHandler> commandHandlers = new HashMap<>();
private final Map<String, SlashCommandHandler> commandHandlers;

/**
* Constructs a new slash command listener using the given Discord api, and
* loads commands from configuration YAML files according to the list of
* resources.
* @param api The Discord api to use.
* @param sendUpdate Set to true if we should update the slash commands in
* the API, or false if we should just assume it's done.
* @param resources The list of classpath resources to load commands from.
*/
public SlashCommandListener(DiscordApi api, String... resources) {
registerSlashCommands(api, resources)
.thenAcceptAsync(commandHandlers::putAll);
public SlashCommandListener(DiscordApi api, boolean sendUpdate, String... resources) {
if (sendUpdate) {
this.commandHandlers = new HashMap<>();
registerSlashCommands(api, resources)
.thenAcceptAsync(commandHandlers::putAll)
.thenRun(() -> log.info("Registered all slash commands."));
} else {
this.commandHandlers = initializeHandlers(CommandDataLoader.load(resources));
log.info("Registered all slash commands.");
}
}

@Override
public void onSlashCommandCreate(SlashCommandCreateEvent event) {
var handler = commandHandlers.get(event.getSlashCommandInteraction().getCommandId());
var handler = commandHandlers.get(event.getSlashCommandInteraction().getCommandName());
if (handler != null) {
try {
handler.handle(event.getSlashCommandInteraction()).respond();
} catch (ResponseException e) {
e.getResponseBuilder().respond();
e.getResponseBuilder().respond(event.getSlashCommandInteraction());
}
} else {
Responses.warningBuilder(event)
Expand All @@ -54,25 +63,21 @@ public void onSlashCommandCreate(SlashCommandCreateEvent event) {
}
}

private CompletableFuture<Map<Long, SlashCommandHandler>> registerSlashCommands(DiscordApi api, String... resources) {
private CompletableFuture<Map<String, SlashCommandHandler>> registerSlashCommands(DiscordApi api, String... resources) {
var commandConfigs = CommandDataLoader.load(resources);
var handlers = initializeHandlers(commandConfigs);
List<SlashCommandBuilder> commandBuilders = Arrays.stream(commandConfigs)
.map(CommandConfig::toData).toList();
return deleteAllSlashCommands(api)
.thenComposeAsync(unused -> api.bulkOverwriteGlobalSlashCommands(commandBuilders))
.thenComposeAsync(slashCommands -> {
Map<Long, SlashCommandHandler> handlersById = new HashMap<>();
Map<String, Long> nameToId = new HashMap<>();
for (var slashCommand : slashCommands) {
var handler = handlers.get(slashCommand.getName());
handlersById.put(slashCommand.getId(), handler);
nameToId.put(slashCommand.getName(), slashCommand.getId());
}
log.info("Registered all slash commands.");
return updatePermissions(api, commandConfigs, nameToId)
.thenRun(() -> log.info("Updated permissions for all slash commands."))
.thenApplyAsync(unused -> handlersById);
.thenApplyAsync(unused -> handlers);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import lombok.Data;
import org.javacord.api.interaction.SlashCommandBuilder;

import java.util.Arrays;
import java.util.Objects;

/**
Expand Down Expand Up @@ -47,18 +46,6 @@ public SlashCommandBuilder toData() {
return builder;
}

@Override
public String toString() {
return "CommandConfig{" +
"name='" + name + '\'' +
", description='" + description + '\'' +
", options=" + Arrays.toString(options) +
", subCommands=" + Arrays.toString(subCommands) +
", subCommandGroups=" + Arrays.toString(subCommandGroups) +
", handler=" + handler +
'}';
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.javadiscord.javabot2.command.data;

import lombok.Data;
import org.javacord.api.interaction.SlashCommandOptionChoice;
import org.javacord.api.interaction.SlashCommandOptionChoiceBuilder;

/**
* DTO for a choice that a slash command option can have.
*/
@Data
public class OptionChoiceConfig {
private String name;
private String value;

/**
* Converts this choice data into a Javacord object for use with the API.
* @return The Javacord option choice object.
*/
public SlashCommandOptionChoice toData() {
return new SlashCommandOptionChoiceBuilder()
.setName(name)
.setValue(value)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import lombok.Data;
import org.javacord.api.interaction.SlashCommandOptionBuilder;
import org.javacord.api.interaction.SlashCommandOptionChoiceBuilder;
import org.javacord.api.interaction.SlashCommandOptionType;

import java.util.Arrays;

/**
* Simple DTO representing an option that can be given to a Discord slash
* command or subcommand.
Expand All @@ -15,26 +16,21 @@ public class OptionConfig {
private String description;
private String type;
private boolean required;
private OptionChoiceConfig[] choices;

/**
* Converts this config data into data that's ready for the Discord API.
* @return The prepared data.
*/
public SlashCommandOptionBuilder toData() {
return new SlashCommandOptionBuilder()
var builder = new SlashCommandOptionBuilder()
.setType(SlashCommandOptionType.valueOf(this.type.toUpperCase()))
.setName(this.name)
.setDescription(this.description)
.setRequired(this.required);
}

@Override
public String toString() {
return "OptionConfig{" +
"name='" + name + '\'' +
", description='" + description + '\'' +
", type='" + type + '\'' +
", required=" + required +
'}';
if (this.choices != null && this.choices.length > 0) {
builder.setChoices(Arrays.stream(this.choices).map(OptionChoiceConfig::toData).toList());
}
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import org.javacord.api.interaction.SlashCommandOptionBuilder;
import org.javacord.api.interaction.SlashCommandOptionType;

import java.util.Arrays;

/**
* Simple DTO for a Discord subcommand.
*/
Expand All @@ -31,13 +29,4 @@ public SlashCommandOptionBuilder toData() {
}
return builder;
}

@Override
public String toString() {
return "SubCommandConfig{" +
"name='" + name + '\'' +
", description='" + description + '\'' +
", options=" + Arrays.toString(options) +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
import org.javacord.api.interaction.SlashCommandOptionBuilder;
import org.javacord.api.interaction.SlashCommandOptionType;

import java.util.Arrays;

/**
* Simple DTO for a group of Discord subcommands.
*/
Expand All @@ -31,13 +29,4 @@ public SlashCommandOptionBuilder toData() {
}
return builder;
}

@Override
public String toString() {
return "SubCommandGroupConfig{" +
"name='" + name + '\'' +
", description='" + description + '\'' +
", subCommands=" + Arrays.toString(subCommands) +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class SystemsConfig {
/**
* The URL used to log in to the MongoDB instance which this bot uses.
*/
private String mongoDatabaseUrl = "mongodb://root:example@localhost:27171/javabot";
private String mongoDatabaseUrl = "mongodb://root:example@localhost:27171";

/**
* The number of threads to allocate to the bot's general purpose async
Expand Down
Loading