001/*
002 * PlotSquared, a land and world management plugin for Minecraft.
003 * Copyright (C) IntellectualSites <https://intellectualsites.com>
004 * Copyright (C) IntellectualSites team and contributors
005 *
006 * This program is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 *
011 * This program is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014 * GNU General Public License for more details.
015 *
016 * You should have received a copy of the GNU General Public License
017 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
018 */
019package com.plotsquared.core.configuration.caption;
020
021import com.plotsquared.core.configuration.Settings;
022import com.plotsquared.core.player.PlotPlayer;
023import com.plotsquared.core.plot.flag.PlotFlag;
024import com.plotsquared.core.plot.flag.implementations.DescriptionFlag;
025import com.plotsquared.core.plot.flag.implementations.FarewellFlag;
026import com.plotsquared.core.plot.flag.implementations.GreetingFlag;
027import com.plotsquared.core.plot.flag.implementations.PlotTitleFlag;
028import net.kyori.adventure.text.Component;
029import net.kyori.adventure.text.event.ClickEvent;
030import net.kyori.adventure.text.minimessage.MiniMessage;
031import net.kyori.adventure.text.minimessage.ParsingException;
032import org.checkerframework.checker.nullness.qual.NonNull;
033import org.checkerframework.checker.nullness.qual.Nullable;
034
035import java.util.Objects;
036import java.util.Set;
037import java.util.regex.Pattern;
038import java.util.stream.Collectors;
039
040import static com.plotsquared.core.configuration.caption.ComponentTransform.nested;
041
042public class CaptionUtility {
043
044    private static final Pattern LEGACY_FORMATTING = Pattern.compile("ยง[a-gklmnor0-9]");
045
046    // flags which values are parsed by minimessage
047    private static final Set<Class<? extends PlotFlag<?, ?>>> MINI_MESSAGE_FLAGS = Set.of(
048            GreetingFlag.class,
049            FarewellFlag.class,
050            DescriptionFlag.class,
051            PlotTitleFlag.class
052    );
053
054    private static final ComponentTransform CLICK_STRIP_TRANSFORM = nested(
055            new ClickStripTransform(
056                    Settings.Chat.CLICK_EVENT_ACTIONS_TO_REMOVE.stream()
057                            .map(ClickEvent.Action.NAMES::value)
058                            .filter(Objects::nonNull)
059                            .collect(Collectors.toUnmodifiableSet())
060            )
061    );
062
063
064    /**
065     * Format a chat message but keep the formatting keys
066     *
067     * @param recipient Message recipient
068     * @param message   Message
069     * @return Formatted message
070     */
071    public static String formatRaw(PlotPlayer<?> recipient, String message) {
072        final ChatFormatter.ChatContext chatContext =
073                new ChatFormatter.ChatContext(recipient, message, true);
074        for (final ChatFormatter chatFormatter : ChatFormatter.formatters) {
075            chatFormatter.format(chatContext);
076        }
077        return chatContext.getMessage();
078    }
079
080    /**
081     * Format a chat message
082     *
083     * @param recipient Message recipient
084     * @param message   Message
085     * @return Formatted message
086     */
087    public static String format(
088            final @Nullable PlotPlayer<?> recipient,
089            final @NonNull String message
090    ) {
091        final ChatFormatter.ChatContext chatContext =
092                new ChatFormatter.ChatContext(recipient, message, false);
093        for (final ChatFormatter chatFormatter : ChatFormatter.formatters) {
094            chatFormatter.format(chatContext);
095        }
096        return chatContext.getMessage();
097    }
098
099    /**
100     * Strips configured click events from a MiniMessage string.
101     *
102     * @param miniMessageString the message from which the specified click events should be removed from.
103     * @return the string without the click events that are configured to be removed.
104     * @see Settings.Chat#CLICK_EVENT_ACTIONS_TO_REMOVE
105     * @since 6.0.10
106     */
107    public static String stripClickEvents(final @NonNull String miniMessageString) {
108        // parse, transform and serialize again
109        Component component;
110        try {
111            component = MiniMessage.miniMessage().deserialize(miniMessageString);
112        } catch (ParsingException e) {
113            // if the String cannot be parsed, we try stripping legacy colors
114            String legacyStripped = LEGACY_FORMATTING.matcher(miniMessageString).replaceAll("");
115            component = MiniMessage.miniMessage().deserialize(legacyStripped);
116        }
117        component = CLICK_STRIP_TRANSFORM.transform(component);
118        return MiniMessage.miniMessage().serialize(component);
119    }
120
121    /**
122     * Strips configured MiniMessage click events from a plot flag value.
123     * This is used before letting the string be parsed by the plot flag.
124     * This method works the same way as {@link #stripClickEvents(String)} but will only
125     * strip click events from messages that target flags that are meant to contain MiniMessage strings.
126     *
127     * @param flag              the flag the message is targeted for.
128     * @param miniMessageString the message from which the specified click events should be removed from.
129     * @return the string without the click events that are configured to be removed.
130     * @see Settings.Chat#CLICK_EVENT_ACTIONS_TO_REMOVE
131     * @see #stripClickEvents(String)
132     * @since 6.0.10
133     */
134    public static String stripClickEvents(
135            final @NonNull PlotFlag<?, ?> flag,
136            final @NonNull String miniMessageString
137    ) {
138        if (MINI_MESSAGE_FLAGS.contains(flag.getClass())) {
139            return stripClickEvents(miniMessageString);
140        }
141        return miniMessageString;
142    }
143
144}