Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
kotlin.code.style=official

org.gradle.caching=true
version = 0.0.2-SNAPSHOT
version = 0.0.5-SNAPSHOT
11 changes: 8 additions & 3 deletions src/main/kotlin/dev/roava/api/GroupApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand All @@ -52,4 +51,10 @@ interface GroupApi {

@DELETE("/v1/groups/{groupId}/users/{userId}")
fun exileUser(@Path("groupId") groupId: Int, @Path("userId") userId: Long): Call<Void>

@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<GroupRankData>

@GET("v1/groups/{groupId}/users")
fun getGroupMembers(@Path("groupId") groupId: Int, @Query("limit") limit: Int, @Query("cursor") cursor: String? = null): Call<Pagination<GroupMemberData>>
}
54 changes: 54 additions & 0 deletions src/main/kotlin/dev/roava/api/ThumbnailApi.kt
Original file line number Diff line number Diff line change
@@ -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<ThumbnailListData>
@GET("/v1/users/avatar-headshot")
fun getHeadShot(
@Query("userIds") userId: Long,
@Query("size") size: String,
@Query("format") format: String,
@Query("isCircular") circular: Boolean
): Call<ThumbnailListData>
@GET("/v1/users/avatar-bust")
fun getBust(
@Query("userIds") userId: Long,
@Query("size") size: String,
@Query("format") format: String,
@Query("isCircular") circular: Boolean
): Call<ThumbnailListData>
}
5 changes: 5 additions & 0 deletions src/main/kotlin/dev/roava/api/UserApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -42,4 +44,7 @@ interface UserApi {

@POST("/v1/usernames/users")
fun getUsernameInformation(@Body data: UserNameRequest): Call<UserListData>

@GET("/v1/users/{userId}/username-history")
fun getPastUsernames(@Path("userId") userId: Long, @Query("limit") limit: String, @Query("cursor") cursor: String? = null): Call<UserNameHistoryData>
}
2 changes: 1 addition & 1 deletion src/main/kotlin/dev/roava/client/RoavaClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
17 changes: 13 additions & 4 deletions src/main/kotlin/dev/roava/client/RoavaInterceptor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -50,15 +59,15 @@ 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
}

private fun retry(request: Request?, token: String): Request? {
return request?.newBuilder()
?.header("X-CSRF-TOKEN", token)
?.header(header, token)
?.build()
}
}
115 changes: 105 additions & 10 deletions src/main/kotlin/dev/roava/group/Group.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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!")
}
}
}

Expand Down Expand Up @@ -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<GroupRankListData>{
val members: MutableList<GroupRankListData> = 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<GroupMemberUserData, GroupMemberRoleData>{
val members: MutableMap<GroupMemberUserData, GroupMemberRoleData> = 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<GroupMemberData>.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
}

}
62 changes: 62 additions & 0 deletions src/main/kotlin/dev/roava/json/group/GroupMemberData.kt
Original file line number Diff line number Diff line change
@@ -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?
)
Loading