123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450 |
- package main
- import (
- "flag"
- "fmt"
- "log"
- "os"
- "os/signal"
- "strings"
- "time"
- "github.com/bwmarrin/discordgo"
- )
- // Bot parameters
- var (
- GuildID = flag.String("guild", "", "Test guild ID")
- BotToken = flag.String("token", "", "Bot access token")
- AppID = flag.String("app", "", "Application ID")
- )
- 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)
- }
- }
- // Important note: call every command in order it's placed in the example.
- var (
- componentsHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){
- "fd_no": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "Huh. I see, maybe some of these resources might help you?",
- Flags: 1 << 6,
- Components: []discordgo.MessageComponent{
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "📜",
- },
- Label: "Documentation",
- Style: discordgo.LinkButton,
- URL: "https://discord.com/developers/docs/interactions/message-components#buttons",
- },
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "🔧",
- },
- Label: "Discord developers",
- Style: discordgo.LinkButton,
- URL: "https://discord.gg/discord-developers",
- },
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "🦫",
- },
- Label: "Discord Gophers",
- Style: discordgo.LinkButton,
- URL: "https://discord.gg/7RuRrVHyXF",
- },
- },
- },
- },
- },
- })
- if err != nil {
- panic(err)
- }
- },
- "fd_yes": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "Great! If you wanna know more or just have questions, feel free to visit Discord Devs and Discord Gophers server. " +
- "But now, when you know how buttons work, let's move onto select menus (execute `/selects single`)",
- Flags: 1 << 6,
- Components: []discordgo.MessageComponent{
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "🔧",
- },
- Label: "Discord developers",
- Style: discordgo.LinkButton,
- URL: "https://discord.gg/discord-developers",
- },
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "🦫",
- },
- Label: "Discord Gophers",
- Style: discordgo.LinkButton,
- URL: "https://discord.gg/7RuRrVHyXF",
- },
- },
- },
- },
- },
- })
- if err != nil {
- panic(err)
- }
- },
- "select": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- var response *discordgo.InteractionResponse
- data := i.MessageComponentData()
- switch data.Values[0] {
- case "go":
- response = &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "This is the way.",
- Flags: 1 << 6,
- },
- }
- default:
- response = &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "It is not the way to go.",
- Flags: 1 << 6,
- },
- }
- }
- err := s.InteractionRespond(i.Interaction, response)
- if err != nil {
- panic(err)
- }
- time.Sleep(time.Second) // Doing that so user won't see instant response.
- _, err = s.FollowupMessageCreate(*AppID, i.Interaction, true, &discordgo.WebhookParams{
- Content: "Anyways, now when you know how to use single select menus, let's see how multi select menus work. " +
- "Try calling `/selects multi` command.",
- Flags: 1 << 6,
- })
- if err != nil {
- panic(err)
- }
- },
- "stackoverflow_tags": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- data := i.MessageComponentData()
- const stackoverflowFormat = `https://stackoverflow.com/questions/tagged/%s`
- err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "Here is your stackoverflow URL: " + fmt.Sprintf(stackoverflowFormat, strings.Join(data.Values, "+")),
- Flags: 1 << 6,
- },
- })
- if err != nil {
- panic(err)
- }
- time.Sleep(time.Second) // Doing that so user won't see instant response.
- _, err = s.FollowupMessageCreate(*AppID, i.Interaction, true, &discordgo.WebhookParams{
- Content: "Now you know everything about select component. If you want to know more or ask a question - feel free to.",
- Components: []discordgo.MessageComponent{
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "📜",
- },
- Label: "Documentation",
- Style: discordgo.LinkButton,
- URL: "https://discord.com/developers/docs/interactions/message-components#select-menus",
- },
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "🔧",
- },
- Label: "Discord developers",
- Style: discordgo.LinkButton,
- URL: "https://discord.gg/discord-developers",
- },
- discordgo.Button{
- Emoji: discordgo.ComponentEmoji{
- Name: "🦫",
- },
- Label: "Discord Gophers",
- Style: discordgo.LinkButton,
- URL: "https://discord.gg/7RuRrVHyXF",
- },
- },
- },
- },
- Flags: 1 << 6,
- })
- if err != nil {
- panic(err)
- }
- },
- }
- commandsHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){
- "buttons": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "Are you comfortable with buttons and other message components?",
- Flags: 1 << 6,
- // Buttons and other components are specified in Components field.
- Components: []discordgo.MessageComponent{
- // ActionRow is a container of all buttons within the same row.
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.Button{
- // Label is what the user will see on the button.
- Label: "Yes",
- // Style provides coloring of the button. There are not so many styles tho.
- Style: discordgo.SuccessButton,
- // Disabled allows bot to disable some buttons for users.
- Disabled: false,
- // CustomID is a thing telling Discord which data to send when this button will be pressed.
- CustomID: "fd_yes",
- },
- discordgo.Button{
- Label: "No",
- Style: discordgo.DangerButton,
- Disabled: false,
- CustomID: "fd_no",
- },
- discordgo.Button{
- Label: "I don't know",
- Style: discordgo.LinkButton,
- Disabled: false,
- // Link buttons don't require CustomID and do not trigger the gateway/HTTP event
- URL: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
- Emoji: discordgo.ComponentEmoji{
- Name: "🤷",
- },
- },
- },
- },
- // The message may have multiple actions rows.
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.Button{
- Label: "Discord Developers server",
- Style: discordgo.LinkButton,
- Disabled: false,
- URL: "https://discord.gg/discord-developers",
- },
- },
- },
- },
- },
- })
- if err != nil {
- panic(err)
- }
- },
- "selects": func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- var response *discordgo.InteractionResponse
- switch i.ApplicationCommandData().Options[0].Name {
- case "single":
- response = &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "Now let's take a look on selects. This is single item select menu.",
- Flags: 1 << 6,
- Components: []discordgo.MessageComponent{
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.SelectMenu{
- // Select menu, as other components, must have a customID, so we set it to this value.
- CustomID: "select",
- Placeholder: "Choose your favorite programming language 👇",
- Options: []discordgo.SelectMenuOption{
- {
- Label: "Go",
- // As with components, this things must have their own unique "id" to identify which is which.
- // In this case such id is Value field.
- Value: "go",
- Emoji: discordgo.ComponentEmoji{
- Name: "🦦",
- },
- // You can also make it a default option, but in this case we won't.
- Default: false,
- Description: "Go programming language",
- },
- {
- Label: "JS",
- Value: "js",
- Emoji: discordgo.ComponentEmoji{
- Name: "🟨",
- },
- Description: "JavaScript programming language",
- },
- {
- Label: "Python",
- Value: "py",
- Emoji: discordgo.ComponentEmoji{
- Name: "🐍",
- },
- Description: "Python programming language",
- },
- },
- },
- },
- },
- },
- },
- }
- case "multi":
- response = &discordgo.InteractionResponse{
- Type: discordgo.InteractionResponseChannelMessageWithSource,
- Data: &discordgo.InteractionResponseData{
- Content: "The tastiest things are left for the end. Let's see how the multi-item select menu works: " +
- "try generating your own stackoverflow search link",
- Flags: 1 << 6,
- Components: []discordgo.MessageComponent{
- discordgo.ActionsRow{
- Components: []discordgo.MessageComponent{
- discordgo.SelectMenu{
- CustomID: "stackoverflow_tags",
- Placeholder: "Select tags to search on StackOverflow",
- // This is where confusion comes from. If you don't specify these things you will get single item select.
- // These fields control the minimum and maximum amount of selected items.
- MinValues: 1,
- MaxValues: 3,
- Options: []discordgo.SelectMenuOption{
- {
- Label: "Go",
- Description: "Simple yet powerful programming language",
- Value: "go",
- // Default works the same for multi-select menus.
- Default: false,
- Emoji: discordgo.ComponentEmoji{
- Name: "🦦",
- },
- },
- {
- Label: "JS",
- Description: "Multiparadigm OOP language",
- Value: "javascript",
- Emoji: discordgo.ComponentEmoji{
- Name: "🟨",
- },
- },
- {
- Label: "Python",
- Description: "OOP prototyping programming language",
- Value: "python",
- Emoji: discordgo.ComponentEmoji{
- Name: "🐍",
- },
- },
- {
- Label: "Web",
- Description: "Web related technologies",
- Value: "web",
- Emoji: discordgo.ComponentEmoji{
- Name: "🌐",
- },
- },
- {
- Label: "Desktop",
- Description: "Desktop applications",
- Value: "desktop",
- Emoji: discordgo.ComponentEmoji{
- Name: "💻",
- },
- },
- },
- },
- },
- },
- },
- },
- }
- }
- err := s.InteractionRespond(i.Interaction, response)
- if err != nil {
- panic(err)
- }
- },
- }
- )
- func main() {
- s.AddHandler(func(s *discordgo.Session, r *discordgo.Ready) {
- log.Println("Bot is up!")
- })
- // Components are part of interactions, so we register InteractionCreate handler
- s.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
- switch i.Type {
- case discordgo.InteractionApplicationCommand:
- if h, ok := commandsHandlers[i.ApplicationCommandData().Name]; ok {
- h(s, i)
- }
- case discordgo.InteractionMessageComponent:
- if h, ok := componentsHandlers[i.MessageComponentData().CustomID]; ok {
- h(s, i)
- }
- }
- })
- _, err := s.ApplicationCommandCreate(*AppID, *GuildID, &discordgo.ApplicationCommand{
- Name: "buttons",
- Description: "Test the buttons if you got courage",
- })
- if err != nil {
- log.Fatalf("Cannot create slash command: %v", err)
- }
- _, err = s.ApplicationCommandCreate(*AppID, *GuildID, &discordgo.ApplicationCommand{
- Name: "selects",
- Options: []*discordgo.ApplicationCommandOption{
- {
- Type: discordgo.ApplicationCommandOptionSubCommand,
- Name: "multi",
- Description: "Multi-item select menu",
- },
- {
- Type: discordgo.ApplicationCommandOptionSubCommand,
- Name: "single",
- Description: "Single-item select menu",
- },
- },
- Description: "Lo and behold: dropdowns are coming",
- })
- if err != nil {
- log.Fatalf("Cannot create slash command: %v", err)
- }
- err = s.Open()
- if err != nil {
- log.Fatalf("Cannot open the session: %v", err)
- }
- defer s.Close()
- stop := make(chan os.Signal, 1)
- signal.Notify(stop, os.Interrupt)
- <-stop
- log.Println("Graceful shutdown")
- }
|