From 6c2c0c5134abfcf670e41375bda051bc2e72f4ec Mon Sep 17 00:00:00 2001 From: Vankka Date: Thu, 13 Jan 2022 19:03:23 +0200 Subject: [PATCH] Package reorganizing, and other minor changes --- .../message/SendableDiscordMessage.java | 6 + .../api/util/DiscordFormattingUtil.java | 2 +- .../events/DiscordMessageReceiveEvent.java | 41 ++++ .../ServerSwitchMessageForwardedEvent.java | 23 +++ .../game/ServerSwitchMessageReceiveEvent.java | 23 +++ .../component/util/PaperComponentUtil.java | 3 +- .../bukkit/player/BukkitPlayer.java | 24 +-- .../channels/ServerSwitchMessageConfig.java | 18 ++ .../channels/base/ProxyChannelConfig.java | 37 +++- .../config/manager/ProxyConfigManager.java | 24 ++- .../discordsrv/proxy/config/package-info.java | 18 ++ .../modules/ServerSwitchMessageModule.java | 20 +- .../channels/base/ServerChannelConfig.java | 17 +- .../config/manager/ServerConfigManager.java | 6 +- .../server/modules/DeathMessageModule.java | 2 +- .../discordsrv/common/AbstractDiscordSRV.java | 33 +-- .../common/channel/ChannelConfigHelper.java | 73 +++++-- .../GlobalChannelLookupModule.java | 3 +- .../renderer/DiscordSRVMinecraftRenderer.java | 10 +- .../main/channels/base/BaseChannelConfig.java | 12 +- .../main/channels/base/ChannelConfig.java | 53 +---- .../main/channels/base/IChannelConfig.java | 64 +++++- .../manager/ConfigurateConfigManager.java | 5 +- .../util/SendableDiscordMessageUtil.java | 2 +- .../connection/jda/JDAConnectionManager.java | 18 +- .../common/event/bus/EventBusImpl.java | 2 +- .../common/function/CheckedFunction.java | 25 +++ .../common/function/CheckedSupplier.java | 25 +++ .../integration/LuckPermsIntegration.java | 2 +- .../game/AbstractGameMessageModule.java | 194 ++++++++++++++++++ .../game}/JoinMessageModule.java | 2 +- .../game}/LeaveMessageModule.java | 2 +- .../game}/MinecraftToDiscordChatModule.java | 55 +++-- .../message/AbstractGameMessageModule.java | 143 ------------- 34 files changed, 693 insertions(+), 294 deletions(-) create mode 100644 api/src/main/java/com/discordsrv/api/discord/events/DiscordMessageReceiveEvent.java rename common/src/main/java/com/discordsrv/common/{module/modules => channel}/GlobalChannelLookupModule.java (94%) rename common/src/main/java/com/discordsrv/common/discord/api/{ => entity}/message/util/SendableDiscordMessageUtil.java (98%) create mode 100644 common/src/main/java/com/discordsrv/common/function/CheckedFunction.java create mode 100644 common/src/main/java/com/discordsrv/common/function/CheckedSupplier.java rename common/src/main/java/com/discordsrv/common/{module/modules => }/integration/LuckPermsIntegration.java (97%) create mode 100644 common/src/main/java/com/discordsrv/common/messageforwarding/game/AbstractGameMessageModule.java rename common/src/main/java/com/discordsrv/common/{module/modules/message => messageforwarding/game}/JoinMessageModule.java (97%) rename common/src/main/java/com/discordsrv/common/{module/modules/message => messageforwarding/game}/LeaveMessageModule.java (97%) rename common/src/main/java/com/discordsrv/common/{module/modules/message => messageforwarding/game}/MinecraftToDiscordChatModule.java (85%) delete mode 100644 common/src/main/java/com/discordsrv/common/module/modules/message/AbstractGameMessageModule.java diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/SendableDiscordMessage.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/SendableDiscordMessage.java index 69d75ee8..8aa563b0 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/SendableDiscordMessage.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/SendableDiscordMessage.java @@ -188,6 +188,9 @@ public interface SendableDiscordMessage { @NotNull Builder setWebhookAvatarUrl(String webhookAvatarUrl); + @NotNull + Builder convertToNonWebhook(); + /** * Builds a {@link SendableDiscordMessage} from this builder. * @return the new {@link SendableDiscordMessage} @@ -252,6 +255,9 @@ public interface SendableDiscordMessage { @NotNull Formatter applyPlaceholderService(); + @NotNull + Formatter convertToNonWebhook(); + @NotNull SendableDiscordMessage build(); } diff --git a/api/src/main/java/com/discordsrv/api/discord/api/util/DiscordFormattingUtil.java b/api/src/main/java/com/discordsrv/api/discord/api/util/DiscordFormattingUtil.java index 1fb68db0..c4b96add 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/util/DiscordFormattingUtil.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/util/DiscordFormattingUtil.java @@ -30,7 +30,7 @@ public final class DiscordFormattingUtil { private DiscordFormattingUtil() {} public static String escapeContent(String content) { - content = escapeChars(content, '*', '_', '|', '`', '~'); + content = escapeChars(content, '*', '_', '|', '`', '~', ':'); content = escapeQuote(content); content = escapeMentions(content); return content; diff --git a/api/src/main/java/com/discordsrv/api/discord/events/DiscordMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/discord/events/DiscordMessageReceiveEvent.java new file mode 100644 index 00000000..db5be911 --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/discord/events/DiscordMessageReceiveEvent.java @@ -0,0 +1,41 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2021 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.events; + +import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel; +import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; + +public class DiscordMessageReceiveEvent extends AbstractDiscordMessageEvent { + + private final ReceivedDiscordMessage message; + + public DiscordMessageReceiveEvent(DiscordMessageChannel channel, ReceivedDiscordMessage message) { + super(channel); + this.message = message; + } + + public ReceivedDiscordMessage getMessage() { + return message; + } +} diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ServerSwitchMessageForwardedEvent.java b/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ServerSwitchMessageForwardedEvent.java index b5f9d587..900449e1 100644 --- a/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ServerSwitchMessageForwardedEvent.java +++ b/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ServerSwitchMessageForwardedEvent.java @@ -1,3 +1,26 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2021 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.event.events.message.forward.game; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/receive/game/ServerSwitchMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/event/events/message/receive/game/ServerSwitchMessageReceiveEvent.java index f943735f..ed80519d 100644 --- a/api/src/main/java/com/discordsrv/api/event/events/message/receive/game/ServerSwitchMessageReceiveEvent.java +++ b/api/src/main/java/com/discordsrv/api/event/events/message/receive/game/ServerSwitchMessageReceiveEvent.java @@ -1,3 +1,26 @@ +/* + * This file is part of the DiscordSRV API, licensed under the MIT License + * Copyright (c) 2016-2021 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.event.events.message.receive.game; import com.discordsrv.api.component.MinecraftComponent; diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java b/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java index e48cf051..d4a51419 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/component/util/PaperComponentUtil.java @@ -22,7 +22,6 @@ import com.discordsrv.api.component.MinecraftComponent; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.component.util.ComponentUtil; import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer; -import org.bukkit.event.Event; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -43,7 +42,7 @@ public final class PaperComponentUtil { private PaperComponentUtil() {} - public static MinecraftComponent getComponent( + public static MinecraftComponent getComponent( DiscordSRV discordSRV, T source, String methodName, Function legacy) { if (!IS_PAPER_ADVENTURE) { return getLegacy(legacy.apply(source)); diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayer.java b/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayer.java index 76c49e2a..c4f37c97 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayer.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/player/BukkitPlayer.java @@ -19,6 +19,7 @@ package com.discordsrv.bukkit.player; import com.discordsrv.bukkit.BukkitDiscordSRV; +import com.discordsrv.bukkit.component.util.PaperComponentUtil; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.player.IPlayer; @@ -29,21 +30,9 @@ import net.kyori.adventure.text.Component; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Method; - @SuppressWarnings("NullableProblems") // BukkitOfflinePlayer nullability public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer { - private static final Method DISPLAY_NAME_METHOD; // Paper 1.16+ - - static { - Method displayNameMethod = null; - try { - displayNameMethod = Player.class.getMethod("displayName"); - } catch (Throwable ignored) {} - DISPLAY_NAME_METHOD = displayNameMethod; - } - private final Player player; private final Audience audience; @@ -83,13 +72,8 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer { @SuppressWarnings("deprecation") // Paper @Override public @NotNull Component displayName() { - if (DISPLAY_NAME_METHOD != null) { - try { - return ComponentUtil.fromUnrelocated(DISPLAY_NAME_METHOD.invoke(player)); - } catch (Throwable ignored) {} - } - - // Use the legacy method - return BukkitComponentSerializer.legacy().deserialize(player.getDisplayName()); + return ComponentUtil.fromAPI( + PaperComponentUtil.getComponent(discordSRV, player, "displayName", Player::getDisplayName) + ); } } diff --git a/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/ServerSwitchMessageConfig.java b/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/ServerSwitchMessageConfig.java index 412de1a1..f65738e6 100644 --- a/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/ServerSwitchMessageConfig.java +++ b/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/ServerSwitchMessageConfig.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.proxy.config.channels; import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed; diff --git a/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/base/ProxyChannelConfig.java b/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/base/ProxyChannelConfig.java index 353d2cc3..6594ecef 100644 --- a/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/base/ProxyChannelConfig.java +++ b/common/proxy/src/main/java/com/discordsrv/proxy/config/channels/base/ProxyChannelConfig.java @@ -1,22 +1,53 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.proxy.config.channels.base; import com.discordsrv.common.config.main.channels.base.IChannelConfig; +import com.discordsrv.common.config.main.channels.base.ThreadConfig; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; -import java.util.ArrayList; import java.util.List; +@ConfigSerializable public class ProxyChannelConfig extends ProxyBaseChannelConfig implements IChannelConfig { public ProxyChannelConfig() { initialize(); } + @Setting(CHANNEL_IDS_OPTION_NAME) @Comment(CHANNEL_IDS_COMMENT) - public List channelIds = new ArrayList<>(); + public List channelIds = CHANNEL_IDS_VALUE; @Override - public List ids() { + public List channelIds() { return channelIds; } + + @Setting(THREADS_OPTION_NAME) + @Comment(THREADS_COMMENT) + public List threads = THREADS_VALUE; + + @Override + public List threads() { + return threads; + } } diff --git a/common/proxy/src/main/java/com/discordsrv/proxy/config/manager/ProxyConfigManager.java b/common/proxy/src/main/java/com/discordsrv/proxy/config/manager/ProxyConfigManager.java index 8a359bf2..77bdf50b 100644 --- a/common/proxy/src/main/java/com/discordsrv/proxy/config/manager/ProxyConfigManager.java +++ b/common/proxy/src/main/java/com/discordsrv/proxy/config/manager/ProxyConfigManager.java @@ -1,8 +1,26 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.proxy.config.manager; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.MainConfig; -import com.discordsrv.common.config.main.channels.base.ChannelConfig; +import com.discordsrv.common.config.main.channels.base.IChannelConfig; import com.discordsrv.common.config.manager.MainConfigManager; import com.discordsrv.proxy.config.channels.base.ProxyBaseChannelConfig; import com.discordsrv.proxy.config.channels.base.ProxyChannelConfig; @@ -15,7 +33,7 @@ public abstract class ProxyConfigManager extends MainConfi } @Override - public ChannelConfig.Serializer getChannelConfigSerializer(ObjectMapper.Factory mapperFactory) { - return new ChannelConfig.Serializer(mapperFactory, ProxyBaseChannelConfig.class, ProxyChannelConfig.class); + public IChannelConfig.Serializer getChannelConfigSerializer(ObjectMapper.Factory mapperFactory) { + return new IChannelConfig.Serializer(mapperFactory, ProxyBaseChannelConfig.class, ProxyChannelConfig.class); } } diff --git a/common/proxy/src/main/java/com/discordsrv/proxy/config/package-info.java b/common/proxy/src/main/java/com/discordsrv/proxy/config/package-info.java index a90e56f8..c4bbb8fd 100644 --- a/common/proxy/src/main/java/com/discordsrv/proxy/config/package-info.java +++ b/common/proxy/src/main/java/com/discordsrv/proxy/config/package-info.java @@ -1 +1,19 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.proxy.config; diff --git a/common/proxy/src/main/java/com/discordsrv/proxy/modules/ServerSwitchMessageModule.java b/common/proxy/src/main/java/com/discordsrv/proxy/modules/ServerSwitchMessageModule.java index d37297f4..2c837cf0 100644 --- a/common/proxy/src/main/java/com/discordsrv/proxy/modules/ServerSwitchMessageModule.java +++ b/common/proxy/src/main/java/com/discordsrv/proxy/modules/ServerSwitchMessageModule.java @@ -1,3 +1,21 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.proxy.modules; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; @@ -9,7 +27,7 @@ import com.discordsrv.api.event.events.message.receive.game.ServerSwitchMessageR import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.channels.base.BaseChannelConfig; import com.discordsrv.common.function.OrDefault; -import com.discordsrv.common.module.modules.message.AbstractGameMessageModule; +import com.discordsrv.common.messageforwarding.game.AbstractGameMessageModule; import com.discordsrv.proxy.config.channels.ServerSwitchMessageConfig; import com.discordsrv.proxy.config.channels.base.ProxyBaseChannelConfig; diff --git a/common/server/src/main/java/com/discordsrv/common/server/config/channels/base/ServerChannelConfig.java b/common/server/src/main/java/com/discordsrv/common/server/config/channels/base/ServerChannelConfig.java index 40f4db58..0c8620e9 100644 --- a/common/server/src/main/java/com/discordsrv/common/server/config/channels/base/ServerChannelConfig.java +++ b/common/server/src/main/java/com/discordsrv/common/server/config/channels/base/ServerChannelConfig.java @@ -19,10 +19,11 @@ package com.discordsrv.common.server.config.channels.base; import com.discordsrv.common.config.main.channels.base.IChannelConfig; +import com.discordsrv.common.config.main.channels.base.ThreadConfig; import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; -import java.util.ArrayList; import java.util.List; @ConfigSerializable @@ -32,11 +33,21 @@ public class ServerChannelConfig extends ServerBaseChannelConfig implements ICha initialize(); } + @Setting(CHANNEL_IDS_OPTION_NAME) @Comment(CHANNEL_IDS_COMMENT) - public List channelIds = new ArrayList<>(); + public List channelIds = CHANNEL_IDS_VALUE; @Override - public List ids() { + public List channelIds() { return channelIds; } + + @Setting(THREADS_OPTION_NAME) + @Comment(THREADS_COMMENT) + public List threads = THREADS_VALUE; + + @Override + public List threads() { + return threads; + } } diff --git a/common/server/src/main/java/com/discordsrv/common/server/config/manager/ServerConfigManager.java b/common/server/src/main/java/com/discordsrv/common/server/config/manager/ServerConfigManager.java index f3ab5995..8367d43d 100644 --- a/common/server/src/main/java/com/discordsrv/common/server/config/manager/ServerConfigManager.java +++ b/common/server/src/main/java/com/discordsrv/common/server/config/manager/ServerConfigManager.java @@ -20,7 +20,7 @@ package com.discordsrv.common.server.config.manager; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.MainConfig; -import com.discordsrv.common.config.main.channels.base.ChannelConfig; +import com.discordsrv.common.config.main.channels.base.IChannelConfig; import com.discordsrv.common.config.manager.MainConfigManager; import com.discordsrv.common.server.config.channels.base.ServerBaseChannelConfig; import com.discordsrv.common.server.config.channels.base.ServerChannelConfig; @@ -33,7 +33,7 @@ public abstract class ServerConfigManager extends MainConf } @Override - public ChannelConfig.Serializer getChannelConfigSerializer(ObjectMapper.Factory mapperFactory) { - return new ChannelConfig.Serializer(mapperFactory, ServerBaseChannelConfig.class, ServerChannelConfig.class); + public IChannelConfig.Serializer getChannelConfigSerializer(ObjectMapper.Factory mapperFactory) { + return new IChannelConfig.Serializer(mapperFactory, ServerBaseChannelConfig.class, ServerChannelConfig.class); } } diff --git a/common/server/src/main/java/com/discordsrv/common/server/modules/DeathMessageModule.java b/common/server/src/main/java/com/discordsrv/common/server/modules/DeathMessageModule.java index b13bd6f8..f6e4efd5 100644 --- a/common/server/src/main/java/com/discordsrv/common/server/modules/DeathMessageModule.java +++ b/common/server/src/main/java/com/discordsrv/common/server/modules/DeathMessageModule.java @@ -28,7 +28,7 @@ import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.main.channels.base.BaseChannelConfig; import com.discordsrv.common.server.config.channels.DeathMessageConfig; import com.discordsrv.common.function.OrDefault; -import com.discordsrv.common.module.modules.message.AbstractGameMessageModule; +import com.discordsrv.common.messageforwarding.game.AbstractGameMessageModule; import com.discordsrv.common.server.config.channels.base.ServerBaseChannelConfig; public class DeathMessageModule extends AbstractGameMessageModule { diff --git a/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java b/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java index e600ae76..7e49f4d0 100644 --- a/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java +++ b/common/src/main/java/com/discordsrv/common/AbstractDiscordSRV.java @@ -22,8 +22,11 @@ import com.discordsrv.api.discord.connection.DiscordConnectionDetails; import com.discordsrv.api.event.bus.EventBus; import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent; import com.discordsrv.api.event.events.lifecycle.DiscordSRVShuttingDownEvent; +import com.discordsrv.common.discord.api.DiscordAPIEventModule; import com.discordsrv.common.api.util.ApiInstanceUtil; import com.discordsrv.common.channel.ChannelConfigHelper; +import com.discordsrv.common.channel.ChannelUpdaterModule; +import com.discordsrv.common.channel.GlobalChannelLookupModule; import com.discordsrv.common.component.ComponentFactory; import com.discordsrv.common.config.connection.ConnectionConfig; import com.discordsrv.common.config.main.MainConfig; @@ -35,22 +38,21 @@ import com.discordsrv.common.discord.connection.jda.JDAConnectionManager; import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl; import com.discordsrv.common.event.bus.EventBusImpl; import com.discordsrv.common.function.CheckedRunnable; +import com.discordsrv.common.integration.LuckPermsIntegration; +import com.discordsrv.common.logging.adapter.DependencyLoggerAdapter; import com.discordsrv.common.logging.dependency.DependencyLoggingHandler; -import com.discordsrv.common.module.modules.message.JoinMessageModule; -import com.discordsrv.common.module.modules.message.LeaveMessageModule; -import com.discordsrv.common.module.type.AbstractModule; -import com.discordsrv.common.module.type.Module; +import com.discordsrv.common.messageforwarding.discord.DiscordChatMessageModule; +import com.discordsrv.common.messageforwarding.discord.DiscordMessageMirroringModule; +import com.discordsrv.common.messageforwarding.game.JoinMessageModule; +import com.discordsrv.common.messageforwarding.game.LeaveMessageModule; +import com.discordsrv.common.messageforwarding.game.MinecraftToDiscordChatModule; import com.discordsrv.common.module.ModuleInitializationFunction; import com.discordsrv.common.module.ModuleManager; -import com.discordsrv.common.module.modules.DiscordAPIEventModule; -import com.discordsrv.common.module.modules.message.DiscordToMinecraftChatModule; -import com.discordsrv.common.module.modules.GlobalChannelLookupModule; -import com.discordsrv.common.module.modules.message.MinecraftToDiscordChatModule; -import com.discordsrv.common.module.modules.integration.LuckPermsIntegration; +import com.discordsrv.common.module.type.AbstractModule; +import com.discordsrv.common.module.type.Module; import com.discordsrv.common.placeholder.ComponentResultStringifier; import com.discordsrv.common.placeholder.PlaceholderServiceImpl; import com.discordsrv.common.placeholder.context.GlobalTextHandlingContext; -import com.discordsrv.common.logging.adapter.DependencyLoggerAdapter; import net.dv8tion.jda.api.JDA; import org.jetbrains.annotations.NotNull; @@ -269,14 +271,19 @@ public abstract class AbstractDiscordSRV nameToChannelCache; - private final Map> discordToConfigMap; + private final Map> textChannelToConfigMap; + private final LoadingCache, Map> threadToConfigCache; public ChannelConfigHelper(DiscordSRV discordSRV) { this.discordSRV = discordSRV; @@ -62,7 +67,33 @@ public class ChannelConfigHelper { return event.getChannelFromProcessing(); } }); - this.discordToConfigMap = new ConcurrentHashMap<>(); + this.textChannelToConfigMap = new ConcurrentHashMap<>(); + this.threadToConfigCache = discordSRV.caffeineBuilder() + .expireAfterWrite(60, TimeUnit.SECONDS) + .expireAfterAccess(30, TimeUnit.SECONDS) + .refreshAfterWrite(10, TimeUnit.SECONDS) + .build(key -> { + Map map = new HashMap<>(); + for (Map.Entry entry : channels().entrySet()) { + String channelName = entry.getKey(); + BaseChannelConfig value = entry.getValue(); + if (value instanceof IChannelConfig) { + IChannelConfig channelConfig = (IChannelConfig) value; + List threads = channelConfig.threads(); + if (threads == null) { + continue; + } + + for (ThreadConfig thread : threads) { + if (Objects.equals(thread.channelId, key.getKey()) + && Objects.equals(thread.threadName, key.getValue())) { + map.put(channelName, value); + } + } + } + } + return map; + }); discordSRV.eventBus().subscribe(this); } @@ -79,16 +110,21 @@ public class ChannelConfigHelper { BaseChannelConfig value = entry.getValue(); if (value instanceof IChannelConfig) { IChannelConfig channelConfig = (IChannelConfig) value; - for (long channelId : channelConfig.ids()) { + List channelIds = channelConfig.channelIds(); + if (channelIds == null) { + continue; + } + + for (long channelId : channelIds) { newMap.computeIfAbsent(channelId, key -> new LinkedHashMap<>()) .put(channelName, value); } } } - synchronized (discordToConfigMap) { - discordToConfigMap.clear(); - discordToConfigMap.putAll(newMap); + synchronized (textChannelToConfigMap) { + textChannelToConfigMap.clear(); + textChannelToConfigMap.putAll(newMap); } } @@ -149,11 +185,11 @@ public class ChannelConfigHelper { return gameChannel != null ? get(gameChannel) : null; } - public Map> orDefault(DiscordTextChannel discordTextChannel) { + public Map> orDefault(DiscordMessageChannel messageChannel) { BaseChannelConfig defaultConfig = getDefault(); Map> channels = new HashMap<>(); - for (Map.Entry entry : getDiscordResolved(discordTextChannel).entrySet()) { + for (Map.Entry entry : getDiscordResolved(messageChannel).entrySet()) { channels.put( entry.getKey(), new OrDefault<>(entry.getValue(), defaultConfig) @@ -162,9 +198,14 @@ public class ChannelConfigHelper { return channels; } - public Map getDiscordResolved(DiscordTextChannel channel) { - Map pairs = getDiscord(channel); - if (pairs == null) { + public Map getDiscordResolved(DiscordMessageChannel channel) { + Map pairs = null; + if (channel instanceof DiscordTextChannel) { + pairs = getText((DiscordTextChannel) channel); + } else if (channel instanceof DiscordThreadChannel) { + pairs = getThread((DiscordThreadChannel) channel); + } + if (pairs == null || pairs.isEmpty()) { return Collections.emptyMap(); } @@ -181,9 +222,11 @@ public class ChannelConfigHelper { return channels; } - public Map getDiscord(DiscordTextChannel channel) { - synchronized (discordToConfigMap) { - return discordToConfigMap.get(channel.getId()); - } + public Map getText(DiscordTextChannel channel) { + return textChannelToConfigMap.get(channel.getId()); + } + + public Map getThread(DiscordThreadChannel channel) { + return threadToConfigCache.get(Pair.of(channel.getParentChannel().getId(), channel.getName())); } } diff --git a/common/src/main/java/com/discordsrv/common/module/modules/GlobalChannelLookupModule.java b/common/src/main/java/com/discordsrv/common/channel/GlobalChannelLookupModule.java similarity index 94% rename from common/src/main/java/com/discordsrv/common/module/modules/GlobalChannelLookupModule.java rename to common/src/main/java/com/discordsrv/common/channel/GlobalChannelLookupModule.java index c7ff5016..a1373361 100644 --- a/common/src/main/java/com/discordsrv/common/module/modules/GlobalChannelLookupModule.java +++ b/common/src/main/java/com/discordsrv/common/channel/GlobalChannelLookupModule.java @@ -16,13 +16,12 @@ * along with this program. If not, see . */ -package com.discordsrv.common.module.modules; +package com.discordsrv.common.channel; import com.discordsrv.api.event.bus.EventPriority; import com.discordsrv.api.event.bus.Subscribe; import com.discordsrv.api.event.events.channel.GameChannelLookupEvent; import com.discordsrv.common.DiscordSRV; -import com.discordsrv.common.channel.DefaultGlobalChannel; import com.discordsrv.common.module.type.AbstractModule; public class GlobalChannelLookupModule extends AbstractModule { diff --git a/common/src/main/java/com/discordsrv/common/component/renderer/DiscordSRVMinecraftRenderer.java b/common/src/main/java/com/discordsrv/common/component/renderer/DiscordSRVMinecraftRenderer.java index 470d9315..a43f4a75 100644 --- a/common/src/main/java/com/discordsrv/common/component/renderer/DiscordSRVMinecraftRenderer.java +++ b/common/src/main/java/com/discordsrv/common/component/renderer/DiscordSRVMinecraftRenderer.java @@ -29,7 +29,6 @@ import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig; import com.discordsrv.common.function.OrDefault; import dev.vankka.mcdiscordreserializer.renderer.implementation.DefaultMinecraftRenderer; -import lombok.NonNull; import net.dv8tion.jda.api.entities.GuildChannel; import net.dv8tion.jda.api.utils.MiscUtil; import net.kyori.adventure.text.Component; @@ -62,14 +61,15 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer { OrDefault config, Supplier supplier ) { + Context oldValue = CONTEXT.get(); CONTEXT.set(new Context(event, config)); T output = supplier.get(); - CONTEXT.remove(); + CONTEXT.set(oldValue); return output; } @Override - public @NotNull Component appendChannelMention(@NonNull Component component, @NonNull String id) { + public @NotNull Component appendChannelMention(@NotNull Component component, @NotNull String id) { Context context = CONTEXT.get(); DiscordToMinecraftChatConfig.Mentions.Format format = context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.channel) : null; @@ -91,7 +91,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer { } @Override - public @NotNull Component appendUserMention(@NonNull Component component, @NonNull String id) { + public @NotNull Component appendUserMention(@NotNull Component component, @NotNull String id) { Context context = CONTEXT.get(); DiscordToMinecraftChatConfig.Mentions.Format format = context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.user) : null; @@ -124,7 +124,7 @@ public class DiscordSRVMinecraftRenderer extends DefaultMinecraftRenderer { } @Override - public @NotNull Component appendRoleMention(@NonNull Component component, @NonNull String id) { + public @NotNull Component appendRoleMention(@NotNull Component component, @NotNull String id) { Context context = CONTEXT.get(); DiscordToMinecraftChatConfig.Mentions.Format format = context != null ? context.config.map(cfg -> cfg.mentions).get(cfg -> cfg.role) : null; diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/base/BaseChannelConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/base/BaseChannelConfig.java index 9b3cefec..14dc64e6 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/channels/base/BaseChannelConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/base/BaseChannelConfig.java @@ -20,11 +20,9 @@ package com.discordsrv.common.config.main.channels.base; import com.discordsrv.common.config.annotation.Order; import com.discordsrv.common.config.annotation.Untranslated; -import com.discordsrv.common.config.main.channels.DiscordToMinecraftChatConfig; -import com.discordsrv.common.config.main.channels.JoinMessageConfig; -import com.discordsrv.common.config.main.channels.LeaveMessageConfig; -import com.discordsrv.common.config.main.channels.MinecraftToDiscordChatConfig; +import com.discordsrv.common.config.main.channels.*; import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; @ConfigSerializable public class BaseChannelConfig { @@ -34,7 +32,11 @@ public class BaseChannelConfig { public JoinMessageConfig joinMessages = new JoinMessageConfig(); public LeaveMessageConfig leaveMessages = new LeaveMessageConfig(); + @Order(10) + @Comment("Settings for synchronizing messages between the defined Discord channels and threads") + public MirroringConfig mirroring = new MirroringConfig(); + @Untranslated(Untranslated.Type.VALUE) - @Order(5) + @Order(50) public String avatarUrlProvider = "https://heads.discordsrv.com/head.png?texture=%texture%&uuid=%uuid%&name=%username%&overlay"; } diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/base/ChannelConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/base/ChannelConfig.java index 48904840..6bae808b 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/channels/base/ChannelConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/base/ChannelConfig.java @@ -18,16 +18,10 @@ package com.discordsrv.common.config.main.channels.base; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.spongepowered.configurate.ConfigurationNode; import org.spongepowered.configurate.objectmapping.ConfigSerializable; -import org.spongepowered.configurate.objectmapping.ObjectMapper; import org.spongepowered.configurate.objectmapping.meta.Comment; -import org.spongepowered.configurate.serialize.SerializationException; -import org.spongepowered.configurate.serialize.TypeSerializer; +import org.spongepowered.configurate.objectmapping.meta.Setting; -import java.lang.reflect.Type; -import java.util.ArrayList; import java.util.List; @ConfigSerializable @@ -37,47 +31,22 @@ public class ChannelConfig extends BaseChannelConfig implements IChannelConfig { initialize(); } + @Setting(CHANNEL_IDS_OPTION_NAME) @Comment(CHANNEL_IDS_COMMENT) - public List channelIds = new ArrayList<>(); + public List channelIds = CHANNEL_IDS_VALUE; @Override - public List ids() { + public List channelIds() { return channelIds; } - public static class Serializer implements TypeSerializer { + @Setting(THREADS_OPTION_NAME) + @Comment(THREADS_COMMENT) + public List threads = THREADS_VALUE; - private final ObjectMapper.Factory mapperFactory; - private final Class baseConfigClass; - private final Class configClass; - - public Serializer(ObjectMapper.Factory mapperFactory, Class baseConfigClass, Class configClass) { - this.mapperFactory = mapperFactory; - this.baseConfigClass = baseConfigClass; - this.configClass = configClass; - } - - @Override - public BaseChannelConfig deserialize(Type type, ConfigurationNode node) throws SerializationException { - return (BaseChannelConfig) mapperFactory.asTypeSerializer() - .deserialize( - ChannelConfig.DEFAULT_KEY.equals(node.key()) ? baseConfigClass : configClass, - node - ); - } - - @Override - public void serialize(Type type, @Nullable BaseChannelConfig obj, ConfigurationNode node) throws SerializationException { - if (obj == null) { - node.set(null); - return; - } - - mapperFactory.asTypeSerializer().serialize( - ChannelConfig.DEFAULT_KEY.equals(node.key()) ? baseConfigClass : configClass, - obj, - node - ); - } + @Override + public List threads() { + return threads; } + } diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/base/IChannelConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/base/IChannelConfig.java index 192e5b67..7f6053c0 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/channels/base/IChannelConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/base/IChannelConfig.java @@ -18,14 +18,38 @@ package com.discordsrv.common.config.main.channels.base; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.objectmapping.ObjectMapper; +import org.spongepowered.configurate.objectmapping.meta.Setting; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.serialize.TypeSerializer; + import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; public interface IChannelConfig { String DEFAULT_KEY = "default"; + + String CHANNEL_IDS_OPTION_NAME = "channelIds"; String CHANNEL_IDS_COMMENT = "The channels this in-game channel will forward to in Discord"; + List CHANNEL_IDS_VALUE = new ArrayList<>(); + + List channelIds(); + + String THREADS_OPTION_NAME = "threads"; + String THREADS_COMMENT = "The threads that this in-game channel will forward to in Discord (this can be used instead of or with the channel-ids option)"; + List THREADS_VALUE = new ArrayList<>(Collections.singletonList(new ThreadConfig())); + + List VALUES = Arrays.asList(CHANNEL_IDS_OPTION_NAME, THREADS_OPTION_NAME); + + List threads(); default void initialize() { // Clear everything besides channelIds by default (these will be filled back in by Configurate if they are in the config itself) @@ -36,7 +60,9 @@ public interface IChannelConfig { if (!Modifier.isPublic(modifiers) || Modifier.isFinal(modifiers) || Modifier.isStatic(modifiers)) { continue; } - if (field.getName().equals("channelIds")) { + + Setting setting = field.getAnnotation(Setting.class); + if (setting != null && VALUES.contains(setting.value())) { continue; } @@ -48,5 +74,39 @@ public interface IChannelConfig { } } - List ids(); + class Serializer implements TypeSerializer { + + private final ObjectMapper.Factory mapperFactory; + private final Class baseConfigClass; + private final Class configClass; + + public Serializer(ObjectMapper.Factory mapperFactory, Class baseConfigClass, Class configClass) { + this.mapperFactory = mapperFactory; + this.baseConfigClass = baseConfigClass; + this.configClass = configClass; + } + + @Override + public BaseChannelConfig deserialize(Type type, ConfigurationNode node) throws SerializationException { + return (BaseChannelConfig) mapperFactory.asTypeSerializer() + .deserialize( + ChannelConfig.DEFAULT_KEY.equals(node.key()) ? baseConfigClass : configClass, + node + ); + } + + @Override + public void serialize(Type type, @Nullable BaseChannelConfig obj, ConfigurationNode node) throws SerializationException { + if (obj == null) { + node.set(null); + return; + } + + mapperFactory.asTypeSerializer().serialize( + ChannelConfig.DEFAULT_KEY.equals(node.key()) ? baseConfigClass : configClass, + obj, + node + ); + } + } } diff --git a/common/src/main/java/com/discordsrv/common/config/manager/manager/ConfigurateConfigManager.java b/common/src/main/java/com/discordsrv/common/config/manager/manager/ConfigurateConfigManager.java index 9298f4ae..b507cbe6 100644 --- a/common/src/main/java/com/discordsrv/common/config/manager/manager/ConfigurateConfigManager.java +++ b/common/src/main/java/com/discordsrv/common/config/manager/manager/ConfigurateConfigManager.java @@ -27,6 +27,7 @@ import com.discordsrv.common.config.annotation.Order; import com.discordsrv.common.config.fielddiscoverer.OrderedFieldDiscovererProxy; import com.discordsrv.common.config.main.channels.base.BaseChannelConfig; import com.discordsrv.common.config.main.channels.base.ChannelConfig; +import com.discordsrv.common.config.main.channels.base.IChannelConfig; import com.discordsrv.common.config.manager.loader.ConfigLoaderProvider; import com.discordsrv.common.config.serializer.ColorSerializer; import com.discordsrv.common.config.serializer.DiscordMessageEmbedSerializer; @@ -93,8 +94,8 @@ public abstract class ConfigurateConfigManager. */ -package com.discordsrv.common.discord.api.message.util; +package com.discordsrv.common.discord.api.entity.message.util; import club.minnced.discord.webhook.send.WebhookMessage; import club.minnced.discord.webhook.send.WebhookMessageBuilder; diff --git a/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java b/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java index 38663d6d..dab7c00b 100644 --- a/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java +++ b/common/src/main/java/com/discordsrv/common/discord/connection/jda/JDAConnectionManager.java @@ -27,13 +27,13 @@ import com.discordsrv.api.event.events.placeholder.PlaceholderLookupEvent; import com.discordsrv.api.placeholder.PlaceholderLookupResult; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.config.connection.ConnectionConfig; -import com.discordsrv.common.discord.api.channel.DiscordDMChannelImpl; -import com.discordsrv.common.discord.api.channel.DiscordTextChannelImpl; -import com.discordsrv.common.discord.api.guild.DiscordGuildImpl; -import com.discordsrv.common.discord.api.guild.DiscordGuildMemberImpl; -import com.discordsrv.common.discord.api.guild.DiscordRoleImpl; -import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; -import com.discordsrv.common.discord.api.DiscordUserImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordDMChannelImpl; +import com.discordsrv.common.discord.api.entity.channel.DiscordTextChannelImpl; +import com.discordsrv.common.discord.api.entity.guild.DiscordGuildImpl; +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; +import com.discordsrv.common.discord.api.entity.DiscordUserImpl; import com.discordsrv.common.discord.connection.DiscordConnectionManager; import com.discordsrv.common.scheduler.Scheduler; import com.discordsrv.common.scheduler.threadfactory.CountingThreadFactory; @@ -239,9 +239,9 @@ public class JDAConnectionManager implements DiscordConnectionManager { jdaBuilder.setMemberCachePolicy(membersIntent ? MemberCachePolicy.ALL : MemberCachePolicy.OWNER); jdaBuilder.setChunkingFilter(membersIntent ? ChunkingFilter.ALL : ChunkingFilter.NONE); - jdaBuilder.setEventManager(new EventManagerProxy(new JDAEventManager(discordSRV), discordSRV.scheduler().forkExecutor())); + jdaBuilder.setEventManager(new EventManagerProxy(new JDAEventManager(discordSRV), discordSRV.scheduler().forkJoinPool())); - jdaBuilder.setCallbackPool(discordSRV.scheduler().forkExecutor()); + jdaBuilder.setCallbackPool(discordSRV.scheduler().forkJoinPool()); jdaBuilder.setGatewayPool(gatewayPool); jdaBuilder.setRateLimitPool(rateLimitPool); diff --git a/common/src/main/java/com/discordsrv/common/event/bus/EventBusImpl.java b/common/src/main/java/com/discordsrv/common/event/bus/EventBusImpl.java index 959e7f2c..e69bbf69 100644 --- a/common/src/main/java/com/discordsrv/common/event/bus/EventBusImpl.java +++ b/common/src/main/java/com/discordsrv/common/event/bus/EventBusImpl.java @@ -195,7 +195,7 @@ public class EventBusImpl implements EventBus { } catch (IllegalAccessException e) { discordSRV.logger().error("Failed to access API listener method: " + eventListener.methodName(), e); } catch (InvocationTargetException e) { - discordSRV.logger().error("Failed to pass " + event.getClass().getSimpleName() + " to " + eventListener, e); + discordSRV.logger().error("Failed to pass " + event.getClass().getSimpleName() + " to " + eventListener, e.getCause()); } long timeTaken = System.currentTimeMillis() - startTime; discordSRV.logger().trace(eventListener + " took " + timeTaken + "ms to execute"); diff --git a/common/src/main/java/com/discordsrv/common/function/CheckedFunction.java b/common/src/main/java/com/discordsrv/common/function/CheckedFunction.java new file mode 100644 index 00000000..addc94c4 --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/function/CheckedFunction.java @@ -0,0 +1,25 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.function; + +@FunctionalInterface +public interface CheckedFunction { + + O apply(I input) throws Throwable; +} diff --git a/common/src/main/java/com/discordsrv/common/function/CheckedSupplier.java b/common/src/main/java/com/discordsrv/common/function/CheckedSupplier.java new file mode 100644 index 00000000..8b5ecf4a --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/function/CheckedSupplier.java @@ -0,0 +1,25 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.function; + +@FunctionalInterface +public interface CheckedSupplier { + + O get() throws Throwable; +} diff --git a/common/src/main/java/com/discordsrv/common/module/modules/integration/LuckPermsIntegration.java b/common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java similarity index 97% rename from common/src/main/java/com/discordsrv/common/module/modules/integration/LuckPermsIntegration.java rename to common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java index 40232d26..98ee06fb 100644 --- a/common/src/main/java/com/discordsrv/common/module/modules/integration/LuckPermsIntegration.java +++ b/common/src/main/java/com/discordsrv/common/integration/LuckPermsIntegration.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.module.modules.integration; +package com.discordsrv.common.integration; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.module.type.PermissionDataProvider; diff --git a/common/src/main/java/com/discordsrv/common/messageforwarding/game/AbstractGameMessageModule.java b/common/src/main/java/com/discordsrv/common/messageforwarding/game/AbstractGameMessageModule.java new file mode 100644 index 00000000..feeee5e4 --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/messageforwarding/game/AbstractGameMessageModule.java @@ -0,0 +1,194 @@ +/* + * This file is part of DiscordSRV, licensed under the GPLv3 License + * Copyright (c) 2016-2021 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.messageforwarding.game; + +import com.discordsrv.api.channel.GameChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel; +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.message.ReceivedDiscordMessage; +import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; +import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; +import com.discordsrv.api.discord.api.util.DiscordFormattingUtil; +import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageReceiveEvent; +import com.discordsrv.api.placeholder.FormattedText; +import com.discordsrv.api.player.DiscordSRVPlayer; +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.config.main.channels.base.IChannelConfig; +import com.discordsrv.common.discord.api.entity.message.ReceivedDiscordMessageClusterImpl; +import com.discordsrv.common.function.OrDefault; +import com.discordsrv.common.module.type.AbstractModule; +import net.dv8tion.jda.api.exceptions.InsufficientPermissionException; +import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.CopyOnWriteArrayList; + +public abstract class AbstractGameMessageModule extends AbstractModule { + + public AbstractGameMessageModule(DiscordSRV discordSRV) { + super(discordSRV); + } + + public abstract OrDefault mapConfig(OrDefault channelConfig); + public abstract boolean isEnabled(OrDefault config); + public abstract SendableDiscordMessage.Builder getFormat(OrDefault config); + public abstract void postClusterToEventBus(ReceivedDiscordMessageCluster cluster); + + public final void process( + @NotNull AbstractGameMessageReceiveEvent event, + @NotNull DiscordSRVPlayer player, + @Nullable GameChannel channel + ) { + if (channel == null) { + // Send to all channels due to lack of specified channel + for (OrDefault channelConfig : discordSRV.channelConfig().getAllChannels()) { + forwardToChannel(event, player, channelConfig); + } + return; + } + + OrDefault channelConfig = discordSRV.channelConfig().orDefault(channel); + forwardToChannel(event, player, channelConfig); + } + + private void forwardToChannel( + @NotNull AbstractGameMessageReceiveEvent event, + @NotNull DiscordSRVPlayer player, + @NotNull OrDefault channelConfig + ) { + OrDefault config = mapConfig(channelConfig); + if (!isEnabled(config)) { + return; + } + + IChannelConfig iChannelConfig = channelConfig.get(cfg -> cfg instanceof IChannelConfig ? (IChannelConfig) cfg : null); + if (iChannelConfig == null) { + return; + } + + List messageChannels = new CopyOnWriteArrayList<>(); + List> futures = new ArrayList<>(); + + List channelIds = iChannelConfig.channelIds(); + if (channelIds != null) { + for (Long channelId : iChannelConfig.channelIds()) { + DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null); + if (textChannel != null) { + messageChannels.add(textChannel); + } else if (channelId > 0) { + discordSRV.logger().error("Unable to find channel with ID " + + Long.toUnsignedString(channelId) + + ", unable to forward message to Discord"); + } + } + } + + discordSRV.discordAPI().findOrCreateThreads(iChannelConfig, messageChannels::add, futures); + + CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).whenComplete((v, t1) -> { + SendableDiscordMessage.Builder format = getFormat(config); + if (format == null) { + return; + } + + String message = convertMessage(config, ComponentUtil.fromAPI(event.getMessage())); + List> messageFutures; + messageFutures = sendMessageToChannels( + config, format, messageChannels, message, + // Context + channelConfig, player + ); + + CompletableFuture.allOf(messageFutures.toArray(new CompletableFuture[0])) + .whenComplete((vo, t2) -> { + List messages = new ArrayList<>(); + for (CompletableFuture future : messageFutures) { + if (future.isCompletedExceptionally()) { + future.exceptionally(t -> { + if (t instanceof InsufficientPermissionException) { + discordSRV.logger().error( + "Unable to send message to a Discord channel" + + " because the bot is lacking the " + + ((InsufficientPermissionException) t).getPermission().getName() + + " permission"); + } else { + discordSRV.logger().error("Failed to deliver a message to Discord", t); + } + return null; + }); + // Ignore ones that failed + continue; + } + + // They are all done, so joining will return the result instantly + messages.add(future.join()); + } + + if (message.isEmpty()) { + // Nothing was delivered + return; + } + + postClusterToEventBus(new ReceivedDiscordMessageClusterImpl(messages)); + }) + .exceptionally(t -> { + if (t instanceof CompletionException) { + return null; + } + discordSRV.logger().error("Failed to publish to event bus", t); + return null; + }); + }); + } + + public String convertMessage(OrDefault config, Component component) { + return DiscordFormattingUtil.escapeContent( + discordSRV.componentFactory().discordSerializer().serialize(component) + ); + } + + public List> sendMessageToChannels( + OrDefault config, + SendableDiscordMessage.Builder format, + List channels, + String message, + Object... context + ) { + SendableDiscordMessage discordMessage = format.toFormatter() + .addContext(context) + .addReplacement("%message%", new FormattedText(message)) + .applyPlaceholderService() + .build(); + + List> futures = new ArrayList<>(); + for (DiscordMessageChannel channel : channels) { + futures.add(channel.sendMessage(discordMessage)); + } + + return futures; + } +} diff --git a/common/src/main/java/com/discordsrv/common/module/modules/message/JoinMessageModule.java b/common/src/main/java/com/discordsrv/common/messageforwarding/game/JoinMessageModule.java similarity index 97% rename from common/src/main/java/com/discordsrv/common/module/modules/message/JoinMessageModule.java rename to common/src/main/java/com/discordsrv/common/messageforwarding/game/JoinMessageModule.java index a9172842..595099d9 100644 --- a/common/src/main/java/com/discordsrv/common/module/modules/message/JoinMessageModule.java +++ b/common/src/main/java/com/discordsrv/common/messageforwarding/game/JoinMessageModule.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.module.modules.message; +package com.discordsrv.common.messageforwarding.game; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; diff --git a/common/src/main/java/com/discordsrv/common/module/modules/message/LeaveMessageModule.java b/common/src/main/java/com/discordsrv/common/messageforwarding/game/LeaveMessageModule.java similarity index 97% rename from common/src/main/java/com/discordsrv/common/module/modules/message/LeaveMessageModule.java rename to common/src/main/java/com/discordsrv/common/messageforwarding/game/LeaveMessageModule.java index 4148005e..52ccbd64 100644 --- a/common/src/main/java/com/discordsrv/common/module/modules/message/LeaveMessageModule.java +++ b/common/src/main/java/com/discordsrv/common/messageforwarding/game/LeaveMessageModule.java @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -package com.discordsrv.common.module.modules.message; +package com.discordsrv.common.messageforwarding.game; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; diff --git a/common/src/main/java/com/discordsrv/common/module/modules/message/MinecraftToDiscordChatModule.java b/common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java similarity index 85% rename from common/src/main/java/com/discordsrv/common/module/modules/message/MinecraftToDiscordChatModule.java rename to common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java index 06d83613..c13beb14 100644 --- a/common/src/main/java/com/discordsrv/common/module/modules/message/MinecraftToDiscordChatModule.java +++ b/common/src/main/java/com/discordsrv/common/messageforwarding/game/MinecraftToDiscordChatModule.java @@ -16,10 +16,12 @@ * along with this program. If not, see . */ -package com.discordsrv.common.module.modules.message; +package com.discordsrv.common.messageforwarding.game; import com.discordsrv.api.channel.GameChannel; +import com.discordsrv.api.discord.api.entity.channel.DiscordMessageChannel; 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.ReceivedDiscordMessageCluster; @@ -116,23 +118,30 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule> sendMessageToChannels( OrDefault config, SendableDiscordMessage.Builder format, - List channelIds, + List channels, String message, Object... context ) { - Map> channels = new LinkedHashMap<>(); - for (Long channelId : channelIds) { - discordSRV.discordAPI().getTextChannelById(channelId) - .ifPresent(textChannel -> channels - .computeIfAbsent(textChannel.getGuild(), key -> new LinkedHashSet<>()) - .add(textChannel)); + Map> channelMap = new LinkedHashMap<>(); + for (DiscordMessageChannel channel : channels) { + DiscordGuild guild; + if (channel instanceof DiscordTextChannel) { + guild = ((DiscordTextChannel) channel).getGuild(); + } else if (channel instanceof DiscordThreadChannel) { + guild = ((DiscordThreadChannel) channel).getParentChannel().getGuild(); + } else { + continue; + } + + channelMap.computeIfAbsent(guild, key -> new LinkedHashSet<>()) + .add(channel); } List> futures = new ArrayList<>(); OrDefault mentionConfig = config.map(cfg -> cfg.mentions); // Format messages per-Guild - for (Map.Entry> entry : channels.entrySet()) { + for (Map.Entry> entry : channelMap.entrySet()) { Guild guild = entry.getKey().getAsJDAGuild(); Placeholders channelMessagePlaceholders = new Placeholders(message); @@ -152,14 +161,32 @@ public class MinecraftToDiscordChatModule extends AbstractGameMessageModule ((CachedMention) mention).searchLength).reversed()) .forEachOrdered(mention -> channelMessagePlaceholders.replaceAll(mention.search, mention.mention)); - SendableDiscordMessage discordMessage = format.toFormatter() + SendableDiscordMessage.Formatter discordMessage = format.toFormatter() .addContext(context) .addReplacement("%message%", new FormattedText(channelMessagePlaceholders.toString())) - .applyPlaceholderService() - .build(); + .applyPlaceholderService(); - for (DiscordTextChannel textChannel : entry.getValue()) { - futures.add(textChannel.sendMessage(discordMessage)); + 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.add(channel.sendMessage(finalMessage)); + } + } + if (!thread.isEmpty()) { + SendableDiscordMessage finalMessage = discordMessage.convertToNonWebhook().build(); + for (DiscordMessageChannel channel : thread) { + futures.add(channel.sendMessage(finalMessage)); + } } } diff --git a/common/src/main/java/com/discordsrv/common/module/modules/message/AbstractGameMessageModule.java b/common/src/main/java/com/discordsrv/common/module/modules/message/AbstractGameMessageModule.java deleted file mode 100644 index 6c922742..00000000 --- a/common/src/main/java/com/discordsrv/common/module/modules/message/AbstractGameMessageModule.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * This file is part of DiscordSRV, licensed under the GPLv3 License - * Copyright (c) 2016-2021 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.module.modules.message; - -import com.discordsrv.api.channel.GameChannel; -import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; -import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; -import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; -import com.discordsrv.api.discord.api.util.DiscordFormattingUtil; -import com.discordsrv.api.event.events.message.receive.game.AbstractGameMessageReceiveEvent; -import com.discordsrv.api.placeholder.FormattedText; -import com.discordsrv.api.player.DiscordSRVPlayer; -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.config.main.channels.base.IChannelConfig; -import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageClusterImpl; -import com.discordsrv.common.function.OrDefault; -import com.discordsrv.common.module.type.AbstractModule; -import net.kyori.adventure.text.Component; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -public abstract class AbstractGameMessageModule extends AbstractModule { - - public AbstractGameMessageModule(DiscordSRV discordSRV) { - super(discordSRV); - } - - public abstract OrDefault mapConfig(OrDefault channelConfig); - public abstract boolean isEnabled(OrDefault config); - public abstract SendableDiscordMessage.Builder getFormat(OrDefault config); - public abstract void postClusterToEventBus(ReceivedDiscordMessageCluster cluster); - - public final void process( - @NotNull AbstractGameMessageReceiveEvent event, - @NotNull DiscordSRVPlayer player, - @Nullable GameChannel channel - ) { - if (channel == null) { - // Send to all channels due to lack of specified channel - for (OrDefault channelConfig : discordSRV.channelConfig().getAllChannels()) { - forwardToChannel(event, player, channelConfig); - } - return; - } - - OrDefault channelConfig = discordSRV.channelConfig().orDefault(channel); - forwardToChannel(event, player, channelConfig); - } - - private void forwardToChannel( - @NotNull AbstractGameMessageReceiveEvent event, - @NotNull DiscordSRVPlayer player, - @NotNull OrDefault channelConfig - ) { - OrDefault config = mapConfig(channelConfig); - if (!isEnabled(config)) { - return; - } - - List channelIds = channelConfig.get(cfg -> cfg instanceof IChannelConfig ? ((IChannelConfig) cfg).ids() : null); - if (channelIds == null || channelIds.isEmpty()) { - return; - } - - SendableDiscordMessage.Builder format = getFormat(config); - if (format == null) { - return; - } - - String message = convertMessage(config, ComponentUtil.fromAPI(event.getMessage())); - List> futures = sendMessageToChannels( - config, format, channelIds, message, - // Context - channelConfig, player - ); - - CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) - .whenComplete((v, t) -> { - if (t != null) { - discordSRV.logger().error("Failed to deliver message to Discord", t); - return; - } - - List messages = new ArrayList<>(); - for (CompletableFuture future : futures) { - // They are all done - messages.add(future.join()); - } - - postClusterToEventBus(new ReceivedDiscordMessageClusterImpl(messages)); - }); - } - - public String convertMessage(OrDefault config, Component component) { - return DiscordFormattingUtil.escapeContent( - discordSRV.componentFactory().discordSerializer().serialize(component) - ); - } - - public List> sendMessageToChannels( - OrDefault config, - SendableDiscordMessage.Builder format, - List channelIds, - String message, - Object... context - ) { - SendableDiscordMessage discordMessage = format.toFormatter() - .addContext(context) - .addReplacement("%message%", new FormattedText(message)) - .applyPlaceholderService() - .build(); - - List> futures = new ArrayList<>(); - for (Long channelId : channelIds) { - discordSRV.discordAPI().getTextChannelById(channelId) - .ifPresent(channel -> futures.add(channel.sendMessage(discordMessage))); - } - - return futures; - } -}