diff --git a/.gitignore b/.gitignore index ab0999e..cee57ed 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,7 @@ tmp/ *.bak *.swp *~.nib -tokens.properties -local.properties +*.properties .settings/ .loadpath .recommenders diff --git a/README.md b/README.md index 754c145..1210c52 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,33 @@ GitHub code size in bytes

-ClashAPI is a very simple yet very complete Kotlin wrapper for the Clash of Clans mobile game API. It allows developers to easily do requests to the game API without bothering about JSON and HTTP handling. +ClashAPI is a very simple yet very complete Kotlin wrapper for the Clash of Clans mobile game API. +It allows developers to easily do requests to the game API without bothering about JSON and HTTP handling. +It is intended to be lightweight and as intuitive as possible to use. ## How does it work? -I analyzed JSON responses from the Clash of Clans API to recreate the models as Java structures so you don't have to deal with deserialization and data categorization each time. You can therefore simply access game data through classes and methods, all documented! +I analyzed JSON responses from the Clash of Clans API to recreate the models as Java structures so you don't have to deal with deserialization and data categorization each time. You can therefore simply access game data through your Java/Kotlin (JVM) classes and methods, all documented! + +## Setup +ClashAPI is available on Maven Central. You can add it to your project using Maven or Gradle. + +### Maven +Inside your `` scope of your `pom.xml` file, add the following: +```xml + + io.github.lycoon + clash-api + 5.0.0 + +``` + +### Gradle +Inside your `dependencies` scope of your `build.gradle` file, add the following: +```gradle +implementation 'io.github.lycoon:clash-api:5.0.0' +``` -## How to use it? +## Quick start ```java // 1. Create an instance of ClashAPI by providing your Clash of Clans API token to the constructor ClashAPI clashAPI = new ClashAPI("token"); @@ -31,9 +52,8 @@ In order to make calls to the Clash of Clans API, Supercell (developer of the ga Though this token is linked to the IP address you gave, I would advise **not to hardcode it** inside your code, for safety sake. Paste it in a separate file that you would access from your code. It will prevent your token being spread if you ever share your files. -## Dependencies -* Kotlin serialization `1.3.1` -* OkHttp `4.9.3` +## Report bugs +You've found a bug? Let me know by opening an issue. ## Disclaimer *This material is unofficial and is not endorsed by Supercell. For more information see Supercell's Fan Content Policy: www.supercell.com/fan-content-policy.* diff --git a/pom.xml b/pom.xml index 30f4ffe..055c6ef 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.github.lycoon clash-api - 4.0.0 + 5.0.0 ${project.groupId}:${project.artifactId} diff --git a/src/main/java/com/lycoon/clashapi/core/ClashAPI.kt b/src/main/java/com/lycoon/clashapi/core/ClashAPI.kt index 6569f09..51d2f25 100644 --- a/src/main/java/com/lycoon/clashapi/core/ClashAPI.kt +++ b/src/main/java/com/lycoon/clashapi/core/ClashAPI.kt @@ -2,412 +2,256 @@ package com.lycoon.clashapi.core import com.lycoon.clashapi.core.CoreUtils.deserialize import com.lycoon.clashapi.core.CoreUtils.formatTag -import com.lycoon.clashapi.core.CoreUtils.checkResponse -import com.lycoon.clashapi.core.exception.ClashAPIException +import com.lycoon.clashapi.core.CoreUtils.unwrapList +import com.lycoon.clashapi.core.interfaces.* +import com.lycoon.clashapi.models.capital.CapitalRaidSeason +import com.lycoon.clashapi.models.capital.CapitalRanking import com.lycoon.clashapi.models.clan.* import com.lycoon.clashapi.models.common.* +import com.lycoon.clashapi.models.league.BuilderBaseLeague +import com.lycoon.clashapi.models.league.CapitalLeague import com.lycoon.clashapi.models.league.League -import com.lycoon.clashapi.models.league.LeagueList import com.lycoon.clashapi.models.league.LeagueSeason -import com.lycoon.clashapi.models.league.LeagueSeasonList import com.lycoon.clashapi.models.player.* import com.lycoon.clashapi.models.war.War -import com.lycoon.clashapi.models.war.Warlog import com.lycoon.clashapi.models.war.WarlogEntry import com.lycoon.clashapi.models.warleague.WarLeague import com.lycoon.clashapi.models.warleague.WarLeagueGroup -import com.lycoon.clashapi.models.warleague.WarLeagueList -import okhttp3.* -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.RequestBody.Companion.toRequestBody -import java.io.IOException + +interface IClashAPI: IClanAPI, IPlayerAPI, ILeagueAPI, ILocationAPI, IGoldPassAPI, ILabelAPI /** * Create an instance of this class to start using the API.

- * * Are you lost? Check the [README](https://github.com/Lycoon/clash-api) to see what ClashAPI is all about. */ -class ClashAPI(private val token: String) { - private val http: OkHttpClient = OkHttpClient() - private fun getBaseRequest(suffix: String): Request.Builder { - return Request.Builder() - .header("authorization", "Bearer $token") - .url(CoreUtils.URL + CoreUtils.API_VERSION + suffix) - } - - @Throws(IOException::class, ClashAPIException::class) - private fun get(url: String): Response { - val res = http.newCall(getBaseRequest(url).build()).execute() - return checkResponse(res) - } - - @Throws(IOException::class, ClashAPIException::class) - private fun post(url: String, body: RequestBody): Response { - val res = http.newCall(getBaseRequest(url).post(body).build()).execute() - return checkResponse(res) - } - - private fun getTokenVerificationBody(token: String) : RequestBody { - val contentType: MediaType? = "application/json; charset=utf-8".toMediaTypeOrNull() - return "{\"token\":\"$token\"}".toRequestBody(contentType) - } - - /** - * Returns the warleague group in which the clan with the given tag is. - * - * @param clanTag `String` of the clan's tag - * @return WarLeagueGroup - * @see WarLeagueGroup - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getWarLeagueGroup(clanTag: String): WarLeagueGroup { +class ClashAPI(token: String) : ClashAPIClient(token), IClashAPI +{ + // ############################################## + // || Clans API || + // ############################################## + + override fun getWarLeagueGroup(clanTag: String): WarLeagueGroup + { val tag = formatTag(clanTag) val res = get("/clans/$tag/currentwar/leaguegroup") return deserialize(res) } - /** - * Returns an individual warleague war associated to the given war tag. - * You can obtain individual CWL war tags from: - * `ClashAPI.getCWLGroup(clanTag).getRounds(index).getWarTags(index)` - * - * @param warTag `String` of the war tag - * @return War - * @see War - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getWarLeagueWar(warTag: String): War { + override fun getWarLeagueWar(warTag: String): War + { val tag = formatTag(warTag) val res = get("/clanwarleagues/wars/$tag") return deserialize(res) } - /** - * Returns the warlog of the clan with the given tag. - * - * @param clanTag `String` of the clan's tag - * @return List - * @see WarlogEntry - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getWarlog(clanTag: String): List { + override fun getWarlog(clanTag: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { val tag = formatTag(clanTag) - val res = get("/clans/$tag/warlog") - return deserialize(res).items - } - - /** - * Returns the clan war occurring in the clan with the given tag. - * - * @param clanTag `String` of the clan's tag - * @return War - * @see War - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getCurrentWar(clanTag: String): War { + val res = get("/clans/$tag/warlog", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getClans(queryParamsBuilder: ClanQueryParamsBuilder?): List + { + val res = get("/clans", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getCurrentWar(clanTag: String): War + { val tag = formatTag(clanTag) val res = get("/clans/$tag/currentwar") return deserialize(res) } - /** - * Returns the clan attached to the tag. - * - * @param clanTag `String` of the clan's tag - * @return Clan - * @see Clan - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getClan(clanTag: String): Clan { + override fun getClan(clanTag: String): Clan + { val tag = formatTag(clanTag) val res = get("/clans/$tag") return deserialize(res) } - /** - * Returns the members of clan attached to the tag. - * - * @param clanTag `String` of the clan's tag - * @return List - * @see ClanMember - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getClanMembers(clanTag: String): List { + override fun getClanMembers(clanTag: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val tag = formatTag(clanTag) + val res = get("/clans/$tag/members", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getCapitalRaidSeasons(clanTag: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { val tag = formatTag(clanTag) - val res = get("/clans/$tag/members") - return deserialize(res).items - } - - /** - * Returns the player attached to the tag. - * - * @param playerTag `String` of the player's tag - * @return Player - * @see Player - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getPlayer(playerTag: String): Player { + val res = get("/clans/$tag/capitalraidseasons", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + // ############################################## + // || Player API || + // ############################################## + + override fun getPlayer(playerTag: String): Player + { val tag = formatTag(playerTag) val res = get("/players/$tag") return deserialize(res) } - /** - * Returns whether the given player tag is verified or not. - * - * @param playerTag `String` of the player's tag - * @param token `String` of the player token - * @return a boolean - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun isVerifiedPlayer(playerTag: String, token: String): Boolean { + override fun isVerifiedPlayer(playerTag: String, token: String): Boolean + { val tag = formatTag(playerTag) val res = post("/players/$tag/verifytoken", getTokenVerificationBody(token)) - return deserialize(res).status == "ok" } - /** - * Returns all leagues from the game. - * - * @return List - * @see League - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getLeagues(): List { - val res = get("/leagues") - return deserialize(res).items - } - - /** - * Returns league season rankings - * - * @param leagueId `String` of the league id - * @param seasonId `String` of the season id - * @return List - * @see PlayerRanking - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getLeagueSeasonRankings(leagueId: String, seasonId: String): List { - val res = get("/leagues/$leagueId/seasons/$seasonId") - return deserialize(res).items - } - - /** - * Returns league information - * - * @param leagueId `String` of the league id - * @return League - * @see League - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getLeague(leagueId: String): League { + // ############################################## + // || League API || + // ############################################## + + override fun getCapitalLeagues(queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/capitalleagues", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getLeagues(queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/leagues", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getLeagueSeasonRankings( + leagueId: String, seasonId: String, + queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/leagues/$leagueId/seasons/$seasonId", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getCapitalLeague(leagueId: String): CapitalLeague + { + val res = get("/capitalleagues/$leagueId") + return deserialize(res) + } + + override fun getBuilderBaseLeague(leagueId: String): BuilderBaseLeague + { + val res = get("/builderbaseleagues/$leagueId") + return deserialize(res) + } + + override fun getBuilderBaseLeagues(queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/builderbaseleagues", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getLeague(leagueId: String): League + { val res = get("/leagues/$leagueId") return deserialize(res) } - /** - * Returns league seasons - * - * @param leagueId `String` of the league id - * @return List - * @see LeagueSeason - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getLeagueSeasons(leagueId: String): List { - val res = get("/leagues/$leagueId/seasons") - return deserialize(res).items - } - - /** - * Returns warleague information - * - * @param leagueId `String` of the league id - * @return WarLeague - * @see WarLeague - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getWarLeague(leagueId: String): WarLeague { + override fun getLeagueSeasons(leagueId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/leagues/$leagueId/seasons", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getWarLeague(leagueId: String): WarLeague + { val res = get("/warleagues/$leagueId") return deserialize(res) } - /** - * Returns all warleagues - * - * @return List - * @see WarLeague - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getWarLeagues(): List { - val res = get("/warleagues") - return deserialize(res).items - } - - /** - * Returns clan rankings for a specific location - * - * @param locationId `String` of the location id - * @return List - * @see ClanRanking - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getClanRankings(locationId: String): List { - val res = get("/locations/${locationId}/rankings/clans") - return deserialize(res).items - } - - /** - * Returns clan versus rankings for a specific location - * - * @param locationId `String` of the location id - * @return List - * @see ClanVersusRanking - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getClanVersusRankings(locationId: String): List { - val res = get("/locations/${locationId}/rankings/clans-versus") - return deserialize(res).items - } - - /** - * Returns player rankings for a specific location - * - * @param locationId `String` of the location id - * @return List - * @see PlayerRanking - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getPlayerRankings(locationId: String): List { - val res = get("/locations/${locationId}/rankings/players") - return deserialize(res).items - } - - /** - * Returns player versus rankings for a specific location - * - * @param locationId `String` of the location id - * @return List - * @see PlayerVersusRanking - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getPlayerVersusRankings(locationId: String): List { - val res = get("/locations/${locationId}/rankings/players-versus") - return deserialize(res).items - } - - /** - * Returns locations - * - * @return List - * @see Location - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getLocations(): List { - val res = get("/locations") - return deserialize(res).items - } - - /** - * Returns specific location - * - * @param locationId `String` of the location id - * @return Location - * @see Location - * - * @throws IOException if the deserialization failed - * @throws ClashAPIException if the request to the game API failed - */ - @Throws(IOException::class, ClashAPIException::class) - fun getLocation(locationId: String): Location { + override fun getWarLeagues(queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/warleagues", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + // ############################################## + // || Location API || + // ############################################## + + override fun getClanRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/${locationId}/rankings/clans", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getPlayerRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/${locationId}/rankings/players", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getClanBuilderBaseRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/${locationId}/rankings/clans-builder-base", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + @Deprecated("Use getClanBuilderBaseRankings instead") + override fun getClanVersusRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/${locationId}/rankings/clans-versus", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getPlayerBuilderBaseRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/${locationId}/rankings/players-builder-base", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + @Deprecated("Use getPlayerBuilderBaseRankings instead") + override fun getPlayerVersusRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/${locationId}/rankings/players-versus", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getLocations(queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getCapitalRankings(locationId: String, queryParamsBuilder: SimpleQueryParamsBuilder?): List + { + val res = get("/locations/$locationId/rankings/capitals", queryParamsBuilder) + return unwrapList(deserialize(res)) + } + + override fun getLocation(locationId: String): Location + { val res = get("/locations/$locationId") return deserialize(res) } - /** - * Returns player labels - * - * @return List