Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * Permits Rate limiting in discord based applications
- *
- * @param shouldWait indicates whether or not the interceptor should throw an exception when rate limit is hit
- */
- open class RateLimitInterceptor @JvmOverloads constructor(protected val shouldWait: Boolean = false) : Interceptor
- {
- @Throws(RateLimitException::class)
- override fun intercept(chain: Interceptor.Chain): Response
- {
- val request = chain.request()
- val requestUrl = request.url().encodedPathSegments()
- val type = requestUrl[2]
- val identifier = requestUrl[3]
- val map = when (type)
- {
- GUILD ->
- {
- limitsPerGuild
- }
- CHANNELS ->
- {
- limitsPerChannel
- }
- else ->
- {
- globalLimits
- }
- }
- var limit = map[identifier]
- if (limit == null)
- {
- limit = Channel(10)
- map[identifier] = limit
- }
- else
- {
- val actualLimit = runBlocking<Limit> { limit!!.receive() }
- if (actualLimit.remaining == 0)
- waitForLimit(actualLimit)
- }
- val response = chain.proceed(request)
- val responseLimit = Limit(response)
- if (responseLimit.reset == null)
- {
- responseLimit.reset = 0
- }
- launch(CommonPool) { limit!!.send(responseLimit) }
- if (response.code() == 429)
- {
- return intercept(chain)
- }
- return response
- }
- @Throws(RateLimitException::class)
- protected open fun waitForLimit(limit: Limit)
- {
- if (limit.isExpired)
- return
- if (shouldWait)
- limit.delayUntilReset()
- else
- throw RateLimitException()
- }
- companion object
- {
- @JvmStatic
- protected val CHANNELS = "channels"
- @JvmStatic
- protected val GUILD = "guilds"
- @JvmStatic
- protected val limitsPerGuild: ConcurrentHashMap<String, Channel<Limit>> = ConcurrentHashMap()
- @JvmStatic
- protected val limitsPerChannel: ConcurrentHashMap<String, Channel<Limit>> = ConcurrentHashMap()
- @JvmStatic
- protected val globalLimits: ConcurrentHashMap<String, Channel<Limit>> = ConcurrentHashMap()
- }
- open class Limit(response: Response)
- {
- open val global: Boolean? = response.header(X_RATELIMIT_GLOBAL)?.toBoolean()
- open val limit: Int? = response.header(X_RATELIMIT_LIMIT)?.toInt()
- open val remaining: Int? = response.header(X_RATELIMIT_REMAINING)?.toInt()
- open var reset: Long? = response.header(X_RATELIMIT_RESET)?.toLong()
- open val isExpired: Boolean
- get()
- {
- return getDelay() <= 0
- }
- open fun getDelay(): Long
- {
- val reset = this@Limit.reset ?: throw NullPointerException("Reset epoch was not specified")
- val time = Date().time.div(1000).minus(reset)
- return time
- }
- open fun delayUntilReset() = runBlocking<Unit>()
- {
- if (isExpired)
- return@runBlocking
- val time = getDelay()
- delay(time)
- }
- companion object
- {
- @JvmStatic
- protected val X_RATELIMIT_GLOBAL = "X-RateLimit-Global"
- @JvmStatic
- protected val X_RATELIMIT_LIMIT = "X-RateLimit-Limit"
- @JvmStatic
- protected val X_RATELIMIT_REMAINING = "X-RateLimit-REMAINING"
- @JvmStatic
- protected val X_RATELIMIT_RESET = "X-RateLimit-RESET"
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement