package main import ( "flag" "log" "os" "os/signal" "strings" "time" "github.com/bwmarrin/discordgo" "github.com/gocolly/colly" ) type BibleStructure struct { bibleref string verse string } func TopicScraper(keyword string) []BibleStructure { c := colly.NewCollector() c.SetRequestTimeout(120 * time.Second) verses := make([]BibleStructure, 0) c.OnHTML("div.verse", func(h *colly.HTMLElement) { v := BibleStructure{} v.bibleref = h.ChildText("a") v.verse = h.ChildText("p") verses = append(verses, v) }) c.Visit("https://www.openbible.info/topics/" + keyword) return verses } func VerseBuilder(search string, n int) string { // If user enters negative number if n < 1 { return "Invalid" } // Set max ammount of verses to 5 if n > 5 { n = 5 } topic := TopicScraper(search) topicLen := len(topic) // If site can't find any topics if topicLen == 0 { return "No results" } var returnString strings.Builder // String to be returned returnString.WriteString("Topic: " + search + "\n") // Populate return string for i := 0; i < n; i++ { if i < topicLen { returnString.WriteString(topic[i].bibleref + "\n" + "> " + topic[i].verse + "\n\n") } else { return returnString.String() } } return returnString.String() } // Bot parameters var ( GuildID = flag.String("guild", "", "Test guild ID. If not passed - bot registers commands globally") BotToken = flag.String("token", "[YOUR-DISCORD-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: "bible-topic", Description: "What does the bible say about a topic?", Type: discordgo.ChatApplicationCommand, Options: []*discordgo.ApplicationCommandOption{ { Name: "topic", Description: "A topic in the bible", Type: discordgo.ApplicationCommandOptionString, Required: true, Autocomplete: true, }, { Name: "amount", Description: "Amount of verses to see (max 5)", Type: discordgo.ApplicationCommandOptionInteger, Required: false, Autocomplete: false, }, }, }, } commandHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){ "bible-topic": func(s *discordgo.Session, i *discordgo.InteractionCreate) { switch i.Type { case discordgo.InteractionApplicationCommand: data := i.ApplicationCommandData() amountOfVerses := 1 if len(data.Options) > 1 { amountOfVerses = int(data.Options[1].IntValue()) } err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: VerseBuilder(data.Options[0].StringValue(), amountOfVerses), }, }) 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: "10 commandments", Value: "10 commandments", }, { Name: "homosexaulity", Value: "homosexaulity", }, { Name: "forgiveness", Value: "forgiveness", }, { Name: "sodom", Value: "sodom", }, { Name: "signs of the end times", Value: "signs of the end times", }, { Name: "bitter woman", Value: "bitter woman", }, { Name: "the purpose of the woman", Value: "the purpose of the woman", }, { Name: "woman", Value: "woman", }, { Name: "abortion", Value: "abortion", }, { Name: "harming your body", Value: "harming your body", }, { Name: "healthy eating", Value: "healthy eating", }, { Name: "confrontation", Value: "confrontation", }, { Name: "confusion", Value: "confusion", }, { Name: "non believers", Value: "non believers", }, { Name: "not giving up", Value: "not giving up", }, { Name: "truth", Value: "truth", }, { Name: "transvestites", Value: "transvestites", }, { Name: "telling the truth", Value: "telling the truth", }, { Name: "not telling the truth", Value: "not telling the truth", }, // 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: data.Options[0].StringValue(), }) } 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) } } }, } ) 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, 1) signal.Notify(stop, os.Interrupt) <-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) } } } }