Initial Discord -> Minecraft message functionality

This commit is contained in:
Vankka 2021-09-01 00:01:50 +03:00
parent a8cce7cf8d
commit 627b538456
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
37 changed files with 521 additions and 60 deletions

View File

@ -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

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -21,4 +21,4 @@
* SOFTWARE.
*/
package com.discordsrv.api.event.events.message.send;
package com.discordsrv.api.event.events.message.forward;

View File

@ -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;
}
}

View File

@ -128,7 +128,6 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
// Player related
this.audiences = BukkitAudiences.create(bootstrap.getPlugin());
playerProvider().subscribe();
super.enable();

View File

@ -25,6 +25,7 @@ import com.discordsrv.common.logging.logger.impl.JavaLoggerImpl;
import com.discordsrv.common.logging.logger.impl.Log4JLoggerImpl;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
public class BukkitConsole implements Console {
@ -49,7 +50,7 @@ public class BukkitConsole implements Console {
}
@Override
public void sendMessage(Identity identity, Component message) {
public void sendMessage(Identity identity, @NotNull Component message) {
discordSRV.audiences().console().sendMessage(identity, message);
}

View File

@ -53,9 +53,11 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
}
@Override
public void sendMessage(Identity identity, Component message) {
public void sendMessage(Identity identity, @NotNull Component message) {
if (audience != null) {
audience.sendMessage(identity, message);
audience.sendMessage(
identity != null ? identity : Identity.nil(),
message);
} else {
player.sendMessage(BukkitComponentSerializer.legacy().serialize(message));
}

View File

@ -113,7 +113,6 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
protected void enable() throws Throwable {
// Player related
this.audiences = BungeeAudiences.create(bootstrap.getPlugin());
playerProvider().subscribe();
super.enable();
}

View File

@ -24,6 +24,7 @@ import com.discordsrv.common.logging.logger.backend.LoggingBackend;
import com.discordsrv.common.logging.logger.impl.JavaLoggerImpl;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
public class BungeeConsole implements Console {
@ -36,7 +37,7 @@ public class BungeeConsole implements Console {
}
@Override
public void sendMessage(Identity identity, Component message) {
public void sendMessage(Identity identity, @NotNull Component message) {
discordSRV.audiences().console().sendMessage(identity, message);
}

View File

@ -42,8 +42,10 @@ public class BungeePlayer implements IPlayer {
}
@Override
public void sendMessage(Identity identity, Component message) {
audience.sendMessage(identity, message);
public void sendMessage(Identity identity, @NotNull Component message) {
audience.sendMessage(
identity != null ? identity : Identity.nil(),
message);
}
@Override

View File

@ -20,10 +20,11 @@ package com.discordsrv.common;
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.api.placeholder.PlaceholderService;
import com.discordsrv.common.api.util.ApiInstanceUtil;
import com.discordsrv.common.channel.ChannelConfig;
import com.discordsrv.common.channel.ChannelConfigHelper;
import com.discordsrv.common.channel.DefaultGlobalChannel;
import com.discordsrv.common.component.ComponentFactory;
import com.discordsrv.common.config.connection.ConnectionConfig;
@ -36,8 +37,9 @@ 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.listener.DefaultChannelLookupListener;
import com.discordsrv.common.listener.DefaultGameChatListener;
import com.discordsrv.common.listener.ChannelLookupListener;
import com.discordsrv.common.listener.DiscordChatListener;
import com.discordsrv.common.listener.GameChatListener;
import com.discordsrv.common.logging.DependencyLoggingFilter;
import com.discordsrv.common.logging.logger.backend.LoggingBackend;
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
@ -71,7 +73,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
// DiscordSRV
private final DefaultGlobalChannel defaultGlobalChannel = new DefaultGlobalChannel(this);
private ChannelConfig channelConfig;
private ChannelConfigHelper channelConfig;
private DiscordConnectionManager discordConnectionManager;
// Internal
@ -136,7 +138,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
}
@Override
public ChannelConfig channelConfig() {
public ChannelConfigHelper channelConfig() {
return channelConfig;
}
@ -227,7 +229,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
configManager().load();
// Utility
channelConfig = new ChannelConfig(this);
channelConfig = new ChannelConfigHelper(this);
eventBus().publish(new DiscordSRVReloadEvent(true));
} catch (Throwable t) {
setStatus(Status.FAILED_TO_LOAD_CONFIG);
throw t;
@ -248,8 +252,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
// Register listeners
// Chat
eventBus().subscribe(new DefaultChannelLookupListener(this));
eventBus().subscribe(new DefaultGameChatListener(this));
eventBus().subscribe(new ChannelLookupListener(this));
eventBus().subscribe(new GameChatListener(this));
eventBus().subscribe(new DiscordChatListener(this));
}
@OverridingMethodsMustInvokeSuper

View File

@ -19,7 +19,7 @@
package com.discordsrv.common;
import com.discordsrv.api.DiscordSRVApi;
import com.discordsrv.common.channel.ChannelConfig;
import com.discordsrv.common.channel.ChannelConfigHelper;
import com.discordsrv.common.channel.DefaultGlobalChannel;
import com.discordsrv.common.component.ComponentFactory;
import com.discordsrv.common.config.connection.ConnectionConfig;
@ -70,7 +70,7 @@ public interface DiscordSRV extends DiscordSRVApi {
// Internal
DefaultGlobalChannel defaultGlobalChannel();
ChannelConfig channelConfig();
ChannelConfigHelper channelConfig();
DiscordConnectionManager discordConnectionManager();
Locale locale();

View File

@ -19,26 +19,34 @@
package com.discordsrv.common.channel;
import com.discordsrv.api.channel.GameChannel;
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
import com.discordsrv.api.event.bus.Subscribe;
import com.discordsrv.api.event.events.channel.GameChannelLookupEvent;
import com.discordsrv.api.event.events.lifecycle.DiscordSRVReloadEvent;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
import com.discordsrv.common.config.main.channels.ChannelConfig;
import com.discordsrv.common.function.OrDefault;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.apache.commons.lang3.tuple.Pair;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class ChannelConfig {
public class ChannelConfigHelper {
private final DiscordSRV discordSRV;
private final LoadingCache<String, GameChannel> channels;
private final LoadingCache<String, GameChannel> nameToChannelCache;
private final Map<String, Pair<String, ChannelConfig>> 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<String, Pair<String, ChannelConfig>> newMap = new HashMap<>();
for (Map.Entry<String, BaseChannelConfig> 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<String, Pair<String, ChannelConfig>> entry : newMap.entrySet()) {
discordToConfigMap.put(entry.getKey(), entry.getValue());
}
}
}
private Map<String, BaseChannelConfig> channels() {
@ -65,9 +102,20 @@ public class ChannelConfig {
return orDefault(gameChannel.getOwnerName(), gameChannel.getChannelName());
}
public OrDefault<BaseChannelConfig> orDefault(String ownerName, String channelName) {
BaseChannelConfig defaultConfig = channels().computeIfAbsent(
public OrDefault<Pair<GameChannel, BaseChannelConfig>> orDefault(DiscordTextChannel discordTextChannel) {
return new OrDefault<>(
getDiscordResolved(discordTextChannel),
Pair.of(null, getDefault())
);
}
private BaseChannelConfig getDefault() {
return channels().computeIfAbsent(
"default", key -> new BaseChannelConfig());
}
public OrDefault<BaseChannelConfig> 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<GameChannel, BaseChannelConfig> getDiscordResolved(DiscordTextChannel channel) {
Pair<String, ? extends BaseChannelConfig> 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<String, ? extends BaseChannelConfig> getDiscord(DiscordTextChannel channel) {
synchronized (discordToConfigMap) {
return discordToConfigMap.get(channel.getId());
}
}
}

View File

@ -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);

View File

@ -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<BaseChannelConfig> {
@ -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
);

View File

@ -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()) {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.main.channels.discordtominecraft;
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
@ConfigSerializable
public class DiscordToMinecraftChatConfig {
public String format = "%user_name%: %message%";
}

View File

@ -83,6 +83,14 @@ public class DiscordDMChannelImpl extends DiscordMessageChannelImpl implements D
return mapExceptions(future);
}
@Override
public CompletableFuture<Void> deleteMessageById(String id) {
CompletableFuture<Void> future = privateChannel()
.deleteMessageById(id)
.submit();
return mapExceptions(future);
}
@Override
public @NotNull CompletableFuture<ReceivedDiscordMessage> editMessageById(String id, SendableDiscordMessage message) {
if (message.isWebhookMessage()) {

View File

@ -83,6 +83,11 @@ public class DiscordTextChannelImpl extends DiscordMessageChannelImpl implements
return message(message, WebhookClient::send, MessageChannel::sendMessage);
}
@Override
public CompletableFuture<Void> deleteMessageById(String id) {
return null; // TODO
}
@Override
public @NotNull CompletableFuture<ReceivedDiscordMessage> editMessageById(String id, SendableDiscordMessage message) {
return message(

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.discord.api.message;
import com.discordsrv.api.discord.api.entity.message.ReceivedDiscordMessage;

View File

@ -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<Void> delete() {
DiscordTextChannel textChannel = discordSRV.discordAPI().getTextChannelById(channelId).orElse(null);

View File

@ -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<Object> 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,11 +179,17 @@ public class JDAConnectionManager implements DiscordConnectionManager {
converted = new DiscordUserImpl((User) o);
} else {
converted = o;
isConversion = false;
}
if (isConversion) {
anyConverted = true;
}
newContext.add(converted);
}
if (anyConverted) {
event.process(PlaceholderLookupResult.newLookup(event.getPlaceholder(), newContext));
}
}
@Subscribe
public void onStatusChange(StatusChangeEvent event) {
@ -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;

View File

@ -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);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<Pair<GameChannel, BaseChannelConfig>> channelPair = discordSRV.channelConfig().orDefault(channel);
GameChannel gameChannel = channelPair.get(Pair::getKey);
if (gameChannel == null) {
return;
}
OrDefault<? extends BaseChannelConfig> channelConfig = channelPair.map(Pair::getValue);
OrDefault<DiscordToMinecraftChatConfig> 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);
}
}

View File

@ -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)));
});
}

View File

@ -69,6 +69,10 @@ public class PlaceholderServiceImpl implements PlaceholderService {
@Override
public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set<Object> 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) {

View File

@ -114,9 +114,6 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
// Service provider
game().eventManager().registerListeners(pluginContainer, this);
// Player related
playerProvider().subscribe();
super.enable();
}

View File

@ -24,6 +24,7 @@ import com.discordsrv.common.logging.logger.impl.Log4JLoggerImpl;
import com.discordsrv.sponge.SpongeDiscordSRV;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.command.exception.CommandException;
public class SpongeConsole implements Console {
@ -37,7 +38,7 @@ public class SpongeConsole implements Console {
}
@Override
public void sendMessage(Identity identity, Component message) {
public void sendMessage(Identity identity, @NotNull Component message) {
discordSRV.game().systemSubject().sendMessage(identity, message);
}

View File

@ -38,8 +38,10 @@ public class SpongePlayer extends SpongeOfflinePlayer implements IPlayer {
}
@Override
public void sendMessage(Identity identity, Component message) {
player.sendMessage(identity, message);
public void sendMessage(Identity identity, @NotNull Component message) {
player.sendMessage(
identity != null ? identity : Identity.nil(),
message);
}
@Override

View File

@ -113,9 +113,6 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
@Override
protected void enable() throws Throwable {
// Player related
playerProvider().subscribe();
super.enable();
}
}

View File

@ -24,6 +24,7 @@ import com.discordsrv.common.logging.logger.impl.Log4JLoggerImpl;
import com.discordsrv.velocity.VelocityDiscordSRV;
import net.kyori.adventure.identity.Identity;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
public class VelocityConsole implements Console {
@ -36,7 +37,7 @@ public class VelocityConsole implements Console {
}
@Override
public void sendMessage(Identity identity, Component message) {
public void sendMessage(Identity identity, @NotNull Component message) {
discordSRV.proxy().getConsoleCommandSource().sendMessage(identity, message);
}

View File

@ -36,8 +36,10 @@ public class VelocityPlayer implements IPlayer {
}
@Override
public void sendMessage(Identity identity, Component message) {
player.sendMessage(identity, message);
public void sendMessage(Identity identity, @NotNull Component message) {
player.sendMessage(
identity != null ? identity : Identity.nil(),
message);
}
@Override