From 7fd31c194b3631bab8f6193d0d53ff73920c245c Mon Sep 17 00:00:00 2001 From: jie65535 Date: Mon, 13 Jun 2022 22:23:00 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AE=98=E6=96=B9API?= =?UTF-8?q?=E5=B0=81=E8=A3=85=EF=BC=8C=E5=BD=93=E5=89=8D=E4=BB=85=E5=AE=8C?= =?UTF-8?q?=E6=88=90=E4=BA=86=E6=90=9C=E7=B4=A2Mod=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/top/jie65535/jcf/CurseforgeApi.kt | 137 ++++++++++++++++++ .../kotlin/top/jie65535/jcf/model/Category.kt | 21 +++ .../top/jie65535/jcf/model/Pagination.kt | 9 ++ .../jie65535/jcf/model/SortableGameVersion.kt | 30 ++++ .../top/jie65535/jcf/model/file/File.kt | 103 +++++++++++++ .../jie65535/jcf/model/file/FileDependency.kt | 7 + .../top/jie65535/jcf/model/file/FileHash.kt | 7 + .../top/jie65535/jcf/model/file/FileIndex.kt | 13 ++ .../top/jie65535/jcf/model/file/FileModule.kt | 7 + .../jcf/model/file/FileRelationType.kt | 16 ++ .../jcf/model/file/FileReleaseType.kt | 13 ++ .../top/jie65535/jcf/model/file/FileStatus.kt | 25 ++++ .../top/jie65535/jcf/model/file/HashAlgo.kt | 12 ++ .../kotlin/top/jie65535/jcf/model/mod/Mod.kt | 116 +++++++++++++++ .../top/jie65535/jcf/model/mod/ModAsset.kt | 11 ++ .../top/jie65535/jcf/model/mod/ModAuthor.kt | 8 + .../top/jie65535/jcf/model/mod/ModLinks.kt | 9 ++ .../jie65535/jcf/model/mod/ModLoaderType.kt | 16 ++ .../top/jie65535/jcf/model/mod/ModStatus.kt | 20 +++ .../request/GetFeaturedModsRequestBody.kt | 8 + .../request/GetModsByIdsListRequestBody.kt | 6 + .../jcf/model/request/ModsSearchSortField.kt | 18 +++ .../jie65535/jcf/model/request/SortOrder.kt | 7 + .../model/response/FeaturedModsResponse.kt | 10 ++ .../model/response/GetFeaturedModsResponse.kt | 6 + .../jcf/model/response/GetModResponse.kt | 8 + .../jcf/model/response/GetModsResponse.kt | 8 + .../jcf/model/response/SearchModsResponse.kt | 10 ++ .../jie65535/jcf/util/EnumIndexSerializer.kt | 24 +++ .../jcf/util/OffsetDateTimeSerializer.kt | 25 ++++ 30 files changed, 710 insertions(+) create mode 100644 src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/Category.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/Pagination.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/SortableGameVersion.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/File.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileDependency.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileHash.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileIndex.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileModule.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileRelationType.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileReleaseType.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/FileStatus.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/file/HashAlgo.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/mod/Mod.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/mod/ModAsset.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/mod/ModAuthor.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/mod/ModLinks.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/mod/ModLoaderType.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/mod/ModStatus.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/request/GetFeaturedModsRequestBody.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/request/GetModsByIdsListRequestBody.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/request/ModsSearchSortField.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/request/SortOrder.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/response/FeaturedModsResponse.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/response/GetFeaturedModsResponse.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/response/GetModResponse.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/response/GetModsResponse.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/model/response/SearchModsResponse.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/util/EnumIndexSerializer.kt create mode 100644 src/main/kotlin/top/jie65535/jcf/util/OffsetDateTimeSerializer.kt diff --git a/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt b/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt new file mode 100644 index 0000000..4845255 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/CurseforgeApi.kt @@ -0,0 +1,137 @@ +package top.jie65535.jcf + +import io.ktor.client.* +import io.ktor.client.engine.okhttp.* +import io.ktor.client.features.* +import io.ktor.client.request.* +import io.ktor.http.* +import kotlinx.serialization.* +import kotlinx.serialization.json.Json +import top.jie65535.jcf.model.mod.* +import top.jie65535.jcf.model.request.* +import top.jie65535.jcf.model.request.SortOrder.* +import top.jie65535.jcf.model.response.* + +@OptIn(ExperimentalSerializationApi::class) +class CurseforgeApi(apiKey: String) { + companion object { + private const val GAME_ID_MINECRAFT = 432 + } + + private val json = Json { + isLenient = true + ignoreUnknownKeys = true + serializersModule + } + private val http = HttpClient(OkHttp) { + install(HttpTimeout) { + this.requestTimeoutMillis = 30_0000 + this.connectTimeoutMillis = 30_0000 + this.socketTimeoutMillis = 30_0000 + } + defaultRequest { + url.protocol = URLProtocol.HTTPS + url.host = "api.curseforge.com" + header("accept", "application/json") + header("x-api-key", apiKey) + } + } + + //region Mods + + /** + * Get all mods that match the search criteria. + * @param gameId Filter by game id. + * @param classId Filter by section id (discoverable via Categories) + * @param categoryId Filter by category id + * @param gameVersion Filter by game version string + * @param searchFilter Filter by free text search in the mod name and author + * @param sortField Filter by ModsSearchSortField enumeration + * @param sortOrder 'asc' if sort is in ascending order, 'desc' if sort is in descending order + * @param modLoaderType Filter only mods associated to a given modloader (Forge, Fabric ...). Must be coupled with gameVersion. + * @param gameVersionTypeId Filter only mods that contain files tagged with versions of the given gameVersionTypeId + * @param slug Filter by slug (coupled with classId will result in a unique result). + * @param index A zero based index of the first item to include in the response, + * @param pageSize The number of items to include in the response, + */ + suspend fun searchMods( + gameId: Int, + classId: Int?, + categoryId: Int?, + gameVersion: String?, + searchFilter: String?, + sortField: ModsSearchSortField?, + sortOrder: SortOrder?, + modLoaderType: ModLoaderType?, + gameVersionTypeId: Int?, + slug: String?, + index: Int?, + pageSize: Int? + ): SearchModsResponse { + return json.decodeFromString( + http.get("/v1/mods/search") { + parameter("gameId", gameId) + parameter("classId", classId) + parameter("categoryId", categoryId) + parameter("gameVersion", gameVersion) + parameter("searchFilter", searchFilter) + parameter("sortField", sortField) + parameter("sortOrder", when(sortOrder){ + ASC -> "asc" + DESC -> "asc" + null -> null + }) + parameter("modLoaderType", modLoaderType) + parameter("gameVersionTypeId", gameVersionTypeId) + parameter("slug", slug) + parameter("index", index) + parameter("pageSize", pageSize) + } + ) + } + + /** + * Get a single mod. + */ + suspend fun getMod(modId: Int): GetModResponse { + return json.decodeFromString( + http.get("/v1/mods/$modId") + ) + } + + /** + * Get a list of mods. + */ + suspend fun getMods(modIds: IntArray): GetModsResponse { + return json.decodeFromString( + http.get("/v1/mods") { + body = json.encodeToString(GetModsByIdsListRequestBody(modIds)) + } + ) + } + + /** + * Get a list of featured, popular and recently updated mods. + */ + suspend fun getFeaturedMods( + gameId: Int, + excludedModIds: IntArray, + gameVersionTypeId: Int? + ): GetFeaturedModsResponse { + return json.decodeFromString( + http.get("/v1/mods/featured") { + body = json.encodeToString(GetFeaturedModsRequestBody(gameId, excludedModIds, gameVersionTypeId)) + } + ) + } + + /** + * Get the full description of a mod in HTML format. + */ + suspend fun getModDescription(modId: Int): String { + return http.get("/v1/mods/$modId/description") + } + + //endregion + +} \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/Category.kt b/src/main/kotlin/top/jie65535/jcf/model/Category.kt new file mode 100644 index 0000000..1d40c52 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/Category.kt @@ -0,0 +1,21 @@ +package top.jie65535.jcf.model + +import kotlinx.serialization.Serializable +import top.jie65535.jcf.util.OffsetDateTimeSerializer +import java.time.OffsetDateTime + +@Serializable +class Category( + val id: Int, + val gameId: Int, + val name: String, + val slug: String, + val url: String, + val iconUrl: String, + @Serializable(OffsetDateTimeSerializer::class) + val dateModified: OffsetDateTime, + val isClass: Boolean?, + val classId: Int?, + val parentCategoryId: Int?, + val displayIndex: Int? +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/Pagination.kt b/src/main/kotlin/top/jie65535/jcf/model/Pagination.kt new file mode 100644 index 0000000..c21ecd2 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/Pagination.kt @@ -0,0 +1,9 @@ +package top.jie65535.jcf.model + +@kotlinx.serialization.Serializable +class Pagination( + val index: Int, + val pageSize: Int, + val resultCount: Int, + val totalCount: Long, +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/SortableGameVersion.kt b/src/main/kotlin/top/jie65535/jcf/model/SortableGameVersion.kt new file mode 100644 index 0000000..0e63a0b --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/SortableGameVersion.kt @@ -0,0 +1,30 @@ +package top.jie65535.jcf.model + +import kotlinx.serialization.Serializable +import top.jie65535.jcf.util.OffsetDateTimeSerializer +import java.time.OffsetDateTime + +@Serializable +class SortableGameVersion( + /** + * Original version name (e.g. 1.5b) + */ + val gameVersionName: String, + /** + * Used for sorting (e.g. 0000000001.0000000005) + */ + val gameVersionPadded: String, + /** + * game version clean name (e.g. 1.5) + */ + val gameVersion: String, + /** + * Game version release date + */ + @Serializable(OffsetDateTimeSerializer::class) + val gameVersionReleaseDate: OffsetDateTime, + /** + * Game version type id + */ + val gameVersionTypeId: Int? +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/File.kt b/src/main/kotlin/top/jie65535/jcf/model/file/File.kt new file mode 100644 index 0000000..f325d15 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/File.kt @@ -0,0 +1,103 @@ +package top.jie65535.jcf.model.file + +import kotlinx.serialization.Serializable +import top.jie65535.jcf.model.SortableGameVersion +import top.jie65535.jcf.util.OffsetDateTimeSerializer +import java.time.OffsetDateTime + +@Serializable +class File( + /** + * The file id + */ + val id: Int, + /** + * The game id related to the mod that this file belongs to + */ + val gameId: Int, + /** + * The mod id + */ + val modId: Int, + /** + * Whether the file is available to download + */ + val isAvailable: Boolean, + /** + * Display name of the file + */ + val displayName: String, + /** + * Exact file name + */ + val fileName: String, + /** + * The file release type + */ + val releaseType: FileReleaseType, + /** + * Status of the file + */ + val fileStatus: FileStatus, + /** + * The file hash (i.e. md5 or sha1) + */ + val hashes: Array, + /** + * The file timestamp + */ + @Serializable(OffsetDateTimeSerializer::class) + val fileDate: OffsetDateTime, + /** + * The file length in bytes + */ + val fileLength: Long, + /** + * The number of downloads for the file + */ + val downloadCount: Long, + /** + * The file download URL + */ + val downloadUrl: String, + /** + * List of game versions this file is relevant for + */ + val gameVersions: Array, + /** + * Metadata used for sorting by game versions + */ + val sortableGameVersions: Array, + /** + * List of dependencies files + */ + val dependencies: Array, + /** + * none + */ + val exposeAsAlternative: Boolean?, + /** + * none + */ + val parentProjectFileId: Int?, + /** + * none + */ + val alternateFileId: Int?, + /** + * none + */ + val isServerPack: Boolean?, + /** + * none + */ + val serverPackFileId: Int?, + /** + * none + */ + val fileFingerprint: Long, + /** + * none + */ + val modules: Array, +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileDependency.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileDependency.kt new file mode 100644 index 0000000..d96b505 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileDependency.kt @@ -0,0 +1,7 @@ +package top.jie65535.jcf.model.file + +@kotlinx.serialization.Serializable +class FileDependency( + val modId: Int, + val relationType: FileRelationType, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileHash.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileHash.kt new file mode 100644 index 0000000..ce8ec45 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileHash.kt @@ -0,0 +1,7 @@ +package top.jie65535.jcf.model.file + +@kotlinx.serialization.Serializable +class FileHash( + val value: String, + val algo: HashAlgo, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileIndex.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileIndex.kt new file mode 100644 index 0000000..7a51815 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileIndex.kt @@ -0,0 +1,13 @@ +package top.jie65535.jcf.model.file + +import top.jie65535.jcf.model.mod.ModLoaderType + +@kotlinx.serialization.Serializable +class FileIndex( + val gameVersion: String, + val fileId: Int, + val filename: String, + val releaseType: FileReleaseType, + val gameVersionTypeId: Int?, + val modLoader: ModLoaderType, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileModule.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileModule.kt new file mode 100644 index 0000000..457389c --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileModule.kt @@ -0,0 +1,7 @@ +package top.jie65535.jcf.model.file + +@kotlinx.serialization.Serializable +class FileModule( + val name: String, + val fingerprint: Long, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileRelationType.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileRelationType.kt new file mode 100644 index 0000000..c28a5f8 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileRelationType.kt @@ -0,0 +1,16 @@ +package top.jie65535.jcf.model.file + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = FileRelationType.IndexSerializer::class) +enum class FileRelationType { + EmbeddedLibrary, + OptionalDependency, + RequiredDependency, + Tool, + Incompatible, + Include; + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileReleaseType.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileReleaseType.kt new file mode 100644 index 0000000..acbbd1e --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileReleaseType.kt @@ -0,0 +1,13 @@ +package top.jie65535.jcf.model.file + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = FileReleaseType.IndexSerializer::class) +enum class FileReleaseType { + Release, + Beta, + Alpha; + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/FileStatus.kt b/src/main/kotlin/top/jie65535/jcf/model/file/FileStatus.kt new file mode 100644 index 0000000..a351924 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/FileStatus.kt @@ -0,0 +1,25 @@ +package top.jie65535.jcf.model.file + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = FileStatus.IndexSerializer::class) +enum class FileStatus{ + Processing, + ChangesRequired, + UnderReview, + Approved, + Rejected, + MalwareDetected, + Deleted, + Archived, + Testing, + Released, + ReadyForReview, + Deprecated, + Baking, + AwaitingPublishing, + FailedPublishing; + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} diff --git a/src/main/kotlin/top/jie65535/jcf/model/file/HashAlgo.kt b/src/main/kotlin/top/jie65535/jcf/model/file/HashAlgo.kt new file mode 100644 index 0000000..3a92961 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/file/HashAlgo.kt @@ -0,0 +1,12 @@ +package top.jie65535.jcf.model.file + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = HashAlgo.IndexSerializer::class) +enum class HashAlgo(val value: Int) { + Sha1(1), + Md5(2); + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} diff --git a/src/main/kotlin/top/jie65535/jcf/model/mod/Mod.kt b/src/main/kotlin/top/jie65535/jcf/model/mod/Mod.kt new file mode 100644 index 0000000..48e7db4 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/mod/Mod.kt @@ -0,0 +1,116 @@ +package top.jie65535.jcf.model.mod + +import kotlinx.serialization.Serializable +import top.jie65535.jcf.model.Category +import top.jie65535.jcf.model.file.File +import top.jie65535.jcf.model.file.FileIndex +import top.jie65535.jcf.util.OffsetDateTimeSerializer +import java.time.OffsetDateTime + +@kotlinx.serialization.Serializable +class Mod( + /** + * The mod id + */ + val id: Int, + /** + * The game id this mod is for + */ + val gameId: Int, + /** + * The name of the mod + */ + val name: String, + /** + * The mod slug that would appear in the URL + */ + val slug: String, + /** + * Relevant links for the mod such as Issue tracker and Wiki + */ + val links: ModLinks, + /** + * Mod summary + */ + val summary: String, + /** + * Current mod status + */ + val status: ModStatus, + /** + * Number of downloads for the mod + */ + val downloadCount: Long, + /** + * Whether the mod is included in the featured mods list + */ + val isFeatured: Boolean, + /** + * The main category of the mod as it was chosen by the mod author + */ + val primaryCategoryId: Int, + /** + * List of categories that this mod is related to + */ + val categories: Array, + /** + * The class id this mod belongs to + */ + val classId: Int?, + /** + * List of the mod's authors + */ + val authors: Array, + /** + * The mod's logo asset + */ + val logo: ModAsset, + /** + * List of screenshots assets + */ + val screenshots: Array, + /** + * The id of the main file of the mod + */ + val mainFileId: Int, + /** + * List of latest files of the mod + */ + val latestFiles: Array, + /** + * List of file related details for the latest files of the mod + */ + val latestFilesIndexes: Array, + /** + * The creation date of the mod + */ + @Serializable(OffsetDateTimeSerializer::class) + val dateCreated: OffsetDateTime, + /** + * The last time the mod was modified + */ + @Serializable(OffsetDateTimeSerializer::class) + val dateModified: OffsetDateTime, + /** + * The release date of the mod + */ + @Serializable(OffsetDateTimeSerializer::class) + val dateReleased: OffsetDateTime, + /** + * Is mod allowed to be distributed + */ + val allowModDistribution: Boolean?, + /** + * The mod popularity rank for the game + */ + val gamePopularityRank: Int, + /** + * Is the mod available for search. This can be false when a mod is experimental, + * in a deleted state or has only alpha files + */ + val isAvailable: Boolean, + /** + * The mod's thumbs up count + */ + val thumbsUpCount: Int, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/mod/ModAsset.kt b/src/main/kotlin/top/jie65535/jcf/model/mod/ModAsset.kt new file mode 100644 index 0000000..2b12abd --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/mod/ModAsset.kt @@ -0,0 +1,11 @@ +package top.jie65535.jcf.model.mod + +@kotlinx.serialization.Serializable +class ModAsset( + val id: Int, + val modId: Int, + val title: String, + val description: String, + val thumbnailUrl: String, + val url: String, +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/mod/ModAuthor.kt b/src/main/kotlin/top/jie65535/jcf/model/mod/ModAuthor.kt new file mode 100644 index 0000000..ebfeafc --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/mod/ModAuthor.kt @@ -0,0 +1,8 @@ +package top.jie65535.jcf.model.mod + +@kotlinx.serialization.Serializable +class ModAuthor( + val id: Int, + val name: String, + val url: String, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/mod/ModLinks.kt b/src/main/kotlin/top/jie65535/jcf/model/mod/ModLinks.kt new file mode 100644 index 0000000..00506c5 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/mod/ModLinks.kt @@ -0,0 +1,9 @@ +package top.jie65535.jcf.model.mod + +@kotlinx.serialization.Serializable +class ModLinks( + val websiteUrl: String, + val wikiUrl: String, + val issuesUrl: String, + val sourceUrl: String +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/mod/ModLoaderType.kt b/src/main/kotlin/top/jie65535/jcf/model/mod/ModLoaderType.kt new file mode 100644 index 0000000..8cc9cc7 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/mod/ModLoaderType.kt @@ -0,0 +1,16 @@ +package top.jie65535.jcf.model.mod + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = ModLoaderType.IndexSerializer::class) +enum class ModLoaderType { + Any, + Forge, + Cauldron, + LiteLoader, + Fabric, + Quilt; + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/mod/ModStatus.kt b/src/main/kotlin/top/jie65535/jcf/model/mod/ModStatus.kt new file mode 100644 index 0000000..bf793e7 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/mod/ModStatus.kt @@ -0,0 +1,20 @@ +package top.jie65535.jcf.model.mod + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = ModStatus.IndexSerializer::class) +enum class ModStatus { + New, + ChangesRequired, + UnderSoftReview, + Approved, + Rejected, + ChangesMade, + Inactive, + Abandoned, + Deleted, + UnderReview; + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} diff --git a/src/main/kotlin/top/jie65535/jcf/model/request/GetFeaturedModsRequestBody.kt b/src/main/kotlin/top/jie65535/jcf/model/request/GetFeaturedModsRequestBody.kt new file mode 100644 index 0000000..3bd2be0 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/request/GetFeaturedModsRequestBody.kt @@ -0,0 +1,8 @@ +package top.jie65535.jcf.model.request + +@kotlinx.serialization.Serializable +class GetFeaturedModsRequestBody( + val gameId: Int, + val excludedModIds: IntArray, + val gameVersionTypeId: Int?, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/request/GetModsByIdsListRequestBody.kt b/src/main/kotlin/top/jie65535/jcf/model/request/GetModsByIdsListRequestBody.kt new file mode 100644 index 0000000..5f2b6e6 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/request/GetModsByIdsListRequestBody.kt @@ -0,0 +1,6 @@ +package top.jie65535.jcf.model.request + +@kotlinx.serialization.Serializable +class GetModsByIdsListRequestBody( + val modIds: IntArray +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/request/ModsSearchSortField.kt b/src/main/kotlin/top/jie65535/jcf/model/request/ModsSearchSortField.kt new file mode 100644 index 0000000..d8021f3 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/request/ModsSearchSortField.kt @@ -0,0 +1,18 @@ +package top.jie65535.jcf.model.request + +import kotlinx.serialization.KSerializer +import top.jie65535.jcf.util.EnumIndexSerializer + +@kotlinx.serialization.Serializable(with = ModsSearchSortField.IndexSerializer::class) +enum class ModsSearchSortField { + Featured, + Popularity, + LastUpdated, + Name, + Author, + TotalDownloads, + Category, + GameVersion; + + internal object IndexSerializer : KSerializer by EnumIndexSerializer() +} \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/request/SortOrder.kt b/src/main/kotlin/top/jie65535/jcf/model/request/SortOrder.kt new file mode 100644 index 0000000..993c9be --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/request/SortOrder.kt @@ -0,0 +1,7 @@ +package top.jie65535.jcf.model.request + +@kotlinx.serialization.Serializable +enum class SortOrder { + ASC, + DESC, +} \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/response/FeaturedModsResponse.kt b/src/main/kotlin/top/jie65535/jcf/model/response/FeaturedModsResponse.kt new file mode 100644 index 0000000..3ab01e5 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/response/FeaturedModsResponse.kt @@ -0,0 +1,10 @@ +package top.jie65535.jcf.model.response + +import top.jie65535.jcf.model.mod.Mod + +@kotlinx.serialization.Serializable +class FeaturedModsResponse( + val featured: Array, + val popular: Array, + val recentlyUpdated: Array, +) diff --git a/src/main/kotlin/top/jie65535/jcf/model/response/GetFeaturedModsResponse.kt b/src/main/kotlin/top/jie65535/jcf/model/response/GetFeaturedModsResponse.kt new file mode 100644 index 0000000..ffd877b --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/response/GetFeaturedModsResponse.kt @@ -0,0 +1,6 @@ +package top.jie65535.jcf.model.response + +@kotlinx.serialization.Serializable +class GetFeaturedModsResponse( + val data: FeaturedModsResponse +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/response/GetModResponse.kt b/src/main/kotlin/top/jie65535/jcf/model/response/GetModResponse.kt new file mode 100644 index 0000000..8dbbb84 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/response/GetModResponse.kt @@ -0,0 +1,8 @@ +package top.jie65535.jcf.model.response + +import top.jie65535.jcf.model.mod.Mod + +@kotlinx.serialization.Serializable +class GetModResponse( + val data: Mod +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/response/GetModsResponse.kt b/src/main/kotlin/top/jie65535/jcf/model/response/GetModsResponse.kt new file mode 100644 index 0000000..4780a38 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/response/GetModsResponse.kt @@ -0,0 +1,8 @@ +package top.jie65535.jcf.model.response + +import top.jie65535.jcf.model.mod.Mod + +@kotlinx.serialization.Serializable +class GetModsResponse( + val data: Array +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/model/response/SearchModsResponse.kt b/src/main/kotlin/top/jie65535/jcf/model/response/SearchModsResponse.kt new file mode 100644 index 0000000..a12a702 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/model/response/SearchModsResponse.kt @@ -0,0 +1,10 @@ +package top.jie65535.jcf.model.response + +import top.jie65535.jcf.model.Pagination +import top.jie65535.jcf.model.mod.Mod + +@kotlinx.serialization.Serializable +class SearchModsResponse( + val data: Mod, + val pagination: Pagination, +) \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/util/EnumIndexSerializer.kt b/src/main/kotlin/top/jie65535/jcf/util/EnumIndexSerializer.kt new file mode 100644 index 0000000..1a9d77c --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/util/EnumIndexSerializer.kt @@ -0,0 +1,24 @@ +package top.jie65535.jcf.util + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder + +@Suppress("FunctionName") +inline fun > EnumIndexSerializer(offset: Int = 1): KSerializer { + return object : KSerializer { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor(T::class.qualifiedName!!, PrimitiveKind.INT) + + override fun serialize(encoder: Encoder, value: T) = + encoder.encodeInt(value.ordinal + offset) + + override fun deserialize(decoder: Decoder): T = + requireNotNull(enumValues().getOrNull(decoder.decodeInt())) { + "index: ${decoder.decodeInt()} not in ${enumValues()}" + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/top/jie65535/jcf/util/OffsetDateTimeSerializer.kt b/src/main/kotlin/top/jie65535/jcf/util/OffsetDateTimeSerializer.kt new file mode 100644 index 0000000..be4f286 --- /dev/null +++ b/src/main/kotlin/top/jie65535/jcf/util/OffsetDateTimeSerializer.kt @@ -0,0 +1,25 @@ +package top.jie65535.jcf.util + +import kotlinx.serialization.KSerializer +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import java.time.OffsetDateTime +import java.time.format.DateTimeFormatter + +object OffsetDateTimeSerializer : KSerializer { + + private val formatter: DateTimeFormatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME + + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor(OffsetDateTime::class.qualifiedName!!, PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): OffsetDateTime = + OffsetDateTime.parse(decoder.decodeString(), formatter) + + override fun serialize(encoder: Encoder, value: OffsetDateTime) = + encoder.encodeString(formatter.format(value)) + +} \ No newline at end of file