Merge pull request #1 from avahe-kellenberger/master

Clean-up and Nim version fix.
This commit is contained in:
SeanOMik 2020-08-07 22:26:22 -05:00 committed by GitHub
commit 3216b4481f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 401 additions and 390 deletions

0
generate_docs.sh Normal file → Executable file
View File

View File

@ -4,4 +4,4 @@ description = "Discord API wrapper written in Nim. Inspired by DisC++, my othe
license = "MIT" license = "MIT"
srcDir = "src" srcDir = "src"
requires "nim >= 1.0.0", "websocket >= 0.4.0 & <= 0.4.1" requires "nim >= 1.2.0", "websocket >= 0.4.0 & <= 0.4.1"

View File

@ -1,17 +1,17 @@
import message, member, channel, guild, discordobject, nimcordutils, httpcore, user, tables import message, member, channel, guild, discordobject, nimcordutils, httpcore, user, tables
type Cache* = ref object type Cache* = ref object
members*: Table[snowflake, GuildMember] members*: Table[Snowflake, GuildMember]
messages*: Table[snowflake, Message] messages*: Table[Snowflake, Message]
channels*: Table[snowflake, Channel] channels*: Table[Snowflake, Channel]
guilds*: Table[snowflake, Guild] guilds*: Table[Snowflake, Guild]
proc getChannel*(cache: var Cache, id: snowflake): Channel = proc getChannel*(cache: var Cache, id: Snowflake): Channel =
## Get a channel object from the id. ## Get a channel object from the id.
## ##
## If for some reason the channel is not in cache, it gets requested via the ## If for some reason the channel is not in cache, it gets requested via the
## Discord REST API. ## Discord REST API.
if (cache.channels.hasKey(id)): if cache.channels.hasKey(id):
return cache.channels[id] return cache.channels[id]
result = newChannel(sendRequest(endpoint("/channels/" & $id), HttpGet, defaultHeaders(), result = newChannel(sendRequest(endpoint("/channels/" & $id), HttpGet, defaultHeaders(),
@ -25,12 +25,12 @@ proc getMessageChannel*(msg: Message, cache: var Cache): Channel =
## Discord REST API. ## Discord REST API.
return cache.getChannel(msg.channelID) return cache.getChannel(msg.channelID)
proc getGuild*(cache: var Cache, id: snowflake): Guild = proc getGuild*(cache: var Cache, id: Snowflake): Guild =
## Get a guild object from it's id. ## Get a guild object from it's id.
## ##
## If for some reason the guild is not in cache, it gets requested via the ## If for some reason the guild is not in cache, it gets requested via the
## Discord REST API. ## Discord REST API.
if (cache.guilds.hasKey(id)): if cache.guilds.hasKey(id):
return cache.guilds[id] return cache.guilds[id]
result = newGuild(sendRequest(endpoint("/guilds/" & $id), HttpGet, defaultHeaders(), result = newGuild(sendRequest(endpoint("/guilds/" & $id), HttpGet, defaultHeaders(),
@ -44,18 +44,18 @@ proc getChannelGuild*(channel: Channel, cache: var Cache): Guild =
## Discord REST API. ## Discord REST API.
return cache.getGuild(channel.guildID) return cache.getGuild(channel.guildID)
proc getUser*(cache: Cache, id: snowflake): User = proc getUser*(cache: Cache, id: Snowflake): User =
## Get a user object from it's id. ## Get a user object from it's id.
## ##
## If for some reason the user is not in cache, it gets requested via the ## If for some reason the user is not in cache, it gets requested via the
## Discord REST API. ## Discord REST API.
if (cache.members.hasKey(id)): if cache.members.hasKey(id):
return cache.members[id].user return cache.members[id].user
return newUser(sendRequest(endpoint("/users/" & $id), HttpGet, defaultHeaders())) return newUser(sendRequest(endpoint("/users/" & $id), HttpGet, defaultHeaders()))
proc cacheGuildChannel*(cache: var Cache, guildID: snowflake, channel: Channel) = proc cacheGuildChannel*(cache: var Cache, guildID: Snowflake, channel: Channel) =
## Adds a channel in cache.guilds[guildID].channels. ## Adds a channel in cache.guilds[guildID].channels.
## Only used for internal library, dont touch! ## Only used for internal library, dont touch!
var guild = cache.getGuild(guildID) var guild = cache.getGuild(guildID)
guild.channels.add(channel) guild.channels.add(channel)

View File

@ -14,21 +14,21 @@ type
Channel* = ref object of DiscordObject Channel* = ref object of DiscordObject
## Discord channel object. ## Discord channel object.
`type`*: ChannelType ## The type of channel. `type`*: ChannelType ## The type of channel.
guildID*: snowflake ## The id of the guild. guildID*: Snowflake ## The id of the guild.
position*: int ## Sorting position of the channel. position*: int ## Sorting position of the channel.
permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles. permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles.
name*: string ## The name of the channel (2-100 characters). name*: string ## The name of the channel (2-100 characters).
topic*: string ## The channel topic (0-1024 characters). topic*: string ## The channel topic (0-1024 characters).
nsfw*: bool ## Whether the channel is nsfw. nsfw*: bool ## Whether the channel is nsfw.
lastMessageID*: snowflake ## The id of the last message sent in this channel (may not point to an existing or valid message). lastMessageID*: Snowflake ## The id of the last message sent in this channel (may not point to an existing or valid message).
bitrate*: int ## The bitrate (in bits) of the voice channel. bitrate*: int ## The bitrate (in bits) of the voice channel.
userLimit*: int ## The user limit of the voice channel. userLimit*: int ## The user limit of the voice channel.
rateLimitPerUser*: int ## Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected. rateLimitPerUser*: int ## Amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected.
recipients*: seq[User] ## The recipients of the DM recipients*: seq[User] ## The recipients of the DM
icon*: string ## Icon hash icon*: string ## Icon hash
ownerID*: snowflake ## ID of the DM creator ownerID*: Snowflake ## ID of the DM creator
applicationID*: snowflake ## Application id of the group DM creator if it is bot-created applicationID*: Snowflake ## Application id of the group DM creator if it is bot-created
parentID*: snowflake ## ID of the parent category for a channel parentID*: Snowflake ## ID of the parent category for a channel
lastPinTimestamp*: string ## When the last pinned message was pinned lastPinTimestamp*: string ## When the last pinned message was pinned
ChannelFields* = ref object ChannelFields* = ref object
@ -41,13 +41,13 @@ type
rateLimitPerUser*: Option[int] rateLimitPerUser*: Option[int]
position*: Option[int] position*: Option[int]
permissionOverwrites*: Option[seq[Permissions]] ## Explicit permission overwrites for members and roles. permissionOverwrites*: Option[seq[Permissions]] ## Explicit permission overwrites for members and roles.
parentID*: Option[snowflake] parentID*: Option[Snowflake]
nsfw*: Option[bool] nsfw*: Option[bool]
Invite* = object Invite* = object
## Represents a code that when used, adds a user to a guild or group DM channel. ## Represents a code that when used, adds a user to a guild or group DM channel.
code*: string ## The invite code (unique ID) code*: string ## The invite code (unique ID)
guildID*: snowflake ## The guild this invite is for guildID*: Snowflake ## The guild this invite is for
channel*: Channel ## The channel this invite is for channel*: Channel ## The channel this invite is for
inviter*: User ## The user who created the invite inviter*: User ## The user who created the invite
targetUser*: User ## The target user for this invite targetUser*: User ## The target user for this invite
@ -74,39 +74,39 @@ proc newChannel*(channel: JsonNode): Channel {.inline.} =
`type`: ChannelType(channel["type"].getInt()) `type`: ChannelType(channel["type"].getInt())
) )
if (channel.contains("guild_id")): if channel.contains("guild_id"):
chan.guildID = getIDFromJson(channel["guild_id"].getStr()) chan.guildID = getIDFromJson(channel["guild_id"].getStr())
if (channel.contains("position")): if channel.contains("position"):
chan.position = channel["position"].getInt() chan.position = channel["position"].getInt()
if (channel.contains("permission_overwrites")): if channel.contains("permission_overwrites"):
for perm in channel["permission_overwrites"]: for perm in channel["permission_overwrites"]:
chan.permissionOverwrites.add(newPermissions(perm)) chan.permissionOverwrites.add(newPermissions(perm))
if (channel.contains("name")): if channel.contains("name"):
chan.name = channel["name"].getStr() chan.name = channel["name"].getStr()
if (channel.contains("topic")): if channel.contains("topic"):
chan.topic = channel["topic"].getStr() chan.topic = channel["topic"].getStr()
if (channel.contains("nsfw")): if channel.contains("nsfw"):
chan.nsfw = channel["nsfw"].getBool() chan.nsfw = channel["nsfw"].getBool()
if (channel.contains("last_message_id")): if channel.contains("last_message_id"):
chan.lastMessageID = getIDFromJson(channel["last_message_id"].getStr()) chan.lastMessageID = getIDFromJson(channel["last_message_id"].getStr())
if (channel.contains("bitrate")): if channel.contains("bitrate"):
chan.bitrate = channel["bitrate"].getInt() chan.bitrate = channel["bitrate"].getInt()
if (channel.contains("user_limit")): if channel.contains("user_limit"):
chan.userLimit = channel["user_limit"].getInt() chan.userLimit = channel["user_limit"].getInt()
if (channel.contains("rate_limit_per_user")): if channel.contains("rate_limit_per_user"):
chan.rateLimitPerUser = channel["rate_limit_per_user"].getInt() chan.rateLimitPerUser = channel["rate_limit_per_user"].getInt()
if (channel.contains("recipients")): if channel.contains("recipients"):
for recipient in channel["recipients"]: for recipient in channel["recipients"]:
chan.recipients.insert(newUser(recipient)) chan.recipients.insert(newUser(recipient))
if (channel.contains("icon")): if channel.contains("icon"):
chan.icon = channel["icon"].getStr() chan.icon = channel["icon"].getStr()
if (channel.contains("owner_id")): if channel.contains("owner_id"):
chan.ownerID = getIDFromJson(channel["owner_id"].getStr()) chan.ownerID = getIDFromJson(channel["owner_id"].getStr())
if (channel.contains("application_id")): if channel.contains("application_id"):
chan.applicationID = getIDFromJson(channel["application_id"].getStr()) chan.applicationID = getIDFromJson(channel["application_id"].getStr())
if (channel.contains("parent_id")): if channel.contains("parent_id"):
chan.parentID = getIDFromJson(channel["parent_id"].getStr()) chan.parentID = getIDFromJson(channel["parent_id"].getStr())
if (channel.contains("last_pin_timestamp")): if channel.contains("last_pin_timestamp"):
chan.lastPinTimestamp = channel["last_pin_timestamp"].getStr() chan.lastPinTimestamp = channel["last_pin_timestamp"].getStr()
return chan return chan
@ -117,23 +117,23 @@ proc newInvite*(json: JsonNode): Invite {.inline.} =
code: json["code"].getStr(), code: json["code"].getStr(),
channel: newChannel(json["channel"]) channel: newChannel(json["channel"])
) )
if (json.contains("guild")): if json.contains("guild"):
invite.guildID = getIDFromJson(json["guild"]["id"].getStr()) invite.guildID = getIDFromJson(json["guild"]["id"].getStr())
if (json.contains("target_user")): if json.contains("target_user"):
invite.targetUser = newUser(json["target_user"]) invite.targetUser = newUser(json["target_user"])
if (json.contains("approximate_presence_count")): if json.contains("approximate_presence_count"):
invite.approximatePresenceCount = json["approximate_presence_count"].getInt() invite.approximatePresenceCount = json["approximate_presence_count"].getInt()
if (json.contains("approximate_member_count")): if json.contains("approximate_member_count"):
invite.approximateMemberCount = json["approximate_member_count"].getInt() invite.approximateMemberCount = json["approximate_member_count"].getInt()
if (json.contains("uses")): if json.contains("uses"):
invite.uses = json["uses"].getInt() invite.uses = json["uses"].getInt()
if (json.contains("max_uses")): if json.contains("max_uses"):
invite.maxUsers = json["max_uses"].getInt() invite.maxUsers = json["max_uses"].getInt()
if (json.contains("max_age")): if json.contains("max_age"):
invite.maxAge = json["max_age"].getInt() invite.maxAge = json["max_age"].getInt()
if (json.contains("temporary")): if json.contains("temporary"):
invite.temporary = json["temporary"].getBool() invite.temporary = json["temporary"].getBool()
if (json.contains("created_at")): if json.contains("created_at"):
invite.createdAt = json["created_at"].getStr() invite.createdAt = json["created_at"].getStr()
return invite return invite
@ -142,10 +142,10 @@ proc sendMessage*(channel: Channel, content: string, tts: bool = false, embed: E
## Send a message through the channel. ## Send a message through the channel.
var messagePayload = %*{"content": content, "tts": tts} var messagePayload = %*{"content": content, "tts": tts}
if (not embed.isNil()): if not embed.isNil():
messagePayload.add("embed", embed.embedJson) messagePayload.add("embed", embed.embedJson)
if (files.len != 0): if files.len != 0:
var client = newHttpClient() var client = newHttpClient()
let endpoint = endpoint("/channels/" & $channel.id & "/messages") let endpoint = endpoint("/channels/" & $channel.id & "/messages")
var multipart = newMultipartData() var multipart = newMultipartData()
@ -154,7 +154,7 @@ proc sendMessage*(channel: Channel, content: string, tts: bool = false, embed: E
for index, file in files: for index, file in files:
var imageStream = newFileStream(file.filePath, fmRead) var imageStream = newFileStream(file.filePath, fmRead)
if (not isNil(imageStream)): if not isNil(imageStream):
let data = imageStream.readALL() let data = imageStream.readALL()
multipart.add("file" & $index, data, file.fileName, "application/octet-stream", false) multipart.add("file" & $index, data, file.fileName, "application/octet-stream", false)
@ -184,37 +184,37 @@ proc modifyChannel*(channel: Channel, modify: ChannelFields): Future[Channel] {.
var modifyPayload = %*{} var modifyPayload = %*{}
if (modify.name.isSome): if modify.name.isSome:
modifyPayload.add("name", %modify.name.get()) modifyPayload.add("name", %modify.name.get())
if (modify.`type`.isSome): if modify.`type`.isSome:
modifyPayload.add("type", %modify.`type`.get()) modifyPayload.add("type", %modify.`type`.get())
if (modify.position.isSome): if modify.position.isSome:
modifyPayload.add("position", %modify.position.get()) modifyPayload.add("position", %modify.position.get())
if (modify.topic.isSome): if modify.topic.isSome:
modifyPayload.add("topic", %modify.topic.get()) modifyPayload.add("topic", %modify.topic.get())
if (modify.nsfw.isSome): if modify.nsfw.isSome:
modifyPayload.add("nsfw", %modify.nsfw.get()) modifyPayload.add("nsfw", %modify.nsfw.get())
if (modify.rateLimitPerUser.isSome): if modify.rateLimitPerUser.isSome:
modifyPayload.add("rate_limit_per_user", %modify.rateLimitPerUser.get()) modifyPayload.add("rate_limit_per_user", %modify.rateLimitPerUser.get())
if (modify.bitrate.isSome): if modify.bitrate.isSome:
modifyPayload.add("bitrate", %modify.bitrate.get()) modifyPayload.add("bitrate", %modify.bitrate.get())
if (modify.userLimit.isSome): if modify.userLimit.isSome:
modifyPayload.add("user_limit", %modify.userLimit.get()) modifyPayload.add("user_limit", %modify.userLimit.get())
if (modify.permissionOverwrites.isSome): if modify.permissionOverwrites.isSome:
var permOverwrites = parseJson("[]") var permOverwrites = parseJson("[]")
for perm in modify.permissionOverwrites.get(): for perm in modify.permissionOverwrites.get():
permOverwrites.add(perm.permissionsToJson()) permOverwrites.add(perm.permissionsToJson())
modifyPayload.add("permission_overwrites", permOverwrites) modifyPayload.add("permission_overwrites", permOverwrites)
if (modify.parentID.isSome): if modify.parentID.isSome:
modifyPayload.add("parent_id", %modify.parentID.get()) modifyPayload.add("parent_id", %modify.parentID.get())
return newChannel(sendRequest(endpoint("/channels/" & $channel.id), HttpPatch, return newChannel(sendRequest(endpoint("/channels/" & $channel.id), HttpPatch,
@ -229,9 +229,9 @@ proc deleteChannel*(channel: Channel) {.async.} =
type MessagesGetRequest* = object type MessagesGetRequest* = object
## Use this type to get a channel's messages by setting some of the fields. ## Use this type to get a channel's messages by setting some of the fields.
## You can only set one of `around`, `before`, or `after`. ## You can only set one of `around`, `before`, or `after`.
around*: Option[snowflake] around*: Option[Snowflake]
before*: Option[snowflake] before*: Option[Snowflake]
after*: Option[snowflake] after*: Option[Snowflake]
limit*: Option[int] limit*: Option[int]
proc getMessages*(channel: Channel, request: MessagesGetRequest): seq[Message] = proc getMessages*(channel: Channel, request: MessagesGetRequest): seq[Message] =
@ -245,24 +245,24 @@ proc getMessages*(channel: Channel, request: MessagesGetRequest): seq[Message] =
var url: string = endpoint("/channels/" & $channel.id & "/messages?") var url: string = endpoint("/channels/" & $channel.id & "/messages?")
if (request.around.isSome): if request.around.isSome:
url = url & "around=" & $request.around.get() url = url & "around=" & $request.around.get()
# Raise some exceptions to make sure the user doesn't # Raise some exceptions to make sure the user doesn't
# try to set more than one of these fields # try to set more than one of these fields
if (request.before.isSome): if request.before.isSome:
if (request.around.isSome): if request.around.isSome:
raise newException(Defect, "You cannot get around and before a message! Choose one...") raise newException(Defect, "You cannot get around and before a message! Choose one...")
url = url & "before=" & $request.before.get() url = url & "before=" & $request.before.get()
if (request.after.isSome): if request.after.isSome:
if (request.around.isSome or request.before.isSome): if request.around.isSome or request.before.isSome:
raise newException(Defect, "You cannot get around/before and after a message! Choose one...") raise newException(Defect, "You cannot get around/before and after a message! Choose one...")
url = url & "after=" & $request.after.get() url = url & "after=" & $request.after.get()
if (request.limit.isSome): if request.limit.isSome:
# Add the `&` for the url if something else is set. # Add the `&` for the url if something else is set.
if (request.around.isSome or request.before.isSome or request.after.isSome): if request.around.isSome or request.before.isSome or request.after.isSome:
url = url & "&" url = url & "&"
url = url & "limit=" & $request.limit.get() url = url & "limit=" & $request.limit.get()
@ -273,15 +273,15 @@ proc getMessages*(channel: Channel, request: MessagesGetRequest): seq[Message] =
for message in response: for message in response:
result.add(newMessage(message)) result.add(newMessage(message))
proc getMessage*(channel: Channel, messageID: snowflake): Message = proc getMessage*(channel: Channel, messageID: Snowflake): Message =
## Requests a message from the channel via the Discord REST API. ## Requests a message from the channel via the Discord REST API.
return newMessage(sendRequest(endpoint("/channels/" & $channel.id & "/messages/" & $messageID), HttpGet, return newMessage(sendRequest(endpoint("/channels/" & $channel.id & "/messages/" & $messageID), HttpGet,
defaultHeaders(), channel.id, RateLimitBucketType.channel)) defaultHeaders(), channel.id, RateLimitBucketType.channel))
proc bulkDeleteMessages*(channel: Channel, messageIDs: seq[snowflake]) {.async.} = proc bulkDeleteMessages*(channel: Channel, messageIDs: seq[Snowflake]) {.async.} =
## Bulk delete channel messages. This endpoint can only delete 2-100 messages. ## Bulk delete channel messages. This endpoint can only delete 2-100 messages.
## This proc takes a seq[snowflakes] represtenting the message's IDs. ## This proc takes a seq[Snowflakes] represtenting the message's IDs.
## The messages can not be older than 2 weeks! ## The messages can not be older than 2 weeks!
## ##
## See also: ## See also:
@ -301,8 +301,8 @@ proc bulkDeleteMessages*(channel: Channel, messages: seq[Message]) {.async.} =
## The messages can not be older than 2 weeks! ## The messages can not be older than 2 weeks!
## ##
## See also: ## See also:
## * `bulkDeleteMessages(channel: Channel, messageIDs: seq[snowflake])`_ ## * `bulkDeleteMessages(channel: Channel, messageIDs: seq[Snowflake])`_
var messageIDs: seq[snowflake] var messageIDs: seq[Snowflake]
for msg in messages: for msg in messages:
messageIDs.add(msg.id) messageIDs.add(msg.id)
@ -329,7 +329,7 @@ type CreateInviteFields* = object
maxUses: Option[int] ## Max number of uses or 0 for unlimited maxUses: Option[int] ## Max number of uses or 0 for unlimited
temporary: Option[bool] ## Whether this invite only grants temporary membership temporary: Option[bool] ## Whether this invite only grants temporary membership
unique: Option[bool] ## If true, don't try to reuse a similar invite (useful for creating many unique one time use invites) unique: Option[bool] ## If true, don't try to reuse a similar invite (useful for creating many unique one time use invites)
targetUser: Option[snowflake] ## The target user id for this invite targetUser: Option[Snowflake] ## The target user id for this invite
targetUserType: Option[int] ## The type of target user for this invite targetUserType: Option[int] ## The type of target user for this invite
proc createChannelInvite*(channel: Channel, fields: CreateInviteFields): Invite = proc createChannelInvite*(channel: Channel, fields: CreateInviteFields): Invite =
@ -344,18 +344,18 @@ proc createChannelInvite*(channel: Channel, fields: CreateInviteFields): Invite
## channel.createChannelInvite(CreateInviteFields(maxAge: 3600, maxUses: 10)) ## channel.createChannelInvite(CreateInviteFields(maxAge: 3600, maxUses: 10))
var createPayload = %*{} var createPayload = %*{}
if (fields.maxAge.isSome): if fields.maxAge.isSome:
createPayload.add("max_age", %fields.maxAge.get()) createPayload.add("max_age", %fields.maxAge.get())
if (fields.maxUses.isSome): if fields.maxUses.isSome:
createPayload.add("max_uses", %fields.maxUses.get()) createPayload.add("max_uses", %fields.maxUses.get())
if (fields.temporary.isSome): if fields.temporary.isSome:
createPayload.add("temporary", %fields.temporary.get()) createPayload.add("temporary", %fields.temporary.get())
if (fields.unique.isSome): if fields.unique.isSome:
createPayload.add("unique", %fields.unique.get()) createPayload.add("unique", %fields.unique.get())
if (fields.targetUser.isSome): if fields.targetUser.isSome:
createPayload.add("target_user", %fields.targetUser.get()) createPayload.add("target_user", %fields.targetUser.get())
# Not sure if its needed because it can only be `1` # Not sure if its needed because it can only be `1`
#[ if (fields.targetUserType.isSome): #[ if fields.targetUserType.isSome:
createPayload.add("target_user_type", %fields.targetUserType.get()) ]# createPayload.add("target_user_type", %fields.targetUserType.get()) ]#
return newInvite(sendRequest(endpoint("/channels/" & $channel.id & "/invites"), HttpPost, return newInvite(sendRequest(endpoint("/channels/" & $channel.id & "/invites"), HttpPost,
@ -391,4 +391,4 @@ proc groupDMAddRecipient*(channel: Channel, user: User, accessToken: string, nic
proc groupDMRemoveRecipient*(channel: Channel, user: User) {.async.} = proc groupDMRemoveRecipient*(channel: Channel, user: User) {.async.} =
## Removes a recipient from a Group DM. ## Removes a recipient from a Group DM.
discard sendRequest(endpoint("/channels/" & $channel.id & "/recipients/" & $user.id), discard sendRequest(endpoint("/channels/" & $channel.id & "/recipients/" & $user.id),
HttpPut, defaultHeaders(), channel.id, RateLimitBucketType.channel) HttpPut, defaultHeaders(), channel.id, RateLimitBucketType.channel)

View File

@ -1,6 +1,6 @@
import websocket, asyncdispatch, json, httpClient, eventdispatcher, strformat import websocket, asyncdispatch, json, httpClient, eventdispatcher, strformat
import eventhandler, streams, nimcordutils, discordobject, user, cache, clientobjects import nimcordutils, cache, clientobjects
import strutils, channel, options, message, emoji, guild, embed, os, presence import strutils, options, presence
type type
DiscordOpCode = enum DiscordOpCode = enum
@ -16,10 +16,23 @@ type
opHello = 10, opHello = 10,
opHeartbeatAck = 11 opHeartbeatAck = 11
# Forward declarations
proc closeConnection*(shard: Shard, code: int = 1000) {.async.}
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 newShard(shardID: int, client: DiscordClient): Shard
proc reconnectShard(shard: Shard) {.async.}
proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.async.}
proc startConnection*(client: DiscordClient, shardAmount: int = 1) {.async.}
proc updateClientPresence*(shard: Shard, presence: Presence) {.async.}
proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.async.} = proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.async.} =
## Send a gateway request. ## Send a gateway request.
## Don't use this unless you know what you're doing! ## Don't use this unless you know what you're doing!
if (msg.len == 0): if msg.len == 0:
echo "Sending gateway payload: ", request echo "Sending gateway payload: ", request
else: else:
echo msg echo msg
@ -29,7 +42,7 @@ proc sendGatewayRequest*(shard: Shard, request: JsonNode, msg: string = "") {.as
proc handleHeartbeat(shard: Shard) {.async.} = proc handleHeartbeat(shard: Shard) {.async.} =
while true: while true:
var heartbeatPayload: JsonNode var heartbeatPayload: JsonNode
if (shard.lastSequence == 0): if shard.lastSequence == 0:
heartbeatPayload = %* { "d": nil, "op": ord(DiscordOpCode.opHeartbeat) } heartbeatPayload = %* { "d": nil, "op": ord(DiscordOpCode.opHeartbeat) }
else: else:
heartbeatPayload = %* { "d": shard.lastSequence, "op": ord(DiscordOpCode.opHeartbeat) } heartbeatPayload = %* { "d": shard.lastSequence, "op": ord(DiscordOpCode.opHeartbeat) }
@ -53,12 +66,9 @@ proc getIdentifyPacket(shard: Shard): JsonNode =
} }
} }
if (shard.client.shardCount != -1): if shard.client.shardCount != -1:
result.add("shard", %*[shard.id, shard.client.shardCount]) result.add("shard", %*[shard.id, shard.client.shardCount])
# For some reason this shows as an error in VSCode, but it compiles fine.
#proc startConnection*(client: DiscordClient, shardCount: int = 1) {.async.}
proc closeConnection*(shard: Shard, code: int = 1000) {.async.} = proc closeConnection*(shard: Shard, code: int = 1000) {.async.} =
echo "Disconnecting with code: ", code echo "Disconnecting with code: ", code
await shard.ws.close(code) await shard.ws.close(code)
@ -73,7 +83,7 @@ proc reconnectShard(shard: Shard) {.async.} =
shard.reconnecting = false shard.reconnecting = false
shard.heartbeatAcked = true shard.heartbeatAcked = true
#waitFor client.startConnection() # waitFor client.startConnection()
# Handle discord disconnect. If it detects that we can reconnect, it will. # Handle discord disconnect. If it detects that we can reconnect, it will.
proc handleGatewayDisconnect(shard: Shard, error: string) {.async.} = proc handleGatewayDisconnect(shard: Shard, error: string) {.async.} =
@ -87,21 +97,20 @@ proc handleGatewayDisconnect(shard: Shard, error: string) {.async.} =
let c = disconnectData.code let c = disconnectData.code
# 4003, 4004, 4005, 4007, 4010, 4011, 4012, 4013 are not reconnectable. # 4003, 4004, 4005, 4007, 4010, 4011, 4012, 4013 are not reconnectable.
if ( (c >= 4003 and c <= 4005) or c == 4007 or (c >= 4010 and c <= 4013) ): if (c >= 4003 and c <= 4005) or c == 4007 or (c >= 4010 and c <= 4013):
echo "The Discord gateway sent a disconnect code that we cannot reconnect to." echo "The Discord gateway sent a disconnect code that we cannot reconnect to."
else: else:
if (not shard.reconnecting): if not shard.reconnecting:
waitFor shard.reconnectShard() waitFor shard.reconnectShard()
else: else:
echo "Gateway is cannot reconnect due to already reconnecting..." echo "Gateway is cannot reconnect due to already reconnecting..."
#TODO: Reconnecting may be done, just needs testing. #TODO: Reconnecting may be done, just needs testing.
proc handleWebsocketPacket(shard: Shard) {.async.} = proc handleWebsocketPacket(shard: Shard) {.async.} =
while true: while true:
var packet: tuple[opcode: Opcode, data: string] var packet: tuple[opcode: Opcode, data: string]
packet = await shard.ws.readData(); packet = await shard.ws.readData()
echo "[SHARD ", $shard.id, "] Received gateway payload: ", packet.data echo "[SHARD ", $shard.id, "] Received gateway payload: ", packet.data
if packet.opcode == Opcode.Close: if packet.opcode == Opcode.Close:
@ -111,12 +120,12 @@ proc handleWebsocketPacket(shard: Shard) {.async.} =
# If we fail to parse the json just stop this loop # If we fail to parse the json just stop this loop
try: try:
json = parseJson(packet.data); json = parseJson(packet.data)
except: except:
echo "Failed to parse websocket payload: ", packet.data echo "Failed to parse websocket payload: ", packet.data
continue continue
if (json.contains("s")): if json.contains("s"):
shard.lastSequence = json["s"].getInt() shard.lastSequence = json["s"].getInt()
case json["op"].getInt() case json["op"].getInt()
@ -173,7 +182,7 @@ proc startConnection*(client: DiscordClient, shardAmount: int = 1) {.async.} =
## .. code-block:: nim ## .. code-block:: nim
## var tokenStream = newFileStream("token.txt", fmRead) ## var tokenStream = newFileStream("token.txt", fmRead)
## var tkn: string ## var tkn: string
## if (not isNil(tokenStream)): ## if not isNil(tokenStream):
## discard tokenStream.readLine(tkn) ## discard tokenStream.readLine(tkn)
## echo "Read token from the file: ", tkn ## echo "Read token from the file: ", tkn
## ##
@ -182,13 +191,14 @@ proc startConnection*(client: DiscordClient, shardAmount: int = 1) {.async.} =
## var bot = newDiscordClient(tkn) ## var bot = newDiscordClient(tkn)
echo "Connecting..." echo "Connecting..."
let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, defaultHeaders()) # let urlResult = sendRequest(endpoint("/gateway/bot"), HttpMethod.HttpGet, defaultHeaders())
if (urlResult.contains("url")): let urlResult = sendRequest(endpoint("/gateway"), HttpMethod.HttpGet, defaultHeaders())
if urlResult.contains("url"):
let url = urlResult["url"].getStr() let url = urlResult["url"].getStr()
client.endpoint = url client.endpoint = url
var shardCount = shardAmount var shardCount = shardAmount
if (shardCount < urlResult["shards"].getInt()): if urlResult.hasKey("shards") and shardCount < urlResult["shards"].getInt():
shardCount = urlResult["shards"].getInt() shardCount = urlResult["shards"].getInt()
client.shardCount = shardCount client.shardCount = shardCount
@ -246,4 +256,4 @@ proc newDiscordClient*(tkn: string): DiscordClient =
var cac: Cache var cac: Cache
new(cac) new(cac)
result = DiscordClient(token: tkn, cache: cac) result = DiscordClient(token: tkn, cache: cac)

View File

@ -1,7 +1,7 @@
type type
snowflake* = uint64 Snowflake* = uint64
DiscordObject* = object of RootObj DiscordObject* = object of RootObj
id*: snowflake id*: Snowflake
proc `==`*(obj1: DiscordObject, obj2: DiscordObject): bool = proc `==`*(obj1: DiscordObject, obj2: DiscordObject): bool =
return obj1.id == obj2.id return obj1.id == obj2.id

View File

@ -12,10 +12,10 @@ proc setTitle*(embed: var Embed, title: string) =
## ##
## Contstraints: ## Contstraints:
## * `title` must be set and cannot be larger than 256 characters. ## * `title` must be set and cannot be larger than 256 characters.
if (title.len < 0 or title.len > 256): if title.len < 0 or title.len > 256:
raise newException(EmbedFieldException, "Embed title can only be 0-256 characters") raise newException(EmbedFieldException, "Embed title can only be 0-256 characters")
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("title", %title) embed.embedJson.add("title", %title)
@ -25,17 +25,17 @@ proc setDescription*(embed: var Embed, description: string) =
## ##
## Contstraints: ## Contstraints:
## * `description` must be set and cannot be larger than 2048 characters. ## * `description` must be set and cannot be larger than 2048 characters.
if (description.len < 0 or description.len > 2048): if description.len < 0 or description.len > 2048:
raise newException(EmbedFieldException, "Embed description can only be 0-2048 characters") raise newException(EmbedFieldException, "Embed description can only be 0-2048 characters")
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("description", %description) embed.embedJson.add("description", %description)
proc setURL*(embed: var Embed, url: string) = proc setURL*(embed: var Embed, url: string) =
## Set the url of the embed. ## Set the url of the embed.
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("url", %url) embed.embedJson.add("url", %url)
@ -43,14 +43,14 @@ proc setURL*(embed: var Embed, url: string) =
proc setTimestamp*(embed: var Embed, timestamp: string) = proc setTimestamp*(embed: var Embed, timestamp: string) =
## Set the timestamp of the embed. ## Set the timestamp of the embed.
## The timestamp is in `ISO8601` format. ## The timestamp is in `ISO8601` format.
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("timestamp", %timestamp) embed.embedJson.add("timestamp", %timestamp)
proc setColor*(embed: var Embed, color: uint) = proc setColor*(embed: var Embed, color: uint) =
## Set the color of the embed. ## Set the color of the embed.
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("color", %color) embed.embedJson.add("color", %color)
@ -62,7 +62,7 @@ proc setFooter*(embed: var Embed, text: string, iconURL: string = "", proxyIconU
## ##
## Contstraints: ## Contstraints:
## * `text` must be set and cannot be larger than 2048 characters. ## * `text` must be set and cannot be larger than 2048 characters.
if (text.len < 0 or text.len > 2048): if text.len < 0 or text.len > 2048:
raise newException(EmbedFieldException, "Embed's footer text can only be 0-2048 characters") raise newException(EmbedFieldException, "Embed's footer text can only be 0-2048 characters")
let footer = %* { let footer = %* {
@ -71,7 +71,7 @@ proc setFooter*(embed: var Embed, text: string, iconURL: string = "", proxyIconU
"proxy_icon_url": proxyIconURL "proxy_icon_url": proxyIconURL
} }
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("footer", footer) embed.embedJson.add("footer", footer)
@ -84,12 +84,12 @@ proc setImage*(embed: var Embed, url: string, proxyIconURL: string = "", height:
"proxy_icon_url": proxyIconURL "proxy_icon_url": proxyIconURL
} }
if (height != -1): if height != -1:
image.add("height", %height) image.add("height", %height)
if (width != -1): if width != -1:
image.add("width", %width) image.add("width", %width)
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("image", image) embed.embedJson.add("image", image)
@ -102,12 +102,12 @@ proc setThumbnail*(embed: var Embed, url: string, proxyIconURL: string = "", hei
"proxy_icon_url": proxyIconURL "proxy_icon_url": proxyIconURL
} }
if (height != -1): if height != -1:
thumbnail.add("height", %height) thumbnail.add("height", %height)
if (width != -1): if width != -1:
thumbnail.add("width", %width) thumbnail.add("width", %width)
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("thumbnail", thumbnail) embed.embedJson.add("thumbnail", thumbnail)
@ -118,12 +118,12 @@ proc setVideo*(embed: var Embed, url: string, height: int = -1, width: int = -1)
"url": url "url": url
} }
if (height != -1): if height != -1:
video.add("height", %height) video.add("height", %height)
if (width != -1): if width != -1:
video.add("width", %width) video.add("width", %width)
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("video", video) embed.embedJson.add("video", video)
@ -135,7 +135,7 @@ proc setProvider*(embed: var Embed, name: string, url: string = "") =
"url": url "url": url
} }
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("provider", provider) embed.embedJson.add("provider", provider)
@ -146,7 +146,7 @@ proc setAuthor*(embed: var Embed, name: string, url: string = "", iconURL: strin
## ##
## Contstraints: ## Contstraints:
## * `name` cannot be larger than 256 characters ## * `name` cannot be larger than 256 characters
if (name.len < 0 or name.len > 256): if name.len < 0 or name.len > 256:
raise newException(EmbedFieldException, "Embed's author name can only be 0-256 characters") raise newException(EmbedFieldException, "Embed's author name can only be 0-256 characters")
let author = %* { let author = %* {
@ -156,7 +156,7 @@ proc setAuthor*(embed: var Embed, name: string, url: string = "", iconURL: strin
"proxy_icon_url": proxyIconURL "proxy_icon_url": proxyIconURL
} }
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
embed.embedJson.add("author", author) embed.embedJson.add("author", author)
@ -167,11 +167,11 @@ proc addField*(embed: var Embed, name: string, value: string, inline: bool = fal
## Contstraints: ## Contstraints:
## * `name` must be set and cannot be larger than 256 characters ## * `name` must be set and cannot be larger than 256 characters
## * `value` must be set and cannot be larger than 1024 characters ## * `value` must be set and cannot be larger than 1024 characters
if (name.len == 0 or value.len == 0): if name.len == 0 or value.len == 0:
raise newException(EmbedFieldException, "Embed's field name or values must be set") raise newException(EmbedFieldException, "Embed's field name or values must be set")
elif (name.len > 256): elif name.len > 256:
raise newException(EmbedFieldException, "Embed's field name can only be 0-256 characters") raise newException(EmbedFieldException, "Embed's field name can only be 0-256 characters")
elif (value.len > 1024): elif value.len > 1024:
raise newException(EmbedFieldException, "Embed's field value can only be 0-1024 characters") raise newException(EmbedFieldException, "Embed's field value can only be 0-1024 characters")
let field = %* { let field = %* {
@ -180,12 +180,12 @@ proc addField*(embed: var Embed, name: string, value: string, inline: bool = fal
"inline": inline "inline": inline
} }
if (embed.embedJson.isNil()): if embed.embedJson.isNil():
embed.embedJson = %*{} embed.embedJson = %*{}
if embed.embedJson.contains("fields"): if embed.embedJson.contains("fields"):
if (embed.embedJson["fields"].len > 25): if embed.embedJson["fields"].len > 25:
raise newException(EmbedFieldException, "Embeds can only have upto 25 fields") raise newException(EmbedFieldException, "Embeds can only have upto 25 fields")
embed.embedJson["fields"].add(field) embed.embedJson["fields"].add(field)
else: else:
embed.embedJson.add("fields", %*[field]) embed.embedJson.add("fields", %*[field])

View File

@ -3,15 +3,15 @@ import json, discordobject, nimcordutils, user, httpcore, strutils, uri, strform
type type
Emoji* = ref object of DiscordObject Emoji* = ref object of DiscordObject
name*: string name*: string
roles*: seq[snowflake] roles*: seq[Snowflake]
user*: User user*: User
requireColons*: bool requireColons*: bool
managed*: bool managed*: bool
animated*: bool animated*: bool
available*: bool available*: bool
guildID*: snowflake guildID*: Snowflake
proc newEmoji*(json: JsonNode, guild: snowflake): Emoji = proc newEmoji*(json: JsonNode, guild: Snowflake): Emoji =
## Construct an emoji with json. ## Construct an emoji with json.
## This shouldn't really be used by the user, only internal use. ## This shouldn't really be used by the user, only internal use.
result = Emoji( result = Emoji(
@ -19,23 +19,23 @@ proc newEmoji*(json: JsonNode, guild: snowflake): Emoji =
guildID: guild guildID: guild
) )
if (json.contains("id")): if json.contains("id"):
result.id = getIDFromJson(json["id"].getStr()) result.id = getIDFromJson(json["id"].getStr())
if (json.contains("roles")): if json.contains("roles"):
for role in json["roles"]: for role in json["roles"]:
result.roles.add(getIDFromJson(role.getStr())) result.roles.add(getIDFromJson(role.getStr()))
if (json.contains("user")): if json.contains("user"):
result.user = newUser(json["user"]) result.user = newUser(json["user"])
if (json.contains("require_colons")): if json.contains("require_colons"):
result.requireColons = json["require_colons"].getBool() result.requireColons = json["require_colons"].getBool()
if (json.contains("managed")): if json.contains("managed"):
result.managed = json["managed"].getBool() result.managed = json["managed"].getBool()
if (json.contains("animated")): if json.contains("animated"):
result.requireColons = json["animated"].getBool() result.requireColons = json["animated"].getBool()
if (json.contains("available")): if json.contains("available"):
result.requireColons = json["available"].getBool() result.requireColons = json["available"].getBool()
proc newEmoji*(name: string, id: snowflake): Emoji = proc newEmoji*(name: string, id: Snowflake): Emoji =
## Construct an emoji using its name, and id. ## Construct an emoji using its name, and id.
return Emoji(name: name, id: id) return Emoji(name: name, id: id)
@ -49,7 +49,7 @@ proc `$`*(emoji: Emoji): string =
# Check if the emoji has a name but not id. # Check if the emoji has a name but not id.
# If its true, this means that the emoji is just a unicode # If its true, this means that the emoji is just a unicode
# representation of the emoji. # representation of the emoji.
if (emoji.id == 0 and not emoji.name.isEmptyOrWhitespace()): if emoji.id == 0 and not emoji.name.isEmptyOrWhitespace():
return emoji.name return emoji.name
else: else:
result = $emoji.id & ":" & emoji.name result = $emoji.id & ":" & emoji.name
@ -61,13 +61,13 @@ proc `$`*(emoji: Emoji): string =
proc `==`*(a: Emoji, b: Emoji): bool = proc `==`*(a: Emoji, b: Emoji): bool =
## Check if two Emojis are equal. ## Check if two Emojis are equal.
# Check if emojis have name but no id # Check if emojis have name but no id
if (a.id == 0 and b.id == 0 and a.name.isEmptyOrWhitespace() and b.name.isEmptyOrWhitespace()): if a.id == 0 and b.id == 0 and a.name.isEmptyOrWhitespace() and b.name.isEmptyOrWhitespace():
return a.name == b.name return a.name == b.name
# Check if emoji has IDs, but no name # Check if emoji has IDs, but no name
elif (a.id != 0 and b.id != 0 and a.name.isEmptyOrWhitespace() and b.name.isEmptyOrWhitespace()): elif a.id != 0 and b.id != 0 and a.name.isEmptyOrWhitespace() and b.name.isEmptyOrWhitespace():
return a.id == b.id return a.id == b.id
# Check if emoji has IDs, and a name # Check if emoji has IDs, and a name
elif (a.id != 0 and b.id != 0 and not a.name.isEmptyOrWhitespace() and not b.name.isEmptyOrWhitespace()): elif a.id != 0 and b.id != 0 and not a.name.isEmptyOrWhitespace() and not b.name.isEmptyOrWhitespace():
return $a == $b return $a == $b
return false return false
@ -77,7 +77,7 @@ proc toUrlEncoding*(emoji: Emoji): string =
## library use. ## library use.
return encodeUrl($emoji, true) return encodeUrl($emoji, true)
proc modifyEmoji*(emoji: var Emoji, name: string, roles: seq[snowflake] = @[]): Future[Emoji] {.async.} = proc modifyEmoji*(emoji: var Emoji, name: string, roles: seq[Snowflake]): Future[Emoji] {.async.} =
## Modify the given emoji. Requires the `MANAGE_EMOJIS` permission. ## Modify the given emoji. Requires the `MANAGE_EMOJIS` permission.
## Changes will be reflected in given `emoji`. ## Changes will be reflected in given `emoji`.
var jsonBody = %* { var jsonBody = %* {
@ -95,4 +95,4 @@ proc modifyEmoji*(emoji: var Emoji, name: string, roles: seq[snowflake] = @[]):
proc deleteEmoji*(emoji: Emoji) {.async.} = proc deleteEmoji*(emoji: Emoji) {.async.} =
## Delete the given emoji. Requires the `MANAGE_EMOJIS` permission. ## Delete the given emoji. Requires the `MANAGE_EMOJIS` permission.
discard sendRequest(endpoint(fmt("/guilds/{emoji.guildID}/emojis/{emoji.id}")), HttpDelete, discard sendRequest(endpoint(fmt("/guilds/{emoji.guildID}/emojis/{emoji.id}")), HttpDelete,
defaultHeaders(), emoji.guildID, RateLimitBucketType.guild) defaultHeaders(), emoji.guildID, RateLimitBucketType.guild)

View File

@ -1,6 +1,6 @@
import eventhandler, json, tables, message, emoji, user, member, role import eventhandler, json, tables, message, emoji, user, member, role
import guild, channel, nimcordutils, httpClient, strformat, cache import guild, channel, nimcordutils, httpClient, strformat, cache
import sequtils, asyncdispatch, clientobjects, discordobject, presence import asyncdispatch, clientobjects, discordobject, presence
proc readyEvent(shard: Shard, json: JsonNode) = proc readyEvent(shard: Shard, json: JsonNode) =
var readyEvent = ReadyEvent(shard: shard, readyPayload: json, name: $EventType.evtReady) var readyEvent = ReadyEvent(shard: shard, readyPayload: json, name: $EventType.evtReady)
@ -26,7 +26,7 @@ proc channelCreateEvent(shard: Shard, json: JsonNode) =
let channelCreateEvent = ChannelCreateEvent(shard: shard, channel: chnl, name: $EventType.evtChannelCreate) let channelCreateEvent = ChannelCreateEvent(shard: shard, channel: chnl, name: $EventType.evtChannelCreate)
# Add the channel to its guild's `channels` field # Add the channel to its guild's `channels` field
if (chnl.guildID != 0): if chnl.guildID != 0:
shard.client.cache.cacheGuildChannel(chnl.guildID, chnl) shard.client.cache.cacheGuildChannel(chnl.guildID, chnl)
shard.client.cache.channels[chnl.id] = chnl shard.client.cache.channels[chnl.id] = chnl
@ -38,15 +38,15 @@ proc channelUpdateEvent(shard: Shard, json: JsonNode) =
shard.client.cache.channels[chnl.id] = chnl shard.client.cache.channels[chnl.id] = chnl
if (chnl.guildID != 0): if chnl.guildID != 0:
let g = shard.client.cache.getGuild(chnl.guildID) let g = shard.client.cache.getGuild(chnl.guildID)
var index = -1 var index = -1
for i, channel in g.channels: for i, channel in g.channels:
if (channel.id == chnl.id): if channel.id == chnl.id:
index = i index = i
if (index != -1): if index != -1:
g.channels[index] = chnl g.channels[index] = chnl
else: else:
g.channels.add(chnl) g.channels.add(chnl)
@ -68,7 +68,7 @@ proc channelPinsUpdate(shard: Shard, json: JsonNode) =
let channelID = getIDFromJson(json["channel_id"].getStr()) let channelID = getIDFromJson(json["channel_id"].getStr())
var channel: Channel var channel: Channel
if (shard.client.cache.channels.hasKey(channelID)): if shard.client.cache.channels.hasKey(channelID):
channel = shard.client.cache.channels[channelID] channel = shard.client.cache.channels[channelID]
channel.lastPinTimestamp = json["last_pin_timestamp"].getStr() channel.lastPinTimestamp = json["last_pin_timestamp"].getStr()
@ -194,15 +194,15 @@ proc guildMembersChunk(shard: Shard, json: JsonNode) =
event.chunkIndex = json["chunk_index"].getInt() event.chunkIndex = json["chunk_index"].getInt()
event.chunkCount = json["chunk_count"].getInt() event.chunkCount = json["chunk_count"].getInt()
if (json.contains("not_found")): if json.contains("not_found"):
for id in json["not_found"]: for id in json["not_found"]:
event.notFound.add(getIDFromJson(id.getStr())) event.notFound.add(getIDFromJson(id.getStr()))
if (json.contains("presences")): if json.contains("presences"):
for presence in json["presences"]: for presence in json["presences"]:
event.presences.add(newPresence(presence)) event.presences.add(newPresence(presence))
if (json.contains("nonce")): if json.contains("nonce"):
event.nonce = json["nonce"].getStr() event.nonce = json["nonce"].getStr()
dispatchEvent(event) dispatchEvent(event)
@ -252,7 +252,7 @@ proc inviteCreate(shard: Shard, json: JsonNode) =
invite.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr())) invite.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
if (json.contains("guild_id")): if json.contains("guild_id"):
invite.guildID =getIDFromJson(json["guild_id"].getStr()) invite.guildID =getIDFromJson(json["guild_id"].getStr())
var event = InviteCreateEvent(shard: shard, invite: invite, name: $EventType.evtInviteCreate) var event = InviteCreateEvent(shard: shard, invite: invite, name: $EventType.evtInviteCreate)
@ -264,7 +264,7 @@ proc inviteDelete(shard: Shard, json: JsonNode) =
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr())) event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
event.code = json["code"].getStr() event.code = json["code"].getStr()
if (json.contains("guild_id")): if json.contains("guild_id"):
event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr())) event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
dispatchEvent(event) dispatchEvent(event)
@ -295,7 +295,7 @@ proc messageDeleteEvent(shard: Shard, json: JsonNode) =
msg = Message(id: msgID) msg = Message(id: msgID)
msg.channelID = getIDFromJson(json["channel_id"].getStr()) msg.channelID = getIDFromJson(json["channel_id"].getStr())
if (json.contains("guild_id")): if json.contains("guild_id"):
msg.guildID = getIDFromJson(json["guild_id"].getStr()) msg.guildID = getIDFromJson(json["guild_id"].getStr())
let event = MessageDeleteEvent(shard: shard, message: msg, name: $EventType.evtMessageDelete) let event = MessageDeleteEvent(shard: shard, message: msg, name: $EventType.evtMessageDelete)
@ -305,7 +305,7 @@ proc messageDeleteBulkEvent(shard: Shard, json: JsonNode) =
var event = MessageDeleteBulkEvent(shard: shard, name: $EventType.evtMessageDeleteBulk) var event = MessageDeleteBulkEvent(shard: shard, name: $EventType.evtMessageDeleteBulk)
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr())) event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
if (json.contains("guild_id")): if json.contains("guild_id"):
event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr())) event.guild = shard.client.cache.getGuild(getIDFromJson(json["guild_id"].getStr()))
for msgIDJson in json["ids"]: for msgIDJson in json["ids"]:
@ -319,7 +319,7 @@ proc messageDeleteBulkEvent(shard: Shard, json: JsonNode) =
msg = Message(id: msgID, channelID: channelID) msg = Message(id: msgID, channelID: channelID)
event.channel = shard.client.cache.getChannel(msg.channelID) event.channel = shard.client.cache.getChannel(msg.channelID)
if (json.contains("guild_id")): if json.contains("guild_id"):
msg.guildID = getIDFromJson(json["guild_id"].getStr()) msg.guildID = getIDFromJson(json["guild_id"].getStr())
event.messages.add(msg) event.messages.add(msg)
@ -337,12 +337,12 @@ proc messageReactionAdd(shard: Shard, json: JsonNode) =
msg = Message(id: msgID) msg = Message(id: msgID)
msg.channelID = getIDFromJson(json["channel_id"].getStr()) msg.channelID = getIDFromJson(json["channel_id"].getStr())
if (json.contains("guild_id")): if json.contains("guild_id"):
msg.guildID = getIDFromJson(json["guild_id"].getStr()) msg.guildID = getIDFromJson(json["guild_id"].getStr())
event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr())) event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr()))
if (json.contains("member")): if json.contains("member"):
event.member = newGuildMember(json["member"], msg.guildID) event.member = newGuildMember(json["member"], msg.guildID)
event.emoji = newEmoji(json["emoji"], msg.guildID) event.emoji = newEmoji(json["emoji"], msg.guildID)
@ -360,7 +360,7 @@ proc messageReactionRemove(shard: Shard, json: JsonNode) =
msg = Message(id: msgID) msg = Message(id: msgID)
msg.channelID = getIDFromJson(json["channel_id"].getStr()) msg.channelID = getIDFromJson(json["channel_id"].getStr())
if (json.contains("guild_id")): if json.contains("guild_id"):
msg.guildID = getIDFromJson(json["guild_id"].getStr()) msg.guildID = getIDFromJson(json["guild_id"].getStr())
event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr())) event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr()))
@ -380,7 +380,7 @@ proc messageReactionRemoveAll(shard: Shard, json: JsonNode) =
msg = Message(id: msgID) msg = Message(id: msgID)
msg.channelID = getIDFromJson(json["channel_id"].getStr()) msg.channelID = getIDFromJson(json["channel_id"].getStr())
if (json.contains("guild_id")): if json.contains("guild_id"):
msg.guildID = getIDFromJson(json["guild_id"].getStr()) msg.guildID = getIDFromJson(json["guild_id"].getStr())
dispatchEvent(event) dispatchEvent(event)
@ -396,7 +396,7 @@ proc messageReactionRemoveEmoji(shard: Shard, json: JsonNode) =
msg = Message(id: msgID) msg = Message(id: msgID)
msg.channelID = getIDFromJson(json["channel_id"].getStr()) msg.channelID = getIDFromJson(json["channel_id"].getStr())
if (json.contains("guild_id")): if json.contains("guild_id"):
msg.guildID = getIDFromJson(json["guild_id"].getStr()) msg.guildID = getIDFromJson(json["guild_id"].getStr())
event.emoji = newEmoji(json["emoji"], msg.guildID) event.emoji = newEmoji(json["emoji"], msg.guildID)
@ -414,9 +414,9 @@ proc presenceUpdate(shard: Shard, json: JsonNode) =
for role in json["roles"]: for role in json["roles"]:
member.roles.add(getIDFromJson(role.getStr())) member.roles.add(getIDFromJson(role.getStr()))
if (json.contains("premium_since")): if json.contains("premium_since"):
member.premiumSince = json["premium_since"].getStr() member.premiumSince = json["premium_since"].getStr()
if (json.contains("nick")): if json.contains("nick"):
member.nick = json["nick"].getStr() member.nick = json["nick"].getStr()
member.presence = newPresence(json) member.presence = newPresence(json)
@ -426,12 +426,12 @@ proc typingStart(shard: Shard, json: JsonNode) =
event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr())) event.channel = shard.client.cache.getChannel(getIDFromJson(json["channel_id"].getStr()))
if (json.contains("guild_id")): if json.contains("guild_id"):
event.channel.guildID = getIDFromJson(json["guild_id"].getStr()) event.channel.guildID = getIDFromJson(json["guild_id"].getStr())
event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr())) event.user = shard.client.cache.getUser(getIDFromJson(json["user_id"].getStr()))
if (json.contains("member")): if json.contains("member"):
event.member = newGuildMember(json["member"], event.channel.guildID) event.member = newGuildMember(json["member"], event.channel.guildID)
dispatchEvent(event) dispatchEvent(event)
@ -504,9 +504,9 @@ let internalEventTable: Table[string, proc(shard: Shard, json: JsonNode) {.nimca
proc handleDiscordEvent*(shard: Shard, json: JsonNode, eventName: string) {.async.} = proc handleDiscordEvent*(shard: Shard, json: JsonNode, eventName: string) {.async.} =
## Handles, and dispatches, a gateway event. Only used internally. ## Handles, and dispatches, a gateway event. Only used internally.
if (internalEventTable.hasKey(eventName)): if internalEventTable.hasKey(eventName):
let eventProc: proc(shard: Shard, json: JsonNode) = internalEventTable[eventName] let eventProc: proc(shard: Shard, json: JsonNode) = internalEventTable[eventName]
eventProc(shard, json) eventProc(shard, json)
else: else:
echo "Failed to find event: ", eventName echo "Failed to find event: ", eventName

View File

@ -130,7 +130,7 @@ type
members*: seq[GuildMember] members*: seq[GuildMember]
chunkIndex*: int chunkIndex*: int
chunkCount*: int chunkCount*: int
notFound*: seq[snowflake] notFound*: seq[Snowflake]
presences*: seq[Presence] presences*: seq[Presence]
nonce*: string nonce*: string
@ -252,7 +252,7 @@ proc registerEventListener*(event: EventType, listener: proc(event: BaseEvent))
## echo "ID: ", bot.clientUser.id ## echo "ID: ", bot.clientUser.id
## echo "--------------------" ## echo "--------------------"
## ) ## )
if (eventListeners.hasKey($event)): if eventListeners.hasKey($event):
eventListeners[$event].add(cast[proc(event: BaseEvent)](listener)) eventListeners[$event].add(cast[proc(event: BaseEvent)](listener))
echo "Added other event listener: ", $event echo "Added other event listener: ", $event
@ -264,10 +264,10 @@ proc registerEventListener*(event: EventType, listener: proc(event: BaseEvent))
proc dispatchEvent*[T: BaseEvent](event: T) = proc dispatchEvent*[T: BaseEvent](event: T) =
## Dispatches an event so something can listen to it. ## Dispatches an event so something can listen to it.
if (eventListeners.hasKey(event.name)): if eventListeners.hasKey(event.name):
let listeners = eventListeners[event.name] let listeners = eventListeners[event.name]
echo "Dispatching event: ", event.name echo "Dispatching event: ", event.name
for index, eventListener in listeners.pairs: for index, eventListener in listeners.pairs:
eventListener(event) eventListener(event)
else: else:
echo "No event listeners for event: ", event.name echo "No event listeners for event: ", event.name

View File

@ -53,9 +53,9 @@ type
VoiceState* = ref object VoiceState* = ref object
## Used to represent a user's voice connection status ## Used to represent a user's voice connection status
guildID*: snowflake guildID*: Snowflake
channelID*: snowflake channelID*: Snowflake
userID*: snowflake userID*: Snowflake
member*: GuildMember member*: GuildMember
sessionID*: string sessionID*: string
deaf*: bool deaf*: bool
@ -72,10 +72,10 @@ type
splash*: string splash*: string
discoverySplash*: string discoverySplash*: string
owner*: bool owner*: bool
ownerID: snowflake ownerID: Snowflake
permissions*: Permissions permissions*: Permissions
region*: string region*: string
afkChannelID*: snowflake afkChannelID*: Snowflake
afkTimeout*: int afkTimeout*: int
verificationLevel*: VerificationLevel verificationLevel*: VerificationLevel
defaultMessageNotifications*: MessageNotificationsLevel defaultMessageNotifications*: MessageNotificationsLevel
@ -84,12 +84,12 @@ type
emojis*: seq[Emoji] emojis*: seq[Emoji]
features*: seq[string] features*: seq[string]
mfaLevel*: MFALevel mfaLevel*: MFALevel
applicationID*: snowflake applicationID*: Snowflake
widgetEnabled*: bool widgetEnabled*: bool
widgetChannelID*: snowflake widgetChannelID*: Snowflake
systemChannelID*: snowflake systemChannelID*: Snowflake
systemChannelFlags*: int systemChannelFlags*: int
rulesChannelID*: snowflake rulesChannelID*: Snowflake
joinedAt*: string joinedAt*: string
large*: bool large*: bool
unavailable*: bool unavailable*: bool
@ -106,7 +106,7 @@ type
premiumTier*: PremiumTier premiumTier*: PremiumTier
premiumSubscriptionCount*: int premiumSubscriptionCount*: int
preferredLocale*: string preferredLocale*: string
publicUpdatesChannelID*: snowflake publicUpdatesChannelID*: Snowflake
maxVideoChannelUsers*: int maxVideoChannelUsers*: int
approximateMemberCount*: int approximateMemberCount*: int
approximatePresenceCount*: int approximatePresenceCount*: int
@ -150,7 +150,7 @@ type
`type`*: string ## Integration type (twitch, youtube, etc) `type`*: string ## Integration type (twitch, youtube, etc)
enabled*: bool enabled*: bool
syncing*: bool syncing*: bool
roleID*: snowflake roleID*: Snowflake
enableEmoticons*: bool enableEmoticons*: bool
expireBehavior*: IntegrationExpireBehavior expireBehavior*: IntegrationExpireBehavior
expireGracePeriod*: int expireGracePeriod*: int
@ -160,7 +160,7 @@ type
GuildWidget* = ref object GuildWidget* = ref object
enabled*: bool enabled*: bool
channelID*: snowflake channelID*: Snowflake
GuildWidgetStyle* = enum GuildWidgetStyle* = enum
guildWidgetStyleShield = "shield", guildWidgetStyleShield = "shield",
@ -199,9 +199,9 @@ proc newGuild*(json: JsonNode): Guild {.inline.} =
) )
# Parse all non guaranteed fields # Parse all non guaranteed fields
if (json.contains("owner")): if json.contains("owner"):
g.owner = json["owner"].getBool() g.owner = json["owner"].getBool()
if (json.contains("permissions")): if json.contains("permissions"):
g.permissions = newPermissions(json["permissions"]) g.permissions = newPermissions(json["permissions"])
for role in json["roles"]: for role in json["roles"]:
g.roles.add(newRole(role, g.id)) g.roles.add(newRole(role, g.id))
@ -209,17 +209,17 @@ proc newGuild*(json: JsonNode): Guild {.inline.} =
g.emojis.add(newEmoji(emoji, g.id)) g.emojis.add(newEmoji(emoji, g.id))
for feature in json["features"]: for feature in json["features"]:
g.features.add(feature.getStr()) g.features.add(feature.getStr())
if (json.contains("widget_enabled")): if json.contains("widget_enabled"):
g.widgetEnabled = json["widget_enabled"].getBool() g.widgetEnabled = json["widget_enabled"].getBool()
if (json.contains("widget_channel_id")): if json.contains("widget_channel_id"):
g.widgetChannelID = getIDFromJson(json["widget_channel_id"].getStr()) g.widgetChannelID = getIDFromJson(json["widget_channel_id"].getStr())
if (json.contains("large")): if json.contains("large"):
g.large = json["large"].getBool() g.large = json["large"].getBool()
if (json.contains("unavailable")): if json.contains("unavailable"):
g.unavailable = json["unavailable"].getBool() g.unavailable = json["unavailable"].getBool()
if (json.contains("member_count")): if json.contains("member_count"):
g.memberCount = json["member_count"].getInt() g.memberCount = json["member_count"].getInt()
if (json.contains("voice_states")): if json.contains("voice_states"):
for voicestate in json["voice_states"]: for voicestate in json["voice_states"]:
var state = VoiceState( var state = VoiceState(
guildID: g.id, guildID: g.id,
@ -234,47 +234,47 @@ proc newGuild*(json: JsonNode): Guild {.inline.} =
suppress: voicestate["suppress"].getBool() suppress: voicestate["suppress"].getBool()
) )
if (voicestate.contains("member")): if voicestate.contains("member"):
state.member = newGuildMember(voicestate["member"], g.id) state.member = newGuildMember(voicestate["member"], g.id)
g.voiceStates.add(state) g.voiceStates.add(state)
if (json.contains("members")): if json.contains("members"):
for member in json["members"]: for member in json["members"]:
g.members.insert(newGuildMember(member, g.id)) g.members.insert(newGuildMember(member, g.id))
if (json.contains("channels")): if json.contains("channels"):
for channel in json["channels"]: for channel in json["channels"]:
g.channels.insert(newChannel(channel)) g.channels.insert(newChannel(channel))
if (json.contains("presences")): if json.contains("presences"):
# Parse all presences # Parse all presences
var tmpPresences = initTable[snowflake, Presence]() var tmpPresences = initTable[Snowflake, Presence]()
for presence in json["presences"]: for presence in json["presences"]:
tmpPresences.add(getIDFromJson(presence["user"]["id"].getStr()), newPresence(presence)) tmpPresences.add(getIDFromJson(presence["user"]["id"].getStr()), newPresence(presence))
# Check if the `tmpPresences` variable has a presence for the member, # Check if the `tmpPresences` variable has a presence for the member,
# if it does, then update the member to include its presence. # if it does, then update the member to include its presence.
for member in g.members: for member in g.members:
if (tmpPresences.hasKey(member.user.id)): if tmpPresences.hasKey(member.user.id):
member.presence = tmpPresences[member.user.id] member.presence = tmpPresences[member.user.id]
if (json.contains("max_presences")): if json.contains("max_presences"):
g.maxPresences = json["max_presences"].getInt() g.maxPresences = json["max_presences"].getInt()
if (json.contains("max_members")): if json.contains("max_members"):
g.maxMembers = json["max_members"].getInt() g.maxMembers = json["max_members"].getInt()
if (json.contains("premium_subscription_count")): if json.contains("premium_subscription_count"):
g.premiumSubscriptionCount = json["premium_subscription_count"].getInt() g.premiumSubscriptionCount = json["premium_subscription_count"].getInt()
if (json.contains("max_video_channel_users")): if json.contains("max_video_channel_users"):
g.maxVideoChannelUsers = json["max_video_channel_users"].getInt() g.maxVideoChannelUsers = json["max_video_channel_users"].getInt()
if (json.contains("approximate_member_count")): if json.contains("approximate_member_count"):
g.approximateMemberCount = json["approximate_member_count"].getInt() g.approximateMemberCount = json["approximate_member_count"].getInt()
if (json.contains("approximate_presence_count")): if json.contains("approximate_presence_count"):
g.approximatePresenceCount = json["approximate_presence_count"].getInt() g.approximatePresenceCount = json["approximate_presence_count"].getInt()
return g return g
proc createGuild*(name: string, region: Option[string], icon: Option[string], verificationLevel: Option[VerificationLevel], proc createGuild*(name: string, region: Option[string], icon: Option[string], verificationLevel: Option[VerificationLevel],
defaultMessageNotifications: Option[MessageNotificationsLevel], explicitContentFilter: Option[ExplicitContentFilterLevel], defaultMessageNotifications: Option[MessageNotificationsLevel], explicitContentFilter: Option[ExplicitContentFilterLevel],
roles: Option[seq[Role]], channels: Option[seq[Channel]], afkChannelID: Option[snowflake], afkTimeout: Option[int], roles: Option[seq[Role]], channels: Option[seq[Channel]], afkChannelID: Option[Snowflake], afkTimeout: Option[int],
systemChannelID: Option[snowflake]): Guild = systemChannelID: Option[Snowflake]): Guild =
## Create a new guild. ## Create a new guild.
## ##
## Some restraints/notes for this endpoint: ## Some restraints/notes for this endpoint:
@ -296,17 +296,17 @@ proc createGuild*(name: string, region: Option[string], icon: Option[string], ve
var json = %* {"name": name} var json = %* {"name": name}
if (region.isSome): if region.isSome:
json.add("region", %region.get()) json.add("region", %region.get())
if (icon.isSome): if icon.isSome:
json.add("icon", %icon.get()) json.add("icon", %icon.get())
if (verificationLevel.isSome): if verificationLevel.isSome:
json.add("verification_level", %ord(verificationLevel.get())) json.add("verification_level", %ord(verificationLevel.get()))
if (defaultMessageNotifications.isSome): if defaultMessageNotifications.isSome:
json.add("default_message_notifications", %ord(defaultMessageNotifications.get())) json.add("default_message_notifications", %ord(defaultMessageNotifications.get()))
if (explicitContentFilter.isSome): if explicitContentFilter.isSome:
json.add("explicit_content_filter", %ord(explicitContentFilter.get())) json.add("explicit_content_filter", %ord(explicitContentFilter.get()))
if (roles.isSome): if roles.isSome:
#json.add("verification_level", %ord(verificationLevel.get())) #json.add("verification_level", %ord(verificationLevel.get()))
var rolesJson = parseJson("[]") var rolesJson = parseJson("[]")
for role in roles.get(): for role in roles.get():
@ -322,7 +322,7 @@ proc createGuild*(name: string, region: Option[string], icon: Option[string], ve
rolesJson.add(roleJson) rolesJson.add(roleJson)
json.add("channels", rolesJson) json.add("channels", rolesJson)
if (channels.isSome): if channels.isSome:
var channelsJson = parseJson("[]") var channelsJson = parseJson("[]")
for channel in channels.get(): for channel in channels.get():
var channelJson = %*{ var channelJson = %*{
@ -336,7 +336,7 @@ proc createGuild*(name: string, region: Option[string], icon: Option[string], ve
"parent_id": channel.parentID "parent_id": channel.parentID
} }
if (channel.permissionOverwrites.len != 0): if channel.permissionOverwrites.len != 0:
channelsJson.add("permission_overwrites", parseJson("[]")) channelsJson.add("permission_overwrites", parseJson("[]"))
for permOverwrite in channel.permissionOverwrites: for permOverwrite in channel.permissionOverwrites:
channelsJson["permission_overwrites"].add(permOverwrite.permissionsToJson()) channelsJson["permission_overwrites"].add(permOverwrite.permissionsToJson())
@ -344,11 +344,11 @@ proc createGuild*(name: string, region: Option[string], icon: Option[string], ve
channelsJson.add(channelJson) channelsJson.add(channelJson)
json.add("channels", channelsJson) json.add("channels", channelsJson)
if (afkChannelID.isSome): if afkChannelID.isSome:
json.add("afk_channel_id", %ord(afkChannelID.get())) json.add("afk_channel_id", %ord(afkChannelID.get()))
if (afkTimeout.isSome): if afkTimeout.isSome:
json.add("afk_timeout", %ord(afkTimeout.get())) json.add("afk_timeout", %ord(afkTimeout.get()))
if (systemChannelID.isSome): if systemChannelID.isSome:
json.add("system_channel_id", %ord(systemChannelID.get())) json.add("system_channel_id", %ord(systemChannelID.get()))
return newGuild(sendRequest(endpoint("/guilds"), HttpPost, return newGuild(sendRequest(endpoint("/guilds"), HttpPost,
@ -386,15 +386,15 @@ type GuildModify* = ref object
verificationLevel*: Option[VerificationLevel] verificationLevel*: Option[VerificationLevel]
defaultMessageNotifications*: Option[MessageNotificationsLevel] defaultMessageNotifications*: Option[MessageNotificationsLevel]
explicitContentFilter*: Option[ExplicitContentFilterLevel] explicitContentFilter*: Option[ExplicitContentFilterLevel]
afkChannelID*: Option[snowflake] afkChannelID*: Option[Snowflake]
afkTimeout*: Option[int] afkTimeout*: Option[int]
icon*: Option[Image] icon*: Option[Image]
ownerID*: Option[snowflake] ownerID*: Option[Snowflake]
splash*: Option[Image] splash*: Option[Image]
banner*: Option[Image] banner*: Option[Image]
systemChannelID*: Option[snowflake] systemChannelID*: Option[Snowflake]
rulesChannelID*: Option[snowflake] rulesChannelID*: Option[Snowflake]
publicUpdatesChannelID*: Option[snowflake] publicUpdatesChannelID*: Option[Snowflake]
preferredLocale*: Option[string] preferredLocale*: Option[string]
proc modifyGuild*(guild: Guild, modify: GuildModify): Future[Guild] {.async.} = proc modifyGuild*(guild: Guild, modify: GuildModify): Future[Guild] {.async.} =
@ -408,49 +408,49 @@ proc modifyGuild*(guild: Guild, modify: GuildModify): Future[Guild] {.async.} =
var modifyPayload = %*{} var modifyPayload = %*{}
if (modify.name.isSome): if modify.name.isSome:
modifyPayload.add("name", %modify.name.get()) modifyPayload.add("name", %modify.name.get())
if (modify.region.isSome): if modify.region.isSome:
modifyPayload.add("region", %modify.region.get()) modifyPayload.add("region", %modify.region.get())
if (modify.verificationLevel.isSome): if modify.verificationLevel.isSome:
modifyPayload.add("verification_level", %modify.verificationLevel.get()) modifyPayload.add("verification_level", %modify.verificationLevel.get())
if (modify.defaultMessageNotifications.isSome): if modify.defaultMessageNotifications.isSome:
modifyPayload.add("default_message_notifications", %modify.defaultMessageNotifications.get()) modifyPayload.add("default_message_notifications", %modify.defaultMessageNotifications.get())
if (modify.explicitContentFilter.isSome): if modify.explicitContentFilter.isSome:
modifyPayload.add("explicit_content_filter", %modify.explicitContentFilter.get()) modifyPayload.add("explicit_content_filter", %modify.explicitContentFilter.get())
if (modify.afkChannelID.isSome): if modify.afkChannelID.isSome:
modifyPayload.add("afk_channel_id", %modify.afkChannelID.get()) modifyPayload.add("afk_channel_id", %modify.afkChannelID.get())
if (modify.afkTimeout.isSome): if modify.afkTimeout.isSome:
modifyPayload.add("afk_timeout", %modify.afkTimeout.get()) modifyPayload.add("afk_timeout", %modify.afkTimeout.get())
if (modify.icon.isSome): if modify.icon.isSome:
modifyPayload.add("icon", %modify.icon.get().imageToDataURI()) modifyPayload.add("icon", %modify.icon.get().imageToDataURI())
if (modify.ownerID.isSome): if modify.ownerID.isSome:
modifyPayload.add("owner_id", %modify.ownerID.get()) modifyPayload.add("owner_id", %modify.ownerID.get())
if (modify.splash.isSome): if modify.splash.isSome:
modifyPayload.add("splash", %modify.splash.get().imageToDataURI()) modifyPayload.add("splash", %modify.splash.get().imageToDataURI())
if (modify.banner.isSome): if modify.banner.isSome:
modifyPayload.add("banner", %modify.banner.get().imageToDataURI()) modifyPayload.add("banner", %modify.banner.get().imageToDataURI())
if (modify.systemChannelID.isSome): if modify.systemChannelID.isSome:
modifyPayload.add("system_channel_id", %modify.systemChannelID.get()) modifyPayload.add("system_channel_id", %modify.systemChannelID.get())
if (modify.rulesChannelID.isSome): if modify.rulesChannelID.isSome:
modifyPayload.add("rules_channel_id", %modify.rulesChannelID.get()) modifyPayload.add("rules_channel_id", %modify.rulesChannelID.get())
if (modify.publicUpdatesChannelID.isSome): if modify.publicUpdatesChannelID.isSome:
modifyPayload.add("public_updates_channel_id", %modify.publicUpdatesChannelID.get()) modifyPayload.add("public_updates_channel_id", %modify.publicUpdatesChannelID.get())
if (modify.preferredLocale.isSome): if modify.preferredLocale.isSome:
modifyPayload.add("preferred_locale", %modify.preferredLocale.get()) modifyPayload.add("preferred_locale", %modify.preferredLocale.get())
return newGuild(sendRequest(endpoint("/guilds/" & $guild.id), HttpPatch, return newGuild(sendRequest(endpoint("/guilds/" & $guild.id), HttpPatch,
@ -488,33 +488,33 @@ proc createGuildChannel*(guild: var Guild, create: ChannelFields): Future[Channe
var createPayload = %*{} var createPayload = %*{}
# Make sure that the name is supplied since its required for this endpoint. # Make sure that the name is supplied since its required for this endpoint.
if (create.name.isSome): if create.name.isSome:
createPayload.add("name", %create.name.get()) createPayload.add("name", %create.name.get())
else: else:
raise newException(Defect, "You must have a channel name when creating it!") raise newException(Defect, "You must have a channel name when creating it!")
if (create.`type`.isSome): if create.`type`.isSome:
createPayload.add("type", %create.`type`.get()) createPayload.add("type", %create.`type`.get())
if (create.position.isSome): if create.position.isSome:
createPayload.add("position", %create.position.get()) createPayload.add("position", %create.position.get())
if (create.topic.isSome): if create.topic.isSome:
createPayload.add("topic", %create.topic.get()) createPayload.add("topic", %create.topic.get())
if (create.nsfw.isSome): if create.nsfw.isSome:
createPayload.add("nsfw", %create.nsfw.get()) createPayload.add("nsfw", %create.nsfw.get())
if (create.rateLimitPerUser.isSome): if create.rateLimitPerUser.isSome:
createPayload.add("rate_limit_per_user", %create.rateLimitPerUser.get()) createPayload.add("rate_limit_per_user", %create.rateLimitPerUser.get())
if (create.bitrate.isSome): if create.bitrate.isSome:
createPayload.add("bitrate", %create.bitrate.get()) createPayload.add("bitrate", %create.bitrate.get())
if (create.userLimit.isSome): if create.userLimit.isSome:
createPayload.add("user_limit", %create.userLimit.get()) createPayload.add("user_limit", %create.userLimit.get())
if (create.permissionOverwrites.isSome): if create.permissionOverwrites.isSome:
var permOverwrites = parseJson("[]") var permOverwrites = parseJson("[]")
for perm in create.permissionOverwrites.get(): for perm in create.permissionOverwrites.get():
permOverwrites.add(perm.permissionsToJson()) permOverwrites.add(perm.permissionsToJson())
@ -536,7 +536,7 @@ proc modifyGuildChannelPositions*(guild: var Guild, channels: seq[Channel]) {.as
defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})), defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})),
guild.id, RateLimitBucketType.guild, jsonBody) guild.id, RateLimitBucketType.guild, jsonBody)
proc getGuildMember*(guild: var Guild, memberID: snowflake): GuildMember = proc getGuildMember*(guild: var Guild, memberID: Snowflake): GuildMember =
## Get a guild member. ## Get a guild member.
## This first checks `guild.members`, but if it doesn't exist there, ## This first checks `guild.members`, but if it doesn't exist there,
## it will be requested from Discord's REST API. ## it will be requested from Discord's REST API.
@ -544,7 +544,7 @@ proc getGuildMember*(guild: var Guild, memberID: snowflake): GuildMember =
## If we end up requesting one, it will add it to `guild.members` ## If we end up requesting one, it will add it to `guild.members`
for member in guild.members: for member in guild.members:
if (member.id == memberID): if member.id == memberID:
return member return member
result = newGuildMember(sendRequest(endpoint(fmt("/guilds/{guild.id}/members/{memberID}")), result = newGuildMember(sendRequest(endpoint(fmt("/guilds/{guild.id}/members/{memberID}")),
@ -576,7 +576,7 @@ proc getGuildBans*(guild: Guild): seq[GuildBan] =
user: newUser(json["user"]) user: newUser(json["user"])
)) ))
proc getGuildBan*(guild: Guild, userID: snowflake): GuildBan = proc getGuildBan*(guild: Guild, userID: Snowflake): GuildBan =
## Returns a ban object for the given user or nil if the ban cannot be found. ## Returns a ban object for the given user or nil if the ban cannot be found.
## Requires the BAN_MEMBERS permission. ## Requires the BAN_MEMBERS permission.
let response = sendRequest(endpoint(fmt("/guilds/{guild.id}/bans{userID}")), HttpGet, let response = sendRequest(endpoint(fmt("/guilds/{guild.id}/bans{userID}")), HttpGet,
@ -590,22 +590,22 @@ proc getGuildBan*(guild: Guild, userID: snowflake): GuildBan =
else: else:
return nil return nil
proc banGuildMember*(guild: Guild, userID: snowflake, reason: Option[string] = none(string), deleteMessageDays: Option[int] = none(int)) {.async.} = proc banGuildMember*(guild: Guild, userID: Snowflake, reason: Option[string] = none(string), deleteMessageDays: Option[int] = none(int)) {.async.} =
## Create a guild ban, and optionally delete previous messages sent by the ## Create a guild ban, and optionally delete previous messages sent by the
## banned user. Requires the BAN_MEMBERS permission. ## banned user. Requires the BAN_MEMBERS permission.
var jsonBody: JsonNode var jsonBody: JsonNode
if (reason.isSome): if reason.isSome:
jsonBody.add("reason", %reason.get()) jsonBody.add("reason", %reason.get())
if (deleteMessageDays.isSome): if deleteMessageDays.isSome:
jsonBody.add("deleteMessageDays", %deleteMessageDays.get()) jsonBody.add("deleteMessageDays", %deleteMessageDays.get())
discard sendRequest(endpoint(fmt("/guilds/{guild.id}/bans/{userID}")), HttpPut, discard sendRequest(endpoint(fmt("/guilds/{guild.id}/bans/{userID}")), HttpPut,
defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})), defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})),
guild.id, RateLimitBucketType.guild, jsonBody) guild.id, RateLimitBucketType.guild, jsonBody)
proc unbanGuildMember*(guild: Guild, userID: snowflake) {.async.} = proc unbanGuildMember*(guild: Guild, userID: Snowflake) {.async.} =
## Remove the ban for a user. Requires the BAN_MEMBERS permissions. ## Remove the ban for a user. Requires the BAN_MEMBERS permissions.
discard sendRequest(endpoint(fmt("/guilds/{guild.id}/bans/{userID}")), HttpDelete, discard sendRequest(endpoint(fmt("/guilds/{guild.id}/bans/{userID}")), HttpDelete,
defaultHeaders(), guild.id, RateLimitBucketType.guild) defaultHeaders(), guild.id, RateLimitBucketType.guild)
@ -634,19 +634,19 @@ proc createGuildRole*(guild: Guild, name: Option[string] = none(string), permiss
var jsonBody: JsonNode var jsonBody: JsonNode
if (name.isSome): if name.isSome:
jsonBody.add("name", %name) jsonBody.add("name", %name)
if (permissions.isSome): if permissions.isSome:
jsonBody.add("permissions", %permissions.get().allowPerms) jsonBody.add("permissions", %permissions.get().allowPerms)
if (color.isSome): if color.isSome:
jsonBody.add("color", %color) jsonBody.add("color", %color)
if (hoist.isSome): if hoist.isSome:
jsonBody.add("hoist", %hoist) jsonBody.add("hoist", %hoist)
if (mentionable.isSome): if mentionable.isSome:
jsonBody.add("mentionable", %mentionable) jsonBody.add("mentionable", %mentionable)
return newRole(sendRequest(endpoint(fmt("/guilds/{guild.id}/roles")), HttpPost, return newRole(sendRequest(endpoint(fmt("/guilds/{guild.id}/roles")), HttpPost,
@ -665,7 +665,7 @@ proc modifyGuildRolePositions*(guild: var Guild, roles: seq[Role]) {.async.} =
defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})), defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})),
guild.id, RateLimitBucketType.guild, jsonBody) guild.id, RateLimitBucketType.guild, jsonBody)
proc getGuildPruneCount*(guild: Guild, days: int = 7, includedRoles: seq[snowflake] = @[]): int = proc getGuildPruneCount*(guild: Guild, days: int = 7, includedRoles: seq[Snowflake]): int =
## Returns the number of members that would be removed in a prune operation. ## Returns the number of members that would be removed in a prune operation.
## Requires the `KICK_MEMBERS` permission. ## Requires the `KICK_MEMBERS` permission.
## ##
@ -687,7 +687,7 @@ proc getGuildPruneCount*(guild: Guild, days: int = 7, includedRoles: seq[snowfla
let jsonBody = sendRequest(url, HttpGet, defaultHeaders(), guild.id, RateLimitBucketType.guild) let jsonBody = sendRequest(url, HttpGet, defaultHeaders(), guild.id, RateLimitBucketType.guild)
return jsonBody["pruned"].getInt() return jsonBody["pruned"].getInt()
proc beginGuildPrune*(guild: Guild, days: int = 7, computePruneCount: bool = false, includedRoles: seq[snowflake] = @[]): Future[Option[int]] {.async.} = proc beginGuildPrune*(guild: Guild, days: int = 7, computePruneCount: bool = false, includedRoles: seq[Snowflake]): Future[Option[int]] {.async.} =
## Returns the number of members that would be removed in a prune operation. ## Returns the number of members that would be removed in a prune operation.
## Requires the `KICK_MEMBERS` permission. ## Requires the `KICK_MEMBERS` permission.
## ##
@ -792,15 +792,15 @@ proc modifyGuildIntegration*(guild: Guild, integration: var Integration,
## discard integration.modifyGuildIntegration(enable_emoticons = true) ## discard integration.modifyGuildIntegration(enable_emoticons = true)
var modifyPayload = %*{} var modifyPayload = %*{}
if (expireBehavior.isSome): if expireBehavior.isSome:
modifyPayload.add("expire_behavior", %expireBehavior.get()) modifyPayload.add("expire_behavior", %expireBehavior.get())
integration.expireBehavior = (expireBehavior.get()) integration.expireBehavior = (expireBehavior.get())
if (expireGracePeriod.isSome): if expireGracePeriod.isSome:
modifyPayload.add("expire_grace_period", %expireGracePeriod.get()) modifyPayload.add("expire_grace_period", %expireGracePeriod.get())
integration.expireGracePeriod = expireGracePeriod.get() integration.expireGracePeriod = expireGracePeriod.get()
if (enableEmoticons.isSome): if enableEmoticons.isSome:
modifyPayload.add("enable_emoticons", %enableEmoticons.get()) modifyPayload.add("enable_emoticons", %enableEmoticons.get())
integration.enableEmoticons = enableEmoticons.get() integration.enableEmoticons = enableEmoticons.get()
@ -827,7 +827,7 @@ proc getGuildWidget*(guild: Guild): GuildWidget =
return GuildWidget(enabled: jsonBody["enabled"].getBool(), return GuildWidget(enabled: jsonBody["enabled"].getBool(),
channelID: getIDFromJson(jsonBody{"channel_id"}.getStr())) channelID: getIDFromJson(jsonBody{"channel_id"}.getStr()))
proc modifyGuildWidget*(guild: Guild, widget: var GuildWidget, enabled: bool, channelID: snowflake) {.async.} = proc modifyGuildWidget*(guild: Guild, widget: var GuildWidget, enabled: bool, channelID: Snowflake) {.async.} =
## Modify a guild widget object for the guild. Requires the `MANAGE_GUILD` permission. ## Modify a guild widget object for the guild. Requires the `MANAGE_GUILD` permission.
widget.enabled = enabled widget.enabled = enabled
widget.channelID = channelID widget.channelID = channelID
@ -863,16 +863,16 @@ proc getGuildWidgetImage*(guild: Guild, style: GuildWidgetStyle): string =
## and a "JOIN MY SERVER" button at the bottom. [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4) ## and a "JOIN MY SERVER" button at the bottom. [Example](https://discord.com/api/guilds/81384788765712384/widget.png?style=banner4)
result = fmt("guilds/{guild.id}/widget.png") result = fmt("guilds/{guild.id}/widget.png")
case (style) case style
of (GuildWidgetStyle.guildWidgetStyleShield): of GuildWidgetStyle.guildWidgetStyleShield:
result &= "?style=shield" result &= "?style=shield"
of (GuildWidgetStyle.guildWidgetStyleBanner1): of GuildWidgetStyle.guildWidgetStyleBanner1:
result &= "?style=banner1" result &= "?style=banner1"
of (GuildWidgetStyle.guildWidgetStyleBanner2): of GuildWidgetStyle.guildWidgetStyleBanner2:
result &= "?style=banner2" result &= "?style=banner2"
of (GuildWidgetStyle.guildWidgetStyleBanner3): of GuildWidgetStyle.guildWidgetStyleBanner3:
result &= "?style=banner3" result &= "?style=banner3"
of (GuildWidgetStyle.guildWidgetStyleBanner4): of GuildWidgetStyle.guildWidgetStyleBanner4:
result &= "?style=banner4" result &= "?style=banner4"
proc requestEmojis*(guild: Guild): seq[Emoji] = proc requestEmojis*(guild: Guild): seq[Emoji] =
@ -887,7 +887,7 @@ proc requestEmojis*(guild: Guild): seq[Emoji] =
result.add(newEmoji(emoji, guild.id)) result.add(newEmoji(emoji, guild.id))
guild.emojis = result guild.emojis = result
proc getEmoji*(guild: Guild, emojiID: snowflake): Emoji = proc getEmoji*(guild: Guild, emojiID: Snowflake): Emoji =
## Returns a guild's emoji with `emojiID`. ## Returns a guild's emoji with `emojiID`.
## If the emoji isn't found in `guild.emojis` then it will request on ## If the emoji isn't found in `guild.emojis` then it will request on
## from the Discord REST API. ## from the Discord REST API.
@ -898,7 +898,7 @@ proc getEmoji*(guild: Guild, emojiID: snowflake): Emoji =
return newEmoji(sendRequest(endpoint("/guilds/{guild.id}/emojis/{emojiID}"), HttpGet, return newEmoji(sendRequest(endpoint("/guilds/{guild.id}/emojis/{emojiID}"), HttpGet,
defaultHeaders(), guild.id, RateLimitBucketType.guild), guild.id) defaultHeaders(), guild.id, RateLimitBucketType.guild), guild.id)
proc createEmoji*(guild: Guild, name: string, image: Image, roles: seq[snowflake] = @[]): Future[Emoji] {.async.} = proc createEmoji*(guild: Guild, name: string, image: Image, roles: seq[Snowflake]): Future[Emoji] {.async.} =
## Create a new emoji for the guild. Requires the `MANAGE_EMOJIS` permission. ## Create a new emoji for the guild. Requires the `MANAGE_EMOJIS` permission.
var jsonBody = %* { var jsonBody = %* {
"name": name, "name": name,
@ -914,5 +914,5 @@ proc createEmoji*(guild: Guild, name: string, image: Image, roles: seq[snowflake
proc getGuildMemberRoles*(guild: Guild, member: GuildMember): seq[Role] = proc getGuildMemberRoles*(guild: Guild, member: GuildMember): seq[Role] =
## Get the role objects for a member's roles. ## Get the role objects for a member's roles.
for role in guild.roles: for role in guild.roles:
if (member.roles.contains(role.id)): if member.roles.contains(role.id):
result.add(role) result.add(role)

View File

@ -9,7 +9,7 @@ proc newImage*(filepath: string): Image =
## Reads from a file that exists at `filepath`. It reads the image data, ## Reads from a file that exists at `filepath`. It reads the image data,
## and image extension for later use. ## and image extension for later use.
var imageStream = newFileStream(filepath, fmRead) var imageStream = newFileStream(filepath, fmRead)
if (not isNil(imageStream)): if not isNil(imageStream):
let data = imageStream.readALL() let data = imageStream.readALL()
# Get the file's extension and remove the `.` from the start of it # Get the file's extension and remove the `.` from the start of it
@ -24,4 +24,4 @@ proc newImage*(filepath: string): Image =
raise newException(IOError, "Failed to open file: " & filepath) raise newException(IOError, "Failed to open file: " & filepath)
proc imageToDataURI*(image: Image): string = proc imageToDataURI*(image: Image): string =
return fmt("data:image/{image.extension};base64,{image.base64Encoded}") return fmt("data:image/{image.extension};base64,{image.base64Encoded}")

View File

@ -25,7 +25,7 @@ proc newLog*(flags: int, filePath: string = ""): Log =
var log = Log(flags: flags) var log = Log(flags: flags)
if filePath.len > 0: if filePath.len > 0:
log.logFile = newFileStream(filePath, fmWrite) log.logFile = newFileStream(filePath, fmWrite)
if (isNil(log.logFile)): if isNil(log.logFile):
raise newException(IOError, "Failed to open log file: " & filePath) raise newException(IOError, "Failed to open log file: " & filePath)
return log return log
@ -38,18 +38,18 @@ proc canLog(log: Log, sev: LogSeverity): bool =
elif (log.flags and int(LoggerFlags.loggerFlagDebugSeverity)) == int(LoggerFlags.loggerFlagDebugSeverity): elif (log.flags and int(LoggerFlags.loggerFlagDebugSeverity)) == int(LoggerFlags.loggerFlagDebugSeverity):
return true return true
case (sev) case sev
of LogSeverity.logSevInfo: of LogSeverity.logSevInfo:
return (log.flags and int(LoggerFlags.loggerFlagInfoSeverity)) == int(LoggerFlags.loggerFlagInfoSeverity); return (log.flags and int(LoggerFlags.loggerFlagInfoSeverity)) == int(LoggerFlags.loggerFlagInfoSeverity)
of LogSeverity.logSevWarn: of LogSeverity.logSevWarn:
return (log.flags and int(LoggerFlags.loggerFlagWarnSeverity)) == int(LoggerFlags.loggerFlagWarnSeverity); return (log.flags and int(LoggerFlags.loggerFlagWarnSeverity)) == int(LoggerFlags.loggerFlagWarnSeverity)
of LogSeverity.logSevError: of LogSeverity.logSevError:
return (log.flags and int(LoggerFlags.loggerFlagErrorSeverity)) == int(LoggerFlags.loggerFlagErrorSeverity); return (log.flags and int(LoggerFlags.loggerFlagErrorSeverity)) == int(LoggerFlags.loggerFlagErrorSeverity)
else: else:
return false; return false
proc severityToString(sev: LogSeverity): string = proc severityToString(sev: LogSeverity): string =
case (sev) case sev
of LogSeverity.logSevInfo: of LogSeverity.logSevInfo:
return "INFO" return "INFO"
of LogSeverity.logSevWarn: of LogSeverity.logSevWarn:
@ -61,7 +61,7 @@ proc severityToString(sev: LogSeverity): string =
#TODO: Remove colors from file. #TODO: Remove colors from file.
template autoLog(log: Log, sev: LogSeverity, args: varargs[untyped]) = template autoLog(log: Log, sev: LogSeverity, args: varargs[untyped]) =
if (log.canLog(sev)): if log.canLog(sev):
let timeFormated = getTime().format("[HH:mm:ss]") let timeFormated = getTime().format("[HH:mm:ss]")
let sevStr = "[" & severityToString(sev) & "]" let sevStr = "[" & severityToString(sev) & "]"
@ -69,7 +69,7 @@ template autoLog(log: Log, sev: LogSeverity, args: varargs[untyped]) =
terminal.styledEcho(logHeader, args) terminal.styledEcho(logHeader, args)
if (log.logFile != nil): if log.logFile != nil:
log.logFile.writeLine(logHeader, args) log.logFile.writeLine(logHeader, args)
template debug*(log: Log, args: varargs[untyped]) = template debug*(log: Log, args: varargs[untyped]) =
@ -90,6 +90,6 @@ template info*(log: Log, args: varargs[untyped]) =
proc closeLog*(log: Log) = proc closeLog*(log: Log) =
## Close log file if it was ever open. ## Close log file if it was ever open.
if (log.logFile != nil): if log.logFile != nil:
log.info(fgYellow, "Closing log...") log.info(fgYellow, "Closing log...")
log.logFile.close() log.logFile.close()

View File

@ -1,18 +1,18 @@
import discordobject, user, json, role, options, asyncdispatch, nimcordutils, httpcore, strformat, strutils, presence import discordobject, user, json, options, asyncdispatch, nimcordutils, httpcore, strformat, strutils, presence
type GuildMember* = ref object of DiscordObject type GuildMember* = ref object of DiscordObject
## This type is a guild member. ## This type is a guild member.
user*: User ## The user this guild member represents. user*: User ## The user this guild member represents.
nick*: string ## This users guild nickname. nick*: string ## This users guild nickname.
roles*: seq[snowflake] ## Array of roles. roles*: seq[Snowflake] ## Array of roles.
joinedAt*: string ## When the user joined the guild. joinedAt*: string ## When the user joined the guild.
premiumSince*: string ## When the user started boosting the guild. premiumSince*: string ## When the user started boosting the guild.
deaf*: bool ## Whether the user is deafened in voice channels. deaf*: bool ## Whether the user is deafened in voice channels.
mute*: bool ## Whether the user is muted in voice channels. mute*: bool ## Whether the user is muted in voice channels.
guildID*: snowflake ## The guild this member is in. guildID*: Snowflake ## The guild this member is in.
presence*: Presence ## The member's presence. presence*: Presence ## The member's presence.
proc newGuildMember*(json: JsonNode, guild: snowflake): GuildMember {.inline.} = proc newGuildMember*(json: JsonNode, guild: Snowflake): GuildMember {.inline.} =
## Construct a GuildMember using json. ## Construct a GuildMember using json.
result = GuildMember( result = GuildMember(
nick: json{"nick"}.getStr(), nick: json{"nick"}.getStr(),
@ -24,7 +24,7 @@ proc newGuildMember*(json: JsonNode, guild: snowflake): GuildMember {.inline.} =
guildID: guild guildID: guild
) )
if (json.contains("user")): if json.contains("user"):
result.user = newUser(json["user"]) result.user = newUser(json["user"])
# Add roles # Add roles
@ -33,46 +33,46 @@ proc newGuildMember*(json: JsonNode, guild: snowflake): GuildMember {.inline.} =
type GuildMemberModify* = ref object type GuildMemberModify* = ref object
nick: Option[string] nick: Option[string]
roles: Option[seq[snowflake]] roles: Option[seq[Snowflake]]
mute: Option[bool] mute: Option[bool]
deaf: Option[bool] deaf: Option[bool]
channelID: Option[snowflake] channelID: Option[Snowflake]
proc modifyGuildMember*(member: GuildMember, memberID: snowflake, modify: GuildMemberModify) {.async.} = proc modifyGuildMember*(member: GuildMember, memberID: Snowflake, modify: GuildMemberModify) {.async.} =
## Modify attributes of a guild member. If the `channel_id` is set to null, ## Modify attributes of a guild member. If the `channel_id` is set to null,
## this will force the target user to be disconnected from voice. ## this will force the target user to be disconnected from voice.
## ##
## The member's new attributes will be reflected to `guild.members`. ## The member's new attributes will be reflected to `guild.members`.
var modifyPayload = %*{} var modifyPayload = %*{}
if (modify.nick.isSome): if modify.nick.isSome:
modifyPayload.add("nick", %modify.nick.get()) modifyPayload.add("nick", %modify.nick.get())
if (modify.roles.isSome): if modify.roles.isSome:
# Convert the roles array to a string representation and remove the `@` # Convert the roles array to a string representation and remove the `@`
# that is at the front of a conversion like this. # that is at the front of a conversion like this.
var rolesStr = ($modify.roles.get()).substr(1) var rolesStr = ($modify.roles.get()).substr(1)
modifyPayload.add(parseJson(rolesStr)) modifyPayload.add(parseJson(rolesStr))
if (modify.mute.isSome): if modify.mute.isSome:
modifyPayload.add("mute", %modify.mute.get()) modifyPayload.add("mute", %modify.mute.get())
if (modify.deaf.isSome): if modify.deaf.isSome:
modifyPayload.add("deaf", %modify.deaf.get()) modifyPayload.add("deaf", %modify.deaf.get())
if (modify.channelID.isSome): if modify.channelID.isSome:
modifyPayload.add("channel_id", %modify.channelID.get()) modifyPayload.add("channel_id", %modify.channelID.get())
discard sendRequest(endpoint(fmt("/guilds/{member.guildID}/members/{member.id}")), HttpPatch, discard sendRequest(endpoint(fmt("/guilds/{member.guildID}/members/{member.id}")), HttpPatch,
defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})), defaultHeaders(newHttpHeaders({"Content-Type": "application/json"})),
member.guildID, RateLimitBucketType.guild, modifyPayload) member.guildID, RateLimitBucketType.guild, modifyPayload)
proc addGuildMemberRole*(member: GuildMember, roleID: snowflake) {.async.} = proc addGuildMemberRole*(member: GuildMember, roleID: Snowflake) {.async.} =
## Adds a role to a guild member. Requires the `MANAGE_ROLES` permission. ## Adds a role to a guild member. Requires the `MANAGE_ROLES` permission.
discard sendRequest(endpoint(fmt("/guilds/{member.guildID}/members/{member.id}/roles/{roleID}")), discard sendRequest(endpoint(fmt("/guilds/{member.guildID}/members/{member.id}/roles/{roleID}")),
HttpPut, defaultHeaders(), member.guildID, RateLimitBucketType.guild) HttpPut, defaultHeaders(), member.guildID, RateLimitBucketType.guild)
proc removeGuildMemberRole*(member: GuildMember, roleID: snowflake) {.async.} = proc removeGuildMemberRole*(member: GuildMember, roleID: Snowflake) {.async.} =
## Remove's a role to a guild member. Requires the `MANAGE_ROLES` permission. ## Remove's a role to a guild member. Requires the `MANAGE_ROLES` permission.
discard sendRequest(endpoint(fmt("/guilds/{member.guildID}/members/{member.id}/roles/{roleID}")), discard sendRequest(endpoint(fmt("/guilds/{member.guildID}/members/{member.id}/roles/{roleID}")),
HttpDelete, defaultHeaders(), member.guildID, RateLimitBucketType.guild) HttpDelete, defaultHeaders(), member.guildID, RateLimitBucketType.guild)

View File

@ -1,4 +1,4 @@
import json, discordobject, nimcordutils, user, member, httpcore, asyncdispatch, emoji, options, embed, role, emoji import json, discordobject, nimcordutils, user, member, httpcore, asyncdispatch, emoji, options, embed, emoji
type type
MessageType* = enum MessageType* = enum
@ -35,9 +35,9 @@ type
name*: string name*: string
MessageReference* = ref object MessageReference* = ref object
messageID*: snowflake messageID*: Snowflake
channelID*: snowflake channelID*: Snowflake
guildID*: snowflake guildID*: Snowflake
MessageFlags* = enum MessageFlags* = enum
msgFlagCrossposted = 0, msgFlagCrossposted = 0,
@ -53,8 +53,8 @@ type
ChannelMention* = ref object ChannelMention* = ref object
## Represents a channel mention inside of a message. ## Represents a channel mention inside of a message.
channelID*: snowflake channelID*: Snowflake
guildID*: snowflake guildID*: Snowflake
channelType*: int channelType*: int
name*: string name*: string
@ -68,8 +68,8 @@ type
width*: int width*: int
Message* = ref object of DiscordObject Message* = ref object of DiscordObject
channelID*: snowflake channelID*: Snowflake
guildID*: snowflake guildID*: Snowflake
author*: User author*: User
member*: GuildMember member*: GuildMember
content*: string content*: string
@ -78,13 +78,13 @@ type
tts*: bool tts*: bool
mentionEveryone*: bool mentionEveryone*: bool
mentions*: seq[User] mentions*: seq[User]
mentionRoles*: seq[snowflake] mentionRoles*: seq[Snowflake]
mentionChannels*: seq[ChannelMention] mentionChannels*: seq[ChannelMention]
attachments*: seq[MessageAttachment] attachments*: seq[MessageAttachment]
embeds*: seq[Embed] embeds*: seq[Embed]
reactions*: seq[Reaction] reactions*: seq[Reaction]
pinned*: bool pinned*: bool
webhookID*: snowflake webhookID*: Snowflake
`type`*: MessageType `type`*: MessageType
activity*: MessageActivity activity*: MessageActivity
application*: MessageApplication application*: MessageApplication
@ -107,19 +107,19 @@ proc newMessage*(messageJson: JsonNode): Message =
flags: messageJson{"flags"}.getInt() flags: messageJson{"flags"}.getInt()
) )
if (messageJson.contains("author")): if messageJson.contains("author"):
msg.author = newUser(messageJson["author"]) msg.author = newUser(messageJson["author"])
if (messageJson.contains("member")): if messageJson.contains("member"):
msg.member = newGuildMember(messageJson["member"], msg.guildID) msg.member = newGuildMember(messageJson["member"], msg.guildID)
if (messageJson.contains("mentions")): if messageJson.contains("mentions"):
for userJson in messageJson["mentions"]: for userJson in messageJson["mentions"]:
msg.mentions.add(newUser(userJson)) msg.mentions.add(newUser(userJson))
for role in messageJson["mention_roles"]: for role in messageJson["mention_roles"]:
msg.mentionRoles.add(getIDFromJson(role.getStr())) msg.mentionRoles.add(getIDFromJson(role.getStr()))
if (messageJson.contains("mention_channels")): if messageJson.contains("mention_channels"):
for channel in messageJson["mention_channels"]: for channel in messageJson["mention_channels"]:
msg.mentionChannels.add(ChannelMention( msg.mentionChannels.add(ChannelMention(
channelID: getIDFromJson(channel["id"].getStr()), channelID: getIDFromJson(channel["id"].getStr()),
@ -142,7 +142,7 @@ proc newMessage*(messageJson: JsonNode): Message =
for embed in messageJson["embeds"]: for embed in messageJson["embeds"]:
msg.embeds.add(Embed(embedJson: embed)) msg.embeds.add(Embed(embedJson: embed))
if (messageJson.contains("reactions")): if messageJson.contains("reactions"):
for reaction in messageJson["reactions"]: for reaction in messageJson["reactions"]:
msg.reactions.add(Reaction( msg.reactions.add(Reaction(
count: uint(reaction["count"].getInt()), count: uint(reaction["count"].getInt()),
@ -150,10 +150,10 @@ proc newMessage*(messageJson: JsonNode): Message =
emoji: newEmoji(reaction["emoji"], msg.guildID) emoji: newEmoji(reaction["emoji"], msg.guildID)
)) ))
if (messageJson.contains("activity")): if messageJson.contains("activity"):
msg.activity = MessageActivity(`type`: MessageActivityType(messageJson["activity"]["type"].getInt()), msg.activity = MessageActivity(`type`: MessageActivityType(messageJson["activity"]["type"].getInt()),
partyID: messageJson["activity"]["party_id"].getStr()) partyID: messageJson["activity"]["party_id"].getStr())
if (messageJson.contains("application")): if messageJson.contains("application"):
msg.application = MessageApplication( msg.application = MessageApplication(
id: getIDFromJson(messageJson["application"]["id"].getStr()), id: getIDFromJson(messageJson["application"]["id"].getStr()),
coverImage: messageJson["application"]{"cover_image"}.getStr(), coverImage: messageJson["application"]{"cover_image"}.getStr(),
@ -161,7 +161,7 @@ proc newMessage*(messageJson: JsonNode): Message =
icon: messageJson["application"]{"icon"}.getStr(), icon: messageJson["application"]{"icon"}.getStr(),
name: messageJson["application"]["name"].getStr() name: messageJson["application"]["name"].getStr()
) )
if (messageJson.contains("message_reference")): if messageJson.contains("message_reference"):
msg.messageReference = MessageReference( msg.messageReference = MessageReference(
messageID: getIDFromJson(messageJson["message_reference"]{"message_id"}.getStr()), messageID: getIDFromJson(messageJson["message_reference"]{"message_id"}.getStr()),
channelID: getIDFromJson(messageJson["message_reference"]["channel_id"].getStr()), channelID: getIDFromJson(messageJson["message_reference"]["channel_id"].getStr()),
@ -212,8 +212,8 @@ type ReactantsGetRequest* = object
## Use this type to get a messages's reactants by setting ## Use this type to get a messages's reactants by setting
## some of the fields. ## some of the fields.
## You can only set one of `before` and `after`. ## You can only set one of `before` and `after`.
before*: Option[snowflake] before*: Option[Snowflake]
after*: Option[snowflake] after*: Option[Snowflake]
limit*: Option[int] limit*: Option[int]
proc getReactants*(message: Message, emoji: Emoji, request: ReactantsGetRequest): seq[User] = proc getReactants*(message: Message, emoji: Emoji, request: ReactantsGetRequest): seq[User] =
@ -224,17 +224,17 @@ proc getReactants*(message: Message, emoji: Emoji, request: ReactantsGetRequest)
# Raise some exceptions to make sure the user doesn't # Raise some exceptions to make sure the user doesn't
# try to set more than one of these fields # try to set more than one of these fields
if (request.before.isSome): if request.before.isSome:
url = url & "before=" & $request.before.get() url = url & "before=" & $request.before.get()
if (request.after.isSome): if request.after.isSome:
if (request.before.isSome): if request.before.isSome:
raise newException(Defect, "You cannot get before and after a message! Choose one...") raise newException(Defect, "You cannot get before and after a message! Choose one...")
url = url & "after=" & $request.after.get() url = url & "after=" & $request.after.get()
if (request.limit.isSome): if request.limit.isSome:
# Add the `&` for the url if something else is set. # Add the `&` for the url if something else is set.
if (request.before.isSome or request.after.isSome): if request.before.isSome or request.after.isSome:
url = url & "&" url = url & "&"
url = url & "limit=" & $request.limit.get() url = url & "limit=" & $request.limit.get()
@ -276,7 +276,7 @@ proc editMessage*(message: Message, content: string, embed: Embed = nil): Future
## Edit a previously sent message. ## Edit a previously sent message.
var jsonBody = %*{"content": content} var jsonBody = %*{"content": content}
if (not embed.isNil()): if embed != nil:
jsonBody.add("embed", embed.embedJson) jsonBody.add("embed", embed.embedJson)
return newMessage(sendRequest(endpoint("/channels/" & $message.channelID & "/messages/" & $message.id), return newMessage(sendRequest(endpoint("/channels/" & $message.channelID & "/messages/" & $message.id),
@ -291,4 +291,4 @@ proc deleteMessage*(message: Message) {.async.} =
## See also: ## See also:
## * `deleteMessage<#deleteMessage,Message>`_ ## * `deleteMessage<#deleteMessage,Message>`_
discard sendRequest(endpoint("/channels/" & $message.channelID & "/messages/" & $message.id), discard sendRequest(endpoint("/channels/" & $message.channelID & "/messages/" & $message.id),
HttpDelete, defaultHeaders(), message.channelID, RateLimitBucketType.channel) HttpDelete, defaultHeaders(), message.channelID, RateLimitBucketType.channel)

View File

@ -1,5 +1,5 @@
import parseutils, json, httpClient, strformat, tables, times, asyncdispatch import parseutils, json, httpClient, strformat, tables, times, asyncdispatch
from discordobject import snowflake from discordobject import Snowflake
proc getIDFromJson*(str: string): uint64 = proc getIDFromJson*(str: string): uint64 =
var num: uint64 var num: uint64
@ -17,10 +17,11 @@ proc endpoint*(url: string): string =
var globalToken*: string var globalToken*: string
proc defaultHeaders*(added: HttpHeaders = newHttpHeaders()): HttpHeaders = proc defaultHeaders*(added: HttpHeaders = newHttpHeaders()): HttpHeaders =
added.add("Authorization", fmt("Bot {globalToken}")) # added.add("Authorization", fmt("Bot {globalToken}"))
added.add("Authorization", fmt("{globalToken}"))
added.add("User-Agent", "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)") added.add("User-Agent", "NimCord (https://github.com/SeanOMik/nimcord, v0.0.0)")
added.add("X-RateLimit-Precision", "millisecond") added.add("X-RateLimit-Precision", "millisecond")
return added; return added
type type
RateLimitBucketType* = enum RateLimitBucketType* = enum
@ -37,16 +38,16 @@ proc newRateLimit(lmt: int = 500, remLmnt: int = 500, ratelmtReset: float = 0):
return RateLimit(limit: lmt, remainingLimit: remLmnt, ratelimitReset: ratelmtReset) return RateLimit(limit: lmt, remainingLimit: remLmnt, ratelimitReset: ratelmtReset)
# Rate limit buckets # Rate limit buckets
let channelRatelimitBucket = newTable[snowflake, RateLimit]() let channelRatelimitBucket = newTable[Snowflake, RateLimit]()
let guildRatelimitBucket = newTable[snowflake, RateLimit]() let guildRatelimitBucket = newTable[Snowflake, RateLimit]()
let webhookRatelimitBucket = newTable[snowflake, RateLimit]() let webhookRatelimitBucket = newTable[Snowflake, RateLimit]()
var globalRateLimit: RateLimit = newRateLimit() var globalRateLimit: RateLimit = newRateLimit()
proc handleRateLimits*(headers: HttpHeaders, objectID: snowflake, bucketType: RateLimitBucketType) = proc handleRateLimits*(headers: HttpHeaders, objectID: Snowflake, bucketType: RateLimitBucketType) =
var obj: RateLimit var obj: RateLimit
if (headers.hasKey("x-ratelimit-global")): if headers.hasKey("x-ratelimit-global"):
obj = globalRateLimit obj = globalRateLimit
elif (headers.hasKey("x-ratelimit-limit")): elif headers.hasKey("x-ratelimit-limit"):
case bucketType: case bucketType:
of RateLimitBucketType.channel: of RateLimitBucketType.channel:
obj = channelRatelimitBucket[objectID] obj = channelRatelimitBucket[objectID]
@ -68,33 +69,33 @@ proc handleRateLimits*(headers: HttpHeaders, objectID: snowflake, bucketType: Ra
discard parseFloat(headers["x-ratelimit-reset"], obj.ratelimitReset) discard parseFloat(headers["x-ratelimit-reset"], obj.ratelimitReset)
proc handleResponse*(response: Response, objectID: snowflake, bucketType: RateLimitBucketType): JsonNode = proc handleResponse*(response: Response, objectID: Snowflake, bucketType: RateLimitBucketType): JsonNode =
echo fmt("Received requested payload: {response.body}") echo fmt("Received requested payload: {response.body}")
handleRateLimits(response.headers, objectID, bucketType) handleRateLimits(response.headers, objectID, bucketType)
return parseJson(response.body()) return parseJson(response.body())
proc waitForRateLimits*(objectID: snowflake, bucketType: RateLimitBucketType) = proc waitForRateLimits*(objectID: Snowflake, bucketType: RateLimitBucketType) =
var rlmt: RateLimit var rlmt: RateLimit
if (globalRateLimit.remainingLimit == 0): if globalRateLimit.remainingLimit == 0:
rlmt = globalRateLimit rlmt = globalRateLimit
else: else:
case bucketType: case bucketType:
of RateLimitBucketType.channel: of RateLimitBucketType.channel:
if (channelRatelimitBucket.hasKey(objectID)): if channelRatelimitBucket.hasKey(objectID):
rlmt = channelRatelimitBucket[objectID] rlmt = channelRatelimitBucket[objectID]
else: else:
channelRatelimitBucket.add(objectID, newRateLimit()) channelRatelimitBucket.add(objectID, newRateLimit())
rlmt = channelRatelimitBucket[objectID] rlmt = channelRatelimitBucket[objectID]
of RateLimitBucketType.guild: of RateLimitBucketType.guild:
if (guildRatelimitBucket.hasKey(objectID)): if guildRatelimitBucket.hasKey(objectID):
rlmt = guildRatelimitBucket[objectID] rlmt = guildRatelimitBucket[objectID]
else: else:
guildRatelimitBucket.add(objectID, newRateLimit()) guildRatelimitBucket.add(objectID, newRateLimit())
rlmt = guildRatelimitBucket[objectID] rlmt = guildRatelimitBucket[objectID]
of RateLimitBucketType.webhook: of RateLimitBucketType.webhook:
if (webhookRatelimitBucket.hasKey(objectID)): if webhookRatelimitBucket.hasKey(objectID):
rlmt = webhookRatelimitBucket[objectID] rlmt = webhookRatelimitBucket[objectID]
else: else:
webhookRatelimitBucket.add(objectID, newRateLimit()) webhookRatelimitBucket.add(objectID, newRateLimit())
@ -102,21 +103,21 @@ proc waitForRateLimits*(objectID: snowflake, bucketType: RateLimitBucketType) =
of RateLimitBucketType.global: of RateLimitBucketType.global:
rlmt = globalRateLimit rlmt = globalRateLimit
if (rlmt != nil and rlmt.remainingLimit == 0): if rlmt != nil and rlmt.remainingLimit == 0:
let millisecondTime: float = rlmt.ratelimitReset * 1000 - epochTime() * 1000 let millisecondTime: float = rlmt.ratelimitReset * 1000 - epochTime() * 1000
if (millisecondTime > 0): if millisecondTime > 0:
echo fmt("Rate limit wait time: {millisecondTime} miliseconds") echo fmt("Rate limit wait time: {millisecondTime} miliseconds")
waitFor sleepAsync(millisecondTime) waitFor sleepAsync(millisecondTime)
proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: snowflake = 0, proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: Snowflake = 0,
bucketType: RateLimitBucketType = global, jsonBody: JsonNode = nil): JsonNode = bucketType: RateLimitBucketType = global, jsonBody: JsonNode = nil): JsonNode =
var client = newHttpClient() var client = newHttpClient()
# Add headers # Add headers
client.headers = headers client.headers = headers
var strPayload: string var strPayload: string
if (jsonBody == nil): if jsonBody == nil:
strPayload = "" strPayload = ""
else: else:
strPayload = $jsonBody strPayload = $jsonBody
@ -124,4 +125,4 @@ proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders
waitForRateLimits(objectID, bucketType) waitForRateLimits(objectID, bucketType)
let response = client.request(endpoint, httpMethod, strPayload) let response = client.request(endpoint, httpMethod, strPayload)
return handleResponse(response, objectId, bucketType) return handleResponse(response, objectId, bucketType)

View File

@ -39,12 +39,12 @@ type
Permissions* = ref object Permissions* = ref object
## This type referes to a user's permissions given by the role or per user. ## This type referes to a user's permissions given by the role or per user.
roleUserID*: snowflake roleUserID*: Snowflake
allowPerms*: uint allowPerms*: uint
denyPerms*: uint denyPerms*: uint
permissionType*: PermissionType permissionType*: PermissionType
proc newPermissions*(id: snowflake, `type`: PermissionType, byteSet: uint): Permissions = proc newPermissions*(id: Snowflake, `type`: PermissionType, byteSet: uint): Permissions =
## Create a new `Permissions` using an id, type, and byte set. ## Create a new `Permissions` using an id, type, and byte set.
result = Permissions(roleUserID: id, permissionType: `type`, allowPerms: byteSet) result = Permissions(roleUserID: id, permissionType: `type`, allowPerms: byteSet)
@ -56,7 +56,7 @@ proc newPermissions*(json: JsonNode): Permissions =
denyPerms: uint(json["deny"].getInt()) denyPerms: uint(json["deny"].getInt())
) )
if (json["type"].getStr() == "role"): if json["type"].getStr() == "role":
result.permissionType = PermissionType.permTypeRole result.permissionType = PermissionType.permTypeRole
else: else:
result.permissionType = PermissionType.permTypeMember result.permissionType = PermissionType.permTypeMember
@ -71,7 +71,7 @@ proc addAllowPermission*(perms: Permissions, perm: Permission): Future[Permissio
## If it finds the permission in denyPerms, it will remove it from that also. ## If it finds the permission in denyPerms, it will remove it from that also.
# Check if the permission is in deny, and remove it. # Check if the permission is in deny, and remove it.
if ((perms.denyPerms and uint(perm)) == uint(perm)): if (perms.denyPerms and uint(perm)) == uint(perm):
perms.denyPerms = perms.denyPerms and (not uint(perm)) perms.denyPerms = perms.denyPerms and (not uint(perm))
perms.allowPerms = perms.allowPerms or uint(perm) perms.allowPerms = perms.allowPerms or uint(perm)
@ -81,7 +81,7 @@ proc addDenyPermission*(perms: Permissions, perm: Permission): Future[Permission
## If it finds the permission in allowPerms, it will remove it from that also. ## If it finds the permission in allowPerms, it will remove it from that also.
# Check if the permission is in allowed, and remove it. # Check if the permission is in allowed, and remove it.
if ((perms.allowPerms and uint(perm)) == uint(perm)): if (perms.allowPerms and uint(perm)) == uint(perm):
perms.allowPerms = perms.allowPerms and (not uint(perm)) perms.allowPerms = perms.allowPerms and (not uint(perm))
perms.denyPerms = perms.denyPerms or uint(perm) perms.denyPerms = perms.denyPerms or uint(perm)
@ -94,7 +94,7 @@ proc permissionsToJson*(perms: Permissions): JsonNode =
"deny": perms.denyPerms "deny": perms.denyPerms
} }
if (perms.permissionType == PermissionType.permTypeMember): if perms.permissionType == PermissionType.permTypeMember:
json.add("type", %"member") json.add("type", %"member")
else: else:
json.add("type", %"role") json.add("type", %"role")

View File

@ -48,7 +48,7 @@ type
url*: string url*: string
createdAt*: uint createdAt*: uint
timestamps*: seq[ActivityTimestamp] timestamps*: seq[ActivityTimestamp]
applicationID*: snowflake applicationID*: Snowflake
details*: string details*: string
state*: string state*: string
emoji*: Emoji emoji*: Emoji
@ -64,7 +64,7 @@ type
activities*: seq[Activity] activities*: seq[Activity]
afk*: bool afk*: bool
proc newActivity*(json: JsonNode, guildID: snowflake): Activity = proc newActivity*(json: JsonNode, guildID: Snowflake): Activity =
## Parse a new activity from json. ## Parse a new activity from json.
var act = Activity( var act = Activity(
name: json["name"].getStr(), name: json["name"].getStr(),
@ -78,44 +78,44 @@ proc newActivity*(json: JsonNode, guildID: snowflake): Activity =
flags: uint(json{"flags"}.getInt()), flags: uint(json{"flags"}.getInt()),
) )
if (json.contains("timestamps")): if json.contains("timestamps"):
var time = ActivityTimestamp() var time = ActivityTimestamp()
if (json["timestamps"].contains("start")): if json["timestamps"].contains("start"):
time.startTime = uint(json["timestamps"]{"start"}.getInt()) time.startTime = uint(json["timestamps"]{"start"}.getInt())
if (json["timestamps"].contains("end")): if json["timestamps"].contains("end"):
time.endTime = uint(json["timestamps"]{"end"}.getInt()) time.endTime = uint(json["timestamps"]{"end"}.getInt())
act.timestamps.add(time) act.timestamps.add(time)
if (json.contains("emoji")): if json.contains("emoji"):
act.emoji = newEmoji(json["emoji"], guildID) act.emoji = newEmoji(json["emoji"], guildID)
if (json.contains("party")): if json.contains("party"):
var party = ActivityParty() var party = ActivityParty()
if (json["party"].contains("id")): if json["party"].contains("id"):
party.id = json["party"]["id"].getStr() party.id = json["party"]["id"].getStr()
if (json["party"].contains("size")): if json["party"].contains("size"):
party.currentSize = uint(json["party"]["size"].elems[0].getInt()) party.currentSize = uint(json["party"]["size"].elems[0].getInt())
party.maxSize = uint(json["party"]["size"].elems[1].getInt()) party.maxSize = uint(json["party"]["size"].elems[1].getInt())
if (json.contains("assets")): if json.contains("assets"):
var assets = ActivityAssets() var assets = ActivityAssets()
if (json["assets"].contains("large_image")): if json["assets"].contains("large_image"):
assets.largeImg = json["assets"]["large_image"].getStr() assets.largeImg = json["assets"]["large_image"].getStr()
if (json["assets"].contains("large_text")): if json["assets"].contains("large_text"):
assets.largeText = json["assets"]["large_text"].getStr() assets.largeText = json["assets"]["large_text"].getStr()
if (json["assets"].contains("small_image")): if json["assets"].contains("small_image"):
assets.smallImg = json["assets"]["small_image"].getStr() assets.smallImg = json["assets"]["small_image"].getStr()
if (json["assets"].contains("small_text")): if json["assets"].contains("small_text"):
assets.smallText = json["assets"]["small_text"].getStr() assets.smallText = json["assets"]["small_text"].getStr()
if (json.contains("secrets")): if json.contains("secrets"):
var secrets = ActivitySecrets() var secrets = ActivitySecrets()
if (json["secrets"].contains("join")): if json["secrets"].contains("join"):
secrets.join = json["secrets"]["join"].getStr() secrets.join = json["secrets"]["join"].getStr()
if (json["secrets"].contains("spectate")): if json["secrets"].contains("spectate"):
secrets.spectate = json["secrets"]["spectate"].getStr() secrets.spectate = json["secrets"]["spectate"].getStr()
if (json["secrets"].contains("match")): if json["secrets"].contains("match"):
secrets.match = json["secrets"]["match"].getStr() secrets.match = json["secrets"]["match"].getStr()
proc newPresence*(json: JsonNode): Presence = proc newPresence*(json: JsonNode): Presence =
@ -124,7 +124,7 @@ proc newPresence*(json: JsonNode): Presence =
status: json["status"].getStr() status: json["status"].getStr()
) )
if (json.contains("game") and json["game"].getFields().len > 0): if json.contains("game") and json["game"].getFields().len > 0:
let guildID = if json.contains("guild_id"): getIDFromJson(json["guild_id"].getStr()) else: 0 let guildID = if json.contains("guild_id"): getIDFromJson(json["guild_id"].getStr()) else: 0
result.game = newActivity(json["game"], guildID) result.game = newActivity(json["game"], guildID)
@ -154,4 +154,4 @@ proc presenceToJson*(presence: Presence): JsonNode =
"name": presence.game.name, "name": presence.game.name,
"type": ord(presence.game.`type`) "type": ord(presence.game.`type`)
} }
} }

View File

@ -8,9 +8,9 @@ type Role* = ref object of DiscordObject
permissions*: Permissions permissions*: Permissions
managed*: bool managed*: bool
mentionable*: bool mentionable*: bool
guildID*: snowflake guildID*: Snowflake
proc newRole*(json: JsonNode, guild: snowflake): Role = proc newRole*(json: JsonNode, guild: Snowflake): Role =
## Parses role from json. ## Parses role from json.
result = Role( result = Role(
id: getIDFromJson(json["id"].getStr()), id: getIDFromJson(json["id"].getStr()),
@ -38,19 +38,19 @@ proc modifyGuildRole*(role: var Role, name: Option[string] = none(string), permi
var jsonBody: JsonNode var jsonBody: JsonNode
if (name.isSome): if name.isSome:
jsonBody.add("name", %name) jsonBody.add("name", %name)
if (permissions.isSome): if permissions.isSome:
jsonBody.add("permissions", %permissions.get().allowPerms) jsonBody.add("permissions", %permissions.get().allowPerms)
if (color.isSome): if color.isSome:
jsonBody.add("color", %color) jsonBody.add("color", %color)
if (hoist.isSome): if hoist.isSome:
jsonBody.add("hoist", %hoist) jsonBody.add("hoist", %hoist)
if (mentionable.isSome): if mentionable.isSome:
jsonBody.add("mentionable", %mentionable) jsonBody.add("mentionable", %mentionable)
result = newRole(sendRequest(endpoint(fmt("/guilds/{role.guildID}/roles/{role.id}")), HttpPatch, result = newRole(sendRequest(endpoint(fmt("/guilds/{role.guildID}/roles/{role.id}")), HttpPatch,
@ -61,4 +61,4 @@ proc modifyGuildRole*(role: var Role, name: Option[string] = none(string), permi
proc deleteGuildRole*(role: Role) {.async.} = proc deleteGuildRole*(role: Role) {.async.} =
## Delete a guild role. Requires the `MANAGE_ROLES` permission. ## Delete a guild role. Requires the `MANAGE_ROLES` permission.
discard sendRequest(endpoint(fmt("/guilds/{role.guildID}/roles/{role.id}")), HttpDelete, discard sendRequest(endpoint(fmt("/guilds/{role.guildID}/roles/{role.id}")), HttpDelete,
defaultHeaders(), role.guildID, RateLimitBucketType.guild) defaultHeaders(), role.guildID, RateLimitBucketType.guild)