Skip to content

Commit

Permalink
Merge pull request #70 from kotlinsyntax/master
Browse files Browse the repository at this point in the history
Cleaned up some methods
  • Loading branch information
Dawsson committed Jul 1, 2024
2 parents 22c628f + 1357e6e commit 277eeea
Show file tree
Hide file tree
Showing 9 changed files with 200 additions and 172 deletions.
70 changes: 33 additions & 37 deletions common/src/main/kotlin/gg/flyte/pluginportal/common/API.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import gg.flyte.pluginportal.common.types.Plugin
import gg.flyte.pluginportal.common.util.GSON
import gg.flyte.pluginportal.common.util.Http.BASE_URL
import okhttp3.OkHttpClient
import okhttp3.Request
import java.net.URLEncoder

object API {
Expand All @@ -16,15 +17,25 @@ object API {
client.cache?.close()
}

private fun get(url: String, params: HashMap<String, String>): String {
val request = okhttp3.Request.Builder()
.url("$BASE_URL$url?" + params.map { "${it.key}=${it.value}" }.joinToString("&"))
.build()
private fun get(url: String, params: Map<String, String>): Pair<String, Int> {
val fullUrl = buildString {
append(BASE_URL)
append(url)
if (params.isNotEmpty()) {
append("?")
append(params.map { (key, value) ->
"${URLEncoder.encode(key, "UTF-8")}=${URLEncoder.encode(value, "UTF-8")}"
}.joinToString("&"))
}
}

val response = client.newCall(request).execute()
return response.body?.string() ?: ""
val request = Request.Builder().url(fullUrl).build()
return client.newCall(request).execute().use { response ->
(response.body?.string() ?: "") to response.code
}
}


private class AuthorisationException(code: Int)
: RuntimeException("Server returned code $code: You do not have permission to access this resource")

Expand All @@ -33,41 +44,26 @@ object API {
"PLEASE REPORT THIS TO THE PLUGIN PORTAL AUTHORS")

fun getPlugin(platform: MarketplacePlatform, id: String): Plugin? {
val response = get("/plugins/${platform.toString().lowercase()}/$id", hashMapOf()).ifEmpty {
return PluginRequestFailedException(platform, id).printStackTrace().let { null }
}
val statusCode: Int = response.substringAfter("\"statusCode\":", "200").slice(0..2).toInt()
when (statusCode) {
200 -> return GSON.fromJson(response, Plugin::class.java)
404 -> return null // not found
401,403 -> { // not authorised
AuthorisationException(statusCode).printStackTrace()
return null
}
400 -> { // bad request
IllegalArgumentException("Server returned code 400: The request for $id on $platform PLEASE REPORT THIS TO THE PLUGIN PORTAL AUTHORS")
.printStackTrace()
return null
}
else -> {
PluginRequestFailedException(platform, id).printStackTrace()
return null
}
}
val (response, code) = get("/plugins/${platform.toString().lowercase()}/$id", emptyMap())
return when (code) {
200 -> GSON.fromJson(response, Plugin::class.java)
404 -> null // not found
401, 403 -> null.also { AuthorisationException(code).printStackTrace() } // not authorised
400 -> null.also {
IllegalArgumentException("Server returned code 400: The request for $id on $platform PLEASE REPORT THIS TO THE PLUGIN PORTAL AUTHORS").printStackTrace()
} // bad request
else -> null.also { PluginRequestFailedException(platform, id).printStackTrace() }
}.takeIf { response.isNotEmpty() }
}

fun getPlugins(prefix: String? = null, limit: Int? = 50): List<Plugin> {
val params = hashMapOf<String, String>()

if (prefix != null) params["prefix"] = URLEncoder.encode(prefix, "UTF-8")
if (limit != null) params["limit"] = limit.toString()
val params = buildMap {
prefix?.let { put("prefix", it) }
limit?.let { put("limit", it.toString()) }
}

return GSON.fromJson(
get(
"/plugins",
params
), Array<Plugin>::class.java
).toList()
val (response, _) = get("/plugins", params)
return GSON.fromJson(response, Array<Plugin>::class.java).toList()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,12 @@ object SearchPlugins {
.build<String, List<Plugin>>()

fun search(query: String): List<Plugin> {
if (searchCache.asMap().containsKey(query)) {
return searchCache.getIfPresent(query)!!
}

val response = API.getPlugins(query).also {
searchCache.put(query, it)
}

return response
return searchCache.get(query) { API.getPlugins(query) }
}

fun getCachedSearch(query: String): List<Plugin>? {
searchCache.asMap().forEach { (key, value) ->
if (query.contains(key, ignoreCase = true)) {
return value
}
}

return null
return searchCache.asMap().entries
.firstOrNull { (key, _) -> query.contains(key, ignoreCase = true) }
?.value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ data class Plugin(
@SerializedName("_id")
val id: String,
val name: String,
val platforms: MutableMap<MarketplacePlatform, PlatformPlugin>,
val platforms: Map<MarketplacePlatform, PlatformPlugin>,
) {
val highestPriorityPlatform get() = MarketplacePlatform.entries.find(platforms::containsKey) ?: platforms.keys.first()
val downloadableName get() = name.replace("/", "")
.replace("\\", "")
val highestPriorityPlatform by lazy {
MarketplacePlatform.entries.find { platforms.containsKey(it) } ?: platforms.keys.first()
}

val downloadableName by lazy {
name.replace(Regex("[/\\\\]"), "")
}

fun getFirstPlatform(): PlatformPlugin? = platforms.values.firstOrNull()

Expand All @@ -31,7 +35,9 @@ data class Plugin(
}
}

val totalDownloads: Int get() = platforms.values.sumOf { platform -> platform.downloads }
val totalDownloads by lazy {
platforms.values.sumOf { it.downloads }
}
}

data class PlatformPlugin(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package gg.flyte.pluginportal.plugin.chat

import DefaultFontInfo
import gg.flyte.pluginportal.common.types.LocalPlugin
import gg.flyte.pluginportal.common.types.Plugin
import gg.flyte.pluginportal.plugin.util.format
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package gg.flyte.pluginportal.plugin.chat

enum class DefaultFontInfo(val character: Char, val length: Int) {

A('A', 5),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,20 @@ import net.kyori.adventure.identity.Identity
import org.bukkit.command.CommandSender
import java.io.File
import java.util.logging.Level
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream

// TODO: After 1k entries move this to a zipped archive folder and create a new file
// TODO: Load & Read & make queryable
object PortalLogger {
private val file = File(PluginPortal.instance.dataFolder, "history.log").createIfNotExists()
private const val MAX_ENTRIES = 1000
private const val ARCHIVE_FOLDER = "archive"
private val dataFolder = PluginPortal.instance.dataFolder
private var currentFile = File(dataFolder, "history.log").apply { createNewFile() }
private var entryCount = 0

init {
loadExistingEntries()
}

fun info(action: Action, message: String) = log(Record(System.currentTimeMillis(), "SYSTEM", action, message))

Expand All @@ -31,7 +40,73 @@ object PortalLogger {
writeToFile(record)
}

private fun writeToFile(record: Record) = async { file.appendLine(record.description) }
private fun writeToFile(record: Record) {
currentFile.appendLine(record.description)
entryCount++

if (entryCount >= MAX_ENTRIES) {
archiveCurrentFile()
createNewFile()
}
}

private fun archiveCurrentFile() {
val archiveFolder = File(dataFolder, ARCHIVE_FOLDER).apply { mkdirs() }
val archiveFile = File(archiveFolder, "history_${System.currentTimeMillis()}.zip")

ZipOutputStream(archiveFile.outputStream()).use { zipOut ->
zipOut.putNextEntry(ZipEntry(currentFile.name))
currentFile.inputStream().use { input ->
input.copyTo(zipOut)
}
}

currentFile.delete()
}

private fun createNewFile() {
currentFile = File(dataFolder, "history.log").apply { createNewFile() }
entryCount = 0
}

private fun loadExistingEntries() {
entryCount = currentFile.readLines().size
}

fun query(filter: (Record) -> Boolean): List<Record> {
val records = mutableListOf<Record>()

// read current
currentFile.readLines().mapNotNull { parseRecord(it) }.filter(filter).let { records.addAll(it) }

// read from archive
File(dataFolder, ARCHIVE_FOLDER).listFiles { file -> file.extension == "zip" }?.forEach { zipFile ->
ZipFile(zipFile).use { zip ->
zip.entries().asSequence().forEach { entry ->
zip.getInputStream(entry).bufferedReader().useLines { lines ->
lines.mapNotNull { parseRecord(it) }.filter(filter).let { records.addAll(it) }
}
}
}
}

return records
}

private fun parseRecord(line: String): Record? {
val parts = line.split("|")
if (parts.size != 4) return null
return try {
Record(
parts[0].toLong(),
parts[1],
Action.valueOf(parts[2]),
parts[3]
)
} catch (e: Exception) {
null
}
}

enum class Action {
// Actions are queried linearly thus AUTO_UPDATE must precede UPDATE etc.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package gg.flyte.pluginportal.plugin.manager

import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import gg.flyte.pluginportal.common.API
import gg.flyte.pluginportal.common.types.MarketplacePlatform
import gg.flyte.pluginportal.common.types.Plugin
Expand All @@ -12,9 +15,22 @@ import net.kyori.adventure.audience.Audience
import net.kyori.adventure.text.Component.newline
import net.kyori.adventure.text.Component.text
import net.kyori.adventure.text.format.NamedTextColor
import java.util.*
import java.util.concurrent.TimeUnit

object MarketplacePluginCache : PluginCache<Plugin>() {

private val pluginCache: LoadingCache<Pair<MarketplacePlatform, String>, Optional<Plugin>> = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build(
object : CacheLoader<Pair<MarketplacePlatform, String>, Optional<Plugin>>() {
override fun load(key: Pair<MarketplacePlatform, String>): Optional<Plugin> {
return Optional.ofNullable(API.getPlugin(key.first, key.second))
}
}
)

fun getFilteredPlugins(
prefix: String,
platform: MarketplacePlatform? = null,
Expand All @@ -24,33 +40,35 @@ object MarketplacePluginCache : PluginCache<Plugin>() {
return plugins
}

fun getPluginById(platform: MarketplacePlatform, platformId: String) = API.getPlugin(platform, platformId) // TODO: Add a caching layer here
fun getPluginById(platform: MarketplacePlatform, platformId: String): Plugin? {
return pluginCache.get(platform to platformId).orElse(null)
}

fun handlePluginSearchFeedback (
fun handlePluginSearchFeedback(
audience: Audience,
name: String,
platform: MarketplacePlatform?,
nameIsId: Boolean,
ifSingle: (Plugin) -> Unit, // Async
ifMore: (List<Plugin>) -> Unit // Also Async
) {
val prefix = if (nameIsId) null else name
val id = if (nameIsId) name else null

if (id != null) {
if (platform == null) return audience.sendFailure("Specify a platform to check the platformId: $id")
return async {
getPluginById(platform, id)?.let { ifSingle.invoke(it) } ?: audience.sendFailure("No plugin found")
ifSingle: (Plugin) -> Unit,
ifMore: (List<Plugin>) -> Unit
) = async {
when {
nameIsId -> {
if (platform == null) {
audience.sendFailure("Specify a platform to check the platformId: $name")
} else {
getPluginById(platform, name)?.let { ifSingle(it) }
?: audience.sendFailure("No plugin found")
}
}
else -> {
val plugins = getFilteredPlugins(name, platform)
when {
plugins.isEmpty() -> audience.sendFailure("No plugins found")
plugins.size == 1 -> ifSingle(plugins.first())
else -> ifMore(plugins.sortedByRelevance(name))
}
}
}

async {
val plugins = getFilteredPlugins(prefix!!, platform) // May not return all results

if (plugins.isEmpty()) return@async audience.sendFailure("No plugins found")

if (plugins.size == 1) ifSingle.invoke(plugins.first())
else ifMore.invoke(plugins.sortedByRelevance(name))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,15 @@ object ChatImage {
* @return The average color of the image.
*/
fun getAverageColor(image: BufferedImage): Color {
var sampled = 0
var sumR: Long = 0
var sumG: Long = 0
var sumB: Long = 0
for (x in 0 until image.width) {
for (y in 0 until image.height) {
val pixel = Color(image.getRGB(x, y))
sumR += pixel.red.toLong()
sumG += pixel.green.toLong()
sumB += pixel.blue.toLong()
sampled++
val (sumR, sumG, sumB) = image.getRGB(0, 0, image.width, image.height, null, 0, image.width)
.asSequence()
.map { Color(it) }
.fold(Triple(0, 0, 0)) { (r, g, b), color ->
Triple(r + color.red, g + color.green, b + color.blue)
}
}
return Color(
(sumR / sampled).toInt(),
(sumG / sampled).toInt(),
(sumB / sampled).toInt()
)

val pixelCount = image.width * image.height
return Color(sumR / pixelCount, sumG / pixelCount, sumB / pixelCount)
}


Expand Down
Loading

0 comments on commit 277eeea

Please sign in to comment.