Skip to content

Commit

Permalink
Implement straightforward cf parsing.
Browse files Browse the repository at this point in the history
  • Loading branch information
Bernasss12 committed Jul 30, 2023
1 parent 37b3680 commit 3421cbe
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 8 deletions.
79 changes: 79 additions & 0 deletions src/main/kotlin/dev/bernasss12/fetching/CurseforgeDataFetcher.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package dev.bernasss12.fetching

import dev.bernasss12.util.Common
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.java.*
import io.ktor.client.plugins.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.server.plugins.*
import io.ktor.util.logging.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject
import kotlin.time.Duration.Companion.minutes

object CurseforgeDataFetcher : DataFetcher {

private const val CURSEFORGE_URL = "https://api.curseforge.com/v1/mods/"
private val logger = KtorSimpleLogger(javaClass.canonicalName)
private val mutex = Mutex()
private var _key = Common.getCurseforgeSecret(logger)
private val apiKey: String?
get() {
if (_key == null) {
// Tries to get the token everytime if it was not previously gotten.
_key = Common.getCurseforgeSecret(logger)
}
return _key
}
private val httpClient = HttpClient(Java) {
install(UserAgent) {
agent = Common.getUserAgent(logger)
}
}

private val cache: MutableMap<UInt, Pair<Long, JsonObject>> = hashMapOf()
private val cacheTimeout = 5.minutes

override suspend fun requestModInfo(modid: String): JsonObject {
val key = apiKey
if (key == null) {
logger.error("No curseforge api key found, this won't do.")
error("No curseforge api key found, this won't do.")
} else {
mutex.withLock {
val id: UInt = modid.toUIntOrNull() ?: throw BadRequestException("modid parameter does not match regular expression for mod ids")

val cacheEntry = cache[id]
if (cacheEntry != null && System.currentTimeMillis() <= cacheEntry.first + cacheTimeout.inWholeMilliseconds) {
return cacheEntry.second
}

val requestUrl = "$CURSEFORGE_URL$id"
val response = httpClient.get(requestUrl) {
headers {
append(HttpHeaders.Accept, ContentType.Application.Json)
append("x-api-key", key)
}
}
if (response.status == HttpStatusCode.OK) {
val currentTime = System.currentTimeMillis()
return Json.decodeFromString<JsonObject>(response.body() as String).also { jsonObject ->
cache[id] = currentTime to jsonObject
}
} else {
val body: String = try {
response.body()
} catch (err: RuntimeException) {
"unable to get response body: ${err.javaClass.name}"
}
logger.error("Requesting $requestUrl returned bad status ${response.status}:\n$body")
throw RuntimeException("Curseforge API request did not succeed")
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ object ModrinthDataFetcher : DataFetcher {

const val MOD_ID_REGEX = "[\\w!@\$()`.+,\"\\-']{3,64}"

private const val modrinthUrl = "https://api.modrinth.com/v2/project/"
private const val MODRINTH_URL = "https://api.modrinth.com/v2/project/"
private val modIdRegex = MOD_ID_REGEX.toRegex()
private val logger = KtorSimpleLogger(javaClass.canonicalName)
private val mutex = Mutex()
Expand Down Expand Up @@ -50,7 +50,7 @@ object ModrinthDataFetcher : DataFetcher {
if (remainingRequests == 0) {
delay((resetAt - System.currentTimeMillis()).milliseconds)
}
val requestUrl = "$modrinthUrl$modid"
val requestUrl = "$MODRINTH_URL$modid"
val response = httpClient.get(requestUrl) {
headers {
append(HttpHeaders.Accept, ContentType.Application.Json)
Expand Down
27 changes: 21 additions & 6 deletions src/main/kotlin/dev/bernasss12/parsing/CurseforgeDataParser.kt
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
package dev.bernasss12.parsing

import dev.bernasss12.fetching.CurseforgeDataFetcher
import io.ktor.util.logging.*
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive

object CurseforgeDataParser : DataParser {

private val fetcher = CurseforgeDataFetcher
private val logger = KtorSimpleLogger(javaClass.canonicalName)

private suspend fun getDataObject(id: String): JsonObject {
return fetcher.requestModInfo(id)["data"]?.jsonObject ?: error("missing \"data\" object")
}

override suspend fun getModName(id: String): String {
TODO("Not yet implemented")
return getDataObject(id)["name"]?.jsonPrimitive?.content ?: "Error".also {
logger.warn("Error while trying to parse 'name' form object...")
}
}

override suspend fun getSupportedGameVersions(id: String): List<String> {
TODO("Not yet implemented")
TODO("traverse latest files, grab versions and then sort them.")
}

override suspend fun getDownloadCount(id: String): UInt {
TODO("Not yet implemented")
override suspend fun getDownloadCount(id: String): UInt? {
return getDataObject(id)["downloadCount"]?.jsonPrimitive?.content?.toUInt()
}

override suspend fun getSupportedModLoaders(id: String): List<String> {
TODO("Not yet implemented")
TODO("traverse latest files and grab modloader names from \"gameVersions\".")
}

override suspend fun getLicence(id: String): String {
TODO("Not yet implemented")
TODO("not availible on the given data, don't know how to go about it...")
}

}
13 changes: 13 additions & 0 deletions src/main/kotlin/dev/bernasss12/plugins/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,19 @@ fun Application.configureRouting() {
status = HttpStatusCode.OK
)
}
get("/curseforge/{modid}/name") {
val modId = getModID(call) ?: return@get
call.respondText(
text = SvgGenerator.generate(
Template.CURSEFORGE_ICON_TEXT,
modId,
CurseforgeDataParser,
SvgGenerator.DataTypes.NAME.toString()
),
contentType = ContentType.Image.SVG,
status = HttpStatusCode.OK
)
}
get("/modrinth/{modid}/versions") {
val modId = getModID(call) ?: return@get
call.respondText(
Expand Down
10 changes: 10 additions & 0 deletions src/main/kotlin/dev/bernasss12/util/Common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ object Common {
?: "Bernasss12/BadgeServer"
}

/**
* Gets the curseforge api token from curseforge-api-token.
*/
fun getCurseforgeSecret(logger: Logger): String? {
return getPrivateString("curseforge-api-token", logger).also {
println(it)
println(it?.chars())
}
}

/**
* Tries to get the user-agent to be sent in the GET requests from the following places, in order:
* ./ Current directory
Expand Down

0 comments on commit 3421cbe

Please sign in to comment.