diff --git a/README.md b/README.md index cf814b1..f2b43dc 100644 --- a/README.md +++ b/README.md @@ -47,12 +47,19 @@ settings menu edits the prefix and the chat-message format. For example, in `tiktok.yml`: ```yaml -prefix: "&#FE2C55[TikTok] &r| " +prefix: "&#FE2C55[TikTok]&r" events: + chat: "{prefix} | {username} > {message}" join: "&e@{username} joined" like: "{prefix}{username} sent {data.likeCount} likes" ``` +The default prefix is just the platform tag (e.g. `[Twitch]`) and the default +chat-message format is `{prefix} | {username} > {message}`. Pressing Done in the +settings menu saves the prefix and chat format to these files, and the menu +reads them back each time it opens. An unclosed placeholder such as `{message` +is closed automatically. + Delete an `events:` line to restore that event's default. Every event is shown in chat by default; change where each one goes (chat, action bar, toast, hidden) in the settings menu. diff --git a/gradle.properties b/gradle.properties index 93c1816..2e06f9f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,6 @@ loom_version=1.19.3 legacy_loom_version=1.14.10 java_version=25 -mod_version=0.1.5 +mod_version=0.1.6 maven_group=dev.streamchat archives_base_name=stream-chat diff --git a/src/main/java/dev/streamchat/StreamChatConfig.java b/src/main/java/dev/streamchat/StreamChatConfig.java index ddc2602..57aaab9 100644 --- a/src/main/java/dev/streamchat/StreamChatConfig.java +++ b/src/main/java/dev/streamchat/StreamChatConfig.java @@ -29,6 +29,7 @@ public final class StreamChatConfig { private static final Path DIR = FabricLoader.getInstance().getConfigDir().resolve("stream-chat"); private static final Path PATH = DIR.resolve("stream-chat.json"); private static final Path LEGACY_PATH = FabricLoader.getInstance().getConfigDir().resolve("stream-chat.json"); + private static final String CHAT_FORMAT = "{prefix} | {username} > {message}"; private static final Map> DEFAULT_FORMATS = defaultFormats(); private static final Pattern PLACEHOLDER = Pattern.compile("\\{(prefix|platform|event|username|message|data\\.[A-Za-z0-9_]+)}"); @@ -43,7 +44,7 @@ public final class StreamChatConfig { private static Map> defaultFormats() { Map> all = new EnumMap<>(Platform.class); Map twitch = new LinkedHashMap<>(); - twitch.put("ChatMessage", "{prefix}{username} > {message}"); + twitch.put("ChatMessage", CHAT_FORMAT); twitch.put("Follow", "{prefix}{username} followed"); twitch.put("Sub", "{prefix}{username} subscribed"); twitch.put("ReSub", "{prefix}{username} resubscribed: {message}"); @@ -52,7 +53,7 @@ public final class StreamChatConfig { twitch.put("Raid", "{prefix}{username} raided with {data.viewers} viewers"); all.put(Platform.TWITCH, twitch); Map youtube = new LinkedHashMap<>(); - youtube.put("Message", "{prefix}{username} > {message}"); + youtube.put("Message", CHAT_FORMAT); youtube.put("SuperChat", "{prefix}{username} sent a Super Chat {data.amount}: {message}"); youtube.put("SuperSticker", "{prefix}{username} sent a Super Sticker"); youtube.put("MemberMilestone", "{prefix}{username} member milestone: {message}"); @@ -60,7 +61,7 @@ public final class StreamChatConfig { youtube.put("GiftMembershipReceived", "{prefix}{username} received a gifted membership"); all.put(Platform.YOUTUBE, youtube); Map tiktok = new LinkedHashMap<>(); - tiktok.put("chat", "{prefix}{username} > {message}"); + tiktok.put("chat", CHAT_FORMAT); tiktok.put("gift", "{prefix}{username} sent {message} x{data.repeatCount}"); tiktok.put("share", "{prefix}{username} shared the stream"); tiktok.put("follow", "{prefix}{username} followed"); @@ -89,9 +90,9 @@ public final class StreamChatConfig { public Map emoteReplacements = new HashMap<>(); public StreamChatConfig() { - prefixes.put("TWITCH", "[Twitch] | "); - prefixes.put("YOUTUBE", "[YouTube] | "); - prefixes.put("TIKTOK", "[TikTok] | "); + prefixes.put("TWITCH", "[Twitch]"); + prefixes.put("YOUTUBE", "[YouTube]"); + prefixes.put("TIKTOK", "[TikTok]"); templates.put("TWITCH", "{prefix}{username} > {message}"); templates.put("YOUTUBE", "{prefix}{username} > {message}"); templates.put("TIKTOK", "{prefix}{username} > {message}"); @@ -162,6 +163,7 @@ public final class StreamChatConfig { if (file.prefix != null) prefixes.put(name, file.prefix); if (file.usernameColor != null) usernameColors.put(name, file.usernameColor); if (file.format != null && !file.format.isBlank()) templates.put(name, file.format); + file.events.replaceAll((event, format) -> closeBraces(format)); eventFormats.computeIfAbsent(name, key -> new LinkedHashMap<>()).putAll(file.events); } catch (IOException exception) { LOGGER.log(Level.SEVERE, "Could not read " + path + "; using defaults.", exception); @@ -203,7 +205,7 @@ public final class StreamChatConfig { } public String getPrefix(Platform platform) { - return prefixes.getOrDefault(platform.name(), "[" + platform.name() + "] | "); + return prefixes.getOrDefault(platform.name(), "[" + platform.name() + "]"); } public String format(StreamEvent event) { @@ -267,7 +269,13 @@ public final class StreamChatConfig { public void setChatFormat(Platform platform, String format) { eventFormats.computeIfAbsent(platform.name(), key -> new LinkedHashMap<>()) - .put(EVENT_NAMES.get(platform).get(0), format); + .put(EVENT_NAMES.get(platform).get(0), closeBraces(format)); + } + + /** Appends a missing '}' so a half-typed placeholder such as "{message" still works. */ + static String closeBraces(String format) { + int open = format.lastIndexOf('{'); + return open >= 0 && format.indexOf('}', open) < 0 ? format + "}" : format; } /** Re-reads every config file from disk into this instance. */ diff --git a/src/versions/legacy/java/dev/streamchat/StreamChatScreen.java b/src/versions/legacy/java/dev/streamchat/StreamChatScreen.java index 1021969..04e3e21 100644 --- a/src/versions/legacy/java/dev/streamchat/StreamChatScreen.java +++ b/src/versions/legacy/java/dev/streamchat/StreamChatScreen.java @@ -27,6 +27,7 @@ public final class StreamChatScreen extends Screen { private EditBox tikFinityUrl; private EditBox prefix; private EditBox template; + private Platform boxPlatform; private final List scrollable = new ArrayList<>(); private final List baseY = new ArrayList<>(); private int scroll; @@ -41,6 +42,8 @@ public final class StreamChatScreen extends Screen { @Override protected void init() { + // Window resizes re-run init(); keep unsaved edits instead of reverting to the stored values. + if (prefix != null) commitFormatting(); int left = width / 2 - 190; int rowWidth = 380; int buttonWidth = 120; @@ -94,6 +97,7 @@ public final class StreamChatScreen extends Screen { prefix = new EditBox(font, left, 230, rowWidth, 20, Component.literal("Platform prefix")); prefix.setValue(config.getPrefix(selectedPlatform)); + boxPlatform = selectedPlatform; prefix.setMaxLength(128); addRenderableWidget(prefix); template = new EditBox(font, left, 270, rowWidth, 20, Component.literal("Chat message format")); @@ -183,31 +187,30 @@ public final class StreamChatScreen extends Screen { @Override public void onClose() { - config.streamerBotUrl = streamerBotUrl.getValue().trim(); - config.streamerBotPassword = streamerBotPassword.getValue(); - config.tikFinityUrl = tikFinityUrl.getValue().trim(); - config.prefixes.put(selectedPlatform.name(), prefix.getValue()); - config.setChatFormat(selectedPlatform, template.getValue()); + commitFormatting(); config.save(); connections.configurationChanged(); minecraft.setScreen(parent); } - private void select(Platform platform) { + private void commitFormatting() { config.streamerBotUrl = streamerBotUrl.getValue().trim(); config.streamerBotPassword = streamerBotPassword.getValue(); config.tikFinityUrl = tikFinityUrl.getValue().trim(); - config.prefixes.put(selectedPlatform.name(), prefix.getValue()); - config.setChatFormat(selectedPlatform, template.getValue()); + config.prefixes.put(boxPlatform.name(), prefix.getValue()); + config.setChatFormat(boxPlatform, template.getValue()); + } + + private void select(Platform platform) { + commitFormatting(); selectedPlatform = platform; - prefix.setValue(config.getPrefix(platform)); - template.setValue(config.getChatFormat(platform)); rebuildRoutes(); } private void reloadConfig() { config.reload(); connections.configurationChanged(); + prefix = null; rebuildRoutes(); } diff --git a/src/versions/modern/java/dev/streamchat/StreamChatScreen.java b/src/versions/modern/java/dev/streamchat/StreamChatScreen.java index c6ab9e7..956756e 100644 --- a/src/versions/modern/java/dev/streamchat/StreamChatScreen.java +++ b/src/versions/modern/java/dev/streamchat/StreamChatScreen.java @@ -28,6 +28,7 @@ public final class StreamChatScreen extends Screen { private EditBox tikFinityUrl; private EditBox prefix; private EditBox template; + private Platform boxPlatform; private final List scrollable = new ArrayList<>(); private final List baseY = new ArrayList<>(); private int scroll; @@ -42,6 +43,8 @@ public final class StreamChatScreen extends Screen { @Override protected void init() { + // Window resizes re-run init(); keep unsaved edits instead of reverting to the stored values. + if (prefix != null) commitFormatting(); int left = width / 2 - 190; int rowWidth = 380; int buttonWidth = 120; @@ -95,6 +98,7 @@ public final class StreamChatScreen extends Screen { prefix = new EditBox(font, left, 230, rowWidth, 20, Component.literal("Platform prefix")); prefix.setValue(config.getPrefix(selectedPlatform)); + boxPlatform = selectedPlatform; prefix.setMaxLength(128); addRenderableWidget(prefix); template = new EditBox(font, left, 270, rowWidth, 20, Component.literal("Chat message format")); @@ -184,31 +188,30 @@ public final class StreamChatScreen extends Screen { @Override public void onClose() { - config.streamerBotUrl = streamerBotUrl.getValue().trim(); - config.streamerBotPassword = streamerBotPassword.getValue(); - config.tikFinityUrl = tikFinityUrl.getValue().trim(); - config.prefixes.put(selectedPlatform.name(), prefix.getValue()); - config.setChatFormat(selectedPlatform, template.getValue()); + commitFormatting(); config.save(); connections.configurationChanged(); minecraft.setScreen(parent); } - private void select(Platform platform) { + private void commitFormatting() { config.streamerBotUrl = streamerBotUrl.getValue().trim(); config.streamerBotPassword = streamerBotPassword.getValue(); config.tikFinityUrl = tikFinityUrl.getValue().trim(); - config.prefixes.put(selectedPlatform.name(), prefix.getValue()); - config.setChatFormat(selectedPlatform, template.getValue()); + config.prefixes.put(boxPlatform.name(), prefix.getValue()); + config.setChatFormat(boxPlatform, template.getValue()); + } + + private void select(Platform platform) { + commitFormatting(); selectedPlatform = platform; - prefix.setValue(config.getPrefix(platform)); - template.setValue(config.getChatFormat(platform)); rebuildRoutes(); } private void reloadConfig() { config.reload(); connections.configurationChanged(); + prefix = null; rebuildRoutes(); }