Skip to content

Commit

Permalink
config improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
maximumpower55 committed May 21, 2024
1 parent dad8609 commit b1d0ed5
Show file tree
Hide file tree
Showing 15 changed files with 183 additions and 122 deletions.
87 changes: 56 additions & 31 deletions src/main/java/one/devos/nautical/teabridge/Config.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
package one.devos.nautical.teabridge;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.net.URI;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.function.Function;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonParser;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import one.devos.nautical.teabridge.util.MoreCodecs;

public record Config(
Discord discord,
Expand All @@ -21,45 +30,61 @@ public record Config(
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().setLenient().disableHtmlEscaping().create();

public static final Codec<Config> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Discord.CODEC.optionalFieldOf("discord", Discord.DEFAULT).forGetter(Config::discord),
Avatars.CODEC.optionalFieldOf("avatars", Avatars.DEFAULT).forGetter(Config::avatars),
Game.CODEC.optionalFieldOf("game", Game.DEFAULT).forGetter(Config::game),
Crashes.CODEC.optionalFieldOf("crashes", Crashes.DEFAULT).forGetter(Config::crashes),
Discord.CODEC.fieldOf("discord").forGetter(Config::discord),
Avatars.CODEC.fieldOf("avatars").forGetter(Config::avatars),
Game.CODEC.fieldOf("game").forGetter(Config::game),
Crashes.CODEC.fieldOf("crashes").forGetter(Config::crashes),
Codec.BOOL.optionalFieldOf("debug", false).forGetter(Config::debug)
).apply(instance, Config::new));

public static Config INSTANCE = new Config(Discord.DEFAULT, Avatars.DEFAULT, Game.DEFAULT, Crashes.DEFAULT, false);

public static void load() throws Exception {
Path configPath = PlatformUtil.getConfigDir().resolve("teabridge.json");
if (Files.exists(configPath))
INSTANCE = CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(Files.newBufferedReader(configPath))).getOrThrow();
Files.writeString(configPath, GSON.toJson(CODEC.encodeStart(JsonOps.INSTANCE, INSTANCE).getOrThrow()), StandardCharsets.UTF_8);
public static final Config DEFAULT = new Config(Discord.DEFAULT, Avatars.DEFAULT, Game.DEFAULT, Crashes.DEFAULT, false);

public static DataResult<Config> load(Path path) {
try {
if (Files.exists(path)) {
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
return CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader));
}
} else {
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8, StandardOpenOption.CREATE)) {
GSON.toJson(CODEC.encodeStart(JsonOps.INSTANCE, DEFAULT).getOrThrow(), writer);
return DataResult.success(DEFAULT);
}
}
} catch (Exception e) {
return DataResult.error(e::getMessage);
}
}

public record Discord(String token, String webhook, int pkMessageDelay, boolean pkMessageDelayMilliseconds) {
public record Discord(String token, URI webhook, int pkMessageDelay, boolean pkMessageDelayMilliseconds) {
public static final String DEFAULT_TOKEN = "";
public static final String DEFAULT_WEBHOOK = "";
public static final URI DEFAULT_WEBHOOK = URI.create("");
public static final int PK_MESSAGE_DELAY = 0;
public static final boolean PK_MESSAGE_DELAY_MILLISECONDS = true;

public static final Codec<Discord> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.STRING.optionalFieldOf("token", DEFAULT_TOKEN).forGetter(Discord::token),
Codec.STRING.optionalFieldOf("webhook", DEFAULT_WEBHOOK).forGetter(Discord::webhook),
Codec.INT.optionalFieldOf("pkMessageDelay", PK_MESSAGE_DELAY).forGetter(Discord::pkMessageDelay),
Codec.BOOL.optionalFieldOf("pkMessageDelayMilliseconds", PK_MESSAGE_DELAY_MILLISECONDS).forGetter(Discord::pkMessageDelayMilliseconds)
Codec.STRING.fieldOf("token").forGetter(Discord::token),
MoreCodecs.URI.fieldOf("webhook").forGetter(Discord::webhook),
Codec.INT.fieldOf("pkMessageDelay").forGetter(Discord::pkMessageDelay),
Codec.BOOL.fieldOf("pkMessageDelayMilliseconds").forGetter(Discord::pkMessageDelayMilliseconds)
).apply(instance, Discord::new));

public static final Discord DEFAULT = new Discord(DEFAULT_TOKEN, DEFAULT_WEBHOOK, PK_MESSAGE_DELAY, PK_MESSAGE_DELAY_MILLISECONDS);
}

public record Avatars(String avatarUrl, boolean useTextureId) {
public static final String DEFAULT_AVATAR_URL = "https://api.nucleoid.xyz/skin/face/256/%s";
public record Avatars(Function<String, URI> avatarUrl, boolean useTextureId) {
public static final Function<String, URI> DEFAULT_AVATAR_URL = a -> URI.create("https://api.nucleoid.xyz/skin/face/256/%s".formatted(a));
public static final boolean DEFAULT_USE_TEXTURE_ID = false;

public static final Codec<Avatars> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.STRING.optionalFieldOf("avatarUrl", DEFAULT_AVATAR_URL).forGetter(Avatars::avatarUrl),
Codec.BOOL.optionalFieldOf("useTextureId", DEFAULT_USE_TEXTURE_ID).forGetter(Avatars::useTextureId)
Codec.STRING
.comapFlatMap(
MoreCodecs.checkedMapper(a -> (Function<String, URI>) b -> URI.create(a.formatted(b))),
b -> URLDecoder.decode(b.apply(URLEncoder.encode("%s", StandardCharsets.UTF_8)).toString(), StandardCharsets.UTF_8)
)
.fieldOf("avatarUrl")
.forGetter(Avatars::avatarUrl),
Codec.BOOL.fieldOf("useTextureId").forGetter(Avatars::useTextureId)
).apply(instance, Avatars::new));

public static final Avatars DEFAULT = new Avatars(DEFAULT_AVATAR_URL, DEFAULT_USE_TEXTURE_ID);
Expand Down Expand Up @@ -87,15 +112,15 @@ public record Game(
public static final boolean DEFAULT_MIRROR_COMMAND_MESSAGES = true;

public static final Codec<Game> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.STRING.optionalFieldOf("serverStartingMessage", DEFAULT_SERVER_STARTING_MESSAGE).forGetter(Game::serverStartingMessage),
Codec.STRING.optionalFieldOf("serverStartMessage", DEFAULT_SERVER_START_MESSAGE).forGetter(Game::serverStartMessage),
Codec.STRING.optionalFieldOf("serverStopMessage", DEFAULT_SERVER_STOP_MESSAGE).forGetter(Game::serverStopMessage),
Codec.STRING.optionalFieldOf("serverCrashMessage", DEFAULT_SERVER_CRASH_MESSAGE).forGetter(Game::serverCrashMessage),
Codec.BOOL.optionalFieldOf("mirrorJoin", true).forGetter(Game::mirrorJoin),
Codec.BOOL.optionalFieldOf("mirrorLeave", true).forGetter(Game::mirrorLeave),
Codec.BOOL.optionalFieldOf("mirrorDeath", true).forGetter(Game::mirrorDeath),
Codec.BOOL.optionalFieldOf("mirrorAdvancements", true).forGetter(Game::mirrorAdvancements),
Codec.BOOL.optionalFieldOf("mirrorCommandMessages", true).forGetter(Game::mirrorCommandMessages)
Codec.STRING.fieldOf("serverStartingMessage").forGetter(Game::serverStartingMessage),
Codec.STRING.fieldOf("serverStartMessage").forGetter(Game::serverStartMessage),
Codec.STRING.fieldOf("serverStopMessage").forGetter(Game::serverStopMessage),
Codec.STRING.fieldOf("serverCrashMessage").forGetter(Game::serverCrashMessage),
Codec.BOOL.fieldOf("mirrorJoin").forGetter(Game::mirrorJoin),
Codec.BOOL.fieldOf("mirrorLeave").forGetter(Game::mirrorLeave),
Codec.BOOL.fieldOf("mirrorDeath").forGetter(Game::mirrorDeath),
Codec.BOOL.fieldOf("mirrorAdvancements").forGetter(Game::mirrorAdvancements),
Codec.BOOL.fieldOf("mirrorCommandMessages").forGetter(Game::mirrorCommandMessages)
).apply(instance, Game::new));

public static final Game DEFAULT = new Game(
Expand All @@ -115,7 +140,7 @@ public record Crashes(boolean uploadToMclogs) {
public static final boolean DEFAULT_UPLOAD_TO_MCLOGS = true;

public static final Codec<Crashes> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.BOOL.optionalFieldOf("uploadToMclogs", DEFAULT_UPLOAD_TO_MCLOGS).forGetter(Crashes::uploadToMclogs)
Codec.BOOL.fieldOf("uploadToMclogs").forGetter(Crashes::uploadToMclogs)
).apply(instance, Crashes::new));

public static final Crashes DEFAULT = new Crashes(DEFAULT_UPLOAD_TO_MCLOGS);
Expand Down
68 changes: 44 additions & 24 deletions src/main/java/one/devos/nautical/teabridge/TeaBridge.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package one.devos.nautical.teabridge;

import java.net.http.HttpClient;
import java.nio.file.Path;

import com.mojang.serialization.DataResult;
import net.fabricmc.loader.api.FabricLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -25,31 +28,34 @@
public class TeaBridge {
public static final Logger LOGGER = LoggerFactory.getLogger("TeaBridge");

public static final HttpClient CLIENT = HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build();
public static final HttpClient CLIENT = HttpClient.newBuilder().build();

public static final String MOD_ID = "teabridge";
public static final Path CONFIG_PATH = FabricLoader.getInstance().getConfigDir().resolve(MOD_ID + ".json");

public static Config config = Config.DEFAULT;

public static void initialize() {
try {
Config.load();
} catch (Exception e) {
LOGGER.warn("Failed to load config using defaults : ", e);
}
Config.load(CONFIG_PATH)
.ifError(e -> LOGGER.error("Failed to load config using defaults : {}", e))
.ifSuccess(loaded -> config = loaded);
Discord.start();

PlatformUtil.registerCommand(TeaBridge::registerCommands);
}

public static void onServerStarting(MinecraftServer server) {
if (Config.INSTANCE.debug()) TeaBridge.LOGGER.warn("DEBUG MODE IS ENABLED, THIS WILL LOG EVERYTHING WILL CAUSE LAG SPIKES!!!!!!");
Discord.send(Config.INSTANCE.game().serverStartingMessage());
if (TeaBridge.config.debug()) TeaBridge.LOGGER.warn("DEBUG MODE IS ENABLED, THIS WILL LOG EVERYTHING WILL CAUSE LAG SPIKES!!!!!!");
Discord.send(TeaBridge.config.game().serverStartingMessage());
}

public static void onServerStart(MinecraftServer server) {
ChannelListener.INSTANCE.setServer(server);
Discord.send(Config.INSTANCE.game().serverStartMessage());
Discord.send(TeaBridge.config.game().serverStartMessage());
}

public static void onServerStop(MinecraftServer server) {
if (!CrashHandler.CRASH_VALUE.get()) Discord.send(Config.INSTANCE.game().serverStopMessage());
if (!CrashHandler.CRASH_VALUE.get()) Discord.send(TeaBridge.config.game().serverStopMessage());
Discord.stop();
}

Expand All @@ -62,23 +68,37 @@ public static void onChatMessage(PlayerChatMessage message, ServerPlayer sender,
}

public static void onCommandMessage(PlayerChatMessage message, CommandSourceStack source, ChatType.Bound params) {
if (!Config.INSTANCE.game().mirrorCommandMessages()) return;
if (!TeaBridge.config.game().mirrorCommandMessages()) return;
if (!source.isPlayer()) Discord.send(message.signedContent());
}

private static void registerCommands(CommandDispatcher<CommandSourceStack> dispatcher) {
dispatcher.register(Commands.literal("teabridge").then(
Commands.literal("reloadConfig").executes(command -> {
try {
Config.load();
command.getSource().sendSuccess(() -> Component.literal("Config reloaded!").withStyle(ChatFormatting.GREEN), false);
} catch (Exception e) {
command.getSource().sendFailure(Component.literal("Failed to reload config!").withStyle(ChatFormatting.RED));
LOGGER.warn("Failed to reload config : ", e);
}

return Command.SINGLE_SUCCESS;
})
));
dispatcher.register(Commands.literal("teabridge")
.requires(source -> source.hasPermission(2))
.then(
Commands.literal("reloadConfig")
.executes(command -> {
CommandSourceStack source = command.getSource();
DataResult<Config> loadResult = Config.load(CONFIG_PATH);
loadResult
.ifError(e -> {
source.sendFailure(
Component.literal("Failed to reload config! check log for details")
.withStyle(ChatFormatting.RED)
);
LOGGER.warn("Failed to reload config : {}", e);
})
.ifSuccess(loaded -> {
config = loaded;
source.sendSuccess(
() -> Component.literal("Config reloaded!")
.withStyle(ChatFormatting.GREEN),
false
);
});
return loadResult.isSuccess() ? Command.SINGLE_SUCCESS : 0;
})
)
);
}
}
22 changes: 10 additions & 12 deletions src/main/java/one/devos/nautical/teabridge/discord/Discord.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.requests.GatewayIntent;
import one.devos.nautical.teabridge.TeaBridge;
import one.devos.nautical.teabridge.Config;
import one.devos.nautical.teabridge.util.MoreCodecs;
import org.jetbrains.annotations.Nullable;

public class Discord {
Expand All @@ -28,34 +28,33 @@ public class Discord {

public static final ProtoWebHook WEB_HOOK = new ProtoWebHook(
() -> selfMember.get().getEffectiveName(),
() -> selfMember.get().getEffectiveAvatarUrl()
() -> URI.create(selfMember.get().getEffectiveAvatarUrl())
);

private static final LinkedBlockingQueue<ScheduledMessage> scheduledMessages = new LinkedBlockingQueue<>();

public static void start() {
if (Config.INSTANCE.discord().token().isEmpty()) {
if (TeaBridge.config.discord().token().isEmpty()) {
TeaBridge.LOGGER.error("Unable to load, no Discord token is specified!");
return;
}

if (Config.INSTANCE.discord().webhook().isEmpty()) {
if (TeaBridge.config.discord().webhook().toString().isEmpty()) {
TeaBridge.LOGGER.error("Unable to load, no Discord webhook is specified!");
return;
}

try {
// Get required data from webhook
HttpResponse<String> response = TeaBridge.CLIENT.send(HttpRequest.newBuilder()
.uri(URI.create(Config.INSTANCE.discord().webhook()))
HttpResponse<String> response = TeaBridge.CLIENT.send(HttpRequest.newBuilder(TeaBridge.config.discord().webhook())
.GET()
.build(), HttpResponse.BodyHandlers.ofString());
if (response.statusCode() / 100 != 2) throw new Exception("Non-success status code from request " + response);
WebHookData webHookData = WebHookData.fromJson(JsonParser.parseString(response.body())).getOrThrow();
if (Config.INSTANCE.debug()) TeaBridge.LOGGER.warn("Webhook response : " + response.body());
if (TeaBridge.config.debug()) TeaBridge.LOGGER.warn("Webhook response : " + response.body());
ChannelListener.INSTANCE.setChannel(webHookData.channelId);

jda = JDABuilder.createDefault(Config.INSTANCE.discord().token())
jda = JDABuilder.createDefault(TeaBridge.config.discord().token())
.enableIntents(GatewayIntent.MESSAGE_CONTENT)
.addEventListeners(ChannelListener.INSTANCE, CommandUtils.INSTANCE)
.build();
Expand Down Expand Up @@ -100,8 +99,7 @@ private static void scheduledSend(ScheduledMessage scheduledMessage) {
String displayName = scheduledMessage.displayName;
if (jda != null) {
try {
HttpResponse<String> response = TeaBridge.CLIENT.send(HttpRequest.newBuilder()
.uri(URI.create(Config.INSTANCE.discord().webhook()))
HttpResponse<String> response = TeaBridge.CLIENT.send(HttpRequest.newBuilder(TeaBridge.config.discord().webhook())
.POST(HttpRequest.BodyPublishers.ofString(webHook.createMessage(message, displayName).toJson().getOrThrow()))
.header("Content-Type", "application/json; charset=utf-8")
.build(), HttpResponse.BodyHandlers.ofString());
Expand All @@ -121,8 +119,8 @@ public static void stop() {

private record WebHookData(long guildId, long channelId) {
public static final Codec<WebHookData> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.STRING.fieldOf("guild_id").xmap(Long::parseLong, String::valueOf).forGetter(WebHookData::guildId),
Codec.STRING.fieldOf("channel_id").xmap(Long::parseLong, String::valueOf).forGetter(WebHookData::channelId)
MoreCodecs.fromString(Long::parseUnsignedLong).fieldOf("guild_id").forGetter(WebHookData::guildId),
MoreCodecs.fromString(Long::parseUnsignedLong).fieldOf("channel_id").forGetter(WebHookData::channelId)
).apply(instance, WebHookData::new));

public static DataResult<WebHookData> fromJson(JsonElement json) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import java.util.function.BiConsumer;

import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import one.devos.nautical.teabridge.Config;
import one.devos.nautical.teabridge.TeaBridge;


Expand All @@ -18,7 +17,7 @@ class PKCompat {
private static final LinkedBlockingQueue<ScheduledMessage> scheduledMessages = new LinkedBlockingQueue<>();

static void initIfEnabled() {
if (!(Config.INSTANCE.discord().pkMessageDelay() > 0)) return;
if (!(TeaBridge.config.discord().pkMessageDelay() > 0)) return;
var thread = new Thread(() -> {
while (true) {
while (scheduledMessages.peek() == null) {}
Expand All @@ -34,12 +33,12 @@ static void initIfEnabled() {
}

static void await(MessageReceivedEvent event, BiConsumer<MessageReceivedEvent, Boolean> handler) {
if (Config.INSTANCE.discord().pkMessageDelay() > 0) {
if (TeaBridge.config.discord().pkMessageDelay() > 0) {
scheduledMessages.add(new ScheduledMessage(
event,
handler,
Config.INSTANCE.discord().pkMessageDelayMilliseconds() ?
Instant.now().plusMillis(Config.INSTANCE.discord().pkMessageDelay()) : Instant.now().plusSeconds(Config.INSTANCE.discord().pkMessageDelay())
TeaBridge.config.discord().pkMessageDelayMilliseconds() ?
Instant.now().plusMillis(TeaBridge.config.discord().pkMessageDelay()) : Instant.now().plusSeconds(TeaBridge.config.discord().pkMessageDelay())
));
return;
}
Expand Down
Loading

0 comments on commit b1d0ed5

Please sign in to comment.