From 0a128e37e9619f2d489171359e34eb34a2765b7a Mon Sep 17 00:00:00 2001 From: Vankka Date: Tue, 1 Feb 2022 12:39:59 +0200 Subject: [PATCH] Add support for webhooks in threads, add username format option for mirroring --- .../channel/DiscordGuildMessageChannel.java | 20 +-- .../entity/channel/DiscordNewsChannel.java | 2 +- .../entity/channel/DiscordTextChannel.java | 2 +- .../channel/DiscordThreadContainer.java | 45 +++++ common/build.gradle | 2 +- .../config/main/channels/MirroringConfig.java | 5 + .../discord/api/DiscordAPIEventModule.java | 8 +- ...> AbstractDiscordGuildMessageChannel.java} | 77 ++------- ...ava => AbstractDiscordMessageChannel.java} | 8 +- ...actDiscordThreadedGuildMessageChannel.java | 101 ++++++++++++ .../entity/channel/DiscordDMChannelImpl.java | 2 +- .../channel/DiscordNewsChannelImpl.java | 2 +- .../channel/DiscordTextChannelImpl.java | 3 +- .../channel/DiscordThreadChannelImpl.java | 46 +----- .../message/ReceivedDiscordMessageImpl.java | 4 +- .../DiscordMessageMirroringModule.java | 155 +++++++++--------- .../game/MinecraftToDiscordChatModule.java | 26 +-- 17 files changed, 274 insertions(+), 234 deletions(-) create mode 100644 api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordThreadContainer.java rename common/src/main/java/com/discordsrv/common/discord/api/entity/channel/{DiscordGuildMessageChannelImpl.java => AbstractDiscordGuildMessageChannel.java} (59%) rename common/src/main/java/com/discordsrv/common/discord/api/entity/channel/{DiscordMessageChannelImpl.java => AbstractDiscordMessageChannel.java} (87%) create mode 100644 common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordThreadedGuildMessageChannel.java diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java index f6fab0a0..d5eb3aec 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordGuildMessageChannel.java @@ -24,24 +24,8 @@ package com.discordsrv.api.discord.api.entity.channel; import com.discordsrv.api.discord.api.entity.Mentionable; -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.concurrent.CompletableFuture; /** - * A regular Discord channel that messages can be sent to (threads not included). + * A regular Discord channel that messages can be sent to. */ -public interface DiscordGuildMessageChannel extends DiscordMessageChannel, DiscordGuildChannel, Mentionable { - - @NotNull - List getActiveThreads(); - - CompletableFuture> retrieveArchivedPrivateThreads(); - CompletableFuture> retrieveArchivedJoinedPrivateThreads(); - CompletableFuture> retrieveArchivedPublicThreads(); - - CompletableFuture createThread(String name, boolean privateThread); - CompletableFuture createThread(String name, long messageId); - -} +public interface DiscordGuildMessageChannel extends DiscordMessageChannel, DiscordGuildChannel, Mentionable {} diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java index d93d74e8..c7464e2e 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordNewsChannel.java @@ -26,7 +26,7 @@ package com.discordsrv.api.discord.api.entity.channel; import com.discordsrv.api.DiscordSRVApi; import net.dv8tion.jda.api.entities.NewsChannel; -public interface DiscordNewsChannel extends DiscordGuildMessageChannel { +public interface DiscordNewsChannel extends DiscordGuildMessageChannel, DiscordThreadContainer { /** * Returns the JDA representation of this object. This should not be used if it can be avoided. diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java index 95138049..de29bbd1 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordTextChannel.java @@ -30,7 +30,7 @@ import org.jetbrains.annotations.Nullable; /** * A Discord text channel. */ -public interface DiscordTextChannel extends DiscordGuildMessageChannel { +public interface DiscordTextChannel extends DiscordGuildMessageChannel, DiscordThreadContainer { /** * Gets the topic of the text channel. diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordThreadContainer.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordThreadContainer.java new file mode 100644 index 00000000..2a7fb33c --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/channel/DiscordThreadContainer.java @@ -0,0 +1,45 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.discordsrv.api.discord.api.entity.channel; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * A Discord channel that contains threads. + */ +public interface DiscordThreadContainer extends DiscordGuildChannel { + + @NotNull + List getActiveThreads(); + + CompletableFuture> retrieveArchivedPrivateThreads(); + CompletableFuture> retrieveArchivedJoinedPrivateThreads(); + CompletableFuture> retrieveArchivedPublicThreads(); + + CompletableFuture createThread(String name, boolean privateThread); + CompletableFuture createThread(String name, long messageId); +} diff --git a/common/build.gradle b/common/build.gradle index cb877fa4..1dfee828 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -27,7 +27,7 @@ dependencies { api 'dev.vankka.dependencydownload:runtime:' + rootProject.ddVersion // Discord Webhooks - runtimeDownloadApi 'club.minnced:discord-webhooks:0.5.7' + runtimeDownloadApi 'club.minnced:discord-webhooks:0.7.5' // Apache Commons runtimeDownloadApi 'org.apache.commons:commons-lang3:3.12.0' diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/MirroringConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/MirroringConfig.java index 025b66bd..ce7328f4 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/channels/MirroringConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/MirroringConfig.java @@ -29,4 +29,9 @@ public class MirroringConfig { @Comment("Users, bots and webhooks to ignore when mirroring") public DiscordIgnores ignores = new DiscordIgnores(); + + @Comment("The format of the username of mirrored messages\n" + + "It's recommended to include some special character if in-game messages use webhooks,\n" + + "in order to prevent Discord users and in-game players being grouped together") + public String usernameFormat = "%user_effective_name% [M]"; } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java b/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java index b13ed08b..99c3f0d0 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/DiscordAPIEventModule.java @@ -25,7 +25,7 @@ import com.discordsrv.api.discord.events.message.DiscordMessageReceiveEvent; import com.discordsrv.api.discord.events.message.DiscordMessageUpdateEvent; import com.discordsrv.api.event.bus.Subscribe; import com.discordsrv.common.DiscordSRV; -import com.discordsrv.common.discord.api.entity.channel.DiscordMessageChannelImpl; +import com.discordsrv.common.discord.api.entity.channel.AbstractDiscordMessageChannel; import com.discordsrv.common.discord.api.entity.guild.DiscordGuildMemberImpl; import com.discordsrv.common.discord.api.entity.guild.DiscordRoleImpl; import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl; @@ -47,7 +47,7 @@ public class DiscordAPIEventModule extends AbstractModule { @Subscribe public void onMessageReceived(MessageReceivedEvent event) { discordSRV.eventBus().publish(new DiscordMessageReceiveEvent( - DiscordMessageChannelImpl.get(discordSRV, event.getChannel()), + AbstractDiscordMessageChannel.get(discordSRV, event.getChannel()), ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()) )); } @@ -55,7 +55,7 @@ public class DiscordAPIEventModule extends AbstractModule { @Subscribe public void onMessageUpdate(MessageUpdateEvent event) { discordSRV.eventBus().publish(new DiscordMessageUpdateEvent( - DiscordMessageChannelImpl.get(discordSRV, event.getChannel()), + AbstractDiscordMessageChannel.get(discordSRV, event.getChannel()), ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()) )); } @@ -63,7 +63,7 @@ public class DiscordAPIEventModule extends AbstractModule { @Subscribe public void onMessageDelete(MessageDeleteEvent event) { discordSRV.eventBus().publish(new DiscordMessageDeleteEvent( - DiscordMessageChannelImpl.get(discordSRV, event.getChannel()), + AbstractDiscordMessageChannel.get(discordSRV, event.getChannel()), event.getMessageIdLong() )); } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordGuildMessageChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java similarity index 59% rename from common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordGuildMessageChannelImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java index e7730ee9..bf3976cf 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordGuildMessageChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordGuildMessageChannel.java @@ -22,7 +22,6 @@ import club.minnced.discord.webhook.WebhookClient; import club.minnced.discord.webhook.receive.ReadonlyMessage; import club.minnced.discord.webhook.send.WebhookMessage; import com.discordsrv.api.discord.api.entity.channel.DiscordGuildMessageChannel; -import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; @@ -30,30 +29,30 @@ import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl; import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl; import com.discordsrv.common.discord.api.entity.message.util.SendableDiscordMessageUtil; -import net.dv8tion.jda.api.entities.*; +import net.dv8tion.jda.api.entities.GuildMessageChannel; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageChannel; import net.dv8tion.jda.api.requests.restaction.MessageAction; -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.BiFunction; -import java.util.function.Function; -import java.util.stream.Collectors; -public abstract class DiscordGuildMessageChannelImpl - extends DiscordMessageChannelImpl +public abstract class AbstractDiscordGuildMessageChannel + extends AbstractDiscordMessageChannel implements DiscordGuildMessageChannel { private final DiscordGuild guild; - public DiscordGuildMessageChannelImpl(DiscordSRV discordSRV, T channel) { + public AbstractDiscordGuildMessageChannel(DiscordSRV discordSRV, T channel) { super(discordSRV, channel); this.guild = new DiscordGuildImpl(discordSRV, channel.getGuild()); } + public CompletableFuture queryWebhookClient() { + return discordSRV.discordAPI().queryWebhookClient(getId()); + } + @Override public @NotNull String getName() { return channel.getName(); @@ -69,60 +68,6 @@ public abstract class DiscordGuildMessageChannelImpl getActiveThreads() { - List threads = channel.getThreadChannels(); - List threadChannels = new ArrayList<>(threads.size()); - for (ThreadChannel thread : threads) { - threadChannels.add(new DiscordThreadChannelImpl(discordSRV, thread)); - } - return threadChannels; - } - - @Override - public CompletableFuture> retrieveArchivedPrivateThreads() { - return threads(IThreadContainer::retrieveArchivedPrivateThreadChannels); - } - - @Override - public CompletableFuture> retrieveArchivedJoinedPrivateThreads() { - return threads(IThreadContainer::retrieveArchivedPrivateJoinedThreadChannels); - } - - @Override - public CompletableFuture> retrieveArchivedPublicThreads() { - return threads(IThreadContainer::retrieveArchivedPublicThreadChannels); - } - - private CompletableFuture> threads(Function action) { - return discordSRV.discordAPI().mapExceptions(() -> - action.apply(channel) - .submit() - .thenApply(channels -> channels.stream() - .map(channel -> new DiscordThreadChannelImpl(discordSRV, channel)) - .collect(Collectors.toList()) - ) - ); - } - - @Override - public CompletableFuture createThread(String name, boolean privateThread) { - return thread(channel -> channel.createThreadChannel(name, privateThread)); - } - - @Override - public CompletableFuture createThread(String name, long messageId) { - return thread(channel -> channel.createThreadChannel(name, messageId)); - } - - private CompletableFuture thread(Function action) { - return discordSRV.discordAPI().mapExceptions(() -> - action.apply(channel) - .submit() - .thenApply(channel -> new DiscordThreadChannelImpl(discordSRV, channel)) - ); - } - @Override public @NotNull CompletableFuture sendMessage(@NotNull SendableDiscordMessage message) { return message(message, WebhookClient::send, MessageChannel::sendMessage); @@ -144,7 +89,7 @@ public abstract class DiscordGuildMessageChannelImpl { CompletableFuture future; if (message.isWebhookMessage()) { - future = discordSRV.discordAPI().queryWebhookClient(getId()) + future = queryWebhookClient() .thenCompose(client -> webhookFunction.apply( client, SendableDiscordMessageUtil.toWebhook(message))) .thenApply(msg -> ReceivedDiscordMessageImpl.fromWebhook(discordSRV, msg)); diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordMessageChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordMessageChannel.java similarity index 87% rename from common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordMessageChannelImpl.java rename to common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordMessageChannel.java index 39a3ba57..51d9e2f9 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordMessageChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordMessageChannel.java @@ -24,10 +24,10 @@ import net.dv8tion.jda.api.entities.*; import java.util.Objects; -public abstract class DiscordMessageChannelImpl +public abstract class AbstractDiscordMessageChannel implements DiscordMessageChannel { - public static DiscordMessageChannelImpl get(DiscordSRV discordSRV, MessageChannel messageChannel) { + public static AbstractDiscordMessageChannel get(DiscordSRV discordSRV, MessageChannel messageChannel) { if (messageChannel instanceof TextChannel) { return new DiscordTextChannelImpl(discordSRV, (TextChannel) messageChannel); } else if (messageChannel instanceof ThreadChannel) { @@ -44,7 +44,7 @@ public abstract class DiscordMessageChannelImpl protected final DiscordSRV discordSRV; protected final T channel; - public DiscordMessageChannelImpl(DiscordSRV discordSRV, T channel) { + public AbstractDiscordMessageChannel(DiscordSRV discordSRV, T channel) { this.discordSRV = discordSRV; this.channel = channel; } @@ -63,7 +63,7 @@ public abstract class DiscordMessageChannelImpl public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - DiscordGuildMessageChannelImpl that = (DiscordGuildMessageChannelImpl) o; + AbstractDiscordGuildMessageChannel that = (AbstractDiscordGuildMessageChannel) o; return Objects.equals(getId(), that.getId()); } diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordThreadedGuildMessageChannel.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordThreadedGuildMessageChannel.java new file mode 100644 index 00000000..4077b895 --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/AbstractDiscordThreadedGuildMessageChannel.java @@ -0,0 +1,101 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.discordsrv.common.discord.api.entity.channel; + +import com.discordsrv.api.discord.api.entity.channel.DiscordGuildMessageChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordThreadContainer; +import com.discordsrv.common.DiscordSRV; +import net.dv8tion.jda.api.entities.GuildMessageChannel; +import net.dv8tion.jda.api.entities.IThreadContainer; +import net.dv8tion.jda.api.entities.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 AbstractDiscordThreadedGuildMessageChannel + extends AbstractDiscordGuildMessageChannel + implements DiscordGuildMessageChannel, DiscordThreadContainer { + + public AbstractDiscordThreadedGuildMessageChannel(DiscordSRV discordSRV, T channel) { + super(discordSRV, channel); + } + + @Override + public @NotNull List getActiveThreads() { + List threads = channel.getThreadChannels(); + List threadChannels = new ArrayList<>(threads.size()); + for (ThreadChannel thread : threads) { + threadChannels.add(new DiscordThreadChannelImpl(discordSRV, thread)); + } + return threadChannels; + } + + @Override + public CompletableFuture> retrieveArchivedPrivateThreads() { + return threads(IThreadContainer::retrieveArchivedPrivateThreadChannels); + } + + @Override + public CompletableFuture> retrieveArchivedJoinedPrivateThreads() { + return threads(IThreadContainer::retrieveArchivedPrivateJoinedThreadChannels); + } + + @Override + public CompletableFuture> retrieveArchivedPublicThreads() { + return threads(IThreadContainer::retrieveArchivedPublicThreadChannels); + } + + private CompletableFuture> threads( + Function action) { + return discordSRV.discordAPI().mapExceptions(() -> + action.apply(channel) + .submit() + .thenApply(channels -> channels.stream() + .map(channel -> new DiscordThreadChannelImpl(discordSRV, channel)) + .collect(Collectors.toList()) + ) + ); + } + + @Override + public CompletableFuture createThread(String name, boolean privateThread) { + return thread(channel -> channel.createThreadChannel(name, privateThread)); + } + + @Override + public CompletableFuture createThread(String name, long messageId) { + return thread(channel -> channel.createThreadChannel(name, messageId)); + } + + private CompletableFuture thread(Function action) { + return discordSRV.discordAPI().mapExceptions(() -> + action.apply(channel) + .submit() + .thenApply(channel -> new DiscordThreadChannelImpl(discordSRV, channel)) + ); + } + +} diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java index 71e0f058..1e8377e5 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordDMChannelImpl.java @@ -32,7 +32,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Objects; import java.util.concurrent.CompletableFuture; -public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements DiscordDMChannel { +public class DiscordDMChannelImpl extends AbstractDiscordMessageChannel implements DiscordDMChannel { private final DiscordUser user; diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java index ba60b5c2..dd55ab20 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordNewsChannelImpl.java @@ -23,7 +23,7 @@ import com.discordsrv.common.DiscordSRV; import net.dv8tion.jda.api.entities.NewsChannel; public class DiscordNewsChannelImpl - extends DiscordGuildMessageChannelImpl + extends AbstractDiscordThreadedGuildMessageChannel implements DiscordNewsChannel { public DiscordNewsChannelImpl(DiscordSRV discordSRV, NewsChannel channel) { diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java index 57a36833..478e5507 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordTextChannelImpl.java @@ -23,7 +23,8 @@ import com.discordsrv.common.DiscordSRV; import net.dv8tion.jda.api.entities.TextChannel; import org.jetbrains.annotations.Nullable; -public class DiscordTextChannelImpl extends DiscordGuildMessageChannelImpl implements DiscordTextChannel { +public class DiscordTextChannelImpl extends AbstractDiscordThreadedGuildMessageChannel + implements DiscordTextChannel { public DiscordTextChannelImpl(DiscordSRV discordSRV, TextChannel textChannel) { super(discordSRV, textChannel); diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordThreadChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordThreadChannelImpl.java index 1eaa1477..a04f2e64 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordThreadChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/channel/DiscordThreadChannelImpl.java @@ -18,15 +18,12 @@ package com.discordsrv.common.discord.api.entity.channel; +import club.minnced.discord.webhook.WebhookClient; import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; import com.discordsrv.api.discord.api.entity.channel.DiscordThreadChannel; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; -import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; -import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl; -import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageImpl; -import com.discordsrv.common.discord.api.entity.message.util.SendableDiscordMessageUtil; import net.dv8tion.jda.api.entities.IThreadContainer; import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.ThreadChannel; @@ -34,7 +31,8 @@ import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; -public class DiscordThreadChannelImpl extends DiscordMessageChannelImpl implements DiscordThreadChannel { +public class DiscordThreadChannelImpl extends AbstractDiscordGuildMessageChannel + implements DiscordThreadChannel { private final DiscordTextChannel textChannel; private final DiscordGuild guild; @@ -50,40 +48,10 @@ public class DiscordThreadChannelImpl extends DiscordMessageChannelImpl sendMessage(@NotNull SendableDiscordMessage message) { - if (message.isWebhookMessage()) { - throw new IllegalArgumentException("Cannot send webhook messages to ThreadChannels"); - } - - return discordSRV.discordAPI().mapExceptions( - channel.sendMessage(SendableDiscordMessageUtil.toJDA(message)) - .submit() - .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)) - ); - } - - @Override - public CompletableFuture deleteMessageById(long id, boolean webhookMessage) { - if (webhookMessage) { - throw new IllegalArgumentException("ThreadChannels do not contain webhook messages"); - } - - return discordSRV.discordAPI().mapExceptions( - channel.deleteMessageById(id).submit() - ); - } - - @Override - public @NotNull CompletableFuture editMessageById(long id, @NotNull SendableDiscordMessage message) { - if (message.isWebhookMessage()) { - throw new IllegalArgumentException("Cannot send webhook messages to ThreadChannels"); - } - - return discordSRV.discordAPI().mapExceptions( - channel.editMessageById(id, SendableDiscordMessageUtil.toJDA(message)) - .submit() - .thenApply(msg -> ReceivedDiscordMessageImpl.fromJDA(discordSRV, msg)) - ); + public CompletableFuture queryWebhookClient() { + return discordSRV.discordAPI() + .queryWebhookClient(getParentChannel().getId()) + .thenApply(client -> client.onThread(getId())); } @Override diff --git a/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java index 90781c7c..d88e767e 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/entity/message/ReceivedDiscordMessageImpl.java @@ -41,7 +41,7 @@ import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.config.main.channels.base.BaseChannelConfig; import com.discordsrv.common.discord.api.entity.DiscordUserImpl; -import com.discordsrv.common.discord.api.entity.channel.DiscordMessageChannelImpl; +import com.discordsrv.common.discord.api.entity.channel.AbstractDiscordMessageChannel; import com.discordsrv.common.discord.api.entity.guild.DiscordGuildMemberImpl; import com.discordsrv.common.function.OrDefault; import com.discordsrv.common.future.util.CompletableFutureUtil; @@ -71,7 +71,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple String webhookUsername = webhookMessage ? message.getAuthor().getName() : null; String webhookAvatarUrl = webhookMessage ? message.getAuthor().getEffectiveAvatarUrl() : null; - DiscordMessageChannel channel = DiscordMessageChannelImpl.get(discordSRV, message.getChannel()); + DiscordMessageChannel channel = AbstractDiscordMessageChannel.get(discordSRV, message.getChannel()); DiscordUser user = new DiscordUserImpl(discordSRV, message.getAuthor()); Member member = message.getMember(); diff --git a/common/src/main/java/com/discordsrv/common/messageforwarding/discord/DiscordMessageMirroringModule.java b/common/src/main/java/com/discordsrv/common/messageforwarding/discord/DiscordMessageMirroringModule.java index 8377bc86..722b600e 100644 --- a/common/src/main/java/com/discordsrv/common/messageforwarding/discord/DiscordMessageMirroringModule.java +++ b/common/src/main/java/com/discordsrv/common/messageforwarding/discord/DiscordMessageMirroringModule.java @@ -41,6 +41,7 @@ import com.discordsrv.common.future.util.CompletableFutureUtil; import com.discordsrv.common.logging.NamedLogger; import com.discordsrv.common.module.type.AbstractModule; import com.github.benmanes.caffeine.cache.Cache; +import org.apache.commons.lang3.tuple.Pair; import java.util.*; import java.util.concurrent.CompletableFuture; @@ -72,7 +73,7 @@ public class DiscordMessageMirroringModule extends AbstractModule { ReceivedDiscordMessage message = event.getDiscordMessage(); DiscordMessageChannel channel = event.getChannel(); - List mirrorChannels = new ArrayList<>(); + List>> mirrorChannels = new ArrayList<>(); List> futures = new ArrayList<>(); for (Map.Entry> entry : channels.entrySet()) { @@ -97,7 +98,7 @@ public class DiscordMessageMirroringModule extends AbstractModule { for (Long channelId : channelIds) { discordSRV.discordAPI().getTextChannelById(channelId).ifPresent(textChannel -> { if (textChannel.getId() != channel.getId()) { - mirrorChannels.add(textChannel); + mirrorChannels.add(Pair.of(textChannel, config)); } }); } @@ -105,43 +106,34 @@ public class DiscordMessageMirroringModule extends AbstractModule { discordSRV.discordAPI().findOrCreateThreads(iChannelConfig, threadChannel -> { if (threadChannel.getId() != channel.getId()) { - mirrorChannels.add(threadChannel); + mirrorChannels.add(Pair.of(threadChannel, config)); } }, futures); } CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).whenComplete((v, t) -> { - List text = new ArrayList<>(); - List thread = new ArrayList<>(); - for (DiscordMessageChannel mirrorChannel : mirrorChannels) { - if (mirrorChannel instanceof DiscordTextChannel) { - text.add((DiscordTextChannel) mirrorChannel); - } else if (mirrorChannel instanceof DiscordThreadChannel) { - thread.add((DiscordThreadChannel) mirrorChannel); - } - } + List>>> messageFutures = new ArrayList<>(); + for (Pair> pair : mirrorChannels) { + DiscordMessageChannel mirrorChannel = pair.getKey(); + OrDefault config = pair.getValue(); + SendableDiscordMessage sendableMessage = convert(event.getDiscordMessage(), config); - SendableDiscordMessage.Builder builder = convert(event.getDiscordMessage()); - List> messageFutures = new ArrayList<>(); - if (!text.isEmpty()) { - SendableDiscordMessage finalMessage = builder.build(); - for (DiscordTextChannel textChannel : text) { - messageFutures.add(textChannel.sendMessage(finalMessage)); - } - } - if (!thread.isEmpty()) { - SendableDiscordMessage finalMessage = builder.convertToNonWebhook().build(); - for (DiscordThreadChannel threadChannel : thread) { - messageFutures.add(threadChannel.sendMessage(finalMessage)); - } + CompletableFuture>> future = + mirrorChannel.sendMessage(sendableMessage).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) -> { Set references = new HashSet<>(); - for (ReceivedDiscordMessage msg : messages) { - references.add(getReference(msg)); + for (Pair> pair : messages) { + references.add(getReference(pair.getKey(), pair.getValue())); } - mapping.put(getReference(message), references); + mapping.put(getReference(message, null), references); }); }); } @@ -149,47 +141,29 @@ public class DiscordMessageMirroringModule extends AbstractModule { @Subscribe public void onDiscordMessageUpdate(DiscordMessageUpdateEvent event) { ReceivedDiscordMessage message = event.getMessage(); - Set references = mapping.get(getReference(message), k -> null); + Set references = mapping.get(getReference(message, null), k -> null); if (references == null) { return; } - Map text = new LinkedHashMap<>(); - Map thread = new LinkedHashMap<>(); for (MessageReference reference : references) { DiscordMessageChannel channel = reference.getMessageChannel(discordSRV); - if (channel instanceof DiscordTextChannel) { - text.put((DiscordTextChannel) channel, reference); - } else if (channel instanceof DiscordThreadChannel) { - thread.put((DiscordThreadChannel) channel, reference); - } - } - SendableDiscordMessage.Builder builder = convert(message); - if (!text.isEmpty()) { - SendableDiscordMessage finalMessage = builder.build(); - for (Map.Entry entry : text.entrySet()) { - entry.getKey().editMessageById(entry.getValue().messageId, finalMessage).whenComplete((v, t) -> { - if (t != null) { - discordSRV.logger().error("Failed to update mirrored message in " + entry.getKey()); - } - }); - } - } - if (!thread.isEmpty()) { - SendableDiscordMessage finalMessage = builder.convertToNonWebhook().build(); - for (Map.Entry entry : thread.entrySet()) { - entry.getKey().editMessageById(entry.getValue().messageId, finalMessage).whenComplete((v, t) -> { - if (t != null) { - discordSRV.logger().error("Failed to update mirrored message in " + entry.getKey()); - } - }); + if (channel == null) { + continue; } + + SendableDiscordMessage sendableMessage = convert(message, reference.config); + channel.editMessageById(reference.messageId, sendableMessage).whenComplete((v, t) -> { + if (t != null) { + discordSRV.logger().error("Failed to update mirrored message in " + channel); + } + }); } } @Subscribe public void onDiscordMessageDelete(DiscordMessageDeleteEvent event) { - Set references = mapping.get(getReference(event.getChannel(), event.getMessageId(), false), k -> null); + Set references = mapping.get(getReference(event.getChannel(), event.getMessageId(), false, null), k -> null); if (references == null) { return; } @@ -208,33 +182,50 @@ public class DiscordMessageMirroringModule extends AbstractModule { } } - private SendableDiscordMessage.Builder convert(ReceivedDiscordMessage message) { + /** + * Converts a given received message to a sendable message. + */ + private SendableDiscordMessage convert(ReceivedDiscordMessage message, OrDefault config) { DiscordGuildMember member = message.getMember().orElse(null); DiscordUser user = message.getAuthor(); + String username = discordSRV.placeholderService().replacePlaceholders( + config.get(cfg -> cfg.usernameFormat, "%user_effective_name% [M]"), + member, user + ); + if (username.length() > 32) { + username = username.substring(0, 32); + } SendableDiscordMessage.Builder builder = SendableDiscordMessage.builder() .setContent(message.getContent().orElse(null)) - .setWebhookUsername(member != null ? member.getEffectiveName() : user.getUsername()) - .setWebhookAvatarUrl(member != null - ? member.getEffectiveServerAvatarUrl() - : user.getEffectiveAvatarUrl()); + .setWebhookUsername(username) // (member != null ? member.getEffectiveName() : user.getUsername()) + " [M]" + .setWebhookAvatarUrl( + member != null + ? member.getEffectiveServerAvatarUrl() + : user.getEffectiveAvatarUrl() + ); for (DiscordMessageEmbed embed : message.getEmbeds()) { builder.addEmbed(embed); } - return builder; + return builder.build(); } - private MessageReference getReference(ReceivedDiscordMessage message) { - return getReference(message.getChannel(), message.getId(), message.isWebhookMessage()); + private MessageReference getReference(ReceivedDiscordMessage message, OrDefault config) { + return getReference(message.getChannel(), message.getId(), message.isWebhookMessage(), config); } - private MessageReference getReference(DiscordMessageChannel channel, long messageId, boolean webhookMessage) { + private MessageReference getReference( + DiscordMessageChannel channel, + long messageId, + boolean webhookMessage, + OrDefault config + ) { if (channel instanceof DiscordTextChannel) { DiscordTextChannel textChannel = (DiscordTextChannel) channel; - return new MessageReference(textChannel, messageId, webhookMessage); + return new MessageReference(textChannel, messageId, webhookMessage, config); } else if (channel instanceof DiscordThreadChannel) { DiscordThreadChannel threadChannel = (DiscordThreadChannel) channel; - return new MessageReference(threadChannel, messageId, webhookMessage); + return new MessageReference(threadChannel, messageId, webhookMessage, config); } throw new IllegalStateException("Unexpected channel type: " + channel.getClass().getName()); } @@ -245,20 +236,38 @@ public class DiscordMessageMirroringModule extends AbstractModule { private final long threadId; private final long messageId; private final boolean webhookMessage; + private final OrDefault config; - public MessageReference(DiscordTextChannel textChannel, long messageId, boolean webhookMessage) { - this(textChannel.getId(), -1L, messageId, webhookMessage); + public MessageReference( + DiscordTextChannel textChannel, + long messageId, + boolean webhookMessage, + OrDefault config + ) { + this(textChannel.getId(), -1L, messageId, webhookMessage, config); } - public MessageReference(DiscordThreadChannel threadChannel, long messageId, boolean webhookMessage) { - this(threadChannel.getParentChannel().getId(), threadChannel.getId(), messageId, webhookMessage); + public MessageReference( + DiscordThreadChannel threadChannel, + long messageId, + boolean webhookMessage, + OrDefault config + ) { + this(threadChannel.getParentChannel().getId(), threadChannel.getId(), messageId, webhookMessage, config); } - public MessageReference(long channelId, long threadId, long messageId, boolean webhookMessage) { + public MessageReference( + long channelId, + long threadId, + long messageId, + boolean webhookMessage, + OrDefault config + ) { this.channelId = channelId; this.threadId = threadId; this.messageId = messageId; this.webhookMessage = webhookMessage; + this.config = config; } public DiscordMessageChannel getMessageChannel(DiscordSRV discordSRV) { diff --git a/common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java b/common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java index 3f71993f..82b5cce6 100644 --- a/common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java +++ b/common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java @@ -162,32 +162,14 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule ((CachedMention) mention).searchLength).reversed()) .forEachOrdered(mention -> channelMessagePlaceholders.replaceAll(mention.search, mention.mention)); - SendableDiscordMessage.Formatter discordMessage = format.toFormatter() + SendableDiscordMessage discordMessage = format.toFormatter() .addContext(context) .addReplacement("%message%", new FormattedText(channelMessagePlaceholders.toString())) - .applyPlaceholderService(); + .applyPlaceholderService() + .build(); - List text = new ArrayList<>(); - List thread = new ArrayList<>(); for (DiscordMessageChannel channel : entry.getValue()) { - if (channel instanceof DiscordTextChannel) { - text.add(channel); - } else if (channel instanceof DiscordThreadChannel) { - thread.add(channel); - } - } - - if (!text.isEmpty()) { - SendableDiscordMessage finalMessage = discordMessage.build(); - for (DiscordMessageChannel channel : text) { - futures.put(channel.sendMessage(finalMessage), channel); - } - } - if (!thread.isEmpty()) { - SendableDiscordMessage finalMessage = discordMessage.convertToNonWebhook().build(); - for (DiscordMessageChannel channel : thread) { - futures.put(channel.sendMessage(finalMessage), channel); - } + futures.put(channel.sendMessage(discordMessage), channel); } }