Get rid of OrDefault. Move advancement spam detection to AwardMessageModule

This commit is contained in:
Vankka 2023-05-25 23:30:12 +03:00
parent 289ea8491f
commit e4b80ebb15
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
27 changed files with 303 additions and 389 deletions

View File

@ -21,36 +21,15 @@ package com.discordsrv.bukkit.listener.award;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.logging.NamedLogger;
import com.github.benmanes.caffeine.cache.Cache;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AbstractBukkitAwardListener implements Listener {
private final Cache<UUID, AtomicInteger> advancementCount;
protected final Logger logger;
protected final IBukkitAwardForwarder forwarder;
public AbstractBukkitAwardListener(DiscordSRV discordSRV, IBukkitAwardForwarder forwarder) {
this.advancementCount = discordSRV.caffeineBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
this.logger = new NamedLogger(discordSRV, "AWARD_LISTENER");
this.forwarder = forwarder;
}
@SuppressWarnings("DataFlowIssue") // Not possible
public boolean checkIfShouldSkip(Player player) {
int count = advancementCount.get(player.getUniqueId(), key -> new AtomicInteger(0)).incrementAndGet();
boolean skip = count >= 5;
if (skip && (count % 5 == 0)) {
logger.debug("Skipping advancement/achievement processing for player: " + player.getName()
+ " currently at " + advancementCount + " advancements/achievements per 5 seconds");
}
return skip;
}
}

View File

@ -73,7 +73,7 @@ public class BukkitAdvancementListener extends AbstractBukkitAwardListener {
try {
ReturnData data = nms.getData(event.getAdvancement());
if (data == null || checkIfShouldSkip(event.getPlayer())) {
if (data == null) {
return;
}

View File

@ -62,10 +62,6 @@ public class PaperModernAdvancementListener extends AbstractBukkitAwardListener
return;
}
if (checkIfShouldSkip(event.getPlayer())) {
return;
}
MinecraftComponent message = MESSAGE_HANDLE.getComponent(discordSRV, event);
MinecraftComponent displayName = DISPLAY_NAME_HANDLE.getComponent(discordSRV, advancement);
forwarder.publishEvent(event, event.getPlayer(), displayName, message, false);

View File

@ -18,11 +18,10 @@
package com.discordsrv.bukkit.config.main;
import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.common.config.annotation.Order;
import com.discordsrv.common.config.main.MainConfig;
import com.discordsrv.common.config.main.PluginIntegrationConfig;
import com.discordsrv.common.config.main.channels.base.ChannelConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.server.ServerBaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.server.ServerChannelConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ -30,10 +29,14 @@ import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class BukkitConfig extends MainConfig {
public BukkitConfig() {
channels.clear();
channels.put(GameChannel.DEFAULT_NAME, new ServerChannelConfig());
channels.put(ChannelConfig.DEFAULT_KEY, new ServerBaseChannelConfig());
@Override
public BaseChannelConfig createDefaultBaseChannel() {
return new ServerBaseChannelConfig();
}
@Override
public BaseChannelConfig createDefaultChannel() {
return new ServerChannelConfig();
}
@Order(5)

View File

@ -34,10 +34,6 @@ public class BukkitAchievementListener extends AbstractBukkitAwardListener {
@EventHandler(priority = EventPriority.MONITOR)
public void onPlayerAchievementAwarded(PlayerAchievementAwardedEvent event) {
if (checkIfShouldSkip(event.getPlayer())) {
return;
}
MinecraftComponent name = ComponentUtil.toAPI(BukkitComponentSerializer.legacy().deserialize(event.getAchievement().name()));
forwarder.publishEvent(event, event.getPlayer(), name, null, event.isCancelled());
}

View File

@ -621,12 +621,12 @@ public abstract class AbstractDiscordSRV<B extends IBootstrap, C extends MainCon
try {
connectionConfigManager().load();
configManager().load();
channelConfig().reload();
} catch (Throwable t) {
setStatus(Status.FAILED_TO_LOAD_CONFIG);
throw t;
}
channelConfig().reload();
}
// Update check

View File

@ -28,23 +28,32 @@ import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.ChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.config.main.channels.base.ThreadConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.apache.commons.lang3.tuple.Pair;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.CommentedConfigurationNode;
import org.spongepowered.configurate.objectmapping.ObjectMapper;
import org.spongepowered.configurate.serialize.SerializationException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class ChannelConfigHelper {
private final DiscordSRV discordSRV;
// game channel name eg. "global" -> game channel ("discordsrv:global")
private final LoadingCache<String, GameChannel> nameToChannelCache;
// game channel name -> config
private final Map<String, BaseChannelConfig> configs;
// caches for Discord channel -> config
private final Map<Long, Map<String, BaseChannelConfig>> textChannelToConfigMap;
private final LoadingCache<Pair<Long, String>, Map<String, BaseChannelConfig>> threadToConfigCache;
private final Map<Pair<Long, String>, Map<String, BaseChannelConfig>> threadToConfigMap;
public ChannelConfigHelper(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
@ -65,62 +74,96 @@ public class ChannelConfigHelper {
return event.getChannelFromProcessing();
}
});
this.textChannelToConfigMap = new ConcurrentHashMap<>();
this.threadToConfigCache = discordSRV.caffeineBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.expireAfterAccess(30, TimeUnit.SECONDS)
.refreshAfterWrite(10, TimeUnit.SECONDS)
.build(key -> {
Map<String, BaseChannelConfig> map = new HashMap<>();
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
String channelName = entry.getKey();
BaseChannelConfig value = entry.getValue();
if (value instanceof IChannelConfig) {
IChannelConfig channelConfig = (IChannelConfig) value;
List<ThreadConfig> threads = channelConfig.threads();
if (threads == null) {
continue;
}
for (ThreadConfig thread : threads) {
if (Objects.equals(thread.channelId, key.getKey())
&& Objects.equals(thread.threadName, key.getValue())) {
map.put(channelName, value);
}
}
}
}
return map;
});
this.configs = new HashMap<>();
this.textChannelToConfigMap = new HashMap<>();
this.threadToConfigMap = new LinkedHashMap<>();
}
public void reload() {
Map<Long, Map<String, BaseChannelConfig>> newMap = new HashMap<>();
@SuppressWarnings("unchecked")
private BaseChannelConfig map(BaseChannelConfig defaultConfig, BaseChannelConfig config)
throws SerializationException {
MainConfigManager<?> configManager = discordSRV.configManager();
CommentedConfigurationNode defaultNode = CommentedConfigurationNode.root(configManager.defaultNodeOptions());
CommentedConfigurationNode target = CommentedConfigurationNode.root(configManager.configNodeOptions());
configManager.defaultObjectMapper()
.get((Class<BaseChannelConfig>) defaultConfig.getClass())
.save(defaultConfig, defaultNode);
ObjectMapper<BaseChannelConfig> mapper = configManager.configObjectMapper()
.get((Class<BaseChannelConfig>) config.getClass());
mapper.save(config, target);
target.mergeFrom(defaultNode);
return mapper.load(target);
}
public void reload() throws SerializationException {
Map<String, BaseChannelConfig> configChannels = discordSRV.config().channels;
BaseChannelConfig defaultConfig = configChannels.computeIfAbsent(ChannelConfig.DEFAULT_KEY, key -> discordSRV.config().createDefaultBaseChannel());
Map<String, BaseChannelConfig> configs = new HashMap<>();
for (Map.Entry<String, BaseChannelConfig> entry : configChannels.entrySet()) {
if (Objects.equals(entry.getKey(), ChannelConfig.DEFAULT_KEY)) {
continue;
}
BaseChannelConfig mapped = map(defaultConfig, entry.getValue());
configs.put(entry.getKey(), mapped);
}
synchronized (this.configs) {
this.configs.clear();
this.configs.putAll(configs);
}
Map<Long, Map<String, BaseChannelConfig>> text = new HashMap<>();
Map<Pair<Long, String>, Map<String, BaseChannelConfig>> thread = new HashMap<>();
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
String channelName = entry.getKey();
BaseChannelConfig value = entry.getValue();
if (value instanceof IChannelConfig) {
IChannelConfig channelConfig = (IChannelConfig) value;
List<Long> channelIds = channelConfig.channelIds();
if (channelIds == null) {
continue;
if (channelIds != null) {
for (long channelId : channelIds) {
text.computeIfAbsent(channelId, key -> new LinkedHashMap<>())
.put(channelName, value);
}
}
for (long channelId : channelIds) {
newMap.computeIfAbsent(channelId, key -> new LinkedHashMap<>())
.put(channelName, value);
List<ThreadConfig> threads = channelConfig.threads();
if (threads != null) {
for (ThreadConfig threadConfig : threads) {
Pair<Long, String> pair = Pair.of(
threadConfig.channelId,
threadConfig.threadName.toLowerCase(Locale.ROOT)
);
thread.computeIfAbsent(pair, key -> new LinkedHashMap<>())
.put(channelName, value);
}
}
}
}
synchronized (textChannelToConfigMap) {
textChannelToConfigMap.clear();
textChannelToConfigMap.putAll(newMap);
textChannelToConfigMap.putAll(text);
}
synchronized (threadToConfigMap) {
threadToConfigMap.clear();
threadToConfigMap.putAll(thread);
}
}
private Map<String, BaseChannelConfig> channels() {
return discordSRV.config().channels;
synchronized (configs) {
return configs;
}
}
private BaseChannelConfig findChannel(String key) {
@ -130,7 +173,7 @@ public class ChannelConfigHelper {
return byExact;
}
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
for (Map.Entry<String, BaseChannelConfig> entry : channels.entrySet()) {
if (entry.getKey().equalsIgnoreCase(key)) {
return entry.getValue();
}
@ -138,55 +181,28 @@ public class ChannelConfigHelper {
return null;
}
private BaseChannelConfig getDefault() {
return channels().computeIfAbsent(ChannelConfig.DEFAULT_KEY, key -> new BaseChannelConfig());
}
public Set<String> getKeys() {
Set<String> keys = new LinkedHashSet<>(channels().keySet());
keys.remove(ChannelConfig.DEFAULT_KEY);
return keys;
}
public Set<OrDefault<BaseChannelConfig>> getAllChannels() {
BaseChannelConfig defaultConfig = getDefault();
Set<OrDefault<BaseChannelConfig>> channelConfigs = new HashSet<>();
public Set<BaseChannelConfig> getAllChannels() {
Set<BaseChannelConfig> channelConfigs = new HashSet<>();
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
if (entry.getKey().equals(ChannelConfig.DEFAULT_KEY)) {
continue;
}
channelConfigs.add(new OrDefault<>(entry.getValue(), defaultConfig));
channelConfigs.add(entry.getValue());
}
return channelConfigs;
}
public OrDefault<BaseChannelConfig> orDefault(GameChannel gameChannel) {
return orDefault(gameChannel.getOwnerName(), gameChannel.getChannelName());
}
public OrDefault<BaseChannelConfig> orDefault(String ownerName, String channelName) {
BaseChannelConfig defaultConfig = getDefault();
if (ownerName == null && channelName.contains(":")) {
String[] parts = channelName.split(":", 2);
if (parts.length == 2) {
ownerName = parts[0];
channelName = parts[1];
}
}
return new OrDefault<>(
get(ownerName, channelName),
defaultConfig
);
}
public BaseChannelConfig get(GameChannel gameChannel) {
return get(gameChannel.getOwnerName(), gameChannel.getChannelName());
return resolve(gameChannel.getOwnerName(), gameChannel.getChannelName());
}
public BaseChannelConfig get(String ownerName, String channelName) {
public BaseChannelConfig resolve(String ownerName, String channelName) {
if (ownerName != null) {
ownerName = ownerName.toLowerCase(Locale.ROOT);
@ -210,26 +226,8 @@ public class ChannelConfigHelper {
return gameChannel != null ? get(gameChannel) : null;
}
public Map<GameChannel, OrDefault<BaseChannelConfig>> orDefault(DiscordMessageChannel messageChannel) {
BaseChannelConfig defaultConfig = getDefault();
Map<GameChannel, OrDefault<BaseChannelConfig>> channels = new HashMap<>();
for (Map.Entry<GameChannel, BaseChannelConfig> entry : getDiscordResolved(messageChannel).entrySet()) {
channels.put(
entry.getKey(),
new OrDefault<>(entry.getValue(), defaultConfig)
);
}
return channels;
}
public Map<GameChannel, BaseChannelConfig> getDiscordResolved(DiscordMessageChannel channel) {
Map<String, BaseChannelConfig> pairs = null;
if (channel instanceof DiscordTextChannel) {
pairs = getText((DiscordTextChannel) channel);
} else if (channel instanceof DiscordThreadChannel) {
pairs = getThread((DiscordThreadChannel) channel);
}
public Map<GameChannel, BaseChannelConfig> resolve(DiscordMessageChannel channel) {
Map<String, BaseChannelConfig> pairs = get(channel);
if (pairs == null || pairs.isEmpty()) {
return Collections.emptyMap();
}
@ -247,11 +245,30 @@ public class ChannelConfigHelper {
return channels;
}
public Map<String, BaseChannelConfig> getText(DiscordTextChannel channel) {
return textChannelToConfigMap.get(channel.getId());
private Map<String, BaseChannelConfig> get(DiscordMessageChannel channel) {
Map<String, BaseChannelConfig> pairs = null;
if (channel instanceof DiscordTextChannel) {
pairs = getByTextChannel((DiscordTextChannel) channel);
} else if (channel instanceof DiscordThreadChannel) {
pairs = getByThreadChannel((DiscordThreadChannel) channel);
}
return pairs;
}
public Map<String, BaseChannelConfig> getThread(DiscordThreadChannel channel) {
return threadToConfigCache.get(Pair.of(channel.getParentChannel().getId(), channel.getName()));
private Map<String, BaseChannelConfig> getByTextChannel(DiscordTextChannel channel) {
synchronized (textChannelToConfigMap) {
return textChannelToConfigMap.get(channel.getId());
}
}
private Map<String, BaseChannelConfig> getByThreadChannel(DiscordThreadChannel channel) {
Pair<Long, String> pair = Pair.of(
channel.getParentChannel().getId(),
channel.getName().toLowerCase(Locale.ROOT)
);
synchronized (threadToConfigMap) {
return threadToConfigMap.get(pair);
}
}
}

View File

@ -23,7 +23,6 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.ChannelLockingConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.module.type.AbstractModule;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.Permission;
@ -34,7 +33,6 @@ import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.requests.restaction.PermissionOverrideAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
@ -52,11 +50,11 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
@Override
public void enable() {
doForAllChannels((config, channelConfig) -> {
OrDefault<ChannelLockingConfig> shutdownConfig = config.map(cfg -> cfg.channelLocking);
OrDefault<ChannelLockingConfig.Channels> channels = shutdownConfig.map(cfg -> cfg.channels);
OrDefault<ChannelLockingConfig.Threads> threads = shutdownConfig.map(cfg -> cfg.threads);
ChannelLockingConfig shutdownConfig = config.channelLocking;
ChannelLockingConfig.Channels channels = shutdownConfig.channels;
ChannelLockingConfig.Threads threads = shutdownConfig.threads;
if (threads.get(cfg -> cfg.unarchive, true)) {
if (threads.unarchive) {
discordSRV.discordAPI().findOrCreateThreads(config, channelConfig, __ -> {}, new ArrayList<>(), false);
}
channelPermissions(channelConfig, channels, true);
@ -66,11 +64,11 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
@Override
public void disable() {
doForAllChannels((config, channelConfig) -> {
OrDefault<ChannelLockingConfig> shutdownConfig = config.map(cfg -> cfg.channelLocking);
OrDefault<ChannelLockingConfig.Channels> channels = shutdownConfig.map(cfg -> cfg.channels);
OrDefault<ChannelLockingConfig.Threads> threads = shutdownConfig.map(cfg -> cfg.threads);
ChannelLockingConfig shutdownConfig = config.channelLocking;
ChannelLockingConfig.Channels channels = shutdownConfig.channels;
ChannelLockingConfig.Threads threads = shutdownConfig.threads;
if (threads.get(cfg -> cfg.archive, true)) {
if (threads.archive) {
for (DiscordThreadChannel thread : discordSRV.discordAPI().findThreads(config, channelConfig)) {
thread.asJDA().getManager()
.setArchived(true)
@ -84,7 +82,7 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
private void channelPermissions(
IChannelConfig channelConfig,
OrDefault<ChannelLockingConfig.Channels> shutdownConfig,
ChannelLockingConfig.Channels shutdownConfig,
boolean state
) {
JDA jda = discordSRV.jda();
@ -92,20 +90,20 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
return;
}
boolean everyone = shutdownConfig.get(cfg -> cfg.everyone, false);
List<Long> roleIds = shutdownConfig.get(cfg -> cfg.roleIds, Collections.emptyList());
boolean everyone = shutdownConfig.everyone;
List<Long> roleIds = shutdownConfig.roleIds;
if (!everyone && roleIds.isEmpty()) {
return;
}
List<Permission> permissions = new ArrayList<>();
if (shutdownConfig.get(cfg -> cfg.read, false)) {
if (shutdownConfig.read) {
permissions.add(Permission.VIEW_CHANNEL);
}
if (shutdownConfig.get(cfg -> cfg.write, true)) {
if (shutdownConfig.write) {
permissions.add(Permission.MESSAGE_SEND);
}
if (shutdownConfig.get(cfg -> cfg.addReactions, true)) {
if (shutdownConfig.addReactions) {
permissions.add(Permission.MESSAGE_ADD_REACTION);
}
@ -150,9 +148,9 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
action.reason("DiscordSRV channel locking").queue();
}
private void doForAllChannels(BiConsumer<OrDefault<BaseChannelConfig>, IChannelConfig> channelConsumer) {
for (OrDefault<BaseChannelConfig> config : discordSRV.channelConfig().getAllChannels()) {
IChannelConfig channelConfig = config.get(cfg -> cfg instanceof IChannelConfig ? (IChannelConfig) cfg : null);
private void doForAllChannels(BiConsumer<BaseChannelConfig, IChannelConfig> channelConsumer) {
for (BaseChannelConfig config : discordSRV.channelConfig().getAllChannels()) {
IChannelConfig channelConfig = config instanceof IChannelConfig ? (IChannelConfig) config : null;
if (channelConfig == null) {
continue;
}

View File

@ -32,7 +32,6 @@ import com.discordsrv.common.command.game.sender.ICommandSender;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -112,8 +111,8 @@ public abstract class BroadcastCommand implements GameCommandExecutor, GameComma
channels.add(messageChannel);
}
} catch (IllegalArgumentException ignored) {
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(null, channel);
IChannelConfig config = channelConfig.get(cfg -> cfg instanceof IChannelConfig ? (IChannelConfig) cfg : null);
BaseChannelConfig channelConfig = discordSRV.channelConfig().resolve(null, channel);
IChannelConfig config = channelConfig instanceof IChannelConfig ? (IChannelConfig) channelConfig : null;
if (config != null) {
for (Long channelId : config.channelIds()) {

View File

@ -27,7 +27,6 @@ import com.discordsrv.api.event.events.message.receive.discord.DiscordChatMessag
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig;
import com.discordsrv.common.function.OrDefault;
import dev.vankka.mcdiscordreserializer.renderer.implementation.DefaultMinecraftRenderer;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
@ -52,7 +51,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
public static void runInContext(
DiscordChatMessageProcessingEvent event,
OrDefault<DiscordToMinecraftChatConfig> config,
DiscordToMinecraftChatConfig config,
Runnable runnable
) {
getWithContext(event, config, () -> {
@ -63,7 +62,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
public static <T> T getWithContext(
DiscordChatMessageProcessingEvent event,
OrDefault<DiscordToMinecraftChatConfig> config,
DiscordToMinecraftChatConfig config,
Supplier<T> supplier
) {
Context oldValue = CONTEXT.get();
@ -84,7 +83,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
GuildChannel guildChannel = jda.getGuildChannelById(channel);
Context context = CONTEXT.get();
String format = context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.messageUrl) : null;
String format = context != null ? context.config.mentions.messageUrl : null;
if (format == null || guildChannel == null) {
return super.appendLink(part, link);
}
@ -111,8 +110,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
@Override
public @NotNull Component appendChannelMention(@NotNull Component component, @NotNull String id) {
Context context = CONTEXT.get();
DiscordToMinecraftChatConfig.Mentions.Format format =
context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.channel) : null;
DiscordToMinecraftChatConfig.Mentions.Format format = context != null ? context.config.mentions.channel : null;
if (format == null) {
return component.append(Component.text("<#" + id + ">"));
}
@ -136,8 +134,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
@Override
public @NotNull Component appendUserMention(@NotNull Component component, @NotNull String id) {
Context context = CONTEXT.get();
DiscordToMinecraftChatConfig.Mentions.Format format =
context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.user) : null;
DiscordToMinecraftChatConfig.Mentions.Format format = context != null ? context.config.mentions.user : null;
DiscordGuild guild = context != null
? discordSRV.discordAPI().getGuildById(context.event.getGuild().getId())
: null;
@ -167,8 +164,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
@Override
public @NotNull Component appendRoleMention(@NotNull Component component, @NotNull String id) {
Context context = CONTEXT.get();
DiscordToMinecraftChatConfig.Mentions.Format format =
context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.role) : null;
DiscordToMinecraftChatConfig.Mentions.Format format = context != null ? context.config.mentions.role : null;
if (format == null) {
return component.append(Component.text("<#" + id + ">"));
}
@ -191,9 +187,9 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer {
private static class Context {
private final DiscordChatMessageProcessingEvent event;
private final OrDefault<DiscordToMinecraftChatConfig> config;
private final DiscordToMinecraftChatConfig config;
public Context(DiscordChatMessageProcessingEvent event, OrDefault<DiscordToMinecraftChatConfig> config) {
public Context(DiscordChatMessageProcessingEvent event, DiscordToMinecraftChatConfig config) {
this.event = event;
this.config = config;
}

View File

@ -39,10 +39,18 @@ public abstract class MainConfig implements Config {
return FILE_NAME;
}
public BaseChannelConfig createDefaultChannel() {
return new ChannelConfig();
}
public BaseChannelConfig createDefaultBaseChannel() {
return new BaseChannelConfig();
}
@DefaultOnly(ChannelConfig.DEFAULT_KEY)
public Map<String, BaseChannelConfig> channels = new LinkedHashMap<String, BaseChannelConfig>() {{
put(GameChannel.DEFAULT_NAME, new ChannelConfig());
put(ChannelConfig.DEFAULT_KEY, new BaseChannelConfig());
put(GameChannel.DEFAULT_NAME, createDefaultChannel());
put(ChannelConfig.DEFAULT_KEY, createDefaultBaseChannel());
}};
public LinkedAccountConfig linkedAccounts = new LinkedAccountConfig();

View File

@ -41,7 +41,6 @@ import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl;
import com.discordsrv.common.discord.api.entity.guild.DiscordGuildMemberImpl;
import com.discordsrv.common.discord.api.entity.guild.DiscordRoleImpl;
import com.discordsrv.common.function.CheckedSupplier;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
@ -93,7 +92,7 @@ public class DiscordAPIImpl implements DiscordAPI {
* @param config the config that specified the threads
* @return the list of active threads
*/
public List<DiscordThreadChannel> findThreads(OrDefault<BaseChannelConfig> config, IChannelConfig channelConfig) {
public List<DiscordThreadChannel> findThreads(BaseChannelConfig config, IChannelConfig channelConfig) {
List<DiscordThreadChannel> channels = new ArrayList<>();
findOrCreateThreads(config, channelConfig, channels::add, null, false);
return channels;
@ -106,7 +105,7 @@ public class DiscordAPIImpl implements DiscordAPI {
* @param futures a possibly null list of {@link CompletableFuture} for tasks that need to be completed to get all threads
*/
public void findOrCreateThreads(
OrDefault<BaseChannelConfig> config,
BaseChannelConfig config,
IChannelConfig channelConfig,
Consumer<DiscordThreadChannel> channelConsumer,
@Nullable List<CompletableFuture<DiscordThreadChannel>> futures,
@ -179,11 +178,11 @@ public class DiscordAPIImpl implements DiscordAPI {
}
private CompletableFuture<DiscordThreadChannel> findOrCreateThread(
OrDefault<BaseChannelConfig> config,
BaseChannelConfig config,
ThreadConfig threadConfig,
DiscordTextChannel textChannel
) {
if (!config.map(cfg -> cfg.channelLocking).map(cfg -> cfg.threads).get(cfg -> cfg.unarchive, true)) {
if (!config.channelLocking.threads.unarchive) {
return textChannel.createThread(threadConfig.threadName, threadConfig.privateThread);
}

View File

@ -40,7 +40,6 @@ import com.discordsrv.api.placeholder.annotation.PlaceholderRemainder;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
@ -324,14 +323,14 @@ public class ReceivedDiscordMessageImpl implements ReceivedDiscordMessage {
//
@Placeholder("message_attachments")
public Component _attachments(OrDefault<BaseChannelConfig> config, @PlaceholderRemainder String suffix) {
public Component _attachments(BaseChannelConfig config, @PlaceholderRemainder String suffix) {
if (suffix.startsWith("_")) {
suffix = suffix.substring(1);
} else if (!suffix.isEmpty()) {
return null;
}
String attachmentFormat = config.map(cfg -> cfg.discordToMinecraft).get(cfg -> cfg.attachmentFormat);
String attachmentFormat = config.discordToMinecraft.attachmentFormat;
List<Component> components = new ArrayList<>();
for (Attachment attachment : attachments) {
components.add(ComponentUtil.fromAPI(

View File

@ -1,76 +0,0 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2023 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.function;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.function.Function;
public class OrDefault<T> {
private final T primary;
private final T secondary;
public OrDefault(T primary, T secondary) {
this.primary = primary;
this.secondary = secondary;
}
@Contract("_, !null -> !null")
public <R> R get(@NotNull Function<T, R> mappingFunction, @Nullable R otherwise) {
R value = get(mappingFunction);
return value != null ? value : otherwise;
}
@NotNull
public <R> Optional<R> opt(@NotNull Function<T, R> mappingFunction, @Nullable R otherwise) {
return Optional.ofNullable(get(mappingFunction, otherwise));
}
@Nullable
public <R> R get(@NotNull Function<T, R> mappingFunction) {
if (primary != null) {
R primaryValue = mappingFunction.apply(primary);
if (primaryValue != null) {
return primaryValue;
}
}
return mappingFunction.apply(secondary);
}
public <R> Optional<R> opt(@NotNull Function<T, R> mappingFunction) {
return Optional.ofNullable(get(mappingFunction));
}
public <R> OrDefault<R> map(@NotNull Function<T, R> mappingFunction) {
R primaryValue = null;
R secondaryValue = null;
if (primary != null) {
primaryValue = mappingFunction.apply(primary);
}
if (secondary != null) {
secondaryValue = mappingFunction.apply(secondary);
}
return new OrDefault<>(primaryValue, secondaryValue);
}
}

View File

@ -36,10 +36,10 @@ import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.module.type.AbstractModule;
import net.kyori.adventure.text.Component;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
@ -54,8 +54,8 @@ public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
@Override
public boolean isEnabled() {
for (OrDefault<BaseChannelConfig> config : discordSRV.channelConfig().getAllChannels()) {
if (config.map(cfg -> cfg.discordToMinecraft).get(cfg -> cfg.enabled, false)) {
for (BaseChannelConfig config : discordSRV.channelConfig().getAllChannels()) {
if (config.discordToMinecraft.enabled) {
return true;
}
}
@ -83,20 +83,20 @@ public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
return;
}
Map<GameChannel, OrDefault<BaseChannelConfig>> channels = discordSRV.channelConfig().orDefault(event.getChannel());
Map<GameChannel, BaseChannelConfig> channels = discordSRV.channelConfig().resolve(event.getChannel());
if (channels == null || channels.isEmpty()) {
return;
}
for (Map.Entry<GameChannel, OrDefault<BaseChannelConfig>> entry : channels.entrySet()) {
for (Map.Entry<GameChannel, BaseChannelConfig> entry : channels.entrySet()) {
process(event, entry.getKey(), entry.getValue());
}
event.markAsProcessed();
}
private void process(DiscordChatMessageProcessingEvent event, GameChannel gameChannel, OrDefault<BaseChannelConfig> channelConfig) {
OrDefault<DiscordToMinecraftChatConfig> chatConfig = channelConfig.map(cfg -> cfg.discordToMinecraft);
if (!chatConfig.get(cfg -> cfg.enabled, true)) {
private void process(DiscordChatMessageProcessingEvent event, GameChannel gameChannel, BaseChannelConfig channelConfig) {
DiscordToMinecraftChatConfig chatConfig = channelConfig.discordToMinecraft;
if (!chatConfig.enabled) {
return;
}
@ -106,22 +106,19 @@ public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
DiscordGuildMember member = discordMessage.getMember();
boolean webhookMessage = discordMessage.isWebhookMessage();
DiscordIgnoresConfig ignores = chatConfig.get(cfg -> cfg.ignores);
DiscordIgnoresConfig ignores = chatConfig.ignores;
if (ignores != null && ignores.shouldBeIgnored(webhookMessage, author, member)) {
// TODO: response for humans
return;
}
String format = chatConfig.opt(cfg -> webhookMessage ? cfg.webhookFormat : cfg.format)
.map(option -> option.replace("\\n", "\n"))
.orElse(null);
if (format == null) {
String format = webhookMessage ? chatConfig.webhookFormat : chatConfig.format;
if (StringUtils.isBlank(format)) {
return;
}
Placeholders message = new Placeholders(event.getMessageContent());
chatConfig.opt(cfg -> cfg.contentRegexFilters)
.ifPresent(filters -> filters.forEach(message::replaceAll));
chatConfig.contentRegexFilters.forEach(message::replaceAll);
Component messageComponent = DiscordSRVMinecraftRenderer.getWithContext(event, chatConfig, () ->
discordSRV.componentFactory().minecraftSerializer().serialize(message.toString()));

View File

@ -38,7 +38,6 @@ import com.discordsrv.common.config.main.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.channels.MirroringConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.module.type.AbstractModule;
@ -71,8 +70,8 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
@Override
public boolean isEnabled() {
for (OrDefault<BaseChannelConfig> config : discordSRV.channelConfig().getAllChannels()) {
if (config.map(cfg -> cfg.mirroring).get(cfg -> cfg.enabled, false)) {
for (BaseChannelConfig config : discordSRV.channelConfig().getAllChannels()) {
if (config.mirroring.enabled) {
return true;
}
}
@ -90,7 +89,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
return;
}
Map<GameChannel, OrDefault<BaseChannelConfig>> channels = discordSRV.channelConfig().orDefault(event.getChannel());
Map<GameChannel, BaseChannelConfig> channels = discordSRV.channelConfig().resolve(event.getChannel());
if (channels == null || channels.isEmpty()) {
return;
}
@ -98,31 +97,31 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
ReceivedDiscordMessage message = event.getDiscordMessage();
DiscordMessageChannel channel = event.getChannel();
List<Pair<DiscordGuildMessageChannel, OrDefault<MirroringConfig>>> mirrorChannels = new ArrayList<>();
List<Pair<DiscordGuildMessageChannel, MirroringConfig>> mirrorChannels = new ArrayList<>();
List<CompletableFuture<DiscordThreadChannel>> futures = new ArrayList<>();
Map<ReceivedDiscordMessage.Attachment, byte[]> attachments = new LinkedHashMap<>();
DiscordMessageEmbed.Builder attachmentEmbed = DiscordMessageEmbed.builder().setDescription("Attachments");
for (Map.Entry<GameChannel, OrDefault<BaseChannelConfig>> entry : channels.entrySet()) {
OrDefault<BaseChannelConfig> channelConfig = entry.getValue();
OrDefault<MirroringConfig> config = channelConfig.map(cfg -> cfg.mirroring);
if (!config.get(cfg -> cfg.enabled, true)) {
for (Map.Entry<GameChannel, BaseChannelConfig> entry : channels.entrySet()) {
BaseChannelConfig channelConfig = entry.getValue();
MirroringConfig config = channelConfig.mirroring;
if (!config.enabled) {
continue;
}
DiscordIgnoresConfig ignores = config.get(cfg -> cfg.ignores);
DiscordIgnoresConfig ignores = config.ignores;
if (ignores != null && ignores.shouldBeIgnored(message.isWebhookMessage(), message.getAuthor(), message.getMember())) {
continue;
}
IChannelConfig iChannelConfig = channelConfig.get(cfg -> cfg instanceof IChannelConfig ? (IChannelConfig) cfg : null);
IChannelConfig iChannelConfig = channelConfig instanceof IChannelConfig ? (IChannelConfig) channelConfig : null;
if (iChannelConfig == null) {
continue;
}
OrDefault<MirroringConfig.AttachmentConfig> attachmentConfig = config.map(cfg -> cfg.attachments);
int maxSize = attachmentConfig.get(cfg -> cfg.maximumSizeKb, -1);
boolean embedAttachments = attachmentConfig.get(cfg -> cfg.embedAttachments, true);
MirroringConfig.AttachmentConfig attachmentConfig = config.attachments;
int maxSize = attachmentConfig.maximumSizeKb;
boolean embedAttachments = attachmentConfig.embedAttachments;
if (maxSize >= 0 || embedAttachments) {
for (ReceivedDiscordMessage.Attachment attachment : message.getAttachments()) {
if (attachments.containsKey(attachment)) {
@ -175,18 +174,18 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
}
CompletableFutureUtil.combine(futures).whenComplete((v, t) -> {
List<CompletableFuture<Pair<ReceivedDiscordMessage, OrDefault<MirroringConfig>>>> messageFutures = new ArrayList<>();
for (Pair<DiscordGuildMessageChannel, OrDefault<MirroringConfig>> pair : mirrorChannels) {
List<CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>>> messageFutures = new ArrayList<>();
for (Pair<DiscordGuildMessageChannel, MirroringConfig> pair : mirrorChannels) {
DiscordGuildMessageChannel mirrorChannel = pair.getKey();
OrDefault<MirroringConfig> config = pair.getValue();
OrDefault<MirroringConfig.AttachmentConfig> attachmentConfig = config.map(cfg -> cfg.attachments);
MirroringConfig config = pair.getValue();
MirroringConfig.AttachmentConfig attachmentConfig = config.attachments;
SendableDiscordMessage.Builder messageBuilder = convert(event.getDiscordMessage(), mirrorChannel, config);
if (!attachmentEmbed.getFields().isEmpty() && attachmentConfig.get(cfg -> cfg.embedAttachments, true)) {
if (!attachmentEmbed.getFields().isEmpty() && attachmentConfig.embedAttachments) {
messageBuilder.addEmbed(attachmentEmbed.build());
}
int maxSize = attachmentConfig.get(cfg -> cfg.maximumSizeKb, -1);
int maxSize = attachmentConfig.maximumSizeKb;
Map<String, InputStream> currentAttachments;
if (!attachments.isEmpty() && maxSize > 0) {
currentAttachments = new LinkedHashMap<>();
@ -199,7 +198,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
currentAttachments = Collections.emptyMap();
}
CompletableFuture<Pair<ReceivedDiscordMessage, OrDefault<MirroringConfig>>> future =
CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>> future =
mirrorChannel.sendMessage(messageBuilder.build(), currentAttachments)
.thenApply(msg -> Pair.of(msg, config));
@ -212,7 +211,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
CompletableFutureUtil.combine(messageFutures).whenComplete((messages, t2) -> {
Map<Long, MessageReference> references = new LinkedHashMap<>();
for (Pair<ReceivedDiscordMessage, OrDefault<MirroringConfig>> pair : messages) {
for (Pair<ReceivedDiscordMessage, MirroringConfig> pair : messages) {
ReceivedDiscordMessage msg = pair.getKey();
references.put(msg.getChannel().getId(), getReference(msg, pair.getValue()));
}
@ -271,12 +270,12 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
private SendableDiscordMessage.Builder convert(
ReceivedDiscordMessage message,
DiscordGuildMessageChannel destinationChannel,
OrDefault<MirroringConfig> config
MirroringConfig config
) {
DiscordGuildMember member = message.getMember();
DiscordUser user = message.getAuthor();
String username = discordSRV.placeholderService().replacePlaceholders(
config.get(cfg -> cfg.usernameFormat, "%user_effective_name% [M]"),
config.usernameFormat, "%user_effective_name% [M]",
member, user
);
if (username.length() > 32) {
@ -308,13 +307,8 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
Long.toUnsignedString(matchingReference.messageId)
) : replyMessage.getJumpUrl();
String replyFormat = config.get(
cfg -> cfg.replyFormat,
"[In reply to %user_effective_name|user_name%](%message_jump_url%)\n"
);
content = discordSRV.placeholderService()
.replacePlaceholders(replyFormat, replyMessage.getMember(), replyMessage.getAuthor())
.replacePlaceholders(config.replyFormat, replyMessage.getMember(), replyMessage.getAuthor())
.replace("%message_jump_url%", jumpUrl) + content;
}
@ -334,7 +328,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
return builder;
}
private MessageReference getReference(ReceivedDiscordMessage message, OrDefault<MirroringConfig> config) {
private MessageReference getReference(ReceivedDiscordMessage message, MirroringConfig config) {
return getReference(message.getChannel(), message.getId(), message.isWebhookMessage(), config);
}
@ -342,7 +336,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
DiscordMessageChannel channel,
long messageId,
boolean webhookMessage,
OrDefault<MirroringConfig> config
MirroringConfig config
) {
if (channel instanceof DiscordTextChannel) {
DiscordTextChannel textChannel = (DiscordTextChannel) channel;
@ -380,13 +374,13 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
private final long threadId;
private final long messageId;
private final boolean webhookMessage;
private final OrDefault<MirroringConfig> config;
private final MirroringConfig config;
public MessageReference(
DiscordTextChannel textChannel,
long messageId,
boolean webhookMessage,
OrDefault<MirroringConfig> config
MirroringConfig config
) {
this(textChannel.getId(), -1L, messageId, webhookMessage, config);
}
@ -395,7 +389,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
DiscordThreadChannel threadChannel,
long messageId,
boolean webhookMessage,
OrDefault<MirroringConfig> config
MirroringConfig config
) {
this(threadChannel.getParentChannel().getId(), threadChannel.getId(), messageId, webhookMessage, config);
}
@ -405,7 +399,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
long threadId,
long messageId,
boolean webhookMessage,
OrDefault<MirroringConfig> config
MirroringConfig config
) {
this.channelId = channelId;
this.threadId = threadId;

View File

@ -33,7 +33,6 @@ import com.discordsrv.common.config.main.channels.IMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageClusterImpl;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.discordsrv.common.logging.NamedLogger;
import com.discordsrv.common.module.type.AbstractModule;
@ -55,19 +54,19 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
@Override
public boolean isEnabled() {
for (OrDefault<BaseChannelConfig> channelConfig : discordSRV.channelConfig().getAllChannels()) {
if (mapConfig(channelConfig).get(IMessageConfig::enabled, false)) {
for (BaseChannelConfig channelConfig : discordSRV.channelConfig().getAllChannels()) {
if (mapConfig(channelConfig).enabled()) {
return true;
}
}
return false;
}
public OrDefault<T> mapConfig(E event, OrDefault<BaseChannelConfig> channelConfig) {
public T mapConfig(E event, BaseChannelConfig channelConfig) {
return mapConfig(channelConfig);
}
public abstract OrDefault<T> mapConfig(OrDefault<BaseChannelConfig> channelConfig);
public abstract T mapConfig(BaseChannelConfig channelConfig);
public abstract void postClusterToEventBus(ReceivedDiscordMessageCluster cluster);
public final CompletableFuture<?> process(
@ -83,27 +82,27 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
if (channel == null) {
// Send to all channels due to lack of specified channel
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (OrDefault<BaseChannelConfig> channelConfig : discordSRV.channelConfig().getAllChannels()) {
for (BaseChannelConfig channelConfig : discordSRV.channelConfig().getAllChannels()) {
futures.add(forwardToChannel(event, srvPlayer, channelConfig));
}
return CompletableFutureUtil.combine(futures);
}
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(channel);
BaseChannelConfig channelConfig = discordSRV.channelConfig().get(channel);
return forwardToChannel(event, srvPlayer, channelConfig);
}
private CompletableFuture<Void> forwardToChannel(
@Nullable E event,
@Nullable IPlayer player,
@NotNull OrDefault<BaseChannelConfig> config
@NotNull BaseChannelConfig config
) {
OrDefault<T> moduleConfig = mapConfig(event, config);
if (!moduleConfig.get(IMessageConfig::enabled, true)) {
T moduleConfig = mapConfig(event, config);
if (!moduleConfig.enabled()) {
return null;
}
IChannelConfig channelConfig = config.get(c -> c instanceof IChannelConfig ? (IChannelConfig) c : null);
IChannelConfig channelConfig = config instanceof IChannelConfig ? (IChannelConfig) config : null;
if (channelConfig == null) {
return null;
}
@ -128,7 +127,7 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
discordSRV.discordAPI().findOrCreateThreads(config, channelConfig, messageChannels::add, futures, true);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenCompose((v) -> {
SendableDiscordMessage.Builder format = moduleConfig.get(IMessageConfig::format);
SendableDiscordMessage.Builder format = moduleConfig.format();
if (format == null) {
return CompletableFuture.completedFuture(null);
}
@ -181,12 +180,12 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
});
}
public String convertComponent(OrDefault<T> config, Component component) {
public String convertComponent(T config, Component component) {
return discordSRV.componentFactory().discordSerializer().serialize(component);
}
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
OrDefault<T> config,
T config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
@ -210,5 +209,5 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
return futures;
}
public abstract void setPlaceholders(OrDefault<T> config, E event, SendableDiscordMessage.Formatter formatter);
public abstract void setPlaceholders(T config, E event, SendableDiscordMessage.Formatter formatter);
}

View File

@ -25,18 +25,41 @@ import com.discordsrv.api.event.bus.EventPriority;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.forward.game.AwardMessageForwardedEvent;
import com.discordsrv.api.event.events.message.receive.game.AwardMessageReceiveEvent;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.AwardMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.server.ServerBaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.github.benmanes.caffeine.cache.Cache;
import net.kyori.adventure.text.Component;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class AwardMessageModule extends AbstractGameMessageModule<AwardMessageConfig, AwardMessageReceiveEvent> {
private final Cache<UUID, AtomicInteger> advancementCount;
public AwardMessageModule(DiscordSRV discordSRV) {
super(discordSRV, "AWARD_MESSAGES");
this.advancementCount = discordSRV.caffeineBuilder()
.expireAfterWrite(5, TimeUnit.SECONDS)
.build();
}
@SuppressWarnings("DataFlowIssue") // Not possible
private boolean checkIfShouldPermit(DiscordSRVPlayer player) {
// Prevent spamming if a lot of advancements are granted at once (the advancement command)
int count = advancementCount.get(player.uniqueId(), key -> new AtomicInteger(0)).incrementAndGet();
boolean permit = count < 5;
if (!permit && (count % 5 == 0)) {
logger().debug("Skipping advancement/achievement processing for player " + player.username()
+ ", currently at " + count + " advancements/achievements within 5 seconds");
}
return permit;
}
@Subscribe(priority = EventPriority.LAST)
@ -45,13 +68,16 @@ public class AwardMessageModule extends AbstractGameMessageModule<AwardMessageCo
return;
}
process(event, event.getPlayer(), event.getGameChannel());
if (!checkIfShouldPermit(event.getPlayer())) {
process(event, event.getPlayer(), event.getGameChannel());
}
event.markAsProcessed();
}
@Override
public OrDefault<AwardMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> ((ServerBaseChannelConfig) cfg).awardMessages);
public AwardMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return ((ServerBaseChannelConfig) channelConfig).awardMessages;
}
@Override
@ -60,7 +86,7 @@ public class AwardMessageModule extends AbstractGameMessageModule<AwardMessageCo
}
@Override
public void setPlaceholders(OrDefault<AwardMessageConfig> config, AwardMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {
public void setPlaceholders(AwardMessageConfig config, AwardMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {
MinecraftComponent nameComponent = event.getName();
Component name = nameComponent != null ? ComponentUtil.fromAPI(nameComponent) : null;

View File

@ -31,7 +31,6 @@ import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.DeathMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.server.ServerBaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
public class DeathMessageModule extends AbstractGameMessageModule<DeathMessageConfig, DeathMessageReceiveEvent> {
@ -50,8 +49,8 @@ public class DeathMessageModule extends AbstractGameMessageModule<DeathMessageCo
}
@Override
public OrDefault<DeathMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> ((ServerBaseChannelConfig) cfg).deathMessages);
public DeathMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return ((ServerBaseChannelConfig) channelConfig).deathMessages;
}
@Override
@ -61,7 +60,7 @@ public class DeathMessageModule extends AbstractGameMessageModule<DeathMessageCo
@Override
public void setPlaceholders(
OrDefault<DeathMessageConfig> config,
DeathMessageConfig config,
DeathMessageReceiveEvent event,
SendableDiscordMessage.Formatter formatter
) {

View File

@ -30,7 +30,6 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.IMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
public class JoinMessageModule extends AbstractGameMessageModule<IMessageConfig, JoinMessageReceiveEvent> {
@ -49,15 +48,13 @@ public class JoinMessageModule extends AbstractGameMessageModule<IMessageConfig,
}
@Override
public OrDefault<IMessageConfig> mapConfig(JoinMessageReceiveEvent event, OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig
.map(BaseChannelConfig::joinMessages)
.map(cfg -> cfg.getForEvent(event));
public IMessageConfig mapConfig(JoinMessageReceiveEvent event, BaseChannelConfig channelConfig) {
return channelConfig.joinMessages().getForEvent(event);
}
@Override
public OrDefault<IMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(BaseChannelConfig::joinMessages);
public IMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return channelConfig.joinMessages();
}
@Override
@ -67,7 +64,7 @@ public class JoinMessageModule extends AbstractGameMessageModule<IMessageConfig,
@Override
public void setPlaceholders(
OrDefault<IMessageConfig> config,
IMessageConfig config,
JoinMessageReceiveEvent event,
SendableDiscordMessage.Formatter formatter
) {

View File

@ -30,7 +30,6 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.LeaveMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
public class LeaveMessageModule extends AbstractGameMessageModule<LeaveMessageConfig, LeaveMessageReceiveEvent> {
@ -49,8 +48,8 @@ public class LeaveMessageModule extends AbstractGameMessageModule<LeaveMessageCo
}
@Override
public OrDefault<LeaveMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> cfg.leaveMessages);
public LeaveMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return channelConfig.leaveMessages;
}
@Override
@ -60,7 +59,7 @@ public class LeaveMessageModule extends AbstractGameMessageModule<LeaveMessageCo
@Override
public void setPlaceholders(
OrDefault<LeaveMessageConfig> config,
LeaveMessageConfig config,
LeaveMessageReceiveEvent event,
SendableDiscordMessage.Formatter formatter
) {

View File

@ -31,7 +31,6 @@ import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.ServerSwitchMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.proxy.ProxyBaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
public class ServerSwitchMessageModule extends AbstractGameMessageModule<ServerSwitchMessageConfig, ServerSwitchMessageReceiveEvent> {
@ -50,8 +49,8 @@ public class ServerSwitchMessageModule extends AbstractGameMessageModule<ServerS
}
@Override
public OrDefault<ServerSwitchMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> ((ProxyBaseChannelConfig) cfg).serverSwitchMessages);
public ServerSwitchMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return ((ProxyBaseChannelConfig) channelConfig).serverSwitchMessages;
}
@Override
@ -61,7 +60,7 @@ public class ServerSwitchMessageModule extends AbstractGameMessageModule<ServerS
@Override
public void setPlaceholders(
OrDefault<ServerSwitchMessageConfig> config,
ServerSwitchMessageConfig config,
ServerSwitchMessageReceiveEvent event,
SendableDiscordMessage.Formatter formatter
) {

View File

@ -26,7 +26,6 @@ import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageR
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.StartMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.player.IPlayer;
import java.util.Collections;
@ -46,8 +45,8 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
}
@Override
public OrDefault<StartMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> cfg.startMessage);
public StartMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return channelConfig.startMessage;
}
@Override
@ -55,21 +54,21 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
@Override
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
OrDefault<StartMessageConfig> config,
StartMessageConfig config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
AbstractGameMessageReceiveEvent event,
Object... context
) {
if (!config.get(cfg -> cfg.enabled, false)) {
if (!config.enabled) {
return Collections.emptyMap();
}
return super.sendMessageToChannels(config, player, format, channels, event, context);
}
@Override
public void setPlaceholders(OrDefault<StartMessageConfig> config, AbstractGameMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {}
public void setPlaceholders(StartMessageConfig config, AbstractGameMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {}
@Override
public void enable() {

View File

@ -26,7 +26,6 @@ import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageR
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.StopMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.player.IPlayer;
import java.util.Collections;
@ -49,8 +48,8 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
}
@Override
public OrDefault<StopMessageConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> cfg.stopMessage);
public StopMessageConfig mapConfig(BaseChannelConfig channelConfig) {
return channelConfig.stopMessage;
}
@Override
@ -58,21 +57,21 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
@Override
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
OrDefault<StopMessageConfig> config,
StopMessageConfig config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
AbstractGameMessageReceiveEvent event,
Object... context
) {
if (!config.get(cfg -> cfg.enabled, false)) {
if (!config.enabled) {
return Collections.emptyMap();
}
return super.sendMessageToChannels(config, player, format, channels, event, context);
}
@Override
public void setPlaceholders(OrDefault<StopMessageConfig> config, AbstractGameMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {}
public void setPlaceholders(StopMessageConfig config, AbstractGameMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {}
@Override
public void disable() {

View File

@ -22,7 +22,6 @@ import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.MinecraftToDiscordChatConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.module.type.AbstractModule;
import com.github.benmanes.caffeine.cache.Cache;
import net.dv8tion.jda.api.entities.Guild;
@ -60,16 +59,14 @@ public class MentionCachingModule extends AbstractModule<DiscordSRV> {
@Override
public boolean isEnabled() {
for (OrDefault<BaseChannelConfig> channel : discordSRV.channelConfig().getAllChannels()) {
OrDefault<MinecraftToDiscordChatConfig> config = channel.map(cfg -> cfg.minecraftToDiscord);
if (!config.get(cfg -> cfg.enabled, false)) {
for (BaseChannelConfig channel : discordSRV.channelConfig().getAllChannels()) {
MinecraftToDiscordChatConfig config = channel.minecraftToDiscord;
if (!config.enabled) {
continue;
}
OrDefault<MinecraftToDiscordChatConfig.Mentions> mentions = config.map(cfg -> cfg.mentions);
if (mentions.get(cfg -> cfg.roles, false)
|| mentions.get(cfg -> cfg.users, false)
|| mentions.get(cfg -> cfg.channels, false)) {
MinecraftToDiscordChatConfig.Mentions mentions = config.mentions;
if (mentions.roles || mentions.users || mentions.channels) {
return true;
}
}

View File

@ -38,7 +38,6 @@ import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.channels.MinecraftToDiscordChatConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.discordsrv.common.messageforwarding.game.AbstractGameMessageModule;
import com.discordsrv.common.player.IPlayer;
@ -70,8 +69,8 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
}
@Override
public OrDefault<MinecraftToDiscordChatConfig> mapConfig(OrDefault<BaseChannelConfig> channelConfig) {
return channelConfig.map(cfg -> cfg.minecraftToDiscord);
public MinecraftToDiscordChatConfig mapConfig(BaseChannelConfig channelConfig) {
return channelConfig.minecraftToDiscord;
}
@Override
@ -80,20 +79,19 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
}
@Override
public String convertComponent(OrDefault<MinecraftToDiscordChatConfig> config, Component component) {
public String convertComponent(MinecraftToDiscordChatConfig config, Component component) {
DiscordSerializer discordSerializer = discordSRV.componentFactory().discordSerializer();
String content = discordSerializer.serialize(component, discordSerializer.getDefaultOptions().withEscapeMarkdown(false));
Placeholders messagePlaceholders = new Placeholders(content);
config.opt(cfg -> cfg.contentRegexFilters)
.ifPresent(patterns -> patterns.forEach(messagePlaceholders::replaceAll));
config.contentRegexFilters.forEach(messagePlaceholders::replaceAll);
return messagePlaceholders.toString();
}
@Override
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
OrDefault<MinecraftToDiscordChatConfig> config,
MinecraftToDiscordChatConfig config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
@ -132,24 +130,22 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
}
@Override
public void setPlaceholders(OrDefault<MinecraftToDiscordChatConfig> config, GameChatMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {}
public void setPlaceholders(MinecraftToDiscordChatConfig config, GameChatMessageReceiveEvent event, SendableDiscordMessage.Formatter formatter) {}
private final Pattern MENTION_PATTERN = Pattern.compile("@\\S+");
private CompletableFuture<SendableDiscordMessage> getMessageForGuild(
OrDefault<MinecraftToDiscordChatConfig> config,
MinecraftToDiscordChatConfig config,
SendableDiscordMessage.Builder format,
Guild guild,
String message,
IPlayer player,
Object[] context
) {
OrDefault<MinecraftToDiscordChatConfig.Mentions> mentionConfig = config.map(cfg -> cfg.mentions);
MinecraftToDiscordChatConfig.Mentions mentionConfig = config.mentions;
MentionCachingModule mentionCaching = discordSRV.getModule(MentionCachingModule.class);
if (mentionCaching != null
&& mentionConfig.get(cfg -> cfg.users, false)
&& mentionConfig.get(cfg -> cfg.uncachedUsers, false)
if (mentionCaching != null && mentionConfig.users && mentionConfig.uncachedUsers
&& player.hasPermission("discordsrv.mention.lookup.user")) {
List<CompletableFuture<List<MentionCachingModule.CachedMention>>> futures = new ArrayList<>();
@ -174,7 +170,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
}
private SendableDiscordMessage getMessageForGuildWithMentions(
OrDefault<MinecraftToDiscordChatConfig> config,
MinecraftToDiscordChatConfig config,
SendableDiscordMessage.Builder format,
Guild guild,
String message,
@ -182,7 +178,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
Object[] context,
Set<MentionCachingModule.CachedMention> memberMentions
) {
OrDefault<MinecraftToDiscordChatConfig.Mentions> mentionConfig = config.map(cfg -> cfg.mentions);
MinecraftToDiscordChatConfig.Mentions mentionConfig = config.mentions;
Set<MentionCachingModule.CachedMention> mentions = new LinkedHashSet<>();
if (memberMentions != null) {
@ -191,13 +187,13 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
MentionCachingModule mentionCaching = discordSRV.getModule(MentionCachingModule.class);
if (mentionCaching != null) {
if (mentionConfig.get(cfg -> cfg.roles, false)) {
if (mentionConfig.roles) {
mentions.addAll(mentionCaching.getRoleMentions(guild).values());
}
if (mentionConfig.get(cfg -> cfg.channels, true)) {
if (mentionConfig.channels) {
mentions.addAll(mentionCaching.getChannelMentions(guild).values());
}
if (mentionConfig.get(cfg -> cfg.users, false)) {
if (mentionConfig.users) {
mentions.addAll(mentionCaching.getMemberMentions(guild).values());
}
}
@ -211,10 +207,10 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
.forEachOrdered(mention -> channelMessagePlaceholders.replaceAll(mention.search(), mention.mention()));
List<AllowedMention> allowedMentions = new ArrayList<>();
if (mentionConfig.get(cfg -> cfg.users, false) && player.hasPermission("discordsrv.mention.user")) {
if (mentionConfig.users && player.hasPermission("discordsrv.mention.user")) {
allowedMentions.add(AllowedMention.ALL_USERS);
}
if (mentionConfig.get(cfg -> cfg.roles, false)) {
if (mentionConfig.roles) {
if (player.hasPermission("discordsrv.mention.roles.mentionable")) {
for (Role role : guild.getRoles()) {
if (role.isMentionable()) {
@ -227,7 +223,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
}
}
boolean everyone = mentionConfig.get(cfg -> cfg.everyone, false) && player.hasPermission("discordsrv.mention.everyone");
boolean everyone = mentionConfig.everyone && player.hasPermission("discordsrv.mention.everyone");
if (everyone) {
allowedMentions.add(AllowedMention.EVERYONE);
}

View File

@ -24,7 +24,6 @@ import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.sender.ICommandSender;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.discordsrv.common.permission.util.PermissionUtil;
import com.discordsrv.common.profile.Profile;
import net.kyori.adventure.text.Component;
@ -66,8 +65,8 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende
@Nullable
@ApiStatus.NonExtendable
@Placeholder("player_avatar_url")
default String getAvatarUrl(OrDefault<BaseChannelConfig> config) {
String avatarUrlProvider = config.get(cfg -> cfg.avatarUrlProvider);
default String getAvatarUrl(BaseChannelConfig config) {
String avatarUrlProvider = config.avatarUrlProvider;
if (avatarUrlProvider == null) {
return null;
}