mirror of
https://github.com/System-End/Discord-MC-Chat.git
synced 2026-04-19 19:45:14 +00:00
fix spoiler mention parsing and server event logging parity
Co-authored-by: Xujiayao <58985541+Xujiayao@users.noreply.github.com>
This commit is contained in:
parent
5517131a89
commit
98dcc2c2ca
2 changed files with 170 additions and 33 deletions
|
|
@ -47,7 +47,7 @@ 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) {}
|
||||
private record CachedMessage(String authorName, String authorRoleColor, String contentRaw, Message contextMessage) {}
|
||||
|
||||
/**
|
||||
* Resolves the OP Level credential for a Discord user based on config mappings.
|
||||
|
|
@ -131,6 +131,7 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
|
||||
List<TextSegment> segments = DiscordMessageParser.buildCommandSegments(effectiveName, roleColor, fullCommand.toString());
|
||||
DiscordEventPacket packet = new DiscordEventPacket(DiscordEventPacket.EventType.COMMAND, segments);
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
}
|
||||
}
|
||||
|
|
@ -379,7 +380,7 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cachedRef.authorName(),
|
||||
cachedRef.authorRoleColor(),
|
||||
null,
|
||||
cachedRef.contextMessage(),
|
||||
cachedRef.contentRaw()
|
||||
);
|
||||
}
|
||||
|
|
@ -411,11 +412,7 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
packet.mentionedPlayerUuids = mentionedPlayerUuids;
|
||||
packet.mentionEveryone = isMentionEveryone;
|
||||
|
||||
if (replySegments != null && !replySegments.isEmpty()) {
|
||||
LOGGER.info(TextSegment.toPlainText(replySegments));
|
||||
}
|
||||
LOGGER.info(TextSegment.toPlainText(mainSegments));
|
||||
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
|
||||
// Cache message for edit/delete reference
|
||||
|
|
@ -458,10 +455,12 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
List<TextSegment> segments = DiscordMessageParser.buildReactionSegments(reactorName, roleColor, emojiText);
|
||||
DiscordEventPacket packet = new DiscordEventPacket(DiscordEventPacket.EventType.REACTION, segments);
|
||||
packet.replySegments = DiscordMessageParser.buildReplySegments(targetMessage);
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
}, error -> {
|
||||
List<TextSegment> segments = DiscordMessageParser.buildReactionSegments(reactorName, roleColor, emojiText);
|
||||
DiscordEventPacket packet = new DiscordEventPacket(DiscordEventPacket.EventType.REACTION, segments);
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
});
|
||||
}
|
||||
|
|
@ -499,7 +498,7 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cached.authorName(),
|
||||
cached.authorRoleColor(),
|
||||
message,
|
||||
cached.contextMessage(),
|
||||
cached.contentRaw()
|
||||
);
|
||||
}
|
||||
|
|
@ -513,6 +512,7 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
DiscordEventPacket packet = new DiscordEventPacket(DiscordEventPacket.EventType.EDIT, notificationSegments);
|
||||
packet.replySegments = replySegments;
|
||||
packet.editedMessageSegments = editedMessageSegments;
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
|
||||
// Update cache
|
||||
|
|
@ -542,6 +542,7 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
// No cached info - send a generic delete notification
|
||||
List<TextSegment> segments = DiscordMessageParser.buildDeleteSegments("Unknown", "white");
|
||||
DiscordEventPacket packet = new DiscordEventPacket(DiscordEventPacket.EventType.DELETE, segments);
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
return;
|
||||
}
|
||||
|
|
@ -551,9 +552,10 @@ public class DiscordEventHandler extends ListenerAdapter {
|
|||
packet.replySegments = DiscordMessageParser.buildReplySegments(
|
||||
cached.authorName(),
|
||||
cached.authorRoleColor(),
|
||||
null,
|
||||
cached.contextMessage(),
|
||||
cached.contentRaw()
|
||||
);
|
||||
logDiscordEventForConsole(packet);
|
||||
NetworkManager.broadcastToClients(packet);
|
||||
}
|
||||
|
||||
|
|
@ -573,6 +575,18 @@ 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()));
|
||||
messageCache.put(message.getId(), new CachedMessage(name, roleColor, message.getContentRaw(), message));
|
||||
}
|
||||
|
||||
private static void logDiscordEventForConsole(DiscordEventPacket packet) {
|
||||
if (packet.replySegments != null && !packet.replySegments.isEmpty()) {
|
||||
LOGGER.info(TextSegment.toPlainText(packet.replySegments));
|
||||
}
|
||||
if (packet.segments != null && !packet.segments.isEmpty()) {
|
||||
LOGGER.info(TextSegment.toPlainText(packet.segments));
|
||||
}
|
||||
if (packet.type == DiscordEventPacket.EventType.EDIT && packet.editedMessageSegments != null && !packet.editedMessageSegments.isEmpty()) {
|
||||
LOGGER.info(TextSegment.toPlainText(packet.editedMessageSegments));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,10 @@ 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*|~`<>)\\]]+)\\|\\|");
|
||||
private static final Pattern SPOILER_USER_MENTION_PATTERN = Pattern.compile("\\|\\|<@!?(\\d+)>\\|\\|");
|
||||
private static final Pattern SPOILER_ROLE_MENTION_PATTERN = Pattern.compile("\\|\\|<@&(\\d+)>\\|\\|");
|
||||
private static final Pattern SPOILER_CHANNEL_MENTION_PATTERN = Pattern.compile("\\|\\|<#(\\d+)>\\|\\|");
|
||||
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?://[^)]+)\\)");
|
||||
private static final List<String> MARKDOWN_DELIMITERS = List.of("***", "~~", "||", "**", "__", "*", "_");
|
||||
|
|
@ -529,16 +533,8 @@ type = "image";
|
|||
} else if (attachment.isVideo()) {
|
||||
type = "video";
|
||||
}
|
||||
String label = "<attachment type=[" + type + "] name=[" + attachment.getFileName() + "]>";
|
||||
|
||||
TextSegment seg = new TextSegment(label, false, "#3366CC");
|
||||
seg.underlined = true;
|
||||
seg.clickUrl = attachment.getUrl();
|
||||
seg.hoverText = I18nManager.getDmccTranslation("discord.message_parser.click_to_open_link");
|
||||
if (attachment.isSpoiler() || attachment.getFileName().startsWith("SPOILER_")) {
|
||||
seg.obfuscated = true;
|
||||
}
|
||||
segments.add(seg);
|
||||
boolean spoilerAttachment = attachment.isSpoiler() || attachment.getFileName().startsWith("SPOILER_");
|
||||
segments.addAll(buildAttachmentSegments(type, attachment.getFileName(), attachment.getUrl(), spoilerAttachment));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -566,19 +562,8 @@ title = safeTruncate(title, 20) + "...";
|
|||
}
|
||||
}
|
||||
|
||||
TextSegment seg;
|
||||
if (embed.getUrl() == null) {
|
||||
seg = new TextSegment("<embed title=[" + title + "]>", false, "yellow");
|
||||
} else {
|
||||
seg = new TextSegment("<embed title=[" + title + "]>", false, "#3366CC");
|
||||
seg.underlined = true;
|
||||
seg.clickUrl = embed.getUrl();
|
||||
seg.hoverText = I18nManager.getDmccTranslation("discord.message_parser.click_to_open_link");
|
||||
}
|
||||
if (isSpoilerWrappedUrl(raw, embed.getUrl())) {
|
||||
seg.obfuscated = true;
|
||||
}
|
||||
segments.add(seg);
|
||||
boolean spoilerEmbed = isSpoilerWrappedUrl(raw, embed.getUrl());
|
||||
segments.addAll(buildEmbedSegments(title, embed.getUrl(), spoilerEmbed));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -618,6 +603,7 @@ List<TextSegment> segments = new ArrayList<>();
|
|||
List<TokenSpan> tokens = new ArrayList<>();
|
||||
|
||||
if (parseMentions) {
|
||||
collectSpoilerMentionTokens(raw, message, tokens);
|
||||
collectUserMentionTokens(raw, message, tokens);
|
||||
collectRoleMentionTokens(raw, message, tokens);
|
||||
collectChannelMentionTokens(raw, message, tokens);
|
||||
|
|
@ -748,6 +734,92 @@ tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
|||
}
|
||||
}
|
||||
|
||||
private static void collectSpoilerMentionTokens(String raw, Message message, List<TokenSpan> tokens) {
|
||||
collectSpoilerUserMentionTokens(raw, message, tokens);
|
||||
collectSpoilerRoleMentionTokens(raw, message, tokens);
|
||||
collectSpoilerChannelMentionTokens(raw, message, tokens);
|
||||
collectSpoilerEveryoneHereTokens(raw, message, tokens);
|
||||
}
|
||||
|
||||
private static void collectSpoilerUserMentionTokens(String raw, Message message, List<TokenSpan> tokens) {
|
||||
Matcher matcher = SPOILER_USER_MENTION_PATTERN.matcher(raw);
|
||||
while (matcher.find()) {
|
||||
String userId = matcher.group(1);
|
||||
String displayName = null;
|
||||
String color = null;
|
||||
for (User user : message.getMentions().getUsers()) {
|
||||
if (user.getId().equals(userId)) {
|
||||
Member member = message.getGuild().getMember(user);
|
||||
displayName = member != null ? member.getEffectiveName() : user.getName();
|
||||
color = getRoleColorHex(member);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (displayName == null) {
|
||||
displayName = userId;
|
||||
}
|
||||
TextSegment seg = new TextSegment("[@" + displayName + "]", false, color != null ? color : "white");
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectSpoilerRoleMentionTokens(String raw, Message message, List<TokenSpan> tokens) {
|
||||
Matcher matcher = SPOILER_ROLE_MENTION_PATTERN.matcher(raw);
|
||||
while (matcher.find()) {
|
||||
String roleId = matcher.group(1);
|
||||
String roleName = roleId;
|
||||
String color = "white";
|
||||
for (Role role : message.getMentions().getRoles()) {
|
||||
if (role.getId().equals(roleId)) {
|
||||
roleName = role.getName();
|
||||
Color roleColor = role.getColors().getPrimary();
|
||||
if (roleColor != null) {
|
||||
color = String.format("#%06X", roleColor.getRGB() & 0xFFFFFF);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
TextSegment seg = new TextSegment("[@" + roleName + "]", false, color);
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectSpoilerChannelMentionTokens(String raw, Message message, List<TokenSpan> tokens) {
|
||||
Matcher matcher = SPOILER_CHANNEL_MENTION_PATTERN.matcher(raw);
|
||||
while (matcher.find()) {
|
||||
String channelId = matcher.group(1);
|
||||
String channelName = channelId;
|
||||
for (GuildChannel channel : message.getMentions().getChannels()) {
|
||||
if (channel.getId().equals(channelId)) {
|
||||
channelName = channel.getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
TextSegment seg = new TextSegment("[#" + channelName + "]", false, "yellow");
|
||||
seg.obfuscated = true;
|
||||
seg.hoverText = seg.text;
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
||||
private static void collectSpoilerEveryoneHereTokens(String raw, Message message, List<TokenSpan> tokens) {
|
||||
if (!message.getMentions().mentionsEveryone()) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
tokens.add(new TokenSpan(matcher.start(), matcher.end(), seg));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects Discord timestamp tokens from the raw content.
|
||||
* Timestamps like {@code <t:1234567890:f>} are resolved using the server's locale.
|
||||
|
|
@ -1412,6 +1484,57 @@ return true;
|
|||
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 fileNameSegment = new TextSegment(fileName, false, URL_COLOR);
|
||||
TextSegment suffix = new TextSegment("]>", false, URL_COLOR);
|
||||
|
||||
applyLinkStyle(prefix, url);
|
||||
applyLinkStyle(fileNameSegment, url);
|
||||
applyLinkStyle(suffix, url);
|
||||
|
||||
if (spoiler) {
|
||||
fileNameSegment.obfuscated = true;
|
||||
fileNameSegment.hoverText = fileName;
|
||||
}
|
||||
|
||||
segments.add(prefix);
|
||||
segments.add(fileNameSegment);
|
||||
segments.add(suffix);
|
||||
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 titleSegment = new TextSegment(title, false, color);
|
||||
TextSegment suffix = new TextSegment("]>", false, color);
|
||||
|
||||
if (url != null) {
|
||||
applyLinkStyle(prefix, url);
|
||||
applyLinkStyle(titleSegment, url);
|
||||
applyLinkStyle(suffix, url);
|
||||
}
|
||||
|
||||
if (spoiler) {
|
||||
titleSegment.obfuscated = true;
|
||||
titleSegment.hoverText = title;
|
||||
}
|
||||
|
||||
segments.add(prefix);
|
||||
segments.add(titleSegment);
|
||||
segments.add(suffix);
|
||||
return segments;
|
||||
}
|
||||
|
||||
private static void applyLinkStyle(TextSegment segment, String url) {
|
||||
segment.underlined = true;
|
||||
segment.clickUrl = url;
|
||||
segment.hoverText = I18nManager.getDmccTranslation("discord.message_parser.click_to_open_link");
|
||||
}
|
||||
|
||||
private static String truncateMainRaw(String raw) {
|
||||
String lineLimited = applyMainLineLimit(raw);
|
||||
int maxLength = containsFullWidthCharacter(raw) ? MAIN_TRUNCATE_LIMIT_WIDE : MAIN_TRUNCATE_LIMIT_NARROW;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue