aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker/skyblock/chat/ChatRulesHandler.java
blob: 13115beeec26d927e26ac4efbcf40e49154b346f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package de.hysky.skyblocker.skyblock.chat;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;

import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.utils.Http;
import de.hysky.skyblocker.utils.Utils;
import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents;
import net.minecraft.client.MinecraftClient;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.MutableText;
import net.minecraft.text.Style;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.CompletableFuture;

public class ChatRulesHandler {
    private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
    private static final Logger LOGGER = LoggerFactory.getLogger(ChatRule.class);
    private static final Path CHAT_RULE_FILE = SkyblockerMod.CONFIG_DIR.resolve("chat_rules.json");
    private static final Codec<Map<String, List<ChatRule>>> MAP_CODEC = Codec.unboundedMap(Codec.STRING, ChatRule.LIST_CODEC);
    /**
     * look up table for the locations input by the users to raw locations
     */
    protected static final HashMap<String, String> locations = new HashMap<>();
    /**
     * list of possible locations still formatted for the tool tip
     */
    protected static final List<String> locationsList = new ArrayList<>();

    protected static final List<ChatRule> chatRuleList = new ArrayList<>();

    public static void init() {
        CompletableFuture.runAsync(ChatRulesHandler::loadChatRules);
        CompletableFuture.runAsync(ChatRulesHandler::loadLocations);
        ClientReceiveMessageEvents.ALLOW_GAME.register(ChatRulesHandler::checkMessage);
    }

    private static void loadChatRules() {
        try (BufferedReader reader = Files.newBufferedReader(CHAT_RULE_FILE)) {
            Map<String, List<ChatRule>> chatRules = MAP_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).result().orElseThrow();
            LOGGER.info("[Sky: " + chatRules.toString());
            
            chatRuleList.addAll(chatRules.get("rules"));

            LOGGER.info("[Skyblocker] Loaded chat rules");
        } catch (NoSuchFileException e) {
            registerDefaultChatRules();
            LOGGER.warn("[Skyblocker] chat rule file not found, using default rules. This is normal when using for the first time.");
        } catch (Exception e) {
            LOGGER.error("[Skyblocker] Failed to load shortcuts file", e);
        }
    }

    private static void registerDefaultChatRules() {
        //clean hub chat
        ChatRule cleanHubRule = new ChatRule("Clean Hub Chat", false, true, true, true, "(selling)|(buying)|(lowb)|(visit)|(/p)|(/ah)|(my ah)", "hub", true, false, false, "", null);
        //mining Ability
        ChatRule miningAbilityRule = new ChatRule("Mining Ability Alert", false, true, false, true, "is now available!", "Crystal Hollows, Dwarven Mines", false, false, true, "&1Ability", SoundEvents.ENTITY_ARROW_HIT_PLAYER);

        chatRuleList.add(cleanHubRule);
        chatRuleList.add(miningAbilityRule);
    }

    private static void loadLocations() {
        try {
            String response = Http.sendGetRequest("https://api.hypixel.net/v2/resources/games");
            JsonObject locationsJson = JsonParser.parseString(response).getAsJsonObject().get("games").getAsJsonObject().get("SKYBLOCK").getAsJsonObject().get("modeNames").getAsJsonObject();
            for (Map.Entry<String, JsonElement> entry : locationsJson.entrySet()) {
                //fix old naming todo remove when hypixel fix
                if (Objects.equals(entry.getKey(), "instanced")) {
                    locationsList.add(entry.getValue().getAsString());
                    locations.put(entry.getValue().getAsString().replace(" ", "").toLowerCase(), "kuudra");
                    continue;
                }
                locationsList.add(entry.getValue().getAsString());
                //add to list in a simplified for so more lenient for user input
                locations.put(entry.getValue().getAsString().replace(" ", "").toLowerCase(), entry.getKey());
            }
        } catch (Exception e) {
            LOGGER.error("[Skyblocker] Failed to load locations!", e);
        }
    }

    protected static void saveChatRules() {
        JsonObject chatRuleJson = new JsonObject();
        chatRuleJson.add("rules", ChatRule.LIST_CODEC.encodeStart(JsonOps.INSTANCE, chatRuleList).result().orElseThrow());
        try (BufferedWriter writer = Files.newBufferedWriter(CHAT_RULE_FILE)) {
            SkyblockerMod.GSON.toJson(chatRuleJson, writer);
            LOGGER.info("[Skyblocker] Saved chat rules file");
        } catch (Exception e) {
            LOGGER.error("[Skyblocker] Failed to save chat rules file", e);
        }
    }

    /**
     * Checks each rule in {@link ChatRulesHandler#chatRuleList} to see if they are a match for the message and if so change outputs based on the options set in the {@link ChatRule}.
     * @param message the chat message
     * @param overlay if its overlay
     */
    private static boolean checkMessage(Text message, boolean overlay) {
        if (!Utils.isOnSkyblock()) return true; //do not work not on skyblock
        if (overlay) return true; //ignore messages in overlay
        String plain =  Formatting.strip(message.getString());

        for (ChatRule rule : chatRuleList) {
            if (rule.isMatch(plain)) {
                //get a replacement message
                Text newMessage;
                if (!rule.getReplaceMessage().isBlank()) {
                    newMessage = formatText(rule.getReplaceMessage());
                } else {
                    newMessage = message;
                }

                if (rule.getShowAnnouncement()) {
                    ChatRuleAnnouncementScreen.setText(newMessage);
                }

                //show in action bar
                if (rule.getShowActionBar() && CLIENT.player != null) {
                    CLIENT.player.sendMessage(newMessage, true);
                }

                //hide message
                if (!rule.getHideMessage() && CLIENT.player != null) {
                    CLIENT.player.sendMessage(newMessage, false);
                }

                //play sound
                if (rule.getCustomSound() != null && CLIENT.player != null) {
                    CLIENT.player.playSound(rule.getCustomSound(), 100f, 0.1f);
                }

                //do not send original message
                return false;
            }
        }
        return true;
    }

    /**
     * Converts a string with color codes into a formatted Text object
     * @param codedString the string with color codes in
     * @return formatted text
     */
    protected static MutableText formatText(String codedString) {
        if (codedString.contains(String.valueOf(Formatting.FORMATTING_CODE_PREFIX)) || codedString.contains("&")) {
            MutableText newText =  Text.literal("");
            String[] parts = codedString.split("[" + Formatting.FORMATTING_CODE_PREFIX +"&]");
            Style style = Style.EMPTY;

            for (String part : parts) {
                if (part.isEmpty()) continue;
                Formatting formatting =  Formatting.byCode(part.charAt(0));

                if (formatting != null) {
                    style = style.withFormatting(formatting);
                    Text.literal(part.substring(1)).getWithStyle(style).forEach(newText::append);
                } else {
                    newText.append(Text.of(part));
                }
            }
            return newText;
        }
        return Text.literal(codedString);
    }
}