mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-12-25 17:08:27 +01:00
More progress on placeholders & Minecraft -> Discord chat
This commit is contained in:
parent
3fb79cc50c
commit
ff8385dfda
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.component;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public interface EnhancedTextBuilder {
|
||||
|
||||
EnhancedTextBuilder addContext(Object... context);
|
||||
|
||||
default EnhancedTextBuilder addReplacement(String target, Object replacement) {
|
||||
return addReplacement(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
default EnhancedTextBuilder addReplacement(Pattern target, Object replacement) {
|
||||
return addReplacement(target, matcher -> replacement);
|
||||
}
|
||||
|
||||
default EnhancedTextBuilder addReplacement(String target, Supplier<Object> replacement) {
|
||||
return addReplacement(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
default EnhancedTextBuilder addReplacement(Pattern target, Supplier<Object> replacement) {
|
||||
return addReplacement(target, matcher -> replacement.get());
|
||||
}
|
||||
|
||||
default EnhancedTextBuilder addReplacement(String target, Function<Matcher, Object> replacement) {
|
||||
return addReplacement(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
EnhancedTextBuilder addReplacement(Pattern target, Function<Matcher, Object> replacement);
|
||||
|
||||
MinecraftComponent build();
|
||||
}
|
@ -66,7 +66,7 @@ public interface MinecraftComponent {
|
||||
*
|
||||
* @param gsonSerializerClass the gson serializer class
|
||||
* @return a adapter that will convert to/from relocated or unrelocated adventure classes to/from json
|
||||
* @throws IllegalArgumentException if the provided class is not a Adventure GsonComponentSerializer
|
||||
* @throws IllegalArgumentException if the provided class is not an Adventure GsonComponentSerializer
|
||||
*/
|
||||
@NotNull
|
||||
Adapter adventureAdapter(@NotNull Class<?> gsonSerializerClass);
|
||||
@ -96,7 +96,7 @@ public interface MinecraftComponent {
|
||||
}
|
||||
|
||||
/**
|
||||
* A Adventure adapter, converts from/to given adventure components from/to json.
|
||||
* An Adventure adapter, converts from/to given adventure components from/to json.
|
||||
*/
|
||||
interface Adapter {
|
||||
|
||||
|
@ -39,4 +39,6 @@ public interface MinecraftComponentFactory {
|
||||
*/
|
||||
@NotNull
|
||||
MinecraftComponent empty();
|
||||
|
||||
EnhancedTextBuilder enhancedBuilder(String content);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel;
|
||||
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.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.discord.api.entity.user.DiscordUser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -37,6 +38,8 @@ import java.util.Optional;
|
||||
*/
|
||||
public interface DiscordAPI {
|
||||
|
||||
SendableDiscordMessage.Formatter format(SendableDiscordMessage.Builder message);
|
||||
|
||||
/**
|
||||
* Gets a Discord message channel by id, the provided entity can be cached and will not update if it changes on Discord.
|
||||
* @param id the id for the message channel
|
||||
|
@ -29,6 +29,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A message that can be sent to Discord.
|
||||
@ -190,5 +194,33 @@ public interface SendableDiscordMessage {
|
||||
SendableDiscordMessage build();
|
||||
}
|
||||
|
||||
interface Formatter {
|
||||
|
||||
Formatter addContext(Object... context);
|
||||
|
||||
default Formatter addReplacement(String target, Object replacement) {
|
||||
return addReplacement(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
default Formatter addReplacement(Pattern target, Object replacement) {
|
||||
return addReplacement(target, matcher -> replacement);
|
||||
}
|
||||
|
||||
default Formatter addReplacement(String target, Supplier<Object> replacement) {
|
||||
return addReplacement(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
default Formatter addReplacement(Pattern target, Supplier<Object> replacement) {
|
||||
return addReplacement(target, matcher -> replacement.get());
|
||||
}
|
||||
|
||||
default Formatter addReplacement(String target, Function<Matcher, Object> replacement) {
|
||||
return addReplacement(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
Formatter addReplacement(Pattern target, Function<Matcher, Object> replacement);
|
||||
|
||||
SendableDiscordMessage build();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,10 +42,10 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
private final String webhookAvatarUrl;
|
||||
|
||||
protected SendableDiscordMessageImpl(String content,
|
||||
List<DiscordMessageEmbed> embeds,
|
||||
Set<AllowedMention> allowedMentions,
|
||||
String webhookUsername,
|
||||
String webhookAvatarUrl) {
|
||||
List<DiscordMessageEmbed> embeds,
|
||||
Set<AllowedMention> allowedMentions,
|
||||
String webhookUsername,
|
||||
String webhookAvatarUrl) {
|
||||
this.content = content;
|
||||
this.embeds = embeds;
|
||||
this.allowedMentions = allowedMentions;
|
||||
@ -78,7 +78,7 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
return webhookAvatarUrl;
|
||||
}
|
||||
|
||||
public static class BuilderImpl implements Builder {
|
||||
public static class BuilderImpl implements SendableDiscordMessage.Builder {
|
||||
|
||||
private String content;
|
||||
private final List<DiscordMessageEmbed> embeds = new ArrayList<>();
|
||||
|
@ -24,43 +24,32 @@
|
||||
package com.discordsrv.api.event.events.message.send.game;
|
||||
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.event.events.Cancellable;
|
||||
import com.discordsrv.api.event.events.Processable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public abstract class AbstractGameMessageSendEvent implements Cancellable, Processable {
|
||||
|
||||
private String discordMessage;
|
||||
private String discordUsername;
|
||||
private SendableDiscordMessage discordMessage;
|
||||
private GameChannel targetChannel;
|
||||
private boolean cancelled;
|
||||
private boolean processed;
|
||||
|
||||
public AbstractGameMessageSendEvent(@NotNull String discordMessage, @Nullable String discordUsername, @NotNull GameChannel targetChannel) {
|
||||
public AbstractGameMessageSendEvent(@NotNull SendableDiscordMessage discordMessage, @NotNull GameChannel targetChannel) {
|
||||
this.discordMessage = discordMessage;
|
||||
this.discordUsername = discordUsername;
|
||||
this.targetChannel = targetChannel;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getDiscordMessage() {
|
||||
public SendableDiscordMessage getDiscordMessage() {
|
||||
return discordMessage;
|
||||
}
|
||||
|
||||
public void setDiscordMessage(@NotNull String discordMessage) {
|
||||
public void setDiscordMessage(@NotNull SendableDiscordMessage discordMessage) {
|
||||
this.discordMessage = discordMessage;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getDiscordUsername() {
|
||||
return discordUsername;
|
||||
}
|
||||
|
||||
public void setDiscordUsername(@Nullable String discordUsername) {
|
||||
this.discordUsername = discordUsername;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public GameChannel getTargetChannel() {
|
||||
return targetChannel;
|
||||
|
@ -24,12 +24,12 @@
|
||||
package com.discordsrv.api.event.events.message.send.game;
|
||||
|
||||
import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ChatMessageSendEvent extends AbstractGameMessageSendEvent {
|
||||
|
||||
public ChatMessageSendEvent(@NotNull String discordMessage, @Nullable String discordUsername, @NotNull GameChannel targetChannel) {
|
||||
super(discordMessage, discordUsername, targetChannel);
|
||||
public ChatMessageSendEvent(@NotNull SendableDiscordMessage discordMessage, @NotNull GameChannel targetChannel) {
|
||||
super(discordMessage, targetChannel);
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ public class PlaceholderLookupResult {
|
||||
public static final PlaceholderLookupResult UNKNOWN_PLACEHOLDER = new PlaceholderLookupResult(Type.UNKNOWN_PLACEHOLDER);
|
||||
|
||||
public static PlaceholderLookupResult success(Object result) {
|
||||
return new PlaceholderLookupResult(String.valueOf(result));
|
||||
return new PlaceholderLookupResult(result);
|
||||
}
|
||||
|
||||
public static PlaceholderLookupResult newLookup(String placeholder, Set<Object> extras) {
|
||||
@ -40,7 +40,7 @@ public class PlaceholderLookupResult {
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final String value;
|
||||
private final Object value;
|
||||
private final Set<Object> extras;
|
||||
|
||||
protected PlaceholderLookupResult(Type type) {
|
||||
@ -49,7 +49,7 @@ public class PlaceholderLookupResult {
|
||||
this.extras = null;
|
||||
}
|
||||
|
||||
protected PlaceholderLookupResult(String value) {
|
||||
protected PlaceholderLookupResult(Object value) {
|
||||
this.type = Type.SUCCESS;
|
||||
this.value = value;
|
||||
this.extras = null;
|
||||
@ -65,7 +65,7 @@ public class PlaceholderLookupResult {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlaceholderResultConverter {
|
||||
|
||||
/**
|
||||
* Converts a successful placeholder lookup result into a {@link String}.
|
||||
* @param result the result
|
||||
* @return the result in {@link String} form
|
||||
*/
|
||||
String convertPlaceholderResult(@NotNull Object result);
|
||||
}
|
@ -23,7 +23,10 @@
|
||||
|
||||
package com.discordsrv.api.placeholder;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public interface PlaceholderService {
|
||||
@ -38,9 +41,16 @@ public interface PlaceholderService {
|
||||
*/
|
||||
Pattern RECURSIVE_PATTERN = Pattern.compile("(\\{)(.+)(})");
|
||||
|
||||
PlaceholderLookupResult lookupPlaceholder(String placeholder, Set<Object> context);
|
||||
PlaceholderLookupResult lookupPlaceholder(String placeholder, Object... context);
|
||||
void addResultConverter(@NotNull PlaceholderResultConverter resultConverter);
|
||||
void removeResultConverter(@NotNull PlaceholderResultConverter resultConverter);
|
||||
|
||||
String replacePlaceholders(@NotNull String placeholder, @NotNull Set<Object> context);
|
||||
String replacePlaceholders(@NotNull String placeholder, @NotNull Object... context);
|
||||
|
||||
PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set<Object> context);
|
||||
PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Object... context);
|
||||
|
||||
Object getResult(@NotNull Matcher matcher, @NotNull Set<Object> context);
|
||||
String getResultAsString(@NotNull Matcher matcher, @NotNull Set<Object> context);
|
||||
|
||||
String replacePlaceholders(String placeholder, Set<Object> context);
|
||||
String replacePlaceholders(String placeholder, Object... context);
|
||||
}
|
||||
|
@ -26,9 +26,11 @@ import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.craftbukkit.BukkitComponentSerializer;
|
||||
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+
|
||||
@ -72,12 +74,13 @@ public class BukkitPlayer extends BukkitOfflinePlayer implements IPlayer {
|
||||
|
||||
@SuppressWarnings("deprecation") // Paper
|
||||
@Override
|
||||
public Component displayName() {
|
||||
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());
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public class BungeePlayer implements IPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component displayName() {
|
||||
public @NotNull Component displayName() {
|
||||
return BungeeComponentUtil.fromLegacy(player.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ import com.discordsrv.common.listener.DefaultChatListener;
|
||||
import com.discordsrv.common.logging.DependencyLoggingFilter;
|
||||
import com.discordsrv.common.logging.logger.backend.LoggingBackend;
|
||||
import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
|
||||
import com.discordsrv.common.placeholder.converter.ComponentResultConverter;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -84,7 +85,7 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
protected final void load() {
|
||||
this.eventBus = new EventBusImpl(this);
|
||||
this.placeholderService = new PlaceholderServiceImpl(this);
|
||||
this.componentFactory = new ComponentFactory();
|
||||
this.componentFactory = new ComponentFactory(this);
|
||||
this.discordAPI = new DiscordAPIImpl(this);
|
||||
this.discordConnectionDetails = new DiscordConnectionDetailsImpl(this);
|
||||
}
|
||||
@ -239,6 +240,9 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
|
||||
discordConnectionManager = new JDAConnectionManager(this);
|
||||
discordConnectionManager.connect().join();
|
||||
|
||||
// Placeholder result converters
|
||||
placeholderService().addResultConverter(new ComponentResultConverter());
|
||||
|
||||
// Register PlayerProvider listeners
|
||||
playerProvider().subscribe();
|
||||
|
||||
|
@ -22,7 +22,6 @@ import com.discordsrv.api.channel.GameChannel;
|
||||
import com.discordsrv.api.event.events.channel.GameChannelLookupEvent;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfigHolder;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
@ -55,7 +54,7 @@ public class ChannelConfig {
|
||||
});
|
||||
}
|
||||
|
||||
private Map<String, ChannelConfigHolder> channels() {
|
||||
private Map<String, BaseChannelConfig> channels() {
|
||||
return discordSRV.config().channels;
|
||||
}
|
||||
|
||||
@ -64,12 +63,12 @@ public class ChannelConfig {
|
||||
}
|
||||
|
||||
public OrDefault<BaseChannelConfig> orDefault(String ownerName, String channelName) {
|
||||
ChannelConfigHolder defaultConfig = channels().computeIfAbsent(
|
||||
"default", key -> new ChannelConfigHolder(new BaseChannelConfig()));
|
||||
BaseChannelConfig defaultConfig = channels().computeIfAbsent(
|
||||
"default", key -> new BaseChannelConfig());
|
||||
|
||||
return new OrDefault<>(
|
||||
get(ownerName, channelName),
|
||||
defaultConfig.get()
|
||||
defaultConfig
|
||||
);
|
||||
}
|
||||
|
||||
@ -79,15 +78,15 @@ public class ChannelConfig {
|
||||
|
||||
public BaseChannelConfig get(String ownerName, String channelName) {
|
||||
if (ownerName != null) {
|
||||
ChannelConfigHolder config = channels().get(ownerName + ":" + channelName);
|
||||
BaseChannelConfig config = channels().get(ownerName + ":" + channelName);
|
||||
if (config != null) {
|
||||
return config.get();
|
||||
return config;
|
||||
}
|
||||
|
||||
GameChannel gameChannel = channels.get(channelName);
|
||||
if (gameChannel != null && gameChannel.getOwnerName().equals(ownerName)) {
|
||||
config = channels().get(channelName);
|
||||
return config != null ? config.get() : null;
|
||||
return config;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -18,14 +18,27 @@
|
||||
|
||||
package com.discordsrv.common.component;
|
||||
|
||||
import com.discordsrv.api.component.EnhancedTextBuilder;
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.component.MinecraftComponentFactory;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ComponentFactory implements MinecraftComponentFactory {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
|
||||
public ComponentFactory(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull MinecraftComponent empty() {
|
||||
return MinecraftComponentImpl.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnhancedTextBuilder enhancedBuilder(String content) {
|
||||
return new EnhancedTextBuilderImpl(discordSRV, content);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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.component;
|
||||
|
||||
import com.discordsrv.api.component.EnhancedTextBuilder;
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import dev.vankka.enhancedlegacytext.EnhancedComponentBuilder;
|
||||
import dev.vankka.enhancedlegacytext.EnhancedLegacyText;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EnhancedTextBuilderImpl implements EnhancedTextBuilder {
|
||||
|
||||
private final Set<Object> context = new HashSet<>();
|
||||
private final Map<Pattern, Function<Matcher, Object>> replacements = new HashMap<>();
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final String enhancedFormat;
|
||||
|
||||
public EnhancedTextBuilderImpl(DiscordSRV discordSRV, String enhancedFormat) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.enhancedFormat = enhancedFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnhancedTextBuilder addContext(Object... context) {
|
||||
this.context.addAll(Arrays.asList(context));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnhancedTextBuilder addReplacement(Pattern target, Function<Matcher, Object> replacement) {
|
||||
this.replacements.put(target, replacement);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinecraftComponent build() {
|
||||
EnhancedComponentBuilder builder = EnhancedLegacyText.get()
|
||||
.buildComponent(enhancedFormat);
|
||||
|
||||
replacements.forEach(builder::replaceAll);
|
||||
builder.replaceAll(PlaceholderService.PATTERN,
|
||||
matcher -> discordSRV.placeholderService().getResult(matcher, context));
|
||||
|
||||
Component component = builder.build();
|
||||
return ComponentUtil.toAPI(component);
|
||||
}
|
||||
}
|
@ -26,6 +26,8 @@ import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
public class ConnectionConfig implements Config {
|
||||
|
||||
public static final String FILE_NAME = "connections.yaml";
|
||||
|
||||
@Override
|
||||
public final String getFileName() {
|
||||
return FILE_NAME;
|
||||
}
|
||||
@ -37,5 +39,6 @@ public class ConnectionConfig implements Config {
|
||||
|
||||
@Comment("Don't know what this is? Neither do I")
|
||||
public String token = "Token here";
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ import com.discordsrv.common.config.Config;
|
||||
import com.discordsrv.common.config.annotation.DefaultOnly;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfigHolder;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -32,13 +31,15 @@ import java.util.Map;
|
||||
public class MainConfig implements Config {
|
||||
|
||||
public static final String FILE_NAME = "config.yaml";
|
||||
|
||||
@Override
|
||||
public final String getFileName() {
|
||||
return FILE_NAME;
|
||||
}
|
||||
|
||||
@DefaultOnly("default")
|
||||
public Map<String, ChannelConfigHolder> channels = new HashMap<String, ChannelConfigHolder>() {{
|
||||
put("default", new ChannelConfigHolder(new BaseChannelConfig()));
|
||||
put("global", new ChannelConfigHolder(new ChannelConfig()));
|
||||
public Map<String, BaseChannelConfig> channels = new HashMap<String, BaseChannelConfig>() {{
|
||||
put("default", new BaseChannelConfig());
|
||||
put("global", new ChannelConfig());
|
||||
}};
|
||||
}
|
||||
|
@ -19,10 +19,50 @@
|
||||
package com.discordsrv.common.config.main.channels;
|
||||
|
||||
import com.discordsrv.common.config.main.channels.minecraftodiscord.MinecraftToDiscordChatConfig;
|
||||
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.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
@ConfigSerializable
|
||||
public class BaseChannelConfig {
|
||||
|
||||
public MinecraftToDiscordChatConfig minecraftToDiscord = new MinecraftToDiscordChatConfig();
|
||||
|
||||
|
||||
public static class Serializer implements TypeSerializer<BaseChannelConfig> {
|
||||
|
||||
private final ObjectMapper.Factory mapperFactory;
|
||||
|
||||
public Serializer(ObjectMapper.Factory mapperFactory) {
|
||||
this.mapperFactory = mapperFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseChannelConfig deserialize(Type type, ConfigurationNode node) throws SerializationException {
|
||||
return (BaseChannelConfig) mapperFactory.asTypeSerializer()
|
||||
.deserialize(
|
||||
"default".equals(node.key()) ? BaseChannelConfig.class : ChannelConfig.class,
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Type type, @Nullable BaseChannelConfig obj, ConfigurationNode node) throws SerializationException {
|
||||
if (obj == null) {
|
||||
node.set(null);
|
||||
return;
|
||||
}
|
||||
|
||||
mapperFactory.asTypeSerializer().serialize(
|
||||
"default".equals(node.key()) ? BaseChannelConfig.class : ChannelConfig.class,
|
||||
obj,
|
||||
node
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ package com.discordsrv.common.config.main.channels;
|
||||
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Comment;
|
||||
import org.spongepowered.configurate.objectmapping.meta.Setting;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
@ -31,8 +30,6 @@ import java.util.List;
|
||||
@ConfigSerializable
|
||||
public class ChannelConfig extends BaseChannelConfig {
|
||||
|
||||
protected static final String CHANNEL_IDS_OPTION_NAME = "ChannelIds";
|
||||
|
||||
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()) {
|
||||
@ -50,7 +47,6 @@ public class ChannelConfig extends BaseChannelConfig {
|
||||
}
|
||||
}
|
||||
|
||||
@Setting(CHANNEL_IDS_OPTION_NAME)
|
||||
@Comment("The channels this in-game channel will forward to in Discord")
|
||||
public List<String> channelIds = new ArrayList<>(Collections.singletonList("channel-id-here"));
|
||||
|
||||
|
@ -1,74 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.config.main.channels;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
import org.spongepowered.configurate.objectmapping.ObjectMapper;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.serialize.TypeSerializer;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* A bit of a trick to have two different types in the same map with Configurate.
|
||||
*/
|
||||
public class ChannelConfigHolder {
|
||||
|
||||
private final BaseChannelConfig obj;
|
||||
|
||||
public ChannelConfigHolder(BaseChannelConfig obj) {
|
||||
this.obj = obj;
|
||||
}
|
||||
|
||||
public BaseChannelConfig get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
public static class Serializer implements TypeSerializer<ChannelConfigHolder> {
|
||||
|
||||
private final ObjectMapper.Factory mapperFactory;
|
||||
|
||||
public Serializer(ObjectMapper.Factory mapperFactory) {
|
||||
this.mapperFactory = mapperFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelConfigHolder deserialize(Type type, ConfigurationNode node) throws SerializationException {
|
||||
boolean channelsNotPresent = node.node(ChannelConfig.CHANNEL_IDS_OPTION_NAME).empty();
|
||||
BaseChannelConfig channelConfig = (BaseChannelConfig) mapperFactory
|
||||
.get(channelsNotPresent ? BaseChannelConfig.class : ChannelConfig.class)
|
||||
.load(node);
|
||||
return new ChannelConfigHolder(channelConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(Type type, @Nullable ChannelConfigHolder obj, ConfigurationNode node) throws SerializationException {
|
||||
map(obj != null ? obj.get() : null, node);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends BaseChannelConfig> void map(BaseChannelConfig obj, ConfigurationNode node) throws SerializationException {
|
||||
mapperFactory
|
||||
.get((Class<T>) (obj instanceof ChannelConfig ? ChannelConfig.class : BaseChannelConfig.class))
|
||||
.save((T) obj, node);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -28,8 +28,6 @@ public class MinecraftToDiscordChatConfig {
|
||||
@Setting("Format")
|
||||
public SendableDiscordMessage.Builder messageFormat = SendableDiscordMessage.builder()
|
||||
.setWebhookUsername("%player_display_name%")
|
||||
.setContent("%player_message%");// TODO
|
||||
|
||||
@Setting("UseWebhooks")
|
||||
public boolean useWebhooks = false;
|
||||
.setContent("%message%");// TODO
|
||||
|
||||
}
|
||||
|
@ -24,7 +24,9 @@ import com.discordsrv.common.config.manager.loader.YamlConfigLoaderProvider;
|
||||
import com.discordsrv.common.config.manager.manager.TranslatedConfigManager;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
public abstract class ConnectionConfigManager<C extends ConnectionConfig> extends TranslatedConfigManager<C, YamlConfigurationLoader> implements YamlConfigLoaderProvider {
|
||||
public abstract class ConnectionConfigManager<C extends ConnectionConfig>
|
||||
extends TranslatedConfigManager<C, YamlConfigurationLoader>
|
||||
implements YamlConfigLoaderProvider {
|
||||
|
||||
public ConnectionConfigManager(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
|
@ -18,22 +18,12 @@
|
||||
|
||||
package com.discordsrv.common.config.manager;
|
||||
|
||||
import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.main.MainConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfigHolder;
|
||||
import com.discordsrv.common.config.manager.loader.YamlConfigLoaderProvider;
|
||||
import com.discordsrv.common.config.manager.manager.TranslatedConfigManager;
|
||||
import com.discordsrv.common.config.serializer.ColorSerializer;
|
||||
import com.discordsrv.common.config.serializer.DiscordMessageEmbedSerializer;
|
||||
import com.discordsrv.common.config.serializer.SendableDiscordMessageSerializer;
|
||||
import org.spongepowered.configurate.ConfigurationOptions;
|
||||
import org.spongepowered.configurate.objectmapping.ObjectMapper;
|
||||
import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public abstract class MainConfigManager<C extends MainConfig>
|
||||
extends TranslatedConfigManager<C, YamlConfigurationLoader>
|
||||
implements YamlConfigLoaderProvider {
|
||||
@ -46,17 +36,4 @@ public abstract class MainConfigManager<C extends MainConfig>
|
||||
protected String fileName() {
|
||||
return MainConfig.FILE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationOptions defaultOptions() {
|
||||
return super.defaultOptions()
|
||||
.serializers(builder -> {
|
||||
ObjectMapper.Factory objectMapper = defaultObjectMapper();
|
||||
builder.register(Color.class, new ColorSerializer());
|
||||
builder.register(ChannelConfigHolder.class, new ChannelConfigHolder.Serializer(objectMapper));
|
||||
builder.register(DiscordMessageEmbed.Builder.class, new DiscordMessageEmbedSerializer());
|
||||
builder.register(DiscordMessageEmbed.Field.class, new DiscordMessageEmbedSerializer.FieldSerializer());
|
||||
builder.register(SendableDiscordMessage.Builder.class, new SendableDiscordMessageSerializer());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -18,9 +18,15 @@
|
||||
|
||||
package com.discordsrv.common.config.manager.manager;
|
||||
|
||||
import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.config.annotation.DefaultOnly;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.manager.loader.ConfigLoaderProvider;
|
||||
import com.discordsrv.common.config.serializer.ColorSerializer;
|
||||
import com.discordsrv.common.config.serializer.DiscordMessageEmbedSerializer;
|
||||
import com.discordsrv.common.config.serializer.SendableDiscordMessageSerializer;
|
||||
import com.discordsrv.common.exception.ConfigException;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||
@ -31,7 +37,9 @@ import org.spongepowered.configurate.loader.AbstractConfigurationLoader;
|
||||
import org.spongepowered.configurate.objectmapping.ConfigSerializable;
|
||||
import org.spongepowered.configurate.objectmapping.ObjectMapper;
|
||||
import org.spongepowered.configurate.serialize.SerializationException;
|
||||
import org.spongepowered.configurate.util.NamingSchemes;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@ -73,31 +81,40 @@ public abstract class ConfigurateConfigManager<T, LT extends AbstractConfigurati
|
||||
|
||||
public ConfigurationOptions defaultOptions() {
|
||||
return ConfigurationOptions.defaults()
|
||||
.shouldCopyDefaults(false);
|
||||
}
|
||||
|
||||
protected ObjectMapper.Factory.Builder objectMapperBuilder() {
|
||||
return ObjectMapper.factoryBuilder();
|
||||
.shouldCopyDefaults(false)
|
||||
.implicitInitialization(false)
|
||||
.serializers(builder -> {
|
||||
ObjectMapper.Factory objectMapper = configObjectMapper();
|
||||
builder.register(Color.class, new ColorSerializer());
|
||||
builder.register(BaseChannelConfig.class, new BaseChannelConfig.Serializer(objectMapper));
|
||||
builder.register(DiscordMessageEmbed.Builder.class, new DiscordMessageEmbedSerializer());
|
||||
builder.register(DiscordMessageEmbed.Field.class, new DiscordMessageEmbedSerializer.FieldSerializer());
|
||||
builder.register(SendableDiscordMessage.Builder.class, new SendableDiscordMessageSerializer());
|
||||
});
|
||||
}
|
||||
|
||||
public ConfigurationOptions configNodeOptions() {
|
||||
return defaultOptions();
|
||||
}
|
||||
|
||||
protected ObjectMapper.Factory.Builder configObjectMapperBuilder() {
|
||||
return objectMapperBuilder();
|
||||
}
|
||||
|
||||
public ObjectMapper.Factory configObjectMapper() {
|
||||
return configObjectMapper;
|
||||
}
|
||||
|
||||
public ConfigurationOptions defaultNodeOptions() {
|
||||
return defaultOptions();
|
||||
}
|
||||
|
||||
protected ObjectMapper.Factory.Builder objectMapperBuilder() {
|
||||
return ObjectMapper.factoryBuilder()
|
||||
.defaultNamingScheme(input -> {
|
||||
String camelCase = NamingSchemes.CAMEL_CASE.coerce(input); // Gets rid of underscores and dashes
|
||||
return Character.toUpperCase(camelCase.charAt(0)) + camelCase.substring(1);
|
||||
});
|
||||
}
|
||||
|
||||
protected ObjectMapper.Factory.Builder configObjectMapperBuilder() {
|
||||
return objectMapperBuilder();
|
||||
}
|
||||
|
||||
protected ObjectMapper.Factory.Builder defaultObjectMapperBuilder() {
|
||||
return configObjectMapperBuilder()
|
||||
return objectMapperBuilder()
|
||||
.addProcessor(DefaultOnly.class, (data, value) -> (value1, destination) -> {
|
||||
String[] children = data.value();
|
||||
boolean whitelist = data.whitelist();
|
||||
@ -131,6 +148,10 @@ public abstract class ConfigurateConfigManager<T, LT extends AbstractConfigurati
|
||||
});
|
||||
}
|
||||
|
||||
public ObjectMapper.Factory configObjectMapper() {
|
||||
return configObjectMapper;
|
||||
}
|
||||
|
||||
public ObjectMapper.Factory defaultObjectMapper() {
|
||||
return defaultObjectMapper;
|
||||
}
|
||||
@ -190,14 +211,19 @@ public abstract class ConfigurateConfigManager<T, LT extends AbstractConfigurati
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void save() throws ConfigException {
|
||||
try {
|
||||
CommentedConfigurationNode node = loader.createNode();
|
||||
node.set(configuration);
|
||||
save(configuration, (Class<T>) configuration.getClass(), node);
|
||||
loader.save(node);
|
||||
} catch (ConfigurateException e) {
|
||||
throw new ConfigException("Failed to load configuration", e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void save(T config, Class<T> clazz, CommentedConfigurationNode node) throws SerializationException {
|
||||
configObjectMapper().get(clazz).save(config, node);
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,8 @@ import org.spongepowered.configurate.yaml.YamlConfigurationLoader;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
public abstract class TranslatedConfigManager<T extends Config, LT extends AbstractConfigurationLoader<CommentedConfigurationNode>> extends ConfigurateConfigManager<T, LT> {
|
||||
public abstract class TranslatedConfigManager<T extends Config, LT extends AbstractConfigurationLoader<CommentedConfigurationNode>>
|
||||
extends ConfigurateConfigManager<T, LT> {
|
||||
|
||||
public TranslatedConfigManager(DiscordSRV discordSRV) {
|
||||
super(discordSRV);
|
||||
@ -55,6 +56,7 @@ public abstract class TranslatedConfigManager<T extends Config, LT extends Abstr
|
||||
return translation;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void translate() throws ConfigException {
|
||||
T config = config();
|
||||
if (config == null) {
|
||||
@ -70,7 +72,8 @@ public abstract class TranslatedConfigManager<T extends Config, LT extends Abstr
|
||||
translation = translation.node(config.getFileName());
|
||||
|
||||
CommentedConfigurationNode node = loader().createNode();
|
||||
node.set(config);
|
||||
save(config, (Class<T>) config.getClass(), node);
|
||||
//node.set(config);
|
||||
translateNode(node, translation, translation.node("_comments"));
|
||||
} catch (ConfigurateException e) {
|
||||
throw new ConfigException(e);
|
||||
|
@ -45,7 +45,9 @@ public class SendableDiscordMessageSerializer implements TypeSerializer<Sendable
|
||||
|
||||
ConfigurationNode webhook = node.node("Webhook");
|
||||
String webhookUsername = webhook.node("Username").getString();
|
||||
if (webhook.node("Enabled").getBoolean(webhook.node("Enable").getBoolean(webhookUsername != null))) {
|
||||
if (webhook.node("Enabled").getBoolean(
|
||||
webhook.node("Enable").getBoolean(
|
||||
webhookUsername != null))) {
|
||||
builder.setWebhookUsername(webhookUsername);
|
||||
builder.setWebhookAvatarUrl(webhook.node("AvatarUrl").getString());
|
||||
}
|
||||
@ -82,7 +84,9 @@ public class SendableDiscordMessageSerializer implements TypeSerializer<Sendable
|
||||
|
||||
List<DiscordMessageEmbed.Builder> embedBuilders = new ArrayList<>();
|
||||
obj.getEmbeds().forEach(embed -> embedBuilders.add(embed.toBuilder()));
|
||||
node.setList(DiscordMessageEmbed.Builder.class, embedBuilders);
|
||||
if (!embedBuilders.isEmpty()) {
|
||||
node.node("Embeds").setList(DiscordMessageEmbed.Builder.class, embedBuilders);
|
||||
}
|
||||
|
||||
node.node("Content").set(obj.getContent());
|
||||
}
|
||||
|
@ -25,18 +25,22 @@ import com.discordsrv.api.discord.api.entity.channel.DiscordDMChannel;
|
||||
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.guild.DiscordGuild;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.discord.api.entity.user.DiscordUser;
|
||||
import com.discordsrv.api.discord.api.exception.NotReadyException;
|
||||
import com.discordsrv.api.discord.api.exception.UnknownChannelException;
|
||||
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.config.main.channels.ChannelConfigHolder;
|
||||
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.message.SendableDiscordMessageFormatterImpl;
|
||||
import com.discordsrv.common.discord.api.user.DiscordUserImpl;
|
||||
import com.github.benmanes.caffeine.cache.*;
|
||||
import com.github.benmanes.caffeine.cache.AsyncCacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
|
||||
import com.github.benmanes.caffeine.cache.Expiry;
|
||||
import com.github.benmanes.caffeine.cache.RemovalListener;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
@ -72,6 +76,11 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
return cachedClients.get(channelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendableDiscordMessage.Formatter format(SendableDiscordMessage.Builder message) {
|
||||
return new SendableDiscordMessageFormatterImpl(discordSRV, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Optional<? extends DiscordMessageChannel> getMessageChannelById(@NotNull String id) {
|
||||
Optional<DiscordTextChannel> textChannel = getTextChannelById(id);
|
||||
@ -163,8 +172,7 @@ public class DiscordAPIImpl implements DiscordAPI {
|
||||
private class WebhookCacheExpiry implements Expiry<String, WebhookClient> {
|
||||
|
||||
private boolean isConfiguredChannel(String channelId) {
|
||||
for (ChannelConfigHolder value : discordSRV.config().channels.values()) {
|
||||
BaseChannelConfig config = value.get();
|
||||
for (BaseChannelConfig config : discordSRV.config().channels.values()) {
|
||||
if (config instanceof ChannelConfig
|
||||
&& ((ChannelConfig) config).channelIds.contains(channelId)) {
|
||||
return true;
|
||||
|
@ -23,11 +23,11 @@ import club.minnced.discord.webhook.receive.ReadonlyMessage;
|
||||
import club.minnced.discord.webhook.receive.ReadonlyUser;
|
||||
import club.minnced.discord.webhook.send.WebhookEmbed;
|
||||
import com.discordsrv.api.discord.api.entity.channel.DiscordTextChannel;
|
||||
import com.discordsrv.api.discord.api.exception.UnknownChannelException;
|
||||
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.exception.UnknownChannelException;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.SendableDiscordMessage;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.placeholder.converter.ComponentResultConverter;
|
||||
import com.discordsrv.common.string.util.Placeholders;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessage.Formatter {
|
||||
|
||||
private final Set<Object> context = new HashSet<>();
|
||||
private final Map<Pattern, Function<Matcher, Object>> replacements = new HashMap<>();
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final SendableDiscordMessage.Builder builder;
|
||||
|
||||
public SendableDiscordMessageFormatterImpl(DiscordSRV discordSRV, SendableDiscordMessage.Builder builder) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendableDiscordMessage.Formatter addContext(Object... context) {
|
||||
this.context.addAll(Arrays.asList(context));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendableDiscordMessage.Formatter addReplacement(Pattern target, Function<Matcher, Object> replacement) {
|
||||
this.replacements.put(target, replacement);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SendableDiscordMessage build() {
|
||||
Function<String, String> placeholders = input -> {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Placeholders(input)
|
||||
.addAll(replacements)
|
||||
.replaceAll(PlaceholderService.PATTERN,
|
||||
matcher -> discordSRV.placeholderService().getResultAsString(matcher, context))
|
||||
.get();
|
||||
};
|
||||
|
||||
ComponentResultConverter.plain(() ->
|
||||
builder.setWebhookUsername(placeholders.apply(builder.getWebhookUsername())));
|
||||
builder.setContent(placeholders.apply(builder.getContent()));
|
||||
|
||||
// TODO: rest of the content, escaping unwanted characters
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
}
|
@ -28,8 +28,9 @@ import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfig;
|
||||
import com.discordsrv.common.config.main.channels.minecraftodiscord.MinecraftToDiscordChatConfig;
|
||||
import com.discordsrv.common.function.OrDefault;
|
||||
import com.discordsrv.common.player.util.PlayerUtil;
|
||||
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
import java.util.Collections;
|
||||
@ -49,23 +50,23 @@ public class DefaultChatListener extends AbstractListener {
|
||||
|
||||
GameChannel gameChannel = event.getGameChannel();
|
||||
Component message = ComponentUtil.fromAPI(event.message());
|
||||
Component displayName = PlayerUtil.displayName(event.getPlayer());
|
||||
|
||||
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(gameChannel);
|
||||
OrDefault<MinecraftToDiscordChatConfig> chatConfig = channelConfig.map(cfg -> cfg.minecraftToDiscord);
|
||||
|
||||
// Component discordMessage = EnhancedLegacyText.get().buildComponent(channelConfig.map(cfg -> cfg.minecraftToDiscord).get(cfg -> cfg.messageFormat))
|
||||
// .replace("%message%", message)
|
||||
// .replace("%player_display_name%", displayName)
|
||||
// .build();
|
||||
//
|
||||
// String username = new Placeholders(channelConfig.map(cfg -> cfg.minecraftToDiscord).get(cfg -> cfg.usernameFormat))
|
||||
// .replace("%player_display_name%", () -> PlainTextComponentSerializer.plainText().serialize(displayName))
|
||||
// .get();
|
||||
SendableDiscordMessage.Builder builder = chatConfig.get(cfg -> cfg.messageFormat);
|
||||
if (builder == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
SendableDiscordMessage discordMessage = discordSRV.discordAPI().format(builder)
|
||||
.addContext(event.getPlayer())
|
||||
.addReplacement("%message%", DiscordSerializer.INSTANCE.serialize(message))
|
||||
.build();
|
||||
|
||||
discordSRV.eventBus().publish(
|
||||
new ChatMessageSendEvent(
|
||||
null,
|
||||
null,
|
||||
discordMessage,
|
||||
gameChannel
|
||||
)
|
||||
);
|
||||
@ -86,13 +87,7 @@ public class DefaultChatListener extends AbstractListener {
|
||||
|
||||
for (String channelId : channelIds) {
|
||||
discordSRV.discordAPI().getTextChannelById(channelId).ifPresent(textChannel ->
|
||||
textChannel.sendMessage(
|
||||
SendableDiscordMessage.builder()
|
||||
.setWebhookUsername(event.getDiscordUsername())
|
||||
.setContent(event.getDiscordMessage())
|
||||
.build()
|
||||
)
|
||||
);
|
||||
textChannel.sendMessage(event.getDiscordMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ package com.discordsrv.common.placeholder;
|
||||
import com.discordsrv.api.event.events.placeholder.PlaceholderLookupEvent;
|
||||
import com.discordsrv.api.placeholder.Placeholder;
|
||||
import com.discordsrv.api.placeholder.PlaceholderLookupResult;
|
||||
import com.discordsrv.api.placeholder.PlaceholderResultConverter;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.placeholder.provider.AnnotationPlaceholderProvider;
|
||||
@ -29,11 +30,13 @@ import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
import com.github.benmanes.caffeine.cache.LoadingCache;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -42,6 +45,7 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
|
||||
private final DiscordSRV discordSRV;
|
||||
private final LoadingCache<Class<?>, Set<PlaceholderProvider>> classProviders;
|
||||
private final Set<PlaceholderResultConverter> converters = new CopyOnWriteArraySet<>();
|
||||
|
||||
public PlaceholderServiceImpl(DiscordSRV discordSRV) {
|
||||
this.discordSRV = discordSRV;
|
||||
@ -58,13 +62,20 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaceholderLookupResult lookupPlaceholder(String placeholder, Object... context) {
|
||||
public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, Object... context) {
|
||||
return lookupPlaceholder(placeholder, getArrayAsSet(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlaceholderLookupResult lookupPlaceholder(String placeholder, Set<Object> context) {
|
||||
public PlaceholderLookupResult lookupPlaceholder(@NotNull String placeholder, @NotNull Set<Object> context) {
|
||||
for (Object o : context) {
|
||||
if (o instanceof PlaceholderProvider) {
|
||||
PlaceholderLookupResult result = ((PlaceholderProvider) o).lookup(placeholder, context);
|
||||
if (result.getType() != PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Set<PlaceholderProvider> providers = classProviders.get(o.getClass());
|
||||
if (providers == null) {
|
||||
continue;
|
||||
@ -88,35 +99,108 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String replacePlaceholders(String input, Object... context) {
|
||||
public String replacePlaceholders(@NotNull String input, Object... context) {
|
||||
return replacePlaceholders(input, getArrayAsSet(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String replacePlaceholders(String input, Set<Object> context) {
|
||||
return processReplacement(PATTERN, input, context);
|
||||
public void addResultConverter(@NotNull PlaceholderResultConverter resultConverter) {
|
||||
converters.add(resultConverter);
|
||||
}
|
||||
|
||||
private String processReplacement(Pattern pattern, String input, Set<Object> context) {
|
||||
@Override
|
||||
public void removeResultConverter(@NotNull PlaceholderResultConverter resultConverter) {
|
||||
converters.remove(resultConverter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String replacePlaceholders(@NotNull String input, @NotNull Set<Object> context) {
|
||||
return getReplacement(PATTERN, input, context);
|
||||
}
|
||||
|
||||
private String getReplacement(Pattern pattern, String input, Set<Object> context) {
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
|
||||
String output = input;
|
||||
while (matcher.find()) {
|
||||
String placeholder = matcher.group(2);
|
||||
String originalPlaceholder = placeholder;
|
||||
|
||||
// Recursive
|
||||
placeholder = processReplacement(RECURSIVE_PATTERN, placeholder, context);
|
||||
|
||||
PlaceholderLookupResult result = lookupPlaceholder(placeholder, context);
|
||||
output = updateBasedOnResult(result, input, originalPlaceholder, matcher);
|
||||
PlaceholderLookupResult result = resolve(placeholder, context);
|
||||
output = updateContent(result, placeholder, matcher, output);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
private String updateBasedOnResult(
|
||||
PlaceholderLookupResult result, String input, String originalPlaceholder, Matcher matcher) {
|
||||
String output = input;
|
||||
@Override
|
||||
public Object getResult(@NotNull Matcher matcher, @NotNull Set<Object> context) {
|
||||
if (matcher.groupCount() < 3) {
|
||||
throw new IllegalStateException("Matcher must have atleast 3 groups");
|
||||
}
|
||||
String placeholder = matcher.group(2);
|
||||
PlaceholderLookupResult result = resolve(matcher, context);
|
||||
return getResultRepresentation(result, placeholder, matcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResultAsString(@NotNull Matcher matcher, @NotNull Set<Object> context) {
|
||||
Object result = getResult(matcher, context);
|
||||
return getResultAsString(result);
|
||||
}
|
||||
|
||||
private String getResultAsString(Object result) {
|
||||
if (result == null) {
|
||||
return "null";
|
||||
} else if (result instanceof String) {
|
||||
return (String) result;
|
||||
}
|
||||
|
||||
String output = null;
|
||||
for (PlaceholderResultConverter converter : converters) {
|
||||
output = converter.convertPlaceholderResult(result);
|
||||
if (output != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (output == null) {
|
||||
output = String.valueOf(result);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public PlaceholderLookupResult resolve(Matcher matcher, Set<Object> context) {
|
||||
return resolve(matcher.group(2), context);
|
||||
}
|
||||
|
||||
private PlaceholderLookupResult resolve(String placeholder, Set<Object> context) {
|
||||
// Recursive
|
||||
placeholder = getReplacement(RECURSIVE_PATTERN, placeholder, context);
|
||||
|
||||
return lookupPlaceholder(placeholder, context);
|
||||
}
|
||||
|
||||
private String updateContent(
|
||||
PlaceholderLookupResult result, String placeholder, Matcher matcher, String input) {
|
||||
Object representation = getResultRepresentation(result, placeholder, matcher);
|
||||
|
||||
String output = getResultAsString(representation);
|
||||
for (PlaceholderResultConverter converter : converters) {
|
||||
output = converter.convertPlaceholderResult(representation);
|
||||
if (output != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (output == null) {
|
||||
output = String.valueOf(representation);
|
||||
}
|
||||
|
||||
return Pattern.compile(
|
||||
matcher.group(1) + placeholder + matcher.group(3),
|
||||
Pattern.LITERAL
|
||||
)
|
||||
.matcher(input)
|
||||
.replaceFirst(output);
|
||||
}
|
||||
|
||||
private Object getResultRepresentation(PlaceholderLookupResult result, String placeholder, Matcher matcher) {
|
||||
while (result != null) {
|
||||
PlaceholderLookupResult.Type type = result.getType();
|
||||
if (type == PlaceholderLookupResult.Type.UNKNOWN_PLACEHOLDER) {
|
||||
@ -124,7 +208,7 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
}
|
||||
|
||||
boolean newLookup = false;
|
||||
String replacement = null;
|
||||
Object replacement = null;
|
||||
switch (type) {
|
||||
case SUCCESS:
|
||||
replacement = result.getValue();
|
||||
@ -136,27 +220,18 @@ public class PlaceholderServiceImpl implements PlaceholderService {
|
||||
replacement = "Error";
|
||||
break;
|
||||
case NEW_LOOKUP:
|
||||
// prevent infinite recursion
|
||||
if (result.getValue().equals(originalPlaceholder)) {
|
||||
break;
|
||||
}
|
||||
result = lookupPlaceholder(result.getValue(), result.getExtras());
|
||||
result = lookupPlaceholder((String) result.getValue(), result.getExtras());
|
||||
newLookup = true;
|
||||
break;
|
||||
}
|
||||
if (replacement != null) {
|
||||
output = Pattern.compile(
|
||||
matcher.group(1)
|
||||
+ originalPlaceholder
|
||||
+ matcher.group(3),
|
||||
Pattern.LITERAL
|
||||
).matcher(output).replaceFirst(replacement);
|
||||
return replacement;
|
||||
}
|
||||
if (!newLookup) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
return matcher.group(1) + placeholder + matcher.group(3);
|
||||
}
|
||||
|
||||
private static class ClassProviderLoader implements CacheLoader<Class<?>, Set<PlaceholderProvider>> {
|
||||
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.placeholder.converter;
|
||||
|
||||
import com.discordsrv.api.component.MinecraftComponent;
|
||||
import com.discordsrv.api.placeholder.PlaceholderResultConverter;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import dev.vankka.mcdiscordreserializer.discord.DiscordSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ComponentResultConverter implements PlaceholderResultConverter {
|
||||
|
||||
private static final ThreadLocal<Boolean> PLAIN_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static void plain(Runnable runnable) {
|
||||
PLAIN_CONTEXT.set(true);
|
||||
runnable.run();
|
||||
PLAIN_CONTEXT.set(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String convertPlaceholderResult(@NotNull Object result) {
|
||||
if (result instanceof MinecraftComponent) {
|
||||
result = ComponentUtil.fromAPI((MinecraftComponent) result);
|
||||
}
|
||||
if (result instanceof Component) {
|
||||
Component component = (Component) result;
|
||||
if (PLAIN_CONTEXT.get()) {
|
||||
return PlainTextComponentSerializer.plainText()
|
||||
.serialize(component);
|
||||
} else {
|
||||
return DiscordSerializer.INSTANCE
|
||||
.serialize(component);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@ import com.discordsrv.api.placeholder.Placeholder;
|
||||
import com.discordsrv.api.player.DiscordSRVPlayer;
|
||||
import com.discordsrv.common.command.game.sender.ICommandSender;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -40,12 +39,8 @@ public interface IPlayer extends DiscordSRVPlayer, IOfflinePlayer, ICommandSende
|
||||
return identity().uuid();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Placeholder("player_display_name")
|
||||
Component displayName();
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
@Placeholder("player_display_name")
|
||||
default String plainDisplayName() {
|
||||
return PlainTextComponentSerializer.plainText()
|
||||
.serialize(displayName());
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.discordsrv.common.player.util;
|
||||
|
||||
import com.discordsrv.api.player.DiscordSRVPlayer;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
public final class PlayerUtil {
|
||||
|
||||
private PlayerUtil() {}
|
||||
|
||||
public static Component displayName(DiscordSRVPlayer player) {
|
||||
if (player instanceof IPlayer) {
|
||||
return ((IPlayer) player).displayName();
|
||||
} else {
|
||||
return Component.text(player.getUsername());
|
||||
}
|
||||
}
|
||||
}
|
@ -18,9 +18,9 @@
|
||||
|
||||
package com.discordsrv.common.string.util;
|
||||
|
||||
import javax.annotation.CheckReturnValue;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@ -28,54 +28,55 @@ import java.util.regex.Pattern;
|
||||
public class Placeholders {
|
||||
|
||||
private final String inputText;
|
||||
private final Map<Pattern, Supplier<String>> replacements = new HashMap<>();
|
||||
private final Map<Pattern, Function<Matcher, Object>> replacements = new HashMap<>();
|
||||
|
||||
public Placeholders(String inputText) {
|
||||
this.inputText = inputText;
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
public Placeholders replace(String replace, String replacement) {
|
||||
return replace(replace, () -> replacement);
|
||||
public Placeholders addAll(Map<Pattern, Function<Matcher, Object>> replacements) {
|
||||
replacements.forEach(this.replacements::put);
|
||||
return this;
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
public Placeholders replaceAll(String regex, String replacement) {
|
||||
return replaceAll(regex, () -> replacement);
|
||||
public Placeholders replace(String target, Object replacement) {
|
||||
return replace(target, matcher -> replacement);
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
public Placeholders replaceAll(Pattern pattern, String replacement) {
|
||||
return replaceAll(pattern, () -> replacement);
|
||||
public Placeholders replaceAll(Pattern pattern, Object replacement) {
|
||||
return replaceAll(pattern, matcher -> replacement);
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
public Placeholders replace(String replace, Supplier<String> replacement) {
|
||||
return replaceAll(Pattern.compile(replace, Pattern.LITERAL), replacement);
|
||||
public Placeholders replace(String target, Supplier<Object> replacement) {
|
||||
return replaceAll(Pattern.compile(target, Pattern.LITERAL), matcher -> replacement);
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
public Placeholders replaceAll(String regex, Supplier<String> replacement) {
|
||||
return replaceAll(Pattern.compile(regex), replacement);
|
||||
public Placeholders replaceAll(Pattern pattern, Supplier<Object> replacement) {
|
||||
return replaceAll(pattern, matcher -> replacement);
|
||||
}
|
||||
|
||||
@CheckReturnValue
|
||||
public Placeholders replaceAll(Pattern pattern, Supplier<String> replacement) {
|
||||
replacements.put(pattern, replacement);
|
||||
public Placeholders replace(String target, Function<Matcher, Object> replacement) {
|
||||
return replaceAll(Pattern.compile(target, Pattern.LITERAL), replacement);
|
||||
}
|
||||
|
||||
public Placeholders replaceAll(Pattern pattern, Function<Matcher, Object> replacement) {
|
||||
this.replacements.put(pattern, replacement);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String get() {
|
||||
String input = inputText;
|
||||
for (Map.Entry<Pattern, Supplier<String>> entry : replacements.entrySet()) {
|
||||
for (Map.Entry<Pattern, Function<Matcher, Object>> entry : replacements.entrySet()) {
|
||||
Pattern pattern = entry.getKey();
|
||||
Matcher matcher = pattern.matcher(input);
|
||||
if (!matcher.find()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Supplier<String> replacement = entry.getValue();
|
||||
input = matcher.replaceAll(replacement.get());
|
||||
Function<Matcher, Object> replacement = entry.getValue();
|
||||
Object value = replacement.apply(matcher);
|
||||
|
||||
input = matcher.replaceAll(String.valueOf(value));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ import com.discordsrv.bukkit.config.connection.BukkitConnectionConfig;
|
||||
import com.discordsrv.bukkit.config.main.BukkitConfig;
|
||||
import com.discordsrv.common.config.Config;
|
||||
import com.discordsrv.common.config.annotation.Untranslated;
|
||||
import com.discordsrv.common.config.main.channels.ChannelConfigHolder;
|
||||
import com.discordsrv.common.config.main.channels.BaseChannelConfig;
|
||||
import org.spongepowered.configurate.CommentedConfigurationNode;
|
||||
import org.spongepowered.configurate.ConfigurateException;
|
||||
import org.spongepowered.configurate.ConfigurationNode;
|
||||
@ -75,9 +75,9 @@ public final class DiscordSRVTranslation {
|
||||
.build();
|
||||
|
||||
|
||||
ChannelConfigHolder.Serializer channelSerializer = new ChannelConfigHolder.Serializer(objectMapper);
|
||||
BaseChannelConfig.Serializer channelSerializer = new BaseChannelConfig.Serializer(objectMapper);
|
||||
CommentedConfigurationNode node = CommentedConfigurationNode.root(ConfigurationOptions.defaults()
|
||||
.serializers(builder -> builder.register(ChannelConfigHolder.class, channelSerializer)));
|
||||
.serializers(builder -> builder.register(BaseChannelConfig.class, channelSerializer)));
|
||||
for (Config config : CONFIG_INSTANCES) {
|
||||
ConfigurationNode section = node.node(config.getFileName());
|
||||
ConfigurationNode configSection = section.copy();
|
||||
|
@ -22,6 +22,7 @@ import com.discordsrv.common.player.IPlayer;
|
||||
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;
|
||||
import org.spongepowered.api.entity.living.player.server.ServerPlayer;
|
||||
|
||||
@ -54,7 +55,7 @@ public class SpongePlayer extends SpongeOfflinePlayer implements IPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component displayName() {
|
||||
public @NotNull Component displayName() {
|
||||
return player.displayName().get();
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public class VelocityPlayer implements IPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component displayName() {
|
||||
public @NotNull Component displayName() {
|
||||
// Use Adventure's Pointer, otherwise username
|
||||
return player.getOrDefaultFrom(
|
||||
Identity.DISPLAY_NAME,
|
||||
|
Loading…
Reference in New Issue
Block a user