Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package com.pepej.truncator.indexer
- import com.pepej.truncator.database.IndexedEvents
- import com.pepej.truncator.telegram.QUESTS_CHAT_ID
- import com.pepej.truncator.telegram.TelegramBatchedMessageSender
- import com.pepej.truncator.utils.toInstant
- import com.pepej.truncator.utils.toReadableTimeDifference
- import kotlinx.datetime.LocalDate
- import kotlinx.datetime.LocalDateTime
- import kotlinx.datetime.LocalTime
- import kotlinx.datetime.atTime
- import org.jetbrains.exposed.sql.insert
- import org.jetbrains.exposed.sql.transactions.experimental.newSuspendedTransaction
- import org.slf4j.LoggerFactory
- import java.io.File
- private val LOG_PREFIX_REGEX = Regex("^\\[\\d{2}:\\d{2}:\\d{2}]\\s*\\[[^]]*]:\\s+(.*)$")
- private val LINE_TIME_REGEX = Regex("^\\[(\\d{2}):(\\d{2}):(\\d{2})]")
- private val LOCAL_CHAT_REGEX = Regex("^\\[L]\\s+(?:[^:]+\\s+)*(\\S+):\\s+(.*)$")
- private val GLOBAL_CHAT_REGEX = Regex("^\\[G]\\s+(?:[^:]+\\s+)*(\\S+):\\s+(.*)$")
- private val QUEST_REGEX = Regex("^(?:[^:]+\\s+)*(\\S+)\\s+завершил квест:\\s+(.*)$")
- private val log = LoggerFactory.getLogger("LogIndexer")
- private fun String.stripLogPrefix() = LOG_PREFIX_REGEX.find(this)?.groups?.get(1)?.value ?: this
- private fun String.parseLineTime(): LocalTime? {
- val match = LINE_TIME_REGEX.find(this)
- return if (match != null) {
- try {
- LocalTime(
- hour = match.groupValues[1].toInt(),
- minute = match.groupValues[2].toInt(),
- second = match.groupValues[3].toInt()
- )
- } catch (e: Exception) {
- null
- }
- } else null
- }
- data class IndexedEventData(
- val eventType: String, // login, logout, quest, command, chat_local, chat_global
- val nick: String,
- val dateTime: LocalDateTime,
- val details: String?,
- val serverId: Int
- )
- private val WIPE_DATE = LocalDateTime(
- year = 2024,
- monthNumber = 12,
- dayOfMonth = 24,
- hour = 11,
- minute = 0,
- second = 0,
- )
- val challenges = mapOf(
- "+Ной" to "Noy",
- "Драконоборец" to "Dracon",
- "Энергон" to "Energon",
- "Материализм" to "Materializm",
- "Oпытофилия" to "Experience",
- "Термоядерка" to "Nuclear",
- "+Антипацифист" to "Killer",
- "Одержимость" to "Music",
- "Пчелиный мегаполис" to "Alvearies",
- "Во все хмельные" to "Coffee",
- "+Профессор" to "Bees",
- "Кислотный пир" to "Acid",
- "На благо сервера" to "Server",
- "Братство камней" to "Stones",
- "ВнизКоллекционер" to "Collection",
- "Почти бесконечность" to "Infinity",
- "Шапито" to "Fish",
- "Закон Мёрфи" to "Singular",
- "+Флорист" to "Flowers",
- "Макросхемы" to "Schemes",
- "Амортенция" to "Potions",
- "Библиотека" to "Library",
- "Легенда" to "Legend",
- "Палладин" to "Palladium",
- "Месть мехов" to "Stamps",
- "Память" to "Memory",
- )
- fun String.removeNonBmpChars(): String {
- val sb = StringBuilder()
- codePoints().forEach { cp ->
- if (cp <= 0xFFFF) {
- sb.appendCodePoint(cp)
- }
- }
- return sb.toString()
- }
- fun String.removeAnsiCodes(): String {
- val ansiRegex = Regex("\\u001B\\[[;\\d]*[ -/]*[@-~]")
- return replace(ansiRegex, "")
- }
- suspend fun indexLogFile(logFile: File, fileDate: LocalDate, srvId: Int) {
- log.info("Начинаем индексировать лог-файл: ${logFile.name}, дата = $fileDate")
- val events = mutableListOf<IndexedEventData>()
- val lines = logFile.readLines()
- for (line in lines) {
- val clean = line.removeNonBmpChars().removeAnsiCodes().stripLogPrefix()
- val time = line.parseLineTime() ?: LocalTime(0, 0, 0)
- val dateTime = fileDate.atTime(time)
- when {
- clean.contains(" logged in with entity id ") -> {
- val regex = Regex("(\\S+)\\[/([\\d.]+):\\d+].*?logged in")
- regex.find(clean)?.let { match ->
- val nick = match.groupValues[1]
- val ip = match.groupValues[2]
- events.add(IndexedEventData("login", nick, dateTime, "IP: $ip", srvId))
- }
- }
- clean.contains(" lost connection:") -> {
- val regex = Regex("^(\\S+) lost connection:")
- regex.find(clean)?.let { match ->
- val nick = match.groupValues[1]
- events.add(IndexedEventData("logout", nick, dateTime, null, srvId))
- }
- }
- clean.contains("завершил квест:") -> {
- QUEST_REGEX.find(clean)?.let { match ->
- val nick = match.groupValues[1]
- val quest = match.groupValues[2].trim()
- val finalQuest = if (quest.startsWith("Испытания ->")) {
- val challenge = quest.replace("Испытания -> ", "")
- val challengeId = challenges[challenge]
- if (challengeId != null) {
- val elapsed = WIPE_DATE.toInstant().toReadableTimeDifference(dateTime.toInstant())
- val message = "$nick (${srvId.serverName}) завершил испытание $challenge! Это заняло $elapsed!"
- val finalMessage = if (challengeId == "Legend") {
- "@sovetskyii !!! \n$message"
- } else {
- message
- }
- TelegramBatchedMessageSender.addToBatchQueue(QUESTS_CHAT_ID, finalMessage, includeTimeStamp = false)
- challengeId
- } else {
- quest
- }
- } else {
- quest
- }
- events.add(IndexedEventData("quest", nick, dateTime, finalQuest, srvId))
- }
- }
- clean.contains("issued server command:") -> {
- val regex = Regex("^(\\S+) issued server command:\\s+(/\\S+)(.*)")
- regex.find(clean)?.let { match ->
- val nick = match.groupValues[1]
- val command = (match.groupValues[2] + " " + match.groupValues[3]).trim()
- events.add(IndexedEventData("command", nick, dateTime, command, srvId))
- }
- }
- LOCAL_CHAT_REGEX.matches(clean) -> {
- LOCAL_CHAT_REGEX.find(clean)?.let { match ->
- val nick = match.groupValues[1]
- val text = match.groupValues[2]
- events.add(IndexedEventData("chat_local", nick, dateTime, text, srvId))
- }
- }
- GLOBAL_CHAT_REGEX.matches(clean) -> {
- GLOBAL_CHAT_REGEX.find(clean)?.let { match ->
- val nick = match.groupValues[1]
- val text = match.groupValues[2]
- events.add(IndexedEventData("chat_global", nick, dateTime, text, srvId))
- }
- }
- }
- }
- newSuspendedTransaction {
- events.forEach { event ->
- IndexedEvents.insert {
- it[eventType] = event.eventType
- it[nick] = event.nick
- it[eventTime] = event.dateTime
- it[details] = event.details
- it[serverId] = event.serverId
- }
- }
- }
- log.info("Индексирование завершено для сервера $srvId: ${events.size} событий")
- }
Add Comment
Please, Sign In to add comment