diff --git a/.idea/misc.xml b/.idea/misc.xml index 38167d7..2266f6b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,9 @@ + + + diff --git a/gradle.properties b/gradle.properties index 736f0f8..7c1de97 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official org.gradle.caching=true -version = 0.0.2-SNAPSHOT \ No newline at end of file +version = 0.0.5-SNAPSHOT \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/api/GroupApi.kt b/src/main/kotlin/dev/roava/api/GroupApi.kt index 46464a3..7d2a1f2 100644 --- a/src/main/kotlin/dev/roava/api/GroupApi.kt +++ b/src/main/kotlin/dev/roava/api/GroupApi.kt @@ -24,10 +24,9 @@ package dev.roava.api -import dev.roava.json.group.GroupData -import dev.roava.json.group.RoleListData -import dev.roava.json.group.RoleRequest +import dev.roava.json.group.* import dev.roava.json.user.UserRolesData +import dev.roava.util.Pagination import retrofit2.Call import retrofit2.http.* @@ -52,4 +51,10 @@ interface GroupApi { @DELETE("/v1/groups/{groupId}/users/{userId}") fun exileUser(@Path("groupId") groupId: Int, @Path("userId") userId: Long): Call + + @GET("v1/groups/{groupId}/roles/{roleSetId}/users") + fun getGroupRankMembers(@Path("groupId") groupId: Int, @Path("roleSetId") roleSetId: Int, @Query("limit") limit: Int, @Query("cursor") cursor: String? = null): Call + + @GET("v1/groups/{groupId}/users") + fun getGroupMembers(@Path("groupId") groupId: Int, @Query("limit") limit: Int, @Query("cursor") cursor: String? = null): Call> } \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/api/ThumbnailApi.kt b/src/main/kotlin/dev/roava/api/ThumbnailApi.kt new file mode 100644 index 0000000..08334c8 --- /dev/null +++ b/src/main/kotlin/dev/roava/api/ThumbnailApi.kt @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.api + +import dev.roava.json.user.ThumbnailListData +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +interface ThumbnailApi { + @GET("/v1/users/avatar") + fun getAvatar( + @Query("userIds") userId: Long, + @Query("size") size: String, + @Query("format") format: String, + @Query("isCircular") circular: Boolean + ): Call + @GET("/v1/users/avatar-headshot") + fun getHeadShot( + @Query("userIds") userId: Long, + @Query("size") size: String, + @Query("format") format: String, + @Query("isCircular") circular: Boolean + ): Call + @GET("/v1/users/avatar-bust") + fun getBust( + @Query("userIds") userId: Long, + @Query("size") size: String, + @Query("format") format: String, + @Query("isCircular") circular: Boolean + ): Call +} diff --git a/src/main/kotlin/dev/roava/api/UserApi.kt b/src/main/kotlin/dev/roava/api/UserApi.kt index 84c14d6..03245c0 100644 --- a/src/main/kotlin/dev/roava/api/UserApi.kt +++ b/src/main/kotlin/dev/roava/api/UserApi.kt @@ -26,12 +26,14 @@ package dev.roava.api import dev.roava.json.user.UserData import dev.roava.json.user.UserListData +import dev.roava.json.user.UserNameHistoryData import dev.roava.json.user.UserNameRequest import retrofit2.Call import retrofit2.http.Body import retrofit2.http.GET import retrofit2.http.POST import retrofit2.http.Path +import retrofit2.http.Query /** * UserApi (for internal use only) @@ -42,4 +44,7 @@ interface UserApi { @POST("/v1/usernames/users") fun getUsernameInformation(@Body data: UserNameRequest): Call + + @GET("/v1/users/{userId}/username-history") + fun getPastUsernames(@Path("userId") userId: Long, @Query("limit") limit: String, @Query("cursor") cursor: String? = null): Call } \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/client/RoavaClient.kt b/src/main/kotlin/dev/roava/client/RoavaClient.kt index 1457f7a..ab9e2ed 100644 --- a/src/main/kotlin/dev/roava/client/RoavaClient.kt +++ b/src/main/kotlin/dev/roava/client/RoavaClient.kt @@ -58,7 +58,7 @@ class RoavaClient { throw RuntimeException("Your cookie is not set properly! Please make sure that you include the entirety of the string, including the _|WARNING:-") } - request = dev.roava.client.RoavaRequest(cookie) + request = RoavaRequest(cookie) this.cookie = cookie try { diff --git a/src/main/kotlin/dev/roava/client/RoavaInterceptor.kt b/src/main/kotlin/dev/roava/client/RoavaInterceptor.kt index 7b4aaf5..7570a46 100644 --- a/src/main/kotlin/dev/roava/client/RoavaInterceptor.kt +++ b/src/main/kotlin/dev/roava/client/RoavaInterceptor.kt @@ -32,11 +32,20 @@ import okhttp3.Response * For Intercepting calls and adding the X-CSRF-TOKEN to the header if the original request failed (for internal use only). */ internal class RoavaInterceptor: Interceptor { + private val header = "X-CSRF-TOKEN" + + private var token: String? = null + override fun intercept(chain: Interceptor.Chain): Response { - var response = chain.proceed(chain.request()) + val orig = chain.request() + val request = orig.newBuilder() + .header(header, token ?: "") + .build() + + var response = chain.proceed(request) if (response.code() == 403 && !hasToken(response)) { - val token: String? = response.header("X-CSRF-TOKEN") + token = response.header(header) token?.let { response.close() @@ -50,7 +59,7 @@ internal class RoavaInterceptor: Interceptor { private fun hasToken(response: Response?): Boolean { response?.let { - return !it.request().header("X-CSRF-TOKEN").isNullOrEmpty() + return !it.request().header(header).isNullOrEmpty() } return false @@ -58,7 +67,7 @@ internal class RoavaInterceptor: Interceptor { private fun retry(request: Request?, token: String): Request? { return request?.newBuilder() - ?.header("X-CSRF-TOKEN", token) + ?.header(header, token) ?.build() } } \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/group/Group.kt b/src/main/kotlin/dev/roava/group/Group.kt index caf28ec..b9745d4 100644 --- a/src/main/kotlin/dev/roava/group/Group.kt +++ b/src/main/kotlin/dev/roava/group/Group.kt @@ -27,9 +27,11 @@ package dev.roava.group import dev.roava.api.GroupApi import dev.roava.client.RoavaClient import dev.roava.client.RoavaRequest -import dev.roava.json.group.GroupData -import dev.roava.json.group.RoleRequest +import dev.roava.json.group.* import dev.roava.user.User +import dev.roava.util.Pagination +import retrofit2.Call +import retrofit2.HttpException /** * A class which represents a Group. @@ -234,16 +236,21 @@ class Group { roleNumber = getRole(roleNumber).id } - runCatching { + val result = runCatching { client.request.createRequest(GroupApi::class.java, "groups") .rankUser(id, userId, RoleRequest(roleNumber)) - .execute().isSuccessful.also { - if (!it) { - throw RuntimeException("Could not rank the provided user!") - } - } - }.onFailure { - throw RuntimeException("Could not rank the provided user!") + .execute() + } + + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Ranking user with id $userId failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("An unknown error has occurred while ranking the user!") + } } } @@ -277,4 +284,92 @@ class Group { fun exileUser(user: User) { exileUser(user.id) } + + /** + * Method to grab all group members with the provided roleset + * + * @return[GroupRankListData] + * @param[roleSetId] The roleSetId of the group rank. + * @throws[RuntimeException] + */ + @Throws(RuntimeException::class) + fun getGroupRankMembers(roleSetId: Int): List{ + val members: MutableList = mutableListOf() + var nextPageCursor: String? = null + fun makeRequest(){ + val result = runCatching { + request.createRequest(GroupApi::class.java, "groups") + .getGroupRankMembers(this.id,roleSetId, 100, nextPageCursor) + .execute() + } + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Grabbing members in the group with id ${this.id} & roleSetId of $roleSetId failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("an unknown error has occurred while fetching the members with that rank!\n${exception.message}") + } + } + result.onSuccess { + nextPageCursor = it.body()?.nextPageCursor + val data = it.body()?.data ?: throw RuntimeException("An unknown error has occurred") + for(i in data){ + members += GroupRankListData(i.hasVerifiedBadge,i.userId,i.username,i.displayName) + } + } + } + makeRequest() + while(nextPageCursor != null) { + makeRequest() + } + return members + } + /** + * Method to grab all group members + * + * @return[Map] + * @throws[RuntimeException] + */ + @Throws(RuntimeException::class) + fun getMembers(): Map{ + val members: MutableMap = mutableMapOf() + var nextPageCursor: String? = null + fun makeRequest(){ + val result = runCatching { +// request.createRequest(GroupApi::class.java, "groups") +// .getGroupMembers(this.id,100, nextPageCursor) +// .execute() + + val request_ = request.createRequest(GroupApi::class.java, "groups")::getGroupMembers + Pagination.next() + request_(this.id,100,nextPageCursor).execute() + + } + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Grabbing members in the group with id ${this.id} failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("an unknown error has occurred while fetching the members!\n${exception.message}") + } + } + result.onSuccess { +// nextPageCursor = it. +// val data = it.body()?.data ?: throw RuntimeException("An unknown error has occurred") +// for(i in data){ +// members += i.user to i.role +// } + } + } + makeRequest() + while(nextPageCursor != null) { + makeRequest() + } + return members + } + } \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/json/group/GroupMemberData.kt b/src/main/kotlin/dev/roava/json/group/GroupMemberData.kt new file mode 100644 index 0000000..78f5b88 --- /dev/null +++ b/src/main/kotlin/dev/roava/json/group/GroupMemberData.kt @@ -0,0 +1,62 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.json.group + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty + +@JsonIgnoreProperties(ignoreUnknown = true) +data class GroupMemberData ( + @JsonProperty("user") + val user: GroupMemberUserData, + @JsonProperty("role") + val role: GroupMemberRoleData +) +@JsonIgnoreProperties(ignoreUnknown = true) +data class GroupMemberUserData ( + @JsonProperty("username") + val username: String?, + @JsonProperty("userId") + val userId: Long?, + @JsonProperty("displayName") + val displayName: String?, + @JsonProperty("hasVerifiedBadge") + val hasVerifiedBadge: Boolean?, + @JsonProperty("buildersClubMembershipType") + val buildersClubMembershipType: Long? +) +@JsonIgnoreProperties(ignoreUnknown = true) +data class GroupMemberRoleData ( + @JsonProperty("id") + val roleId: Long?, + @JsonProperty("name") + val name: String?, + @JsonProperty("description") + val description: String?, + @JsonProperty("rank") + val rank: Long?, + @JsonProperty("memberCount") + val memberCount: Long? +) \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/json/group/GroupRankData.kt b/src/main/kotlin/dev/roava/json/group/GroupRankData.kt new file mode 100644 index 0000000..b2c1eb7 --- /dev/null +++ b/src/main/kotlin/dev/roava/json/group/GroupRankData.kt @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.json.group + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty + +@JsonIgnoreProperties(ignoreUnknown = true) +data class GroupRankData( + @JsonProperty("nextPageCursor") + val nextPageCursor: String?, + @JsonProperty("previousPageCursor") + val previousPageCursor: String?, + @JsonProperty("data") + val data: List +) +data class GroupRankListData( + @JsonProperty("hasVerifiedBadge") + val hasVerifiedBadge: Boolean?, + @JsonProperty("userId") + val userId: Long?, + @JsonProperty("username") + val username: String?, + @JsonProperty("displayName") + val displayName: String? +) \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/json/user/ThumbnailData.kt b/src/main/kotlin/dev/roava/json/user/ThumbnailData.kt new file mode 100644 index 0000000..14ee7f4 --- /dev/null +++ b/src/main/kotlin/dev/roava/json/user/ThumbnailData.kt @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.json.user + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty +@JsonIgnoreProperties(ignoreUnknown = true) +data class ThumbnailData( + @JsonProperty("imageUrl") + val thumbnail: String? +) \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/json/user/ThumbnailListData.kt b/src/main/kotlin/dev/roava/json/user/ThumbnailListData.kt new file mode 100644 index 0000000..3f96014 --- /dev/null +++ b/src/main/kotlin/dev/roava/json/user/ThumbnailListData.kt @@ -0,0 +1,33 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.json.user + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty +@JsonIgnoreProperties(ignoreUnknown = true) +data class ThumbnailListData ( + @JsonProperty("data") + val data: List? +) \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/json/user/UserNameHistoryData.kt b/src/main/kotlin/dev/roava/json/user/UserNameHistoryData.kt new file mode 100644 index 0000000..0689a23 --- /dev/null +++ b/src/main/kotlin/dev/roava/json/user/UserNameHistoryData.kt @@ -0,0 +1,40 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.json.user + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.annotation.JsonProperty + +@JsonIgnoreProperties(ignoreUnknown = true) +data class UserNameHistoryData( + @JsonProperty("nextPageCursor") + val nextPageCursor: String?, + @JsonProperty("data") + val data: List +) +data class UserNameHistory( + @JsonProperty("name") + val name: String? +) \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/user/User.kt b/src/main/kotlin/dev/roava/user/User.kt index abe198f..fd1a920 100644 --- a/src/main/kotlin/dev/roava/user/User.kt +++ b/src/main/kotlin/dev/roava/user/User.kt @@ -24,14 +24,12 @@ package dev.roava.user -import dev.roava.api.FriendApi -import dev.roava.api.GroupApi -import dev.roava.api.InventoryApi -import dev.roava.api.UserApi +import dev.roava.api.* import dev.roava.client.RoavaRequest import dev.roava.group.Group import dev.roava.json.user.UserData import dev.roava.json.user.UserNameRequest +import retrofit2.HttpException /** * A class which represents a User which is not authenticated by the [dev.roava.client.RoavaClient]. @@ -126,7 +124,7 @@ class User { */ @Throws(RuntimeException::class) fun getFriends(): List { - var friends = mutableListOf() + val friends = mutableListOf() try { val userData = request.createRequest(FriendApi::class.java, "friends") @@ -154,22 +152,123 @@ class User { fun getGroups(): List { val groups = mutableListOf() - try { - val groupData = request.createRequest(GroupApi::class.java, "groups") + val result = runCatching { + request.createRequest(GroupApi::class.java, "groups") .getUserRoleInfo(id) .execute() - .body() + } + + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + throw RuntimeException("Ranking user with id ${this.id} failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("An unknown error has occurred while fetching the user's groups!") + } + }.onSuccess { + val groupData = it.body() + for (group in groupData?.data!!) { groups.add(Group(group.groupData!!)) } - } catch(exception: Exception) { - throw RuntimeException("Could not fetch the user's groups!") } return groups.toList() } + /** + * Method to get a User's Avatar + * + * Available Values: + * 30x30, 48x48, 60x60, 75x75, 100x100, 110x110, 140x140, 150x150, 150x200, 180x180, 250x250, 352x352, 420x420, 720x720 + * @throws[RuntimeException] + * @return[String] + */ + @Throws(RuntimeException::class) + fun getAvatar(size: String,isCircular: Boolean): String { + var thumbnail = "" + val result = runCatching { + request.createRequest(ThumbnailApi::class.java, "thumbnails") + .getAvatar(id,size,"Png",isCircular) + .execute() + } + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Grabbing thumbnail of user with id ${this.id} failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("an unknown error has occurred while fetching the user's thumbnail!\n${exception.message}") + } + }.onSuccess { + thumbnail = it.body()?.data?.get(0)?.thumbnail?: "" + } + return thumbnail + } + + /** + * Method to get a User's Headshot + * + * Available Values: + * 30x30, 48x48, 60x60, 75x75, 100x100, 110x110, 140x140, 150x150, 150x200, 180x180, 250x250, 352x352, 420x420, 720x720 + * @throws[RuntimeException] + * @return[String] + */ + @Throws(RuntimeException::class) + fun getHeadShot(size: String,isCircular: Boolean): String { + var thumbnail = "" + val result = runCatching { + request.createRequest(ThumbnailApi::class.java, "thumbnails") + .getHeadShot(id, size, "Png", isCircular) + .execute() + } + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Grabbing headshot of user with id ${this.id} failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("an unknown error has occurred while fetching the user's headshot!\n${exception.message}") + } + }.onSuccess { + thumbnail = it.body()?.data?.get(0)?.thumbnail ?: "" + } + return thumbnail + } + /** + * Method to get a User's Bust + * + * Available Values: + * 48x48, 50x50, 60x60, 75x75, 100x100, 150x150, 180x180, 352x352, 420x420 + * @throws[RuntimeException] + * @return[String] + */ + @Throws(RuntimeException::class) + fun getBust(size: String,isCircular: Boolean): String { + var thumbnail = "" + val result = runCatching { + request.createRequest(ThumbnailApi::class.java, "thumbnails") + .getBust(id, size, "Png", isCircular) + .execute() + } + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Grabbing bust of user with id ${this.id} failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("an unknown error has occurred while fetching the user's bust!\n${exception.message}") + } + }.onSuccess { + thumbnail = it.body()?.data?.get(0)?.thumbnail ?: "" + } + return thumbnail + } /** * Method to get if a User is in a group * @@ -208,4 +307,45 @@ class User { }.getOrElse { throw RuntimeException("Could not fetch the user's items!") } + + /** + * Method to get a user's past 100 usernames + * + * @throws[RuntimeException] + * @return[List] + */ + @Throws(RuntimeException::class) + fun getPastUsers(): List { + var cursor: String? = null + val pastNames: MutableList = mutableListOf() + fun makeRequest(){ + val result = runCatching { + request.createRequest(UserApi::class.java, "users") + .getPastUsernames(id, "100", cursor) + .execute() + } + result.onFailure { exception -> + if (exception is HttpException) { + val errorCode = exception.code() + val message = exception.message() + + throw RuntimeException("Grabbing past usernames of user with id ${this.id} failed with message \"$message\" and response code $errorCode") + } else { + throw RuntimeException("an unknown error has occurred while fetching the user's past names!\n${exception.message}") + } + } + result.onSuccess { + cursor = it.body()?.nextPageCursor + val data = it.body()?.data ?: throw RuntimeException("An unknown error has occurred") + for (i in data) { + pastNames += i.name ?: continue + } + } + } + makeRequest() + while(cursor != null){ + makeRequest() + } + return pastNames + } } \ No newline at end of file diff --git a/src/main/kotlin/dev/roava/util/Pagination.kt b/src/main/kotlin/dev/roava/util/Pagination.kt new file mode 100644 index 0000000..afe67b5 --- /dev/null +++ b/src/main/kotlin/dev/roava/util/Pagination.kt @@ -0,0 +1,60 @@ +/* + * MIT License + * + * Copyright (c) 2024 RoavaDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package dev.roava.util + +import com.fasterxml.jackson.annotation.JsonProperty +import dev.roava.client.RoavaClient +import dev.roava.client.RoavaRequest +import retrofit2.Call +import kotlin.reflect.KFunction + +// T would be a data class +class Pagination( + private val url: String, + private val client: RoavaClient, + + @JsonProperty("nextPageCursor") + private val nextCursor: String?, + + @JsonProperty("previousPageCursor") + private val previousCursor: String?, + + @JsonProperty("data") + private val data: List? // this is where we actually get what we are after +) { + // this function might or might not be necessary + private fun paginate(function: () -> Any): Pagination { + // return a new pagination object where you take the current URL with the cursor and return a new pagination object + return function.execute() + } + + fun next(): Pagination? { + return this.nextCursor?.let { this.paginate(it) } + } + + fun previous(): Pagination? { + return this.previousCursor?.let { this.paginate(it) }; + } +} \ No newline at end of file diff --git a/src/test/kotlin/dev/roava/group/GroupTest.kt b/src/test/kotlin/dev/roava/group/GroupTest.kt index e3a3c7a..a7d30d6 100644 --- a/src/test/kotlin/dev/roava/group/GroupTest.kt +++ b/src/test/kotlin/dev/roava/group/GroupTest.kt @@ -24,9 +24,13 @@ package dev.roava.group +import dev.roava.json.group.GroupMemberData +import dev.roava.json.group.GroupMemberRoleData +import dev.roava.json.group.GroupMemberUserData import org.junit.jupiter.api.Test import org.junit.jupiter.api.Assertions.* +import kotlin.test.assertIs internal class GroupTest { private val testGroup = Group(15771240) @@ -119,4 +123,13 @@ internal class GroupTest { assertThrows(RuntimeException::class.java, executable) } + @Test + fun testGroupRankMembers(){ + val group = testGroup.getGroupRankMembers(88561132) + assertIs>(group) + } + @Test + fun testGroupMembers(){ + assertIs>(testGroup.getMembers()) + } } \ No newline at end of file diff --git a/src/test/kotlin/dev/roava/user/UserTest.kt b/src/test/kotlin/dev/roava/user/UserTest.kt index 90c6071..06f6f7f 100644 --- a/src/test/kotlin/dev/roava/user/UserTest.kt +++ b/src/test/kotlin/dev/roava/user/UserTest.kt @@ -24,9 +24,9 @@ package dev.roava.user -import org.junit.jupiter.api.Test - import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import kotlin.test.assertContains internal class UserTest { private val testUser = User(3838771115) @@ -91,4 +91,20 @@ internal class UserTest { fun testDescription() { assertEquals(testUser.description, "This is a test description") } + @Test + fun testAvatar() { + assertContains(testUser.getAvatar("30x30",true), "https://tr.rbxcdn.com/") + } + @Test + fun testHeadShot(){ + assertContains(testUser.getHeadShot("48x48", true), "https://tr.rbxcdn.com/") + } + @Test + fun testBust(){ + assertContains(testUser.getBust("48x48",true), "https://tr.rbxcdn.com/") + } + @Test + fun testPast(){ + assertEquals(User(1157409).getPastUsers().size, 127) + } } \ No newline at end of file