mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-11-29 13:05:44 +01:00
Improve game chat handling flow, add DiscordPermissionUtil
This commit is contained in:
parent
c9c101b803
commit
501d638744
@ -33,8 +33,8 @@ import com.discordsrv.common.config.main.generic.ThreadConfig;
|
|||||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.spongepowered.configurate.CommentedConfigurationNode;
|
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||||
import org.spongepowered.configurate.objectmapping.ObjectMapper;
|
import org.spongepowered.configurate.objectmapping.ObjectMapper;
|
||||||
import org.spongepowered.configurate.serialize.SerializationException;
|
import org.spongepowered.configurate.serialize.SerializationException;
|
||||||
@ -65,7 +65,7 @@ public class ChannelConfigHelper {
|
|||||||
.build(new CacheLoader<String, GameChannel>() {
|
.build(new CacheLoader<String, GameChannel>() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable GameChannel load(@NonNull String channelName) {
|
public @Nullable GameChannel load(@NotNull String channelName) {
|
||||||
GameChannelLookupEvent event = new GameChannelLookupEvent(null, channelName);
|
GameChannelLookupEvent event = new GameChannelLookupEvent(null, channelName);
|
||||||
discordSRV.eventBus().publish(event);
|
discordSRV.eventBus().publish(event);
|
||||||
if (!event.isProcessed()) {
|
if (!event.isProcessed()) {
|
||||||
@ -199,11 +199,13 @@ public class ChannelConfigHelper {
|
|||||||
return channelConfigs;
|
return channelConfigs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseChannelConfig get(GameChannel gameChannel) {
|
@Nullable
|
||||||
|
public BaseChannelConfig get(@NotNull GameChannel gameChannel) {
|
||||||
return resolve(gameChannel.getOwnerName(), gameChannel.getChannelName());
|
return resolve(gameChannel.getOwnerName(), gameChannel.getChannelName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseChannelConfig resolve(String ownerName, String channelName) {
|
@Nullable
|
||||||
|
public BaseChannelConfig resolve(@Nullable String ownerName, @NotNull String channelName) {
|
||||||
if (ownerName != null) {
|
if (ownerName != null) {
|
||||||
ownerName = ownerName.toLowerCase(Locale.ROOT);
|
ownerName = ownerName.toLowerCase(Locale.ROOT);
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@
|
|||||||
package com.discordsrv.common.channel;
|
package com.discordsrv.common.channel;
|
||||||
|
|
||||||
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
|
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
|
||||||
import com.discordsrv.api.discord.entity.channel.DiscordMessageChannel;
|
|
||||||
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
|
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.config.main.channels.ChannelLockingConfig;
|
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.BaseChannelConfig;
|
||||||
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
|
import com.discordsrv.common.config.main.channels.base.IChannelConfig;
|
||||||
|
import com.discordsrv.common.discord.util.DiscordPermissionUtil;
|
||||||
import com.discordsrv.common.module.type.AbstractModule;
|
import com.discordsrv.common.module.type.AbstractModule;
|
||||||
import net.dv8tion.jda.api.Permission;
|
import net.dv8tion.jda.api.Permission;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
@ -55,7 +55,6 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
|
|||||||
doForAllChannels((config, channelConfig) -> {
|
doForAllChannels((config, channelConfig) -> {
|
||||||
ChannelLockingConfig shutdownConfig = config.channelLocking;
|
ChannelLockingConfig shutdownConfig = config.channelLocking;
|
||||||
ChannelLockingConfig.Channels channels = shutdownConfig.channels;
|
ChannelLockingConfig.Channels channels = shutdownConfig.channels;
|
||||||
ChannelLockingConfig.Threads threads = shutdownConfig.threads;
|
|
||||||
|
|
||||||
discordSRV.destinations()
|
discordSRV.destinations()
|
||||||
.lookupDestination(((IChannelConfig) config).destination(), false, true)
|
.lookupDestination(((IChannelConfig) config).destination(), false, true)
|
||||||
@ -131,8 +130,9 @@ public class ChannelLockingModule extends AbstractModule<DiscordSRV> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Guild guild = messageChannel.getGuild();
|
Guild guild = messageChannel.getGuild();
|
||||||
if (!guild.getSelfMember().hasPermission(messageChannel, Permission.MANAGE_PERMISSIONS)) {
|
String missingPermissions = DiscordPermissionUtil.missingPermissionsString(messageChannel, Permission.VIEW_CHANNEL, Permission.MANAGE_PERMISSIONS);
|
||||||
logger().error("Cannot change permissions of " + channel + ": lacking \"Manage Permissions\" permission");
|
if (missingPermissions != null) {
|
||||||
|
logger().error("Cannot lock #" + channel.getName() + ": " + missingPermissions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
|||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.config.main.generic.DestinationConfig;
|
import com.discordsrv.common.config.main.generic.DestinationConfig;
|
||||||
import com.discordsrv.common.config.main.generic.ThreadConfig;
|
import com.discordsrv.common.config.main.generic.ThreadConfig;
|
||||||
|
import com.discordsrv.common.discord.util.DiscordPermissionUtil;
|
||||||
import com.discordsrv.common.logging.Logger;
|
import com.discordsrv.common.logging.Logger;
|
||||||
import com.discordsrv.common.logging.NamedLogger;
|
import com.discordsrv.common.logging.NamedLogger;
|
||||||
import net.dv8tion.jda.api.Permission;
|
import net.dv8tion.jda.api.Permission;
|
||||||
@ -113,7 +114,7 @@ public class DestinationLookupHelper {
|
|||||||
return createThread(threadContainer, threadConfig, logFailures);
|
return createThread(threadContainer, threadConfig, logFailures);
|
||||||
}).exceptionally(t -> {
|
}).exceptionally(t -> {
|
||||||
if (logFailures) {
|
if (logFailures) {
|
||||||
logger.error("Failed to lookup threads in channel ID " + Long.toUnsignedString(channelId), t);
|
logger.error("Failed to lookup threads in channel #" + threadContainer.getName(), t);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -164,11 +165,15 @@ public class DestinationLookupHelper {
|
|||||||
boolean privateThread = !forum && threadConfig.privateThread;
|
boolean privateThread = !forum && threadConfig.privateThread;
|
||||||
|
|
||||||
IThreadContainer container = threadContainer.getAsJDAThreadContainer();
|
IThreadContainer container = threadContainer.getAsJDAThreadContainer();
|
||||||
if (!container.getGuild().getSelfMember().hasPermission(container, privateThread ? Permission.CREATE_PRIVATE_THREADS : Permission.CREATE_PUBLIC_THREADS)) {
|
String missingPermissions = DiscordPermissionUtil.missingPermissionsString(
|
||||||
|
container,
|
||||||
|
Permission.VIEW_CHANNEL,
|
||||||
|
privateThread ? Permission.CREATE_PRIVATE_THREADS : Permission.CREATE_PUBLIC_THREADS
|
||||||
|
);
|
||||||
|
if (missingPermissions != null) {
|
||||||
if (logFailures) {
|
if (logFailures) {
|
||||||
logger.error("Failed to create thread \"" + threadConfig.threadName + "\" "
|
logger.error("Failed to create thread \"" + threadConfig.threadName + "\" "
|
||||||
+ "in channel ID " + Long.toUnsignedString(threadContainer.getId())
|
+ "in channel #" + threadContainer.getName() + ": " + missingPermissions);
|
||||||
+ ": lacking \"Create " + (privateThread ? "Private" : "Public") + " Threads\" permission");
|
|
||||||
}
|
}
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
@ -185,7 +190,7 @@ public class DestinationLookupHelper {
|
|||||||
return future.exceptionally(t -> {
|
return future.exceptionally(t -> {
|
||||||
if (logFailures) {
|
if (logFailures) {
|
||||||
logger.error("Failed to create thread \"" + threadConfig.threadName + "\" "
|
logger.error("Failed to create thread \"" + threadConfig.threadName + "\" "
|
||||||
+ "in channel ID " + Long.toUnsignedString(threadContainer.getId()), t);
|
+ "in channel #" + threadContainer.getName(), t);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
@ -193,11 +198,17 @@ public class DestinationLookupHelper {
|
|||||||
|
|
||||||
private CompletableFuture<DiscordThreadChannel> unarchiveThread(DiscordThreadChannel channel, boolean logFailures) {
|
private CompletableFuture<DiscordThreadChannel> unarchiveThread(DiscordThreadChannel channel, boolean logFailures) {
|
||||||
ThreadChannel jdaChannel = channel.asJDA();
|
ThreadChannel jdaChannel = channel.asJDA();
|
||||||
if ((jdaChannel.isLocked() || !jdaChannel.isOwner()) && !jdaChannel.getGuild().getSelfMember().hasPermission(jdaChannel, Permission.MANAGE_THREADS)) {
|
|
||||||
|
EnumSet<Permission> requiredPermissions = EnumSet.of(Permission.VIEW_CHANNEL);
|
||||||
|
if (jdaChannel.isLocked() || !jdaChannel.isOwner()) {
|
||||||
|
requiredPermissions.add(Permission.MANAGE_THREADS);
|
||||||
|
}
|
||||||
|
|
||||||
|
String missingPermissions = DiscordPermissionUtil.missingPermissionsString(jdaChannel, requiredPermissions);
|
||||||
|
if (missingPermissions != null) {
|
||||||
if (logFailures) {
|
if (logFailures) {
|
||||||
logger.error("Cannot unarchive thread \"" + channel.getName() + "\" "
|
logger.error("Cannot unarchive thread \"" + channel.getName() + "\" "
|
||||||
+ "in channel ID " + Long.toUnsignedString(channel.getParentChannel().getId())
|
+ "in channel #" + channel.getParentChannel().getName() + ": " + missingPermissions);
|
||||||
+ ": lacking \"Manage Threads\" permission");
|
|
||||||
}
|
}
|
||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
@ -210,7 +221,7 @@ public class DestinationLookupHelper {
|
|||||||
).thenApply(v -> channel).exceptionally(t -> {
|
).thenApply(v -> channel).exceptionally(t -> {
|
||||||
if (logFailures) {
|
if (logFailures) {
|
||||||
logger.error("Failed to unarchive thread \"" + channel.getName() + "\" "
|
logger.error("Failed to unarchive thread \"" + channel.getName() + "\" "
|
||||||
+ "in channel ID " + Long.toUnsignedString(channel.getParentChannel().getId()), t);
|
+ "in channel #" + channel.getParentChannel().getName(), t);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
@ -135,4 +135,9 @@ public class DiscordForumChannelImpl implements DiscordForumChannel {
|
|||||||
public DiscordChannelType getType() {
|
public DiscordChannelType getType() {
|
||||||
return DiscordChannelType.FORUM;
|
return DiscordChannelType.FORUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Forum:" + getName() + "(" + Long.toUnsignedString(getId()) + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package com.discordsrv.common.discord.util;
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public final class DiscordPermissionUtil {
|
||||||
|
|
||||||
|
private DiscordPermissionUtil() {}
|
||||||
|
|
||||||
|
public static String missingPermissionsString(GuildChannel channel, Permission... permissions) {
|
||||||
|
return missingPermissionsString(channel, Arrays.asList(permissions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String missingPermissionsString(GuildChannel channel, Collection<Permission> permissions) {
|
||||||
|
if (channel instanceof ThreadChannel) {
|
||||||
|
channel = ((ThreadChannel) channel).getParentChannel();
|
||||||
|
}
|
||||||
|
EnumSet<Permission> missingPermissions = checkMissingPermissions(channel, permissions);
|
||||||
|
return format(missingPermissions, "#" + channel.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumSet<Permission> checkMissingPermissions(GuildChannel channel, Collection<Permission> permissions) {
|
||||||
|
if (channel instanceof ThreadChannel) {
|
||||||
|
channel = ((ThreadChannel) channel).getParentChannel();
|
||||||
|
}
|
||||||
|
EnumSet<Permission> missingPermissions = EnumSet.noneOf(Permission.class);
|
||||||
|
for (Permission permission : permissions) {
|
||||||
|
if (!channel.getGuild().getSelfMember().hasPermission(channel, permission)) {
|
||||||
|
missingPermissions.add(permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missingPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String missingPermissionsString(Guild guild, Permission... permissions) {
|
||||||
|
return missingPermissionsString(guild, Arrays.asList(permissions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String missingPermissionsString(Guild guild, Collection<Permission> permissions) {
|
||||||
|
EnumSet<Permission> missingPermissions = checkMissingPermissions(guild, permissions);
|
||||||
|
return format(missingPermissions, guild.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnumSet<Permission> checkMissingPermissions(Guild guild, Collection<Permission> permissions) {
|
||||||
|
EnumSet<Permission> missingPermissions = EnumSet.noneOf(Permission.class);
|
||||||
|
for (Permission permission : permissions) {
|
||||||
|
if (!guild.getSelfMember().hasPermission(permission)) {
|
||||||
|
missingPermissions.add(permission);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missingPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String format(EnumSet<Permission> permissions, String where) {
|
||||||
|
if (permissions.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "the bot is lacking permissions in " + where + ": "
|
||||||
|
+ permissions.stream().map(Permission::getName).collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
}
|
@ -20,7 +20,9 @@ package com.discordsrv.common.messageforwarding.game;
|
|||||||
|
|
||||||
import com.discordsrv.api.channel.GameChannel;
|
import com.discordsrv.api.channel.GameChannel;
|
||||||
import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext;
|
import com.discordsrv.api.discord.connection.jda.errorresponse.ErrorCallbackContext;
|
||||||
|
import com.discordsrv.api.discord.entity.channel.DiscordGuildChannel;
|
||||||
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
|
import com.discordsrv.api.discord.entity.channel.DiscordGuildMessageChannel;
|
||||||
|
import com.discordsrv.api.discord.entity.channel.DiscordThreadChannel;
|
||||||
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
|
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessage;
|
||||||
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
|
import com.discordsrv.api.discord.entity.message.ReceivedDiscordMessageCluster;
|
||||||
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||||
@ -31,18 +33,36 @@ 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.IChannelConfig;
|
||||||
import com.discordsrv.common.config.main.generic.IMessageConfig;
|
import com.discordsrv.common.config.main.generic.IMessageConfig;
|
||||||
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageClusterImpl;
|
import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageClusterImpl;
|
||||||
|
import com.discordsrv.common.discord.util.DiscordPermissionUtil;
|
||||||
import com.discordsrv.common.future.util.CompletableFutureUtil;
|
import com.discordsrv.common.future.util.CompletableFutureUtil;
|
||||||
import com.discordsrv.common.logging.NamedLogger;
|
import com.discordsrv.common.logging.NamedLogger;
|
||||||
import com.discordsrv.common.module.type.AbstractModule;
|
import com.discordsrv.common.module.type.AbstractModule;
|
||||||
import com.discordsrv.common.player.IPlayer;
|
import com.discordsrv.common.player.IPlayer;
|
||||||
import com.discordsrv.common.testing.TestHelper;
|
import com.discordsrv.common.testing.TestHelper;
|
||||||
|
import net.dv8tion.jda.api.Permission;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.concrete.ThreadChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstracted flow to send in-game messages to a given destination and publish the results to the event bus.
|
||||||
|
* <p>
|
||||||
|
* Order of operations:
|
||||||
|
* - Event (E generic) is received, implementation calls {@link #process(AbstractGameMessageReceiveEvent, DiscordSRVPlayer, GameChannel)}
|
||||||
|
* - {@link IPlayer} and {@link BaseChannelConfig} (uses {@link #mapConfig(AbstractGameMessageReceiveEvent, BaseChannelConfig)} are resolved, then {@link #forwardToChannel(AbstractGameMessageReceiveEvent, IPlayer, BaseChannelConfig)} is called
|
||||||
|
* - Destinations are looked up and {@link #sendMessageToChannels} gets called
|
||||||
|
* - {@link #setPlaceholders(IMessageConfig, AbstractGameMessageReceiveEvent, SendableDiscordMessage.Formatter)} is called to set any additional placeholders
|
||||||
|
* - {@link #sendMessageToChannel(DiscordGuildMessageChannel, SendableDiscordMessage)} is called (once per channel) to send messages to individual channels
|
||||||
|
* - {@link #postClusterToEventBus(ReceivedDiscordMessageCluster)} is called with all messages that were sent (if any messages were sent)
|
||||||
|
*
|
||||||
|
* @param <T> config model
|
||||||
|
* @param <E> the event indicating a message was received from in-game, of type {@link AbstractGameMessageReceiveEvent}
|
||||||
|
*/
|
||||||
public abstract class AbstractGameMessageModule<T extends IMessageConfig, E extends AbstractGameMessageReceiveEvent> extends AbstractModule<DiscordSRV> {
|
public abstract class AbstractGameMessageModule<T extends IMessageConfig, E extends AbstractGameMessageReceiveEvent> extends AbstractModule<DiscordSRV> {
|
||||||
|
|
||||||
public AbstractGameMessageModule(DiscordSRV discordSRV, String loggerName) {
|
public AbstractGameMessageModule(DiscordSRV discordSRV, String loggerName) {
|
||||||
@ -86,6 +106,10 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
BaseChannelConfig channelConfig = discordSRV.channelConfig().get(channel);
|
BaseChannelConfig channelConfig = discordSRV.channelConfig().get(channel);
|
||||||
|
if (channelConfig == null) {
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
return forwardToChannel(event, srvPlayer, channelConfig);
|
return forwardToChannel(event, srvPlayer, channelConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,33 +135,19 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
|
|||||||
return CompletableFuture.completedFuture(null);
|
return CompletableFuture.completedFuture(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> messageFutures;
|
List<CompletableFuture<ReceivedDiscordMessage>> messageFutures = sendMessageToChannels(
|
||||||
messageFutures = sendMessageToChannels(
|
|
||||||
moduleConfig, player, format, messageChannels, event,
|
moduleConfig, player, format, messageChannels, event,
|
||||||
// Context
|
// Context
|
||||||
config, player
|
config, player
|
||||||
);
|
);
|
||||||
|
|
||||||
return CompletableFuture.allOf(messageFutures.keySet().toArray(new CompletableFuture[0]))
|
return CompletableFutureUtil.combine(messageFutures).whenComplete((vo, t2) -> {
|
||||||
.whenComplete((vo, t2) -> {
|
|
||||||
Set<ReceivedDiscordMessage> messages = new LinkedHashSet<>();
|
Set<ReceivedDiscordMessage> messages = new LinkedHashSet<>();
|
||||||
for (Map.Entry<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> entry : messageFutures.entrySet()) {
|
for (CompletableFuture<ReceivedDiscordMessage> future : messageFutures) {
|
||||||
CompletableFuture<ReceivedDiscordMessage> future = entry.getKey();
|
ReceivedDiscordMessage message = future.join();
|
||||||
if (future.isCompletedExceptionally()) {
|
if (message != null) {
|
||||||
future.exceptionally(t -> {
|
messages.add(message);
|
||||||
if (t instanceof CompletionException) {
|
|
||||||
t = t.getCause();
|
|
||||||
}
|
}
|
||||||
ErrorCallbackContext.context("Failed to deliver a message to " + entry.getValue()).accept(t);
|
|
||||||
TestHelper.fail(t);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
// Ignore ones that failed
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// They are all done, so joining will return the result instantly
|
|
||||||
messages.add(future.join());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (messages.isEmpty()) {
|
if (messages.isEmpty()) {
|
||||||
@ -146,23 +156,19 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
|
|||||||
}
|
}
|
||||||
|
|
||||||
postClusterToEventBus(new ReceivedDiscordMessageClusterImpl(messages));
|
postClusterToEventBus(new ReceivedDiscordMessageClusterImpl(messages));
|
||||||
})
|
}).exceptionally(t -> {
|
||||||
.exceptionally(t -> {
|
|
||||||
if (t instanceof CompletionException) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
discordSRV.logger().error("Failed to publish to event bus", t);
|
discordSRV.logger().error("Failed to publish to event bus", t);
|
||||||
TestHelper.fail(t);
|
TestHelper.fail(t);
|
||||||
return null;
|
return null;
|
||||||
});
|
}).thenApply(v -> (Void) null);
|
||||||
}).exceptionally(t -> {
|
}).exceptionally(t -> {
|
||||||
discordSRV.logger().error("Error in sending message", t);
|
discordSRV.logger().error("Error in forwarding message", t);
|
||||||
TestHelper.fail(t);
|
TestHelper.fail(t);
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
|
public List<CompletableFuture<ReceivedDiscordMessage>> sendMessageToChannels(
|
||||||
T config,
|
T config,
|
||||||
IPlayer player,
|
IPlayer player,
|
||||||
SendableDiscordMessage.Builder format,
|
SendableDiscordMessage.Builder format,
|
||||||
@ -179,16 +185,52 @@ public abstract class AbstractGameMessageModule<T extends IMessageConfig, E exte
|
|||||||
SendableDiscordMessage discordMessage = formatter
|
SendableDiscordMessage discordMessage = formatter
|
||||||
.build();
|
.build();
|
||||||
if (discordMessage.isEmpty()) {
|
if (discordMessage.isEmpty()) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> futures = new LinkedHashMap<>();
|
List<CompletableFuture<ReceivedDiscordMessage>> futures = new ArrayList<>();
|
||||||
for (DiscordGuildMessageChannel channel : channels) {
|
for (DiscordGuildMessageChannel channel : channels) {
|
||||||
futures.put(channel.sendMessage(discordMessage), channel);
|
futures.add(sendMessageToChannel(channel, discordMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
return futures;
|
return futures;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
protected final CompletableFuture<ReceivedDiscordMessage> sendMessageToChannel(DiscordGuildMessageChannel channel, SendableDiscordMessage message) {
|
||||||
|
GuildChannel permissionChannel = (GuildMessageChannel) channel.getAsJDAMessageChannel();
|
||||||
|
|
||||||
|
Permission sendPermission;
|
||||||
|
if (message.isWebhookMessage()) {
|
||||||
|
if (permissionChannel instanceof ThreadChannel) {
|
||||||
|
permissionChannel = ((ThreadChannel) permissionChannel).getParentChannel();
|
||||||
|
}
|
||||||
|
sendPermission = Permission.MANAGE_WEBHOOKS;
|
||||||
|
} else {
|
||||||
|
sendPermission = permissionChannel instanceof ThreadChannel
|
||||||
|
? Permission.MESSAGE_SEND_IN_THREADS
|
||||||
|
: Permission.MESSAGE_SEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
String missingPermissions = DiscordPermissionUtil.missingPermissionsString(permissionChannel, Permission.VIEW_CHANNEL, sendPermission);
|
||||||
|
if (missingPermissions != null) {
|
||||||
|
logger().error("Failed to send message to " + describeDestination(channel) + ": " + missingPermissions);
|
||||||
|
return CompletableFuture.completedFuture(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel.sendMessage(message).exceptionally(t -> {
|
||||||
|
ErrorCallbackContext.context("Failed to deliver a message to " + describeDestination(channel)).accept(t);
|
||||||
|
TestHelper.fail(t);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private String describeDestination(DiscordGuildChannel channel) {
|
||||||
|
if (channel instanceof DiscordThreadChannel) {
|
||||||
|
return "\"" + channel.getName() + "\" in #" + ((DiscordThreadChannel) channel).getParentChannel().getName();
|
||||||
|
}
|
||||||
|
return "#" + channel.getName();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void setPlaceholders(T config, E event, SendableDiscordMessage.Formatter formatter);
|
public abstract void setPlaceholders(T config, E event, SendableDiscordMessage.Formatter formatter);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import com.discordsrv.common.player.IPlayer;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class StartMessageModule extends AbstractGameMessageModule<StartMessageConfig, AbstractGameMessageReceiveEvent> {
|
public class StartMessageModule extends AbstractGameMessageModule<StartMessageConfig, AbstractGameMessageReceiveEvent> {
|
||||||
@ -53,7 +53,7 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
|
|||||||
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
|
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
|
public List<CompletableFuture<ReceivedDiscordMessage>> sendMessageToChannels(
|
||||||
StartMessageConfig config,
|
StartMessageConfig config,
|
||||||
IPlayer player,
|
IPlayer player,
|
||||||
SendableDiscordMessage.Builder format,
|
SendableDiscordMessage.Builder format,
|
||||||
@ -62,7 +62,7 @@ public class StartMessageModule extends AbstractGameMessageModule<StartMessageCo
|
|||||||
Object... context
|
Object... context
|
||||||
) {
|
) {
|
||||||
if (!config.enabled) {
|
if (!config.enabled) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return super.sendMessageToChannels(config, player, format, channels, event, context);
|
return super.sendMessageToChannels(config, player, format, channels, event, context);
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ import com.discordsrv.common.player.IPlayer;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@ -56,7 +56,7 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
|
|||||||
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
|
public void postClusterToEventBus(ReceivedDiscordMessageCluster cluster) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
|
public List<CompletableFuture<ReceivedDiscordMessage>> sendMessageToChannels(
|
||||||
StopMessageConfig config,
|
StopMessageConfig config,
|
||||||
IPlayer player,
|
IPlayer player,
|
||||||
SendableDiscordMessage.Builder format,
|
SendableDiscordMessage.Builder format,
|
||||||
@ -65,7 +65,7 @@ public class StopMessageModule extends AbstractGameMessageModule<StopMessageConf
|
|||||||
Object... context
|
Object... context
|
||||||
) {
|
) {
|
||||||
if (!config.enabled) {
|
if (!config.enabled) {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
return super.sendMessageToChannels(config, player, format, channels, event, context);
|
return super.sendMessageToChannels(config, player, format, channels, event, context);
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> sendMessageToChannels(
|
public List<CompletableFuture<ReceivedDiscordMessage>> sendMessageToChannels(
|
||||||
MinecraftToDiscordChatConfig config,
|
MinecraftToDiscordChatConfig config,
|
||||||
IPlayer player,
|
IPlayer player,
|
||||||
SendableDiscordMessage.Builder format,
|
SendableDiscordMessage.Builder format,
|
||||||
@ -95,7 +95,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component message = ComponentUtil.fromAPI(event.getMessage());
|
Component message = ComponentUtil.fromAPI(event.getMessage());
|
||||||
Map<CompletableFuture<ReceivedDiscordMessage>, DiscordGuildMessageChannel> futures = new LinkedHashMap<>();
|
List<CompletableFuture<ReceivedDiscordMessage>> futures = new ArrayList<>();
|
||||||
|
|
||||||
// Format messages per-Guild
|
// Format messages per-Guild
|
||||||
for (Map.Entry<DiscordGuild, Set<DiscordGuildMessageChannel>> entry : channelMap.entrySet()) {
|
for (Map.Entry<DiscordGuild, Set<DiscordGuildMessageChannel>> entry : channelMap.entrySet()) {
|
||||||
@ -103,7 +103,7 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule<Mine
|
|||||||
CompletableFuture<SendableDiscordMessage> messageFuture = getMessageForGuild(config, format, guild, message, player, context);
|
CompletableFuture<SendableDiscordMessage> messageFuture = getMessageForGuild(config, format, guild, message, player, context);
|
||||||
|
|
||||||
for (DiscordGuildMessageChannel channel : entry.getValue()) {
|
for (DiscordGuildMessageChannel channel : entry.getValue()) {
|
||||||
futures.put(messageFuture.thenCompose(channel::sendMessage), channel);
|
futures.add(messageFuture.thenCompose(msg -> sendMessageToChannel(channel, msg)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user