Fully implement Discord bot (#33)

* Fully implement Discord bot

* 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.

* 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.
This commit is contained in:
Paldiu 2023-08-29 17:32:13 -05:00 committed by GitHub
parent b4c8e05310
commit 7d5cb35e50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1169 additions and 66 deletions

View File

@ -8,6 +8,9 @@ repositories {
dependencies {
library 'io.projectreactor:reactor-core:3.5.4'
library 'io.github.classgraph:classgraph:4.8.162'
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')
@ -19,7 +22,7 @@ bukkit {
description = "Freedom Network Suite Core Module (API & Library)"
}
var weight = 1
def weight = 1
test {
useJUnitPlatform()

View File

@ -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.

View File

@ -43,6 +43,7 @@ import org.bukkit.command.PluginIdentifiableCommand;
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 @@ import org.jetbrains.annotations.NotNull;
* <br>
* 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 final class BukkitDelegate extends Command implements PluginIdentifiableC
return this.plugin;
}
}

View File

@ -27,6 +27,7 @@ import java.lang.annotation.ElementType;
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 @@ import java.lang.annotation.Target;
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ApiStatus.Internal
public @interface Completions
{
/**

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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> 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 <T> List<T> getList(String path, Class<T> clazz)
{
// TODO: Figure out how to parse lists with Night Config.
return new ArrayList<>();
}
@Override
@ApiStatus.Internal
public @Unmodifiable List<String> 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 <T> Optional<T> get(String path, Class<T> 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> T getOrDefault(String path, Class<T> clazz, T fallback)
{
return this.get(path, clazz).orElse(fallback);
}
@Override
public <T> void set(final String path, final T value) {
this.config.set(path, value);
}
private UnmodifiableConfig getConfig()
{
return config.unmodifiable();
}
public ConfigType getConfigType()
{
return configType;
}
}

View File

@ -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;

View File

@ -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<Color> getColors();

View File

@ -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;

View File

@ -21,7 +21,7 @@
* SOFTWARE.
*/
package fns.patchwork.data;
package fns.patchwork.registry;
import fns.patchwork.config.Configuration;
import java.util.HashMap;

View File

@ -21,7 +21,7 @@
* SOFTWARE.
*/
package fns.patchwork.data;
package fns.patchwork.registry;
import fns.patchwork.event.FEvent;
import fns.patchwork.provider.EventProvider;

View File

@ -21,7 +21,7 @@
* SOFTWARE.
*/
package fns.patchwork.data;
package fns.patchwork.registry;
import fns.patchwork.permissible.Group;
import java.util.ArrayList;

View File

@ -21,7 +21,7 @@
* SOFTWARE.
*/
package fns.patchwork.data;
package fns.patchwork.registry;
import fns.patchwork.provider.ModuleProvider;
import java.util.ArrayList;

View File

@ -21,7 +21,7 @@
* SOFTWARE.
*/
package fns.patchwork.data;
package fns.patchwork.registry;
import fns.patchwork.service.Service;
import fns.patchwork.service.ServiceSubscription;

View File

@ -21,7 +21,7 @@
* SOFTWARE.
*/
package fns.patchwork.data;
package fns.patchwork.registry;
import fns.patchwork.user.User;
import fns.patchwork.user.UserData;

View File

@ -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

View File

@ -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<File> 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<File> getOrCreateDirectory(final Path directoryPath)
{
Optional<File> 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<File> 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<File> 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();
}
}
}

View File

@ -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 final class InterpolationUtils
{
return componentRGBGradient(length, from, to, InterpolationUtils::linear);
}
/**
* Interpolates a range of values and returns the results in a {@link Double} array.
* <br>
* 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);
}
}

View File

@ -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")

View File

@ -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")

View File

@ -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;
}
}

View File

@ -34,9 +34,8 @@ public class Veritas extends JavaPlugin
{
this.aggregate = new Aggregate(this);
getAggregate()
.getLogger()
.info("Veritas has been enabled!");
Aggregate.getLogger()
.info("Veritas has been enabled!");
}
public Aggregate getAggregate()

View File

@ -109,7 +109,7 @@ public class BukkitNative implements Listener
if (!plugin.getServer().hasWhitelist() && bot != null)
{
plugin.getAggregate().getBot().messageChatChannel(player.getName()
+ " \u00BB "
+ " » "
+ message, true);
}
}

View File

@ -27,6 +27,7 @@ import discord4j.core.event.domain.message.MessageCreateEvent;
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.ClickEvent;
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 class ServerListener
this.bot = plugin.getAggregate().getBot();
}
public void minecraftChatBound()
public Mono<Void> minecraftChatBound()
{
bot.getClient()
return bot.getClient()
.getEventDispatcher()
.on(MessageCreateEvent.class)
.filter(m -> m.getMessage()
@ -62,8 +65,9 @@ public class ServerListener
.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 @@ public class ServerListener
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 @@ public class ServerListener
.clickEvent(ClickEvent.openUrl(attachment.getUrl())));
}
}
Bukkit.broadcast(builder.append(prefix, user, message).build());
return message;
}
}

View File

@ -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<String> DISCORD_SUBDOMAINS;
private final List<String> 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 class BotClient
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 @@ public class BotClient
return "";
}
for (String subdomain : DISCORD_SUBDOMAINS)
for (String subdomain : subdomains)
{
if (message.toLowerCase().contains(subdomain + "/invite"))
{

View File

@ -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 @@ public class BotConfig
}
catch (IOException e)
{
plugin.getAggregate().getLogger().error(e);
Aggregate.getLogger().error(e);
}
}
}

View File

@ -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<Void> handle(ChatInputInteractionEvent event)
{
final List<Embed> 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();
}
}

View File

@ -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<Void> 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<Embed> 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<Void> staffList(final ChatInputInteractionEvent event)
{
final EmbedWrapper wrapper = new EmbedWrapper();
final List<Embed> 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();
}
}

View File

@ -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<Void> handle(ChatInputInteractionEvent event)
{
final double[] tps = Bukkit.getServer().getTPS();
final EmbedWrapper e = new EmbedWrapper();
final List<Embed> 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();
}
}

View File

@ -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.
* <br>
* 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<Void> handle(final ChatInputInteractionEvent event);
default Embed embedContent(final String field,
final String value,
final boolean inline)
{
return new Embed(field, value, inline);
}
}

View File

@ -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<BotCommand> 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<String> 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<ApplicationCommandRequest> 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<String> getCommandsJson(final JavaPlugin plugin, final List<String> 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<String> 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<Void> handle(final ChatInputInteractionEvent event)
{
return Flux.fromIterable(commands)
.filter(cmd -> cmd.getName().equals(event.getCommandName()))
.next()
.flatMap(cmd -> cmd.handle(event));
}
}

View File

@ -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)
{
}

View File

@ -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<EmbedCreateSpec> embeds = new ArrayList<>();
public List<EmbedCreateSpec> 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<Embed> content)
{
final EmbedCreateSpec.Builder builder = create()
.title(title)
.description(description);
content.forEach(t -> builder.addField(t.fieldName(),
t.value(),
t.inline()));
addEmbed(builder.build());
}
}

View File

@ -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);
}
}
}

View File

@ -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.

View File

@ -0,0 +1,4 @@
{
"name": "help",
"description": "Shows a list of commands."
}

View File

@ -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
}
]
}

View File

@ -0,0 +1,4 @@
{
"name": "tps",
"description": "Shows the current server TPS."
}