-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmain.go
More file actions
350 lines (291 loc) · 10.8 KB
/
main.go
File metadata and controls
350 lines (291 loc) · 10.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
package main
import (
"fmt"
"log"
"os"
"strconv"
"github.com/switchupcb/disgo"
"github.com/switchupcb/disgo/tools"
)
// Environment Variables.
var (
// token represents the bot's token.
token = os.Getenv("TOKEN")
// appid represents the bot's ApplicationID.
//
// Use Developer Mode to find it, or call GetCurrentUser (request) in your program
// and set it programmatically.
appid = os.Getenv("APPID")
)
const (
// set cleanup to true to remove the created Application Command upon program termination.
cleanup = false
)
func main() {
// enable the logger for the API Wrapper.
// zerolog.SetGlobalLevel(zerolog.DebugLevel)
log.Println("Program is started.")
// create a new Bot Client.
bot := &disgo.Client{
ApplicationID: appid, // REQUIRED (for this example).
Authentication: disgo.BotToken(token), // or BearerToken("TOKEN")
Config: disgo.DefaultConfig(),
Handlers: new(disgo.Handlers),
Sessions: disgo.NewSessionManager(),
}
log.Println("Creating an application command...")
// Create a Create Global Application Command request.
//
// Use subcommand groups and subcommands to create a /calculate command (unusable),
// which provides a subcommand group "add" (with usable commands "int", "string"),
// and subcommand "subtract" (usable).
//
// COMMAND /calculate
// GROUP "add"
// SUBCOMMAND "int" (adds two integers)
// OPTION 1: The first integer.
// OPTION 2: The second integer.
//
// SUBCOMMAND "string" (adds two strings)
// OPTION 1: The first string.
// OPTION 2: The second string.
//
// SUBCOMMAND "subtract" (adds two doubles)
// OPTION 1: The first double.
// OPTION 2: The second double.
//
request := &disgo.CreateGlobalApplicationCommand{
Name: "calculate",
Description: disgo.Pointer("Calculate an operation using two values."),
// create the `/calculate <group>` subcommand groups.
Options: []*disgo.ApplicationCommandOption{
// subcommand group `/calculate add`
{
Name: "add",
Description: "Add two values together.",
Type: disgo.FlagApplicationCommandOptionTypeSUB_COMMAND_GROUP,
Options: []*disgo.ApplicationCommandOption{
// create the `/calculate add int` subcommand.
{
Name: "int",
Description: "Add two integers together.",
Type: disgo.FlagApplicationCommandOptionTypeSUB_COMMAND,
// create the OPTIONS for the `/calculate add int` subcommand.
Options: []*disgo.ApplicationCommandOption{
{
Name: "addend",
Description: "The first integer to add.",
Type: disgo.FlagApplicationCommandOptionTypeINTEGER,
},
{
Name: "summand",
Description: "The second integer to add.",
Type: disgo.FlagApplicationCommandOptionTypeINTEGER,
},
},
},
// create the `/calculate add string` subcommand.
{
Name: "string",
Description: "Concatenate two strings.",
Type: disgo.FlagApplicationCommandOptionTypeSUB_COMMAND,
// create the OPTIONS for the `/calculate add string` subcommand.
Options: []*disgo.ApplicationCommandOption{
{
Name: "addend",
Description: "The first string to concatenate.",
Type: disgo.FlagApplicationCommandOptionTypeSTRING,
},
{
Name: "summand",
Description: "The second string to concatenate.",
Type: disgo.FlagApplicationCommandOptionTypeSTRING,
},
},
},
},
},
// subcommand `/calculate subtract`
{
Name: "subtract",
Description: "Subtract one value from another.",
Type: disgo.FlagApplicationCommandOptionTypeSUB_COMMAND,
// create the OPTIONS for the `/calculate subtract` subcommand.
Options: []*disgo.ApplicationCommandOption{
{
Name: "minuend",
Description: "The number to subtract from.",
Type: disgo.FlagApplicationCommandOptionTypeNUMBER,
Required: disgo.Pointer(true),
},
{
Name: "subtrahend",
Description: "The number to subtract.",
Type: disgo.FlagApplicationCommandOptionTypeNUMBER,
Required: disgo.Pointer(true),
// Explicitly defining every field of an option struct is not necessary.
// In any case, a description of each field is found in the Discord API Documentation.
// https://discord.com/developers/docs/interactions/application-commands#application-command-object
NameLocalizations: nil,
DescriptionLocalizations: nil,
Choices: nil,
Options: nil,
ChannelTypes: nil,
MinValue: nil,
MaxValue: nil,
Autocomplete: nil,
},
},
},
},
}
// Register the new command by sending the request to Discord using the bot.
//
// returns a disgo.ApplicationCommand
newCommand, err := request.Send(bot)
if err != nil {
log.Printf("failure sending command to Discord: %v", err)
return
}
log.Println("Adding an event handler.")
// Add an event handler to the bot.
//
// confirm the event handler is added to the bot.
if err := bot.Handle(disgo.FlagGatewayEventNameInteractionCreate, func(i *disgo.InteractionCreate) {
log.Printf("calculate called by %s.", i.Interaction.User.Username)
// see func declaration below.
if err := onInteraction(bot, i.Interaction); err != nil {
log.Println(err)
}
}); err != nil {
// when the Handle(eventname, function) parameters are not configured correctly.
log.Printf("Failed to add event handler to bot: %v", err)
os.Exit(1)
}
log.Println("Connecting to the Discord Gateway...")
// Connect a new session to the Discord Gateway (WebSocket Connection).
session := disgo.NewSession()
if err := session.Connect(bot); err != nil {
log.Printf("can't open websocket session to Discord Gateway: %v", err)
return
}
log.Println("Successfully connected to the Discord Gateway. Waiting for interactions...")
// end the program using a SIGINT call via `Ctrl + C` from the terminal.
if err := tools.InterceptSignal(tools.Signals, session); err != nil {
log.Printf("error exiting program: %v", err)
}
log.Println("Exiting program due to signal...")
// tools.InterceptSignal() calls s.Disconnect() which disconnects the Session from the Discord Gateway.
log.Println("Disconnected from the Discord Gateway.")
if cleanup {
log.Println("Deleting the application command...")
requestDeleteGlobalApplicationCommand := &disgo.DeleteGlobalApplicationCommand{CommandID: newCommand.ID}
if err := requestDeleteGlobalApplicationCommand.Send(bot); err != nil {
log.Printf("error deleting Global Application Command: %v", err)
return
}
}
log.Printf("Program executed successfully.")
}
// onInteraction calculates an operation based on the user's input, then responds to the interaction.
//
// In this example, onInteraction is called when a user sends a `/calculate` interaction to the bot.
func onInteraction(bot *disgo.Client, interaction *disgo.Interaction) error {
// access the interaction's options in the order provided by the user.
//
// The following commands assist with type converting Interaction Data
// into the respective data structs.
//
// ApplicationCommand()
// MessageComponent()
// ModalSubmit()
//
options := interaction.ApplicationCommand().Options
// Alternatively, convert the option slice into a map (in an efficient manner).
//
// Note: It's recommended to specify the amount explicitly when possible (i.e 5).
// Otherwise, the tools package provides an easy way to determine the amount of options.
optionMap := tools.OptionsToMap(nil, options, tools.NumOptions(options))
// This example stores the calculation in the following variable.
var calculation string
// Perform the given calculation based on the user's input.
//
// Use the option name to select it from the map.
if _, ok := optionMap["add"]; ok {
// add two integers (in a safe manner).
if group, ok := optionMap["int"]; ok && len(group.Options) != 0 {
var sum int64
for _, option := range group.Options {
value, err := option.Value.Int64()
if err != nil {
return fmt.Errorf("An option expected to be an integer was not one: %w", err)
}
sum += value
}
calculation = strconv.FormatInt(sum, 10)
}
// concatenate two strings (in a safe manner).
if group, ok := optionMap["string"]; ok {
for _, option := range group.Options {
calculation += option.Value.String()
}
}
}
// subtract two doubles.
if _, ok := optionMap["subtract"]; ok {
// The options for this command are required, but server-side validation is provided.
minuend, ok := optionMap["minuend"]
if !ok {
return fmt.Errorf("The minuend was not provided")
}
subtrahend, ok := optionMap["subtrahend"]
if !ok {
return fmt.Errorf("The subtrahend was not provided")
}
minuendFloat, err := minuend.Value.Float64()
if err != nil {
return fmt.Errorf("An option expected to be a double (float) was not one: %w", err)
}
subtrahendFloat, err := subtrahend.Value.Float64()
if err != nil {
return fmt.Errorf("An option expected to be a double (float) was not one: %w", err)
}
difference := minuendFloat - subtrahendFloat
calculation = fmt.Sprintf("%.3f", difference)
}
// calculation is empty when no subcommand options were provided.
if calculation == "" {
calculation = "There was nothing to do."
}
log.Println("Creating a response to the interaction...")
// send an interaction response to reply to the user.
requestCreateInteractionResponse := &disgo.CreateInteractionResponse{
InteractionID: interaction.ID,
// Interaction tokens are valid for 15 minutes,
// but an initial response to an interaction must be sent within 3 seconds (of receiving it),
// otherwise the token is invalidated.
InteractionToken: interaction.Token,
// https://discord.com/developers/docs/interactions/receiving-and-responding#responding-to-an-interaction
InteractionResponse: &disgo.InteractionResponse{
Type: disgo.FlagInteractionCallbackTypeCHANNEL_MESSAGE_WITH_SOURCE,
// Any of the following objects can be used.
//
// Messages
// https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-messages
//
// Autocomplete
// https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-autocomplete
//
// Modal
// https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal
Data: &disgo.Messages{
Content: disgo.Pointer(calculation), // or &calculation
},
},
}
if err := requestCreateInteractionResponse.Send(bot); err != nil {
return fmt.Errorf("error sending interaction response: %w", err)
}
log.Println("Sent a response to the interaction.")
return nil
}