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.Set;
036import java.util.regex.Pattern;
037
038import static com.plotsquared.core.configuration.caption.ComponentTransform.nested;
039import static com.plotsquared.core.configuration.caption.ComponentTransform.stripClicks;
040
041public class CaptionUtility {
042
043    private static final Pattern LEGACY_FORMATTING = Pattern.compile("ยง[a-gklmnor0-9]");
044
045    // flags which values are parsed by minimessage
046    private static final Set<Class<? extends PlotFlag<?, ?>>> MINI_MESSAGE_FLAGS = Set.of(
047            GreetingFlag.class,
048            FarewellFlag.class,
049            DescriptionFlag.class,
050            PlotTitleFlag.class
051    );
052
053    private static final ComponentTransform CLICK_STRIP_TRANSFORM = nested(
054            stripClicks(
055                    Settings.Chat.CLICK_EVENT_ACTIONS_TO_REMOVE.stream()
056                            .map(ClickEvent.Action::valueOf)
057                            .toArray(ClickEvent.Action[]::new)
058            )
059    );
060
061
062    /**
063     * Format a chat message but keep the formatting keys
064     *
065     * @param recipient Message recipient
066     * @param message   Message
067     * @return Formatted message
068     */
069    public static String formatRaw(PlotPlayer<?> recipient, String message) {
070        final ChatFormatter.ChatContext chatContext =
071                new ChatFormatter.ChatContext(recipient, message, true);
072        for (final ChatFormatter chatFormatter : ChatFormatter.formatters) {
073            chatFormatter.format(chatContext);
074        }
075        return chatContext.getMessage();
076    }
077
078    /**
079     * Format a chat message
080     *
081     * @param recipient Message recipient
082     * @param message   Message
083     * @return Formatted message
084     */
085    public static String format(
086            final @Nullable PlotPlayer<?> recipient,
087            final @NonNull String message
088    ) {
089        final ChatFormatter.ChatContext chatContext =
090                new ChatFormatter.ChatContext(recipient, message, false);
091        for (final ChatFormatter chatFormatter : ChatFormatter.formatters) {
092            chatFormatter.format(chatContext);
093        }
094        return chatContext.getMessage();
095    }
096
097    /**
098     * Strips configured click events from a MiniMessage string.
099     *
100     * @param miniMessageString the message from which the specified click events should be removed from.
101     * @return the string without the click events that are configured to be removed.
102     * @see Settings.Chat#CLICK_EVENT_ACTIONS_TO_REMOVE
103     * @since 6.0.10
104     */
105    public static String stripClickEvents(final @NonNull String miniMessageString) {
106        // parse, transform and serialize again
107        Component component;
108        try {
109            component = MiniMessage.miniMessage().deserialize(miniMessageString);
110        } catch (ParsingException e) {
111            // if the String cannot be parsed, we try stripping legacy colors
112            String legacyStripped = LEGACY_FORMATTING.matcher(miniMessageString).replaceAll("");
113            component = MiniMessage.miniMessage().deserialize(legacyStripped);
114        }
115        component = CLICK_STRIP_TRANSFORM.transform(component);
116        return MiniMessage.miniMessage().serialize(component);
117    }
118
119    /**
120     * Strips configured MiniMessage click events from a plot flag value.
121     * This is used before letting the string be parsed by the plot flag.
122     * This method works the same way as {@link #stripClickEvents(String)} but will only
123     * strip click events from messages that target flags that are meant to contain MiniMessage strings.
124     *
125     * @param flag              the flag the message is targeted for.
126     * @param miniMessageString the message from which the specified click events should be removed from.
127     * @return the string without the click events that are configured to be removed.
128     * @see Settings.Chat#CLICK_EVENT_ACTIONS_TO_REMOVE
129     * @see #stripClickEvents(String)
130     * @since 6.0.10
131     */
132    public static String stripClickEvents(
133            final @NonNull PlotFlag<?, ?> flag,
134            final @NonNull String miniMessageString
135    ) {
136        if (MINI_MESSAGE_FLAGS.contains(flag.getClass())) {
137            return stripClickEvents(miniMessageString);
138        }
139        return miniMessageString;
140    }
141
142}