diff options
Diffstat (limited to 'src/main/kotlin/org/polyfrost/chatting/chat')
8 files changed, 742 insertions, 0 deletions
diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt new file mode 100644 index 0000000..0d6909e --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatRegexes.kt @@ -0,0 +1,11 @@ +package org.polyfrost.chatting.chat + +data class ChatRegexes(val regexList: List<String>?) { + val compiledRegexList: MutableList<Regex> = arrayListOf() + + init { + regexList?.forEach { + compiledRegexList.add(Regex(it)) + } + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt new file mode 100644 index 0000000..982329a --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatScrollingHook.kt @@ -0,0 +1,5 @@ +package org.polyfrost.chatting.chat + +object ChatScrollingHook { + var shouldSmooth = false +}
\ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt new file mode 100644 index 0000000..d20a358 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSearchingManager.kt @@ -0,0 +1,42 @@ +package org.polyfrost.chatting.chat + +import cc.polyfrost.oneconfig.libs.caffeine.cache.Cache +import cc.polyfrost.oneconfig.libs.caffeine.cache.Caffeine +import cc.polyfrost.oneconfig.libs.universal.wrappers.message.UTextComponent +import net.minecraft.client.gui.ChatLine +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicInteger + +object ChatSearchingManager { + private var counter: AtomicInteger = AtomicInteger(0) + private var POOL: ThreadPoolExecutor = ThreadPoolExecutor( + 50, 50, + 0L, TimeUnit.SECONDS, + LinkedBlockingQueue() + ) { r -> + Thread( + r, + "Chat Filter Cache Thread ${counter.incrementAndGet()}" + ) + } + + @JvmStatic + val cache: Cache<String, List<ChatLine>> = Caffeine.newBuilder().executor(POOL).maximumSize(5000).build() + + var lastSearch = "" + + @JvmStatic + fun filterMessages(text: String, list: List<ChatLine>): List<ChatLine>? { + if (text.isBlank()) return list + val cached = cache.getIfPresent(text) + return cached ?: run { + cache.put(text, list.filter { + UTextComponent.stripFormatting(it.chatComponent.unformattedText).lowercase() + .contains(text.lowercase()) + }) + cache.getIfPresent(text) + } + } +}
\ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt new file mode 100644 index 0000000..0c85553 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatShortcuts.kt @@ -0,0 +1,79 @@ +package org.polyfrost.chatting.chat + +import cc.polyfrost.oneconfig.config.core.ConfigUtils +import org.polyfrost.chatting.Chatting +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import java.io.File + +object ChatShortcuts { + private val oldShortcutsFile = File(Chatting.oldModDir, "chatshortcuts.json") + private val shortcutsFile = ConfigUtils.getProfileFile("chatshortcuts.json") + private val PARSER = JsonParser() + + private var initialized = false + + val shortcuts = object : ArrayList<Pair<String, String>>() { + private val comparator = Comparator<Pair<String, String>> { o1, o2 -> + return@Comparator o2.first.length.compareTo(o1.first.length) + } + + override fun add(element: Pair<String, String>): Boolean { + val value = super.add(element) + sortWith(comparator) + return value + } + } + + fun initialize() { + if (initialized) { + return + } else { + initialized = true + } + if (shortcutsFile.exists()) { + try { + val jsonObj = PARSER.parse(shortcutsFile.readText()).asJsonObject + for (shortcut in jsonObj.entrySet()) { + shortcuts.add(shortcut.key to shortcut.value.asString) + } + return + } catch (_: Throwable) { + shortcutsFile.renameTo(File(shortcutsFile.parentFile, "chatshortcuts.json.bak")) + } + } + shortcutsFile.createNewFile() + if (oldShortcutsFile.exists()) { + shortcutsFile.writeText( + oldShortcutsFile.readText() + ) + } else { + shortcutsFile.writeText(JsonObject().toString()) + } + } + + fun removeShortcut(key: String) { + shortcuts.removeIf { it.first == key } + val jsonObj = PARSER.parse(shortcutsFile.readText()).asJsonObject + jsonObj.remove(key) + shortcutsFile.writeText(jsonObj.toString()) + } + + fun writeShortcut(key: String, value: String) { + shortcuts.add(key to value) + val jsonObj = PARSER.parse(shortcutsFile.readText()).asJsonObject + jsonObj.addProperty(key, value) + shortcutsFile.writeText(jsonObj.toString()) + } + + fun handleSentCommand(command: String): String { + shortcuts.forEach { + if (command == it.first || (command.startsWith(it.first) && command.substringAfter(it.first) + .startsWith(" ")) + ) { + return command.replaceFirst(it.first, it.second) + } + } + return command + } +}
\ No newline at end of file diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt new file mode 100644 index 0000000..da5dde8 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatSpamBlock.kt @@ -0,0 +1,124 @@ +package org.polyfrost.chatting.chat + +import org.polyfrost.chatting.config.ChattingConfig +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import java.text.Normalizer +import net.minecraft.util.ChatComponentText +import net.minecraft.util.EnumChatFormatting +import net.minecraftforge.client.event.ClientChatReceivedEvent +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent + +object ChatSpamBlock { + /* + Made by @KTibow + Based off of Unspam (also by @KTibow) + Algorithm based off of https://paulgraham.com/spam.html + */ + private val PLAYER_MESSAGE = Regex("^(\\[VIP\\+?\\] |\\[MVP\\+?\\+?\\] |)(\\w{2,16}): (.*)$") + + @SubscribeEvent + fun onChat(event: ClientChatReceivedEvent) { + val message = event.message.unformattedText.replace(Regex("\u00A7."), "") + if (!PLAYER_MESSAGE.matches(message)) return + + val (rank, player, content) = PLAYER_MESSAGE.matchEntire(message)!!.destructured + + if (ChattingConfig.spamThreshold != 100) { + val tokens = tokenize(content) + val spamProb = findSpamProbability(tokens) + if (spamProb * 100 > ChattingConfig.spamThreshold) { + if (ChattingConfig.hideSpam) { + event.isCanceled = true + } else { + var newMessage = + EnumChatFormatting.DARK_GRAY.toString() + + EnumChatFormatting.STRIKETHROUGH.toString() + if (!ChattingConfig.customChatFormatting) { + newMessage += rank + } + newMessage += "$player${EnumChatFormatting.DARK_GRAY}: $content" + event.message = ChatComponentText(newMessage) + } + return + } + } + if (ChattingConfig.customChatFormatting) { + val coloredPlayer = findRankColor(rank) + player + EnumChatFormatting.RESET.toString() + event.message = ChatComponentText("$coloredPlayer: $content") + } + } + + private fun tokenize(message: String): MutableList<String> { + val strippedMessage = + Normalizer.normalize(message, Normalizer.Form.NFKC) + .replace(Regex("[^\\w\\s/]"), " ") + .lowercase() + .trim() + val tokens = strippedMessage.split(Regex("\\s+")).toMutableList() + if (tokens.size <= 2) { + tokens.add("TINY_LENGTH") + } else if (tokens.size <= 4) { + tokens.add("SMALL_LENGTH") + } else if (tokens.size <= 7) { + tokens.add("MEDIUM_LENGTH") + } else { + tokens.add("LONG_LENGTH") + } + if (message.replace(Regex("[\\w\\s]"), "").length > 2) { + tokens.add("SPECIAL_CHARS") + } else if (message.replace(Regex("[\\w\\s]"), "").isNotEmpty()) { + tokens.add("SPECIAL_CHAR") + } else { + tokens.add("LOW_SPECIAL_CHARS") + } + if (message.replace(Regex("[^A-Z]"), "").length >= message.length / 4) { + tokens.add("HIGH_CAPS") + } else { + tokens.add("LOW_CAPS") + } + return tokens + } + + private fun findSpamProbability(tokens: MutableList<String>): Double { + val tokenProbs = mutableMapOf<String, Double>() + for (token in tokens) { + if (!spamInfoJson.has(token)) continue + val spamInToken = spamInfoJson.get(token).asJsonObject.get("spam").asDouble + val fineInToken = spamInfoJson.get(token).asJsonObject.get("fine").asDouble + tokenProbs[token] = + ((spamInToken / messageCountsJson.get("spam").asInt) / + (fineInToken / messageCountsJson.get("fine").asInt + + spamInToken / messageCountsJson.get("spam").asInt)) + } + val spamProbs = tokenProbs.values.toMutableList() + val fineProbs = tokenProbs.values.map { 1 - it }.toMutableList() + val spamProbability = spamProbs.reduce { a, b -> a * b } + val fineProbability = fineProbs.reduce { a, b -> a * b } + return spamProbability / (spamProbability + fineProbability) + } + + private fun findRankColor(rank: String): String { + println(rank) + return when (rank) { + "[VIP] ", + "[VIP+] " -> EnumChatFormatting.GREEN.toString() + "[MVP] ", + "[MVP+] " -> EnumChatFormatting.AQUA.toString() + "[MVP++] " -> EnumChatFormatting.GOLD.toString() + else -> EnumChatFormatting.GRAY.toString() + } + } + + private fun getResourceAsText(path: String): String? = + object {}.javaClass.getResource(path)?.readText() + private val spamInfoJson: JsonObject + private val messageCountsJson: JsonObject + + init { + // Load the file spamInfo.json from resources/ + val spamInfo = getResourceAsText("/spamInfo.json") + spamInfoJson = JsonParser().parse(spamInfo).asJsonObject + messageCountsJson = JsonParser().parse(" { \"fine\": 668, \"spam\": 230 }").asJsonObject + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt new file mode 100644 index 0000000..bd65f11 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTab.kt @@ -0,0 +1,112 @@ +package org.polyfrost.chatting.chat + +import org.polyfrost.chatting.gui.components.TabButton +import com.google.gson.annotations.SerializedName +import net.minecraft.client.Minecraft +import net.minecraft.util.EnumChatFormatting +import net.minecraft.util.IChatComponent +import java.util.* + +data class ChatTab( + val enabled: Boolean, + val name: String, + val unformatted: Boolean, + val lowercase: Boolean?, + @SerializedName("starts") val startsWith: List<String>?, + val contains: List<String>?, + @SerializedName("ends") val endsWith: List<String>?, + val equals: List<String>?, + @SerializedName("regex") val uncompiledRegex: List<String>?, + @SerializedName("ignore_starts") val ignoreStartsWith: List<String>?, + @SerializedName("ignore_contains") val ignoreContains: List<String>?, + @SerializedName("ignore_ends") val ignoreEndsWith: List<String>?, + @SerializedName("ignore_equals") val ignoreEquals: List<String>?, + @SerializedName("ignore_regex") val uncompiledIgnoreRegex: List<String>?, + val color: Int?, + @SerializedName("hovered_color") val hoveredColor: Int?, + @SerializedName("selected_color") val selectedColor: Int?, + val prefix: String?, +) { + lateinit var button: TabButton + lateinit var compiledRegex: ChatRegexes + lateinit var compiledIgnoreRegex: ChatRegexes + + //Ugly hack to make GSON not make button / regex null + fun initialize() { + compiledRegex = ChatRegexes(uncompiledRegex) + compiledIgnoreRegex = ChatRegexes(uncompiledIgnoreRegex) + val width = Minecraft.getMinecraft().fontRendererObj.getStringWidth(name) + button = TabButton(653452, run { + val returnValue = x - 2 + x += 6 + width + return@run returnValue + }, width + 4, 12, this) + } + + fun shouldRender(chatComponent: IChatComponent): Boolean { + val message = + (if (unformatted) EnumChatFormatting.getTextWithoutFormattingCodes(chatComponent.unformattedText) else chatComponent.formattedText).let { + if (lowercase == true) it.lowercase( + Locale.ENGLISH + ) else it + } + ignoreStartsWith?.forEach { + if (message.startsWith(it)) { + return false + } + } + ignoreEquals?.forEach { + if (message == it) { + return false + } + } + ignoreEndsWith?.forEach { + if (message.endsWith(it)) { + return false + } + } + ignoreContains?.forEach { + if (message.contains(it)) { + return false + } + } + compiledIgnoreRegex.compiledRegexList.forEach { + if (it.matches(message)) { + return false + } + } + if (startsWith.isNullOrEmpty() && equals.isNullOrEmpty() && endsWith.isNullOrEmpty() && contains.isNullOrEmpty() && uncompiledRegex.isNullOrEmpty()) { + return true + } + equals?.forEach { + if (message == it) { + return true + } + } + startsWith?.forEach { + if (message.startsWith(it)) { + return true + } + } + endsWith?.forEach { + if (message.endsWith(it)) { + return true + } + } + contains?.forEach { + if (message.contains(it)) { + return true + } + } + compiledRegex.compiledRegexList.forEach { + if (it.matches(message)) { + return true + } + } + return false + } + + companion object { + private var x = 4 + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt new file mode 100644 index 0000000..b46f55d --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabs.kt @@ -0,0 +1,354 @@ +package org.polyfrost.chatting.chat + +import cc.polyfrost.oneconfig.config.core.ConfigUtils +import org.polyfrost.chatting.Chatting +import org.polyfrost.chatting.gui.components.TabButton +import com.google.gson.GsonBuilder +import com.google.gson.JsonArray +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import com.google.gson.JsonPrimitive +import net.minecraft.client.Minecraft +import net.minecraft.util.IChatComponent +import java.io.File + +object ChatTabs { + private val GSON = GsonBuilder().setPrettyPrinting().create() + private val PARSER = JsonParser() + val tabs = arrayListOf<ChatTab>() + var currentTabs: ArrayList<ChatTab?> = object : ArrayList<ChatTab?>() { + override fun add(element: ChatTab?): Boolean { + if (element == null) return false + val returnValue = super.add(element) + if (Minecraft.getMinecraft().theWorld != null && returnValue) { + Minecraft.getMinecraft().ingameGUI.chatGUI.refreshChat() + } + return returnValue + } + } + var hasCancelledAnimation = false + private var initialized = false + + private val tabFile = ConfigUtils.getProfileFile("chattabs.json") + private val oldTabFile = File(Chatting.oldModDir, "chattabs.json") + + fun initialize() { + if (initialized) { + return + } else { + initialized = true + } + if (!tabFile.exists()) { + if (oldTabFile.exists()) { + tabFile.writeText(oldTabFile.readText()) + handleFile() + } else { + generateNewFile() + } + } else { + handleFile() + } + tabs.forEach { + it.initialize() + } + currentTabs.clear() + currentTabs.add(tabs[0]) + } + + private fun handleFile() { + try { + val chatTabJson = GSON.fromJson(tabFile.readText(), ChatTabsJson::class.java) + when (chatTabJson.version) { + 1 -> { + // ver 2 adds `enabled` + chatTabJson.tabs.forEach { + applyVersion2Changes(it.asJsonObject) + applyVersion3Changes(it.asJsonObject) + applyVersion4Changes(it.asJsonObject) + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 2 -> { + // ver 3 adds ignore_ + chatTabJson.tabs.forEach { + applyVersion3Changes(it.asJsonObject) + applyVersion4Changes(it.asJsonObject) + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 3 -> { + // ver 4 adds color options + chatTabJson.tabs.forEach { + applyVersion4Changes(it.asJsonObject) + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 4 -> { + // ver 5 adds lowercase + chatTabJson.tabs.forEach { + applyVersion5Changes(it.asJsonObject) + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + 5 -> { + // ver 6 changes pm regex + chatTabJson.tabs.forEach { + applyVersion6Changes(it.asJsonObject) + } + chatTabJson.version = ChatTabsJson.VERSION + tabFile.writeText(GSON.toJson(chatTabJson)) + } + } + chatTabJson.tabs.forEach { + val chatTab = GSON.fromJson(it.toString(), ChatTab::class.java) + if (chatTab.enabled) { + tabs.add(chatTab) + } + } + } catch (e: Throwable) { + e.printStackTrace() + tabFile.delete() + generateNewFile() + } + } + + private fun applyVersion2Changes(json: JsonObject) { + json.addProperty("enabled", true) + } + + private fun applyVersion3Changes(json: JsonObject) { + json.add("ignore_starts", JsonArray()) + json.add("ignore_contains", JsonArray()) + json.add("ignore_ends", JsonArray()) + json.add("ignore_equals", JsonArray()) + json.add("ignore_regex", JsonArray()) + } + + private fun applyVersion4Changes(json: JsonObject) { + json.addProperty("color", TabButton.color) + json.addProperty("hovered_color", TabButton.hoveredColor) + json.addProperty("selected_color", TabButton.selectedColor) + } + + private fun applyVersion5Changes(json: JsonObject) { + json.addProperty("lowercase", false) + } + + private fun applyVersion6Changes(json: JsonObject) { + if (json.has("starts")) { + val starts = json["starts"].asJsonArray + var detected = false + starts.iterator().let { + while (it.hasNext()) { + when (it.next().asString) { + "To " -> { + detected = true + it.remove() + } + "From " -> { + detected = true + it.remove() + } + } + } + } + if (detected) { + json.add("regex", JsonArray().apply { + add(JsonPrimitive("^(?<type>§dTo|§dFrom) (?<prefix>.+): §r(?<message>.*)(?:§r)?\$")) + }) + json.remove("unformatted") + json.addProperty("unformatted", false) + } + } + if (json.has("ends")) { + val ends = json["ends"].asJsonArray + var detected = false + ends.iterator().let { + while (it.hasNext()) { + when (it.next().asString) { + "§r§ehas invited you to join their party!", -> { + detected = true + it.remove() + } + } + } + } + if (detected) { + json.add("contains", JsonArray().apply { + add(JsonPrimitive("§r§ehas invited you to join their party!")) + }) + } + } + } + + fun shouldRender(message: IChatComponent): Boolean { + if (currentTabs.isEmpty()) return true + for (tab in currentTabs) { + if (tab?.shouldRender(message) == true) { + return true + } + } + return false + } + + private fun generateNewFile() { + tabFile.createNewFile() + val jsonObject = JsonObject() + val defaultTabs = generateDefaultTabs() + jsonObject.add("tabs", defaultTabs) + jsonObject.addProperty("version", ChatTabsJson.VERSION) + tabFile.writeText(GSON.toJson(jsonObject)) + } + + private fun generateDefaultTabs(): JsonArray { + val all = ChatTab( + true, + "ALL", + unformatted = false, + lowercase = false, + startsWith = null, + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = null, + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "" + ) + val party = ChatTab( + true, + "PARTY", + unformatted = false, + lowercase = false, + startsWith = listOf("§r§9Party §8> ", "§r§9P §8> ", "§eThe party was transferred to §r", "§eKicked §r"), + contains = listOf("§r§ehas invited you to join their party!"), + endsWith = listOf( + "§r§eto the party! They have §r§c60 §r§eseconds to accept.§r", + "§r§ehas disbanded the party!§r", + "§r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before they are removed from the party.§r", + " §r§ejoined the party.§r", + " §r§ehas left the party.§r", + " §r§ehas been removed from the party.§r", + "§r§e because they were offline.§r" + ), + equals = listOf("§cThe party was disbanded because all invites expired and the party was empty§r"), + uncompiledRegex = listOf( //regexes from https://github.com/kwevin/Hychat-Tabs/blob/main/tabs/re-add%20prefixes%20%26%20fix%20shortened%20tags/chat.json cause i cant write regex + "(§r)*(§9Party §8\u003e)+(.*)", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§einvited §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto the party! They have §r§c60 §r§eseconds to accept\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas left the party\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ejoined the party\\.§r", + "§eYou left the party\\.§r", + "§eYou have joined §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)\u0027s §r§eparty!§r", + "§cThe party was disbanded because all invites expired and the party was empty§r", + "§cYou cannot invite that player since they\u0027re not online\\.§r", + "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e, warped you to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e\u0027s house\\.§r", + "§eSkyBlock Party Warp §r§7\\([0-9]+ players?\\)§r", + "§a. §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§f §r§awarped to your server§r", + "§eYou summoned §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§f §r§eto your server\\.§r", + "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e, warped you to their house\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§aenabled Private Game§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§cdisabled Private Game§r", + "§cThe party is now muted\\. §r", + "§aThe party is no longer muted\\.§r", + "§cThere are no offline players to remove\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas been removed from the party\\.§r", + "§eThe party was transferred to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eby §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has promoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Leader§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has promoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Moderator§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eis now a Party Moderator§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r§e has demoted §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§eto Party Member§r", + "§cYou can\u0027t demote yourself!§r", + "§6Party Members \\([0-9]+\\)§r", + "§eParty Leader: §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) ?§r(?:§[a-zA-Z0-9]).§r", + "§eParty Members: §r(?:(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r(?:§[a-zA-Z0-9]) . §r)+", + "§eParty Moderators: §r(?:(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+)§r(?:§[a-zA-Z0-9]) . §r)+", + "§eThe party invite to §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas expired§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§cdisabled All Invite§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§aenabled All Invite§r", + "§cYou cannot invite that player\\.§r", + "§cYou are not allowed to invite players\\.§r", + "§eThe party leader, §r(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before the party is disbanded\\.§r", + "(?:(?:§[a-zA-Z0-9])*\\[(?:(?:VIP)|(?:VIP§r§6\\+)|(?:MVP)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+)|(?:MVP(?:§r)?(?:§[a-zA-Z0-9])\\+\\+)|(?:(?:§r)?§fYOUTUBE))(?:§r)?(?:(?:§[a-zA-Z0-9]))?\\] [a-zA-Z0-9_]+|§7[a-zA-Z0-9_]+) §r§ehas disconnected, they have §r§c5 §r§eminutes to rejoin before they are removed from the party.§r", + "§cYou are not in a party right now\\.§r", + "§cThis party is currently muted\\.§r", + "(§r)*(§9P §8\u003e)+(.*)" + ), + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "/pc " + ) + val guild = ChatTab( + true, + "GUILD", + unformatted = true, + lowercase = false, + startsWith = listOf("Guild >", "G >"), + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = null, + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "/gc " + ) + val pm = ChatTab( + true, + "PM", + unformatted = false, + lowercase = false, + startsWith = null, + contains = null, + endsWith = null, + equals = null, + uncompiledRegex = listOf("^(?<type>§dTo|§dFrom) (?<prefix>.+): §r(?<message>.*)(?:§r)?\$"), + ignoreStartsWith = null, + ignoreContains = null, + ignoreEndsWith = null, + ignoreEquals = null, + uncompiledIgnoreRegex = null, + color = TabButton.color, + hoveredColor = TabButton.hoveredColor, + selectedColor = TabButton.selectedColor, + prefix = "/r " + ) + tabs.add(all) + tabs.add(party) + tabs.add(guild) + tabs.add(pm) + val jsonArray = JsonArray() + jsonArray.add(PARSER.parse(GSON.toJson(all)).asJsonObject) + jsonArray.add(PARSER.parse(GSON.toJson(party)).asJsonObject) + jsonArray.add(PARSER.parse(GSON.toJson(guild)).asJsonObject) + jsonArray.add(PARSER.parse(GSON.toJson(pm)).asJsonObject) + return jsonArray + } +} diff --git a/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt new file mode 100644 index 0000000..c5939c3 --- /dev/null +++ b/src/main/kotlin/org/polyfrost/chatting/chat/ChatTabsJson.kt @@ -0,0 +1,15 @@ +package org.polyfrost.chatting.chat + +import com.google.gson.JsonArray +import com.google.gson.annotations.SerializedName + +data class ChatTabsJson(@SerializedName("tabs") val tabs: JsonArray, var version: Int) { + + override fun toString(): String { + return "{\"tabs\": $tabs, \"version\": $version}" + } + + companion object { + const val VERSION = 6 + } +}
\ No newline at end of file |