Coverage Summary for Class: GuildParser (com.galarzaa.tibiakt.core.section.community.guild.parser)
| Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
| GuildParser |
100%
(1/1)
|
85.7%
(6/7)
|
63.5%
(33/52)
|
93.4%
(57/61)
|
85.8%
(488/569)
|
/*
* 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.guild.parser
import com.galarzaa.tibiakt.core.enums.StringEnum
import com.galarzaa.tibiakt.core.exceptions.ParsingException
import com.galarzaa.tibiakt.core.html.boxContent
import com.galarzaa.tibiakt.core.html.parseTablesMap
import com.galarzaa.tibiakt.core.html.wholeCleanText
import com.galarzaa.tibiakt.core.parser.Parser
import com.galarzaa.tibiakt.core.section.community.guild.builder.GuildBuilder
import com.galarzaa.tibiakt.core.section.community.guild.builder.guild
import com.galarzaa.tibiakt.core.section.community.guild.model.Guild
import com.galarzaa.tibiakt.core.text.clean
import com.galarzaa.tibiakt.core.text.nullIfBlank
import com.galarzaa.tibiakt.core.text.remove
import com.galarzaa.tibiakt.core.time.parseTibiaDate
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
import org.jsoup.nodes.Element
import org.jsoup.nodes.TextNode
import org.jsoup.select.Elements
/** Parser for the guild information page. */
public object GuildParser : Parser<Guild?> {
private val descriptionRegex =
Regex(
"""(?<description>.*)?The guild was founded on (?<world>\w+) on (?<date>[^.]+)\.""",
setOf(RegexOption.DOT_MATCHES_ALL, RegexOption.MULTILINE)
)
private val guildHallRegex =
Regex("""Their home on (?<world>\w+) is (?<name>[^.]+)\. The rent is paid until (?<paidUntil>[^.]+)""")
private val disbandingRegex = Regex("""It will be disbanded on (\w+\s\d+\s\d+)\s([^.]+).""")
override fun fromContent(content: String): Guild? {
val document: Document = Jsoup.parse(content)
val boxContent = document.boxContent()
return guild {
val tables = boxContent.parseTablesMap("table.Table1, table.Table3")
if ("Error" in tables) {
return null
}
tables["Guild Information"]?.apply { parseGuildInformation(this) }
?: throw ParsingException("Guild Information container not found")
name = boxContent.selectFirst("h1")?.text()?.trim() ?: throw ParsingException("Guild title not found")
val guildImg = boxContent.selectFirst("img[width=64]")
logoUrl = guildImg?.attr("src")
tables["Guild Members"]?.apply { parseGuildMembers(this) }
tables["Invited Characters"]?.apply { parseGuildMembers(this) }
}
}
private fun GuildBuilder.parseGuildMembers(parsedContent: Element) {
val rows = parsedContent.select("tr[bgcolor]")
var currentRank = ""
for (row in rows) {
val columns = row.select("td")
when (columns.size) {
6 -> currentRank = parseMemberRow(columns, currentRank)
2 -> parseInviteRow(columns)
}
}
}
private fun GuildBuilder.parseInviteRow(columns: Elements) {
val (name, date) = columns.map { it.text().clean() }
if (date.contains("Invitation Date")) return
addInvite(name, parseTibiaDate(date))
}
private fun GuildBuilder.parseMemberRow(columns: List<Element>, currentRank: String): String {
var rank = columns[0].text().clean()
if (rank.isBlank()) {
rank = currentRank
}
val nameLink = columns[1].selectFirst("a") ?: return currentRank
val titleNode = nameLink.nextSibling()
addMember {
this.rank = rank
name = nameLink.text().clean()
title = (titleNode as? TextNode)?.text()?.remove("(")?.remove(")")?.clean()
vocation = StringEnum.Companion.fromValue(columns[2].text().clean())
?: throw ParsingException("unknown vocation in member: ${columns[2].text().clean()}")
level = columns[3].text().toInt()
joinedOn = parseTibiaDate(columns[4].text().clean())
isOnline = columns[5].text().contains("online")
}
return rank
}
private fun GuildBuilder.parseGuildInformation(container: Element) {
val containerText = container.wholeCleanText()
descriptionRegex.find(containerText)?.apply {
description = groups["description"]?.value?.trim().nullIfBlank()
foundedOn = parseTibiaDate(groups["date"]!!.value)
world = groups["world"]!!.value
}
areApplicationsOpen = containerText.contains("opened for applications")
isActive = containerText.contains("it is currently active", true)
container.selectFirst("a")?.apply { homepage = text() }
guildHallRegex.find(containerText)?.apply {
guildHall(groups["name"]!!.value, parseTibiaDate(groups["paidUntil"]!!.value))
}
disbandingRegex.find(containerText)?.apply {
val (_, date, reason) = groupValues
disbandsOn = parseTibiaDate(date)
disbandingReason = reason
}
}
}