From c71ab845b9e9098c15cd55528f6220427624619d Mon Sep 17 00:00:00 2001 From: Paul Reilly Date: Mon, 28 Aug 2023 01:53:48 -0500 Subject: [PATCH 1/5] Fully implement Discord bot --- Patchwork/build.gradle | 2 + .../java/fns/patchwork/base/Registration.java | 12 +- .../fns/patchwork/config/Configuration.java | 3 +- .../config/WrappedTomlConfiguration.java | 184 ++++++++++++++++++ .../java/fns/patchwork/event/EventBus.java | 2 +- .../java/fns/patchwork/particle/Trail.java | 3 +- .../patchwork/{api => provider}/Context.java | 3 +- .../{data => registry}/ConfigRegistry.java | 2 +- .../{data => registry}/EventRegistry.java | 2 +- .../{data => registry}/GroupRegistry.java | 2 +- .../{data => registry}/ModuleRegistry.java | 2 +- .../ServiceTaskRegistry.java | 2 +- .../{data => registry}/UserRegistry.java | 2 +- .../{api => serializer}/Serializable.java | 2 +- .../patchwork/utils/InterpolationUtils.java | 21 +- .../src/main/java/fns/veritas/Aggregate.java | 32 ++- .../src/main/java/fns/veritas/Veritas.java | 5 +- .../java/fns/veritas/bukkit/BukkitNative.java | 2 +- .../fns/veritas/bukkit/ServerListener.java | 23 ++- .../java/fns/veritas/client/BotClient.java | 22 ++- .../java/fns/veritas/client/BotConfig.java | 4 +- .../java/fns/veritas/cmd/HelpCommand.java | 69 +++++++ .../java/fns/veritas/cmd/ListCommand.java | 102 ++++++++++ .../main/java/fns/veritas/cmd/TpsCommand.java | 64 ++++++ .../java/fns/veritas/cmd/base/BotCommand.java | 35 ++-- .../veritas/cmd/base/BotCommandHandler.java | 139 +++++++++++++ .../java/fns/veritas/messaging/Embed.java | 28 +++ .../fns/veritas/messaging/EmbedWrapper.java | 63 ++++++ .../messaging/SimpleMessageWrapper.java | 72 +++++++ Veritas/src/main/resources/ExampleCommand.txt | 33 ++++ Veritas/src/main/resources/commands/help.json | 4 + Veritas/src/main/resources/commands/list.json | 12 ++ Veritas/src/main/resources/commands/tps.json | 4 + 33 files changed, 892 insertions(+), 65 deletions(-) create mode 100644 Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java rename Patchwork/src/main/java/fns/patchwork/{api => provider}/Context.java (99%) rename Patchwork/src/main/java/fns/patchwork/{data => registry}/ConfigRegistry.java (98%) rename Patchwork/src/main/java/fns/patchwork/{data => registry}/EventRegistry.java (98%) rename Patchwork/src/main/java/fns/patchwork/{data => registry}/GroupRegistry.java (98%) rename Patchwork/src/main/java/fns/patchwork/{data => registry}/ModuleRegistry.java (98%) rename Patchwork/src/main/java/fns/patchwork/{data => registry}/ServiceTaskRegistry.java (99%) rename Patchwork/src/main/java/fns/patchwork/{data => registry}/UserRegistry.java (99%) rename Patchwork/src/main/java/fns/patchwork/{api => serializer}/Serializable.java (98%) create mode 100644 Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java create mode 100644 Veritas/src/main/java/fns/veritas/cmd/ListCommand.java create mode 100644 Veritas/src/main/java/fns/veritas/cmd/TpsCommand.java rename Patchwork/src/main/java/fns/patchwork/api/Interpolator.java => Veritas/src/main/java/fns/veritas/cmd/base/BotCommand.java (65%) create mode 100644 Veritas/src/main/java/fns/veritas/cmd/base/BotCommandHandler.java create mode 100644 Veritas/src/main/java/fns/veritas/messaging/Embed.java create mode 100644 Veritas/src/main/java/fns/veritas/messaging/EmbedWrapper.java create mode 100644 Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java create mode 100644 Veritas/src/main/resources/ExampleCommand.txt create mode 100644 Veritas/src/main/resources/commands/help.json create mode 100644 Veritas/src/main/resources/commands/list.json create mode 100644 Veritas/src/main/resources/commands/tps.json diff --git a/Patchwork/build.gradle b/Patchwork/build.gradle index 6ce2389..5b8f357 100644 --- a/Patchwork/build.gradle +++ b/Patchwork/build.gradle @@ -8,6 +8,8 @@ repositories { dependencies { library 'io.projectreactor:reactor-core:3.5.4' library 'io.github.classgraph:classgraph:4.8.162' + library 'org.tomlj:tomlj:1.1.0' + library 'com.google.code.gson:gson:2.8.9' api 'org.slf4j:slf4j-api:1.7.36' testImplementation platform('org.junit:junit-bom:5.9.1') diff --git a/Patchwork/src/main/java/fns/patchwork/base/Registration.java b/Patchwork/src/main/java/fns/patchwork/base/Registration.java index b619965..fc159dc 100644 --- a/Patchwork/src/main/java/fns/patchwork/base/Registration.java +++ b/Patchwork/src/main/java/fns/patchwork/base/Registration.java @@ -23,12 +23,12 @@ package fns.patchwork.base; -import fns.patchwork.data.ConfigRegistry; -import fns.patchwork.data.EventRegistry; -import fns.patchwork.data.GroupRegistry; -import fns.patchwork.data.ModuleRegistry; -import fns.patchwork.data.ServiceTaskRegistry; -import fns.patchwork.data.UserRegistry; +import fns.patchwork.registry.ConfigRegistry; +import fns.patchwork.registry.EventRegistry; +import fns.patchwork.registry.GroupRegistry; +import fns.patchwork.registry.ModuleRegistry; +import fns.patchwork.registry.ServiceTaskRegistry; +import fns.patchwork.registry.UserRegistry; /** * This class is a holder for each registry in the data package. diff --git a/Patchwork/src/main/java/fns/patchwork/config/Configuration.java b/Patchwork/src/main/java/fns/patchwork/config/Configuration.java index 4bcb347..a5cc16f 100644 --- a/Patchwork/src/main/java/fns/patchwork/config/Configuration.java +++ b/Patchwork/src/main/java/fns/patchwork/config/Configuration.java @@ -23,9 +23,8 @@ package fns.patchwork.config; -import fns.patchwork.api.Context; +import fns.patchwork.provider.Context; import fns.patchwork.provider.ContextProvider; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; import java.io.File; diff --git a/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java new file mode 100644 index 0000000..585ed4e --- /dev/null +++ b/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java @@ -0,0 +1,184 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.patchwork.config; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Unmodifiable; +import org.tomlj.Toml; +import org.tomlj.TomlParseResult; + +// TODO: Finish implementation +public class WrappedTomlConfiguration implements Configuration +{ + private final Map previousValues = new HashMap<>(); + private final TomlParseResult toml; + private final File file; + + + public WrappedTomlConfiguration(final JavaPlugin plugin, final File file) throws IOException + { + if (!file.exists() && file.createNewFile()) + { + plugin.saveResource(file.getName(), true); + } + + this.toml = Toml.parse(Path.of(file.toURI())); + this.file = file; + } + + @Override + public void save() throws IOException + { + // Create a backup file + final File backup = new File(this.file.getParentFile(), this.file.getName() + ".bak"); + if (backup.exists() && !Files.deleteIfExists(Path.of(backup.toURI()))) + { + throw new IOException("Failed to delete existing backup file: " + backup.getName()); + } + + // Serialize the current configuration to a temporary file + final File tempFile = new File(this.file.getParentFile(), this.file.getName() + ".temp"); + try (FileWriter tempFileWriter = new FileWriter(tempFile)) + { + // Convert the updated TomlTable to TOML format and write it to the temporary file + String tomlString = this.toml.toToml(); + tempFileWriter.write(tomlString); + } + + // Compare the new configuration with the previous one + TomlParseResult newToml = Toml.parse(Path.of(tempFile.toURI())); + for (Map.Entry entry : newToml.toMap().entrySet()) + { + String key = entry.getKey(); + Object newValue = entry.getValue(); + Object oldValue = previousValues.get(key); + + if (oldValue == null || !oldValue.equals(newValue)) + { + // Value has changed, update it + this.toml.toMap().replace(key, newValue); + previousValues.put(key, newValue); + } + } + + // Save the updated configuration to the original file + try (FileWriter fileWriter = new FileWriter(this.file)) + { + // Convert the updated TomlTable to TOML format and write it to the original file + String tomlString = this.toml.toToml(); + fileWriter.write(tomlString); + } + + // Delete the temporary file and the backup file + Files.delete(Path.of(tempFile.toURI())); + Files.delete(Path.of(backup.toURI())); + } + + @Override + public void load() throws IOException + { + // TODO: Implement + } + + @Override + public String getFileName() + { + return null; + } + + @Override + public File getConfigurationFile() + { + return null; + } + + @Override + public String getString(String path) + { + return null; + } + + @Override + public boolean getBoolean(String path) + { + return false; + } + + @Override + public @Unmodifiable List getList(String path, Class clazz) + { + return null; + } + + @Override + public @Unmodifiable List getStringList(String path) + { + return null; + } + + @Override + public int getInt(String path) + { + return 0; + } + + @Override + public long getLong(String path) + { + return 0; + } + + @Override + public double getDouble(String path) + { + return 0; + } + + @Override + public void set(String path, T value) + { + // TODO: Implement + } + + @Override + public Optional get(String path, Class clazz) + { + return Optional.empty(); + } + + @Override + public T getOrDefault(String path, Class clazz, T fallback) + { + return null; + } +} diff --git a/Patchwork/src/main/java/fns/patchwork/event/EventBus.java b/Patchwork/src/main/java/fns/patchwork/event/EventBus.java index 7a57ac4..c6065f9 100644 --- a/Patchwork/src/main/java/fns/patchwork/event/EventBus.java +++ b/Patchwork/src/main/java/fns/patchwork/event/EventBus.java @@ -23,7 +23,7 @@ package fns.patchwork.event; -import fns.patchwork.api.Context; +import fns.patchwork.provider.Context; import fns.patchwork.base.Patchwork; import fns.patchwork.service.Service; import java.util.HashSet; diff --git a/Patchwork/src/main/java/fns/patchwork/particle/Trail.java b/Patchwork/src/main/java/fns/patchwork/particle/Trail.java index 6e6ca6b..e98d250 100644 --- a/Patchwork/src/main/java/fns/patchwork/particle/Trail.java +++ b/Patchwork/src/main/java/fns/patchwork/particle/Trail.java @@ -23,7 +23,6 @@ package fns.patchwork.particle; -import fns.patchwork.api.Interpolator; import fns.patchwork.utils.InterpolationUtils; import java.util.Set; import java.util.UUID; @@ -101,7 +100,7 @@ public interface Trail * @see #getColor() * @see Particle * @see InterpolationUtils - * @see Interpolator + * @see InterpolationUtils.Interpolator */ @Nullable Set getColors(); diff --git a/Patchwork/src/main/java/fns/patchwork/api/Context.java b/Patchwork/src/main/java/fns/patchwork/provider/Context.java similarity index 99% rename from Patchwork/src/main/java/fns/patchwork/api/Context.java rename to Patchwork/src/main/java/fns/patchwork/provider/Context.java index 2d34a6f..450f846 100644 --- a/Patchwork/src/main/java/fns/patchwork/api/Context.java +++ b/Patchwork/src/main/java/fns/patchwork/provider/Context.java @@ -21,9 +21,8 @@ * SOFTWARE. */ -package fns.patchwork.api; +package fns.patchwork.provider; -import fns.patchwork.provider.ContextProvider; import java.util.function.Function; import net.kyori.adventure.text.Component; import org.bukkit.Location; diff --git a/Patchwork/src/main/java/fns/patchwork/data/ConfigRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/ConfigRegistry.java similarity index 98% rename from Patchwork/src/main/java/fns/patchwork/data/ConfigRegistry.java rename to Patchwork/src/main/java/fns/patchwork/registry/ConfigRegistry.java index 185091f..a8b237a 100644 --- a/Patchwork/src/main/java/fns/patchwork/data/ConfigRegistry.java +++ b/Patchwork/src/main/java/fns/patchwork/registry/ConfigRegistry.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.data; +package fns.patchwork.registry; import fns.patchwork.config.Configuration; import java.util.HashMap; diff --git a/Patchwork/src/main/java/fns/patchwork/data/EventRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/EventRegistry.java similarity index 98% rename from Patchwork/src/main/java/fns/patchwork/data/EventRegistry.java rename to Patchwork/src/main/java/fns/patchwork/registry/EventRegistry.java index 85deaca..e886209 100644 --- a/Patchwork/src/main/java/fns/patchwork/data/EventRegistry.java +++ b/Patchwork/src/main/java/fns/patchwork/registry/EventRegistry.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.data; +package fns.patchwork.registry; import fns.patchwork.event.FEvent; import fns.patchwork.provider.EventProvider; diff --git a/Patchwork/src/main/java/fns/patchwork/data/GroupRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/GroupRegistry.java similarity index 98% rename from Patchwork/src/main/java/fns/patchwork/data/GroupRegistry.java rename to Patchwork/src/main/java/fns/patchwork/registry/GroupRegistry.java index 02b7168..f9afbc4 100644 --- a/Patchwork/src/main/java/fns/patchwork/data/GroupRegistry.java +++ b/Patchwork/src/main/java/fns/patchwork/registry/GroupRegistry.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.data; +package fns.patchwork.registry; import fns.patchwork.permissible.Group; import java.util.ArrayList; diff --git a/Patchwork/src/main/java/fns/patchwork/data/ModuleRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/ModuleRegistry.java similarity index 98% rename from Patchwork/src/main/java/fns/patchwork/data/ModuleRegistry.java rename to Patchwork/src/main/java/fns/patchwork/registry/ModuleRegistry.java index 2449ff0..a085228 100644 --- a/Patchwork/src/main/java/fns/patchwork/data/ModuleRegistry.java +++ b/Patchwork/src/main/java/fns/patchwork/registry/ModuleRegistry.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.data; +package fns.patchwork.registry; import fns.patchwork.provider.ModuleProvider; import java.util.ArrayList; diff --git a/Patchwork/src/main/java/fns/patchwork/data/ServiceTaskRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/ServiceTaskRegistry.java similarity index 99% rename from Patchwork/src/main/java/fns/patchwork/data/ServiceTaskRegistry.java rename to Patchwork/src/main/java/fns/patchwork/registry/ServiceTaskRegistry.java index 8814146..9a88d83 100644 --- a/Patchwork/src/main/java/fns/patchwork/data/ServiceTaskRegistry.java +++ b/Patchwork/src/main/java/fns/patchwork/registry/ServiceTaskRegistry.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.data; +package fns.patchwork.registry; import fns.patchwork.service.Service; import fns.patchwork.service.ServiceSubscription; diff --git a/Patchwork/src/main/java/fns/patchwork/data/UserRegistry.java b/Patchwork/src/main/java/fns/patchwork/registry/UserRegistry.java similarity index 99% rename from Patchwork/src/main/java/fns/patchwork/data/UserRegistry.java rename to Patchwork/src/main/java/fns/patchwork/registry/UserRegistry.java index 571ca52..1cdc92c 100644 --- a/Patchwork/src/main/java/fns/patchwork/data/UserRegistry.java +++ b/Patchwork/src/main/java/fns/patchwork/registry/UserRegistry.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.data; +package fns.patchwork.registry; import fns.patchwork.user.User; import fns.patchwork.user.UserData; diff --git a/Patchwork/src/main/java/fns/patchwork/api/Serializable.java b/Patchwork/src/main/java/fns/patchwork/serializer/Serializable.java similarity index 98% rename from Patchwork/src/main/java/fns/patchwork/api/Serializable.java rename to Patchwork/src/main/java/fns/patchwork/serializer/Serializable.java index 902e36b..b458c78 100644 --- a/Patchwork/src/main/java/fns/patchwork/api/Serializable.java +++ b/Patchwork/src/main/java/fns/patchwork/serializer/Serializable.java @@ -21,7 +21,7 @@ * SOFTWARE. */ -package fns.patchwork.api; +package fns.patchwork.serializer; /** * This interface represents a Serializable object. Objects which require custom serialization and cannot simply diff --git a/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java b/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java index 2ba4ebf..4573f33 100644 --- a/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java +++ b/Patchwork/src/main/java/fns/patchwork/utils/InterpolationUtils.java @@ -23,7 +23,6 @@ package fns.patchwork.utils; -import fns.patchwork.api.Interpolator; import java.util.LinkedHashSet; import java.util.Set; import net.kyori.adventure.text.format.NamedTextColor; @@ -155,4 +154,24 @@ public static Set standardComponentGradient(final int length, final T { return componentRGBGradient(length, from, to, InterpolationUtils::linear); } + + /** + * Interpolates a range of values and returns the results in a {@link Double} array. + *
+ * This is a functional interface, to allow for lambda expressions, but also for anonymous custom interpolation + * implementations. + */ + @FunctionalInterface + public static interface Interpolator + { + /** + * Interpolates a range of values and returns the results in a {@link Double} array. + * + * @param from The starting value. + * @param to The ending value. + * @param max The number of values to interpolate. + * @return The interpolated values. + */ + double[] interpolate(final double from, final double to, final int max); + } } diff --git a/Veritas/src/main/java/fns/veritas/Aggregate.java b/Veritas/src/main/java/fns/veritas/Aggregate.java index d2371c9..935d951 100644 --- a/Veritas/src/main/java/fns/veritas/Aggregate.java +++ b/Veritas/src/main/java/fns/veritas/Aggregate.java @@ -24,31 +24,53 @@ package fns.veritas; import fns.patchwork.utils.logging.FNS4J; +import fns.veritas.bukkit.BukkitNative; +import fns.veritas.bukkit.ServerListener; import fns.veritas.client.BotClient; import fns.veritas.client.BotConfig; +import org.bukkit.Bukkit; public class Aggregate { - private final FNS4J logger; + private static final FNS4J logger = FNS4J.getLogger("Veritas"); private final BotClient bot; private final Veritas plugin; + private final BukkitNative bukkitNativeListener; + private final ServerListener serverListener; public Aggregate(final Veritas plugin) { this.plugin = plugin; - this.logger = FNS4J.getLogger(plugin.getName()); this.bot = new BotClient(new BotConfig(plugin)); + this.bukkitNativeListener = new BukkitNative(plugin); + this.serverListener = new ServerListener(plugin); + + Bukkit.getServer().getPluginManager().registerEvents(this.getBukkitNativeListener(), plugin); + this.getServerListener().minecraftChatBound().subscribe(); } - public FNS4J getLogger() { + public static FNS4J getLogger() + { return logger; } - public BotClient getBot() { + public ServerListener getServerListener() + { + return serverListener; + } + + public BukkitNative getBukkitNativeListener() + { + return bukkitNativeListener; + } + + public BotClient getBot() + { return bot; } - public Veritas getPlugin() { + public Veritas getPlugin() + { return plugin; } } diff --git a/Veritas/src/main/java/fns/veritas/Veritas.java b/Veritas/src/main/java/fns/veritas/Veritas.java index 16b3949..4af1cfe 100644 --- a/Veritas/src/main/java/fns/veritas/Veritas.java +++ b/Veritas/src/main/java/fns/veritas/Veritas.java @@ -34,9 +34,8 @@ public void onEnable() { this.aggregate = new Aggregate(this); - getAggregate() - .getLogger() - .info("Veritas has been enabled!"); + Aggregate.getLogger() + .info("Veritas has been enabled!"); } public Aggregate getAggregate() diff --git a/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java b/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java index 3863616..b25a779 100644 --- a/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java +++ b/Veritas/src/main/java/fns/veritas/bukkit/BukkitNative.java @@ -109,7 +109,7 @@ public void onAsyncPlayerChat(final AsyncChatEvent event) if (!plugin.getServer().hasWhitelist() && bot != null) { plugin.getAggregate().getBot().messageChatChannel(player.getName() - + " \u00BB " + + " ยป " + message, true); } } diff --git a/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java b/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java index f2a2635..90ce95e 100644 --- a/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java +++ b/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java @@ -27,6 +27,7 @@ import discord4j.core.object.entity.Attachment; import discord4j.core.object.entity.Member; import discord4j.core.object.entity.Message; +import fns.veritas.Aggregate; import fns.veritas.Veritas; import fns.veritas.client.BotClient; import net.kyori.adventure.text.Component; @@ -35,6 +36,8 @@ import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Bukkit; +import org.jetbrains.annotations.NotNull; +import reactor.core.publisher.Mono; public class ServerListener { @@ -48,9 +51,9 @@ public ServerListener(final Veritas plugin) this.bot = plugin.getAggregate().getBot(); } - public void minecraftChatBound() + public Mono minecraftChatBound() { - bot.getClient() + return bot.getClient() .getEventDispatcher() .on(MessageCreateEvent.class) .filter(m -> m.getMessage() @@ -62,8 +65,9 @@ public void minecraftChatBound() .orElseThrow(IllegalAccessError::new) .getId() .equals(plugin.getAggregate().getBot().getClient().getSelfId())) - .doOnError(plugin.getAggregate().getLogger()::error) - .subscribe(this::doMessageBodyDetails); + .doOnError(Aggregate.getLogger()::error) + .doOnNext(this::doMessageBodyDetails) + .then(); } private void doMessageBodyDetails(MessageCreateEvent m) @@ -83,6 +87,14 @@ private void doMessageBodyDetails(MessageCreateEvent m) user = user.append(Component.text(member.getDisplayName().trim())); + final TextComponent message = builder(msg); + + Bukkit.broadcast(builder.append(prefix, user, message).build()); + } + + @NotNull + private TextComponent builder(Message msg) + { TextComponent message = Component.text(": ", NamedTextColor.DARK_GRAY) .append( Component.text(msg.getContent(), NamedTextColor.WHITE)); @@ -102,7 +114,6 @@ private void doMessageBodyDetails(MessageCreateEvent m) .clickEvent(ClickEvent.openUrl(attachment.getUrl()))); } } - - Bukkit.broadcast(builder.append(prefix, user, message).build()); + return message; } } diff --git a/Veritas/src/main/java/fns/veritas/client/BotClient.java b/Veritas/src/main/java/fns/veritas/client/BotClient.java index 178feaf..806a8b4 100644 --- a/Veritas/src/main/java/fns/veritas/client/BotClient.java +++ b/Veritas/src/main/java/fns/veritas/client/BotClient.java @@ -23,36 +23,40 @@ package fns.veritas.client; -import com.google.common.collect.ImmutableList; import discord4j.common.util.Snowflake; import discord4j.core.DiscordClientBuilder; import discord4j.core.GatewayDiscordClient; +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; import discord4j.core.object.entity.Guild; import discord4j.core.object.entity.Message; import discord4j.core.object.entity.channel.TextChannel; import discord4j.core.spec.MessageCreateSpec; +import fns.veritas.cmd.base.BotCommandHandler; +import java.util.List; import reactor.core.publisher.Mono; public class BotClient { private final GatewayDiscordClient client; private final BotConfig config; - private final ImmutableList DISCORD_SUBDOMAINS; + private final List subdomains; public BotClient(final BotConfig config) { this.config = config; - this.DISCORD_SUBDOMAINS = ImmutableList.of("discordapp.com", "discord.com", "discord.gg"); + this.subdomains = List.of("discordapp.com", "discord.com", "discord.gg"); + this.client = DiscordClientBuilder.create(config.getToken()) .build() .login() .block(); - } - public void validateConnection() - { if (client == null) throw new IllegalStateException(); + + final BotCommandHandler handler = new BotCommandHandler(client.getRestClient()); + + client.on(ChatInputInteractionEvent.class, handler::handle); } public String getBotId() @@ -87,14 +91,14 @@ public String getInviteLink() public void messageChatChannel(String message, boolean system) { - String chat_channel_id = config.getChatChannelId().asString(); + String channelID = config.getChatChannelId().asString(); String sanitizedMessage = (system) ? message : sanitizeChatMessage(message); if (sanitizedMessage.isBlank()) return; - if (!chat_channel_id.isEmpty()) + if (!channelID.isEmpty()) { MessageCreateSpec spec = MessageCreateSpec.builder() .content(sanitizedMessage) @@ -124,7 +128,7 @@ private String sanitizeChatMessage(String message) return ""; } - for (String subdomain : DISCORD_SUBDOMAINS) + for (String subdomain : subdomains) { if (message.toLowerCase().contains(subdomain + "/invite")) { diff --git a/Veritas/src/main/java/fns/veritas/client/BotConfig.java b/Veritas/src/main/java/fns/veritas/client/BotConfig.java index 3b57fa6..c675137 100644 --- a/Veritas/src/main/java/fns/veritas/client/BotConfig.java +++ b/Veritas/src/main/java/fns/veritas/client/BotConfig.java @@ -24,8 +24,8 @@ package fns.veritas.client; import discord4j.common.util.Snowflake; -import discord4j.discordjson.Id; import fns.patchwork.config.WrappedBukkitConfiguration; +import fns.veritas.Aggregate; import fns.veritas.Veritas; import java.io.File; import java.io.IOException; @@ -108,7 +108,7 @@ private Function f0(final Veritas plugin) } catch (IOException e) { - plugin.getAggregate().getLogger().error(e); + Aggregate.getLogger().error(e); } } } diff --git a/Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java b/Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java new file mode 100644 index 0000000..b3cc27a --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/HelpCommand.java @@ -0,0 +1,69 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd; + +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import fns.veritas.cmd.base.BotCommand; +import fns.veritas.messaging.Embed; +import fns.veritas.messaging.EmbedWrapper; +import java.util.ArrayList; +import java.util.List; +import reactor.core.publisher.Mono; + +public class HelpCommand implements BotCommand +{ + @Override + public String getName() + { + return "help"; + } + + @Override + public Mono handle(ChatInputInteractionEvent event) + { + final List content = new ArrayList<>(); + final EmbedWrapper e = new EmbedWrapper(); + + content.add(embedContent("help", + "Shows this message. \n" + + "Use /help info to see information about the server.", + false)); + content.add(embedContent("tps", + "Shows the server's current TPS.", + false)); + content.add(embedContent("list", + "Shows a list of all online players. \n" + + "Use /list staff to show online staff.", + false)); + e.quickEmbed("Command List:", + "A list of all currently supported commands", + content); + + return event.reply() + .withContent("Here is a list of all currently supported commands:") + .withEmbeds(e.getEmbeds()) + .withEphemeral(true) + .then(); + } +} diff --git a/Veritas/src/main/java/fns/veritas/cmd/ListCommand.java b/Veritas/src/main/java/fns/veritas/cmd/ListCommand.java new file mode 100644 index 0000000..b3df0d1 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/ListCommand.java @@ -0,0 +1,102 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd; + +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import discord4j.core.object.command.ApplicationCommandInteractionOption; +import discord4j.core.object.command.ApplicationCommandInteractionOptionValue; +import fns.patchwork.kyori.PlainTextWrapper; +import fns.veritas.cmd.base.BotCommand; +import fns.veritas.messaging.Embed; +import fns.veritas.messaging.EmbedWrapper; +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Bukkit; +import reactor.core.publisher.Mono; + +public class ListCommand implements BotCommand +{ + @Override + public String getName() + { + return "list"; + } + + @Override + public Mono handle(final ChatInputInteractionEvent event) + { + final boolean showStaff = event.getOption("staff") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asBoolean) + .orElse(false); + + if (showStaff) + return staffList(event); + + final EmbedWrapper e = new EmbedWrapper(); + final List embeds = new ArrayList<>(); + + Bukkit.getOnlinePlayers() + .forEach(player -> + { + final String display = PlainTextWrapper.toPlainText(player.displayName()); + final String actual = PlainTextWrapper.toPlainText(player.name()); + + final Embed embed = new Embed(display, actual, false); + embeds.add(embed); + }); + + e.quickEmbed("Player List", "List of currently online players:", embeds); + + return event.reply() + .withEmbeds(e.getEmbeds()) + .withEphemeral(true) + .then(); + } + + private Mono staffList(final ChatInputInteractionEvent event) + { + final EmbedWrapper wrapper = new EmbedWrapper(); + final List embeds = new ArrayList<>(); + + Bukkit.getOnlinePlayers() + .stream() + .filter(player -> player.hasPermission("fns.marker.staff")) + .forEach(player -> + { + final String display = PlainTextWrapper.toPlainText(player.displayName()); + final String actual = PlainTextWrapper.toPlainText(player.name()); + + final Embed embed = new Embed(display, actual, false); + embeds.add(embed); + }); + + wrapper.quickEmbed("Staff List", "List of currently online staff members:", embeds); + + return event.reply() + .withEmbeds(wrapper.getEmbeds()) + .withEphemeral(true) + .then(); + } +} diff --git a/Veritas/src/main/java/fns/veritas/cmd/TpsCommand.java b/Veritas/src/main/java/fns/veritas/cmd/TpsCommand.java new file mode 100644 index 0000000..f6f27c7 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/TpsCommand.java @@ -0,0 +1,64 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd; + +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import fns.veritas.cmd.base.BotCommand; +import fns.veritas.messaging.Embed; +import fns.veritas.messaging.EmbedWrapper; +import java.util.ArrayList; +import java.util.List; +import org.bukkit.Bukkit; +import reactor.core.publisher.Mono; + +public class TpsCommand implements BotCommand +{ + @Override + public String getName() + { + return "tps"; + } + + @Override + public Mono handle(ChatInputInteractionEvent event) + { + final double[] tps = Bukkit.getServer().getTPS(); + final EmbedWrapper e = new EmbedWrapper(); + + final List embeds = new ArrayList<>(); + + embeds.add(embedContent("1 Minute:", String.valueOf(tps[0]), false)); + embeds.add(embedContent("5 Minutes:", String.valueOf(tps[1]), false)); + embeds.add(embedContent("15 Minutes:", String.valueOf(tps[2]), false)); + + e.quickEmbed("Server TPS:", + "Current TPS (1m, 5m, 15m)", + embeds); + + return event.reply() + .withEmbeds(e.getEmbeds()) + .withEphemeral(true) + .then(); + } +} diff --git a/Patchwork/src/main/java/fns/patchwork/api/Interpolator.java b/Veritas/src/main/java/fns/veritas/cmd/base/BotCommand.java similarity index 65% rename from Patchwork/src/main/java/fns/patchwork/api/Interpolator.java rename to Veritas/src/main/java/fns/veritas/cmd/base/BotCommand.java index df105d5..5825479 100644 --- a/Patchwork/src/main/java/fns/patchwork/api/Interpolator.java +++ b/Veritas/src/main/java/fns/veritas/cmd/base/BotCommand.java @@ -21,24 +21,23 @@ * SOFTWARE. */ -package fns.patchwork.api; +package fns.veritas.cmd.base; -/** - * Interpolates a range of values and returns the results in a {@link Double} array. - *
- * This is a functional interface, to allow for lambda expressions, but also for anonymous custom interpolation - * implementations. - */ -@FunctionalInterface -public interface Interpolator +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import fns.patchwork.utils.container.Trio; +import fns.veritas.messaging.Embed; +import reactor.core.publisher.Mono; + +public interface BotCommand { - /** - * Interpolates a range of values and returns the results in a {@link Double} array. - * - * @param from The starting value. - * @param to The ending value. - * @param max The number of values to interpolate. - * @return The interpolated values. - */ - double[] interpolate(final double from, final double to, final int max); + String getName(); + + Mono handle(final ChatInputInteractionEvent event); + + default Embed embedContent(final String field, + final String value, + final boolean inline) + { + return new Embed(field, value, inline); + } } diff --git a/Veritas/src/main/java/fns/veritas/cmd/base/BotCommandHandler.java b/Veritas/src/main/java/fns/veritas/cmd/base/BotCommandHandler.java new file mode 100644 index 0000000..de9a636 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/base/BotCommandHandler.java @@ -0,0 +1,139 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd.base; + +import discord4j.common.JacksonResources; +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import discord4j.discordjson.json.ApplicationCommandRequest; +import discord4j.rest.RestClient; +import discord4j.rest.service.ApplicationService; +import fns.patchwork.utils.logging.FNS4J; +import fns.veritas.Veritas; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.bukkit.Bukkit; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +public class BotCommandHandler +{ + private final List commands = new ArrayList<>(); + private final RestClient restClient; + + public BotCommandHandler(final RestClient restClient) + { + this.restClient = restClient; + } + + public void registerFromPluginDirectory(final Veritas plugin) throws IOException + { + final List jsonFiles = new ArrayList<>(); + final File commandsFolder = new File(plugin.getDataFolder(), "commands"); + if (!commandsFolder.exists() && commandsFolder.mkdirs()) + { + FNS4J.getLogger("Veritas").info("Created cmds folder. Copying default cmds..."); + plugin.saveResource("commands/", true); + } + + final File[] files = commandsFolder.listFiles(); + + if (files == null) + throw new IOException("Commands folder is empty or is not a valid directory!"); + + Stream.of(files) + .map(File::getName) + .filter(name -> name.endsWith(".json")) + .forEach(jsonFiles::add); + + final JacksonResources d4jMapper = JacksonResources.create(); + + final ApplicationService applicationService = restClient.getApplicationService(); + final long applicationId = Objects.requireNonNull(restClient.getApplicationId().block()); + + final List cmds = new ArrayList<>(); + for (final String json : getCommandsJson(plugin, jsonFiles)) + { + final ApplicationCommandRequest request = d4jMapper.getObjectMapper() + .readValue(json, ApplicationCommandRequest.class); + + cmds.add(request); + } + + applicationService.bulkOverwriteGlobalApplicationCommand(applicationId, cmds) + .doOnNext(cmd -> Bukkit.getLogger().info("Successfully registered Global Command " + + cmd.name())) + .doOnError(e -> Bukkit.getLogger().severe("Failed to register global cmds.\n" + + e.getMessage())) + .subscribe(); + } + + private @NotNull List getCommandsJson(final JavaPlugin plugin, final List fileNames) throws IOException + { + final String commandsFolderName = "commands/"; + final URL url = this.getClass().getClassLoader().getResource(commandsFolderName); + Objects.requireNonNull(url, commandsFolderName + " could not be found"); + + final List list = new ArrayList<>(); + for (final String file : fileNames) + { + final String resourceFileAsString = getResourceFileAsString(plugin, commandsFolderName + file); + list.add(Objects.requireNonNull(resourceFileAsString, "Command file not found: " + file)); + } + return list; + } + + private @Nullable String getResourceFileAsString(final JavaPlugin plugin, final String fileName) throws IOException + { + try (final InputStream resourceAsStream = plugin.getResource(fileName)) + { + if (resourceAsStream == null) + return null; + try (final InputStreamReader inputStreamReader = new InputStreamReader(resourceAsStream); + final BufferedReader reader = new BufferedReader(inputStreamReader)) + { + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + } + } + + public Mono handle(final ChatInputInteractionEvent event) + { + return Flux.fromIterable(commands) + .filter(cmd -> cmd.getName().equals(event.getCommandName())) + .next() + .flatMap(cmd -> cmd.handle(event)); + } +} diff --git a/Veritas/src/main/java/fns/veritas/messaging/Embed.java b/Veritas/src/main/java/fns/veritas/messaging/Embed.java new file mode 100644 index 0000000..2248341 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/messaging/Embed.java @@ -0,0 +1,28 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.messaging; + +public record Embed(String fieldName, String value, boolean inline) +{ +} diff --git a/Veritas/src/main/java/fns/veritas/messaging/EmbedWrapper.java b/Veritas/src/main/java/fns/veritas/messaging/EmbedWrapper.java new file mode 100644 index 0000000..1fb5178 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/messaging/EmbedWrapper.java @@ -0,0 +1,63 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.messaging; + +import discord4j.core.spec.EmbedCreateSpec; +import java.util.ArrayList; +import java.util.List; + +public class EmbedWrapper +{ + private final List embeds = new ArrayList<>(); + + public List getEmbeds() + { + return embeds; + } + + public void addEmbed(final EmbedCreateSpec embed) + { + this.embeds.add(embed); + } + + public EmbedCreateSpec.Builder create() + { + return EmbedCreateSpec.builder(); + } + + public void quickEmbed(final String title, + final String description, + final List content) + { + final EmbedCreateSpec.Builder builder = create() + .title(title) + .description(description); + + content.forEach(t -> builder.addField(t.fieldName(), + t.value(), + t.inline())); + + addEmbed(builder.build()); + } +} diff --git a/Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java b/Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java new file mode 100644 index 0000000..6d94b1b --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java @@ -0,0 +1,72 @@ +/* + * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite + * Copyright (C) 2023 Total Freedom Server Network and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.messaging; + +import discord4j.core.object.component.LayoutComponent; +import discord4j.core.spec.MessageCreateFields; +import discord4j.core.spec.MessageCreateSpec; +import discord4j.rest.util.AllowedMentions; + +public class SimpleMessageWrapper +{ + private final MessageCreateSpec.Builder spec; + + public SimpleMessageWrapper() + { + this.spec = MessageCreateSpec.builder(); + } + + public void setContent(final String content) + { + this.spec.content(content); + } + + public void setEmbeds(final EmbedWrapper embed) + { + this.spec.addAllEmbeds(embed.getEmbeds()); + } + + public void setAttachments(final MessageCreateFields.File... files) + { + this.spec.addFiles(files); + } + + public void setSpoilerAttachments(final MessageCreateFields.FileSpoiler... files) + { + this.spec.addFileSpoilers(files); + } + + public void setAllowedMentions(final AllowedMentions allowedMentions) + { + this.spec.allowedMentions(allowedMentions); + } + + public void setLayoutComponents(final LayoutComponent... components) + { + for (final LayoutComponent component : components) + { + this.spec.addComponent(component); + } + } +} diff --git a/Veritas/src/main/resources/ExampleCommand.txt b/Veritas/src/main/resources/ExampleCommand.txt new file mode 100644 index 0000000..5df6392 --- /dev/null +++ b/Veritas/src/main/resources/ExampleCommand.txt @@ -0,0 +1,33 @@ +{ + "name": "", + "description": "", + "options": [ + { + "name": "", + "description": "", + "type": 3, + "required": true + } + ] +} + +# <-- Types --> # + +1 -> Sub Command +2 -> Sub Command Group +3 -> String +4 -> Integer +5 -> Boolean +6 -> User +7 -> Channel +8 -> Role +9 -> Mentionable +10 -> Number + +# <-- Choices --> # +(From the official documentation) +Choices can be defined on the STRING, INTEGER, and NUMBER option types. +Choices are preset values the user can pick when selecting the option that contains them. + +CAUTION +If you specify choices for an option, these are the only valid values a user may pick. \ No newline at end of file diff --git a/Veritas/src/main/resources/commands/help.json b/Veritas/src/main/resources/commands/help.json new file mode 100644 index 0000000..ad70352 --- /dev/null +++ b/Veritas/src/main/resources/commands/help.json @@ -0,0 +1,4 @@ +{ + "name": "help", + "description": "Shows a list of commands." +} \ No newline at end of file diff --git a/Veritas/src/main/resources/commands/list.json b/Veritas/src/main/resources/commands/list.json new file mode 100644 index 0000000..f73548e --- /dev/null +++ b/Veritas/src/main/resources/commands/list.json @@ -0,0 +1,12 @@ +{ + "name": "list", + "description": "List all players on the server.", + "options": [ + { + "type": 5, + "name": "staff", + "description": "Show only staff members currently online.", + "required": false + } + ] +} \ No newline at end of file diff --git a/Veritas/src/main/resources/commands/tps.json b/Veritas/src/main/resources/commands/tps.json new file mode 100644 index 0000000..c3ac8b1 --- /dev/null +++ b/Veritas/src/main/resources/commands/tps.json @@ -0,0 +1,4 @@ +{ + "name": "tps", + "description": "Shows the current server TPS." +} \ No newline at end of file From e71c167d5e3d5eadcfc8f0881d97d52710f601eb Mon Sep 17 00:00:00 2001 From: Paul Reilly Date: Mon, 28 Aug 2023 18:17:12 -0500 Subject: [PATCH 2/5] Fix plugin-yml plugin errors & switch to night-config # Changes: - Migrate from com.google.gson:gson and com.tomlj:tomlj over to com.electronwill.night-config:core, toml, and json - Add the appropriate bukkit tags required by the minecrell/plugin-yml gradle plugin to Veritas and Tyr. --- Patchwork/build.gradle | 7 ++++--- Tyr/build.gradle | 6 ++++++ Veritas/build.gradle | 6 ++++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Patchwork/build.gradle b/Patchwork/build.gradle index 5b8f357..fbf0c03 100644 --- a/Patchwork/build.gradle +++ b/Patchwork/build.gradle @@ -8,8 +8,9 @@ repositories { dependencies { library 'io.projectreactor:reactor-core:3.5.4' library 'io.github.classgraph:classgraph:4.8.162' - library 'org.tomlj:tomlj:1.1.0' - library 'com.google.code.gson:gson:2.8.9' + library 'com.electronwill.night-config:core:3.6.7' + library 'com.electronwill.night-config:toml:3.6.7' + library 'com.electronwill.night-config:json:3.6.7' api 'org.slf4j:slf4j-api:1.7.36' testImplementation platform('org.junit:junit-bom:5.9.1') @@ -21,7 +22,7 @@ bukkit { description = "Freedom Network Suite Core Module (API & Library)" } -var weight = 1 +def weight = 1 test { useJUnitPlatform() diff --git a/Tyr/build.gradle b/Tyr/build.gradle index 8e258d1..4bd615f 100644 --- a/Tyr/build.gradle +++ b/Tyr/build.gradle @@ -9,6 +9,12 @@ repositories { mavenCentral() } +bukkit { + main = "fns.tyr.Tyr" + description = "SSH -> RCON Module for Freedom Network Suite" + depend = ["Patchwork", "Datura"] +} + dependencies { compileOnly project(":Patchwork") compileOnly project(":Datura") diff --git a/Veritas/build.gradle b/Veritas/build.gradle index 777e8e8..afed021 100644 --- a/Veritas/build.gradle +++ b/Veritas/build.gradle @@ -9,6 +9,12 @@ repositories { mavenCentral() } +bukkit { + main = "fns.veritas.Veritas" + description = "Discord Module for Freedom Network Suite" + depend = ["Patchwork", "Datura"] +} + dependencies { compileOnly project(":Patchwork") compileOnly project(":Datura") From 26f4e0746bc19eb070c3c558599ede707f609f42 Mon Sep 17 00:00:00 2001 From: Paul Reilly Date: Mon, 28 Aug 2023 21:52:40 -0500 Subject: [PATCH 3/5] Replace TOML wrapper with generic support # Changes: - Removed specific TOML wrapper in favor of GenericConfiguration. - Added ConfigType enum to define configuration formats, parsers, and writers for TOML and JSON. - Created FileUtils class containing useful file and directory creation methods - Added @ApiStatus.Internal to both the BukkitDelegate class and Completions annotation to specify that they should not be used externally. --- .../fns/patchwork/command/BukkitDelegate.java | 4 + .../command/annotation/Completions.java | 2 + .../java/fns/patchwork/config/ConfigType.java | 88 +++++++ .../config/GenericConfiguration.java | 224 ++++++++++++++++++ .../config/WrappedTomlConfiguration.java | 184 -------------- .../java/fns/patchwork/utils/FileUtils.java | 129 ++++++++++ 6 files changed, 447 insertions(+), 184 deletions(-) create mode 100644 Patchwork/src/main/java/fns/patchwork/config/ConfigType.java create mode 100644 Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java delete mode 100644 Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java create mode 100644 Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java diff --git a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java index 8e8c1fa..01162c6 100644 --- a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java +++ b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java @@ -43,6 +43,7 @@ import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; /** @@ -60,6 +61,8 @@ *
* This class is not meant to be used outside Patchwork. */ +@ApiStatus.Internal +@ApiStatus.NonExtendable public final class BukkitDelegate extends Command implements PluginIdentifiableCommand { private final JavaPlugin plugin; @@ -274,3 +277,4 @@ public List tabComplete(final CommandSender sender, final String alias, return this.plugin; } } + diff --git a/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java b/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java index 54f2a30..20be942 100644 --- a/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java +++ b/Patchwork/src/main/java/fns/patchwork/command/annotation/Completions.java @@ -27,6 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.jetbrains.annotations.ApiStatus; /** * A marker interface which represents a holder for multiple {@link Completion} annotations. @@ -36,6 +37,7 @@ */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) +@ApiStatus.Internal public @interface Completions { /** diff --git a/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java b/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java new file mode 100644 index 0000000..1a77e45 --- /dev/null +++ b/Patchwork/src/main/java/fns/patchwork/config/ConfigType.java @@ -0,0 +1,88 @@ +/* + * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite + * Copyright (C) 2023 Simplex Development and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.patchwork.config; + +import com.electronwill.nightconfig.core.Config; +import com.electronwill.nightconfig.core.ConfigFormat; +import com.electronwill.nightconfig.core.io.ConfigParser; +import com.electronwill.nightconfig.core.io.ConfigWriter; +import com.electronwill.nightconfig.json.FancyJsonWriter; +import com.electronwill.nightconfig.json.JsonFormat; +import com.electronwill.nightconfig.json.JsonParser; +import com.electronwill.nightconfig.json.MinimalJsonWriter; +import com.electronwill.nightconfig.toml.TomlFormat; +import com.electronwill.nightconfig.toml.TomlParser; +import com.electronwill.nightconfig.toml.TomlWriter; + +public enum ConfigType +{ + TOML(TomlFormat.instance(), + ".toml", + new TomlWriter(), + new TomlParser()), + JSON(JsonFormat.minimalInstance(), + ".json", + new MinimalJsonWriter(), + new JsonParser()), + JSON_FANCY(JsonFormat.fancyInstance(), + ".json", + new FancyJsonWriter(), + new JsonParser()); + + private final ConfigFormat format; + private final String fileExtension; + private final ConfigWriter writer; + private final ConfigParser parser; + + ConfigType(final ConfigFormat format, + final String fileExtension, + final ConfigWriter writer, + final ConfigParser parser) + { + this.format = format; + this.fileExtension = fileExtension; + this.writer = writer; + this.parser = parser; + } + + public ConfigFormat getFormat() + { + return this.format; + } + + public String getExtension() + { + return this.fileExtension; + } + + public ConfigWriter getWriter() + { + return this.writer; + } + + public ConfigParser getParser() + { + return this.parser; + } +} diff --git a/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java new file mode 100644 index 0000000..410e791 --- /dev/null +++ b/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java @@ -0,0 +1,224 @@ +/* + * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite + * Copyright (C) 2023 Simplex Development and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.patchwork.config; + +import com.electronwill.nightconfig.core.Config; +import com.electronwill.nightconfig.core.ConfigFormat; +import com.electronwill.nightconfig.core.UnmodifiableConfig; +import fns.patchwork.utils.FileUtils; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Unmodifiable; + +public final class GenericConfiguration implements Configuration +{ + private final File configFile; + private final String fileName; + private final Config config; + private final ConfigType configType; + + public GenericConfiguration(@NotNull final ConfigType configType, + @Nullable final JavaPlugin plugin, + @NotNull final File dataFolder, + @NotNull final String fileName, + final boolean isConcurrent) throws IOException + { + if (!fileName.endsWith(configType.getExtension())) + throw new IllegalArgumentException("File name must end with " + configType.getExtension() + "!"); + + // Ternary just to piss off Allink :) + final Optional file = (plugin != null) ? + FileUtils.getOrCreateFileWithResource(dataFolder, fileName, plugin) : + FileUtils.getOrCreateFile(dataFolder, fileName); + + if (file.isEmpty()) + throw new FileNotFoundException(); + + this.configFile = file.get(); + this.fileName = fileName; + this.configType = configType; + + final ConfigFormat format = configType.getFormat(); + + // Another ternary just to piss off Allink :) + this.config = isConcurrent ? format.createConcurrentConfig() : format.createConfig(); + + this.load(); + } + + public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName) + throws IOException + { + this(type, null, dataFolder, fileName, false); + } + + public GenericConfiguration(final ConfigType type, final JavaPlugin plugin, final String fileName) + throws IOException + { + this(type, plugin, plugin.getDataFolder(), fileName, false); + } + + public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName, + final boolean isConcurrent) + throws IOException + { + this(type, null, dataFolder, fileName, isConcurrent); + } + + @Override + public void save() throws IOException + { + final File backup = new File(this.configFile.getParentFile(), this.fileName + ".bak"); + + if (backup.exists()) + Files.delete(backup.toPath()); + + Files.copy(this.configFile.toPath(), backup.toPath()); + + try (final FileWriter writer = new FileWriter(this.configFile)) + { + this.configType.getWriter().write(this.getConfig(), writer); + } + } + + @Override + public void load() throws IOException { + try (final FileReader reader = new FileReader(this.configFile)) { + this.config.clear(); + + final UnmodifiableConfig parsed = this.configType.getParser().parse(reader).unmodifiable(); + this.config.putAll(parsed); + } + } + + @Override + public String getFileName() + { + return fileName; + } + + @Override + public File getConfigurationFile() + { + return configFile; + } + + @Override + public String getString(final String path) + { + if (!(this.getConfig().get(path) instanceof String)) + throw new IllegalArgumentException(String.format("Value at path %s is not a string!", path)); + + return this.getConfig().get(path); + } + + @Override + public boolean getBoolean(String path) + { + if (!(this.getConfig().get(path) instanceof Boolean)) + throw new IllegalArgumentException(String.format("Value at path %s is not a boolean!", path)); + + return this.getConfig().get(path); + } + + @Override + @ApiStatus.Internal + public @Unmodifiable List getList(String path, Class clazz) + { + // TODO: Figure out how to parse lists with Night Config. + + return new ArrayList<>(); + } + + @Override + @ApiStatus.Internal + public @Unmodifiable List getStringList(String path) + { + // TODO: Figure out how to parse lists with Night Config. + + return new ArrayList<>(); + } + + @Override + public int getInt(String path) + { + return this.getConfig().getInt(path); + } + + @Override + public long getLong(String path) + { + return this.getConfig().getLong(path); + } + + @Override + public double getDouble(String path) + { + if (!(this.getConfig().get(path) instanceof Double)) + throw new IllegalArgumentException(String.format("Value at path %s is not a double!", path)); + + return this.getConfig().get(path); + } + + @Override + public Optional get(String path, Class clazz) + { + // I love ternary statements, sorry Allink :) + return clazz.isInstance(this.getConfig().get(path)) ? + Optional.of(clazz.cast(this.getConfig().get(path))) : + Optional.empty(); + } + + @Override + public T getOrDefault(String path, Class clazz, T fallback) + { + return this.get(path, clazz).orElse(fallback); + } + + @Override + public void set(final String path, final T value) { + this.config.set(path, value); + } + + private UnmodifiableConfig getConfig() + { + return config.unmodifiable(); + } + + public ConfigType getConfigType() + { + return configType; + } +} diff --git a/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java deleted file mode 100644 index 585ed4e..0000000 --- a/Patchwork/src/main/java/fns/patchwork/config/WrappedTomlConfiguration.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite - * Copyright (C) 2023 Total Freedom Server Network and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package fns.patchwork.config; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.Unmodifiable; -import org.tomlj.Toml; -import org.tomlj.TomlParseResult; - -// TODO: Finish implementation -public class WrappedTomlConfiguration implements Configuration -{ - private final Map previousValues = new HashMap<>(); - private final TomlParseResult toml; - private final File file; - - - public WrappedTomlConfiguration(final JavaPlugin plugin, final File file) throws IOException - { - if (!file.exists() && file.createNewFile()) - { - plugin.saveResource(file.getName(), true); - } - - this.toml = Toml.parse(Path.of(file.toURI())); - this.file = file; - } - - @Override - public void save() throws IOException - { - // Create a backup file - final File backup = new File(this.file.getParentFile(), this.file.getName() + ".bak"); - if (backup.exists() && !Files.deleteIfExists(Path.of(backup.toURI()))) - { - throw new IOException("Failed to delete existing backup file: " + backup.getName()); - } - - // Serialize the current configuration to a temporary file - final File tempFile = new File(this.file.getParentFile(), this.file.getName() + ".temp"); - try (FileWriter tempFileWriter = new FileWriter(tempFile)) - { - // Convert the updated TomlTable to TOML format and write it to the temporary file - String tomlString = this.toml.toToml(); - tempFileWriter.write(tomlString); - } - - // Compare the new configuration with the previous one - TomlParseResult newToml = Toml.parse(Path.of(tempFile.toURI())); - for (Map.Entry entry : newToml.toMap().entrySet()) - { - String key = entry.getKey(); - Object newValue = entry.getValue(); - Object oldValue = previousValues.get(key); - - if (oldValue == null || !oldValue.equals(newValue)) - { - // Value has changed, update it - this.toml.toMap().replace(key, newValue); - previousValues.put(key, newValue); - } - } - - // Save the updated configuration to the original file - try (FileWriter fileWriter = new FileWriter(this.file)) - { - // Convert the updated TomlTable to TOML format and write it to the original file - String tomlString = this.toml.toToml(); - fileWriter.write(tomlString); - } - - // Delete the temporary file and the backup file - Files.delete(Path.of(tempFile.toURI())); - Files.delete(Path.of(backup.toURI())); - } - - @Override - public void load() throws IOException - { - // TODO: Implement - } - - @Override - public String getFileName() - { - return null; - } - - @Override - public File getConfigurationFile() - { - return null; - } - - @Override - public String getString(String path) - { - return null; - } - - @Override - public boolean getBoolean(String path) - { - return false; - } - - @Override - public @Unmodifiable List getList(String path, Class clazz) - { - return null; - } - - @Override - public @Unmodifiable List getStringList(String path) - { - return null; - } - - @Override - public int getInt(String path) - { - return 0; - } - - @Override - public long getLong(String path) - { - return 0; - } - - @Override - public double getDouble(String path) - { - return 0; - } - - @Override - public void set(String path, T value) - { - // TODO: Implement - } - - @Override - public Optional get(String path, Class clazz) - { - return Optional.empty(); - } - - @Override - public T getOrDefault(String path, Class clazz, T fallback) - { - return null; - } -} diff --git a/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java b/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java new file mode 100644 index 0000000..0f62b70 --- /dev/null +++ b/Patchwork/src/main/java/fns/patchwork/utils/FileUtils.java @@ -0,0 +1,129 @@ +/* + * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite + * Copyright (C) 2023 Simplex Development and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.patchwork.utils; + +import fns.patchwork.utils.logging.FNS4J; +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Optional; +import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +public final class FileUtils +{ + @NonNls + private static final String CREATED_DIRECTORY = "Created directory "; + + private FileUtils() + { + throw new AssertionError(); + } + + public static String getExtension(@NotNull final File file) + { + return file.getName() + .substring(file.getName() + .lastIndexOf('.')); + } + + public static Optional getOrCreateDirectory(final File parentDirectory, final String directoryName) + { + if (parentDirectory.mkdirs()) + FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath()); + + final File directory = new File(parentDirectory, directoryName); + if (directory.mkdirs()) + FNS4J.PATCHWORK.info(CREATED_DIRECTORY + directory.getAbsolutePath()); + + if (directory.exists()) + return Optional.of(directory); + + return Optional.empty(); + } + + public static Optional getOrCreateDirectory(final Path directoryPath) + { + Optional directory = Optional.empty(); + + if (directoryPath.toFile().mkdirs()) + directory = Optional.of(directoryPath.toFile()); + + if (directory.isPresent()) + FNS4J.PATCHWORK.info(CREATED_DIRECTORY + directoryPath.toAbsolutePath()); + + return directory; + } + + public static Optional getOrCreateFile(final File parentDirectory, final String fileName) + { + if (parentDirectory.mkdirs()) + FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath()); + + final File file = new File(parentDirectory, fileName); + try + { + if (file.createNewFile()) + FNS4J.PATCHWORK.info("Created file " + file.getAbsolutePath()); + + return Optional.of(file); + } + catch (final IOException ex) + { + FNS4J.PATCHWORK.error("Failed to create file " + fileName + ": " + ex.getMessage()); + return Optional.empty(); + } + } + + public static Optional getOrCreateFileWithResource(final File parentDirectory, + final String fileName, + final JavaPlugin plugin) + { + if (parentDirectory.mkdirs()) + FNS4J.PATCHWORK.info(CREATED_DIRECTORY + parentDirectory.getAbsolutePath()); + + final File file = new File(parentDirectory, fileName); + try + { + if (file.createNewFile()) + { + FNS4J.PATCHWORK.info("Created file " + file.getAbsolutePath()); + FNS4J.PATCHWORK.info( + "Copying default file from resources/" + fileName + " to " + file.getAbsolutePath()); + + plugin.saveResource(fileName, true); + FNS4J.PATCHWORK.info( + "Successfully copied default file from resources/" + fileName + " to " + file.getAbsolutePath()); + } + + return Optional.of(file); + } + catch (final IOException ex) + { + FNS4J.PATCHWORK.error("Failed to create file " + fileName + ": " + ex.getMessage()); + return Optional.empty(); + } + } +} From 4681fc9596a61883a596ce6a9d63be38891965f9 Mon Sep 17 00:00:00 2001 From: Paul Reilly Date: Wed, 30 Aug 2023 20:49:22 -0500 Subject: [PATCH 4/5] Minor tweaks to GenericConfig & ContextProvider # Changes: - Changed Configuration#getList(String, Class) to Configuration#getCollection(String, Class) - Renamed GenericConfiguration -> GenericConfig - Implemented semantics for GenericConfig#getCollection and GenericConfig#getStringList - Adjusted return value of ContextProvider#fromString to return Optional instead of @Nullable T - Adjusted classes which used previous API methods to use the newly updated ones. --- .../fns/patchwork/command/BukkitDelegate.java | 88 ++++++++------ .../fns/patchwork/config/Configuration.java | 3 +- ...cConfiguration.java => GenericConfig.java} | 112 +++++++++++++----- .../config/WrappedBukkitConfiguration.java | 4 +- .../patchwork/provider/ContextProvider.java | 14 +-- .../src/main/java/fns/veritas/Aggregate.java | 20 +++- .../java/fns/veritas/client/BotConfig.java | 10 +- 7 files changed, 170 insertions(+), 81 deletions(-) rename Patchwork/src/main/java/fns/patchwork/config/{GenericConfiguration.java => GenericConfig.java} (59%) diff --git a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java index 01162c6..46efcf9 100644 --- a/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java +++ b/Patchwork/src/main/java/fns/patchwork/command/BukkitDelegate.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.Set; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; @@ -156,50 +157,19 @@ private void processSubCommands(final @NotNull String @NotNull [] args, if (argTypes.length > args.length) return; - final Object[] objects = new Object[argTypes.length + 1]; - - for (int i = 0; i < argTypes.length; i++) - { - final Class argType = argTypes[i]; - final String arg = args[i]; + final Player p = (sender instanceof Player player) ? player : null; - if (argType.equals(String.class)) - { - if (i == argTypes.length - 1) - { - final String[] reasonArgs = Arrays.copyOfRange(args, i, args.length - 1); - final String reason = String.join(" ", reasonArgs); - objects[i] = reason; - } - else - { - continue; - } - } + final Object[] objects = new Object[argTypes.length + 1]; - if (argType.equals(Location.class)) - { - final String[] locationArgs = Arrays.copyOfRange(args, i, i + 3); - final String location = String.join(" ", locationArgs); - objects[i] = location; - } + parseArguments(args, provider, argTypes, objects); - final Object obj = provider.fromString(arg, argType); - if (obj == null) - { - FNS4J.getLogger("Datura") - .error("Failed to parse argument " + arg + " for type " + argType.getName()); - return; - } - objects[i] = obj; - } try { if (noConsole) { command.getSubcommands() .get(node) - .invoke(command, (Player) sender, objects); + .invoke(command, p, objects); } else { @@ -215,11 +185,55 @@ private void processSubCommands(final @NotNull String @NotNull [] args, } } + private void parseArguments(@NotNull String @NotNull [] args, + ContextProvider provider, + Class[] argTypes, + Object[] objects) + { + for (int i = 0; i < argTypes.length; i++) + { + final Class argType = argTypes[i]; + String arg = args[i]; + + boolean wasResolved = false; + + if (argType.equals(String.class) && (i == argTypes.length - 1)) + { + final String[] reasonArgs = Arrays.copyOfRange(args, i, args.length - 1); + final String reason = String.join(" ", reasonArgs); + objects[i] = reason; + wasResolved = true; + } + + if (argType.equals(Location.class)) + { + final String[] locationArgs = Arrays.copyOfRange(args, i, i + 3); + arg = String.join(" ", locationArgs); + } + + if (!wasResolved) + { + final Optional obj = provider.fromString(arg, argType); + if (obj.isEmpty()) + { + FNS4J.getLogger("Datura") + .error("Failed to parse argument " + arg + " for type " + argType.getName()); + continue; + } + objects[i] = obj.get(); + } + } + } + @Override - public List tabComplete(final CommandSender sender, final String alias, final String[] args) + public @NotNull List tabComplete(final @NotNull CommandSender sender, final @NotNull String alias, + final String[] args) { - final Set completions = command.getCompletions(); final List results = new ArrayList<>(); + final Set completions = command.getCompletions(); + + if (completions == null || completions.isEmpty()) + return results; if (args.length == 0) { diff --git a/Patchwork/src/main/java/fns/patchwork/config/Configuration.java b/Patchwork/src/main/java/fns/patchwork/config/Configuration.java index b7b4780..19ef8c2 100644 --- a/Patchwork/src/main/java/fns/patchwork/config/Configuration.java +++ b/Patchwork/src/main/java/fns/patchwork/config/Configuration.java @@ -25,6 +25,7 @@ import fns.patchwork.provider.Context; import fns.patchwork.provider.ContextProvider; +import java.util.Collection; import org.jetbrains.annotations.Unmodifiable; import java.io.File; @@ -87,7 +88,7 @@ public interface Configuration * @param clazz The class of the type. * @return The List object. */ - @Unmodifiable List getList(String path, Class clazz); + @Unmodifiable Collection getCollection(String path, Class clazz); /** * Gets a List object from the associated path. The List that is returned will be the String values which are stored diff --git a/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/GenericConfig.java similarity index 59% rename from Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java rename to Patchwork/src/main/java/fns/patchwork/config/GenericConfig.java index 410e791..507eb8f 100644 --- a/Patchwork/src/main/java/fns/patchwork/config/GenericConfiguration.java +++ b/Patchwork/src/main/java/fns/patchwork/config/GenericConfig.java @@ -26,7 +26,9 @@ import com.electronwill.nightconfig.core.Config; import com.electronwill.nightconfig.core.ConfigFormat; import com.electronwill.nightconfig.core.UnmodifiableConfig; +import fns.patchwork.provider.ContextProvider; import fns.patchwork.utils.FileUtils; +import fns.patchwork.utils.logging.FNS4J; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; @@ -34,26 +36,28 @@ import java.io.IOException; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import org.bukkit.plugin.java.JavaPlugin; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Unmodifiable; -public final class GenericConfiguration implements Configuration +public final class GenericConfig implements Configuration { + private static final ContextProvider PROVIDER = new ContextProvider(); private final File configFile; private final String fileName; private final Config config; private final ConfigType configType; - public GenericConfiguration(@NotNull final ConfigType configType, - @Nullable final JavaPlugin plugin, - @NotNull final File dataFolder, - @NotNull final String fileName, - final boolean isConcurrent) throws IOException + public GenericConfig(@NotNull final ConfigType configType, + @Nullable final JavaPlugin plugin, + @NotNull final File dataFolder, + @NotNull final String fileName, + final boolean isConcurrent) throws IOException { if (!fileName.endsWith(configType.getExtension())) throw new IllegalArgumentException("File name must end with " + configType.getExtension() + "!"); @@ -78,20 +82,20 @@ public GenericConfiguration(@NotNull final ConfigType configType, this.load(); } - public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName) + public GenericConfig(final ConfigType type, final File dataFolder, final String fileName) throws IOException { this(type, null, dataFolder, fileName, false); } - public GenericConfiguration(final ConfigType type, final JavaPlugin plugin, final String fileName) + public GenericConfig(final ConfigType type, final JavaPlugin plugin, final String fileName) throws IOException { this(type, plugin, plugin.getDataFolder(), fileName, false); } - public GenericConfiguration(final ConfigType type, final File dataFolder, final String fileName, - final boolean isConcurrent) + public GenericConfig(final ConfigType type, final File dataFolder, final String fileName, + final boolean isConcurrent) throws IOException { this(type, null, dataFolder, fileName, isConcurrent); @@ -114,8 +118,10 @@ public void save() throws IOException } @Override - public void load() throws IOException { - try (final FileReader reader = new FileReader(this.configFile)) { + public void load() throws IOException + { + try (final FileReader reader = new FileReader(this.configFile)) + { this.config.clear(); final UnmodifiableConfig parsed = this.configType.getParser().parse(reader).unmodifiable(); @@ -145,7 +151,7 @@ public String getString(final String path) } @Override - public boolean getBoolean(String path) + public boolean getBoolean(final String path) { if (!(this.getConfig().get(path) instanceof Boolean)) throw new IllegalArgumentException(String.format("Value at path %s is not a boolean!", path)); @@ -153,22 +159,70 @@ public boolean getBoolean(String path) return this.getConfig().get(path); } + + /* + * I am pretty sure that this works, but not really. + * This is sort of a shot in the dark because Night Config did specify that they support collections + * in TOML and JSON files, but there is no specific get method for objects that are not primitives. + * Additionally, not all primitives are natively supported. + */ @Override - @ApiStatus.Internal - public @Unmodifiable List getList(String path, Class clazz) + public @Unmodifiable Collection getCollection(String path, Class clazz) { - // TODO: Figure out how to parse lists with Night Config. - - return new ArrayList<>(); + if (!(this.getConfig().get(path) instanceof Collection)) + throw new IllegalArgumentException(String.format("Value at path %s is not a collection!", path)); + + final Collection collection = this.getConfig().get(path); + final Collection collected = new ArrayList<>(); + + collection.stream() + .map(obj -> + { + final Optional optional; + + if (obj instanceof String string) + optional = PROVIDER.fromString(string, clazz); + else if (clazz.isInstance(obj)) + optional = Optional.of(clazz.cast(obj)); + else + optional = Optional.empty(); + + return optional; + }) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toCollection(() -> collected)); + + return collected; } + /* + * I am pretty sure that this works, but not really. + * This is sort of a shot in the dark because Night Config did specify that they support collections + * in TOML and JSON files, but there is no specific get method for objects that are not primitives. + * Additionally, not all primitives are natively supported. + */ @Override - @ApiStatus.Internal public @Unmodifiable List getStringList(String path) { - // TODO: Figure out how to parse lists with Night Config. + if (!(this.getConfig().get(path) instanceof Collection c)) + throw new IllegalArgumentException(String.format("Value at path %s is not a collection!", path)); + + final Collection collection = this.getConfig().get(path); + final List list = new ArrayList<>(); - return new ArrayList<>(); + if (c.isEmpty() || !(c.toArray()[0] instanceof String)) + { + FNS4J.PATCHWORK.warn(String.format("Collection at path %s is empty or does not contain strings!", path)); + FNS4J.PATCHWORK.warn("Returning empty list!"); + return list; + } + + collection.stream() + .map(String.class::cast) + .collect(Collectors.toCollection(() -> list)); + + return list; } @Override @@ -195,20 +249,22 @@ public double getDouble(String path) @Override public Optional get(String path, Class clazz) { - // I love ternary statements, sorry Allink :) - return clazz.isInstance(this.getConfig().get(path)) ? - Optional.of(clazz.cast(this.getConfig().get(path))) : - Optional.empty(); + return this.getConfig() + .getOptional(path) + .filter(clazz::isInstance) + .map(clazz::cast); } @Override public T getOrDefault(String path, Class clazz, T fallback) { - return this.get(path, clazz).orElse(fallback); + return this.get(path, clazz) + .orElse(fallback); } @Override - public void set(final String path, final T value) { + public void set(final String path, final T value) + { this.config.set(path, value); } diff --git a/Patchwork/src/main/java/fns/patchwork/config/WrappedBukkitConfiguration.java b/Patchwork/src/main/java/fns/patchwork/config/WrappedBukkitConfiguration.java index f50e146..1baf944 100644 --- a/Patchwork/src/main/java/fns/patchwork/config/WrappedBukkitConfiguration.java +++ b/Patchwork/src/main/java/fns/patchwork/config/WrappedBukkitConfiguration.java @@ -94,7 +94,7 @@ public boolean getBoolean(String path) } @Override - public List getList(String path, Class clazz) + public List getCollection(String path, Class clazz) { return this.contextProvider.getList(this.getStringList(path), clazz); } @@ -132,7 +132,7 @@ public void set(String path, T value) @Override public Optional get(String path, Class clazz) { - return Optional.ofNullable(this.contextProvider.fromString(path, clazz)); + return this.contextProvider.fromString(path, clazz); } @Override diff --git a/Patchwork/src/main/java/fns/patchwork/provider/ContextProvider.java b/Patchwork/src/main/java/fns/patchwork/provider/ContextProvider.java index 9bb86f3..476a3a6 100644 --- a/Patchwork/src/main/java/fns/patchwork/provider/ContextProvider.java +++ b/Patchwork/src/main/java/fns/patchwork/provider/ContextProvider.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.stream.Stream; import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; @@ -60,7 +61,7 @@ */ public class ContextProvider { - public T fromString(final String string, final Class clazz) + public Optional fromString(final String string, final Class clazz) { return Stream.of(toBoolean(string, clazz), toLong(string, clazz), @@ -74,9 +75,9 @@ public T fromString(final String string, final Class clazz) toLocation(string, clazz), toComponent(string, clazz)) .filter(Objects::nonNull) - .findFirst() + .filter(clazz::isInstance) .map(clazz::cast) - .orElse(null); + .findFirst(); } private @Nullable Boolean toBoolean(final String string, final Class clazz) @@ -227,10 +228,9 @@ private OfflinePlayer toOfflinePlayer(final String string, final Class clazz) public @NotNull List<@Nullable T> getList(final List resolvable, final Class clazz) { final List resolved = new ArrayList<>(); - for (final String entry : resolvable) - { - resolved.add(this.fromString(entry, clazz)); - } + + resolvable.forEach(entry -> this.fromString(entry, clazz).ifPresent(resolved::add)); + return resolved; } } diff --git a/Veritas/src/main/java/fns/veritas/Aggregate.java b/Veritas/src/main/java/fns/veritas/Aggregate.java index 51afc09..c01a1a7 100644 --- a/Veritas/src/main/java/fns/veritas/Aggregate.java +++ b/Veritas/src/main/java/fns/veritas/Aggregate.java @@ -28,6 +28,7 @@ import fns.veritas.bukkit.ServerListener; import fns.veritas.client.BotClient; import fns.veritas.client.BotConfig; +import java.io.IOException; import org.bukkit.Bukkit; public class Aggregate @@ -40,13 +41,30 @@ public class Aggregate public Aggregate(final Veritas plugin) { + BotClient bot1; this.plugin = plugin; - this.bot = new BotClient(new BotConfig(plugin)); + + try + { + bot1 = new BotClient(new BotConfig(plugin)); + } + catch (IOException ex) + { + getLogger().error("Failed to load bot config! Shutting down..."); + getLogger().error(ex); + this.bot = null; + this.serverListener = null; + this.bukkitNativeListener = null; + Bukkit.getPluginManager().disablePlugin(plugin); + return; + } + this.bukkitNativeListener = new BukkitNative(plugin); this.serverListener = new ServerListener(plugin); Bukkit.getServer().getPluginManager().registerEvents(this.getBukkitNativeListener(), plugin); this.getServerListener().minecraftChatBound().subscribe(); + this.bot = bot1; } public static FNS4J getLogger() diff --git a/Veritas/src/main/java/fns/veritas/client/BotConfig.java b/Veritas/src/main/java/fns/veritas/client/BotConfig.java index 08407ad..d2e1783 100644 --- a/Veritas/src/main/java/fns/veritas/client/BotConfig.java +++ b/Veritas/src/main/java/fns/veritas/client/BotConfig.java @@ -24,7 +24,8 @@ package fns.veritas.client; import discord4j.common.util.Snowflake; -import fns.patchwork.config.WrappedBukkitConfiguration; +import fns.patchwork.config.ConfigType; +import fns.patchwork.config.GenericConfig; import fns.veritas.Aggregate; import fns.veritas.Veritas; import java.io.File; @@ -41,12 +42,11 @@ public class BotConfig public static final String GUILD_ID = "guild_id"; @NonNls private static final String BOT_TOKEN = "bot_token"; - private final WrappedBukkitConfiguration config; + private final GenericConfig config; - public BotConfig(final Veritas plugin) + public BotConfig(final Veritas plugin) throws IOException { - this.config = new WrappedBukkitConfiguration(f0(plugin), - new File(plugin.getDataFolder(), "config.yml")); + this.config = new GenericConfig(ConfigType.TOML, plugin, "config.toml"); } public String getToken() From 85cc1f7ae0caafe1b5544ba75de70da805e1e546 Mon Sep 17 00:00:00 2001 From: Paul Reilly Date: Mon, 4 Sep 2023 22:57:36 -0500 Subject: [PATCH 5/5] Bot Command Implementation # Changes: - Added /ban [duration] command. Bans a user on the server for the specified reason and duration (in minutes). If no duration is specified, the default is 5 minutes. - Added /kick command. Kicks a player on the server for the specified reason. - Added /whisper command. Send a private message to a player on the server. --- .../patchwork/kyori/MiniMessageWrapper.java | 16 +-- .../src/main/java/fns/veritas/Aggregate.java | 25 ++++- .../fns/veritas/bukkit/ServerListener.java | 30 +++--- .../java/fns/veritas/client/BotClient.java | 60 ++++++------ .../java/fns/veritas/client/BotConfig.java | 39 +++++--- .../main/java/fns/veritas/cmd/BanCommand.java | 98 +++++++++++++++++++ .../java/fns/veritas/cmd/KickCommand.java | 90 +++++++++++++++++ .../java/fns/veritas/cmd/WhisperCommand.java | 75 ++++++++++++++ .../messaging/SimpleMessageWrapper.java | 72 -------------- Veritas/src/main/resources/commands/ban.json | 24 +++++ Veritas/src/main/resources/commands/kick.json | 18 ++++ .../src/main/resources/commands/whisper.json | 18 ++++ Veritas/src/main/resources/config.toml | 10 ++ 13 files changed, 434 insertions(+), 141 deletions(-) create mode 100644 Veritas/src/main/java/fns/veritas/cmd/BanCommand.java create mode 100644 Veritas/src/main/java/fns/veritas/cmd/KickCommand.java create mode 100644 Veritas/src/main/java/fns/veritas/cmd/WhisperCommand.java delete mode 100644 Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java create mode 100644 Veritas/src/main/resources/commands/ban.json create mode 100644 Veritas/src/main/resources/commands/kick.json create mode 100644 Veritas/src/main/resources/commands/whisper.json create mode 100644 Veritas/src/main/resources/config.toml diff --git a/Patchwork/src/main/java/fns/patchwork/kyori/MiniMessageWrapper.java b/Patchwork/src/main/java/fns/patchwork/kyori/MiniMessageWrapper.java index c814ab9..fcb292e 100644 --- a/Patchwork/src/main/java/fns/patchwork/kyori/MiniMessageWrapper.java +++ b/Patchwork/src/main/java/fns/patchwork/kyori/MiniMessageWrapper.java @@ -37,14 +37,14 @@ public class MiniMessageWrapper private static final MiniMessage unsafe = MiniMessage.miniMessage(); private static final MiniMessage safe = MiniMessage.builder() .tags(TagResolver.resolver( - StandardTags.color(), - StandardTags.rainbow(), - StandardTags.gradient(), - StandardTags.newline(), - StandardTags.decorations(TextDecoration.ITALIC), - StandardTags.decorations(TextDecoration.BOLD), - StandardTags.decorations(TextDecoration.STRIKETHROUGH), - StandardTags.decorations(TextDecoration.UNDERLINED) + StandardTags.color(), + StandardTags.rainbow(), + StandardTags.gradient(), + StandardTags.newline(), + StandardTags.decorations(TextDecoration.ITALIC), + StandardTags.decorations(TextDecoration.BOLD), + StandardTags.decorations(TextDecoration.STRIKETHROUGH), + StandardTags.decorations(TextDecoration.UNDERLINED) )) .build(); diff --git a/Veritas/src/main/java/fns/veritas/Aggregate.java b/Veritas/src/main/java/fns/veritas/Aggregate.java index c01a1a7..16d8607 100644 --- a/Veritas/src/main/java/fns/veritas/Aggregate.java +++ b/Veritas/src/main/java/fns/veritas/Aggregate.java @@ -34,6 +34,13 @@ public class Aggregate { private static final FNS4J logger = FNS4J.getLogger("Veritas"); + private static final String FAILED_PACKET = """ + Failed to process inbound chat packet. + An offending element was found transmitted through the stream. + The element has been dropped, and ignored. + Offending element: %s + Caused by: %s + Stack Trace: %s"""; private final BotClient bot; private final Veritas plugin; private final BukkitNative bukkitNativeListener; @@ -62,8 +69,17 @@ public Aggregate(final Veritas plugin) this.bukkitNativeListener = new BukkitNative(plugin); this.serverListener = new ServerListener(plugin); - Bukkit.getServer().getPluginManager().registerEvents(this.getBukkitNativeListener(), plugin); - this.getServerListener().minecraftChatBound().subscribe(); + Bukkit.getServer() + .getPluginManager() + .registerEvents(this.getBukkitNativeListener(), plugin); + this.getServerListener() + .minecraftChatBound() + .onErrorContinue((th, v) -> Aggregate.getLogger() + .error(FAILED_PACKET.formatted( + v.getClass().getName(), + th.getCause(), + th.getMessage()))) + .subscribe(); this.bot = bot1; } @@ -87,6 +103,11 @@ public BotClient getBot() return bot; } + public BotConfig getBotConfig() + { + return bot.getConfig(); + } + public Veritas getPlugin() { return plugin; diff --git a/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java b/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java index 6d9e113..f0ac173 100644 --- a/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java +++ b/Veritas/src/main/java/fns/veritas/bukkit/ServerListener.java @@ -54,20 +54,20 @@ public ServerListener(final Veritas plugin) public Mono minecraftChatBound() { return bot.getClient() - .getEventDispatcher() - .on(MessageCreateEvent.class) - .filter(m -> m.getMessage() - .getChannelId() - .equals(bot.getChatChannelId())) - .filter(m -> m.getMember().orElse(null) != null) - .filter(m -> !m.getMessage() - .getAuthor() - .orElseThrow(IllegalAccessError::new) - .getId() - .equals(plugin.getAggregate().getBot().getClient().getSelfId())) - .doOnError(Aggregate.getLogger()::error) - .doOnNext(this::doMessageBodyDetails) - .then(); + .getEventDispatcher() + .on(MessageCreateEvent.class) + .filter(m -> m.getMessage() + .getChannelId() + .equals(bot.getConfig().getChatChannelId())) + .filter(m -> m.getMember().orElse(null) != null) + .filter(m -> !m.getMessage() + .getAuthor() + .orElseThrow(IllegalAccessError::new) + .getId() + .equals(bot.getClient().getSelfId())) + .doOnError(Aggregate.getLogger()::error) + .doOnNext(this::doMessageBodyDetails) + .then(); } private void doMessageBodyDetails(MessageCreateEvent m) @@ -81,7 +81,7 @@ private void doMessageBodyDetails(MessageCreateEvent m) .hoverEvent(HoverEvent.showText( Component.text("Click to join our Discord server!"))) .clickEvent(ClickEvent.openUrl( - plugin.getAggregate().getBot().getInviteLink()))) + plugin.getAggregate().getBotConfig().getInviteLink()))) .append(Component.text("] ", NamedTextColor.DARK_GRAY)); TextComponent user = Component.empty(); diff --git a/Veritas/src/main/java/fns/veritas/client/BotClient.java b/Veritas/src/main/java/fns/veritas/client/BotClient.java index 46bc391..a20a3ad 100644 --- a/Veritas/src/main/java/fns/veritas/client/BotClient.java +++ b/Veritas/src/main/java/fns/veritas/client/BotClient.java @@ -23,16 +23,20 @@ package fns.veritas.client; -import discord4j.common.util.Snowflake; import discord4j.core.DiscordClientBuilder; import discord4j.core.GatewayDiscordClient; import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; import discord4j.core.object.entity.Guild; import discord4j.core.object.entity.Message; +import discord4j.core.object.entity.PartialMember; +import discord4j.core.object.entity.Role; +import discord4j.core.object.entity.User; +import discord4j.core.object.entity.channel.Channel; import discord4j.core.object.entity.channel.TextChannel; import discord4j.core.spec.MessageCreateSpec; import fns.veritas.cmd.base.BotCommandHandler; import java.util.List; +import java.util.Objects; import reactor.core.publisher.Mono; public class BotClient @@ -58,37 +62,11 @@ public BotClient(final BotConfig config) client.on(ChatInputInteractionEvent.class, handler::handle); } - - public String getBotId() - { - return client.getSelfId().asString(); - } - - public Mono getServerGuildId() - { - return client.getGuildById(config.getId()); - } - public GatewayDiscordClient getClient() { return client; } - public Snowflake getChatChannelId() - { - return config.getChatChannelId(); - } - - public Snowflake getLogChannelId() - { - return config.getLogChannelId(); - } - - public String getInviteLink() - { - return config.getInviteLink(); - } - public void messageChatChannel(String message, boolean system) { String channelID = config.getChatChannelId().asString(); @@ -119,7 +97,6 @@ private String sanitizeChatMessage(String message) if (message.contains("@")) { - // \u200B is Zero Width Space, invisible on Discord newMessage = message.replace("@", "@\u200B"); } @@ -144,6 +121,33 @@ private String sanitizeChatMessage(String message) return deformat(newMessage); } + public Mono isAdmin(final User user) + { + return getGuild().flatMap(guild -> guild.getMemberById(user.getId())) + .flatMapMany(PartialMember::getRoles) + .filter(role -> getConfig().getAdminRoleId().asLong() == role.getId().asLong()) + .filter(Objects::nonNull) + .next() + .hasElement(); + } + + public Mono getLogsChannel() { + return getGuild().flatMap(guild -> guild.getChannelById(getConfig().getLogChannelId())); + } + + public Mono getChatChannel() { + return getGuild().flatMap(guild -> guild.getChannelById(getConfig().getChatChannelId())); + } + + public Mono getGuild() { + return getClient().getGuildById(getConfig().getGuildId()); + } + + public BotConfig getConfig() + { + return config; + } + public String deformat(String input) { return input.replaceAll("([_\\\\`*>|])", "\\\\$1"); diff --git a/Veritas/src/main/java/fns/veritas/client/BotConfig.java b/Veritas/src/main/java/fns/veritas/client/BotConfig.java index d2e1783..c498c4a 100644 --- a/Veritas/src/main/java/fns/veritas/client/BotConfig.java +++ b/Veritas/src/main/java/fns/veritas/client/BotConfig.java @@ -39,9 +39,17 @@ public class BotConfig { @NonNls - public static final String GUILD_ID = "guild_id"; + private static final String GUILD_ID = "bot_settings.guild_id"; @NonNls - private static final String BOT_TOKEN = "bot_token"; + private static final String BOT_TOKEN = "bot_settings.bot_token"; + @NonNls + private static final String MC_CHANNEL_ID = "bot_settings.mc_channel_id"; + @NonNls + private static final String LOG_CHANNEL_ID = "bot_settings.log_channel_id"; + @NonNls + private static final String INVITE_LINK = "bot_settings.invite_link"; + + private final GenericConfig config; public BotConfig(final Veritas plugin) throws IOException @@ -54,29 +62,29 @@ public String getToken() return config.getString(BOT_TOKEN); } - public String getPrefix() + public Snowflake getGuildId() { - return config.getString("bot_prefix"); + return Snowflake.of(config.getLong(GUILD_ID)); } - public Snowflake getId() + public Snowflake getChatChannelId() { - return Snowflake.of(config.getString(GUILD_ID)); + return Snowflake.of(config.getLong(MC_CHANNEL_ID)); } - public Snowflake getChatChannelId() + public Snowflake getLogChannelId() { - return Snowflake.of(config.getString("channel_id")); + return Snowflake.of(config.getLong(LOG_CHANNEL_ID)); } - public Snowflake getLogChannelId() + public Snowflake getAdminRoleId() { - return Snowflake.of(config.getString("log_channel_id")); + return Snowflake.of(config.getLong("admin_settings.admin_role_id")); } public String getInviteLink() { - return config.getString("invite_link"); + return config.getString(INVITE_LINK); } private Function f0(final Veritas plugin) @@ -94,11 +102,10 @@ private Function f0(final Veritas plugin) catch (IOException | InvalidConfigurationException ex) { fc.addDefault(BOT_TOKEN, "token"); - fc.addDefault("bot_prefix", "!"); - fc.addDefault(GUILD_ID, GUILD_ID); - fc.addDefault("channel_id", "nil"); - fc.addDefault("log_channel_id", "nil"); - fc.addDefault("invite_link", "https://discord.gg/invite"); + fc.addDefault(GUILD_ID, 0); + fc.addDefault(MC_CHANNEL_ID, 0); + fc.addDefault(LOG_CHANNEL_ID, 0); + fc.addDefault(INVITE_LINK, "https://discord.gg/invite"); fc.options().copyDefaults(true); diff --git a/Veritas/src/main/java/fns/veritas/cmd/BanCommand.java b/Veritas/src/main/java/fns/veritas/cmd/BanCommand.java new file mode 100644 index 0000000..564a5fe --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/BanCommand.java @@ -0,0 +1,98 @@ +/* + * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite + * Copyright (C) 2023 Simplex Development and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd; + + +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import discord4j.core.object.command.ApplicationCommandInteractionOption; +import discord4j.core.object.command.ApplicationCommandInteractionOptionValue; +import discord4j.core.object.entity.User; +import fns.patchwork.base.Shortcuts; +import fns.veritas.Veritas; +import fns.veritas.cmd.base.BotCommand; +import java.time.Duration; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import reactor.core.publisher.Mono; + +public class BanCommand implements BotCommand +{ + + + @Override + public String getName() + { + return "ban"; + } + + @Override + public Mono handle(ChatInputInteractionEvent event) + { + final String playerName = event.getOption("player") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asString) + .orElseThrow(); + final String reason = event.getOption("reason") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asString) + .orElseThrow(); + final Duration duration = event.getOption("duration") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asLong) + .map(Duration::ofMinutes) + .orElse(Duration.ofMinutes(5)); + + final User user = event.getInteraction().getUser(); + return Shortcuts.provideModule(Veritas.class) + .getAggregate() + .getBot() + .isAdmin(user) + .doOnSuccess(b -> + { + if (Boolean.FALSE.equals(b)) + return; + + final Player player = Bukkit.getPlayer(playerName); + if (player == null) + { + event.reply() + .withEphemeral(true) + .withContent("Player not found") + .block(); + return; + } + + player.ban(reason, duration, user.getUsername()); + event.reply() + .withContent("Kicked " + playerName) + .withEphemeral(true) + .block(); + + event.getInteractionResponse() + .createFollowupMessage(user.getUsername() + ": Kicked " + playerName) + .then(); + }) + .then(); + } +} diff --git a/Veritas/src/main/java/fns/veritas/cmd/KickCommand.java b/Veritas/src/main/java/fns/veritas/cmd/KickCommand.java new file mode 100644 index 0000000..22e5e11 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/KickCommand.java @@ -0,0 +1,90 @@ +/* + * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite + * Copyright (C) 2023 Simplex Development and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd; + +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import discord4j.core.object.command.ApplicationCommandInteractionOption; +import discord4j.core.object.command.ApplicationCommandInteractionOptionValue; +import discord4j.core.object.entity.User; +import fns.patchwork.base.Shortcuts; +import fns.patchwork.kyori.MiniMessageWrapper; +import fns.veritas.Veritas; +import fns.veritas.cmd.base.BotCommand; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import reactor.core.publisher.Mono; + +public class KickCommand implements BotCommand +{ + @Override + public String getName() + { + return "kick"; + } + + @Override + public Mono handle(ChatInputInteractionEvent event) + { + final String playerName = event.getOption("player") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asString) + .orElseThrow(); + final String reason = event.getOption("reason") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asString) + .orElseThrow(); + + final User user = event.getInteraction().getUser(); + return Shortcuts.provideModule(Veritas.class) + .getAggregate() + .getBot() + .isAdmin(user) + .doOnSuccess(b -> + { + if (Boolean.FALSE.equals(b)) + return; + + final Player player = Bukkit.getPlayer(playerName); + if (player == null) + { + event.reply() + .withEphemeral(true) + .withContent("Player not found") + .block(); + return; + } + + player.kick(MiniMessageWrapper.deserialize(true, reason)); + event.reply() + .withContent("Kicked " + playerName) + .withEphemeral(true) + .block(); + + event.getInteractionResponse() + .createFollowupMessage(user.getUsername() + ": Kicked " + playerName) + .then(); + }) + .then(); + } +} diff --git a/Veritas/src/main/java/fns/veritas/cmd/WhisperCommand.java b/Veritas/src/main/java/fns/veritas/cmd/WhisperCommand.java new file mode 100644 index 0000000..2717910 --- /dev/null +++ b/Veritas/src/main/java/fns/veritas/cmd/WhisperCommand.java @@ -0,0 +1,75 @@ +/* + * This file is part of FreedomNetworkSuite - https://github.com/SimplexDevelopment/FreedomNetworkSuite + * Copyright (C) 2023 Simplex Development and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package fns.veritas.cmd; + +import discord4j.core.event.domain.interaction.ChatInputInteractionEvent; +import discord4j.core.object.command.ApplicationCommandInteractionOption; +import discord4j.core.object.command.ApplicationCommandInteractionOptionValue; +import fns.patchwork.kyori.MiniMessageWrapper; +import fns.veritas.cmd.base.BotCommand; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import reactor.core.publisher.Mono; + +public class WhisperCommand implements BotCommand +{ + @Override + public String getName() + { + return "whisper"; + } + + @Override + public Mono handle(ChatInputInteractionEvent event) + { + final String player = event.getOption("player") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asString) + .orElseThrow(); + + final String message = event.getOption("message") + .flatMap(ApplicationCommandInteractionOption::getValue) + .map(ApplicationCommandInteractionOptionValue::asString) + .orElseThrow(); + final Component c = MiniMessageWrapper.deserialize(true, + "[Whisper] " + + event.getInteraction().getUser().getUsername() + + ": " + + message); + + final Player actual = Bukkit.getPlayer(player); + if (actual == null) { + return event.reply("Player not found!") + .withEphemeral(true) + .then(); + } + + actual.sendMessage(c); + + return event.reply("Sent!") + .withEphemeral(true) + .then(); + } +} diff --git a/Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java b/Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java deleted file mode 100644 index 6d94b1b..0000000 --- a/Veritas/src/main/java/fns/veritas/messaging/SimpleMessageWrapper.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This file is part of Freedom-Network-Suite - https://github.com/AtlasMediaGroup/Freedom-Network-Suite - * Copyright (C) 2023 Total Freedom Server Network and contributors - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package fns.veritas.messaging; - -import discord4j.core.object.component.LayoutComponent; -import discord4j.core.spec.MessageCreateFields; -import discord4j.core.spec.MessageCreateSpec; -import discord4j.rest.util.AllowedMentions; - -public class SimpleMessageWrapper -{ - private final MessageCreateSpec.Builder spec; - - public SimpleMessageWrapper() - { - this.spec = MessageCreateSpec.builder(); - } - - public void setContent(final String content) - { - this.spec.content(content); - } - - public void setEmbeds(final EmbedWrapper embed) - { - this.spec.addAllEmbeds(embed.getEmbeds()); - } - - public void setAttachments(final MessageCreateFields.File... files) - { - this.spec.addFiles(files); - } - - public void setSpoilerAttachments(final MessageCreateFields.FileSpoiler... files) - { - this.spec.addFileSpoilers(files); - } - - public void setAllowedMentions(final AllowedMentions allowedMentions) - { - this.spec.allowedMentions(allowedMentions); - } - - public void setLayoutComponents(final LayoutComponent... components) - { - for (final LayoutComponent component : components) - { - this.spec.addComponent(component); - } - } -} diff --git a/Veritas/src/main/resources/commands/ban.json b/Veritas/src/main/resources/commands/ban.json new file mode 100644 index 0000000..cddf194 --- /dev/null +++ b/Veritas/src/main/resources/commands/ban.json @@ -0,0 +1,24 @@ +{ + "name": "ban", + "description": "Bans a user from the server.", + "options": [ + { + "name": "player", + "description": "The player to ban.", + "type": 3, + "required": true + }, + { + "name": "reason", + "description": "The reason for the ban.", + "type": 3, + "required": true + }, + { + "name": "duration", + "description": "The duration of the ban, in minutes. Default is 5 minutes.", + "type": 4, + "required": false + } + ] +} \ No newline at end of file diff --git a/Veritas/src/main/resources/commands/kick.json b/Veritas/src/main/resources/commands/kick.json new file mode 100644 index 0000000..9129e21 --- /dev/null +++ b/Veritas/src/main/resources/commands/kick.json @@ -0,0 +1,18 @@ +{ + "name": "kick", + "description": "Kicks a user from the server", + "options": [ + { + "name": "player", + "type": 3, + "description": "The player to kick", + "required": true + }, + { + "name": "reason", + "type": 3, + "description": "The reason for kicking the player", + "required": true + } + ] +} \ No newline at end of file diff --git a/Veritas/src/main/resources/commands/whisper.json b/Veritas/src/main/resources/commands/whisper.json new file mode 100644 index 0000000..c6d26ac --- /dev/null +++ b/Veritas/src/main/resources/commands/whisper.json @@ -0,0 +1,18 @@ +{ + "name": "whisper", + "description": "Whisper to a user.", + "options": [ + { + "name": "player", + "type": 3, + "description": "The in-game user to whisper to.", + "required": true + }, + { + "name": "message", + "type": 3, + "description": "The message to send.", + "required": true + } + ] +} \ No newline at end of file diff --git a/Veritas/src/main/resources/config.toml b/Veritas/src/main/resources/config.toml new file mode 100644 index 0000000..5aa33e2 --- /dev/null +++ b/Veritas/src/main/resources/config.toml @@ -0,0 +1,10 @@ +[bot_settings] +bot_token = "xyz-123-REPLACE-ME" +invite_link = "https://discord.gg/invite" +guild_id = 0 +mc_channel_id = 0 +log_channel_id = 0 + +[admin_settings] +# This role will be able to use the /kick and /ban commands. +admin_role_id = 0 \ No newline at end of file