mirror of
https://github.com/jie65535/mirai-console-jcf-plugin.git
synced 2025-06-02 17:39:15 +08:00
Added Curseforge Api Tests
Upgraded kotlin version to 1.6.21 Upgraded mirai console version to 2.11.1
This commit is contained in:
parent
be20a22fe9
commit
0558bbfd07
1
.gitignore
vendored
1
.gitignore
vendored
@ -119,3 +119,4 @@ run/
|
|||||||
|
|
||||||
# Local Test Launch point
|
# Local Test Launch point
|
||||||
src/test/kotlin/RunTerminal.kt
|
src/test/kotlin/RunTerminal.kt
|
||||||
|
/api_key.txt
|
||||||
|
@ -1,15 +1,20 @@
|
|||||||
plugins {
|
plugins {
|
||||||
val kotlinVersion = "1.5.10"
|
val kotlinVersion = "1.6.21"
|
||||||
kotlin("jvm") version kotlinVersion
|
kotlin("jvm") version kotlinVersion
|
||||||
kotlin("plugin.serialization") version kotlinVersion
|
kotlin("plugin.serialization") version kotlinVersion
|
||||||
|
|
||||||
id("net.mamoe.mirai-console") version "2.7.0"
|
id("net.mamoe.mirai-console") version "2.11.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "me.jie65535"
|
group = "top.jie65535"
|
||||||
version = "0.1.1"
|
version = "1.0.0"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
maven("https://maven.aliyun.com/repository/public")
|
maven("https://maven.aliyun.com/repository/public")
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.2")
|
||||||
}
|
}
|
@ -9,6 +9,8 @@ import kotlinx.serialization.*
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import top.jie65535.jcf.model.Category
|
import top.jie65535.jcf.model.Category
|
||||||
import top.jie65535.jcf.model.file.File
|
import top.jie65535.jcf.model.file.File
|
||||||
|
import top.jie65535.jcf.model.game.Game
|
||||||
|
import top.jie65535.jcf.model.game.GameVersionType
|
||||||
import top.jie65535.jcf.model.mod.*
|
import top.jie65535.jcf.model.mod.*
|
||||||
import top.jie65535.jcf.model.request.*
|
import top.jie65535.jcf.model.request.*
|
||||||
import top.jie65535.jcf.model.request.SortOrder.*
|
import top.jie65535.jcf.model.request.SortOrder.*
|
||||||
@ -46,8 +48,54 @@ class CurseforgeApi(apiKey: String) {
|
|||||||
|
|
||||||
//region - Game -
|
//region - Game -
|
||||||
|
|
||||||
// Minecraft Game ID is 432
|
/**
|
||||||
// Ignore Game APIs
|
* Get all games that are available to the provided API key.
|
||||||
|
*/
|
||||||
|
suspend fun getGames(index: Int? = null, pageSize: Int? = null): GetGamesResponse {
|
||||||
|
return json.decodeFromString(
|
||||||
|
http.get("/v1/games") {
|
||||||
|
parameter("index", index)
|
||||||
|
parameter("pageSize", pageSize)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a single game. A private game is only accessible by its respective API key.
|
||||||
|
*/
|
||||||
|
suspend fun getGame(gameId: Int): Game {
|
||||||
|
return json.decodeFromString<GetGameResponse>(
|
||||||
|
http.get("/v1/games/$gameId")
|
||||||
|
).data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available versions for each known version type of the specified game.
|
||||||
|
* A private game is only accessible to its respective API key.
|
||||||
|
*/
|
||||||
|
suspend fun getVersions(gameId: Int): Array<GameVersionsByType> {
|
||||||
|
return json.decodeFromString<GetVersionsResponse>(
|
||||||
|
http.get("/v1/games/$gameId/versions")
|
||||||
|
).data
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all available version types of the specified game.
|
||||||
|
*
|
||||||
|
* A private game is only accessible to its respective API key.
|
||||||
|
*
|
||||||
|
* Currently, when creating games via the CurseForge Core Console,
|
||||||
|
* you are limited to a single game version type.
|
||||||
|
* This means that this endpoint is probably not useful in most cases
|
||||||
|
* and is relevant mostly when handling existing games that have
|
||||||
|
* multiple game versions such as World of Warcraft and Minecraft
|
||||||
|
* (e.g. 517 for wow_retail).
|
||||||
|
*/
|
||||||
|
suspend fun getVersionTypes(gameId: Int): Array<GameVersionType> {
|
||||||
|
return json.decodeFromString<GetVersionTypesResponse>(
|
||||||
|
http.get("/v1/games/$gameId/versions")
|
||||||
|
).data
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
|
|
||||||
@ -137,6 +185,7 @@ class CurseforgeApi(apiKey: String) {
|
|||||||
suspend fun getMods(modIds: IntArray): Array<Mod> {
|
suspend fun getMods(modIds: IntArray): Array<Mod> {
|
||||||
return json.decodeFromString<GetModsResponse>(
|
return json.decodeFromString<GetModsResponse>(
|
||||||
http.post("/v1/mods") {
|
http.post("/v1/mods") {
|
||||||
|
headers.append("Content-Type", "application/json")
|
||||||
body = json.encodeToString(GetModsByIdsListRequestBody(modIds))
|
body = json.encodeToString(GetModsByIdsListRequestBody(modIds))
|
||||||
}
|
}
|
||||||
).data
|
).data
|
||||||
@ -147,11 +196,12 @@ class CurseforgeApi(apiKey: String) {
|
|||||||
*/
|
*/
|
||||||
suspend fun getFeaturedMods(
|
suspend fun getFeaturedMods(
|
||||||
gameId: Int,
|
gameId: Int,
|
||||||
excludedModIds: IntArray,
|
excludedModIds: IntArray = intArrayOf(),
|
||||||
gameVersionTypeId: Int? = null
|
gameVersionTypeId: Int? = null
|
||||||
): FeaturedModsResponse {
|
): FeaturedModsResponse {
|
||||||
return json.decodeFromString<GetFeaturedModsResponse>(
|
return json.decodeFromString<GetFeaturedModsResponse>(
|
||||||
http.get("/v1/mods/featured") {
|
http.get("/v1/mods/featured") {
|
||||||
|
headers.append("Content-Type", "application/json")
|
||||||
body = json.encodeToString(GetFeaturedModsRequestBody(gameId, excludedModIds, gameVersionTypeId))
|
body = json.encodeToString(GetFeaturedModsRequestBody(gameId, excludedModIds, gameVersionTypeId))
|
||||||
}
|
}
|
||||||
).data
|
).data
|
||||||
@ -207,6 +257,7 @@ class CurseforgeApi(apiKey: String) {
|
|||||||
suspend fun getFiles(fileIds: IntArray): Array<File> {
|
suspend fun getFiles(fileIds: IntArray): Array<File> {
|
||||||
return json.decodeFromString<GetFilesResponse>(
|
return json.decodeFromString<GetFilesResponse>(
|
||||||
http.post("/v1/mods/files") {
|
http.post("/v1/mods/files") {
|
||||||
|
headers.append("Content-Type", "application/json")
|
||||||
body = json.encodeToString(GetModFilesRequestBody(fileIds))
|
body = json.encodeToString(GetModFilesRequestBody(fileIds))
|
||||||
}
|
}
|
||||||
).data
|
).data
|
||||||
|
@ -24,17 +24,17 @@ class Category(
|
|||||||
/**
|
/**
|
||||||
* The category slug as it appear in the URL
|
* The category slug as it appear in the URL
|
||||||
*/
|
*/
|
||||||
val slug: String,
|
val slug: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The category URL
|
* The category URL
|
||||||
*/
|
*/
|
||||||
val url: String,
|
val url: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* URL for the category icon
|
* URL for the category icon
|
||||||
*/
|
*/
|
||||||
val iconUrl: String,
|
val iconUrl: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Last modified date of the category
|
* Last modified date of the category
|
||||||
@ -45,20 +45,20 @@ class Category(
|
|||||||
/**
|
/**
|
||||||
* A top level category for other categories
|
* A top level category for other categories
|
||||||
*/
|
*/
|
||||||
val isClass: Boolean?,
|
val isClass: Boolean? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class id of the category, meaning - the class of which this category is under
|
* The class id of the category, meaning - the class of which this category is under
|
||||||
*/
|
*/
|
||||||
val classId: Int?,
|
val classId: Int? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The parent category for this category
|
* The parent category for this category
|
||||||
*/
|
*/
|
||||||
val parentCategoryId: Int?,
|
val parentCategoryId: Int? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The display index for this category
|
* The display index for this category
|
||||||
*/
|
*/
|
||||||
val displayIndex: Int?
|
val displayIndex: Int? = null
|
||||||
)
|
)
|
||||||
|
@ -30,5 +30,5 @@ class SortableGameVersion(
|
|||||||
/**
|
/**
|
||||||
* Game version type id
|
* Game version type id
|
||||||
*/
|
*/
|
||||||
val gameVersionTypeId: Int?
|
val gameVersionTypeId: Int? = null
|
||||||
)
|
)
|
||||||
|
@ -59,7 +59,7 @@ class File(
|
|||||||
/**
|
/**
|
||||||
* The file download URL
|
* The file download URL
|
||||||
*/
|
*/
|
||||||
val downloadUrl: String,
|
val downloadUrl: String?,
|
||||||
/**
|
/**
|
||||||
* List of game versions this file is relevant for
|
* List of game versions this file is relevant for
|
||||||
*/
|
*/
|
||||||
@ -75,23 +75,23 @@ class File(
|
|||||||
/**
|
/**
|
||||||
* none
|
* none
|
||||||
*/
|
*/
|
||||||
val exposeAsAlternative: Boolean?,
|
val exposeAsAlternative: Boolean? = null,
|
||||||
/**
|
/**
|
||||||
* none
|
* none
|
||||||
*/
|
*/
|
||||||
val parentProjectFileId: Int?,
|
val parentProjectFileId: Int? = null,
|
||||||
/**
|
/**
|
||||||
* none
|
* none
|
||||||
*/
|
*/
|
||||||
val alternateFileId: Int?,
|
val alternateFileId: Int? = null,
|
||||||
/**
|
/**
|
||||||
* none
|
* none
|
||||||
*/
|
*/
|
||||||
val isServerPack: Boolean?,
|
val isServerPack: Boolean? = null,
|
||||||
/**
|
/**
|
||||||
* none
|
* none
|
||||||
*/
|
*/
|
||||||
val serverPackFileId: Int?,
|
val serverPackFileId: Int? = null,
|
||||||
/**
|
/**
|
||||||
* none
|
* none
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +8,6 @@ class FileIndex(
|
|||||||
val fileId: Int,
|
val fileId: Int,
|
||||||
val filename: String,
|
val filename: String,
|
||||||
val releaseType: FileReleaseType,
|
val releaseType: FileReleaseType,
|
||||||
val gameVersionTypeId: Int?,
|
val gameVersionTypeId: Int? = null,
|
||||||
val modLoader: ModLoaderType,
|
val modLoader: ModLoaderType? = null,
|
||||||
)
|
)
|
||||||
|
@ -12,5 +12,5 @@ enum class FileRelationType {
|
|||||||
Incompatible,
|
Incompatible,
|
||||||
Include;
|
Include;
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<FileRelationType> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<FileRelationType> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
||||||
|
@ -9,5 +9,5 @@ enum class FileReleaseType {
|
|||||||
Beta,
|
Beta,
|
||||||
Alpha;
|
Alpha;
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<FileReleaseType> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<FileReleaseType> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
||||||
|
@ -21,5 +21,5 @@ enum class FileStatus{
|
|||||||
AwaitingPublishing,
|
AwaitingPublishing,
|
||||||
FailedPublishing;
|
FailedPublishing;
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<FileStatus> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<FileStatus> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,5 @@ enum class HashAlgo(val value: Int) {
|
|||||||
Sha1(1),
|
Sha1(1),
|
||||||
Md5(2);
|
Md5(2);
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<HashAlgo> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<HashAlgo> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
||||||
|
12
src/main/kotlin/top/jie65535/jcf/model/game/CoreApiStatus.kt
Normal file
12
src/main/kotlin/top/jie65535/jcf/model/game/CoreApiStatus.kt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package top.jie65535.jcf.model.game
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import top.jie65535.jcf.util.EnumIndexSerializer
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable(with = CoreApiStatus.IndexSerializer::class)
|
||||||
|
enum class CoreApiStatus {
|
||||||
|
Private,
|
||||||
|
Public;
|
||||||
|
|
||||||
|
internal object IndexSerializer : KSerializer<CoreApiStatus> by EnumIndexSerializer(values())
|
||||||
|
}
|
16
src/main/kotlin/top/jie65535/jcf/model/game/CoreStatus.kt
Normal file
16
src/main/kotlin/top/jie65535/jcf/model/game/CoreStatus.kt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package top.jie65535.jcf.model.game
|
||||||
|
|
||||||
|
import kotlinx.serialization.KSerializer
|
||||||
|
import top.jie65535.jcf.util.EnumIndexSerializer
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable(with = CoreStatus.IndexSerializer::class)
|
||||||
|
enum class CoreStatus {
|
||||||
|
Draft,
|
||||||
|
Test,
|
||||||
|
PendingReview,
|
||||||
|
Rejected,
|
||||||
|
Approved,
|
||||||
|
Live;
|
||||||
|
|
||||||
|
internal object IndexSerializer : KSerializer<CoreStatus> by EnumIndexSerializer(values())
|
||||||
|
}
|
17
src/main/kotlin/top/jie65535/jcf/model/game/Game.kt
Normal file
17
src/main/kotlin/top/jie65535/jcf/model/game/Game.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package top.jie65535.jcf.model.game
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
import top.jie65535.jcf.util.OffsetDateTimeSerializer
|
||||||
|
import java.time.OffsetDateTime
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
class Game(
|
||||||
|
val id: Int,
|
||||||
|
val name: String,
|
||||||
|
val slug: String?,
|
||||||
|
@Serializable(OffsetDateTimeSerializer::class)
|
||||||
|
val dateModified: OffsetDateTime,
|
||||||
|
val assets: GameAssets,
|
||||||
|
val status: CoreStatus,
|
||||||
|
val apiStatus: CoreApiStatus,
|
||||||
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
package top.jie65535.jcf.model.game
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GameAssets(
|
||||||
|
val iconUrl: String?,
|
||||||
|
val tileUrl: String?,
|
||||||
|
val coverUrl: String?,
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
package top.jie65535.jcf.model.game
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GameVersionType(
|
||||||
|
val id: Int? = null,
|
||||||
|
val gameId: Int? = null,
|
||||||
|
val name: String? = null,
|
||||||
|
val slug: String? = null,
|
||||||
|
)
|
@ -27,7 +27,7 @@ class Mod(
|
|||||||
/**
|
/**
|
||||||
* The mod slug that would appear in the URL
|
* The mod slug that would appear in the URL
|
||||||
*/
|
*/
|
||||||
val slug: String,
|
val slug: String?,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Relevant links for the mod such as Issue tracker and Wiki
|
* Relevant links for the mod such as Issue tracker and Wiki
|
||||||
@ -67,7 +67,7 @@ class Mod(
|
|||||||
/**
|
/**
|
||||||
* The class id this mod belongs to
|
* The class id this mod belongs to
|
||||||
*/
|
*/
|
||||||
val classId: Int?,
|
val classId: Int? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of the mod's authors
|
* List of the mod's authors
|
||||||
@ -120,7 +120,7 @@ class Mod(
|
|||||||
/**
|
/**
|
||||||
* Is mod allowed to be distributed
|
* Is mod allowed to be distributed
|
||||||
*/
|
*/
|
||||||
val allowModDistribution: Boolean?,
|
val allowModDistribution: Boolean? = null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The mod popularity rank for the game
|
* The mod popularity rank for the game
|
||||||
|
@ -6,6 +6,6 @@ class ModAsset(
|
|||||||
val modId: Int,
|
val modId: Int,
|
||||||
val title: String,
|
val title: String,
|
||||||
val description: String,
|
val description: String,
|
||||||
val thumbnailUrl: String,
|
val thumbnailUrl: String?,
|
||||||
val url: String,
|
val url: String?,
|
||||||
)
|
)
|
@ -4,5 +4,5 @@ package top.jie65535.jcf.model.mod
|
|||||||
class ModAuthor(
|
class ModAuthor(
|
||||||
val id: Int,
|
val id: Int,
|
||||||
val name: String,
|
val name: String,
|
||||||
val url: String,
|
val url: String?,
|
||||||
)
|
)
|
||||||
|
@ -2,8 +2,8 @@ package top.jie65535.jcf.model.mod
|
|||||||
|
|
||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
class ModLinks(
|
class ModLinks(
|
||||||
val websiteUrl: String,
|
val websiteUrl: String?,
|
||||||
val wikiUrl: String,
|
val wikiUrl: String?,
|
||||||
val issuesUrl: String,
|
val issuesUrl: String?,
|
||||||
val sourceUrl: String
|
val sourceUrl: String?
|
||||||
)
|
)
|
||||||
|
@ -12,5 +12,5 @@ enum class ModLoaderType {
|
|||||||
Fabric,
|
Fabric,
|
||||||
Quilt;
|
Quilt;
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<ModLoaderType> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<ModLoaderType> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
@ -16,5 +16,5 @@ enum class ModStatus {
|
|||||||
Deleted,
|
Deleted,
|
||||||
UnderReview;
|
UnderReview;
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<ModStatus> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<ModStatus> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,5 @@ package top.jie65535.jcf.model.request
|
|||||||
class GetFeaturedModsRequestBody(
|
class GetFeaturedModsRequestBody(
|
||||||
val gameId: Int,
|
val gameId: Int,
|
||||||
val excludedModIds: IntArray,
|
val excludedModIds: IntArray,
|
||||||
val gameVersionTypeId: Int?,
|
val gameVersionTypeId: Int? = null,
|
||||||
)
|
)
|
||||||
|
@ -14,5 +14,5 @@ enum class ModsSearchSortField {
|
|||||||
Category,
|
Category,
|
||||||
GameVersion;
|
GameVersion;
|
||||||
|
|
||||||
internal object IndexSerializer : KSerializer<ModsSearchSortField> by EnumIndexSerializer()
|
internal object IndexSerializer : KSerializer<ModsSearchSortField> by EnumIndexSerializer(values())
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package top.jie65535.jcf.model.response
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GameVersionsByType(
|
||||||
|
val type: Int,
|
||||||
|
val versions: Array<String>
|
||||||
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
package top.jie65535.jcf.model.response
|
||||||
|
|
||||||
|
import top.jie65535.jcf.model.game.Game
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GetGameResponse(
|
||||||
|
val data: Game
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package top.jie65535.jcf.model.response
|
||||||
|
|
||||||
|
import top.jie65535.jcf.model.game.Game
|
||||||
|
import top.jie65535.jcf.model.Pagination
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GetGamesResponse(
|
||||||
|
val data: Array<Game>,
|
||||||
|
val pagination: Pagination
|
||||||
|
)
|
@ -0,0 +1,8 @@
|
|||||||
|
package top.jie65535.jcf.model.response
|
||||||
|
|
||||||
|
import top.jie65535.jcf.model.game.GameVersionType
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GetVersionTypesResponse(
|
||||||
|
val data: Array<GameVersionType>
|
||||||
|
)
|
@ -0,0 +1,6 @@
|
|||||||
|
package top.jie65535.jcf.model.response
|
||||||
|
|
||||||
|
@kotlinx.serialization.Serializable
|
||||||
|
class GetVersionsResponse(
|
||||||
|
val data: Array<GameVersionsByType>
|
||||||
|
)
|
@ -8,7 +8,7 @@ import kotlinx.serialization.encoding.Decoder
|
|||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
|
||||||
@Suppress("FunctionName")
|
@Suppress("FunctionName")
|
||||||
inline fun <reified T : Enum<T>> EnumIndexSerializer(offset: Int = 1): KSerializer<T> {
|
inline fun <reified T : Enum<T>> EnumIndexSerializer(values: Array<T>, offset: Int = 1): KSerializer<T> {
|
||||||
return object : KSerializer<T> {
|
return object : KSerializer<T> {
|
||||||
override val descriptor: SerialDescriptor =
|
override val descriptor: SerialDescriptor =
|
||||||
PrimitiveSerialDescriptor(T::class.qualifiedName!!, PrimitiveKind.INT)
|
PrimitiveSerialDescriptor(T::class.qualifiedName!!, PrimitiveKind.INT)
|
||||||
@ -17,8 +17,6 @@ inline fun <reified T : Enum<T>> EnumIndexSerializer(offset: Int = 1): KSerializ
|
|||||||
encoder.encodeInt(value.ordinal + offset)
|
encoder.encodeInt(value.ordinal + offset)
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): T =
|
override fun deserialize(decoder: Decoder): T =
|
||||||
requireNotNull(enumValues<T>().getOrNull(decoder.decodeInt())) {
|
values[decoder.decodeInt() - offset]
|
||||||
"index: ${decoder.decodeInt()} not in ${enumValues<T>()}"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,9 +6,11 @@ import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
|
|||||||
import kotlinx.serialization.descriptors.SerialDescriptor
|
import kotlinx.serialization.descriptors.SerialDescriptor
|
||||||
import kotlinx.serialization.encoding.Decoder
|
import kotlinx.serialization.encoding.Decoder
|
||||||
import kotlinx.serialization.encoding.Encoder
|
import kotlinx.serialization.encoding.Encoder
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDateTime
|
||||||
import java.time.OffsetDateTime
|
import java.time.OffsetDateTime
|
||||||
|
import java.time.ZoneId
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
object OffsetDateTimeSerializer : KSerializer<OffsetDateTime> {
|
object OffsetDateTimeSerializer : KSerializer<OffsetDateTime> {
|
||||||
|
|
||||||
private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME
|
||||||
@ -16,10 +18,16 @@ object OffsetDateTimeSerializer : KSerializer<OffsetDateTime> {
|
|||||||
override val descriptor: SerialDescriptor =
|
override val descriptor: SerialDescriptor =
|
||||||
PrimitiveSerialDescriptor(OffsetDateTime::class.qualifiedName!!, PrimitiveKind.STRING)
|
PrimitiveSerialDescriptor(OffsetDateTime::class.qualifiedName!!, PrimitiveKind.STRING)
|
||||||
|
|
||||||
override fun deserialize(decoder: Decoder): OffsetDateTime =
|
override fun deserialize(decoder: Decoder): OffsetDateTime {
|
||||||
OffsetDateTime.parse(decoder.decodeString(), formatter)
|
return try {
|
||||||
|
OffsetDateTime.parse(decoder.decodeString(), formatter)
|
||||||
|
} catch(ignore: Throwable) {
|
||||||
|
OffsetDateTime.MIN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun serialize(encoder: Encoder, value: OffsetDateTime) =
|
override fun serialize(encoder: Encoder, value: OffsetDateTime) =
|
||||||
encoder.encodeString(formatter.format(value))
|
encoder.encodeString(formatter.format(value))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
143
src/test/kotlin/CurseforgeApiTest.kt
Normal file
143
src/test/kotlin/CurseforgeApiTest.kt
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package top.jie65535
|
||||||
|
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.junit.Test
|
||||||
|
import top.jie65535.jcf.CurseforgeApi
|
||||||
|
import top.jie65535.jcf.model.request.ModsSearchSortField
|
||||||
|
import top.jie65535.jcf.model.request.SortOrder
|
||||||
|
import java.io.File
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertNotNull
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class CurseforgeApiTest {
|
||||||
|
companion object {
|
||||||
|
private const val GAME_ID_MINECRAFT = 432
|
||||||
|
private const val MOD_ID_JEI = 238222
|
||||||
|
private const val FILE_ID_JEI = 3835406
|
||||||
|
private const val CLASS_ID_WORLDS = 17
|
||||||
|
private const val CLASS_ID_BUKKIT_PLUGINS = 5
|
||||||
|
private const val CLASS_ID_CUSTOMIZATION = 4546
|
||||||
|
private const val CLASS_ID_MODPACKS = 4471
|
||||||
|
private const val CLASS_ID_RESOURCE_PACKS = 12
|
||||||
|
private const val CLASS_ID_ADDONS = 4559
|
||||||
|
private const val CLASS_ID_MODS = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 api_key.txt 文件中读取
|
||||||
|
private val api = CurseforgeApi(File("api_key.txt").readText())
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getGames() = runTest {
|
||||||
|
val games = api.getGames()
|
||||||
|
val game = assertNotNull(games.data.find { it.name == "Minecraft" })
|
||||||
|
assert(game.id == GAME_ID_MINECRAFT)
|
||||||
|
printResult("getGames", games)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getGame() = runTest {
|
||||||
|
val game = api.getGame(GAME_ID_MINECRAFT)
|
||||||
|
assertEquals(game.name, "Minecraft")
|
||||||
|
printResult("getGame", game)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getVersions() = runTest {
|
||||||
|
val versions = api.getVersions(GAME_ID_MINECRAFT)
|
||||||
|
printResult("getVersions", versions)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getVersionTypes() = runTest {
|
||||||
|
val versionTypes = api.getVersionTypes(GAME_ID_MINECRAFT)
|
||||||
|
printResult("getVersionTypes", versionTypes)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getCategories() = runTest {
|
||||||
|
val categories = api.getCategories(GAME_ID_MINECRAFT)
|
||||||
|
val classes = categories.filter { it.isClass == true }
|
||||||
|
for (gameClass in classes) {
|
||||||
|
println("[${gameClass.id}] ${gameClass.name}")
|
||||||
|
for (category in categories.filter { it.classId == gameClass.id }) {
|
||||||
|
println(" | [${category.id}] ${category.name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printResult("getCategories", categories)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun searchMods() = runTest {
|
||||||
|
val response = api.searchMods(GAME_ID_MINECRAFT, searchFilter = "create", sortField = ModsSearchSortField.TotalDownloads, sortOrder = SortOrder.DESC, pageSize = 10)
|
||||||
|
for (mod in response.data) {
|
||||||
|
println("[${mod.id}] ${mod.name}\t ${mod.summary}\t DownloadCount: ${mod.downloadCount}")
|
||||||
|
}
|
||||||
|
printResult("searchMods", response)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getMod() = runTest {
|
||||||
|
val mod = api.getMod(MOD_ID_JEI)
|
||||||
|
assertEquals(mod.id, MOD_ID_JEI)
|
||||||
|
printResult("getMod", mod)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getMods() = runTest {
|
||||||
|
val mods = api.getMods(intArrayOf(MOD_ID_JEI))
|
||||||
|
assertEquals(mods.size, 1)
|
||||||
|
assertEquals(mods[0].id, MOD_ID_JEI)
|
||||||
|
printResult("getMods", mods)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getFeaturedMods() = runTest {
|
||||||
|
// Error: HTTP 404
|
||||||
|
val featuredMods = api.getFeaturedMods(GAME_ID_MINECRAFT)
|
||||||
|
printResult("getFeaturedMods", featuredMods)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getModDescription() = runTest {
|
||||||
|
val modDescription = api.getModDescription(MOD_ID_JEI)
|
||||||
|
printResult("getModDescription", modDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getModFile() = runTest {
|
||||||
|
val modFile = api.getModFile(MOD_ID_JEI, FILE_ID_JEI)
|
||||||
|
printResult("getModFile", modFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getModFiles() = runTest {
|
||||||
|
val modFiles = api.getModFiles(GAME_ID_MINECRAFT)
|
||||||
|
printResult("getModFiles", modFiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getFiles() = runTest {
|
||||||
|
val files = api.getFiles(intArrayOf(FILE_ID_JEI))
|
||||||
|
printResult("getFiles", files)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getModFileChangelog() = runTest {
|
||||||
|
val modFileChangelog = api.getModFileChangelog(MOD_ID_JEI, FILE_ID_JEI)
|
||||||
|
printResult("getModFileChangelog", modFileChangelog)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getModFileDownloadURL() = runTest {
|
||||||
|
val modFileDownloadUrl = api.getModFileDownloadURL(MOD_ID_JEI, FILE_ID_JEI)
|
||||||
|
printResult("getModFileDownloadURL", modFileDownloadUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T> printResult(name: String, obj: T) {
|
||||||
|
println("$name result: ${Json.encodeToString(obj)}")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user