From f1ec478904ef1c711e9f5c0071fda0c363e298de Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Thu, 18 Jun 2020 00:52:32 -0500 Subject: [PATCH] Finish all channel endpoints that should be in channel.nim There's still some other channel related endpoints but they should be in `message.nim` not `channel.nim` --- src/channel.nim | 160 ++++++++++++++++++++++++++++++++++++++++++- src/client.nim | 11 ++- src/nimcordutils.nim | 4 +- 3 files changed, 168 insertions(+), 7 deletions(-) diff --git a/src/channel.nim b/src/channel.nim index b5dd7c8..cedf6d2 100644 --- a/src/channel.nim +++ b/src/channel.nim @@ -44,11 +44,27 @@ type #permissionOverwrites*: seq[Permissions] ## Explicit permission overwrites for members and roles. parentID*: Option[snowflake] + 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 + proc newChannel*(channel: JsonNode): Channel {.inline.} = ## Parses the channel from json. var chan = Channel( id: getIDFromJson(channel["id"].getStr()), - `type`: ChannelType(channel["type"].getInt()), + `type`: ChannelType(channel["type"].getInt()) ) if (channel.contains("guild_id")): @@ -87,6 +103,34 @@ proc newChannel*(channel: JsonNode): Channel {.inline.} = return chan +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 proc sendMessage*(channel: Channel, content: string, tts: bool = false): Message = ## Send a message through the channel. let messagePayload = %*{"content": content, "tts": tts} @@ -153,7 +197,7 @@ type MessagesGetRequest* = object limit*: Option[int] proc getMessages*(channel: Channel, request: MessagesGetRequest): seq[Message] = - ## Gets messages in the channel. + ## Gets messages from the channel. ## ## Examples: ## .. code-block:: nim @@ -188,4 +232,114 @@ proc getMessages*(channel: Channel, request: MessagesGetRequest): seq[Message] = channel.id, RateLimitBucketType.channel) for message in response: - result.add(newMessage(message)) \ No newline at end of file + 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) \ No newline at end of file diff --git a/src/client.nim b/src/client.nim index aeb7295..f6cc545 100644 --- a/src/client.nim +++ b/src/client.nim @@ -146,8 +146,15 @@ registerEventListener(EventType.evtMessageCreate, proc(bEvt: BaseEvent) = var channel: Channel = event.message.getMessageChannel(event.client.cache) if (channel != nil): discard channel.getMessages(MessagesGetRequest(limit: some(15), before: some(event.message.id))) - else: - echo "Failed to get channel!" + elif (event.message.content.startsWith("?bulkDeleteMessages")): + var channel: Channel = event.message.getMessageChannel(event.client.cache) + if (channel != nil): + var amount: int = 25 + if (event.message.content.len > 19): + amount = parseIntEasy(event.message.content.substr(20)) + let messages = channel.getMessages(MessagesGetRequest(limit: some(amount), before: some(event.message.id))) + discard channel.bulkDeleteMessages(messages) + ) waitFor bot.startConnection() \ No newline at end of file diff --git a/src/nimcordutils.nim b/src/nimcordutils.nim index 2e3b9c4..638a20d 100644 --- a/src/nimcordutils.nim +++ b/src/nimcordutils.nim @@ -110,13 +110,13 @@ proc waitForRateLimits*(objectID: snowflake, bucketType: RateLimitBucketType) = discard sleepAsync(millisecondTime) proc sendRequest*(endpoint: string, httpMethod: HttpMethod, headers: HttpHeaders, objectID: snowflake = 0, - bucketType: RateLimitBucketType = global, jsonBody: JsonNode = %*{}): JsonNode = + bucketType: RateLimitBucketType = global, jsonBody: JsonNode = nil): JsonNode = var client = newHttpClient() # Add headers client.headers = headers var strPayload: string - if ($jsonBody == "{}"): + if (jsonBody == nil): strPayload = "" else: strPayload = $jsonBody