2020-06-17 05:34:23 +00:00
|
|
|
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch
|
2020-05-31 05:10:18 +00:00
|
|
|
from discordobject import snowflake
|
|
|
|
|
|
|
|
proc getIDFromJson*(str: string): uint64 =
|
|
|
|
var num: uint64
|
2020-06-18 03:16:58 +00:00
|
|
|
discard parseBiggestUInt(str, num)
|
2020-05-31 05:10:18 +00:00
|
|
|
return num
|
|
|
|
|
2020-06-18 03:18:22 +00:00
|
|
|
proc parseIntEasy*(str: string): int =
|
|
|
|
var num: int
|
|
|
|
discard parseInt(str, num)
|
|
|
|
return num
|
|
|
|
|
2020-06-17 05:34:23 +00:00
|
|
|
proc endpoint*(url: string): string =
|
|
|
|
return fmt("https://discord.com/api/v6{url}")
|
2020-05-31 05:10:18 +00:00
|
|
|
|
2020-06-18 03:18:22 +00:00
|
|
|
var globalToken*: string
|
|
|
|
|
|
|
|
proc defaultHeaders*(added: HttpHeaders = newHttpHeaders()): HttpHeaders =
|
|
|
|
added.add("Authorization", fmt("Bot {globalToken}"))
|
|
|
|
added.add("User-Agent", "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)")
|
|
|
|
added.add("X-RateLimit-Precision", "millisecond")
|
|
|
|
return added;
|
|
|
|
|
2020-06-17 05:34:23 +00:00
|
|
|
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:
|
2020-06-18 03:17:46 +00:00
|
|
|
if (channelRatelimitBucket.hasKey(objectID)):
|
|
|
|
rlmt = channelRatelimitBucket[objectID]
|
|
|
|
else:
|
|
|
|
channelRatelimitBucket.add(objectID, newRateLimit())
|
|
|
|
rlmt = channelRatelimitBucket[objectID]
|
2020-06-17 05:34:23 +00:00
|
|
|
of RateLimitBucketType.guild:
|
2020-06-18 03:17:46 +00:00
|
|
|
if (guildRatelimitBucket.hasKey(objectID)):
|
|
|
|
rlmt = guildRatelimitBucket[objectID]
|
|
|
|
else:
|
|
|
|
guildRatelimitBucket.add(objectID, newRateLimit())
|
|
|
|
rlmt = guildRatelimitBucket[objectID]
|
2020-06-17 05:34:23 +00:00
|
|
|
of RateLimitBucketType.webhook:
|
2020-06-18 03:17:46 +00:00
|
|
|
if (webhookRatelimitBucket.hasKey(objectID)):
|
|
|
|
rlmt = webhookRatelimitBucket[objectID]
|
|
|
|
else:
|
|
|
|
webhookRatelimitBucket.add(objectID, newRateLimit())
|
|
|
|
rlmt = webhookRatelimitBucket[objectID]
|
2020-06-17 05:34:23 +00:00
|
|
|
of RateLimitBucketType.global:
|
|
|
|
rlmt = globalRateLimit
|
|
|
|
|
2020-06-18 03:17:46 +00:00
|
|
|
if (rlmt != nil and rlmt.remainingLimit == 0):
|
2020-06-17 05:34:23 +00:00
|
|
|
let millisecondTime: float = rlmt.ratelimitReset * 1000 - epochTime() * 1000
|
|
|
|
|
|
|
|
if (millisecondTime > 0):
|
|
|
|
echo fmt("Rate limit wait time: {millisecondTime} miliseconds")
|
2020-06-18 03:18:22 +00:00
|
|
|
discard sleepAsync(millisecondTime)
|
|
|
|
|
|
|
|
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
|
2020-06-18 04:49:28 +00:00
|
|
|
echo "Sending ", httpMethod, " request, URL: ", endpoint, ", headers: ", $headers, " body: ", strPayload
|
2020-06-18 03:18:22 +00:00
|
|
|
|
|
|
|
waitForRateLimits(objectID, bucketType)
|
|
|
|
let response = client.request(endpoint, httpMethod, strPayload)
|
|
|
|
return handleResponse(response, objectId, bucketType)
|