Skip to content

Commit

Permalink
Add veiled links feature
Browse files Browse the repository at this point in the history
Veiled links hide full links from the in-game chat, replacing those with
components that show the domain of the link, with a hover component
featuring the full link and a click event to open it in the browser.
  • Loading branch information
sciwhiz12 committed Feb 29, 2024
1 parent 7d2d471 commit a8f48d4
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// 1.20.4 2024-03-01T05:26:31.6773566 Languages: en_us for mod: concord
a6410469fc775e4579b4bf2128796e41e43dd44d assets/concord/lang/en_us.json
// 1.20.4 2024-03-01T06:11:22.5691513 Languages: en_us for mod: concord
9aada5c1a216ea46fcf82e4f1c65fe748c2bd007 assets/concord/lang/en_us.json
2 changes: 2 additions & 0 deletions src/data/resources/assets/concord/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"chat.concord.hover.header": "%s %s- %s %s",
"chat.concord.hover.reply": "Replied message: %s",
"chat.concord.hover.roles": "Roles: ",
"chat.concord.link.bare": "link:%s",
"chat.concord.link.hover.click": "Click to open URL in browser",
"chat.concord.reply": "in reply to %s: ",
"chat.concord.reply.unknown": "an unknown user",
"chat.concord.status.do_not_disturb": "Do Not Disturb",
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/dev/sciwhiz12/concord/ConcordConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class ConcordConfig {
public static final ModConfigSpec.EnumValue<CrownVisibility> HIDE_CROWN;
public static final ModConfigSpec.ConfigValue<String> WEBHOOK_AVATAR_URL;
public static final ModConfigSpec.BooleanValue HIDE_ROLES;
public static final ModConfigSpec.BooleanValue VEILED_LINKS;

public static final ModConfigSpec.BooleanValue ALLOW_MENTIONS;
public static final ModConfigSpec.BooleanValue ALLOW_PUBLIC_MENTIONS;
Expand Down Expand Up @@ -144,12 +145,13 @@ public static void register(ModContainer container) {
"This will cause in-game messages to have color formatting.",
"To use it, send a message with a dollar sign ($) followed by either an English-language color (ie. $red), or a hex code (ie. $#FF0000).",
"Names are delimited by a space which will be consumed, so the string \"this is a $red colored text\" will be shown as \"this is a colored text\".",
"Please note that Custom Formatting will override Legacy Formatting when enabled. This is intentional.")
"Overrides legacy formatting when enabled. Is overridden by veiled links.")
.define("use_custom_formatting", false);

USE_LEGACY_FORMATTING = builder.comment("Allow Discord users to put legacy-style chat formatting (&5, etc) in a message.",
"This will cause in-game messages to have color, bold, italic, strikethrough and \"obfuscated\" formatting.",
"Note however, that this only works with vanilla formatting codes, and is likely to cause weirdness.")
"Note however, that this only works with vanilla formatting codes, and is likely to cause weirdness.",
"Is overridden by custom formatting or veiled links.")
.define("use_legacy_formatting", false);


Expand All @@ -171,6 +173,13 @@ public static void register(ModContainer container) {
"such as for private servers whose players stream on public platforms.")
.define("hide_roles", false);

VEILED_LINKS = builder.comment("Obscures links by veiling the full URL through an on-hover component.",
"(For now, this affects all links, while Markdown is not implemented yet.)",
"This is useful to prevent public watchers of in-game chat (such as through players streaming on",
"public platforms) from accessing links, and/or reducing the space links might occupy in in-game chat.",
"Overrides legacy formatting and custom formatting when enabled.")
.define("veiled_links", true);

builder.pop();
}

Expand Down
73 changes: 68 additions & 5 deletions src/main/java/dev/sciwhiz12/concord/msg/FormattingUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,83 @@

package dev.sciwhiz12.concord.msg;

import com.google.common.base.CharMatcher;
import dev.sciwhiz12.concord.ConcordConfig;
import dev.sciwhiz12.concord.util.Translations;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.TextColor;
import net.minecraft.network.chat.*;

import static net.minecraft.ChatFormatting.WHITE;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static net.minecraft.ChatFormatting.*;

// Package-private class for formatting-related helper/utility methods
final class FormattingUtilities {
private FormattingUtilities() {
}

// Adapted from net.neoforged.neoforge.common.CommonHooks.URL_PATTERN
private static final Pattern URL_PATTERN = Pattern.compile(
"https?://(?<domain>(www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6})\\b([-a-zA-Z0-9()@:%_+.~#?&/=]*)",
Pattern.CASE_INSENSITIVE);
private static final CharMatcher END_PUNCTUATION = CharMatcher.anyOf(".,;:)]\"'");

public static MutableComponent redactLinks(String input) {
final Matcher urlMatcher = URL_PATTERN.matcher(input);
if (!urlMatcher.find()) {
// No URLs found to redact -- return in full
return Component.literal(input).withStyle(WHITE);
}

MutableComponent base = Component.literal("").withStyle(WHITE);
int lastPosition = 0;
do {
final int urlStart = urlMatcher.start();
int urlEnd = urlMatcher.end();
final String urlDomain = urlMatcher.group("domain");

// Adjust to avoid including common punctuation at the end of the URL
final String url;
final String originalUrl = urlMatcher.group();
final String trimmedUrl = END_PUNCTUATION.trimTrailingFrom(originalUrl);
if (!trimmedUrl.equals(originalUrl)) {
// Move the URL end leftwards the amount of characters removed from the trimmed URL
urlEnd -= originalUrl.length() - trimmedUrl.length();
url = trimmedUrl;
} else {
url = originalUrl;
}

// Append everything from before the URL start since the last URL (or the string start, for the first URL)
base.append(input.substring(lastPosition, urlStart));

// Create the link component
MutableComponent linkComponent = Translations.CHAT_BARE_LINK.component(urlDomain);
linkComponent = ComponentUtils.wrapInSquareBrackets(linkComponent);
linkComponent.withStyle(BLUE);

final MutableComponent attachmentHoverComponent = Component.literal("");
attachmentHoverComponent.append(Component.literal(url).withStyle(DARK_GRAY)).append("\n");
attachmentHoverComponent.append(Translations.HOVER_LINK_CLICK.component());

linkComponent.withStyle(style ->
style.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, attachmentHoverComponent))
.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url)));

base.append(linkComponent);

lastPosition = urlEnd;
} while (urlMatcher.find());

// Add everything up to the end of the string
if (lastPosition < input.length()) {
base.append(input.substring(lastPosition));
}

return base;
}

/**
* Search a user-input string for legacy-style ChatFormatting.
* ie. the input "&5Sup?" will be sent to Minecraft as "Sup?" with a purple color.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@ static MutableComponent createUserComponent(boolean useIcons, ConcordConfig.Crow

static MutableComponent createContentComponent(Message message) {
final String content = message.getContentDisplay();
final MutableComponent text = FormattingUtilities.processCustomFormatting(content);
final MutableComponent text;
if (ConcordConfig.VEILED_LINKS.get()) {
text = FormattingUtilities.redactLinks(content);
} else {
text = FormattingUtilities.processCustomFormatting(content);
}

boolean skipSpace = content.length() <= 0 || Character.isWhitespace(content.codePointAt(content.length() - 1));
for (StickerItem sticker : message.getStickers()) {
Expand Down Expand Up @@ -143,6 +148,7 @@ static MutableComponent createContentComponent(Message message) {
if (extension != null) {
attachmentComponent = Translations.CHAT_ATTACHMENT_WITH_EXTENSION.component(extension);
} else {
// TODO: fix bug!
attachmentComponent = Translations.CHAT_ATTACHMENT_WITH_EXTENSION.component();
}
attachmentComponent = ComponentUtils.wrapInSquareBrackets(attachmentComponent);
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/dev/sciwhiz12/concord/util/Translations.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public enum Translations implements Translation {
CHAT_ATTACHMENT_WITH_EXTENSION("chat", "attachment", "1.1.0", "attachment:%s"),
CHAT_ATTACHMENT_NO_EXTENSION("chat", "attachment.no_extension", "1.1.0", "attachment"),
CHAT_STICKER("chat", "sticker", "1.1.0", "sticker:%s"),
CHAT_BARE_LINK("chat", "link.bare", "1.3.0", "link:%s"),

// Hover text

Expand All @@ -53,6 +54,8 @@ public enum Translations implements Translation {

HOVER_ATTACHMENT_FILENAME("chat", "attachment.hover.filename", "1.1.0", "File name: %s"),
HOVER_ATTACHMENT_CLICK("chat", "attachment.hover.click", "1.1.0", "Click to open attachment in browser"),

HOVER_LINK_CLICK("chat", "link.hover.click", "1.3.0", "Click to open URL in browser"),

// Commands

Expand Down

0 comments on commit a8f48d4

Please sign in to comment.