mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-12-26 17:18:29 +01:00
Add escaping content for sending to Discord, adventure updated
This commit is contained in:
parent
212a23b17e
commit
3b1fd58043
@ -28,6 +28,9 @@ import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Minecraft equivalent for {@link com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage.Formatter}.
|
||||
*/
|
||||
public interface EnhancedTextBuilder {
|
||||
|
||||
EnhancedTextBuilder addContext(Object... context);
|
||||
|
@ -192,8 +192,17 @@ public interface SendableDiscordMessage {
|
||||
*/
|
||||
@NotNull
|
||||
SendableDiscordMessage build();
|
||||
|
||||
/**
|
||||
* Creates a copy of this {@link Builder}.
|
||||
* @return a copy of this builder
|
||||
*/
|
||||
Builder clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Discord equivalent for {@link com.discordsrv.api.component.EnhancedTextBuilder}.
|
||||
*/
|
||||
interface Formatter {
|
||||
|
||||
Formatter addContext(Object... context);
|
||||
|
@ -157,5 +157,17 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
||||
public @NotNull SendableDiscordMessage build() {
|
||||
return new SendableDiscordMessageImpl(content, embeds, allowedMentions, webhookUsername, webhookAvatarUrl);
|
||||
}
|
||||
|
||||
@SuppressWarnings("MethodDoesntCallSuperMethod")
|
||||
@Override
|
||||
public Builder clone() {
|
||||
BuilderImpl clone = new BuilderImpl();
|
||||
clone.setContent(content);
|
||||
embeds.forEach(clone::addEmbed);
|
||||
allowedMentions.forEach(clone::addAllowedMention);
|
||||
clone.setWebhookUsername(webhookUsername);
|
||||
clone.setWebhookAvatarUrl(webhookAvatarUrl);
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
public final class DiscordFormattingUtil {
|
||||
|
||||
private DiscordFormattingUtil() {}
|
||||
|
||||
public static String escapeContent(String content) {
|
||||
content = escapeChars(content, '*', '_', '|', '`', '~', '>');
|
||||
return content;
|
||||
}
|
||||
|
||||
private static String escapeChars(String input, char... characters) {
|
||||
for (char character : characters) {
|
||||
input = input.replace(
|
||||
String.valueOf(character),
|
||||
"\\" + character);
|
||||
}
|
||||
return input;
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Represents content that doesn't need to be processed for the purposes of DiscordSRV's processing.
|
||||
*/
|
||||
public class FormattedText implements CharSequence {
|
||||
|
||||
private final CharSequence text;
|
||||
|
||||
public FormattedText(CharSequence text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return text.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return text.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return text.subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text.toString();
|
||||
}
|
||||
}
|
@ -49,4 +49,9 @@ public interface DiscordSRVPlayer {
|
||||
@NotNull
|
||||
UUID uuid();
|
||||
|
||||
|
||||
@Placeholder("totally_my_username") // TODO: remove
|
||||
default String totallyMyUsername() {
|
||||
return "*hi";
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ ext {
|
||||
// Configurate
|
||||
configurateVersion = '4.1.1'
|
||||
// Adventure & Adventure Platform
|
||||
adventureVersion = '4.8.1'
|
||||
adventureVersion = '4.9.1'
|
||||
adventurePlatformVersion = '4.0.0-SNAPSHOT'
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ dependencies {
|
||||
|
||||
// Adventure
|
||||
runtimeDownloadApi 'net.kyori:adventure-platform-bukkit:' + rootProject.adventurePlatformVersion
|
||||
runtimeDownloadApi 'net.kyori:adventure-text-serializer-craftbukkit:' + rootProject.adventurePlatformVersion
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
|
@ -24,7 +24,7 @@ import com.discordsrv.bukkit.BukkitDiscordSRV;
|
||||
import com.discordsrv.common.channel.DefaultGlobalChannel;
|
||||
import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import net.kyori.adventure.text.serializer.craftbukkit.BukkitComponentSerializer;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
|
@ -23,8 +23,8 @@ import com.discordsrv.common.component.util.ComponentUtil;
|
||||
import com.discordsrv.common.player.IPlayer;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.identity.Identity;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.craftbukkit.BukkitComponentSerializer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -18,7 +18,10 @@
|
||||
|
||||
package com.discordsrv.common.discord.api.message;
|
||||
|
||||
import com.discordsrv.api.discord.api.entity.message.DiscordMessageEmbed;
|
||||
import com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage;
|
||||
import com.discordsrv.api.discord.api.util.DiscordFormattingUtil;
|
||||
import com.discordsrv.api.placeholder.FormattedText;
|
||||
import com.discordsrv.api.placeholder.PlaceholderService;
|
||||
import com.discordsrv.common.DiscordSRV;
|
||||
import com.discordsrv.common.placeholder.converter.ComponentResultConverter;
|
||||
@ -39,7 +42,7 @@ public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessa
|
||||
|
||||
public SendableDiscordMessageFormatterImpl(DiscordSRV discordSRV, SendableDiscordMessage.Builder builder) {
|
||||
this.discordSRV = discordSRV;
|
||||
this.builder = builder;
|
||||
this.builder = builder.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,7 +53,7 @@ public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessa
|
||||
|
||||
@Override
|
||||
public SendableDiscordMessage.Formatter addReplacement(Pattern target, Function<Matcher, Object> replacement) {
|
||||
this.replacements.put(target, replacement);
|
||||
this.replacements.put(target, wrapFunction(replacement));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -64,16 +67,90 @@ public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessa
|
||||
return new Placeholders(input)
|
||||
.addAll(replacements)
|
||||
.replaceAll(PlaceholderService.PATTERN,
|
||||
matcher -> discordSRV.placeholderService().getResultAsString(matcher, context))
|
||||
wrapFunction(
|
||||
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
|
||||
List<DiscordMessageEmbed> embeds = new ArrayList<>(builder.getEmbeds());
|
||||
builder.getEmbeds().clear();
|
||||
|
||||
for (DiscordMessageEmbed embed : embeds) {
|
||||
DiscordMessageEmbed.Builder embedBuilder = embed.toBuilder();
|
||||
|
||||
// TODO: check which parts allow formatting more thoroughly
|
||||
ComponentResultConverter.plainComponents(() -> {
|
||||
embedBuilder.setAuthor(
|
||||
placeholders.apply(
|
||||
embedBuilder.getAuthorName()),
|
||||
placeholders.apply(
|
||||
embedBuilder.getAuthorUrl()),
|
||||
placeholders.apply(
|
||||
embedBuilder.getAuthorImageUrl()));
|
||||
|
||||
embedBuilder.setTitle(
|
||||
placeholders.apply(
|
||||
embedBuilder.getTitle()),
|
||||
placeholders.apply(
|
||||
embedBuilder.getTitleUrl()));
|
||||
|
||||
embedBuilder.setThumbnailUrl(
|
||||
placeholders.apply(
|
||||
embedBuilder.getThumbnailUrl()));
|
||||
|
||||
embedBuilder.setImageUrl(
|
||||
placeholders.apply(
|
||||
embedBuilder.getImageUrl()));
|
||||
|
||||
embedBuilder.setFooter(
|
||||
placeholders.apply(
|
||||
embedBuilder.getFooter()),
|
||||
placeholders.apply(
|
||||
embedBuilder.getFooterImageUrl()));
|
||||
});
|
||||
|
||||
embedBuilder.setDescription(
|
||||
placeholders.apply(
|
||||
embedBuilder.getDescription())
|
||||
);
|
||||
|
||||
List<DiscordMessageEmbed.Field> fields = new ArrayList<>(embedBuilder.getFields());
|
||||
embedBuilder.getFields().clear();
|
||||
|
||||
fields.forEach(field -> embedBuilder.addField(
|
||||
placeholders.apply(
|
||||
field.getTitle()),
|
||||
placeholders.apply(
|
||||
field.getValue()),
|
||||
field.isInline()
|
||||
));
|
||||
|
||||
builder.addEmbed(embedBuilder.build());
|
||||
}
|
||||
|
||||
ComponentResultConverter.plainComponents(() -> {
|
||||
builder.setWebhookUsername(placeholders.apply(builder.getWebhookUsername()));
|
||||
builder.setWebhookAvatarUrl(placeholders.apply(builder.getWebhookAvatarUrl()));
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private Function<Matcher, Object> wrapFunction(Function<Matcher, Object> function) {
|
||||
return matcher -> {
|
||||
Object result = function.apply(matcher);
|
||||
if (result instanceof FormattedText) {
|
||||
// Process as regular text
|
||||
return result.toString();
|
||||
} else if (result instanceof CharSequence) {
|
||||
// Escape content
|
||||
return DiscordFormattingUtil.escapeContent(
|
||||
result.toString());
|
||||
}
|
||||
|
||||
// Use default behaviour for everything else
|
||||
return result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,6 @@ public class DiscordChatListener extends AbstractListener {
|
||||
}
|
||||
|
||||
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);
|
||||
@ -73,6 +72,8 @@ public class DiscordChatListener extends AbstractListener {
|
||||
}
|
||||
|
||||
DiscordUser user = event.getDiscordMessage().getAuthor();
|
||||
Component message = MinecraftSerializer.INSTANCE.serialize(event.getMessageContent());
|
||||
|
||||
MinecraftComponent component = discordSRV.componentFactory()
|
||||
.enhancedBuilder(format)
|
||||
.addContext(event.getDiscordMessage(), user)
|
||||
|
@ -52,7 +52,6 @@ public class GameChatListener extends AbstractListener {
|
||||
}
|
||||
|
||||
GameChannel gameChannel = event.getGameChannel();
|
||||
Component message = ComponentUtil.fromAPI(event.message());
|
||||
|
||||
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(gameChannel);
|
||||
OrDefault<MinecraftToDiscordChatConfig> chatConfig = channelConfig.map(cfg -> cfg.minecraftToDiscord);
|
||||
@ -62,9 +61,12 @@ public class GameChatListener extends AbstractListener {
|
||||
return;
|
||||
}
|
||||
|
||||
Component message = ComponentUtil.fromAPI(event.message());
|
||||
String serializedMessage = DiscordSerializer.INSTANCE.serialize(message);
|
||||
|
||||
SendableDiscordMessage discordMessage = discordSRV.discordAPI().format(builder)
|
||||
.addContext(event.getPlayer())
|
||||
.addReplacement("%message%", DiscordSerializer.INSTANCE.serialize(message))
|
||||
.addReplacement("%message%", serializedMessage)
|
||||
.build();
|
||||
|
||||
List<Long> channelIds = channelConfig.get(cfg -> cfg instanceof ChannelConfig ? ((ChannelConfig) cfg).channelIds : null);
|
||||
|
@ -30,7 +30,7 @@ public class ComponentResultConverter implements PlaceholderResultConverter {
|
||||
|
||||
private static final ThreadLocal<Boolean> PLAIN_CONTEXT = new ThreadLocal<>();
|
||||
|
||||
public static void plain(Runnable runnable) {
|
||||
public static void plainComponents(Runnable runnable) {
|
||||
PLAIN_CONTEXT.set(true);
|
||||
runnable.run();
|
||||
PLAIN_CONTEXT.set(false);
|
||||
|
@ -76,7 +76,9 @@ public class Placeholders {
|
||||
Function<Matcher, Object> replacement = entry.getValue();
|
||||
Object value = replacement.apply(matcher);
|
||||
|
||||
input = matcher.replaceAll(String.valueOf(value));
|
||||
input = matcher.replaceAll(
|
||||
Matcher.quoteReplacement(
|
||||
String.valueOf(value)));
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user