mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2025-02-27 03:32:33 +01:00
Move around configs, make IChannelConfig better / consolidate channel+thread lookup
This commit is contained in:
parent
95272cfa80
commit
06b1dff50a
@ -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
|
||||
|
@ -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> {
|
||||
}
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
@ -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;
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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) -> {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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
|
||||
) {
|
||||
|
@ -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
|
||||
) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user