Fix for non-text channels in ChannelConfigHelper, resolution by full channel atom. Fix forwarding messages from non-text, non-thread channels to Minecraft. Add test for voice channel messages

This commit is contained in:
Vankka 2024-11-30 15:21:44 +02:00
parent 611f8bba36
commit ed82f642f1
No known key found for this signature in database
GPG Key ID: 62E48025ED4E7EBB
8 changed files with 78 additions and 47 deletions

View File

@ -24,6 +24,7 @@
package com.discordsrv.api.events.message.process.discord;
import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.events.Cancellable;
import com.discordsrv.api.events.Processable;
@ -51,6 +52,10 @@ public class DiscordChatMessageProcessEvent implements Cancellable, Processable.
this.destinationChannel = destinationChannel;
}
public DiscordMessageChannel getChannel() {
return message.getChannel();
}
public ReceivedDiscordMessage getMessage() {
return message;
}

View File

@ -23,9 +23,7 @@
package com.discordsrv.api.events.message.receive.discord;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
import com.discordsrv.api.discord.entity.channel.*;
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.events.Cancellable;
@ -39,15 +37,12 @@ import org.jetbrains.annotations.NotNull;
public class DiscordChatMessageReceiveEvent implements Cancellable {
private final ReceivedDiscordMessage message;
private final DiscordMessageChannel channel;
private final DiscordGuildMessageChannel channel;
private boolean cancelled;
public DiscordChatMessageReceiveEvent(@NotNull ReceivedDiscordMessage discordMessage, @NotNull DiscordMessageChannel channel) {
public DiscordChatMessageReceiveEvent(@NotNull ReceivedDiscordMessage discordMessage, @NotNull DiscordGuildMessageChannel channel) {
this.message = discordMessage;
this.channel = channel;
if (!(channel instanceof DiscordTextChannel) && !(channel instanceof DiscordThreadChannel)) {
throw new IllegalStateException("Cannot process messages that aren't from a text channel or thread");
}
}
public ReceivedDiscordMessage getMessage() {
@ -59,13 +54,7 @@ public class DiscordChatMessageReceiveEvent implements Cancellable {
}
public DiscordGuild getGuild() {
if (channel instanceof DiscordTextChannel) {
return ((DiscordTextChannel) channel).getGuild();
} else if (channel instanceof DiscordThreadChannel) {
return ((DiscordThreadChannel) channel).getParentChannel().getGuild();
} else {
throw new IllegalStateException("Message isn't from a text channel or thread");
}
return channel.getGuild();
}
@Override

View File

@ -112,7 +112,7 @@ public abstract class BroadcastCommand implements GameCommandExecutor, GameComma
channels.add(messageChannel);
}
} catch (IllegalArgumentException ignored) {
BaseChannelConfig channelConfig = discordSRV.channelConfig().resolve(null, channel);
BaseChannelConfig channelConfig = discordSRV.channelConfig().resolve(channel);
CC config = channelConfig != null ? (CC) channelConfig : null;
if (config != null) {

View File

@ -22,6 +22,8 @@ import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.discord.connection.details.DiscordGatewayIntent;
import com.discordsrv.api.discord.entity.DiscordUser;
import com.discordsrv.api.discord.entity.channel.DiscordChannel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;
@ -95,12 +97,19 @@ public class DiscordChatMessageModule extends AbstractModule<DiscordSRV> {
@Subscribe
public void onDiscordMessageReceived(DiscordMessageReceiveEvent event) {
if (!discordSRV.isReady() || event.getMessage().isFromSelf()
|| !(event.getTextChannel() != null || event.getThreadChannel() != null)) {
if (!discordSRV.isReady() || event.getMessage().isFromSelf()) {
return;
}
discordSRV.eventBus().publish(new DiscordChatMessageReceiveEvent(event.getMessage(), event.getChannel()));
DiscordChannel channel = event.getChannel();
if (!(channel instanceof DiscordGuildMessageChannel)) {
return;
}
discordSRV.eventBus().publish(new DiscordChatMessageReceiveEvent(
event.getMessage(),
(DiscordGuildMessageChannel) channel
));
}
@Subscribe

View File

@ -20,7 +20,6 @@ package com.discordsrv.common.helper;
import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
import com.discordsrv.api.events.channel.GameChannelLookupEvent;
import com.discordsrv.common.DiscordSRV;
@ -30,6 +29,8 @@ import com.discordsrv.common.config.main.channels.base.ChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import com.discordsrv.common.config.main.generic.ThreadConfig;
import com.discordsrv.common.core.logging.Logger;
import com.discordsrv.common.core.logging.NamedLogger;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.apache.commons.lang3.tuple.Pair;
@ -45,6 +46,7 @@ import java.util.concurrent.TimeUnit;
public class ChannelConfigHelper {
private final DiscordSRV discordSRV;
private final Logger logger;
// game channel name eg. "global" -> game channel ("discordsrv:global")
private final LoadingCache<String, GameChannel> nameToChannelCache;
@ -53,11 +55,13 @@ public class ChannelConfigHelper {
private final Map<String, BaseChannelConfig> configs;
// caches for Discord channel -> config
private final Map<Long, Map<String, BaseChannelConfig>> textChannelToConfigMap;
private final Map<Long, Map<String, BaseChannelConfig>> messageChannelToConfigMap;
private final Map<Pair<Long, String>, Map<String, BaseChannelConfig>> threadToConfigMap;
public ChannelConfigHelper(DiscordSRV discordSRV) {
this.discordSRV = discordSRV;
this.logger = new NamedLogger(discordSRV, "CHANNEL_CONFIG_HELPER");
this.nameToChannelCache = discordSRV.caffeineBuilder()
.expireAfterWrite(60, TimeUnit.SECONDS)
.expireAfterAccess(30, TimeUnit.SECONDS)
@ -65,21 +69,33 @@ public class ChannelConfigHelper {
.build(new CacheLoader<String, GameChannel>() {
@Override
public @Nullable GameChannel load(@NotNull String channelName) {
GameChannelLookupEvent event = new GameChannelLookupEvent(null, channelName);
public @Nullable GameChannel load(@NotNull String channelAtom) {
Pair<String, String> channelPair = parseOwnerAndChannel(channelAtom);
GameChannelLookupEvent event = new GameChannelLookupEvent(channelPair.getKey(), channelPair.getValue());
discordSRV.eventBus().publish(event);
if (!event.isProcessed()) {
return null;
}
return event.getChannelFromProcessing();
GameChannel channel = event.getChannelFromProcessing();
logger.trace(channelAtom + " looked up to " + GameChannel.toString(channel));
return channel;
}
});
this.configs = new HashMap<>();
this.textChannelToConfigMap = new HashMap<>();
this.messageChannelToConfigMap = new HashMap<>();
this.threadToConfigMap = new LinkedHashMap<>();
}
private Pair<String, String> parseOwnerAndChannel(String channelAtom) {
String[] split = channelAtom.split(":", 2);
String channelName = split[split.length - 1];
String ownerName = split.length == 2 ? split[0] : null;
return Pair.of(ownerName, channelName);
}
@SuppressWarnings("unchecked")
private BaseChannelConfig map(BaseChannelConfig defaultConfig, BaseChannelConfig config)
throws SerializationException {
@ -120,7 +136,7 @@ public class ChannelConfigHelper {
this.configs.putAll(configs);
}
Map<Long, Map<String, BaseChannelConfig>> text = new HashMap<>();
Map<Long, Map<String, BaseChannelConfig>> messageChannel = new HashMap<>();
Map<Pair<Long, String>, Map<String, BaseChannelConfig>> thread = new HashMap<>();
for (Map.Entry<String, BaseChannelConfig> entry : channels().entrySet()) {
@ -132,7 +148,7 @@ public class ChannelConfigHelper {
List<Long> channelIds = destination.channelIds;
if (channelIds != null) {
for (long channelId : channelIds) {
text.computeIfAbsent(channelId, key -> new LinkedHashMap<>())
messageChannel.computeIfAbsent(channelId, key -> new LinkedHashMap<>())
.put(channelName, value);
}
}
@ -151,9 +167,9 @@ public class ChannelConfigHelper {
}
}
synchronized (textChannelToConfigMap) {
textChannelToConfigMap.clear();
textChannelToConfigMap.putAll(text);
synchronized (messageChannelToConfigMap) {
messageChannelToConfigMap.clear();
messageChannelToConfigMap.putAll(messageChannel);
}
synchronized (threadToConfigMap) {
threadToConfigMap.clear();
@ -204,6 +220,12 @@ public class ChannelConfigHelper {
return resolve(gameChannel.getOwnerName(), gameChannel.getChannelName());
}
@Nullable
public BaseChannelConfig resolve(@NotNull String channelAtom) {
Pair<String, String> channelPair = parseOwnerAndChannel(channelAtom);
return resolve(channelPair.getKey(), channelPair.getValue());
}
@Nullable
public BaseChannelConfig resolve(@Nullable String ownerName, @NotNull String channelName) {
if (ownerName != null) {
@ -216,6 +238,7 @@ public class ChannelConfigHelper {
}
// Check if this owner has the highest priority for this channel name
// in case they are, we can also use "channel" config directly
GameChannel gameChannel = nameToChannelCache.get(channelName);
if (gameChannel != null && gameChannel.getOwnerName().equalsIgnoreCase(ownerName)) {
config = findChannel(channelName);
@ -249,19 +272,16 @@ public class ChannelConfigHelper {
}
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);
if (channel instanceof DiscordThreadChannel) {
return getByThreadChannel((DiscordThreadChannel) channel);
}
return pairs;
return getByMessageChannel(channel);
}
private Map<String, BaseChannelConfig> getByTextChannel(DiscordTextChannel channel) {
synchronized (textChannelToConfigMap) {
return textChannelToConfigMap.get(channel.getId());
private Map<String, BaseChannelConfig> getByMessageChannel(DiscordMessageChannel channel) {
synchronized (messageChannelToConfigMap) {
return messageChannelToConfigMap.get(channel.getId());
}
}

View File

@ -26,16 +26,18 @@ import org.junit.jupiter.api.extension.ExtensionContext;
public class FullBootExtension implements BeforeAllCallback, ExtensionContext.Store.CloseableResource {
public static String BOT_TOKEN = System.getenv("DISCORDSRV_AUTOTEST_BOT_TOKEN");
public static String TEST_CHANNEL_ID = System.getenv("DISCORDSRV_AUTOTEST_CHANNEL_ID");
public static String TEXT_CHANNEL_ID = System.getenv("DISCORDSRV_AUTOTEST_CHANNEL_ID");
public static String FORUM_CHANNEL_ID = System.getenv("DISCORDSRV_AUTOTEST_FORUM_ID");
public static String VOICE_CHANNEL_ID = System.getenv("DISCORDSRV_AUTOTEST_VOICE_ID");
public boolean started = false;
@Override
public void beforeAll(ExtensionContext context) {
Assumptions.assumeTrue(BOT_TOKEN != null, "Automated testing bot token");
Assumptions.assumeTrue(TEST_CHANNEL_ID != null, "Automated testing channel id");
Assumptions.assumeTrue(FORUM_CHANNEL_ID != null, "Automated testing forum id");
Assumptions.assumeTrue(TEXT_CHANNEL_ID != null, "Automated testing text channel id");
Assumptions.assumeTrue(FORUM_CHANNEL_ID != null, "Automated testing forum channel id");
Assumptions.assumeTrue(VOICE_CHANNEL_ID != null, "Automated testing voice channel id");
if (started) return;
started = true;

View File

@ -241,22 +241,24 @@ public class MockDiscordSRV extends AbstractDiscordSRV<IBootstrap, MainConfig, C
public MainConfig config() {
MainConfig config = new MainConfig() {};
if (StringUtils.isNotEmpty(FullBootExtension.TEST_CHANNEL_ID) && StringUtils.isNotEmpty(FullBootExtension.FORUM_CHANNEL_ID)) {
if (StringUtils.isNotEmpty(FullBootExtension.TEXT_CHANNEL_ID) && StringUtils.isNotEmpty(FullBootExtension.FORUM_CHANNEL_ID)) {
ChannelConfig global = (ChannelConfig) config.channels.get(GameChannel.DEFAULT_NAME);
DestinationConfig destination = global.destination = new DestinationConfig();
long channelId = Long.parseLong(FullBootExtension.TEST_CHANNEL_ID);
long textChannelId = Long.parseLong(FullBootExtension.TEXT_CHANNEL_ID);
long voiceChannelId = Long.parseLong(FullBootExtension.VOICE_CHANNEL_ID);
long forumId = Long.parseLong(FullBootExtension.FORUM_CHANNEL_ID);
List<Long> channelIds = destination.channelIds;
channelIds.clear();
channelIds.add(channelId);
channelIds.add(textChannelId);
channelIds.add(voiceChannelId);
List<ThreadConfig> threadConfigs = destination.threads;
threadConfigs.clear();
ThreadConfig thread = new ThreadConfig();
thread.channelId = channelId;
thread.channelId = textChannelId;
threadConfigs.add(thread);
ThreadConfig forumThread = new ThreadConfig();

View File

@ -21,6 +21,7 @@ package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordTextChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
import com.discordsrv.api.discord.entity.channel.DiscordVoiceChannel;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.eventbus.EventBus;
import com.discordsrv.api.eventbus.Subscribe;
@ -163,6 +164,7 @@ public class MinecraftToDiscordChatMessageTest {
@Subscribe
public void onForwarded(GameChatMessageForwardedEvent event) {
int text = 0;
int voice = 0;
int thread = 0;
for (ReceivedDiscordMessage message : event.getDiscordMessage().getMessages()) {
String content = message.getContent();
@ -170,13 +172,15 @@ public class MinecraftToDiscordChatMessageTest {
DiscordMessageChannel channel = message.getChannel();
if (channel instanceof DiscordTextChannel) {
text++;
} else if (channel instanceof DiscordVoiceChannel) {
voice++;
} else if (channel instanceof DiscordThreadChannel) {
thread++;
}
}
}
success.complete(text == 1 && thread == 2);
success.complete(text == 1 && voice == 1 && thread == 2);
}
}
}