Move around configs, make IChannelConfig better / consolidate channel+thread lookup

This commit is contained in:
Vankka 2023-06-17 16:21:16 +03:00
parent 95272cfa80
commit 06b1dff50a
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
32 changed files with 432 additions and 331 deletions

View File

@ -70,6 +70,14 @@ public interface DiscordAPI {
@Nullable
DiscordTextChannel getTextChannelById(long id);
/**
* Gets a Discord forum channel by id, the provided entity should not be stored for long periods of time.
* @param id the id for the text channel
* @return the forum channel
*/
@Nullable
DiscordForumChannel getForumChannelById(long id);
/**
* Gets a Discord voice channel by id, the provided entity should be stored for long periods of time.
* @param id the id for the voice channel

View File

@ -0,0 +1,7 @@
package com.discordsrv.api.discord.entity.channel;
import com.discordsrv.api.discord.entity.JDAEntity;
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
public interface DiscordForumChannel extends DiscordThreadContainer, JDAEntity<ForumChannel> {
}

View File

@ -27,7 +27,8 @@ import com.discordsrv.common.DiscordSRV;
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.config.main.generic.ThreadConfig;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import com.discordsrv.common.config.manager.MainConfigManager;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
@ -126,9 +127,9 @@ public class ChannelConfigHelper {
String channelName = entry.getKey();
BaseChannelConfig value = entry.getValue();
if (value instanceof IChannelConfig) {
IChannelConfig channelConfig = (IChannelConfig) value;
DestinationConfig destination = ((IChannelConfig) value).destination();
List<Long> channelIds = channelConfig.channelIds();
List<Long> channelIds = destination.channelIds;
if (channelIds != null) {
for (long channelId : channelIds) {
text.computeIfAbsent(channelId, key -> new LinkedHashMap<>())
@ -136,7 +137,7 @@ public class ChannelConfigHelper {
}
}
List<ThreadConfig> threads = channelConfig.threads();
List<ThreadConfig> threads = destination.threads;
if (threads != null) {
for (ThreadConfig threadConfig : threads) {
Pair<Long, String> pair = Pair.of(

View File

@ -18,18 +18,19 @@
package com.discordsrv.common.channel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
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.module.type.AbstractModule;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.IPermissionHolder;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.attribute.IPermissionContainer;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.requests.restaction.PermissionOverrideAction;
import java.util.ArrayList;
@ -54,42 +55,57 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
ChannelLockingConfig.Channels channels = shutdownConfig.channels;
ChannelLockingConfig.Threads threads = shutdownConfig.threads;
if (threads.unarchive) {
discordSRV.discordAPI().findOrCreateThreads(config, channelConfig, __ -> {}, new ArrayList<>(), false);
}
channelPermissions(channelConfig, channels, true);
discordSRV.discordAPI()
.findOrCreateDestinations((BaseChannelConfig & IChannelConfig) config, threads.unarchive, true)
.whenComplete((destinations, t) -> {
if (channels.everyone || !channels.roleIds.isEmpty()) {
for (DiscordGuildMessageChannel destination : destinations) {
channelPermissions(channels, destination, true);
}
}
});
});
}
@Override
public void disable() {
doForAllChannels((config, channelConfig) -> {
if (!(config instanceof IChannelConfig)) {
return;
}
ChannelLockingConfig shutdownConfig = config.channelLocking;
ChannelLockingConfig.Channels channels = shutdownConfig.channels;
ChannelLockingConfig.Threads threads = shutdownConfig.threads;
if (threads.archive) {
for (DiscordThreadChannel thread : discordSRV.discordAPI().findThreads(config, channelConfig)) {
thread.asJDA().getManager()
boolean archive = threads.archive;
boolean isChannels = channels.everyone || !channels.roleIds.isEmpty();
if (!threads.archive && !isChannels) {
return;
}
List<DiscordGuildMessageChannel> destinations = discordSRV.discordAPI()
.findDestinations((BaseChannelConfig & IChannelConfig) config, true);
for (DiscordGuildMessageChannel destination : destinations) {
if (archive && destination instanceof DiscordThreadChannel) {
((DiscordThreadChannel) destination).asJDA().getManager()
.setArchived(true)
.reason("DiscordSRV channel locking")
.queue();
}
if (isChannels) {
channelPermissions(channels, destination, false);
}
}
channelPermissions(channelConfig, channels, false);
});
}
private void channelPermissions(
IChannelConfig channelConfig,
ChannelLockingConfig.Channels shutdownConfig,
DiscordGuildMessageChannel channel,
boolean state
) {
JDA jda = discordSRV.jda();
if (jda == null) {
return;
}
boolean everyone = shutdownConfig.everyone;
List<Long> roleIds = shutdownConfig.roleIds;
if (!everyone && roleIds.isEmpty()) {
@ -107,33 +123,31 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
permissions.add(Permission.MESSAGE_ADD_REACTION);
}
for (Long channelId : channelConfig.channelIds()) {
TextChannel channel = jda.getTextChannelById(channelId);
if (channel == null) {
GuildMessageChannel messageChannel = (GuildMessageChannel) channel.getAsJDAMessageChannel();
if (!(messageChannel instanceof IPermissionContainer)) {
return;
}
Guild guild = messageChannel.getGuild();
if (!guild.getSelfMember().hasPermission(messageChannel, Permission.MANAGE_PERMISSIONS)) {
logger().error("Cannot change permissions of " + channel + ": lacking \"Manage Permissions\" permission");
return;
}
if (everyone) {
setPermission((IPermissionContainer) messageChannel, guild.getPublicRole(), permissions, state);
}
for (Long roleId : roleIds) {
Role role = guild.getRoleById(roleId);
if (role == null) {
continue;
}
Guild guild = channel.getGuild();
if (!guild.getSelfMember().hasPermission(channel, Permission.MANAGE_PERMISSIONS)) {
logger().error("Cannot change permissions of " + channel + ": lacking \"Manage Permissions\" permission");
continue;
}
if (everyone) {
setPermission(channel, guild.getPublicRole(), permissions, state);
}
for (Long roleId : roleIds) {
Role role = channel.getGuild().getRoleById(roleId);
if (role == null) {
continue;
}
setPermission(channel, role, permissions, state);
}
setPermission((IPermissionContainer) messageChannel, role, permissions, state);
}
}
private void setPermission(TextChannel channel, IPermissionHolder holder, List<Permission> permissions, boolean state) {
private void setPermission(IPermissionContainer channel, IPermissionHolder holder, List<Permission> permissions, boolean state) {
PermissionOverrideAction action = channel.upsertPermissionOverride(holder);
if ((state ? action.getAllowedPermissions() : action.getDeniedPermissions()).containsAll(permissions)) {
// Already correct

View File

@ -19,9 +19,8 @@
package com.discordsrv.common.command.game.commands.subcommand;
import com.discordsrv.api.component.MinecraftComponent;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
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.message.SendableDiscordMessage;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.command.game.abstraction.GameCommand;
@ -32,7 +31,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.future.util.CompletableFutureUtil;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
@ -98,11 +96,16 @@ public abstract class BroadcastCommand implements GameCommandExecutor, GameComma
@Override
public void execute(ICommandSender sender, GameCommandArguments arguments) {
doExecute(sender, arguments);
}
@SuppressWarnings("unchecked") // Wacky generics
private <CC extends BaseChannelConfig & IChannelConfig> void doExecute(ICommandSender sender, GameCommandArguments arguments) {
String channel = arguments.getString("channel");
String content = arguments.getString("content");
List<DiscordMessageChannel> channels = new ArrayList<>();
List<CompletableFuture<DiscordThreadChannel>> futures = new ArrayList<>();
CompletableFuture<List<DiscordGuildMessageChannel>> future = null;
try {
long id = Long.parseUnsignedLong(channel);
@ -112,24 +115,17 @@ public abstract class BroadcastCommand implements GameCommandExecutor, GameComma
}
} catch (IllegalArgumentException ignored) {
BaseChannelConfig channelConfig = discordSRV.channelConfig().resolve(null, channel);
IChannelConfig config = channelConfig instanceof IChannelConfig ? (IChannelConfig) channelConfig : null;
CC config = channelConfig instanceof IChannelConfig ? (CC) channelConfig : null;
if (config != null) {
for (Long channelId : config.channelIds()) {
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId);
if (textChannel != null) {
channels.add(textChannel);
}
}
discordSRV.discordAPI().findOrCreateThreads(channelConfig, config, channels::add, futures, false);
future = discordSRV.discordAPI().findOrCreateDestinations(config, true, false);
}
}
if (!futures.isEmpty()) {
CompletableFutureUtil.combine(futures).whenComplete((v, t) -> execute(sender, content, channel, channels));
if (future != null) {
future.whenComplete((messageChannels, t) -> doBroadcast(sender, content, channel, messageChannels));
} else {
execute(sender, content, channel, channels);
doBroadcast(sender, content, channel, channels);
}
}
@ -145,7 +141,7 @@ public abstract class BroadcastCommand implements GameCommandExecutor, GameComma
.collect(Collectors.toList());
}
private void execute(ICommandSender sender, String content, String channel, List<DiscordMessageChannel> channels) {
private void doBroadcast(ICommandSender sender, String content, String channel, List<? extends DiscordMessageChannel> channels) {
if (channels.isEmpty()) {
sender.sendMessage(
Component.text()

View File

@ -21,6 +21,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable

View File

@ -21,6 +21,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable

View File

@ -19,7 +19,7 @@
package com.discordsrv.common.config.main.channels;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.generic.DiscordIgnoresConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;

View File

@ -22,6 +22,7 @@ import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.events.message.receive.game.JoinMessageReceiveEvent;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;

View File

@ -21,6 +21,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable

View File

@ -21,6 +21,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.DefaultOnly;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;

View File

@ -18,7 +18,7 @@
package com.discordsrv.common.config.main.channels;
import com.discordsrv.common.config.main.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.generic.DiscordIgnoresConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;

View File

@ -21,6 +21,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.DiscordMessageEmbed;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable

View File

@ -20,6 +20,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable

View File

@ -20,6 +20,7 @@ package com.discordsrv.common.config.main.channels;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.common.config.annotation.Untranslated;
import com.discordsrv.common.config.main.generic.IMessageConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable

View File

@ -18,12 +18,10 @@
package com.discordsrv.common.config.main.channels.base;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import org.spongepowered.configurate.objectmapping.meta.Setting;
import java.util.List;
@ConfigSerializable
public class ChannelConfig extends BaseChannelConfig implements IChannelConfig {
@ -31,22 +29,11 @@ public class ChannelConfig extends BaseChannelConfig implements IChannelConfig {
initialize();
}
@Setting(CHANNEL_IDS_OPTION_NAME)
@Comment(CHANNEL_IDS_COMMENT)
public List<Long> channelIds = CHANNEL_IDS_VALUE;
@Setting(nodeFromParent = true)
public DestinationConfig destination = new DestinationConfig();
@Override
public List<Long> channelIds() {
return channelIds;
public DestinationConfig destination() {
return destination;
}
@Setting(THREADS_OPTION_NAME)
@Comment(THREADS_COMMENT)
public List<ThreadConfig> threads = THREADS_VALUE;
@Override
public List<ThreadConfig> threads() {
return threads;
}
}

View File

@ -18,38 +18,22 @@
package com.discordsrv.common.config.main.channels.base;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.spongepowered.configurate.ConfigurationNode;
import org.spongepowered.configurate.objectmapping.ObjectMapper;
import org.spongepowered.configurate.objectmapping.meta.Setting;
import org.spongepowered.configurate.serialize.SerializationException;
import org.spongepowered.configurate.serialize.TypeSerializer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public interface IChannelConfig {
String DEFAULT_KEY = "default";
String CHANNEL_IDS_OPTION_NAME = "channel-ids";
String CHANNEL_IDS_COMMENT = "The channels this in-game channel will forward to in Discord";
List<Long> CHANNEL_IDS_VALUE = new ArrayList<>();
List<Long> channelIds();
String THREADS_OPTION_NAME = "threads";
String THREADS_COMMENT = "The threads that this in-game channel will forward to in Discord (this can be used instead of or with the channel-ids option)";
List<ThreadConfig> THREADS_VALUE = new ArrayList<>(Collections.singletonList(new ThreadConfig()));
List<String> VALUES = Arrays.asList(CHANNEL_IDS_OPTION_NAME, THREADS_OPTION_NAME);
List<ThreadConfig> threads();
DestinationConfig destination();
default void initialize() {
// Clear everything besides channelIds by default (these will be filled back in by Configurate if they are in the config itself)
@ -61,11 +45,6 @@ public interface IChannelConfig {
continue;
}
Setting setting = field.getAnnotation(Setting.class);
if (setting != null && VALUES.contains(setting.value())) {
continue;
}
try {
field.set(this, null);
} catch (IllegalAccessException ignored) {}

View File

@ -19,13 +19,10 @@
package com.discordsrv.common.config.main.channels.base.proxy;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.config.main.channels.base.ThreadConfig;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import org.spongepowered.configurate.objectmapping.meta.Setting;
import java.util.List;
@ConfigSerializable
public class ProxyChannelConfig extends ProxyBaseChannelConfig implements IChannelConfig {
@ -33,21 +30,11 @@ public class ProxyChannelConfig extends ProxyBaseChannelConfig implements IChann
initialize();
}
@Setting(CHANNEL_IDS_OPTION_NAME)
@Comment(CHANNEL_IDS_COMMENT)
public List<Long> channelIds = CHANNEL_IDS_VALUE;
@Setting(nodeFromParent = true)
public DestinationConfig destination = new DestinationConfig();
@Override
public List<Long> channelIds() {
return channelIds;
}
@Setting(THREADS_OPTION_NAME)
@Comment(THREADS_COMMENT)
public List<ThreadConfig> threads = THREADS_VALUE;
@Override
public List<ThreadConfig> threads() {
return threads;
public DestinationConfig destination() {
return destination;
}
}

View File

@ -19,13 +19,10 @@
package com.discordsrv.common.config.main.channels.base.server;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.config.main.channels.base.ThreadConfig;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import org.spongepowered.configurate.objectmapping.meta.Setting;
import java.util.List;
@ConfigSerializable
public class ServerChannelConfig extends ServerBaseChannelConfig implements IChannelConfig {
@ -33,21 +30,11 @@ public class ServerChannelConfig extends ServerBaseChannelConfig implements ICha
initialize();
}
@Setting(CHANNEL_IDS_OPTION_NAME)
@Comment(CHANNEL_IDS_COMMENT)
public List<Long> channelIds = CHANNEL_IDS_VALUE;
@Setting(nodeFromParent = true)
public DestinationConfig destination = new DestinationConfig();
@Override
public List<Long> channelIds() {
return channelIds;
}
@Setting(THREADS_OPTION_NAME)
@Comment(THREADS_COMMENT)
public List<ThreadConfig> threads = THREADS_VALUE;
@Override
public List<ThreadConfig> threads() {
return threads;
public DestinationConfig destination() {
return destination;
}
}

View File

@ -0,0 +1,22 @@
package com.discordsrv.common.config.main.generic;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
import org.spongepowered.configurate.objectmapping.meta.Comment;
import org.spongepowered.configurate.objectmapping.meta.Setting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ConfigSerializable
public class DestinationConfig {
@Setting("channel-ids")
@Comment("The channels this in-game channel will forward to in Discord")
public List<Long> channelIds = new ArrayList<>();
@Setting("threads")
@Comment("The threads that this in-game channel will forward to in Discord (this can be used instead of or with the channel-ids option)")
public List<ThreadConfig> threads = new ArrayList<>(Collections.singletonList(new ThreadConfig()));
}

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main;
package com.discordsrv.common.config.main.generic;
import com.discordsrv.api.discord.entity.DiscordUser;
import com.discordsrv.api.discord.entity.guild.DiscordGuildMember;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main.channels;
package com.discordsrv.common.config.main.generic;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;

View File

@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main.channels.base;
package com.discordsrv.common.config.main.generic;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;

View File

@ -34,7 +34,8 @@ import com.discordsrv.api.discord.exception.RestErrorResponseException;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
import com.discordsrv.common.config.main.channels.base.ThreadConfig;
import com.discordsrv.common.config.main.generic.ThreadConfig;
import com.discordsrv.common.config.main.generic.DestinationConfig;
import com.discordsrv.common.discord.api.entity.DiscordUserImpl;
import com.discordsrv.common.discord.api.entity.channel.*;
import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl;
@ -58,6 +59,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.*;
import java.util.function.BiConsumer;
@ -87,85 +89,88 @@ public class DiscordAPIImpl implements DiscordAPI {
return cachedClients;
}
/**
* Finds active threads based for the provided {@link IChannelConfig}.
* @param config the config that specified the threads
* @return the list of active threads
*/
public List<DiscordThreadChannel> findThreads(BaseChannelConfig config, IChannelConfig channelConfig) {
List<DiscordThreadChannel> channels = new ArrayList<>();
findOrCreateThreads(config, channelConfig, channels::add, null, false);
return channels;
}
/**
* Finds or potentially unarchives or creates threads based on the provided {@link IChannelConfig}.
* @param config the config
* @param channelConsumer the consumer that will take the channels as they are gathered
* @param futures a possibly null list of {@link CompletableFuture} for tasks that need to be completed to get all threads
*/
public void findOrCreateThreads(
BaseChannelConfig config,
IChannelConfig channelConfig,
Consumer<DiscordThreadChannel> channelConsumer,
@Nullable List<CompletableFuture<DiscordThreadChannel>> futures,
public <T extends BaseChannelConfig & IChannelConfig> List<DiscordGuildMessageChannel> findDestinations(
T config,
boolean log
) {
List<ThreadConfig> threads = channelConfig.threads();
if (threads == null) {
return;
}
return findOrCreateDestinations(config, false, log).join();
}
for (ThreadConfig threadConfig : threads) {
long channelId = threadConfig.channelId;
DiscordTextChannel channel = getTextChannelById(channelId);
if (channel == null) {
if (channelId > 0 && log) {
discordSRV.logger().error("Unable to find channel with ID " + Long.toUnsignedString(channelId));
}
public <T extends BaseChannelConfig & IChannelConfig> CompletableFuture<List<DiscordGuildMessageChannel>> findOrCreateDestinations(
T config,
boolean create,
boolean log
) {
DestinationConfig destination = config.destination();
List<DiscordGuildMessageChannel> channels = new CopyOnWriteArrayList<>();
for (Long channelId : destination.channelIds) {
DiscordMessageChannel channel = getMessageChannelById(channelId);
if (!(channel instanceof DiscordGuildMessageChannel)) {
continue;
}
channels.add((DiscordGuildMessageChannel) channel);
}
// Check if a thread by the same name is still active
DiscordThreadChannel thread = findThread(threadConfig, channel.getActiveThreads());
if (thread != null) {
ThreadChannel jdaChannel = thread.asJDA();
if (!jdaChannel.isArchived()) {
channelConsumer.accept(thread);
List<CompletableFuture<Void>> threadFutures = new ArrayList<>();
List<ThreadConfig> threadConfigs = destination.threads;
if (threadConfigs != null && !threadConfigs.isEmpty()) {
for (ThreadConfig threadConfig : threadConfigs) {
long channelId = threadConfig.channelId;
DiscordThreadContainer channel = getTextChannelById(channelId);
if (channel == null) {
channel = getForumChannelById(channelId);
}
if (channel == null) {
if (channelId > 0 && log) {
discordSRV.logger().error("Unable to find channel with ID " + Long.toUnsignedString(channelId));
}
continue;
}
}
if (futures == null) {
// Futures not specified, don't try to unarchive or create threads
continue;
}
CompletableFuture<DiscordThreadChannel> future;
if (thread != null) {
// Unarchive the thread
future = new CompletableFuture<>();
unarchiveOrCreateThread(threadConfig, channel, thread, future);
} else {
// Find or create the thread
future = findOrCreateThread(config, threadConfig, channel);
}
futures.add(future.handle((threadChannel, t) -> {
if (t != null) {
ErrorCallbackContext.context(
"Failed to deliver message to thread \""
+ threadConfig.threadName + "\" in channel " + channel
).accept(t);
throw new RuntimeException(); // Just here to fail the future
// Check if a thread by the same name is still active
DiscordThreadChannel thread = findThread(threadConfig, channel.getActiveThreads());
if (thread != null) {
ThreadChannel jdaChannel = thread.asJDA();
if (!jdaChannel.isArchived()) {
channels.add(getThreadChannel(jdaChannel));
continue;
}
}
if (threadChannel != null) {
channelConsumer.accept(threadChannel);
if (!create) {
continue;
}
return threadChannel;
}));
CompletableFuture<DiscordThreadChannel> future;
if (thread != null) {
// Unarchive the thread
future = new CompletableFuture<>();
unarchiveOrCreateThread(threadConfig, channel, thread, future);
} else {
// Find or create the thread
future = findOrCreateThread(config, threadConfig, channel);
}
DiscordThreadContainer container = channel;
threadFutures.add(future.handle((threadChannel, t) -> {
if (t != null) {
ErrorCallbackContext.context(
"Failed to deliver message to thread \""
+ threadConfig.threadName + "\" in channel " + container
).accept(t);
throw new RuntimeException(); // Just here to fail the future
}
if (threadChannel != null) {
channels.add(threadChannel);
}
return null;
}));
}
}
return CompletableFutureUtil.combine(threadFutures).thenApply(v -> channels);
}
private DiscordThreadChannel findThread(ThreadConfig config, List<DiscordThreadChannel> threads) {
@ -180,17 +185,17 @@ public class DiscordAPIImpl implements DiscordAPI {
private CompletableFuture<DiscordThreadChannel> findOrCreateThread(
BaseChannelConfig config,
ThreadConfig threadConfig,
DiscordTextChannel textChannel
DiscordThreadContainer container
) {
if (!config.channelLocking.threads.unarchive) {
return textChannel.createThread(threadConfig.threadName, threadConfig.privateThread);
return container.createThread(threadConfig.threadName, threadConfig.privateThread);
}
CompletableFuture<DiscordThreadChannel> completableFuture = new CompletableFuture<>();
lookupThreads(
textChannel,
container,
threadConfig.privateThread,
lookup -> findOrCreateThread(threadConfig, textChannel, lookup, completableFuture),
lookup -> findOrCreateThread(threadConfig, container, lookup, completableFuture),
(thread, throwable) -> {
if (throwable != null) {
completableFuture.completeExceptionally(throwable);
@ -203,7 +208,7 @@ public class DiscordAPIImpl implements DiscordAPI {
private void findOrCreateThread(
ThreadConfig config,
DiscordTextChannel textChannel,
DiscordThreadContainer container,
ThreadChannelLookup lookup,
CompletableFuture<DiscordThreadChannel> completableFuture
) {
@ -222,7 +227,7 @@ public class DiscordAPIImpl implements DiscordAPI {
}
DiscordThreadChannel thread = findThread(config, channels);
unarchiveOrCreateThread(config, textChannel, thread, completableFuture);
unarchiveOrCreateThread(config, container, thread, completableFuture);
}).exceptionally(t -> {
if (t instanceof CompletionException) {
completableFuture.completeExceptionally(t.getCause());
@ -235,7 +240,7 @@ public class DiscordAPIImpl implements DiscordAPI {
private void unarchiveOrCreateThread(
ThreadConfig config,
DiscordTextChannel textChannel,
DiscordThreadContainer container,
DiscordThreadChannel thread,
CompletableFuture<DiscordThreadChannel> future
) {
@ -256,7 +261,7 @@ public class DiscordAPIImpl implements DiscordAPI {
return;
}
textChannel.createThread(config.threadName, config.privateThread).whenComplete(((threadChannel, t) -> {
container.createThread(config.threadName, config.privateThread).whenComplete(((threadChannel, t) -> {
if (t != null) {
future.completeExceptionally(t);
} else {
@ -266,7 +271,7 @@ public class DiscordAPIImpl implements DiscordAPI {
}
public void lookupThreads(
DiscordTextChannel textChannel,
DiscordThreadContainer container,
boolean privateThreads,
Consumer<ThreadChannelLookup> lookupConsumer,
BiConsumer<DiscordThreadChannel, Throwable> channelConsumer
@ -275,7 +280,7 @@ public class DiscordAPIImpl implements DiscordAPI {
synchronized (threadLookups) {
for (ThreadChannelLookup threadLookup : threadLookups) {
if (threadLookup.isPrivateThreads() != privateThreads
|| threadLookup.getChannelId() != textChannel.getId()) {
|| threadLookup.getChannelId() != container.getId()) {
continue;
}
@ -284,10 +289,10 @@ public class DiscordAPIImpl implements DiscordAPI {
}
lookup = new ThreadChannelLookup(
textChannel.getId(), privateThreads,
container.getId(), privateThreads,
privateThreads
? textChannel.retrieveArchivedPrivateThreads()
: textChannel.retrieveArchivedPublicThreads()
? container.retrieveArchivedPrivateThreads()
: container.retrieveArchivedPublicThreads()
);
threadLookups.add(lookup);
}
@ -393,6 +398,15 @@ public class DiscordAPIImpl implements DiscordAPI {
return new DiscordTextChannelImpl(discordSRV, jda);
}
@Override
public @Nullable DiscordForumChannel getForumChannelById(long id) {
return mapJDAEntity(jda -> jda.getForumChannelById(id), this::getForumChannel);
}
public DiscordForumChannelImpl getForumChannel(ForumChannel jda) {
return new DiscordForumChannelImpl(discordSRV, jda);
}
@Override
public @Nullable DiscordVoiceChannel getVoiceChannelById(long id) {
return mapJDAEntity(jda -> jda.getVoiceChannelById(id), this::getVoiceChannel);
@ -537,10 +551,19 @@ public class DiscordAPIImpl implements DiscordAPI {
private boolean isConfiguredChannel(Long channelId) {
for (BaseChannelConfig config : discordSRV.config().channels.values()) {
if (config instanceof IChannelConfig
&& ((IChannelConfig) config).channelIds().contains(channelId)) {
DestinationConfig destination = config instanceof IChannelConfig ? ((IChannelConfig) config).destination() : null;
if (destination == null) {
continue;
}
if (destination.channelIds.contains(channelId)) {
return true;
}
for (ThreadConfig thread : destination.threads) {
if (Objects.equals(thread.channelId, channelId)) {
return true;
}
}
}
return false;
}

View File

@ -0,0 +1,113 @@
package com.discordsrv.common.discord.api.entity.channel;
import com.discordsrv.api.discord.entity.channel.DiscordForumChannel;
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
import com.discordsrv.common.DiscordSRV;
import net.dv8tion.jda.api.entities.channel.attribute.IThreadContainer;
import net.dv8tion.jda.api.entities.channel.concrete.ForumChannel;
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
import net.dv8tion.jda.api.requests.restaction.ThreadChannelAction;
import net.dv8tion.jda.api.requests.restaction.pagination.ThreadChannelPaginationAction;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
import java.util.stream.Collectors;
public class DiscordForumChannelImpl implements DiscordForumChannel {
private final DiscordSRV discordSRV;
private final ForumChannel channel;
private final DiscordGuild guild;
public DiscordForumChannelImpl(DiscordSRV discordSRV, ForumChannel channel) {
this.discordSRV = discordSRV;
this.channel = channel;
this.guild = discordSRV.discordAPI().getGuild(channel.getGuild());;
}
@Override
public ForumChannel asJDA() {
return channel;
}
@Override
public long getId() {
return channel.getIdLong();
}
@Override
public @NotNull String getName() {
return channel.getName();
}
@Override
public @NotNull DiscordGuild getGuild() {
return guild;
}
@Override
public @NotNull String getJumpUrl() {
return channel.getJumpUrl();
}
@Override
public @NotNull List<DiscordThreadChannel> getActiveThreads() {
List<ThreadChannel> threads = channel.getThreadChannels();
List<DiscordThreadChannel> threadChannels = new ArrayList<>(threads.size());
for (ThreadChannel thread : threads) {
threadChannels.add(discordSRV.discordAPI().getThreadChannel(thread));
}
return threadChannels;
}
@Override
public CompletableFuture<List<DiscordThreadChannel>> retrieveArchivedPrivateThreads() {
return threads(IThreadContainer::retrieveArchivedPrivateThreadChannels);
}
@Override
public CompletableFuture<List<DiscordThreadChannel>> retrieveArchivedJoinedPrivateThreads() {
return threads(IThreadContainer::retrieveArchivedPrivateJoinedThreadChannels);
}
@Override
public CompletableFuture<List<DiscordThreadChannel>> retrieveArchivedPublicThreads() {
return threads(IThreadContainer::retrieveArchivedPublicThreadChannels);
}
@SuppressWarnings("CodeBlock2Expr")
private CompletableFuture<List<DiscordThreadChannel>> threads(
Function<IThreadContainer, ThreadChannelPaginationAction> action) {
return discordSRV.discordAPI().mapExceptions(() -> {
return action.apply(channel)
.submit()
.thenApply(channels -> channels.stream()
.map(channel -> discordSRV.discordAPI().getThreadChannel(channel))
.collect(Collectors.toList())
);
});
}
@Override
public CompletableFuture<DiscordThreadChannel> createThread(String name, boolean privateThread) {
return thread(channel -> channel.createThreadChannel(name, privateThread));
}
@Override
public CompletableFuture<DiscordThreadChannel> createThread(String name, long messageId) {
return thread(channel -> channel.createThreadChannel(name, messageId));
}
@SuppressWarnings("CodeBlock2Expr")
private CompletableFuture<DiscordThreadChannel> thread(Function<ForumChannel, ThreadChannelAction> action) {
return discordSRV.discordAPI().mapExceptions(() -> {
return action.apply(channel)
.submit()
.thenApply(channel -> discordSRV.discordAPI().getThreadChannel(channel));
});
}
}

View File

@ -33,7 +33,7 @@ import com.discordsrv.api.placeholder.util.Placeholders;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.component.renderer.DiscordSRVMinecraftRenderer;
import com.discordsrv.common.component.util.ComponentUtil;
import com.discordsrv.common.config.main.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.generic.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
import com.discordsrv.common.logging.NamedLogger;

View File

@ -34,7 +34,7 @@ import com.discordsrv.api.discord.events.message.DiscordMessageUpdateEvent;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.message.receive.discord.DiscordChatMessageProcessingEvent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.DiscordIgnoresConfig;
import com.discordsrv.common.config.main.generic.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;
@ -83,8 +83,9 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
return Arrays.asList(DiscordGatewayIntent.GUILD_MESSAGES, DiscordGatewayIntent.MESSAGE_CONTENT);
}
@SuppressWarnings("unchecked") // Wacky generics
@Subscribe
public void onDiscordChatMessageProcessing(DiscordChatMessageProcessingEvent event) {
public <CC extends BaseChannelConfig & IChannelConfig> void onDiscordChatMessageProcessing(DiscordChatMessageProcessingEvent event) {
if (checkCancellation(event)) {
return;
}
@ -95,16 +96,14 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
}
ReceivedDiscordMessage message = event.getDiscordMessage();
DiscordMessageChannel channel = event.getChannel();
List<Pair<DiscordGuildMessageChannel, MirroringConfig>> mirrorChannels = new ArrayList<>();
List<CompletableFuture<DiscordThreadChannel>> futures = new ArrayList<>();
List<CompletableFuture<List<Pair<DiscordGuildMessageChannel, MirroringConfig>>>> futures = new ArrayList<>();
Map<ReceivedDiscordMessage.Attachment, byte[]> attachments = new LinkedHashMap<>();
DiscordMessageEmbed.Builder attachmentEmbed = DiscordMessageEmbed.builder().setDescription("Attachments");
for (Map.Entry<GameChannel, BaseChannelConfig> entry : channels.entrySet()) {
BaseChannelConfig channelConfig = entry.getValue();
MirroringConfig config = channelConfig.mirroring;
BaseChannelConfig baseChannelConfig = entry.getValue();
MirroringConfig config = baseChannelConfig.mirroring;
if (!config.enabled) {
continue;
}
@ -114,8 +113,8 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
continue;
}
IChannelConfig iChannelConfig = channelConfig instanceof IChannelConfig ? (IChannelConfig) channelConfig : null;
if (iChannelConfig == null) {
CC channelConfig = baseChannelConfig instanceof IChannelConfig ? (CC) baseChannelConfig : null;
if (channelConfig == null) {
continue;
}
@ -156,57 +155,54 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
}
}
List<Long> channelIds = iChannelConfig.channelIds();
if (channelIds != null) {
for (Long channelId : channelIds) {
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId);
if (textChannel != null && textChannel.getId() != channel.getId()) {
mirrorChannels.add(Pair.of(textChannel, config));
}
}
}
discordSRV.discordAPI().findOrCreateThreads(channelConfig, iChannelConfig, threadChannel -> {
if (threadChannel.getId() != channel.getId()) {
mirrorChannels.add(Pair.of(threadChannel, config));
}
}, futures, false);
futures.add(
discordSRV.discordAPI().findOrCreateDestinations(channelConfig, true, true)
.thenApply(messageChannels -> {
List<Pair<DiscordGuildMessageChannel, MirroringConfig>> pairs = new ArrayList<>();
for (DiscordGuildMessageChannel messageChannel : messageChannels) {
pairs.add(Pair.of(messageChannel, config));
}
return pairs;
})
);
}
CompletableFutureUtil.combine(futures).whenComplete((v, t) -> {
CompletableFutureUtil.combine(futures).whenComplete((lists, t) -> {
List<CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>>> messageFutures = new ArrayList<>();
for (Pair<DiscordGuildMessageChannel, MirroringConfig> pair : mirrorChannels) {
DiscordGuildMessageChannel mirrorChannel = pair.getKey();
MirroringConfig config = pair.getValue();
MirroringConfig.AttachmentConfig attachmentConfig = config.attachments;
for (List<Pair<DiscordGuildMessageChannel, MirroringConfig>> pairs : lists) {
for (Pair<DiscordGuildMessageChannel, MirroringConfig> pair : pairs) {
DiscordGuildMessageChannel mirrorChannel = pair.getKey();
MirroringConfig config = pair.getValue();
MirroringConfig.AttachmentConfig attachmentConfig = config.attachments;
SendableDiscordMessage.Builder messageBuilder = convert(event.getDiscordMessage(), mirrorChannel, config);
if (!attachmentEmbed.getFields().isEmpty() && attachmentConfig.embedAttachments) {
messageBuilder.addEmbed(attachmentEmbed.build());
}
SendableDiscordMessage.Builder messageBuilder = convert(event.getDiscordMessage(), mirrorChannel, config);
if (!attachmentEmbed.getFields().isEmpty() && attachmentConfig.embedAttachments) {
messageBuilder.addEmbed(attachmentEmbed.build());
}
int maxSize = attachmentConfig.maximumSizeKb;
Map<String, InputStream> currentAttachments;
if (!attachments.isEmpty() && maxSize > 0) {
currentAttachments = new LinkedHashMap<>();
attachments.forEach((attachment, bytes) -> {
if (bytes != null && attachment.sizeBytes() <= maxSize) {
currentAttachments.put(attachment.fileName(), new ByteArrayInputStream(bytes));
}
int maxSize = attachmentConfig.maximumSizeKb;
Map<String, InputStream> currentAttachments;
if (!attachments.isEmpty() && maxSize > 0) {
currentAttachments = new LinkedHashMap<>();
attachments.forEach((attachment, bytes) -> {
if (bytes != null && attachment.sizeBytes() <= maxSize) {
currentAttachments.put(attachment.fileName(), new ByteArrayInputStream(bytes));
}
});
} else {
currentAttachments = Collections.emptyMap();
}
CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>> future =
mirrorChannel.sendMessage(messageBuilder.build(), currentAttachments)
.thenApply(msg -> Pair.of(msg, config));
messageFutures.add(future);
future.exceptionally(t2 -> {
discordSRV.logger().error("Failed to mirror message to " + mirrorChannel, t2);
return null;
});
} else {
currentAttachments = Collections.emptyMap();
}
CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>> future =
mirrorChannel.sendMessage(messageBuilder.build(), currentAttachments)
.thenApply(msg -> Pair.of(msg, config));
messageFutures.add(future);
future.exceptionally(t2 -> {
discordSRV.logger().error("Failed to mirror message to " + mirrorChannel, t2);
return null;
});
}
CompletableFutureUtil.combine(messageFutures).whenComplete((messages, t2) -> {

View File

@ -20,18 +20,16 @@ package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext;
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.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageReceiveEvent;
import com.discordsrv.api.player.DiscordSRVPlayer;
import com.discordsrv.common.DiscordSRV;
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.config.main.generic.IMessageConfig;
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageClusterImpl;
import com.discordsrv.common.future.util.CompletableFutureUtil;
import com.discordsrv.common.logging.NamedLogger;
@ -44,7 +42,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class AbstractGameMessageModule<T extends IMessageConfig, E extends AbstractGameMessageReceiveEvent> extends AbstractModule<DiscordSRV> {
@ -92,7 +89,8 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
return forwardToChannel(event, srvPlayer, channelConfig);
}
private CompletableFuture<Void> forwardToChannel(
@SuppressWarnings("unchecked") // Wacky generis
private <CC extends BaseChannelConfig & IChannelConfig> CompletableFuture<Void> forwardToChannel(
@Nullable E event,
@Nullable IPlayer player,
@NotNull BaseChannelConfig config
@ -102,37 +100,18 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
return null;
}
IChannelConfig channelConfig = config instanceof IChannelConfig ? (IChannelConfig) config : null;
CC channelConfig = config instanceof IChannelConfig ? (CC) config : null;
if (channelConfig == null) {
return null;
}
List<DiscordMessageChannel> messageChannels = new CopyOnWriteArrayList<>();
List<CompletableFuture<DiscordThreadChannel>> futures = new ArrayList<>();
List<Long> channelIds = channelConfig.channelIds();
if (channelIds != null) {
for (Long channelId : channelConfig.channelIds()) {
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId);
if (textChannel != null) {
messageChannels.add(textChannel);
} else if (channelId > 0) {
discordSRV.logger().error("Unable to find channel with ID "
+ Long.toUnsignedString(channelId)
+ ", unable to forward message to Discord");
}
}
}
discordSRV.discordAPI().findOrCreateThreads(config, channelConfig, messageChannels::add, futures, true);
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).thenCompose((v) -> {
return discordSRV.discordAPI().findOrCreateDestinations(channelConfig, true, true).thenCompose(messageChannels -> {
SendableDiscordMessage.Builder format = moduleConfig.format();
if (format == null) {
return CompletableFuture.completedFuture(null);
}
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> messageFutures;
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> messageFutures;
messageFutures = sendMessageToChannels(
moduleConfig, player, format, messageChannels, event,
// Context
@ -142,7 +121,7 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
return CompletableFuture.allOf(messageFutures.keySet().toArray(new CompletableFuture[0]))
.whenComplete((vo, t2) -> {
Set<ReceivedDiscordMessage> messages = new LinkedHashSet<>();
for (Map.Entry<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> entry : messageFutures.entrySet()) {
for (Map.Entry<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> entry : messageFutures.entrySet()) {
CompletableFuture<ReceivedDiscordMessage> future = entry.getKey();
if (future.isCompletedExceptionally()) {
future.exceptionally(t -> {
@ -184,11 +163,11 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
return discordSRV.componentFactory().discordSerializer().serialize(component);
}
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
T config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
List<DiscordGuildMessageChannel> channels,
E event,
Object... context
) {
@ -201,8 +180,8 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
SendableDiscordMessage discordMessage = formatter
.build();
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> futures = new LinkedHashMap<>();
for (DiscordMessageChannel channel : channels) {
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> futures = new LinkedHashMap<>();
for (DiscordGuildMessageChannel channel : channels) {
futures.put(channel.sendMessage(discordMessage), channel);
}

View File

@ -28,7 +28,7 @@ import com.discordsrv.api.event.events.message.receive.game.JoinMessageReceiveEv
import com.discordsrv.api.placeholder.FormattedText;
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.generic.IMessageConfig;
import com.discordsrv.common.config.main.channels.base.BaseChannelConfig;
public class JoinMessageModule extends AbstractGameMessageModule<IMessageConfig, JoinMessageReceiveEvent> {

View File

@ -18,7 +18,7 @@
package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
@ -53,11 +53,11 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
@Override
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
StartMessageConfig config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
List<DiscordGuildMessageChannel> channels,
AbstractGameMessageReceiveEvent event,
Object... context
) {

View File

@ -18,7 +18,7 @@
package com.discordsrv.common.messageforwarding.game;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
@ -56,11 +56,11 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
@Override
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
StopMessageConfig config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
List<DiscordGuildMessageChannel> channels,
AbstractGameMessageReceiveEvent event,
Object... context
) {

View File

@ -19,8 +19,7 @@
package com.discordsrv.common.messageforwarding.game.minecrafttodiscord;
import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildChannel;
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
import com.discordsrv.api.discord.entity.guild.DiscordGuild;
import com.discordsrv.api.discord.entity.message.AllowedMention;
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
@ -88,36 +87,31 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
}
@Override
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> sendMessageToChannels(
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
MinecraftToDiscordChatConfig config,
IPlayer player,
SendableDiscordMessage.Builder format,
List<DiscordMessageChannel> channels,
List<DiscordGuildMessageChannel> channels,
GameChatMessageReceiveEvent event,
Object... context
) {
Map<DiscordGuild, Set<DiscordMessageChannel>> channelMap = new LinkedHashMap<>();
for (DiscordMessageChannel channel : channels) {
DiscordGuild guild;
if (channel instanceof DiscordGuildChannel) {
guild = ((DiscordGuildChannel) channel).getGuild();
} else {
continue;
}
Map<DiscordGuild, Set<DiscordGuildMessageChannel>> channelMap = new LinkedHashMap<>();
for (DiscordGuildMessageChannel channel : channels) {
DiscordGuild guild = channel.getGuild();
channelMap.computeIfAbsent(guild, key -> new LinkedHashSet<>())
.add(channel);
}
Component message = ComponentUtil.fromAPI(event.getMessage());
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordMessageChannel> futures = new LinkedHashMap<>();
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> futures = new LinkedHashMap<>();
// Format messages per-Guild
for (Map.Entry<DiscordGuild, Set<DiscordMessageChannel>> entry : channelMap.entrySet()) {
for (Map.Entry<DiscordGuild, Set<DiscordGuildMessageChannel>> entry : channelMap.entrySet()) {
Guild guild = entry.getKey().asJDA();
CompletableFuture<SendableDiscordMessage> messageFuture = getMessageForGuild(config, format, guild, message, player, context);
for (DiscordMessageChannel channel : entry.getValue()) {
for (DiscordGuildMessageChannel channel : entry.getValue()) {
futures.put(messageFuture.thenCompose(channel::sendMessage), channel);
}
}