2020-06-18 03:18:54 +00:00
import json , discordobject , user , options , nimcordutils , message , httpcore , asyncdispatch , asyncfutures , strutils
type
ChannelType * = enum
2020-06-18 03:36:37 +00:00
## This enum shows the type of the channel.
2020-06-18 03:18:54 +00:00
chanTypeGuildText = 0 ,
chanTypeDM = 1 ,
chanTypeGuildVoice = 2 ,
chanTypeGroupDM = 3 ,
chanTypeGuildCategory = 4 ,
chanTypeGuildNews = 5 ,
chanTypeGuildStore = 6
Channel * = ref object of DiscordObject
2020-06-18 03:36:37 +00:00
## Discord channel object.
2020-06-18 03:18:54 +00:00
` type ` * : ChannelType ## The type of channel.
guildID * : snowflake ## The id of the guild.
position * : int ## Sorting position of the channel.
#permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles.
name * : string ## The name of the channel (2-100 characters).
topic * : string ## The channel topic (0-1024 characters).
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).
bitrate * : int ## The bitrate (in bits) 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.
recipients * : seq [ User ] ## The recipients of the DM
icon * : string ## Icon hash
ownerID : snowflake ## ID of the DM creator
applicationID : snowflake ## Application id of the group DM creator if it is bot-created
parentID : snowflake ## ID of the parent category for a channel
lastPinTimestamp : string ## When the last pinned message was pinned
2020-06-18 04:51:20 +00:00
ChannelModify * = ref object
2020-06-18 03:18:54 +00:00
## Use this type to modify a channel by setting each fields.
name * : Option [ string ]
` type ` * : Option [ ChannelType ]
position * : Option [ int ]
topic * : Option [ string ]
nsfw * : Option [ bool ]
rateLimitPerUser * : Option [ int ]
bitrate * : Option [ int ]
userLimit * : Option [ int ]
#permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles.
parentID * : Option [ snowflake ]
2020-06-18 05:52:32 +00:00
Invite * = object
## Represents a code that when used, adds a user to a guild or group DM channel.
code * : string ## The invite code (unique ID)
guildID * : snowflake ## The guild this invite is for
channel * : Channel ## The channel this invite is for
inviter * : User ## The user who created the invite
targetUser * : User ## The target user for this invite
#targetUserType* # Not sure if this is needed because it can only be `1`
approximatePresenceCount * : int ## Approximate count of online members (only present when target_user is set)
approximateMemberCount * : int ## Approximate count of total members
uses * : int ## Number of times this invite has been used
maxUsers * : int ## Max number of times this invite can be used
maxAge * : int ## Duration (in seconds) after which the invite expires
temporary * : bool ## Whether this invite only grants temporary membership
createdAt : string ## When this invite was created
2020-06-18 03:18:54 +00:00
proc newChannel * ( channel : JsonNode ) : Channel {. inline . } =
2020-06-18 03:36:37 +00:00
## Parses the channel from json.
2020-06-18 03:18:54 +00:00
var chan = Channel (
id : getIDFromJson ( channel [ " id " ] . getStr ( ) ) ,
2020-06-18 05:52:32 +00:00
` type ` : ChannelType ( channel [ " type " ] . getInt ( ) )
2020-06-18 03:18:54 +00:00
)
if ( channel . contains ( " guild_id " ) ) :
chan . guildID = getIDFromJson ( channel [ " guild_id " ] . getStr ( ) )
if ( channel . contains ( " position " ) ) :
chan . position = channel [ " position " ] . getInt ( )
if ( channel . contains ( " permission_overwrites " ) ) :
echo " permission_overwrites "
if ( channel . contains ( " name " ) ) :
chan . name = channel [ " name " ] . getStr ( )
if ( channel . contains ( " topic " ) ) :
chan . topic = channel [ " topic " ] . getStr ( )
if ( channel . contains ( " nsfw " ) ) :
chan . nsfw = channel [ " nsfw " ] . getBool ( )
if ( channel . contains ( " last_message_id " ) ) :
chan . lastMessageID = getIDFromJson ( channel [ " last_message_id " ] . getStr ( ) )
if ( channel . contains ( " bitrate " ) ) :
chan . bitrate = channel [ " bitrate " ] . getInt ( )
if ( channel . contains ( " user_limit " ) ) :
chan . userLimit = channel [ " user_limit " ] . getInt ( )
if ( channel . contains ( " rate_limit_per_user " ) ) :
chan . rateLimitPerUser = channel [ " rate_limit_per_user " ] . getInt ( )
if ( channel . contains ( " recipients " ) ) :
for recipient in channel [ " recipients " ] :
chan . recipients . insert ( newUser ( recipient ) )
if ( channel . contains ( " icon " ) ) :
chan . icon = channel [ " icon " ] . getStr ( )
if ( channel . contains ( " owner_id " ) ) :
chan . ownerID = getIDFromJson ( channel [ " owner_id " ] . getStr ( ) )
if ( channel . contains ( " application_id " ) ) :
chan . applicationID = getIDFromJson ( channel [ " application_id " ] . getStr ( ) )
if ( channel . contains ( " parent_id " ) ) :
chan . parentID = getIDFromJson ( channel [ " parent_id " ] . getStr ( ) )
if ( channel . contains ( " last_pin_timestamp " ) ) :
chan . lastPinTimestamp = channel [ " last_pin_timestamp " ] . getStr ( )
return chan
2020-06-18 05:52:32 +00:00
proc newInvite * ( json : JsonNode ) : Invite {. inline . } =
## Parses an invite from json.
var invite = Invite (
code : json [ " code " ] . getStr ( ) ,
channel : newChannel ( json [ " channel " ] )
)
if ( json . contains ( " guild " ) ) :
invite . guildID = getIDFromJson ( json [ " guild " ] [ " id " ] . getStr ( ) )
if ( json . contains ( " target_user " ) ) :
invite . targetUser = newUser ( json [ " target_user " ] )
if ( json . contains ( " approximate_presence_count " ) ) :
invite . approximatePresenceCount = json [ " approximate_presence_count " ] . getInt ( )
if ( json . contains ( " approximate_member_count " ) ) :
invite . approximateMemberCount = json [ " approximate_member_count " ] . getInt ( )
if ( json . contains ( " uses " ) ) :
invite . uses = json [ " uses " ] . getInt ( )
if ( json . contains ( " max_uses " ) ) :
invite . maxUsers = json [ " max_uses " ] . getInt ( )
if ( json . contains ( " max_age " ) ) :
invite . maxAge = json [ " max_age " ] . getInt ( )
if ( json . contains ( " temporary " ) ) :
invite . temporary = json [ " temporary " ] . getBool ( )
if ( json . contains ( " created_at " ) ) :
invite . createdAt = json [ " created_at " ] . getStr ( )
return invite
#TODO: Embeds, and files
2020-06-18 03:18:54 +00:00
proc sendMessage * ( channel : Channel , content : string , tts : bool = false ) : Message =
2020-06-18 03:36:37 +00:00
## Send a message through the channel.
2020-06-18 03:18:54 +00:00
let messagePayload = % * { " content " : content , " tts " : tts }
return newMessage ( sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /messages " ) , HttpPost ,
defaultHeaders ( newHttpHeaders ( { " Content-Type " : " application/json " } ) ) , channel . id ,
RateLimitBucketType . channel , messagePayload ) )
proc modifyChannel * ( channel : Channel , modify : ChannelModify ) : Future [ Channel ] {. async . } =
2020-06-18 03:36:37 +00:00
## Modifies the channel.
##
## Examples:
2020-06-18 04:51:20 +00:00
## .. code-block:: nim
## var chan = getChannel(703084913510973472)
## chan = chan.modifyChannel(ChannelModify(topic: some("This is the channel topic")))
2020-06-18 03:36:37 +00:00
2020-06-18 03:18:54 +00:00
var modifyPayload = % * { }
if ( modify . name . isSome ) :
modifyPayload . add ( " name " , % modify . name . get ( ) )
if ( modify . ` type ` . isSome ) :
modifyPayload . add ( " type " , % modify . ` type ` . get ( ) )
if ( modify . position . isSome ) :
modifyPayload . add ( " position " , % modify . position . get ( ) )
if ( modify . topic . isSome ) :
modifyPayload . add ( " topic " , % modify . topic . get ( ) )
if ( modify . nsfw . isSome ) :
modifyPayload . add ( " nsfw " , % modify . nsfw . get ( ) )
if ( modify . rateLimitPerUser . isSome ) :
modifyPayload . add ( " rate_limit_per_user " , % modify . rateLimitPerUser . get ( ) )
if ( modify . bitrate . isSome ) :
modifyPayload . add ( " bitrate " , % modify . bitrate . get ( ) )
if ( modify . userLimit . isSome ) :
modifyPayload . add ( " user_limit " , % modify . userLimit . get ( ) )
#[ if (modify.name.isSome):
modifyPayload . add ( " permission_overwrites " , % modify . parentID . get ( ) 0 ] #
if ( modify . parentID . isSome ) :
modifyPayload . add ( " parent_id " , % modify . parentID . get ( ) )
return newChannel ( sendRequest ( endpoint ( " /channels/ " & $ channel . id ) , HttpPatch ,
defaultHeaders ( newHttpHeaders ( { " Content-Type " : " application/json " } ) ) ,
channel . id , RateLimitBucketType . channel , modifyPayload ) )
proc deleteChannel * ( channel : Channel ) {. async . } =
2020-06-18 03:36:37 +00:00
## Delete the channel.
2020-06-18 03:18:54 +00:00
discard sendRequest ( endpoint ( " /channels/ " & $ channel . id ) , HttpDelete ,
2020-06-18 04:51:38 +00:00
defaultHeaders ( ) , channel . id , RateLimitBucketType . channel )
type MessagesGetRequest * = object
## 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`.
around * : Option [ snowflake ]
before * : Option [ snowflake ]
after * : Option [ snowflake ]
limit * : Option [ int ]
proc getMessages * ( channel : Channel , request : MessagesGetRequest ) : seq [ Message ] =
2020-06-18 05:52:32 +00:00
## Gets messages from the channel.
2020-06-18 04:51:38 +00:00
##
## Examples:
## .. code-block:: nim
## var chan = getChannel(703084913510973472)
## channel.getMessages(MessagesGetRequest(limit: some(15), before: some(723030179760570428)))
var url : string = endpoint ( " /channels/ " & $ channel . id & " /messages? " )
if ( request . around . isSome ) :
url = url & " around= " & $ request . around . get ( )
# Raise some exceptions to make sure the user doesn't
# try to set more than one of these fields
if ( request . before . isSome ) :
if ( request . around . isSome ) :
raise newException ( Defect , " You cannot get around and before a message! Choose one... " )
url = url & " before= " & $ request . before . get ( )
if ( request . after . isSome ) :
if ( request . around . isSome or request . before . isSome ) :
raise newException ( Defect , " You cannot get around/before and after a message! Choose one... " )
url = url & " after= " & $ request . after . get ( )
if ( request . limit . isSome ) :
# Add the `&` for the url if something else is set.
if ( request . around . isSome or request . before . isSome or request . after . isSome ) :
url = url & " & "
url = url & " limit= " & $ request . limit . get ( )
let response = sendRequest ( url , HttpGet , defaultHeaders ( newHttpHeaders ( ) ) ,
channel . id , RateLimitBucketType . channel )
for message in response :
2020-06-18 05:52:32 +00:00
result . add ( newMessage ( message ) )
proc getMessage * ( channel : Channel , messageID : snowflake ) : Message =
## Requests a message from the channel via the Discord REST API.
return newMessage ( sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /messages/ " & $ messageID ) , HttpGet ,
defaultHeaders ( ) , channel . id , RateLimitBucketType . channel ) )
proc bulkDeleteMessages * ( channel : Channel , messageIDs : seq [ snowflake ] ) {. async . } =
## Bulk delete channel messages. This endpoint can only delete 2-100 messages.
## This proc takes a seq[snowflakes] represtenting the message's IDs.
## The messages can not be older than 2 weeks!
##
## See also:
## * `bulkDeleteMessages(channel: Channel, messages: seq[Message])`_
# Remove the `@` from the string conversion
let stringPayload : string = ( $ messageIDs ) . substr ( 1 )
let jsonPayload = % * { " messages " : parseJson ( stringPayload ) }
discard sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /messages/bulk-delete " ) , HttpPost ,
defaultHeaders ( newHttpHeaders ( { " Content-Type " : " application/json " } ) ) , channel . id ,
RateLimitBucketType . channel , jsonPayload )
proc bulkDeleteMessages * ( channel : Channel , messages : seq [ Message ] ) {. async . } =
## Delete multiple messages in a single request. This endpoint can only delete 2-100 messages.
## This proc takes a seq[Message] represtenting the message's you want to delete.
## The messages can not be older than 2 weeks!
##
## See also:
## * `bulkDeleteMessages(channel: Channel, messageIDs: seq[snowflake])`_
var messageIDs : seq [ snowflake ]
for msg in messages :
messageIDs . add ( msg . id )
waitFor channel . bulkDeleteMessages ( messageIDs )
#TODO: https://discord.com/developers/docs/resources/channel#edit-channel-permissions
proc getChannelInvites * ( channel : Channel ) : seq [ Invite ] =
## Returns a list of invite objects (with invite metadata) for the channel.
## Only usable for guild channels. Requires the MANAGE_CHANNELS permission.
let json = sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /invites " ) , HttpGet ,
defaultHeaders ( ) , channel . id , RateLimitBucketType . channel )
for invite in json :
result . add ( newInvite ( invite ) )
type CreateInviteFields * = object
maxAge : Option [ int ] ## Duration of invite in seconds before expiry, or 0 for never
maxUses : Option [ int ] ## Max number of uses or 0 for unlimited
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)
targetUser : Option [ snowflake ] ## The target user id for this invite
targetUserType : Option [ int ] ## The type of target user for this invite
proc createChannelInvite * ( channel : Channel , fields : CreateInviteFields ) : Invite =
## Create a new invite object for the channel. Only usable for guild channels.
## Requires the CREATE_INSTANT_INVITE permission.
##
## Examples:
## .. code-block:: nim
## var chan = getChannel(703084913510973472)
## # Create an invite that lasts 1 day, and can only be used 10 times
## channel.createChannelInvite(CreateInviteFields(maxAge: 3600, maxUses: 10))
var createPayload = % * { }
if ( fields . maxAge . isSome ) :
createPayload . add ( " max_age " , % fields . maxAge . get ( ) )
if ( fields . maxUses . isSome ) :
createPayload . add ( " max_uses " , % fields . maxUses . get ( ) )
if ( fields . temporary . isSome ) :
createPayload . add ( " temporary " , % fields . temporary . get ( ) )
if ( fields . unique . isSome ) :
createPayload . add ( " unique " , % fields . unique . get ( ) )
if ( fields . targetUser . isSome ) :
createPayload . add ( " target_user " , % fields . targetUser . get ( ) )
# Not sure if its needed because it can only be `1`
#[ if (fields.targetUserType.isSome):
createPayload . add ( " target_user_type " , % fields . targetUserType . get ( ) ) ] #
return newInvite ( sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /invites " ) , HttpPost ,
defaultHeaders ( newHttpHeaders ( { " Content-Type " : " application/json " } ) ) , channel . id ,
RateLimitBucketType . channel , createPayload ) )
#TODO: https://discord.com/developers/docs/resources/channel#delete-channel-permission
proc triggerTypingIndicator * ( channel : Channel ) {. async . } =
## Post a typing indicator for the specified channel.
discard sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /typing " ) , HttpPost ,
defaultHeaders ( ) , channel . id , RateLimitBucketType . channel )
proc getPinnedMessages * ( channel : Channel ) : seq [ Message ] =
## Returns all pinned messages in the channel
let json = sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /pins " ) , HttpGet ,
defaultHeaders ( ) , channel . id , RateLimitBucketType . channel )
for message in json :
result . add ( newMessage ( message ) )
proc groupDMAddRecipient * ( channel : Channel , user : User , accessToken : string , nick : string ) {. async . } =
## Adds a recipient to a Group DM using their access token.
let jsonBody = % * { " access_token " : accessToken , " nick " : nick }
discard sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /recipients/ " & $ user . id ) ,
HttpPut , defaultHeaders ( newHttpHeaders ( { " Content-Type " : " application/json " } ) ) ,
channel . id , RateLimitBucketType . channel , jsonBody )
proc groupDMRemoveRecipient * ( channel : Channel , user : User ) {. async . } =
discard sendRequest ( endpoint ( " /channels/ " & $ channel . id & " /recipients/ " & $ user . id ) ,
HttpPut , defaultHeaders ( ) , channel . id , RateLimitBucketType . channel )