mirror of
https://github.com/DiscordSRV/Ascension.git
synced 2024-11-22 11:55:54 +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.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minecraft equivalent for {@link com.discordsrv.api.discord.api.entity.message.SendableDiscordMessage.Formatter}.
|
||||||
|
*/
|
||||||
public interface EnhancedTextBuilder {
|
public interface EnhancedTextBuilder {
|
||||||
|
|
||||||
EnhancedTextBuilder addContext(Object... context);
|
EnhancedTextBuilder addContext(Object... context);
|
||||||
|
@ -192,8 +192,17 @@ public interface SendableDiscordMessage {
|
|||||||
*/
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
SendableDiscordMessage build();
|
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 {
|
interface Formatter {
|
||||||
|
|
||||||
Formatter addContext(Object... context);
|
Formatter addContext(Object... context);
|
||||||
|
@ -157,5 +157,17 @@ public class SendableDiscordMessageImpl implements SendableDiscordMessage {
|
|||||||
public @NotNull SendableDiscordMessage build() {
|
public @NotNull SendableDiscordMessage build() {
|
||||||
return new SendableDiscordMessageImpl(content, embeds, allowedMentions, webhookUsername, webhookAvatarUrl);
|
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
|
@NotNull
|
||||||
UUID uuid();
|
UUID uuid();
|
||||||
|
|
||||||
|
|
||||||
|
@Placeholder("totally_my_username") // TODO: remove
|
||||||
|
default String totallyMyUsername() {
|
||||||
|
return "*hi";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ ext {
|
|||||||
// Configurate
|
// Configurate
|
||||||
configurateVersion = '4.1.1'
|
configurateVersion = '4.1.1'
|
||||||
// Adventure & Adventure Platform
|
// Adventure & Adventure Platform
|
||||||
adventureVersion = '4.8.1'
|
adventureVersion = '4.9.1'
|
||||||
adventurePlatformVersion = '4.0.0-SNAPSHOT'
|
adventurePlatformVersion = '4.0.0-SNAPSHOT'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ dependencies {
|
|||||||
|
|
||||||
// Adventure
|
// Adventure
|
||||||
runtimeDownloadApi 'net.kyori:adventure-platform-bukkit:' + rootProject.adventurePlatformVersion
|
runtimeDownloadApi 'net.kyori:adventure-platform-bukkit:' + rootProject.adventurePlatformVersion
|
||||||
runtimeDownloadApi 'net.kyori:adventure-text-serializer-craftbukkit:' + rootProject.adventurePlatformVersion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
|
@ -24,7 +24,7 @@ import com.discordsrv.bukkit.BukkitDiscordSRV;
|
|||||||
import com.discordsrv.common.channel.DefaultGlobalChannel;
|
import com.discordsrv.common.channel.DefaultGlobalChannel;
|
||||||
import com.discordsrv.common.component.util.ComponentUtil;
|
import com.discordsrv.common.component.util.ComponentUtil;
|
||||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
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.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
|
@ -23,8 +23,8 @@ import com.discordsrv.common.component.util.ComponentUtil;
|
|||||||
import com.discordsrv.common.player.IPlayer;
|
import com.discordsrv.common.player.IPlayer;
|
||||||
import net.kyori.adventure.audience.Audience;
|
import net.kyori.adventure.audience.Audience;
|
||||||
import net.kyori.adventure.identity.Identity;
|
import net.kyori.adventure.identity.Identity;
|
||||||
|
import net.kyori.adventure.platform.bukkit.BukkitComponentSerializer;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.serializer.craftbukkit.BukkitComponentSerializer;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@ -18,7 +18,10 @@
|
|||||||
|
|
||||||
package com.discordsrv.common.discord.api.message;
|
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.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.api.placeholder.PlaceholderService;
|
||||||
import com.discordsrv.common.DiscordSRV;
|
import com.discordsrv.common.DiscordSRV;
|
||||||
import com.discordsrv.common.placeholder.converter.ComponentResultConverter;
|
import com.discordsrv.common.placeholder.converter.ComponentResultConverter;
|
||||||
@ -39,7 +42,7 @@ public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessa
|
|||||||
|
|
||||||
public SendableDiscordMessageFormatterImpl(DiscordSRV discordSRV, SendableDiscordMessage.Builder builder) {
|
public SendableDiscordMessageFormatterImpl(DiscordSRV discordSRV, SendableDiscordMessage.Builder builder) {
|
||||||
this.discordSRV = discordSRV;
|
this.discordSRV = discordSRV;
|
||||||
this.builder = builder;
|
this.builder = builder.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -50,7 +53,7 @@ public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessa
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SendableDiscordMessage.Formatter addReplacement(Pattern target, Function<Matcher, Object> replacement) {
|
public SendableDiscordMessage.Formatter addReplacement(Pattern target, Function<Matcher, Object> replacement) {
|
||||||
this.replacements.put(target, replacement);
|
this.replacements.put(target, wrapFunction(replacement));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,16 +67,90 @@ public class SendableDiscordMessageFormatterImpl implements SendableDiscordMessa
|
|||||||
return new Placeholders(input)
|
return new Placeholders(input)
|
||||||
.addAll(replacements)
|
.addAll(replacements)
|
||||||
.replaceAll(PlaceholderService.PATTERN,
|
.replaceAll(PlaceholderService.PATTERN,
|
||||||
matcher -> discordSRV.placeholderService().getResultAsString(matcher, context))
|
wrapFunction(
|
||||||
|
matcher -> discordSRV.placeholderService().getResultAsString(matcher, context)))
|
||||||
.get();
|
.get();
|
||||||
};
|
};
|
||||||
|
|
||||||
ComponentResultConverter.plain(() ->
|
|
||||||
builder.setWebhookUsername(placeholders.apply(builder.getWebhookUsername())));
|
|
||||||
builder.setContent(placeholders.apply(builder.getContent()));
|
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();
|
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();
|
DiscordTextChannel channel = event.getChannel();
|
||||||
Component message = MinecraftSerializer.INSTANCE.serialize(event.getMessageContent());
|
|
||||||
|
|
||||||
OrDefault<Pair<GameChannel, BaseChannelConfig>> channelPair = discordSRV.channelConfig().orDefault(channel);
|
OrDefault<Pair<GameChannel, BaseChannelConfig>> channelPair = discordSRV.channelConfig().orDefault(channel);
|
||||||
GameChannel gameChannel = channelPair.get(Pair::getKey);
|
GameChannel gameChannel = channelPair.get(Pair::getKey);
|
||||||
@ -73,6 +72,8 @@ public class DiscordChatListener extends AbstractListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
DiscordUser user = event.getDiscordMessage().getAuthor();
|
DiscordUser user = event.getDiscordMessage().getAuthor();
|
||||||
|
Component message = MinecraftSerializer.INSTANCE.serialize(event.getMessageContent());
|
||||||
|
|
||||||
MinecraftComponent component = discordSRV.componentFactory()
|
MinecraftComponent component = discordSRV.componentFactory()
|
||||||
.enhancedBuilder(format)
|
.enhancedBuilder(format)
|
||||||
.addContext(event.getDiscordMessage(), user)
|
.addContext(event.getDiscordMessage(), user)
|
||||||
|
@ -52,7 +52,6 @@ public class GameChatListener extends AbstractListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
GameChannel gameChannel = event.getGameChannel();
|
GameChannel gameChannel = event.getGameChannel();
|
||||||
Component message = ComponentUtil.fromAPI(event.message());
|
|
||||||
|
|
||||||
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(gameChannel);
|
OrDefault<BaseChannelConfig> channelConfig = discordSRV.channelConfig().orDefault(gameChannel);
|
||||||
OrDefault<MinecraftToDiscordChatConfig> chatConfig = channelConfig.map(cfg -> cfg.minecraftToDiscord);
|
OrDefault<MinecraftToDiscordChatConfig> chatConfig = channelConfig.map(cfg -> cfg.minecraftToDiscord);
|
||||||
@ -62,9 +61,12 @@ public class GameChatListener extends AbstractListener {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component message = ComponentUtil.fromAPI(event.message());
|
||||||
|
String serializedMessage = DiscordSerializer.INSTANCE.serialize(message);
|
||||||
|
|
||||||
SendableDiscordMessage discordMessage = discordSRV.discordAPI().format(builder)
|
SendableDiscordMessage discordMessage = discordSRV.discordAPI().format(builder)
|
||||||
.addContext(event.getPlayer())
|
.addContext(event.getPlayer())
|
||||||
.addReplacement("%message%", DiscordSerializer.INSTANCE.serialize(message))
|
.addReplacement("%message%", serializedMessage)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
List<Long> channelIds = channelConfig.get(cfg -> cfg instanceof ChannelConfig ? ((ChannelConfig) cfg).channelIds : null);
|
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<>();
|
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);
|
PLAIN_CONTEXT.set(true);
|
||||||
runnable.run();
|
runnable.run();
|
||||||
PLAIN_CONTEXT.set(false);
|
PLAIN_CONTEXT.set(false);
|
||||||
|
@ -76,7 +76,9 @@ public class Placeholders {
|
|||||||
Function<Matcher, Object> replacement = entry.getValue();
|
Function<Matcher, Object> replacement = entry.getValue();
|
||||||
Object value = replacement.apply(matcher);
|
Object value = replacement.apply(matcher);
|
||||||
|
|
||||||
input = matcher.replaceAll(String.valueOf(value));
|
input = matcher.replaceAll(
|
||||||
|
Matcher.quoteReplacement(
|
||||||
|
String.valueOf(value)));
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user