Make a bunch of image url getters and memory optimize the image hashes

This commit is contained in:
SeanOMik 2020-08-13 19:39:38 -05:00
parent fadf533066
commit 0764147bed
No known key found for this signature in database
GPG Key ID: FA4D55AC05268A88
5 changed files with 220 additions and 27 deletions

View File

@ -22,7 +22,7 @@ proc getIdentifyPacket(shard: Shard): JsonNode
proc handleGatewayDisconnect(shard: Shard, error: string) {.async.}
proc handleHeartbeat(shard: Shard) {.async.}
proc handleWebsocketPacket(shard: Shard) {.async.}
proc newDiscordClient*(tkn: string): DiscordClient
proc newDiscordClient*(tkn: string, commandPrefix: string): DiscordClient
proc newShard(shardID: int, client: DiscordClient): Shard
proc reconnectShard(shard: Shard) {.async.}
proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.async.}

View File

@ -96,3 +96,21 @@ proc deleteEmoji*(emoji: Emoji) {.async.} =
## Delete the given emoji. Requires the `MANAGE_EMOJIS` permission.
discard sendRequest(endpoint(fmt("/guilds/{emoji.guildID}/emojis/{emoji.id}")), HttpDelete,
defaultHeaders(), emoji.guildID, RateLimitBucketType.guild)
proc getEmojiURL*(emoji: Emoji, imageType: ImageType = ImageType.imgTypeAuto): string =
## Get the URL for the emoji's.
result = "https://cdn.discordapp.com/emojis/" & $emoji.id
# Choose the type of image automaticly.
var tmp = imageType
if emoji.animated:
tmp = ImageType.imgTypeGif
else:
tmp = ImageType.imgTypePng
case tmp:
of ImageType.imgTypeGif:
result &= ".gif"
discard
else: # The only other possible image type is png
result &= ".png"

View File

@ -1,6 +1,6 @@
import json, discordobject, channel, member, options, nimcordutils, emoji
import role, permission, httpcore, strformat, image, asyncdispatch, user
import permission, presence, tables
import permission, presence, tables, strutils
type
VerificationLevel* = enum
@ -68,9 +68,10 @@ type
Guild* = ref object of DiscordObject
## Discord Guild object
name*: string ## The name of the current guild
icon*: string ## The hash of the current guild's icon
splash*: string ## The hash of the current guild's splash
discoverySplash*: string
iconRaw: array[2, uint64] ## The split hash for the 128bit hexadeximal icon.
isIconGif: bool ## Wether the avatar is a gif.
splashRaw: array[2, uint64] ## The split hash for the 128bit hexadeximal splash.
discoverySplashRaw: array[2, uint64] ## The split hash for the 128bit hexadeximal discovery splash.
owner*: bool ## Whether or not the current user is the owner of the current guild
ownerID: Snowflake ## The snowflake id of the current guild's owner
permissions*: Permissions
@ -102,7 +103,7 @@ type
maxMembers*: int ## The maximum amount of members in the current guild?
vanityUrlCode*: string ## The vanity invite for the current guild (ex: https://discord.gg/discord-api)
description*: string
banner*: string ## The hash code of the current guild
bannerRaw: array[2, uint64] ## The split hash for the 128bit hexadeximal banner.
premiumTier*: PremiumTier
premiumSubscriptionCount*: int
preferredLocale*: string
@ -175,9 +176,8 @@ proc newGuild*(json: JsonNode): Guild {.inline.} =
var g = Guild(
id: getIDFromJson(json["id"].getStr()),
name: json["name"].getStr(),
icon: json["icon"].getStr(),
splash: json["splash"].getStr(),
discoverySplash: json["discovery_splash"].getStr(),
splashRaw: splitAvatarHash(json["splash"].getStr()), # No need to remove prefixed "a_", can't be animated.
discoverySplashRaw: splitAvatarHash(json["discovery_splash"].getStr()), # No need to remove prefixed "a_", can't be animated.
ownerID: getIDFromJson(json["owner_id"].getStr()),
region: json["region"].getStr(),
afkChannelID: getIDFromJson(json["afk_channel_id"].getStr()),
@ -192,13 +192,23 @@ proc newGuild*(json: JsonNode): Guild {.inline.} =
rulesChannelID: getIDFromJson(json["rules_channel_id"].getStr()),
vanityUrlCode: json["vanity_url_code"].getStr(),
description: json["description"].getStr(),
banner: json["banner"].getStr(),
bannerRaw: splitAvatarHash(json["banner"].getStr()), # No need to remove prefixed "a_", can't be animated.
premiumTier: PremiumTier(json["premium_tier"].getInt()),
preferredLocale: json["preferred_locale"].getStr(),
publicUpdatesChannelID: getIDFromJson(json["public_updates_channel_id"].getStr())
)
# Parse all non guaranteed fields
if json.contains("icon"):
let iconStr = json["icon"].getStr()
# If the icon is animated we need to remove the prefixed "a_"
if iconStr.startsWith("a_"):
g.isIconGif = true
g.iconRaw = splitAvatarHash(iconStr.substr(2))
else:
g.isIconGif = false
g.iconRaw = splitAvatarHash(iconStr)
if json.contains("owner"):
g.owner = json["owner"].getBool()
if json.contains("permissions"):
@ -916,3 +926,96 @@ proc getGuildMemberRoles*(guild: Guild, member: GuildMember): seq[Role] =
for role in guild.roles:
if member.roles.contains(role.id):
result.add(role)
proc getGuildIconURL*(guild: Guild, imageType: ImageType = ImageType.imgTypeAuto): string =
## Get the URL for the guild's icon.
result = "https://cdn.discordapp.com/icons/" & $guild.id & "/" & $combineAvatarHash(guild.iconRaw)
# If we're finding the image type automaticly, then we need to
# check if the avatar is a gif.
var tmp = imageType
if (imageType == ImageType.imgTypeAuto):
if guild.isIconGif:
tmp = ImageType.imgTypeGif
else:
tmp = ImageType.imgTypePng
case tmp:
of ImageType.imgTypeGif:
result &= ".gif"
discard
of ImageType.imgTypeJpeg:
result &= ".jpeg"
discard
of ImageType.imgTypePng:
result &= ".png"
discard
of ImageType.imgTypeWebp:
result &= ".webp"
discard
of ImageType.imgTypeAuto:
result &= ".png" # Just incase
discard
proc getGuildSplashURL*(guild: Guild, imageType: ImageType = ImageType.imgTypePng): string =
## Get the URL for the guild's splash.
result = "https://cdn.discordapp.com/splashes/" & $guild.id & "/" & $combineAvatarHash(guild.splashRaw)
case imageType:
of ImageType.imgTypeGif:
result &= ".png" # The guild's splash can't be a gif.
discard
of ImageType.imgTypeJpeg:
result &= ".jpeg"
discard
of ImageType.imgTypePng:
result &= ".png"
discard
of ImageType.imgTypeWebp:
result &= ".webp"
discard
of ImageType.imgTypeAuto:
result &= ".png"
discard
proc getGuildDiscoverySplashURL*(guild: Guild, imageType: ImageType = ImageType.imgTypePng): string =
## Get the URL for the guild's discovery splash.
result = "https://cdn.discordapp.com/discovery-splashes/" & $guild.id & "/" & $combineAvatarHash(guild.discoverySplashRaw)
case imageType:
of ImageType.imgTypeGif:
result &= ".png" # The guild's discovery splash can't be a gif.
discard
of ImageType.imgTypeJpeg:
result &= ".jpeg"
discard
of ImageType.imgTypePng:
result &= ".png"
discard
of ImageType.imgTypeWebp:
result &= ".webp"
discard
of ImageType.imgTypeAuto:
result &= ".png" # Just incase
discard
proc getGuildBannerURL*(guild: Guild, imageType: ImageType = ImageType.imgTypePng): string =
## Get the URL for the guild's banner.
result = "https://cdn.discordapp.com/banners/" & $guild.id & "/" & $combineAvatarHash(guild.bannerRaw)
case imageType:
of ImageType.imgTypeGif:
result &= ".png" # The guild's banner can't be a gif.
discard
of ImageType.imgTypeJpeg:
result &= ".jpeg"
discard
of ImageType.imgTypePng:
result &= ".png"
discard
of ImageType.imgTypeWebp:
result &= ".webp"
discard
of ImageType.imgTypeAuto:
result &= ".png" # Just incase
discard

View File

@ -1,6 +1,13 @@
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch, strutils
from discordobject import Snowflake
type ImageType* = enum
imgTypeAuto = 0,
imgTypeWebp = 1,
imgTypePng = 2,
imgTypeJpeg = 3,
imgTypeGif = 4
proc getIDFromJson*(str: string): uint64 =
var num: uint64
discard parseBiggestUInt(str, num)
@ -17,12 +24,22 @@ proc endpoint*(url: string): string =
var globalToken*: string
proc defaultHeaders*(added: HttpHeaders = newHttpHeaders()): HttpHeaders =
# added.add("Authorization", fmt("Bot {globalToken}"))
added.add("Authorization", fmt("{globalToken}"))
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
proc splitAvatarHash*(hash: string): array[2, uint64] =
var first: uint64
discard parseBiggestUInt(hash.substr(0, 16), first)
var second: uint64
discard parseBiggestUInt(hash.substr(0, 16), first)
return [first, second]
proc combineAvatarHash*(hash: array[2, uint64]): string =
return (BiggestInt hash[0]).toHex(16) & (BiggestInt hash[1]).toHex(16)
type
RateLimitBucketType* = enum
channel,
@ -125,4 +142,4 @@ proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders
waitForRateLimits(objectID, bucketType)
let response = client.request(endpoint, httpMethod, strPayload)
return handleResponse(response, objectId, bucketType)
return handleResponse(response, objectId, bucketType)

View File

@ -1,4 +1,4 @@
import json, discordobject, nimcordutils
import json, discordobject, nimcordutils, strutils
type
NitroSubscription* = enum
@ -7,41 +7,52 @@ type
nitro = 2
User* = ref object of DiscordObject
## This type is a discord user.
## This type is any discord user.
username*: string ## The user's username, not unique across the platform.
discriminator*: cushort ## The user's 4-digit discord-tag.
avatar*: string ## The user's avatar hash.
bot*: bool ## Whether the user belongs to an OAuth2 application.
system*: bool ## Whether the user is an Official Discord System user (part of the urgent message system).
flags*: int ## The flags on a user's account.
premiumType*: NitroSubscription ## The type of Nitro subscription on a user's account.
publicFlags*: int ## The public flags on a user's account.
publicFlags*: int ## The public [flags](https://discord.com/developers/docs/resources/user#user-object-user-flags) on a user's account. (User Badges)
avatarRaw: array[2, uint64] ## The split hash for the 128bit hexadeximal avatar.
isAvatarGif: bool ## Wether the avatar is a gif.
ClientUser* = ref object of User
## This type is the clients discord user.
mfaEnabled*: bool ## Whether the user has two factor authentication enabled on their account.
locale*: string ## The user's chosen language option.
verified*: bool ## Whether or not the current user has a verified email.
email*: string ## The current user's email
premiumType*: NitroSubscription ## The type of Nitro subscription on a user's account.
flags*: int ## The [flags](https://discord.com/developers/docs/resources/user#user-object-user-flags) on a user's account.
proc newUser*(user: JsonNode): User {.inline.} =
return User(
result = User(
id: getIDFromJson(user["id"].getStr()),
username: user["username"].getStr(),
discriminator: cushort(parseIntEasy(user["discriminator"].getStr())),
avatar: user["avatar"].getStr(),
bot: user{"bot"}.getBool(),
system: user{"system"}.getBool(),
flags: user{"flags"}.getInt(),
premiumType: NitroSubscription(user{"premium_type"}.getInt()),
publicFlags: user{"public_flags"}.getInt()
)
if user.contains("avatar"):
let avatarStr = user["avatar"].getStr()
# If the avatar is animated we need to remove the prefixed "a_"
if avatarStr.startsWith("a_"):
result.isAvatarGif = true
result.avatarRaw = splitAvatarHash(avatarStr.substr(2))
else:
result.isAvatarGif = false
result.avatarRaw = splitAvatarHash(avatarStr)
proc newClientUser*(clientUser: JsonNode): ClientUser {.inline.} =
return ClientUser(
result = ClientUser(
id: getIDFromJson(clientUser["id"].getStr()),
username: clientUser["username"].getStr(),
discriminator: cushort(parseIntEasy(clientUser["discriminator"].getStr())),
avatar: clientUser["avatar"].getStr(),
bot: clientUser{"bot"}.getBool(),
system: clientUser{"system"}.getBool(),
mfaEnabled: clientUser{"mfa_enabled"}.getBool(),
@ -51,4 +62,48 @@ proc newClientUser*(clientUser: JsonNode): ClientUser {.inline.} =
flags: clientUser{"flags"}.getInt(),
premiumType: NitroSubscription(clientUser{"premium_type"}.getInt()),
publicFlags: clientUser{"public_flags"}.getInt()
)
)
if clientUser.contains("avatar"):
let avatarStr = clientUser["avatar"].getStr()
# If the avatar is animated we need to remove the prefixed "a_"
if avatarStr.startsWith("a_"):
result.isAvatarGif = true
result.avatarRaw = splitAvatarHash(avatarStr.substr(2))
else:
result.isAvatarGif = false
result.avatarRaw = splitAvatarHash(avatarStr)
proc getUserAvatarURL*(user: User, imageType: ImageType = ImageType.imgTypeAuto): string =
# If the user doesn't have an avatar, then return a default avatar url.
if user.avatarRaw.len == 0:
return "https://cdn.discordapp.com/embed/avatars/" & $(user.discriminator mod 5) & ".png"
result = "https://cdn.discordapp.com/avatars/" & $user.id & "/" & $combineAvatarHash(user.avatarRaw)
# If we're finding the image type automaticly, then we need to
# check if the avatar is a gif.
var tmp = imageType
if (imageType == ImageType.imgTypeAuto):
if user.isAvatarGif:
tmp = ImageType.imgTypeGif
else:
tmp = ImageType.imgTypePng
case tmp:
of ImageType.imgTypeGif:
result &= ".gif"
discard
of ImageType.imgTypeJpeg:
result &= ".jpeg"
discard
of ImageType.imgTypePng:
result &= ".png"
discard
of ImageType.imgTypeWebp:
result &= ".webp"
discard
of ImageType.imgTypeAuto:
result &= ".png" # Just incase
discard