Skip to content

Commit 1e7b882

Browse files
feat (command): support deferred responses and follow up messages
1 parent ec7060f commit 1e7b882

File tree

4 files changed

+48
-8
lines changed

4 files changed

+48
-8
lines changed

Sources/DiscordKitBot/ApplicationCommand/CommandData.swift

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import Foundation
99
import DiscordKitCore
1010

1111
/// Provides methods to get parameters of and respond to application command interactions
12-
public struct CommandData {
12+
public class CommandData {
1313
internal init(
1414
optionValues: [OptionData],
15-
rest: DiscordREST, token: String, interactionID: Snowflake
15+
rest: DiscordREST, applicationID: String, interactionID: Snowflake, token: String
1616
) {
1717
self.rest = rest
1818
self.token = token
1919
self.interactionID = interactionID
20+
self.applicationID = applicationID
2021

2122
self.optionValues = Self.unwrapOptionDatas(optionValues)
2223
}
@@ -27,9 +28,15 @@ public struct CommandData {
2728
/// instance gets deallocated.
2829
private weak var rest: DiscordREST?
2930

31+
/// The ID of this bot application
32+
private let applicationID: String
33+
3034
/// Values of options in this command
3135
private let optionValues: [String: OptionData]
3236

37+
/// If this reply has already been deferred
38+
fileprivate var replyDeferred = false
39+
3340
// MARK: Parameters for executing callbacks
3441
/// The token to use when carrying out actions with this interaction
3542
let token: String
@@ -82,14 +89,40 @@ public extension CommandData {
8289

8390
// MARK: - Callback APIs
8491
public extension CommandData {
92+
/// Wrapper function to send an interaction response with the current interaction's ID and token
93+
private func sendResponse(_ response: InteractionResponse) async throws {
94+
try await rest?.sendInteractionResponse(response, interactionID: interactionID, token: token)
95+
}
96+
8597
/// Reply to this interaction with a plain text response
98+
///
99+
/// If a prior call to ``deferReply()`` was made, this function automatically
100+
/// calls ``followUp(_:)`` instead.
86101
func reply(_ content: String) async throws {
87-
try await rest?.sendInteractionResponse(
102+
if replyDeferred {
103+
_ = try await followUp(content)
104+
return
105+
}
106+
try await sendResponse(
88107
.init(
89108
type: .interactionReply,
90109
data: .message(.init(content: content))
91-
),
92-
interactionID: interactionID, token: token
110+
)
93111
)
94112
}
113+
114+
/// Send a follow up response to this interaction
115+
///
116+
/// By default, this creates a second reply to this interaction, appearing as a
117+
/// reply in clients. However, if a call to ``deferReply()`` was made, this
118+
/// edits the loading message with the content provided.
119+
func followUp(_ content: String) async throws -> Message {
120+
try await rest!.sendInteractionFollowUp(.init(content: content), applicationID: applicationID, token: token)
121+
}
122+
123+
/// Defer the reply to this interaction - the user sees a loading state
124+
func deferReply() async throws {
125+
try await sendResponse(.init(type: .deferredInteractionReply, data: nil))
126+
replyDeferred = true
127+
}
95128
}

Sources/DiscordKitBot/Client.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ extension Client {
114114
Task {
115115
await handler(.init(
116116
optionValues: commandData.options ?? [],
117-
rest: rest, token: token, interactionID: id
117+
rest: rest, applicationID: applicationID!, interactionID: id, token: token
118118
))
119119
}
120120
}

Sources/DiscordKitBot/REST/APICommand.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,11 @@ public extension DiscordREST {
8080
func sendInteractionResponse(_ response: InteractionResponse, interactionID: Snowflake, token: String) async throws {
8181
try await postReq(path: "interactions/\(interactionID)/\(token)/callback", body: response)
8282
}
83+
84+
/// Send a follow up response to an interaction
85+
///
86+
/// > POST: `/webhooks/{application.id}/{interaction.token}`
87+
func sendInteractionFollowUp(_ message: NewMessage, applicationID: Snowflake, token: String) async throws -> Message {
88+
try await postReq(path: "webhooks/\(applicationID)/\(token)", body: message)
89+
}
8390
}

Sources/DiscordKitCore/Objects/Data/Interaction.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public struct MessageInteraction: Codable {
196196

197197
// MARK: - Interaction Response
198198
public struct InteractionResponse: Encodable {
199-
public init(type: InteractionResponse.ResponseType, data: InteractionResponse.ResponseData) {
199+
public init(type: InteractionResponse.ResponseType, data: InteractionResponse.ResponseData?) {
200200
self.type = type
201201
self.data = data
202202
}
@@ -246,5 +246,5 @@ public struct InteractionResponse: Encodable {
246246
}
247247

248248
public let type: ResponseType
249-
public let data: ResponseData
249+
public let data: ResponseData?
250250
}

0 commit comments

Comments
 (0)