innit commit

This commit is contained in:
2026-01-28 21:46:33 -05:00
commit 89662571ec
32 changed files with 1641 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
package dev.chimken.graylist;
import dev.chimken.graylist.commands.*;
import dev.chimken.graylist.managers.CommandManager;
import dev.chimken.graylist.managers.ServiceManager;
import dev.chimken.graylist.managers.WhitelistManager;
import dev.chimken.graylist.services.Elyby;
import dev.chimken.graylist.services.Floodgate;
import dev.chimken.graylist.services.Mojang;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.logging.Logger;
public final class Graylist extends JavaPlugin {
private final FileConfiguration config = getConfig();
private final Logger logger = getLogger();
public final ServiceManager serviceManager = new ServiceManager(config, logger);
public final WhitelistManager whitelistManager = new WhitelistManager(logger);
private final CommandManager commandManager = new CommandManager("graylist");
@Override
public void onEnable() {
saveDefaultConfig();
try {
serviceManager.register(new Mojang());
serviceManager.register(new Floodgate());
serviceManager.register(new Elyby());
} catch (ServiceManager.ServiceAlreadyExists e) {
throw new RuntimeException(e);
}
// TODO: Add modify command for editing whitelist entries directly
// commandManager.register(new Modify());
commandManager.register(new AddRemove_CMD());
commandManager.register(new List_CMD());
commandManager.register(new Off_CMD());
commandManager.register(new On_CMD());
commandManager.register(new Reload_CMD());
commandManager.register(new Services_CMD());
commandManager.register(new Status_CMD());
this.getLifecycleManager().registerEventHandler(
LifecycleEvents.COMMANDS,
commands ->
commands.registrar().register(commandManager.build())
);
}
@Override
public void onDisable() {
serviceManager.clear();
}
}

View File

@@ -0,0 +1,22 @@
package dev.chimken.graylist;
import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.Style;
import net.kyori.adventure.text.format.TextColor;
import java.util.UUID;
import static net.kyori.adventure.text.Component.text;
public class TextStyles {
public static Style buildUsernameStyle(UUID uuid) {
return Style.style()
.color(TextColor.fromHexString("#7FFFFD"))
.hoverEvent(HoverEvent.showText(
text(uuid.toString())
))
.clickEvent(ClickEvent.copyToClipboard(uuid.toString()))
.build();
}
}

View File

@@ -0,0 +1,88 @@
package dev.chimken.graylist;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import org.apache.commons.lang3.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
public class Util {
public static final File WHITELIST_FILE = new File(Bukkit.getServer().getWorldContainer(), "whitelist.json");
public static String expandUUIDString(String uuid) {
StringBuilder builder = new StringBuilder(uuid);
builder.insert(8, "-");
builder.insert(13, "-");
builder.insert(18, "-");
builder.insert(23, "-");
return builder.toString();
}
public static JsonObject fetchJSON(String url) throws RuntimeException {
try (HttpClient client = HttpClient.newHttpClient()) {
HttpRequest req = HttpRequest.newBuilder()
.uri(URI.create(url))
.build();
HttpResponse<String> res = client.send(req, HttpResponse.BodyHandlers.ofString());
return JsonParser.parseString(res.body()).getAsJsonObject();
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
public static String removePrefix(String text, String prefix) {
return StringUtils.replaceOnce(text, prefix, "");
}
public static UUID findKnownUUIDByName(String name) {
Set<OfflinePlayer> playerlist = new HashSet<>(Bukkit.getOnlinePlayers());
OfflinePlayer onlinePlayer = playerlist.stream()
.filter(p -> Objects.equals(p.getName(), name))
.findFirst()
.orElse(null);
if (onlinePlayer != null) return onlinePlayer.getUniqueId();
Set<OfflinePlayer> whitelist = Bukkit.getWhitelistedPlayers();
OfflinePlayer trustedPlayer = whitelist.stream()
.filter(p -> Objects.equals(p.getName(), name))
.findFirst()
.orElse(null);
if (trustedPlayer != null) return trustedPlayer.getUniqueId();
return null;
}
public static String findKnownNameByUUID(UUID uuid) {
Set<OfflinePlayer> playerlist = new HashSet<>(Bukkit.getOnlinePlayers());
OfflinePlayer onlinePlayer = playerlist.stream()
.filter(p -> Objects.equals(p.getUniqueId(), uuid))
.findFirst()
.orElse(null);
if (onlinePlayer != null) return onlinePlayer.getName();
Set<OfflinePlayer> whitelist = Bukkit.getWhitelistedPlayers();
OfflinePlayer trustedPlayer = whitelist.stream()
.filter(p -> Objects.equals(p.getUniqueId(), uuid))
.findFirst()
.orElse(null);
if (trustedPlayer != null) return trustedPlayer.getName();
return null;
}
}

View File

@@ -0,0 +1,30 @@
package dev.chimken.graylist.abstracts;
import com.mojang.brigadier.builder.ArgumentBuilder;
import dev.chimken.graylist.Graylist;
import dev.chimken.graylist.managers.ServiceManager;
import dev.chimken.graylist.managers.WhitelistManager;
import org.bukkit.configuration.file.FileConfiguration;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public abstract class GraylistCommandBundle<S> {
private static final Graylist graylist = Graylist.getPlugin(Graylist.class);
protected final FileConfiguration config = graylist.getConfig();
protected final Logger logger = graylist.getLogger();
protected final ServiceManager serviceManager = graylist.serviceManager;
protected final WhitelistManager whitelistManager = graylist.whitelistManager;
private final List<ArgumentBuilder<S, ?>> commands = new ArrayList<>();
public final List<ArgumentBuilder<S, ?>> getCommands() {
return commands;
}
public final void register(ArgumentBuilder<S, ?> command) {
commands.add(command);
}
}

View File

@@ -0,0 +1,63 @@
package dev.chimken.graylist.abstracts;
import org.bukkit.configuration.ConfigurationSection;
import javax.annotation.Nullable;
import java.text.MessageFormat;
import java.util.UUID;
public abstract class GraylistService {
private final String SERVICE_ID;
private final String SERVICE_NAMESPACE;
private final boolean DEFAULT_STATUS;
public final String getID() {
return SERVICE_ID;
}
public final @Nullable String getNamespace() {
return SERVICE_NAMESPACE;
}
public final String getConfigPath() {
return "services.configs." + (
SERVICE_NAMESPACE != null
? SERVICE_NAMESPACE + "." + SERVICE_ID
: SERVICE_ID
);
}
public final boolean getDefaultStatus() {
return DEFAULT_STATUS;
}
private ConfigurationSection config;
public final void setConfig(ConfigurationSection config) {
this.config = config;
}
public GraylistService(String id, boolean enabled) {
this(id, null, enabled);
}
public GraylistService(String id, @Nullable String namespace, boolean enabled) {
SERVICE_ID = id;
SERVICE_NAMESPACE = namespace;
DEFAULT_STATUS = enabled;
}
public abstract UUID findUUIDByName(String name) throws UserNotFound;
public abstract String findNameByUUID(UUID uuid) throws UserNotFound;
public class UserNotFound extends Exception {
public UserNotFound (String search) {
super(MessageFormat.format("Couldn't find '{0}' on service '{1}'", search, SERVICE_ID));
}
}
public enum PlayerWhitelistStatus {
ABSENT,
PRESENT,
ADDED,
REMOVED,
}
}

View File

@@ -0,0 +1,221 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import dev.chimken.graylist.TextStyles;
import dev.chimken.graylist.Util;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import dev.chimken.graylist.abstracts.GraylistService;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.logging.Level;
import static io.papermc.paper.command.brigadier.Commands.*;
import static net.kyori.adventure.text.Component.*;
import static net.kyori.adventure.text.minimessage.tag.resolver.Placeholder.component;
public class AddRemove_CMD extends GraylistCommandBundle<CommandSourceStack> {
public AddRemove_CMD() {
register(literal("add")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.add"))
.then(build(true))
);
register(literal("remove")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.remove"))
.then(build(false))
);
}
public ArgumentBuilder<CommandSourceStack, ?> build(boolean value) {
return argument("user", StringArgumentType.string())
.suggests((ctx, builder) -> {
final String operation = ctx.getInput();
if (operation.startsWith("/graylist add")) {
Bukkit.getOnlinePlayers().stream()
.map(Player::getName)
.forEach(builder::suggest);
} else {
Bukkit.getWhitelistedPlayers().stream()
.map(player -> {
final String name = player.getName();
if (Objects.equals(name, "<unknown>")) {
return player.getUniqueId().toString();
} else {
return name;
}
})
.forEach(builder::suggest);
}
return builder.buildFuture();
})
.executes(ctx -> executor(ctx, value))
.then(argument("service", StringArgumentType.word())
.suggests((ctx, builder) -> {
serviceManager.getServices().forEach(
(s, service) -> builder.suggest(s)
);
return builder.buildFuture();
})
.executes(ctx -> executor(ctx, value))
);
}
public int executor(CommandContext<CommandSourceStack> ctx, boolean value) {
final String ref = ctx.getArgument("user", String.class);
final CommandSender sender = ctx.getSource().getSender();
GraylistService service = serviceManager.getServices().get(
getServiceID(ctx)
);
UUID uuid = null;
String name = null;
String unprefixed_name = null;
// Determine whether UUID or name was provided
try {
uuid = UUID.fromString(ref);
} catch (IllegalArgumentException __) {
name = ref;
// reassigned to keep Java happy for use inside lambda
String checkName = name;
String service_id = serviceManager.getPrefixes().entrySet().stream()
.filter(entry -> checkName.startsWith(entry.getValue()))
.findFirst()
.map(Map.Entry::getKey)
.orElse(null);
if (service_id != null)
service = serviceManager.getServices().get(service_id);
}
// If service undetermined
if (service == null) {
sender.sendRichMessage("Couldn't determine service for <user>",
component("user", text(ref))
);
return Command.SINGLE_SUCCESS;
}
// Search for prefix if name was provided
if (name != null) {
String service_prefix = config.getString(service.getConfigPath() + ".prefix");
// If prefix exists but name doesn't start with it, add it
if (service_prefix != null) {
if (!name.startsWith(service_prefix)) {
name = service_prefix + name;
unprefixed_name = name;
} else {
unprefixed_name = Util.removePrefix(name, service_prefix);
}
}
}
// Isolate logging
{
String user = name != null ? name : uuid.toString();
// Verbose (console)
logger.log(Level.INFO, "Searching " + service.getID() + " for " + user);
// Simple (in-game)
sender.sendRichMessage("Finding <user>...",
component("user", text(user))
);
}
// Depending on input ID, find opposite
if (uuid != null) {
try {
name = Util.findKnownNameByUUID(uuid);
// If unknown name, query service with UUID
if (name == null) name = service.findNameByUUID(uuid);
} catch (GraylistService.UserNotFound e) { /* ignore */ }
} else {
try {
uuid = Util.findKnownUUIDByName(name);
// If unknown UUID, query service with name (minus prefix)
if (uuid == null) uuid = service.findUUIDByName(unprefixed_name);
} catch (GraylistService.UserNotFound e) {
sender.sendRichMessage("Couldn't find <name>",
component("name", text(name))
);
return Command.SINGLE_SUCCESS;
}
}
if (name == null) name = "<unknown>";
GraylistService.PlayerWhitelistStatus status = setWhitelisted(uuid, name, value);
switch (status) {
case ABSENT -> sender.sendRichMessage("<name> is not whitelisted",
component("name",
text(name).style(TextStyles.buildUsernameStyle(uuid))
)
);
case PRESENT -> sender.sendRichMessage("<name> is already whitelisted",
component("name",
text(name).style(TextStyles.buildUsernameStyle(uuid))
)
);
case ADDED -> sender.sendRichMessage("Added <name> to the whitelist",
component("name",
text(name).style(TextStyles.buildUsernameStyle(uuid))
)
);
case REMOVED -> sender.sendRichMessage("Removed <name> from the whitelist",
component("name",
text(name).style(TextStyles.buildUsernameStyle(uuid))
)
);
}
return Command.SINGLE_SUCCESS;
}
public String getServiceID (CommandContext<CommandSourceStack> ctx) {
String service_id;
try {
service_id = ctx.getArgument("service", String.class).toLowerCase(Locale.ROOT);
} catch (IllegalArgumentException e) {
service_id = serviceManager.getDefaultServiceID();
}
return service_id;
}
public GraylistService.PlayerWhitelistStatus setWhitelisted (UUID uuid, String name, boolean value) {
return whitelistManager.setWhitelisted(
uuid,
name,
value
);
}
}

View File

@@ -0,0 +1,48 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.TextStyles;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Bukkit;
import java.util.List;
import java.util.UUID;
import static io.papermc.paper.command.brigadier.Commands.literal;
import static net.kyori.adventure.text.Component.text;
public class List_CMD extends GraylistCommandBundle<CommandSourceStack> {
public List_CMD() {
register(literal("list")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.list"))
.executes(ctx -> {
List<TextComponent> playerlist = Bukkit.getWhitelistedPlayers().stream()
.map(player -> {
final String name = player.getName();
final UUID uuid = player.getUniqueId();
return text(name != null ? name : "<unknown>")
.style(TextStyles.buildUsernameStyle(uuid));
})
.toList();
int count = playerlist.size();
TextComponent msg = text("There are " + count + " whitelisted player(s): \n");
for (int i = 0; i <= (count - 1); i++) {
if (i > 0 || i == (count - 1))
msg = msg.append(text(", "));
msg = msg.append(playerlist.get(i));
}
ctx.getSource().getSender().sendMessage(msg);
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,29 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import static io.papermc.paper.command.brigadier.Commands.literal;
public class Modify_CMD extends GraylistCommandBundle<CommandSourceStack> {
public Modify_CMD() {
register(literal("modify")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.edit"))
.executes(ctx -> {
CommandSender sender = ctx.getSource().getSender();
if (Bukkit.getServer().hasWhitelist())
sender.sendMessage("Whitelist is already turned on");
else {
Bukkit.getServer().setWhitelist(true);
sender.sendMessage("Whitelist is now turned on");
}
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,29 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import static io.papermc.paper.command.brigadier.Commands.literal;
public class Off_CMD extends GraylistCommandBundle<CommandSourceStack> {
public Off_CMD() {
register(literal("off")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.off"))
.executes(ctx -> {
CommandSender sender = ctx.getSource().getSender();
if (!Bukkit.getServer().hasWhitelist())
sender.sendMessage("Whitelist is already turned off");
else {
Bukkit.getServer().setWhitelist(false);
sender.sendMessage("Whitelist is now turned off");
}
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,29 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import static io.papermc.paper.command.brigadier.Commands.literal;
public class On_CMD extends GraylistCommandBundle<CommandSourceStack> {
public On_CMD() {
register(literal("on")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.on"))
.executes(ctx -> {
CommandSender sender = ctx.getSource().getSender();
if (Bukkit.getServer().hasWhitelist())
sender.sendMessage("Whitelist is already turned on");
else {
Bukkit.getServer().setWhitelist(true);
sender.sendMessage("Whitelist is now turned on");
}
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,22 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import static io.papermc.paper.command.brigadier.Commands.*;
public class Reload_CMD extends GraylistCommandBundle<CommandSourceStack> {
public Reload_CMD() {
register(literal("reload")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.reload"))
.executes(ctx -> {
Bukkit.getServer().reloadWhitelist();
ctx.getSource().getSender().sendMessage("Reloaded the whitelist");
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,31 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import dev.chimken.graylist.abstracts.GraylistService;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import static io.papermc.paper.command.brigadier.Commands.literal;
public class Services_CMD extends GraylistCommandBundle<CommandSourceStack> {
public Services_CMD() {
register(literal("services")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.services"))
.executes(ctx -> {
String flatList = String.join(
", ",
serviceManager.getServices().values().stream().map(GraylistService::getID).toList()
);
ctx.getSource().getSender().sendRichMessage("There are <count> service(s) available: <list>",
Placeholder.component("count", Component.text(serviceManager.getServices().keySet().toArray().length)),
Placeholder.component("list", Component.text(flatList))
);
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,23 @@
package dev.chimken.graylist.commands;
import com.mojang.brigadier.Command;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import static io.papermc.paper.command.brigadier.Commands.*;
public class Status_CMD extends GraylistCommandBundle<CommandSourceStack> {
public Status_CMD() {
register(literal("status")
.requires(ctx -> ctx.getSender().hasPermission("graylist.command.status"))
.executes(ctx -> {
ctx.getSource().getSender().sendMessage(
"Whitelist is currently " + (Bukkit.hasWhitelist() ? "on" : "off")
);
return Command.SINGLE_SUCCESS;
})
);
}
}

View File

@@ -0,0 +1,17 @@
package dev.chimken.graylist.managers;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import dev.chimken.graylist.abstracts.GraylistCommandBundle;
import io.papermc.paper.command.brigadier.CommandSourceStack;
public class CommandManager extends LiteralArgumentBuilder<CommandSourceStack> {
public CommandManager (String label) {
super(label);
this.requires(ctx -> ctx.getSender().hasPermission("graylist"));
}
public void register(GraylistCommandBundle<CommandSourceStack> bundle) {
bundle.getCommands().forEach(this::then);
}
}

View File

@@ -0,0 +1,104 @@
package dev.chimken.graylist.managers;
import dev.chimken.graylist.abstracts.GraylistService;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ServiceManager {
private final FileConfiguration config;
private final Logger logger;
private final HashMap<String, GraylistService> services = new HashMap<>();
private final HashMap<String, String> prefixes = new HashMap<>();
private String default_service_id;
public static final String INTERNAL_NAMESPACE = "internal";
public String getDefaultServiceID() { return default_service_id; }
public GraylistService getDefaultService() {
return services.get(default_service_id);
}
public HashMap<String, GraylistService> getServices() {
return services;
}
public HashMap<String, String> getPrefixes() {
return prefixes;
}
public void clear() {
services.clear();
prefixes.clear();
default_service_id = null;
}
public ServiceManager (FileConfiguration config, Logger logger) {
this.config = config;
this.logger = logger;
default_service_id = config.getString("services.default");
}
public void register(GraylistService service) throws ServiceAlreadyExists {
final String id = service.getID();
if (services.containsKey(id))
throw new ServiceAlreadyExists(id);
final String namespace = service.getNamespace();
final String service_config_path = service.getConfigPath();
ConfigurationSection service_config = config.getConfigurationSection(service_config_path);
if (service_config == null) {
service_config = config.createSection(service_config_path);
service_config.set("enabled", true);
}
final int isEnabled = service_config.isBoolean("enabled")
? service_config.getBoolean("enabled")
? 1 // Is enabled
: 0 // Isn't enabled
: 2; // Not set (use default)
// If disabled (in config or by default)
if (isEnabled == 0 || (isEnabled == 2 && !service.getDefaultStatus())) {
if (!Objects.equals(namespace, INTERNAL_NAMESPACE)) logger.log(
Level.INFO,
MessageFormat.format(
"Disabled service: {0} (external)",
service.getID()
)
);
return;
}
service.setConfig(service_config);
services.put(id, service);
String prefix = service_config.getString("prefix");
if (prefix != null) prefixes.put(id, prefix);
logger.log(
Level.INFO,
MessageFormat.format(
"Registered service: {0} ({1})",
service.getID(),
Objects.equals(namespace, INTERNAL_NAMESPACE) ? "internal" : "external"
)
);
}
public static class ServiceAlreadyExists extends Exception {
ServiceAlreadyExists (String name) {
super("Failed to register '" + name + "' because it already exists");
}
}
}

View File

@@ -0,0 +1,61 @@
package dev.chimken.graylist.managers;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import dev.chimken.graylist.Util;
import dev.chimken.graylist.abstracts.GraylistService;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Logger;
public class WhitelistManager {
private final Logger logger;
public WhitelistManager (Logger logger) {
this.logger = logger;
}
public GraylistService.PlayerWhitelistStatus setWhitelisted(UUID uuid, String name, boolean value) {
OfflinePlayer player = Bukkit.getOfflinePlayer(uuid);
try {
JsonArray whitelist = JsonParser.parseReader(new FileReader(Util.WHITELIST_FILE)).getAsJsonArray();
if (value) {
if (player.isWhitelisted())
return GraylistService.PlayerWhitelistStatus.PRESENT;
else {
JsonObject entry = new JsonObject();
entry.addProperty("uuid", uuid.toString());
entry.addProperty("name", name);
whitelist.add(entry);
try (FileWriter writer = new FileWriter(Util.WHITELIST_FILE)) {
new Gson().toJson(whitelist, writer);
}
Bukkit.reloadWhitelist();
return GraylistService.PlayerWhitelistStatus.ADDED;
}
} else {
if (!player.isWhitelisted())
return GraylistService.PlayerWhitelistStatus.ABSENT;
else {
player.setWhitelisted(false);
return GraylistService.PlayerWhitelistStatus.REMOVED;
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,34 @@
package dev.chimken.graylist.services;
import dev.chimken.graylist.Util;
import dev.chimken.graylist.abstracts.GraylistService;
import java.util.UUID;
import static dev.chimken.graylist.Util.fetchJSON;
import static dev.chimken.graylist.managers.ServiceManager.INTERNAL_NAMESPACE;
public class Elyby extends GraylistService {
public Elyby() {
super("elyby", INTERNAL_NAMESPACE, false);
}
@Override
public UUID findUUIDByName(String name) throws UserNotFound {
try {
final String id = fetchJSON("http://skinsystem.ely.by/profile/" + name)
.get("id")
.getAsString();
// Use expandUUIDString because UUID is collapsed initially
return UUID.fromString(Util.expandUUIDString(id));
} catch (RuntimeException e) {
throw new UserNotFound(name);
}
}
@Override
public String findNameByUUID(UUID uuid) {
return null;
}
}

View File

@@ -0,0 +1,40 @@
package dev.chimken.graylist.services;
import dev.chimken.graylist.abstracts.GraylistService;
import java.util.UUID;
import static dev.chimken.graylist.Util.fetchJSON;
import static dev.chimken.graylist.managers.ServiceManager.INTERNAL_NAMESPACE;
// TODO: Don't rely on mcprofile.io; implement Xbox API instead
public class Floodgate extends GraylistService {
public Floodgate() {
super("floodgate", INTERNAL_NAMESPACE, false);
}
@Override
public UUID findUUIDByName(String name) throws UserNotFound {
try {
final String id = fetchJSON("https://mcprofile.io/api/v1/bedrock/gamertag/" + name)
.get("floodgateuid")
.getAsString();
return UUID.fromString(id);
} catch (RuntimeException e) {
throw new UserNotFound(name);
}
}
@Override
public String findNameByUUID(UUID uuid) throws UserNotFound {
try {
return fetchJSON("https://mcprofile.io/api/v1/bedrock/fuid/" + uuid.toString())
.get("gamertag")
.getAsString();
} catch (RuntimeException e) {
throw new UserNotFound(uuid.toString());
}
}
}

View File

@@ -0,0 +1,40 @@
package dev.chimken.graylist.services;
import dev.chimken.graylist.Util;
import dev.chimken.graylist.abstracts.GraylistService;
import java.util.*;
import static dev.chimken.graylist.Util.fetchJSON;
import static dev.chimken.graylist.managers.ServiceManager.INTERNAL_NAMESPACE;
public class Mojang extends GraylistService {
public Mojang () {
super("mojang", INTERNAL_NAMESPACE, true);
}
@Override
public UUID findUUIDByName(String name) throws UserNotFound {
try {
final String id = fetchJSON("https://api.mojang.com/users/profiles/minecraft/" + name)
.get("id")
.getAsString();
// Use expandUUIDString because UUID is collapsed initially
return UUID.fromString(Util.expandUUIDString(id));
} catch (RuntimeException e) {
throw new UserNotFound(name);
}
}
@Override
public String findNameByUUID(UUID uuid) throws UserNotFound {
try {
return fetchJSON("https://api.minecraftservices.com/minecraft/profile/lookup/" + uuid.toString())
.get("name")
.getAsString();
} catch (RuntimeException e) {
throw new UserNotFound(uuid.toString());
}
}
}

View File

@@ -0,0 +1,29 @@
# ------------------
# Graylist Config
# ------------------
## Plugin Language
# Default is "en-US"
lang: en-US
## Service Configuration
services:
## Default service to use when a service isn't specified.
default: mojang
## List of service configs
# By default, built-in services (except Mojang) are disabled
configs:
internal.mojang:
# enabled: false
# prefix:
internal.floodgate:
#enabled: true
prefix: "."
internal.elyby:
#enabled: true
#prefix:

View File

@@ -0,0 +1,7 @@
graylist.add:
added: "Added <name> to the whitelist"
present: "<name> is already whitelisted"
graylist.remove:
removed: "Removed <name> from the whitelist"
absent: "<name> is not whitelisted"

View File

@@ -0,0 +1,70 @@
name: Graylist
version: ${version}
main: dev.chimken.graylist.Graylist
api-version: '1.21'
permissions:
graylist:
default: op
graylist.*:
description: Gives access to all Graylist commands
default: op
children:
graylist.manage: true
graylist.view: true
graylist.manage:
description: Gives access to all Graylist management commands
children:
graylist: true
graylist.command.add: true
graylist.command.remove: true
graylist.command.on: true
graylist.command.off: true
graylist.command.reload: true
graylist.view:
description: Gives access to all Graylist state viewing commands
children:
graylist: true
graylist.command.list: true
graylist.command.status: true
graylist.command.services: true
commands:
graylist:
usage: /graylist [subcommand]
permission: graylist
children:
add:
description: Add a user to the whitelist
usage: /graylist add <username|uuid> [service]
permission: graylist.command.add
remove:
description: Remove a user from the whitelist
usage: /graylist remove <username|uuid> [service]
permission: graylist.command.remove
on:
description: Enable the whitelist
usage: /graylist on
permission: graylist.command.on
off:
description: Disable the whitelist
usage: /graylist off
permission: graylist.command.off
list:
description: List all whitelisted users
usage: /graylist list
permission: graylist.command.list
status:
description: Get the current whitelist state
usage: /graylist status
permission: graylist.command.status
services:
description: Get all available services
usage: /graylist services
permission: graylist.command.services
reload:
description: Reload the whitelist
usage: /graylist reload
permission: graylist.command.reload