package main import ( "database/sql" "fmt" "log" "os" "os/signal" "syscall" "strings" "github.com/BurntSushi/toml" "github.com/bwmarrin/discordgo" _ "github.com/lib/pq" ) var ( config Config client *discordgo.Session db *sql.DB ) type Config struct { Discord Discord `toml:"discord"` Database Database `toml:"database"` } type Discord struct { Token string `toml:"token"` AppID string `toml:"appid"` GuildID string `toml:"guildid"` } type Database struct { Host string `toml:"host"` Port int `toml:"port"` Name string `toml:"name"` Username string `toml:"username"` Password string `toml:"password"` } func loadConfig(filename string) (Config, error) { var config Config _, err := toml.DecodeFile(filename, &config) return config, err } func connectDb(config Config) (*sql.DB, error) { connectionString := fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", config.Database.Host, config.Database.Port, config.Database.Username, config.Database.Password, config.Database.Name, ) db, err := sql.Open("postgres", connectionString) if err != nil { return nil, fmt.Errorf("error connecting to the database: %v", err) } err = db.Ping() if err != nil { db.Close() return nil, fmt.Errorf("error pinging the database: %v", err) } log.Println("Successfully connected to the database.") return db, nil } func init() { var err error config, err = loadConfig("config.toml") if err != nil { log.Println("Error occurred whilst trying to load config:", err) return } client, err = discordgo.New("Bot " + config.Discord.Token) if err != nil { log.Println("Error initializing bot:", err) return } db, err = connectDb(config) if err != nil { log.Println("Error initializing db connection:", err) return } } func searchLink(message, format, sep string) string { return fmt.Sprintf(format, strings.Join( strings.Split( message, " ", ), sep, )) } var ( commands = []discordgo.ApplicationCommand{ { Name: "rickroll-em", Type: discordgo.UserApplicationCommand, }, { Name: "google-it", Type: discordgo.MessageApplicationCommand, }, { Name: "stackoverflow-it", Type: discordgo.MessageApplicationCommand, }, { Name: "godoc-it", Type: discordgo.MessageApplicationCommand, }, { Name: "discordjs-it", Type: discordgo.MessageApplicationCommand, }, { Name: "discordpy-it", Type: discordgo.MessageApplicationCommand, }, } commandsHandlers = map[string]func(client *discordgo.Session, i *discordgo.InteractionCreate){ "rickroll-em": func(client *discordgo.Session, i *discordgo.InteractionCreate) { err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Operation rickroll has begun", Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { panic(err) } ch, err := client.UserChannelCreate( i.ApplicationCommandData().TargetID, ) if err != nil { _, err = client.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ Content: fmt.Sprintf("Mission failed. Cannot send a message to this user: %q", err.Error()), Flags: discordgo.MessageFlagsEphemeral, }) if err != nil { panic(err) } } _, err = client.ChannelMessageSend( ch.ID, fmt.Sprintf("%s sent you this: https://youtu.be/dQw4w9WgXcQ", i.Member.Mention()), ) if err != nil { panic(err) } }, "google-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( i.ApplicationCommandData().Resolved.Messages[i.ApplicationCommandData().TargetID].Content, "https://google.com/search?q=%s", "+"), Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { panic(err) } }, "stackoverflow-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( i.ApplicationCommandData().Resolved.Messages[i.ApplicationCommandData().TargetID].Content, "https://stackoverflow.com/search?q=%s", "+"), Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { panic(err) } }, "godoc-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( i.ApplicationCommandData().Resolved.Messages[i.ApplicationCommandData().TargetID].Content, "https://pkg.go.dev/search?q=%s", "+"), Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { panic(err) } }, "discordjs-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( i.ApplicationCommandData().Resolved.Messages[i.ApplicationCommandData().TargetID].Content, "https://discord.js.org/#/docs/main/stable/search?query=%s", "+"), Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { panic(err) } }, "discordpy-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( i.ApplicationCommandData().Resolved.Messages[i.ApplicationCommandData().TargetID].Content, "https://discordpy.readthedocs.io/en/stable/search.html?q=%s", "+"), Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { panic(err) } }, } ) func main() { if client == nil { log.Println("Bot client is not initialized") return } client.AddHandler(func(client *discordgo.Session, r *discordgo.Ready) { log.Println("Bot is online") }) client.AddHandler(func(client *discordgo.Session, i *discordgo.InteractionCreate) { if h, ok := commandsHandlers[i.ApplicationCommandData().Name]; ok { h(client, i) } }) cmdIDs := make(map[string]string, len(commands)) for _, cmd := range commands { rcmd, err := client.ApplicationCommandCreate(config.Discord.AppID, config.Discord.GuildID, &cmd) if err != nil { log.Fatalf("Cannot create slash command %q: %v", cmd.Name, err) } cmdIDs[rcmd.ID] = rcmd.Name } err := client.Open() if err != nil { log.Println("Error opening connection:", err) return } stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) <-stop log.Println("Gracefully shutting down.") client.Close() if db != nil { db.Close() } }