mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2025-02-17 01:51:32 +01:00
Fix mirroring, add reply handling for messages from game message forwarding
This commit is contained in:
parent
9fd3903550
commit
9c7b065d62
@ -32,12 +32,14 @@ import com.discordsrv.api.discord.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.discord.events.message.DiscordMessageDeleteEvent;
|
||||
import com.discordsrv.api.discord.events.message.DiscordMessageUpdateEvent;
|
||||
import com.discordsrv.api.event.bus.Subscribe;
|
||||
import com.discordsrv.api.event.events.message.forward.game.AbstractGameMessageForwardedEvent;
|
||||
import com.discordsrv.api.event.events.message.receive.discord.DiscordChatMessageProcessingEvent;
|
||||
import com.discordsrv.api.placeholder.provider.SinglePlaceholder;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
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;
|
||||
import com.discordsrv.common.config.main.generic.DiscordIgnoresConfig;
|
||||
import com.discordsrv.common.future.util.CompletableFutureUtil;
|
||||
import com.discordsrv.common.logging.NamedLogger;
|
||||
import com.discordsrv.common.module.type.AbstractModule;
|
||||
@ -46,8 +48,8 @@ import net.dv8tion.jda.api.entities.Message;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
@ -58,13 +60,13 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
private final Cache<String, Mirror> mapping;
|
||||
private final Cache<Long, Cache<Long, Sync>> mapping;
|
||||
|
||||
public DiscordMessageMirroringModule(DiscordSRV discordSRV) {
|
||||
super(discordSRV, new NamedLogger(discordSRV, "DISCORD_MIRRORING"));
|
||||
this.mapping = discordSRV.caffeineBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.expireAfterWrite(60, TimeUnit.MINUTES)
|
||||
.expireAfterAccess(30, TimeUnit.MINUTES)
|
||||
.build();
|
||||
}
|
||||
|
||||
@ -97,7 +99,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
ReceivedDiscordMessage message = event.getDiscordMessage();
|
||||
|
||||
List<CompletableFuture<List<Pair<DiscordGuildMessageChannel, MirroringConfig>>>> futures = new ArrayList<>();
|
||||
List<CompletableFuture<MirrorOperation>> futures = new ArrayList<>();
|
||||
Map<ReceivedDiscordMessage.Attachment, byte[]> attachments = new LinkedHashMap<>();
|
||||
DiscordMessageEmbed.Builder attachmentEmbed = DiscordMessageEmbed.builder().setDescription("Attachments");
|
||||
|
||||
@ -140,7 +142,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
bytes = body.bytes();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
discordSRV.logger().error("Failed to download attachment for mirroring", e);
|
||||
logger().error("Failed to download attachment for mirroring", e);
|
||||
}
|
||||
attachments.put(attachment, bytes);
|
||||
continue;
|
||||
@ -158,21 +160,26 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
futures.add(
|
||||
discordSRV.discordAPI().findOrCreateDestinations(channelConfig, true, true)
|
||||
.thenApply(messageChannels -> {
|
||||
List<Pair<DiscordGuildMessageChannel, MirroringConfig>> pairs = new ArrayList<>();
|
||||
List<MirrorTarget> targets = new ArrayList<>();
|
||||
for (DiscordGuildMessageChannel messageChannel : messageChannels) {
|
||||
pairs.add(Pair.of(messageChannel, config));
|
||||
targets.add(new MirrorTarget(messageChannel, config));
|
||||
}
|
||||
return pairs;
|
||||
return new MirrorOperation(message, config, targets);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
CompletableFutureUtil.combine(futures).whenComplete((lists, t) -> {
|
||||
List<CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>>> messageFutures = new ArrayList<>();
|
||||
for (List<Pair<DiscordGuildMessageChannel, MirroringConfig>> pairs : lists) {
|
||||
for (Pair<DiscordGuildMessageChannel, MirroringConfig> pair : pairs) {
|
||||
DiscordGuildMessageChannel mirrorChannel = pair.getKey();
|
||||
MirroringConfig config = pair.getValue();
|
||||
for (MirrorOperation operation : lists) {
|
||||
List<CompletableFuture<MirroredMessage>> mirrorFutures = new ArrayList<>();
|
||||
|
||||
for (MirrorTarget target : operation.targets) {
|
||||
DiscordGuildMessageChannel mirrorChannel = target.targetChannel;
|
||||
if (mirrorChannel.getId() == event.getChannel().getId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MirroringConfig config = target.config;
|
||||
MirroringConfig.AttachmentConfig attachmentConfig = config.attachments;
|
||||
|
||||
SendableDiscordMessage.Builder messageBuilder = convert(event.getDiscordMessage(), mirrorChannel, config);
|
||||
@ -193,73 +200,137 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
currentAttachments = Collections.emptyMap();
|
||||
}
|
||||
|
||||
CompletableFuture<Pair<ReceivedDiscordMessage, MirroringConfig>> future =
|
||||
CompletableFuture<MirroredMessage> future =
|
||||
mirrorChannel.sendMessage(messageBuilder.build(), currentAttachments)
|
||||
.thenApply(msg -> Pair.of(msg, config));
|
||||
.thenApply(msg -> new MirroredMessage(msg, config));
|
||||
|
||||
messageFutures.add(future);
|
||||
mirrorFutures.add(future);
|
||||
future.exceptionally(t2 -> {
|
||||
discordSRV.logger().error("Failed to mirror message to " + mirrorChannel, t2);
|
||||
logger().error("Failed to mirror message to " + mirrorChannel, t2);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
CompletableFutureUtil.combine(messageFutures).whenComplete((messages, t2) -> {
|
||||
Map<Long, MessageReference> references = new LinkedHashMap<>();
|
||||
for (Pair<ReceivedDiscordMessage, MirroringConfig> pair : messages) {
|
||||
ReceivedDiscordMessage msg = pair.getKey();
|
||||
references.put(msg.getChannel().getId(), getReference(msg, pair.getValue()));
|
||||
}
|
||||
mapping.put(getCacheKey(message), new Mirror(getReference(message, null), references));
|
||||
});
|
||||
CompletableFutureUtil.combine(mirrorFutures).whenComplete((messages, t2) -> {
|
||||
MessageReference reference = getReference(operation.originalMessage, operation.configForOriginalMessage);
|
||||
|
||||
Map<ReceivedDiscordMessage, MessageReference> references = new LinkedHashMap<>();
|
||||
references.put(message, reference);
|
||||
for (MirroredMessage mirroredMessage : messages) {
|
||||
references.put(mirroredMessage.message, getReference(mirroredMessage));
|
||||
}
|
||||
|
||||
putIntoCache(reference, references);
|
||||
});
|
||||
}
|
||||
}).exceptionally(t -> {
|
||||
logger().error("Failed to mirror message", t);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDiscordMessageUpdate(DiscordMessageUpdateEvent event) {
|
||||
ReceivedDiscordMessage message = event.getMessage();
|
||||
Mirror mirror = mapping.get(getCacheKey(message), k -> null);
|
||||
if (mirror == null) {
|
||||
Cache<Long, Sync> syncs = mapping.getIfPresent(event.getChannel().getId());
|
||||
if (syncs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MessageReference reference : mirror.mirrors.values()) {
|
||||
ReceivedDiscordMessage message = event.getMessage();
|
||||
Sync sync = syncs.getIfPresent(message.getId());
|
||||
if (sync == null || sync.original == null || !sync.original.isMatching(message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MessageReference reference : sync.mirrors) {
|
||||
DiscordGuildMessageChannel channel = reference.getMessageChannel(discordSRV);
|
||||
if (channel == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SendableDiscordMessage sendableMessage = convert(message, channel, reference.config).build();
|
||||
channel.editMessageById(reference.messageId, sendableMessage, null).whenComplete((v, t) -> {
|
||||
if (t != null) {
|
||||
discordSRV.logger().error("Failed to update mirrored message in " + channel);
|
||||
}
|
||||
channel.editMessageById(reference.messageId, sendableMessage, null).exceptionally(t -> {
|
||||
logger().error("Failed to update mirrored message in " + channel);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onDiscordMessageDelete(DiscordMessageDeleteEvent event) {
|
||||
Mirror mirror = mapping.get(getCacheKey(event.getChannel(), event.getMessageId()), k -> null);
|
||||
if (mirror == null) {
|
||||
Cache<Long, Sync> syncs = mapping.getIfPresent(event.getChannel().getId());
|
||||
if (syncs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MessageReference reference : mirror.mirrors.values()) {
|
||||
Sync sync = syncs.getIfPresent(event.getMessageId());
|
||||
if (sync == null || sync.original == null || !sync.original.isMatching(event.getChannel())
|
||||
|| sync.original.messageId != event.getMessageId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (MessageReference reference : sync.mirrors) {
|
||||
DiscordMessageChannel channel = reference.getMessageChannel(discordSRV);
|
||||
if (channel == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
channel.deleteMessageById(reference.messageId, reference.webhookMessage).whenComplete((v, t) -> {
|
||||
if (t != null) {
|
||||
discordSRV.logger().error("Failed to delete mirrored message in " + channel);
|
||||
}
|
||||
channel.deleteMessageById(reference.messageId, reference.webhookMessage).exceptionally(t -> {
|
||||
logger().error("Failed to delete mirrored message in " + channel);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameMessageForwarded(AbstractGameMessageForwardedEvent event) {
|
||||
Set<? extends ReceivedDiscordMessage> messages = event.getDiscordMessage().getMessages();
|
||||
|
||||
Map<ReceivedDiscordMessage, MessageReference> references = new LinkedHashMap<>();
|
||||
for (ReceivedDiscordMessage message : messages) {
|
||||
DiscordMessageChannel channel = message.getChannel();
|
||||
|
||||
MirroringConfig config = discordSRV.channelConfig().resolve(channel).values().iterator().next().mirroring; // TODO: add channel to event
|
||||
MessageReference reference = getReference(message, config);
|
||||
references.put(message, reference);
|
||||
}
|
||||
|
||||
putIntoCache(null, references);
|
||||
}
|
||||
|
||||
@SuppressWarnings("DataFlowIssue") // Supplier always returns a non-null value
|
||||
@NotNull
|
||||
private Cache<Long, Sync> getCache(long channelId) {
|
||||
return mapping.get(
|
||||
channelId,
|
||||
k -> discordSRV.caffeineBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MINUTES)
|
||||
.expireAfterAccess(10, TimeUnit.MINUTES)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
private void putIntoCache(@Nullable MessageReference original, Map<ReceivedDiscordMessage, MessageReference> references) {
|
||||
if (original == null && references.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<ReceivedDiscordMessage, MessageReference> entry : references.entrySet()) {
|
||||
ReceivedDiscordMessage message = entry.getKey();
|
||||
MessageReference reference = entry.getValue();
|
||||
|
||||
List<MessageReference> refs = new ArrayList<>();
|
||||
for (MessageReference ref : references.values()) {
|
||||
if (ref == reference) {
|
||||
continue;
|
||||
}
|
||||
refs.add(ref);
|
||||
}
|
||||
|
||||
getCache(message.getChannel().getId()).put(message.getId(), new Sync(original, refs));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a given received message to a sendable message.
|
||||
*/
|
||||
@ -270,10 +341,7 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
) {
|
||||
DiscordGuildMember member = message.getMember();
|
||||
DiscordUser user = message.getAuthor();
|
||||
String username = discordSRV.placeholderService().replacePlaceholders(
|
||||
config.usernameFormat, "%user_effective_name% [M]",
|
||||
member, user
|
||||
);
|
||||
String username = discordSRV.placeholderService().replacePlaceholders(config.usernameFormat, member, user);
|
||||
if (username.length() > 32) {
|
||||
username = username.substring(0, 32);
|
||||
}
|
||||
@ -284,15 +352,12 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
|
||||
if (replyMessage != null) {
|
||||
MessageReference matchingReference = null;
|
||||
for (Mirror mirror : mapping.asMap().values()) {
|
||||
if (!mirror.hasMessage(replyMessage)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageReference ref = mirror.getForChannel(destinationChannel);
|
||||
if (ref != null) {
|
||||
matchingReference = ref;
|
||||
break;
|
||||
Cache<Long, Sync> syncs = mapping.getIfPresent(replyMessage.getChannel().getId());
|
||||
if (syncs != null) {
|
||||
Sync sync = syncs.getIfPresent(replyMessage.getId());
|
||||
if (sync != null) {
|
||||
matchingReference = sync.getForChannel(destinationChannel);
|
||||
}
|
||||
}
|
||||
|
||||
@ -304,9 +369,13 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
) : replyMessage.getJumpUrl();
|
||||
|
||||
content = discordSRV.placeholderService()
|
||||
.replacePlaceholders(config.replyFormat, replyMessage.getMember(), replyMessage.getAuthor())
|
||||
.replace("%message_jump_url%", jumpUrl)
|
||||
.replace("%message%", content);
|
||||
.replacePlaceholders(
|
||||
config.replyFormat,
|
||||
replyMessage.getMember(),
|
||||
replyMessage.getAuthor(),
|
||||
new SinglePlaceholder("message_jump_url", jumpUrl),
|
||||
new SinglePlaceholder("message", content)
|
||||
);
|
||||
}
|
||||
|
||||
SendableDiscordMessage.Builder builder = SendableDiscordMessage.builder()
|
||||
@ -318,13 +387,16 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
? member.getEffectiveServerAvatarUrl()
|
||||
: user.getEffectiveAvatarUrl()
|
||||
);
|
||||
builder.getAllowedMentions().clear();
|
||||
for (DiscordMessageEmbed embed : message.getEmbeds()) {
|
||||
builder.addEmbed(embed);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
private MessageReference getReference(MirroredMessage message) {
|
||||
return getReference(message.message, message.config);
|
||||
}
|
||||
|
||||
private MessageReference getReference(ReceivedDiscordMessage message, MirroringConfig config) {
|
||||
return getReference(message.getChannel(), message.getId(), message.isWebhookMessage(), config);
|
||||
}
|
||||
@ -345,24 +417,61 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
throw new IllegalStateException("Unexpected channel type: " + channel.getClass().getName());
|
||||
}
|
||||
|
||||
private static String getCacheKey(ReceivedDiscordMessage message) {
|
||||
return getCacheKey(message.getChannel(), message.getId());
|
||||
}
|
||||
private static class MirrorOperation {
|
||||
|
||||
private static String getCacheKey(DiscordMessageChannel channel, long messageId) {
|
||||
if (channel instanceof DiscordTextChannel) {
|
||||
return getCacheKey(channel.getId(), 0L, messageId);
|
||||
} else if (channel instanceof DiscordThreadChannel) {
|
||||
long parentId = ((DiscordThreadChannel) channel).getParentChannel().getId();
|
||||
return getCacheKey(parentId, channel.getId(), messageId);
|
||||
private final ReceivedDiscordMessage originalMessage;
|
||||
private final MirroringConfig configForOriginalMessage;
|
||||
private final List<MirrorTarget> targets;
|
||||
|
||||
public MirrorOperation(ReceivedDiscordMessage originalMessage, MirroringConfig configForOriginalMessage, List<MirrorTarget> targets) {
|
||||
this.originalMessage = originalMessage;
|
||||
this.configForOriginalMessage = configForOriginalMessage;
|
||||
this.targets = targets;
|
||||
}
|
||||
throw new IllegalStateException("Unexpected channel type: " + channel.getClass().getName());
|
||||
|
||||
}
|
||||
|
||||
private static String getCacheKey(long channelId, long threadId, long messageId) {
|
||||
return Long.toUnsignedString(channelId)
|
||||
+ (threadId > 0 ? ":" + Long.toUnsignedString(threadId) : "")
|
||||
+ ":" + Long.toUnsignedString(messageId);
|
||||
private static class MirrorTarget {
|
||||
|
||||
private final DiscordGuildMessageChannel targetChannel;
|
||||
private final MirroringConfig config;
|
||||
|
||||
public MirrorTarget(DiscordGuildMessageChannel targetChannel, MirroringConfig config) {
|
||||
this.targetChannel = targetChannel;
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MirroredMessage {
|
||||
|
||||
private final ReceivedDiscordMessage message;
|
||||
private final MirroringConfig config;
|
||||
|
||||
public MirroredMessage(ReceivedDiscordMessage message, MirroringConfig config) {
|
||||
this.message = message;
|
||||
this.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Sync {
|
||||
|
||||
private final MessageReference original;
|
||||
private final List<MessageReference> mirrors;
|
||||
|
||||
public Sync(MessageReference original, List<MessageReference> mirrors) {
|
||||
this.original = original;
|
||||
this.mirrors = mirrors;
|
||||
}
|
||||
|
||||
public MessageReference getForChannel(DiscordGuildMessageChannel channel) {
|
||||
for (MessageReference mirror : mirrors) {
|
||||
if (mirror.isMatching(channel)) {
|
||||
return mirror;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MessageReference {
|
||||
@ -421,44 +530,15 @@ public class DiscordMessageMirroringModule extends AbstractModule<DiscordSRV> {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isMatching(ReceivedDiscordMessage message) {
|
||||
return isMatching((DiscordGuildMessageChannel) message.getChannel())
|
||||
&& message.getId() == messageId;
|
||||
}
|
||||
|
||||
public boolean isMatching(DiscordGuildMessageChannel channel) {
|
||||
public boolean isMatching(DiscordMessageChannel channel) {
|
||||
return channel instanceof DiscordThreadChannel
|
||||
? channel.getId() == threadId
|
||||
&& ((DiscordThreadChannel) channel).getParentChannel().getId() == channelId
|
||||
: channel.getId() == channelId;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Mirror {
|
||||
|
||||
private final MessageReference original;
|
||||
private final Map<Long, MessageReference> mirrors; // thread/channel id -> reference
|
||||
|
||||
public Mirror(MessageReference original, Map<Long, MessageReference> mirrors) {
|
||||
this.original = original;
|
||||
this.mirrors = mirrors;
|
||||
}
|
||||
|
||||
public boolean hasMessage(ReceivedDiscordMessage message) {
|
||||
if (original.isMatching(message)) {
|
||||
return true;
|
||||
}
|
||||
MessageReference reference = mirrors.get(message.getChannel().getId());
|
||||
return reference != null && reference.isMatching(message);
|
||||
}
|
||||
|
||||
public MessageReference getForChannel(DiscordGuildMessageChannel channel) {
|
||||
long id = channel.getId();
|
||||
if (original.isMatching(channel)) {
|
||||
return original;
|
||||
} else {
|
||||
return mirrors.get(id);
|
||||
}
|
||||
public boolean isMatching(ReceivedDiscordMessage message) {
|
||||
return isMatching(message.getChannel()) && messageId == message.getId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,11 +82,8 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set<Object> lookupContexts) {
|
||||
Set<Object> contexts = new HashSet<>(lookupContexts);
|
||||
contexts.addAll(globalContext);
|
||||
contexts.removeIf(Objects::isNull);
|
||||
for (Object context : contexts) {
|
||||
if (context == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (context instanceof PlaceholderProvider) {
|
||||
PlaceholderLookupResult result = ((PlaceholderProvider) context).lookup(placeholder, contexts);
|
||||
if (result.getType() != PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
|
Loading…
Reference in New Issue
Block a user