mirror of
https://github.com/System-End/Discord-MC-Chat.git
synced 2026-04-19 19:45:14 +00:00
fix spoiler edge cases and webhook reply fallback consistency
Co-authored-by: Xujiayao <58985541+Xujiayao@users.noreply.github.com>
This commit is contained in:
parent
98dcc2c2ca
commit
abcf8a94ce
2 changed files with 61 additions and 23 deletions
|
|
@ -47,7 +47,20 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
private static final ConcurrentHashMap<String, CachedMessage> messageCache = new ConcurrentHashMap<>();
|
||||
private static final int MAX_CACHE_SIZE = 200;
|
||||
|
||||
private record CachedMessage(String authorName, String authorRoleColor, String contentRaw, Message contextMessage) {}
|
||||
/**
|
||||
* Cached Discord message metadata for reply/edit/delete context rendering.
|
||||
* <p>
|
||||
* Stores the already-rendered one-line reply preview so fallback rendering keeps the
|
||||
* same formatting pipeline (including webhook message formatting) without retaining full
|
||||
* JDA {@link Message} objects in memory.
|
||||
* {@code replySegments} may be null when a preview cannot be produced for a message.
|
||||
*
|
||||
* @param authorName Cached effective name of the message author.
|
||||
* @param authorRoleColor Cached role color of the message author.
|
||||
* @param contentRaw Cached raw content for fallback rebuilding.
|
||||
* @param replySegments Cached rendered reply preview, nullable.
|
||||
*/
|
||||
private record CachedMessage(String authorName, String authorRoleColor, String contentRaw, List<TextSegment> replySegments) {}
|
||||
|
||||
/**
|
||||
* Resolves the OP Level credential for a Discord user based on config mappings.
|
||||
|
|
@ -380,9 +393,12 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cachedRef.authorName(),
|
||||
cachedRef.authorRoleColor(),
|
||||
cachedRef.contextMessage(),
|
||||
null,
|
||||
cachedRef.contentRaw()
|
||||
);
|
||||
if (cachedRef.replySegments() != null && !cachedRef.replySegments().isEmpty()) {
|
||||
replySegments = cachedRef.replySegments();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -495,12 +511,15 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
CachedMessage cached = messageCache.get(message.getId());
|
||||
List<TextSegment> replySegments = null;
|
||||
if (cached != null && cached.contentRaw() != null) {
|
||||
replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cached.authorName(),
|
||||
cached.authorRoleColor(),
|
||||
cached.contextMessage(),
|
||||
cached.contentRaw()
|
||||
);
|
||||
replySegments = cached.replySegments();
|
||||
if (replySegments == null || replySegments.isEmpty()) {
|
||||
replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cached.authorName(),
|
||||
cached.authorRoleColor(),
|
||||
null,
|
||||
cached.contentRaw()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Build edit notification segments
|
||||
|
|
@ -552,9 +571,12 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
packet.replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cached.authorName(),
|
||||
cached.authorRoleColor(),
|
||||
cached.contextMessage(),
|
||||
null,
|
||||
cached.contentRaw()
|
||||
);
|
||||
if (cached.replySegments() != null && !cached.replySegments().isEmpty()) {
|
||||
packet.replySegments = cached.replySegments();
|
||||
}
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
}
|
||||
|
|
@ -575,7 +597,8 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
Member member = message.getMember();
|
||||
String name = member != null ? member.getEffectiveName() : message.getAuthor().getName();
|
||||
String roleColor = DiscordMessageParser.getRoleColorHex(member);
|
||||
messageCache.put(message.getId(), new CachedMessage(name, roleColor, message.getContentRaw(), message));
|
||||
List<TextSegment> replySegments = DiscordMessageParser.buildReplySegments(message);
|
||||
messageCache.put(message.getId(), new CachedMessage(name, roleColor, message.getContentRaw(), replySegments));
|
||||
}
|
||||
|
||||
private static void logDiscordEventForConsole(DiscordEventPacket packet) {
|
||||
|
|
|
|||
|
|
@ -92,9 +92,13 @@ private static final Pattern BOLD_ITALIC_URL_PATTERN = Pattern.compile("\\*\\*\\
|
|||
private static final Pattern BOLD_URL_PATTERN = Pattern.compile("\\*\\*(https?://[^\\s*|~`<>)\\]]+)\\*\\*");
|
||||
private static final Pattern SPOILER_ITALIC_URL_PATTERN = Pattern.compile("\\|\\|\\*(https?://[^\\s*|~`<>)\\]]+)\\*\\|\\|");
|
||||
private static final Pattern SPOILER_URL_PATTERN = Pattern.compile("\\|\\|(https?://[^\\s*|~`<>)\\]]+)\\|\\|");
|
||||
// Matches spoiler-wrapped user mentions: ||<@123>|| / ||<@!123>||
|
||||
private static final Pattern SPOILER_USER_MENTION_PATTERN = Pattern.compile("\\|\\|<@!?(\\d+)>\\|\\|");
|
||||
// Matches spoiler-wrapped role mentions: ||<@&123>||
|
||||
private static final Pattern SPOILER_ROLE_MENTION_PATTERN = Pattern.compile("\\|\\|<@&(\\d+)>\\|\\|");
|
||||
// Matches spoiler-wrapped channel mentions: ||<#123>||
|
||||
private static final Pattern SPOILER_CHANNEL_MENTION_PATTERN = Pattern.compile("\\|\\|<#(\\d+)>\\|\\|");
|
||||
// Matches spoiler-wrapped @everyone/@here tokens: ||@everyone|| / ||@here||
|
||||
private static final Pattern SPOILER_EVERYONE_HERE_PATTERN = Pattern.compile("\\|\\|@(everyone|here)\\|\\|");
|
||||
private static final Pattern SPOILER_CONTENT_PATTERN = Pattern.compile("\\|\\|(.+?)\\|\\|");
|
||||
private static final Pattern LINK_TOKEN_PATTERN = Pattern.compile("\\[([^]]+)]\\((https?://[^)]+)\\)");
|
||||
|
|
@ -106,6 +110,9 @@ private static final int REPLY_TRUNCATE_LIMIT_NARROW = 40;
|
|||
private static final int MAIN_TRUNCATE_LIMIT_WIDE = 200;
|
||||
private static final int MAIN_TRUNCATE_LIMIT_NARROW = 400;
|
||||
private static final String URL_COLOR = "#3366CC";
|
||||
private static final String ATTACHMENT_LABEL_PREFIX = "<attachment type=[%s] name=[";
|
||||
private static final String EMBED_LABEL_PREFIX = "<embed title=[";
|
||||
private static final String LABEL_SUFFIX = "]>";
|
||||
|
||||
/**
|
||||
* Builds the main message line segments for a Discord chat message.
|
||||
|
|
@ -758,9 +765,8 @@ break;
|
|||
if (displayName == null) {
|
||||
displayName = userId;
|
||||
}
|
||||
TextSegment seg = new TextSegment("[@" + displayName + "]", false, color != null ? color : "white");
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
TextSegment seg = new TextSegment("[@" + displayName + "]", false, colorOrDefault(color));
|
||||
applySpoilerStyle(seg);
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
|
@ -782,8 +788,7 @@ break;
|
|||
}
|
||||
}
|
||||
TextSegment seg = new TextSegment("[@" + roleName + "]", false, color);
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
applySpoilerStyle(seg);
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
|
@ -800,8 +805,7 @@ break;
|
|||
}
|
||||
}
|
||||
TextSegment seg = new TextSegment("[#" + channelName + "]", false, "yellow");
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
applySpoilerStyle(seg);
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
|
@ -814,8 +818,7 @@ Matcher matcher = SPOILER_EVERYONE_HERE_PATTERN.matcher(raw);
|
|||
while (matcher.find()) {
|
||||
String mention = matcher.group(1);
|
||||
TextSegment seg = new TextSegment("[@" + mention + "]", false, "yellow");
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
applySpoilerStyle(seg);
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
|
@ -1486,9 +1489,9 @@ return false;
|
|||
|
||||
private static List<TextSegment> buildAttachmentSegments(String type, String fileName, String url, boolean spoiler) {
|
||||
List<TextSegment> segments = new ArrayList<>();
|
||||
TextSegment prefix = new TextSegment("<attachment type=[" + type + "] name=[", false, URL_COLOR);
|
||||
TextSegment prefix = new TextSegment(String.format(ATTACHMENT_LABEL_PREFIX, type), false, URL_COLOR);
|
||||
TextSegment fileNameSegment = new TextSegment(fileName, false, URL_COLOR);
|
||||
TextSegment suffix = new TextSegment("]>", false, URL_COLOR);
|
||||
TextSegment suffix = new TextSegment(LABEL_SUFFIX, false, URL_COLOR);
|
||||
|
||||
applyLinkStyle(prefix, url);
|
||||
applyLinkStyle(fileNameSegment, url);
|
||||
|
|
@ -1508,9 +1511,9 @@ return segments;
|
|||
private static List<TextSegment> buildEmbedSegments(String title, String url, boolean spoiler) {
|
||||
List<TextSegment> segments = new ArrayList<>();
|
||||
String color = url == null ? "yellow" : URL_COLOR;
|
||||
TextSegment prefix = new TextSegment("<embed title=[", false, color);
|
||||
TextSegment prefix = new TextSegment(EMBED_LABEL_PREFIX, false, color);
|
||||
TextSegment titleSegment = new TextSegment(title, false, color);
|
||||
TextSegment suffix = new TextSegment("]>", false, color);
|
||||
TextSegment suffix = new TextSegment(LABEL_SUFFIX, false, color);
|
||||
|
||||
if (url != null) {
|
||||
applyLinkStyle(prefix, url);
|
||||
|
|
@ -1532,8 +1535,20 @@ return segments;
|
|||
private static void applyLinkStyle(TextSegment segment, String url) {
|
||||
segment.underlined = true;
|
||||
segment.clickUrl = url;
|
||||
if (segment.hoverText == null) {
|
||||
segment.hoverText = I18nManager.getDmccTranslation("discord.message_parser.click_to_open_link");
|
||||
}
|
||||
}
|
||||
|
||||
private static void applySpoilerStyle(TextSegment segment) {
|
||||
segment.obfuscated = true;
|
||||
// Obfuscated Minecraft text is unreadable in chat, so we keep original plain text as hover preview.
|
||||
segment.hoverText = segment.text;
|
||||
}
|
||||
|
||||
private static String colorOrDefault(String color) {
|
||||
return color != null ? color : "white";
|
||||
}
|
||||
|
||||
private static String truncateMainRaw(String raw) {
|
||||
String lineLimited = applyMainLineLimit(raw);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue