diff --git a/common/src/main/java/com/discordsrv/common/feature/mention/MentionCachingModule.java b/common/src/main/java/com/discordsrv/common/feature/mention/MentionCachingModule.java index c201c372..68d6f811 100644 --- a/common/src/main/java/com/discordsrv/common/feature/mention/MentionCachingModule.java +++ b/common/src/main/java/com/discordsrv/common/feature/mention/MentionCachingModule.java @@ -43,29 +43,30 @@ import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameE import net.dv8tion.jda.api.events.role.RoleCreateEvent; import net.dv8tion.jda.api.events.role.RoleDeleteEvent; import net.dv8tion.jda.api.events.role.update.RoleUpdateNameEvent; -import net.kyori.adventure.text.Component; +import org.apache.commons.lang3.tuple.Pair; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public class MentionCachingModule extends AbstractModule { - private static final Pattern USER_MENTION_PATTERN = Pattern.compile("(?> memberMentions = new ConcurrentHashMap<>(); - private final Map> memberMentionsCache = new ConcurrentHashMap<>(); + private static final Pattern USER_MENTION_PATTERN = Pattern.compile("(?, CachedMention> memberMentionsCache; private final Map> roleMentions = new ConcurrentHashMap<>(); private final Map> channelMentions = new ConcurrentHashMap<>(); public MentionCachingModule(DiscordSRV discordSRV) { super(discordSRV); + this.memberMentionsCache = discordSRV.caffeineBuilder() + .expireAfterWrite(Duration.ofMinutes(5)) + .build(); } @Override @@ -108,7 +109,7 @@ public class MentionCachingModule extends AbstractModule { @Override public void disable() { - memberMentions.clear(); + memberMentionsCache.invalidateAll(); roleMentions.clear(); channelMentions.clear(); } @@ -117,32 +118,31 @@ public class MentionCachingModule extends AbstractModule { MinecraftToDiscordChatConfig.Mentions config, Guild guild, IPlayer player, - Component message + String messageContent ) { - List mentions = new ArrayList<>(); - if (config.users) { - mentions.addAll(getMemberMentions(guild).values()); - } - List>> futures = new ArrayList<>(); - if (config.users && config.uncachedUsers && player.hasPermission(Permission.MENTION_USER_LOOKUP)) { - String messageContent = discordSRV.componentFactory().plainSerializer().serialize(message); + List mentions = new ArrayList<>(); + + if (config.users) { + boolean uncached = config.uncachedUsers && player.hasPermission(Permission.MENTION_USER_LOOKUP); + Matcher matcher = USER_MENTION_PATTERN.matcher(messageContent); while (matcher.find()) { - String mention = matcher.group(); - boolean perfectMatch = false; - for (CachedMention cachedMention : mentions) { - if (cachedMention.search().matcher(mention).matches()) { - perfectMatch = true; - break; - } + String username = matcher.group(1); + + List members = guild.getMembersByName(username, false); + boolean any = false; + for (Member member : members) { + mentions.add(getMemberMention(member)); + any = true; + break; } - if (!perfectMatch) { - futures.add(lookupMemberMentions(guild, mention)); + + if (!any && uncached) { + futures.add(lookupMemberMentions(guild, username)); } } } - if (config.roles) { mentions.addAll(getRoleMentions(guild).values()); } @@ -163,8 +163,7 @@ public class MentionCachingModule extends AbstractModule { @Subscribe public void onGuildDelete(GuildLeaveEvent event) { long guildId = event.getGuild().getIdLong(); - memberMentions.remove(guildId); - memberMentionsCache.remove(guildId); + memberMentionsCache.asMap().keySet().removeIf(pair -> pair.getKey() == guildId); roleMentions.remove(guildId); channelMentions.remove(guildId); } @@ -173,38 +172,29 @@ public class MentionCachingModule extends AbstractModule { // Member // - private CompletableFuture> lookupMemberMentions(Guild guild, String mention) { - Cache cache = memberMentionsCache.computeIfAbsent(guild.getIdLong(), key -> discordSRV.caffeineBuilder() - .expireAfterAccess(10, TimeUnit.MINUTES) - .build() - ); - CachedMention cached = cache.getIfPresent(mention); - if (cached != null) { - return CompletableFuture.completedFuture(Collections.singletonList(cached)); - } - + private CompletableFuture> lookupMemberMentions(Guild guild, String username) { CompletableFuture> memberFuture = new CompletableFuture<>(); - guild.retrieveMembersByPrefix(mention.substring(1), 100) + guild.retrieveMembersByPrefix(username, 100) .onSuccess(memberFuture::complete).onError(memberFuture::completeExceptionally); return memberFuture.thenApply(members -> { List cachedMentions = new ArrayList<>(); for (Member member : members) { - CachedMention cachedMention = cache.get(member.getUser().getName(), k -> convertMember(member)); - cachedMentions.add(cachedMention); + cachedMentions.add(getMemberMention(member)); } return cachedMentions; }); } - private Map getMemberMentions(Guild guild) { - return memberMentions.computeIfAbsent(guild.getIdLong(), key -> { - Map mentions = new LinkedHashMap<>(); - for (Member member : guild.getMembers()) { - mentions.put(member.getIdLong(), convertMember(member)); - } - return mentions; - }); + private Pair getMemberKey(Member member) { + return Pair.of(member.getGuild().getIdLong(), member.getIdLong()); + } + + private CachedMention getMemberMention(Member member) { + return memberMentionsCache.get( + getMemberKey(member), + key -> convertMember(member) + ); } private CachedMention convertMember(Member member) { @@ -224,13 +214,18 @@ public class MentionCachingModule extends AbstractModule { return; } - getMemberMentions(event.getGuild()).put(member.getIdLong(), convertMember(member)); + memberMentionsCache.put(getMemberKey(member), convertMember(member)); } @Subscribe public void onMemberUpdate(GuildMemberUpdateNicknameEvent event) { Member member = event.getMember(); - getMemberMentions(event.getGuild()).replace(member.getIdLong(), convertMember(member)); + if (member.getGuild().getMemberCache().getElementById(member.getIdLong()) == null) { + // Member is not cached + return; + } + + memberMentionsCache.put(getMemberKey(member), convertMember(member)); } @Subscribe @@ -240,7 +235,7 @@ public class MentionCachingModule extends AbstractModule { return; } - getMemberMentions(event.getGuild()).remove(member.getIdLong()); + memberMentionsCache.invalidate(getMemberKey(member)); } // diff --git a/common/src/main/java/com/discordsrv/common/feature/mention/MentionGameRenderingModule.java b/common/src/main/java/com/discordsrv/common/feature/mention/MentionGameRenderingModule.java index ae7670f8..b613e15d 100644 --- a/common/src/main/java/com/discordsrv/common/feature/mention/MentionGameRenderingModule.java +++ b/common/src/main/java/com/discordsrv/common/feature/mention/MentionGameRenderingModule.java @@ -86,7 +86,9 @@ public class MentionGameRenderingModule extends AbstractModule { guilds.add(channel.getGuild()); } - Component component = ComponentUtil.fromAPI(event.getMessage()); + Component message = ComponentUtil.fromAPI(event.getMessage()); + String messageContent = discordSRV.componentFactory().plainSerializer().serialize(message); + List cachedMentions = new ArrayList<>(); for (DiscordGuild guild : guilds) { cachedMentions.addAll( @@ -94,7 +96,7 @@ public class MentionGameRenderingModule extends AbstractModule { config.minecraftToDiscord.mentions, guild.asJDA(), (IPlayer) event.getPlayer(), - component + messageContent ).join() ); } @@ -103,13 +105,13 @@ public class MentionGameRenderingModule extends AbstractModule { DiscordGuild guild = guilds.size() == 1 ? guilds.iterator().next() : null; for (CachedMention cachedMention : cachedMentions) { - component = component.replaceText( + message = message.replaceText( TextReplacementConfig.builder().match(cachedMention.search()) .replacement(() -> replacement(cachedMention, mentionsConfig, guild)) .build() ); } - event.process(ComponentUtil.toAPI(component)); + event.process(ComponentUtil.toAPI(message)); } private Component replacement(CachedMention mention, MentionsConfig config, DiscordGuild guild) { diff --git a/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/MinecraftToDiscordChatModule.java b/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/MinecraftToDiscordChatModule.java index 9aa73768..ba126f1f 100644 --- a/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/MinecraftToDiscordChatModule.java +++ b/common/src/main/java/com/discordsrv/common/feature/messageforwarding/game/MinecraftToDiscordChatModule.java @@ -121,7 +121,8 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule getMessageForGuildWithMentions(config, format, guild, message, player, context, mentions)); }