Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Slash commands options auto completion (#1014)
* feat(interactions): options autocompletion * fix(examples/autocomplete): typo in comment Replaced "returining" with "returning"
- Loading branch information
1 parent
007bf76
commit fd6228c
Showing
2 changed files
with
276 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,255 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"os" | ||
"os/signal" | ||
|
||
"github.com/bwmarrin/discordgo" | ||
) | ||
|
||
// Bot parameters | ||
var ( | ||
GuildID = flag.String("guild", "", "Test guild ID. If not passed - bot registers commands globally") | ||
BotToken = flag.String("token", "", "Bot access token") | ||
RemoveCommands = flag.Bool("rmcmd", true, "Remove all commands after shutdowning or not") | ||
) | ||
|
||
var s *discordgo.Session | ||
|
||
func init() { flag.Parse() } | ||
|
||
func init() { | ||
var err error | ||
s, err = discordgo.New("Bot " + *BotToken) | ||
if err != nil { | ||
log.Fatalf("Invalid bot parameters: %v", err) | ||
} | ||
} | ||
|
||
var ( | ||
commands = []*discordgo.ApplicationCommand{ | ||
{ | ||
Name: "single-autocomplete", | ||
Description: "Showcase of single autocomplete option", | ||
Type: discordgo.ChatApplicationCommand, | ||
Options: []*discordgo.ApplicationCommandOption{ | ||
{ | ||
Name: "autocomplete-option", | ||
Description: "Autocomplete option", | ||
Type: discordgo.ApplicationCommandOptionString, | ||
Required: true, | ||
Autocomplete: true, | ||
}, | ||
}, | ||
}, | ||
{ | ||
Name: "multi-autocomplete", | ||
Description: "Showcase of multiple autocomplete option", | ||
Type: discordgo.ChatApplicationCommand, | ||
Options: []*discordgo.ApplicationCommandOption{ | ||
{ | ||
Name: "autocomplete-option-1", | ||
Description: "Autocomplete option 1", | ||
Type: discordgo.ApplicationCommandOptionString, | ||
Required: true, | ||
Autocomplete: true, | ||
}, | ||
{ | ||
Name: "autocomplete-option-2", | ||
Description: "Autocomplete option 2", | ||
Type: discordgo.ApplicationCommandOptionString, | ||
Required: true, | ||
Autocomplete: true, | ||
}, | ||
}, | ||
}, | ||
} | ||
|
||
commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){ | ||
"single-autocomplete": func(s *discordgo.Session, i *discordgo.InteractionCreate) { | ||
switch i.Type { | ||
case discordgo.InteractionApplicationCommand: | ||
data := i.ApplicationCommandData() | ||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ | ||
Type: discordgo.InteractionResponseChannelMessageWithSource, | ||
Data: &discordgo.InteractionResponseData{ | ||
Content: fmt.Sprintf( | ||
"You picked %q autocompletion", | ||
// Autocompleted options do not affect usual flow of handling application command. They are ordinary options at this stage | ||
data.Options[0].StringValue(), | ||
), | ||
}, | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
// Autocomplete options introduce a new interaction type (8) for returning custom autocomplete results. | ||
case discordgo.InteractionApplicationCommandAutocomplete: | ||
data := i.ApplicationCommandData() | ||
choices := []*discordgo.ApplicationCommandOptionChoice{ | ||
{ | ||
Name: "Autocomplete", | ||
Value: "autocomplete", | ||
}, | ||
{ | ||
Name: "Autocomplete is best!", | ||
Value: "autocomplete_is_best", | ||
}, | ||
{ | ||
Name: "Choice 3", | ||
Value: "choice3", | ||
}, | ||
{ | ||
Name: "Choice 4", | ||
Value: "choice4", | ||
}, | ||
{ | ||
Name: "Choice 5", | ||
Value: "choice5", | ||
}, | ||
// And so on, up to 25 choices | ||
} | ||
|
||
if data.Options[0].StringValue() != "" { | ||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{ | ||
Name: data.Options[0].StringValue(), // To get user input you just get value of the autocomplete option. | ||
Value: "choice_custom", | ||
}) | ||
} | ||
|
||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ | ||
Type: discordgo.InteractionApplicationCommandAutocompleteResult, | ||
Data: &discordgo.InteractionResponseData{ | ||
Choices: choices, // This is basically the whole purpose of autocomplete interaction - return custom options to the user. | ||
}, | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
}, | ||
"multi-autocomplete": func(s *discordgo.Session, i *discordgo.InteractionCreate) { | ||
switch i.Type { | ||
case discordgo.InteractionApplicationCommand: | ||
data := i.ApplicationCommandData() | ||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ | ||
Type: discordgo.InteractionResponseChannelMessageWithSource, | ||
Data: &discordgo.InteractionResponseData{ | ||
Content: fmt.Sprintf( | ||
"Option 1: %s\nOption 2: %s", | ||
data.Options[0].StringValue(), | ||
data.Options[1].StringValue(), | ||
), | ||
}, | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
case discordgo.InteractionApplicationCommandAutocomplete: | ||
data := i.ApplicationCommandData() | ||
var choices []*discordgo.ApplicationCommandOptionChoice | ||
switch { | ||
// In this case there are multiple autocomplete options. The Focused field shows which option user is focused on. | ||
case data.Options[0].Focused: | ||
choices = []*discordgo.ApplicationCommandOptionChoice{ | ||
{ | ||
Name: "Autocomplete 4 first option", | ||
Value: "autocomplete_default", | ||
}, | ||
{ | ||
Name: "Choice 3", | ||
Value: "choice3", | ||
}, | ||
{ | ||
Name: "Choice 4", | ||
Value: "choice4", | ||
}, | ||
{ | ||
Name: "Choice 5", | ||
Value: "choice5", | ||
}, | ||
} | ||
if data.Options[0].StringValue() != "" { | ||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{ | ||
Name: data.Options[0].StringValue(), | ||
Value: "choice_custom", | ||
}) | ||
} | ||
|
||
case data.Options[1].Focused: | ||
choices = []*discordgo.ApplicationCommandOptionChoice{ | ||
{ | ||
Name: "Autocomplete 4 second option", | ||
Value: "autocomplete_1_default", | ||
}, | ||
{ | ||
Name: "Choice 3.1", | ||
Value: "choice3_1", | ||
}, | ||
{ | ||
Name: "Choice 4.1", | ||
Value: "choice4_1", | ||
}, | ||
{ | ||
Name: "Choice 5.1", | ||
Value: "choice5_1", | ||
}, | ||
} | ||
if data.Options[1].StringValue() != "" { | ||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{ | ||
Name: data.Options[1].StringValue(), | ||
Value: "choice_custom_2", | ||
}) | ||
} | ||
} | ||
|
||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ | ||
Type: discordgo.InteractionApplicationCommandAutocompleteResult, | ||
Data: &discordgo.InteractionResponseData{ | ||
Choices: choices, | ||
}, | ||
}) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} | ||
}, | ||
} | ||
) | ||
|
||
func main() { | ||
s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) { log.Println("Bot is up!") }) | ||
s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) { | ||
if h, ok := commandHandlers[i.ApplicationCommandData().Name]; ok { | ||
h(s, i) | ||
} | ||
}) | ||
err := s.Open() | ||
if err != nil { | ||
log.Fatalf("Cannot open the session: %v", err) | ||
} | ||
defer s.Close() | ||
|
||
createdCommands, err := s.ApplicationCommandBulkOverwrite(s.State.User.ID, *GuildID, commands) | ||
|
||
if err != nil { | ||
log.Fatalf("Cannot register commands: %v", err) | ||
} | ||
|
||
stop := make(chan os.Signal) | ||
signal.Notify(stop, os.Interrupt) //nolint: staticcheck | ||
<-stop | ||
log.Println("Gracefully shutting down") | ||
|
||
if *RemoveCommands { | ||
for _, cmd := range createdCommands { | ||
err := s.ApplicationCommandDelete(s.State.User.ID, *GuildID, cmd.ID) | ||
if err != nil { | ||
log.Fatalf("Cannot delete %q command: %v", cmd.Name, err) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters