package downloader import com.mohamedrejeb.ksoup.html.parser.KsoupHtmlHandler import com.mohamedrejeb.ksoup.html.parser.KsoupHtmlParser import fuel.Fuel import fuel.method import kotlinx.coroutines.runBlocking import java.io.FileNotFoundException import java.nio.charset.Charset import java.nio.file.Files import java.nio.file.Paths import java.time.LocalDate import java.time.Month import java.time.ZoneId fun main() { var cookie = "" if (Files.exists(Paths.get("gradle.properties"))) { val cookieFromProps = Files.readAllLines(Paths.get("gradle.properties")) .map { it.split("=") } .filter { it.size == 2 } .filter { it[0] == "cookie" } .map { it[1] }.firstOrNull() if (cookieFromProps != null) { cookie = cookieFromProps } } val downloader = Downloader(2022, "aoc2022", cookie) downloader.updateToLatest() } class Downloader(val year: Int, val packageName: String, val sessionCookie: String) { companion object { val INPUT_FILENAME = "Day%02d.txt" val HTML_FILENAME = "Day%02d_description.html" val DESC_FILENAME = "Day%02d_description.txt" val TEMPLATE_FILENAME = "Day_template.txt" val GENCLASS_FILENAME = "Day%02d.kt" } fun updateToLatest() { val targetDir = Paths.get("src", packageName) if (!Files.exists(targetDir)) { Files.createDirectories(targetDir) } val now = LocalDate.now(ZoneId.of("UTC-1")) val lastDay = if (now.isBefore(LocalDate.of(year, Month.DECEMBER, 25))) { if (now.isAfter(LocalDate.of(year, Month.NOVEMBER, 30))) { now.dayOfMonth } else { throw IllegalStateException("You're too early.") } } else { 25 } for (day in 1..lastDay) { val inputFile = targetDir.resolve(INPUT_FILENAME.format(day)) val htmlFile = targetDir.resolve(HTML_FILENAME.format(day)) val descriptionFile = targetDir.resolve(DESC_FILENAME.format(day)) val genClassFile = targetDir.resolve(GENCLASS_FILENAME.format(day)) if (!Files.exists(inputFile)) { println("Attempting to download input for day $day") val (code, data) = downloadInput(day) if (code != 200) { throw FileNotFoundException("Could not download input for $year/$day: Status $code, Message: $data") } Files.write(inputFile, data.toByteArray(Charset.defaultCharset())) } else { println("Skipping input for day $day") } if (!Files.exists(htmlFile)) { println("Attempting to download html for day $day") val (code, data) = downloadDescription(day) if (code != 200) { throw FileNotFoundException("Could not download html for $year/$day: Status $code, Message: $data") } Files.write(htmlFile, data.toByteArray(Charset.defaultCharset())) } else { println("Skipping html for day $day") } var description = "" var testInput = "" if (Files.exists(htmlFile)) { val html = Files.readAllBytes(htmlFile).toString(Charsets.UTF_8) var enableDescription = false var preFound = false var codeFound = false val handler = KsoupHtmlHandler .Builder() .onOpenTag { name, attributes, isImplied -> when (name) { "article" -> enableDescription = (attributes["class"] == "day-desc") "pre" -> preFound = enableDescription "code" -> codeFound = preFound } } .onCloseTag { name, isImplied -> when (name) { "article" -> enableDescription = false "pre" -> preFound = false "code" -> codeFound = false "h2" -> if (enableDescription) description += "\n" } } .onText { text -> if (enableDescription) { description += text } if (preFound) { testInput += text } } .build() val ksoupHtmlParser = KsoupHtmlParser( handler = handler, ) ksoupHtmlParser.write(html) ksoupHtmlParser.end() testInput = testInput.trim() Files.writeString(descriptionFile, description) } if (!Files.exists(genClassFile)) { var template = Files.readString(Paths.get("src", TEMPLATE_FILENAME)) template = template.replace("\${package}", packageName) template = template.replace("\${day}", "%02d".format(day)) template = template.replace("\${testinput}", testInput) template = template.replace("\${description}", description) Files.writeString(genClassFile, template) } } } fun downloadInput(day: Int): Pair { return downloadStuff(day, "/input") } fun downloadDescription(day: Int): Pair { return downloadStuff(day, "") } fun downloadStuff(day: Int, suffix: String): Pair { return runBlocking { val req = Fuel.method( url = "https://adventofcode.com/$year/day/$day$suffix", method = "GET", headers = mapOf( "User-Agent" to "git.platon42.de/chrisly42/advent-of-code-2023", "Cookie" to "session=$sessionCookie" ) ) req.statusCode to req.body } } }