Current scope: tibiakt-core| all classes
|
com.galarzaa.tibiakt.core.section.community.highscores.parser
Coverage Summary for Class: HighscoresParser (com.galarzaa.tibiakt.core.section.community.highscores.parser)
| Class | Class, % | Method, % | Branch, % | Line, % | Instruction, % |
|---|---|---|---|---|---|
| HighscoresParser | 100% (1/1) | 100% (5/5) | 67.4% (31/46) | 90.7% (49/54) | 84% (367/437) |
/*
* Copyright © 2025 Allan Galarza
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.galarzaa.tibiakt.core.section.community.highscores.parser
import com.galarzaa.tibiakt.core.collections.getContaining
import com.galarzaa.tibiakt.core.collections.offsetStart
import com.galarzaa.tibiakt.core.domain.world.PvpType
import com.galarzaa.tibiakt.core.enums.IntEnum
import com.galarzaa.tibiakt.core.enums.StringEnum
import com.galarzaa.tibiakt.core.exceptions.ParsingException
import com.galarzaa.tibiakt.core.html.FormData
import com.galarzaa.tibiakt.core.html.TABLE_SELECTOR
import com.galarzaa.tibiakt.core.html.cellsText
import com.galarzaa.tibiakt.core.html.cleanText
import com.galarzaa.tibiakt.core.html.formData
import com.galarzaa.tibiakt.core.html.parsePagination
import com.galarzaa.tibiakt.core.html.parseTablesMap
import com.galarzaa.tibiakt.core.html.rows
import com.galarzaa.tibiakt.core.parser.Parser
import com.galarzaa.tibiakt.core.section.community.highscores.builder.HighscoresBuilder
import com.galarzaa.tibiakt.core.section.community.highscores.builder.highscores
import com.galarzaa.tibiakt.core.section.community.highscores.model.Highscores
import com.galarzaa.tibiakt.core.text.nullIfBlank
import com.galarzaa.tibiakt.core.text.parseLong
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import kotlin.time.Clock
import kotlin.time.Duration.Companion.minutes
/** Parser for highscores. */
public object HighscoresParser : Parser<Highscores?> {
private val numericMatch = Regex("""(\d+)""")
override fun fromContent(content: String): Highscores? {
val document: Document = Jsoup.parse(content)
val boxContent =
document.selectFirst("div.BoxContent") ?: throw ParsingException("BoxContent container not found")
val tables = boxContent.parseTablesMap("table.Table1, table.Table3")
if ("Error" in tables)
return null
return highscores {
tables["Highscores Filter"]?.also {
val formData = boxContent.selectFirst("form")?.formData()
?: throw ParsingException("could not find highscores form")
parseHighscoresFilter(formData)
}
tables.getContaining("HighscoresLast")?.also {
parseHighscoresTable(it)
}
val lastUpdateText = boxContent.selectFirst("span.RightArea")?.cleanText()
?: throw ParsingException("Could not find last update label")
numericMatch.find(lastUpdateText)?.also {
val minutes = it.groups[0]!!.value.toInt()
lastUpdatedAt = Clock.System.now().minus(minutes.minutes)
}
val paginationData = boxContent.selectFirst("small")?.parsePagination()
?: throw ParsingException("could not find pagination block")
currentPage = paginationData.currentPage
totalPages = paginationData.totalPages
resultsCount = paginationData.resultsCount
}
}
private fun HighscoresBuilder.parseHighscoresFilter(formData: FormData) {
world = formData.values["world"].nullIfBlank()
category = IntEnum.Companion.fromValue(formData.values["category"]?.toInt())
?: throw ParsingException("could not find category form value")
battlEyeType = IntEnum.Companion.fromValue(
formData.values["beprotection"]?.toInt()
?: throw ParsingException("could not find beprotection form value")
)
vocation = IntEnum.Companion.fromValue(formData.values["profession"]?.toInt())
for (pvpType in formData.valuesMultiple["${PvpType.Companion.QUERY_PARAM_HIGHSCORES}[]"].orEmpty()) {
PvpType.Companion.fromHighscoresFilterValue(pvpType.toInt())?.apply {
worldTypes.add(this)
}
}
}
private fun HighscoresBuilder.parseHighscoresTable(element: Element) {
val entriesTable = element.selectFirst(TABLE_SELECTOR)
for (row in entriesTable.rows().offsetStart(1)) {
val columns = row.cellsText()
if (columns.size < 6) {
return
}
val columnOffset = if (columns.size == 7) 1 else 0
val loyaltyTitle = if (columns.size == 7) columns[2] else null
addEntry {
rank = columns[0].toInt()
name = columns[1]
vocation = StringEnum.Companion.fromValue(columns[2 + columnOffset])
?: throw ParsingException("invalid vocation found: ${columns[2 + columnOffset]}")
world = columns[3 + columnOffset]
level = columns[4 + columnOffset].toInt()
value = columns[5 + columnOffset].parseLong()
additionalValue = loyaltyTitle
}
}
}
}