From 627b5384568f41317a4f59ae5323eb9dc9970f34 Mon Sep 17 00:00:00 2001 From: Vankka Date: Wed, 1 Sep 2021 00:01:50 +0300 Subject: [PATCH] Initial Discord -> Minecraft message functionality --- .../message/ReceivedDiscordMessage.java | 8 ++ .../ReceivedDiscordMessageCluster.java | 23 +++++ .../discord/api/entity/user/DiscordUser.java | 4 + .../lifecycle/DiscordSRVReloadEvent.java | 39 +++++++++ .../discord/DiscordMessageForwardedEvent.java | 48 +++++++++++ .../AbstractGameMessageForwardedEvent.java} | 6 +- .../game/ChatMessageForwardedEvent.java} | 6 +- .../{send => forward}/package-info.java | 2 +- .../discord/DiscordMessageReceiveEvent.java | 86 +++++++++++++++++++ .../discordsrv/bukkit/BukkitDiscordSRV.java | 1 - .../bukkit/console/BukkitConsole.java | 3 +- .../bukkit/player/BukkitPlayer.java | 6 +- .../discordsrv/bungee/BungeeDiscordSRV.java | 1 - .../bungee/console/BungeeConsole.java | 3 +- .../bungee/player/BungeePlayer.java | 6 +- .../discordsrv/common/AbstractDiscordSRV.java | 21 +++-- .../com/discordsrv/common/DiscordSRV.java | 4 +- ...elConfig.java => ChannelConfigHelper.java} | 84 ++++++++++++++++-- .../command/game/sender/ICommandSender.java | 7 +- .../main/channels/BaseChannelConfig.java | 7 +- .../config/main/channels/ChannelConfig.java | 2 + .../DiscordToMinecraftChatConfig.java | 27 ++++++ .../api/channel/DiscordDMChannelImpl.java | 8 ++ .../api/channel/DiscordTextChannelImpl.java | 5 ++ .../ReceivedDiscordMessageClusterImpl.java | 18 ++++ .../message/ReceivedDiscordMessageImpl.java | 19 +++- .../connection/jda/JDAConnectionManager.java | 13 ++- ...stener.java => ChannelLookupListener.java} | 4 +- .../common/listener/DiscordChatListener.java | 84 ++++++++++++++++++ ...hatListener.java => GameChatListener.java} | 8 +- .../placeholder/PlaceholderServiceImpl.java | 4 + .../discordsrv/sponge/SpongeDiscordSRV.java | 3 - .../sponge/console/SpongeConsole.java | 3 +- .../sponge/player/SpongePlayer.java | 6 +- .../velocity/VelocityDiscordSRV.java | 3 - .../velocity/console/VelocityConsole.java | 3 +- .../velocity/player/VelocityPlayer.java | 6 +- 37 files changed, 521 insertions(+), 60 deletions(-) create mode 100644 api/src/main/java/com/discordsrv/api/event/events/lifecycle/DiscordSRVReloadEvent.java create mode 100644 api/src/main/java/com/discordsrv/api/event/events/message/forward/discord/DiscordMessageForwardedEvent.java rename api/src/main/java/com/discordsrv/api/event/events/message/{send/game/AbstractGameMessageSentEvent.java => forward/game/AbstractGameMessageForwardedEvent.java} (88%) rename api/src/main/java/com/discordsrv/api/event/events/message/{send/game/ChatMessageSentEvent.java => forward/game/ChatMessageForwardedEvent.java} (85%) rename api/src/main/java/com/discordsrv/api/event/events/message/{send => forward}/package-info.java (95%) create mode 100644 api/src/main/java/com/discordsrv/api/event/events/message/receive/discord/DiscordMessageReceiveEvent.java rename common/src/main/java/com/discordsrv/common/channel/{ChannelConfig.java => ChannelConfigHelper.java} (54%) create mode 100644 common/src/main/java/com/discordsrv/common/config/main/channels/discordtominecraft/DiscordToMinecraftChatConfig.java rename common/src/main/java/com/discordsrv/common/listener/{DefaultChannelLookupListener.java => ChannelLookupListener.java} (91%) create mode 100644 common/src/main/java/com/discordsrv/common/listener/DiscordChatListener.java rename common/src/main/java/com/discordsrv/common/listener/{DefaultGameChatListener.java => GameChatListener.java} (94%) diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessage.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessage.java index 08d30d32..08b16f1a 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessage.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessage.java @@ -26,6 +26,7 @@ package com.discordsrv.api.discord.api.entity.message; import com.discordsrv.api.discord.api.entity.Snowflake; import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; import com.discordsrv.api.discord.api.entity.guild.DiscordGuild; +import com.discordsrv.api.discord.api.entity.user.DiscordUser; import org.jetbrains.annotations.NotNull; import java.util.concurrent.CompletableFuture; @@ -42,6 +43,13 @@ public interface ReceivedDiscordMessage extends SendableDiscordMessage, Snowflak @NotNull DiscordTextChannel getChannel(); + /** + * Gets the user that sent the message. + * @return the user that sent the message + */ + @NotNull + DiscordUser getAuthor(); + /** * Gets the Discord server the message was posted in. * @return the Discord server the message was posted in diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessageCluster.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessageCluster.java index f8557f75..784d250c 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessageCluster.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/message/ReceivedDiscordMessageCluster.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.discord.api.entity.message; import java.util.List; diff --git a/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java b/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java index a4c1967d..2c5fb7c6 100644 --- a/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java +++ b/api/src/main/java/com/discordsrv/api/discord/api/entity/user/DiscordUser.java @@ -24,6 +24,7 @@ package com.discordsrv.api.discord.api.entity.user; import com.discordsrv.api.discord.api.entity.Snowflake; +import com.discordsrv.api.placeholder.Placeholder; import org.jetbrains.annotations.NotNull; /** @@ -35,6 +36,7 @@ public interface DiscordUser extends Snowflake { * Gets the username of the Discord user. * @return the user's username */ + @Placeholder("user_name") @NotNull String getUsername(); @@ -42,6 +44,7 @@ public interface DiscordUser extends Snowflake { * Gets the Discord user's discriminator. * @return the user's discriminator */ + @Placeholder("user_discriminator") @NotNull String getDiscriminator(); @@ -49,6 +52,7 @@ public interface DiscordUser extends Snowflake { * Gets the Discord user's username followed by a {@code #} and their discriminator. * @return the Discord user's username & discriminator in the following format {@code Username#1234} */ + @Placeholder("user_tag") default String getAsTag() { return getUsername() + "#" + getDiscriminator(); } diff --git a/api/src/main/java/com/discordsrv/api/event/events/lifecycle/DiscordSRVReloadEvent.java b/api/src/main/java/com/discordsrv/api/event/events/lifecycle/DiscordSRVReloadEvent.java new file mode 100644 index 00000000..c77cc18b --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/event/events/lifecycle/DiscordSRVReloadEvent.java @@ -0,0 +1,39 @@ +/* + * 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.lifecycle; + +import com.discordsrv.api.event.events.Event; + +public class DiscordSRVReloadEvent implements Event { + + private final boolean config; + + public DiscordSRVReloadEvent(boolean config) { + this.config = config; + } + + public boolean isConfig() { + return config; + } +} diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/forward/discord/DiscordMessageForwardedEvent.java b/api/src/main/java/com/discordsrv/api/event/events/message/forward/discord/DiscordMessageForwardedEvent.java new file mode 100644 index 00000000..cda6573b --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/event/events/message/forward/discord/DiscordMessageForwardedEvent.java @@ -0,0 +1,48 @@ +/* + * 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.discord; + +import com.discordsrv.api.channel.GameChannel; +import com.discordsrv.api.component.MinecraftComponent; +import com.discordsrv.api.event.events.Event; +import org.jetbrains.annotations.NotNull; + +public class DiscordMessageForwardedEvent implements Event { + + private final MinecraftComponent message; + private final GameChannel channel; + + public DiscordMessageForwardedEvent(@NotNull MinecraftComponent message, @NotNull GameChannel channel) { + this.message = message; + this.channel = channel; + } + + public MinecraftComponent getMessage() { + return message; + } + + public GameChannel getChannel() { + return channel; + } +} diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/send/game/AbstractGameMessageSentEvent.java b/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/AbstractGameMessageForwardedEvent.java similarity index 88% rename from api/src/main/java/com/discordsrv/api/event/events/message/send/game/AbstractGameMessageSentEvent.java rename to api/src/main/java/com/discordsrv/api/event/events/message/forward/game/AbstractGameMessageForwardedEvent.java index e3806929..1b3f0e5c 100644 --- a/api/src/main/java/com/discordsrv/api/event/events/message/send/game/AbstractGameMessageSentEvent.java +++ b/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/AbstractGameMessageForwardedEvent.java @@ -21,17 +21,17 @@ * SOFTWARE. */ -package com.discordsrv.api.event.events.message.send.game; +package com.discordsrv.api.event.events.message.forward.game; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; import com.discordsrv.api.event.events.Event; import org.jetbrains.annotations.NotNull; -public abstract class AbstractGameMessageSentEvent implements Event { +public abstract class AbstractGameMessageForwardedEvent implements Event { private final ReceivedDiscordMessageCluster discordMessage; - public AbstractGameMessageSentEvent(@NotNull ReceivedDiscordMessageCluster discordMessage) { + public AbstractGameMessageForwardedEvent(@NotNull ReceivedDiscordMessageCluster discordMessage) { this.discordMessage = discordMessage; } diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/send/game/ChatMessageSentEvent.java b/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ChatMessageForwardedEvent.java similarity index 85% rename from api/src/main/java/com/discordsrv/api/event/events/message/send/game/ChatMessageSentEvent.java rename to api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ChatMessageForwardedEvent.java index 506fb561..c5b05f6d 100644 --- a/api/src/main/java/com/discordsrv/api/event/events/message/send/game/ChatMessageSentEvent.java +++ b/api/src/main/java/com/discordsrv/api/event/events/message/forward/game/ChatMessageForwardedEvent.java @@ -21,14 +21,14 @@ * SOFTWARE. */ -package com.discordsrv.api.event.events.message.send.game; +package com.discordsrv.api.event.events.message.forward.game; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessageCluster; import org.jetbrains.annotations.NotNull; -public class ChatMessageSentEvent extends AbstractGameMessageSentEvent { +public class ChatMessageForwardedEvent extends AbstractGameMessageForwardedEvent { - public ChatMessageSentEvent(@NotNull ReceivedDiscordMessageCluster discordMessage) { + public ChatMessageForwardedEvent(@NotNull ReceivedDiscordMessageCluster discordMessage) { super(discordMessage); } } diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/send/package-info.java b/api/src/main/java/com/discordsrv/api/event/events/message/forward/package-info.java similarity index 95% rename from api/src/main/java/com/discordsrv/api/event/events/message/send/package-info.java rename to api/src/main/java/com/discordsrv/api/event/events/message/forward/package-info.java index 03a6c112..ab779473 100644 --- a/api/src/main/java/com/discordsrv/api/event/events/message/send/package-info.java +++ b/api/src/main/java/com/discordsrv/api/event/events/message/forward/package-info.java @@ -21,4 +21,4 @@ * SOFTWARE. */ -package com.discordsrv.api.event.events.message.send; +package com.discordsrv.api.event.events.message.forward; diff --git a/api/src/main/java/com/discordsrv/api/event/events/message/receive/discord/DiscordMessageReceiveEvent.java b/api/src/main/java/com/discordsrv/api/event/events/message/receive/discord/DiscordMessageReceiveEvent.java new file mode 100644 index 00000000..d4fc1d2b --- /dev/null +++ b/api/src/main/java/com/discordsrv/api/event/events/message/receive/discord/DiscordMessageReceiveEvent.java @@ -0,0 +1,86 @@ +/* + * 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.discord; + +import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; +import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; +import com.discordsrv.api.event.events.Cancellable; +import com.discordsrv.api.event.events.Processable; +import org.jetbrains.annotations.NotNull; + +public class DiscordMessageReceiveEvent implements Cancellable, Processable { + + private final ReceivedDiscordMessage discordMessage; + private String messageContent; + private DiscordTextChannel channel; + private boolean cancelled; + private boolean processed; + + public DiscordMessageReceiveEvent(@NotNull ReceivedDiscordMessage discordMessage, @NotNull DiscordTextChannel channel) { + this.discordMessage = discordMessage; + this.messageContent = discordMessage.getContent(); + this.channel = channel; + } + + public ReceivedDiscordMessage getDiscordMessage() { + return discordMessage; + } + + public String getMessageContent() { + return messageContent; + } + + public void setMessageContent(@NotNull String messageContent) { + this.messageContent = messageContent; + } + + public DiscordTextChannel getChannel() { + return channel; + } + + public void setChannel(DiscordTextChannel channel) { + this.channel = channel; + } + + @Override + public boolean isCancelled() { + return cancelled; + } + + @Override + public void setCancelled(boolean cancelled) { + this.cancelled = cancelled; + } + + @Override + public boolean isProcessed() { + return processed; + } + + @Override + public void markAsProcessed() { + this.processed = true; + } + +} diff --git a/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java b/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java index cbb205a3..0cb53b4b 100644 --- a/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java +++ b/bukkit/src/main/java/com/discordsrv/bukkit/BukkitDiscordSRV.java @@ -128,7 +128,6 @@ public class BukkitDiscordSRV extends ServerDiscordSRV channels; + private final LoadingCache nameToChannelCache; + private final Map> discordToConfigMap; - public ChannelConfig(DiscordSRV discordSRV) { + public ChannelConfigHelper(DiscordSRV discordSRV) { this.discordSRV = discordSRV; - this.channels = discordSRV.caffeineBuilder() + this.nameToChannelCache = discordSRV.caffeineBuilder() .expireAfterWrite(60, TimeUnit.SECONDS) .expireAfterAccess(30, TimeUnit.SECONDS) .refreshAfterWrite(10, TimeUnit.SECONDS) @@ -55,6 +63,35 @@ public class ChannelConfig { return event.getChannelFromProcessing(); } }); + this.discordToConfigMap = new ConcurrentHashMap<>(); + + discordSRV.eventBus().subscribe(this); + } + + @Subscribe + public void onReload(DiscordSRVReloadEvent event) { + if (!event.isConfig()) { + return; + } + + Map> newMap = new HashMap<>(); + for (Map.Entry entry : channels().entrySet()) { + String channelName = entry.getKey(); + BaseChannelConfig value = entry.getValue(); + if (value instanceof ChannelConfig) { + ChannelConfig channelConfig = (ChannelConfig) value; + for (String channelId : channelConfig.channelIds) { + newMap.put(channelId, Pair.of(channelName, channelConfig)); + } + } + } + + synchronized (discordToConfigMap) { + discordToConfigMap.clear(); + for (Map.Entry> entry : newMap.entrySet()) { + discordToConfigMap.put(entry.getKey(), entry.getValue()); + } + } } private Map channels() { @@ -65,9 +102,20 @@ public class ChannelConfig { return orDefault(gameChannel.getOwnerName(), gameChannel.getChannelName()); } - public OrDefault orDefault(String ownerName, String channelName) { - BaseChannelConfig defaultConfig = channels().computeIfAbsent( + public OrDefault> orDefault(DiscordTextChannel discordTextChannel) { + return new OrDefault<>( + getDiscordResolved(discordTextChannel), + Pair.of(null, getDefault()) + ); + } + + private BaseChannelConfig getDefault() { + return channels().computeIfAbsent( "default", key -> new BaseChannelConfig()); + } + + public OrDefault orDefault(String ownerName, String channelName) { + BaseChannelConfig defaultConfig = getDefault(); return new OrDefault<>( get(ownerName, channelName), @@ -86,7 +134,7 @@ public class ChannelConfig { return config; } - GameChannel gameChannel = channels.get(channelName); + GameChannel gameChannel = nameToChannelCache.get(channelName); if (gameChannel != null && gameChannel.getOwnerName().equals(ownerName)) { config = channels().get(channelName); return config; @@ -94,7 +142,27 @@ public class ChannelConfig { return null; } - GameChannel gameChannel = channels.get(channelName); + GameChannel gameChannel = nameToChannelCache.get(channelName); return gameChannel != null ? get(gameChannel) : null; } + + public Pair getDiscordResolved(DiscordTextChannel channel) { + Pair pair = getDiscord(channel); + if (pair == null) { + return null; + } + + GameChannel gameChannel = nameToChannelCache.get(pair.getKey()); + if (gameChannel == null) { + return null; + } + + return Pair.of(gameChannel, pair.getValue()); + } + + public Pair getDiscord(DiscordTextChannel channel) { + synchronized (discordToConfigMap) { + return discordToConfigMap.get(channel.getId()); + } + } } diff --git a/common/src/main/java/com/discordsrv/common/command/game/sender/ICommandSender.java b/common/src/main/java/com/discordsrv/common/command/game/sender/ICommandSender.java index 5f8db799..ab78f7c1 100644 --- a/common/src/main/java/com/discordsrv/common/command/game/sender/ICommandSender.java +++ b/common/src/main/java/com/discordsrv/common/command/game/sender/ICommandSender.java @@ -20,14 +20,17 @@ package com.discordsrv.common.command.game.sender; import net.kyori.adventure.identity.Identity; import net.kyori.adventure.text.Component; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface ICommandSender { - default void sendMessage(Component message) { + default void sendMessage(@NotNull Component message) { + // Identity is converted to Identity.nil() later sendMessage(null, message); } - void sendMessage(Identity identity, Component message); + void sendMessage(@Nullable Identity identity, @NotNull Component message); boolean hasPermission(String permission); void runCommand(String command); diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/BaseChannelConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/BaseChannelConfig.java index 684db393..967cb4b2 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/channels/BaseChannelConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/BaseChannelConfig.java @@ -18,6 +18,7 @@ package com.discordsrv.common.config.main.channels; +import com.discordsrv.common.config.main.channels.discordtominecraft.DiscordToMinecraftChatConfig; import com.discordsrv.common.config.main.channels.minecraftodiscord.MinecraftToDiscordChatConfig; import org.checkerframework.checker.nullness.qual.Nullable; import org.spongepowered.configurate.ConfigurationNode; @@ -32,7 +33,7 @@ import java.lang.reflect.Type; public class BaseChannelConfig { public MinecraftToDiscordChatConfig minecraftToDiscord = new MinecraftToDiscordChatConfig(); - + public DiscordToMinecraftChatConfig discordToMinecraft = new DiscordToMinecraftChatConfig(); public static class Serializer implements TypeSerializer { @@ -46,7 +47,7 @@ public class BaseChannelConfig { public BaseChannelConfig deserialize(Type type, ConfigurationNode node) throws SerializationException { return (BaseChannelConfig) mapperFactory.asTypeSerializer() .deserialize( - "default".equals(node.key()) ? BaseChannelConfig.class : ChannelConfig.class, + ChannelConfig.DEFAULT_KEY.equals(node.key()) ? BaseChannelConfig.class : ChannelConfig.class, node ); } @@ -59,7 +60,7 @@ public class BaseChannelConfig { } mapperFactory.asTypeSerializer().serialize( - "default".equals(node.key()) ? BaseChannelConfig.class : ChannelConfig.class, + ChannelConfig.DEFAULT_KEY.equals(node.key()) ? BaseChannelConfig.class : ChannelConfig.class, obj, node ); diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/ChannelConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/ChannelConfig.java index f3dd4149..0f0cf9a6 100644 --- a/common/src/main/java/com/discordsrv/common/config/main/channels/ChannelConfig.java +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/ChannelConfig.java @@ -30,6 +30,8 @@ import java.util.List; @ConfigSerializable public class ChannelConfig extends BaseChannelConfig { + public static final String DEFAULT_KEY = "default"; + public ChannelConfig() { // Clear everything besides channelIds by default (these will be filled back in by Configurate if they are in the config itself) for (Field field : getClass().getFields()) { diff --git a/common/src/main/java/com/discordsrv/common/config/main/channels/discordtominecraft/DiscordToMinecraftChatConfig.java b/common/src/main/java/com/discordsrv/common/config/main/channels/discordtominecraft/DiscordToMinecraftChatConfig.java new file mode 100644 index 00000000..6381270d --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/config/main/channels/discordtominecraft/DiscordToMinecraftChatConfig.java @@ -0,0 +1,27 @@ +/* + * 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.config.main.channels.discordtominecraft; + +import org.spongepowered.configurate.objectmapping.ConfigSerializable; + +@ConfigSerializable +public class DiscordToMinecraftChatConfig { + + public String format = "%user_name%: %message%"; +} diff --git a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java index cf6d14cb..1610ea4c 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordDMChannelImpl.java @@ -83,6 +83,14 @@ public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements D return mapExceptions(future); } + @Override + public CompletableFuture deleteMessageById(String id) { + CompletableFuture future = privateChannel() + .deleteMessageById(id) + .submit(); + return mapExceptions(future); + } + @Override public @NotNull CompletableFuture editMessageById(String id, SendableDiscordMessage message) { if (message.isWebhookMessage()) { diff --git a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java index c566022b..3982869b 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/channel/DiscordTextChannelImpl.java @@ -83,6 +83,11 @@ public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements return message(message, WebhookClient::send, MessageChannel::sendMessage); } + @Override + public CompletableFuture deleteMessageById(String id) { + return null; // TODO + } + @Override public @NotNull CompletableFuture editMessageById(String id, SendableDiscordMessage message) { return message( diff --git a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.java index ba3aeb7d..59dec600 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageClusterImpl.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.common.discord.api.message; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; diff --git a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java b/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java index d1e43626..a231f16e 100644 --- a/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java +++ b/common/src/main/java/com/discordsrv/common/discord/api/message/ReceivedDiscordMessageImpl.java @@ -27,8 +27,11 @@ import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed; import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage; import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; import com.discordsrv.api.discord.api.entity.message.impl.SendableDiscordMessageImpl; +import com.discordsrv.api.discord.api.entity.user.DiscordUser; import com.discordsrv.api.discord.api.exception.UnknownChannelException; import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.discord.api.channel.DiscordTextChannelImpl; +import com.discordsrv.common.discord.api.user.DiscordUserImpl; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; import net.dv8tion.jda.api.entities.Role; @@ -51,11 +54,12 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple String webhookUsername = webhookMessage ? message.getAuthor().getName() : null; String webhookAvatarUrl = webhookMessage ? message.getAuthor().getEffectiveAvatarUrl() : null; - DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(message.getChannel().getId()) - .orElse(null); + DiscordTextChannel textChannel = new DiscordTextChannelImpl(discordSRV, message.getTextChannel()); + DiscordUser user = new DiscordUserImpl(message.getAuthor()); return new ReceivedDiscordMessageImpl( discordSRV, textChannel, + user, message.getChannel().getId(), message.getId(), message.getContentRaw(), @@ -106,9 +110,12 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById( Long.toUnsignedString(webhookMessage.getChannelId())).orElse(null); + DiscordUser user = discordSRV.discordAPI().getUserById( + Long.toUnsignedString(webhookMessage.getAuthor().getId())).orElse(null); return new ReceivedDiscordMessageImpl( discordSRV, textChannel, + user, Long.toUnsignedString(webhookMessage.getChannelId()), Long.toUnsignedString(webhookMessage.getId()), webhookMessage.getContent(), @@ -120,12 +127,14 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple private final DiscordSRV discordSRV; private final DiscordTextChannel textChannel; + private final DiscordUser author; private final String channelId; private final String id; private ReceivedDiscordMessageImpl( DiscordSRV discordSRV, DiscordTextChannel textChannel, + DiscordUser author, String channelId, String id, String content, @@ -136,6 +145,7 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple super(content, embeds, null, webhookUsername, webhookAvatarUrl); this.discordSRV = discordSRV; this.textChannel = textChannel; + this.author = author; this.channelId = channelId; this.id = id; } @@ -150,6 +160,11 @@ public class ReceivedDiscordMessageImpl extends SendableDiscordMessageImpl imple return textChannel; } + @Override + public @NotNull DiscordUser getAuthor() { + return author; + } + @Override public CompletableFuture delete() { DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null); 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 ab758da3..164d1819 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 @@ -109,7 +109,7 @@ public class JDAConnectionManager implements DiscordConnectionManager { discordSRV.logger().error("| server requiring 2FA for moderation actions"); if (user != null) { discordSRV.logger().error("|"); - discordSRV.logger().error("| The Discord bot's owner is " + user.getAsTag()); + discordSRV.logger().error("| The Discord bot's owner is " + user.getAsTag() + " (" + user.getId() + ")"); } discordSRV.logger().error("|"); discordSRV.logger().error("| You can view instructions for enabling 2FA here:"); @@ -159,8 +159,10 @@ public class JDAConnectionManager implements DiscordConnectionManager { } Set newContext = new HashSet<>(); + boolean anyConverted = false; for (Object o : event.getContext()) { Object converted; + boolean isConversion = true; if (o instanceof PrivateChannel) { converted = new DiscordDMChannelImpl(discordSRV, (PrivateChannel) o); } else if (o instanceof TextChannel) { @@ -177,10 +179,16 @@ public class JDAConnectionManager implements DiscordConnectionManager { converted = new DiscordUserImpl((User) o); } else { converted = o; + isConversion = false; + } + if (isConversion) { + anyConverted = true; } newContext.add(converted); } - event.process(PlaceholderLookupResult.newLookup(event.getPlaceholder(), newContext)); + if (anyConverted) { + event.process(PlaceholderLookupResult.newLookup(event.getPlaceholder(), newContext)); + } } @Subscribe @@ -305,6 +313,7 @@ public class JDAConnectionManager implements DiscordConnectionManager { @SuppressWarnings("BusyWait") private void connectInternal() { + discordSRV.discordConnectionDetails().requestGatewayIntent(GatewayIntent.GUILD_MESSAGES); // TODO: figure out how DiscordSRV required intents are going to work detailsAccepted = false; ConnectionConfig.Bot botConfig = discordSRV.connectionConfig().bot; diff --git a/common/src/main/java/com/discordsrv/common/listener/DefaultChannelLookupListener.java b/common/src/main/java/com/discordsrv/common/listener/ChannelLookupListener.java similarity index 91% rename from common/src/main/java/com/discordsrv/common/listener/DefaultChannelLookupListener.java rename to common/src/main/java/com/discordsrv/common/listener/ChannelLookupListener.java index bdf068b2..b0a8f89b 100644 --- a/common/src/main/java/com/discordsrv/common/listener/DefaultChannelLookupListener.java +++ b/common/src/main/java/com/discordsrv/common/listener/ChannelLookupListener.java @@ -23,9 +23,9 @@ import com.discordsrv.api.event.bus.Subscribe; import com.discordsrv.api.event.events.channel.GameChannelLookupEvent; import com.discordsrv.common.DiscordSRV; -public class DefaultChannelLookupListener extends AbstractListener { +public class ChannelLookupListener extends AbstractListener { - public DefaultChannelLookupListener(DiscordSRV discordSRV) { + public ChannelLookupListener(DiscordSRV discordSRV) { super(discordSRV); } diff --git a/common/src/main/java/com/discordsrv/common/listener/DiscordChatListener.java b/common/src/main/java/com/discordsrv/common/listener/DiscordChatListener.java new file mode 100644 index 00000000..130d0cfd --- /dev/null +++ b/common/src/main/java/com/discordsrv/common/listener/DiscordChatListener.java @@ -0,0 +1,84 @@ +/* + * 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.listener; + +import com.discordsrv.api.channel.GameChannel; +import com.discordsrv.api.component.MinecraftComponent; +import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel; +import com.discordsrv.api.discord.api.entity.user.DiscordUser; +import com.discordsrv.api.event.bus.Subscribe; +import com.discordsrv.api.event.events.message.receive.discord.DiscordMessageReceiveEvent; +import com.discordsrv.common.DiscordSRV; +import com.discordsrv.common.config.main.channels.BaseChannelConfig; +import com.discordsrv.common.config.main.channels.discordtominecraft.DiscordToMinecraftChatConfig; +import com.discordsrv.common.discord.api.channel.DiscordTextChannelImpl; +import com.discordsrv.common.discord.api.message.ReceivedDiscordMessageImpl; +import com.discordsrv.common.function.OrDefault; +import dev.vankka.mcdiscordreserializer.minecraft.MinecraftSerializer; +import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; +import net.kyori.adventure.text.Component; +import org.apache.commons.lang3.tuple.Pair; + +public class DiscordChatListener extends AbstractListener { + + public DiscordChatListener(DiscordSRV discordSRV) { + super(discordSRV); + } + + @Subscribe + public void onGuildMessageReceived(GuildMessageReceivedEvent event) { + discordSRV.eventBus().publish( + new DiscordMessageReceiveEvent( + ReceivedDiscordMessageImpl.fromJDA(discordSRV, event.getMessage()), + new DiscordTextChannelImpl(discordSRV, event.getChannel()))); + } + + @Subscribe + public void onDiscordMessageReceive(DiscordMessageReceiveEvent event) { + if (checkCancellation(event) || checkProcessor(event) || !discordSRV.isReady()) { + return; + } + + DiscordTextChannel channel = event.getChannel(); + Component message = MinecraftSerializer.INSTANCE.serialize(event.getMessageContent()); + + OrDefault> channelPair = discordSRV.channelConfig().orDefault(channel); + GameChannel gameChannel = channelPair.get(Pair::getKey); + if (gameChannel == null) { + return; + } + + OrDefault channelConfig = channelPair.map(Pair::getValue); + OrDefault chatConfig = channelConfig.map(cfg -> cfg.discordToMinecraft); + + String format = chatConfig.get(cfg -> cfg.format); + if (format == null) { + return; + } + + DiscordUser user = event.getDiscordMessage().getAuthor(); + MinecraftComponent component = discordSRV.componentFactory() + .enhancedBuilder(format) + .addContext(event.getDiscordMessage(), user) + .addReplacement("%message%", message) + .build(); + + gameChannel.sendMessage(component); + } +} diff --git a/common/src/main/java/com/discordsrv/common/listener/DefaultGameChatListener.java b/common/src/main/java/com/discordsrv/common/listener/GameChatListener.java similarity index 94% rename from common/src/main/java/com/discordsrv/common/listener/DefaultGameChatListener.java rename to common/src/main/java/com/discordsrv/common/listener/GameChatListener.java index efe146c4..46e87ed8 100644 --- a/common/src/main/java/com/discordsrv/common/listener/DefaultGameChatListener.java +++ b/common/src/main/java/com/discordsrv/common/listener/GameChatListener.java @@ -24,7 +24,7 @@ import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage; import com.discordsrv.api.event.bus.EventPriority; import com.discordsrv.api.event.bus.Subscribe; import com.discordsrv.api.event.events.message.receive.game.ChatMessageReceiveEvent; -import com.discordsrv.api.event.events.message.send.game.ChatMessageSentEvent; +import com.discordsrv.api.event.events.message.forward.game.ChatMessageForwardedEvent; import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.component.util.ComponentUtil; import com.discordsrv.common.config.main.channels.BaseChannelConfig; @@ -39,9 +39,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; -public class DefaultGameChatListener extends AbstractListener { +public class GameChatListener extends AbstractListener { - public DefaultGameChatListener(DiscordSRV discordSRV) { + public GameChatListener(DiscordSRV discordSRV) { super(discordSRV); } @@ -91,7 +91,7 @@ public class DefaultGameChatListener extends AbstractListener { } discordSRV.eventBus().publish( - new ChatMessageSentEvent( + new ChatMessageForwardedEvent( new ReceivedDiscordMessageClusterImpl(messages))); }); } diff --git a/common/src/main/java/com/discordsrv/common/placeholder/PlaceholderServiceImpl.java b/common/src/main/java/com/discordsrv/common/placeholder/PlaceholderServiceImpl.java index 30ced20d..c1383aa4 100644 --- a/common/src/main/java/com/discordsrv/common/placeholder/PlaceholderServiceImpl.java +++ b/common/src/main/java/com/discordsrv/common/placeholder/PlaceholderServiceImpl.java @@ -69,6 +69,10 @@ public class PlaceholderServiceImpl implements PlaceholderService { @Override public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set context) { for (Object o : context) { + if (o == null) { + continue; + } + if (o instanceof PlaceholderProvider) { PlaceholderLookupResult result = ((PlaceholderProvider) o).lookup(placeholder, context); if (result.getType() != PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) { diff --git a/sponge/src/main/java/com/discordsrv/sponge/SpongeDiscordSRV.java b/sponge/src/main/java/com/discordsrv/sponge/SpongeDiscordSRV.java index 9e51766e..bfdf9930 100644 --- a/sponge/src/main/java/com/discordsrv/sponge/SpongeDiscordSRV.java +++ b/sponge/src/main/java/com/discordsrv/sponge/SpongeDiscordSRV.java @@ -114,9 +114,6 @@ public class SpongeDiscordSRV extends ServerDiscordSRV