Push untested ratelimiting.
This commit is contained in:
parent
2e12d71f0a
commit
2bf20854d6
|
@ -1,4 +1,4 @@
|
|||
import websocket, asyncnet, asyncdispatch, json, httpClient, strformat, eventdispatcher, eventhandler, streams
|
||||
import websocket, asyncnet, asyncdispatch, json, httpClient, eventdispatcher, strformat, eventhandler, streams, nimcordutils, discordobject
|
||||
|
||||
type
|
||||
DiscordOpCode = enum
|
||||
|
@ -24,6 +24,31 @@ type
|
|||
heartbeatAcked: bool
|
||||
lastSequence: int
|
||||
|
||||
var globalClient: DiscordClient
|
||||
|
||||
proc defaultHeaders*(client: DiscordClient, added: HttpHeaders = newHttpHeaders()): HttpHeaders =
|
||||
added.add("Authorization", fmt("Bot {client.token}"))
|
||||
added.add("User-Agent", "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)")
|
||||
added.add("X-RateLimit-Precision", "millisecond")
|
||||
return added;
|
||||
|
||||
proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: snowflake = 0, bucketType: RateLimitBucketType = global, jsonBody: JsonNode = %*{}): JsonNode =
|
||||
var client = newHttpClient()
|
||||
# Add headers
|
||||
client.headers = headers
|
||||
|
||||
var strPayload: string
|
||||
if ($jsonBody == "{}"):
|
||||
strPayload = ""
|
||||
else:
|
||||
strPayload = $jsonBody
|
||||
|
||||
echo "Sending GET request, URL: ", endpoint, ", body: ", strPayload
|
||||
|
||||
waitForRateLimits(objectID, bucketType)
|
||||
|
||||
return handleResponse(client.request(endpoint, httpMethod, strPayload), objectId, bucketType)
|
||||
|
||||
proc sendGatewayRequest*(client: DiscordClient, request: JsonNode, msg: string = "") {.async.} =
|
||||
if (msg.len == 0):
|
||||
echo "Sending gateway payload: ", request
|
||||
|
@ -75,16 +100,13 @@ proc handleWebsocketPacket(client: DiscordClient) {.async.} =
|
|||
else:
|
||||
discard
|
||||
|
||||
proc endpoint*(url: string): string =
|
||||
return fmt("https://discord.com/api/v6{url}")
|
||||
|
||||
proc startConnection*(client: DiscordClient) {.async.} =
|
||||
globalClient = client
|
||||
|
||||
client.httpClient = newAsyncHttpClient()
|
||||
client.httpClient.headers = newHttpHeaders({"Authorization": fmt("Bot {client.token}")})
|
||||
|
||||
let urlResult = parseJson(await client.httpClient.getContent(endpoint("/gateway/bot")))
|
||||
echo "Got result: ", $urlResult
|
||||
|
||||
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, client.defaultHeaders())
|
||||
if (urlResult.contains("url")):
|
||||
let url = urlResult["url"].getStr()
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
include parseutils, json, httpClient
|
||||
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch
|
||||
from discordobject import snowflake
|
||||
|
||||
proc getIDFromJson*(str: string): uint64 =
|
||||
|
@ -6,9 +6,84 @@ proc getIDFromJson*(str: string): uint64 =
|
|||
discard parseOct(str, num)
|
||||
return num
|
||||
|
||||
proc endpoint*(url: string): string =
|
||||
return fmt("https://discord.com/api/v6{url}")
|
||||
|
||||
type RateLimitBucketType = enum
|
||||
CHANNEL,
|
||||
GUILD,
|
||||
WEBHOOK,
|
||||
GLOBAL
|
||||
type
|
||||
RateLimitBucketType* = enum
|
||||
channel,
|
||||
guild,
|
||||
webhook,
|
||||
global
|
||||
RateLimit = ref object {.requiresInit.}
|
||||
limit: int
|
||||
remainingLimit: int
|
||||
ratelimitReset: float
|
||||
|
||||
proc newRateLimit(lmt: int = 500, remLmnt: int = 500, ratelmtReset: float = 0): RateLimit =
|
||||
return RateLimit(limit: lmt, remainingLimit: remLmnt, ratelimitReset: ratelmtReset)
|
||||
|
||||
# Rate limit buckets
|
||||
let channelRatelimitBucket = newTable[snowflake, RateLimit]()
|
||||
let guildRatelimitBucket = newTable[snowflake, RateLimit]()
|
||||
let webhookRatelimitBucket = newTable[snowflake, RateLimit]()
|
||||
var globalRateLimit: RateLimit = newRateLimit()
|
||||
|
||||
proc handleRateLimits*(headers: HttpHeaders, objectID: snowflake, bucketType: RateLimitBucketType) =
|
||||
var obj: RateLimit
|
||||
if (headers.hasKey("x-ratelimit-global")):
|
||||
obj = globalRateLimit
|
||||
elif (headers.hasKey("x-ratelimit-limit")):
|
||||
case bucketType:
|
||||
of RateLimitBucketType.channel:
|
||||
obj = channelRatelimitBucket[objectID]
|
||||
discard
|
||||
of RateLimitBucketType.guild:
|
||||
obj = guildRatelimitBucket[objectID]
|
||||
discard
|
||||
of RateLimitBucketType.webhook:
|
||||
obj = webhookRatelimitBucket[objectID]
|
||||
discard
|
||||
of RateLimitBucketType.global:
|
||||
obj = globalRateLimit
|
||||
discard
|
||||
else:
|
||||
return
|
||||
|
||||
discard parseInt(headers["x-ratelimit-limit"], obj.limit)
|
||||
discard parseInt(headers["x-ratelimit-remaining"], obj.remainingLimit)
|
||||
discard parseFloat(headers["x-ratelimit-reset"], obj.ratelimitReset)
|
||||
|
||||
|
||||
proc handleResponse*(response: Response, objectID: snowflake, bucketType: RateLimitBucketType): JsonNode =
|
||||
echo fmt("Received requested payload: {response.body}")
|
||||
|
||||
handleRateLimits(response.headers, objectID, bucketType)
|
||||
|
||||
return parseJson(response.body())
|
||||
|
||||
proc waitForRateLimits*(objectID: snowflake, bucketType: RateLimitBucketType) =
|
||||
var rlmt: RateLimit
|
||||
if (globalRateLimit.remainingLimit == 0):
|
||||
rlmt = globalRateLimit
|
||||
else:
|
||||
case bucketType:
|
||||
of RateLimitBucketType.channel:
|
||||
rlmt = channelRatelimitBucket[objectID]
|
||||
discard
|
||||
of RateLimitBucketType.guild:
|
||||
rlmt = guildRatelimitBucket[objectID]
|
||||
discard
|
||||
of RateLimitBucketType.webhook:
|
||||
rlmt = webhookRatelimitBucket[objectID]
|
||||
discard
|
||||
of RateLimitBucketType.global:
|
||||
rlmt = globalRateLimit
|
||||
discard
|
||||
|
||||
if (rlmt.remainingLimit == 0):
|
||||
let millisecondTime: float = rlmt.ratelimitReset * 1000 - epochTime() * 1000
|
||||
|
||||
if (millisecondTime > 0):
|
||||
echo fmt("Rate limit wait time: {millisecondTime} miliseconds")
|
||||
discard sleepAsync(millisecondTime)
|
Reference in New Issue