From 9afda27ab4a11ea48b2ed70d6ac764bdb913df69 Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 17:16:13 +0200 Subject: [PATCH 01/10] Actually add db connection --- go.mod | 1 + main.go | 54 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index b5f4657..77e66f2 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.4 require ( github.com/BurntSushi/toml v1.4.0 github.com/bwmarrin/discordgo v0.28.1 + github.com/lib/pq v1.10.9 ) require ( diff --git a/main.go b/main.go index 102b92f..1cea043 100644 --- a/main.go +++ b/main.go @@ -2,15 +2,23 @@ package main import ( "fmt" + "log" "os" "os/signal" "syscall" + "database/sql" + "github.com/BurntSushi/toml" "github.com/bwmarrin/discordgo" + _ "github.com/lib/pq" ) -var client *discordgo.Session +var ( + config Config + client *discordgo.Session + db *sql.DB +) type Config struct { Discord Discord `toml:"discord"` @@ -37,35 +45,63 @@ func loadConfig(filename string) (Config, error) { return config, err } -func init() { - config, err := loadConfig("config.toml") +func connectDb(config Config) { + var err 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, + ) + log.Println(connectionString) + + db, err := sql.Open("postgres", connectionString) if err != nil { - fmt.Println("Error occurred whilst trying to load config:", err) + log.Fatalf("Error connecting to the database: %v", err) + } + defer db.Close() + + err = db.Ping() + if err != nil { + log.Fatalf("Error pinging the database: %v", err) + } +} + +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 { - fmt.Println("Error initializing bot:", err) + log.Println("Error initializing bot:", err) return } + connectDb(config) + } func main() { if client == nil { - fmt.Println("Bot client is not initialized") + log.Println("Bot client is not initialized") return } client.AddHandler(func(client *discordgo.Session, r *discordgo.Ready) { - fmt.Println("Bot is online") + log.Println("Bot is online") }) err := client.Open() if err != nil { - fmt.Println("Error opening connection:", err) + log.Println("Error opening connection:", err) return } @@ -73,7 +109,7 @@ func main() { signal.Notify(stop, os.Interrupt, syscall.SIGTERM) <-stop - fmt.Println("Gracefully shutting down.") + log.Println("Gracefully shutting down.") client.Close() } -- 2.45.2 From f24c836cbc7dcf0795b841895393eccb4806b6ef Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 17:27:04 +0200 Subject: [PATCH 02/10] Refactor and modify db init --- main.go | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/main.go b/main.go index 1cea043..266369b 100644 --- a/main.go +++ b/main.go @@ -1,13 +1,12 @@ package main import ( + "database/sql" "fmt" "log" "os" "os/signal" "syscall" - "database/sql" - "github.com/BurntSushi/toml" "github.com/bwmarrin/discordgo" @@ -15,9 +14,9 @@ import ( ) var ( - config Config - client *discordgo.Session - db *sql.DB + config Config + client *discordgo.Session + db *sql.DB ) type Config struct { @@ -38,16 +37,13 @@ type Database struct { 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) { - var err error - +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, @@ -60,14 +56,17 @@ func connectDb(config Config) { db, err := sql.Open("postgres", connectionString) if err != nil { - log.Fatalf("Error connecting to the database: %v", err) + return nil, fmt.Errorf("error connecting to the database: %v", err) } - defer db.Close() err = db.Ping() if err != nil { - log.Fatalf("Error pinging the database: %v", err) + db.Close() + return nil, fmt.Errorf("error pinging the database: %v", err) } + + log.Println("Successfully connected to the database.") + return db, nil } func init() { @@ -84,9 +83,11 @@ func init() { return } - connectDb(config) - - + db, err = connectDb(config) + if err != nil { + log.Println("Error initializing db connection:", err) + return + } } func main() { @@ -103,7 +104,7 @@ func main() { if err != nil { log.Println("Error opening connection:", err) return - } + } stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) @@ -111,5 +112,9 @@ func main() { log.Println("Gracefully shutting down.") client.Close() + + if db != nil { + db.Close() + } } -- 2.45.2 From 6de41e01801ee0ed40fbc41d24eea6c64476455a Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 17:37:07 +0200 Subject: [PATCH 03/10] Steal slash commands for testing purposes --- main.go | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 266369b..99b01c3 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "os" "os/signal" "syscall" + "strings" "github.com/BurntSushi/toml" "github.com/bwmarrin/discordgo" @@ -26,6 +27,7 @@ type Config struct { type Discord struct { Token string `toml:"client"` + AppID string `toml:"appid"` GuildID string `toml:"guildid"` } @@ -52,7 +54,6 @@ func connectDb(config Config) (*sql.DB, error) { config.Database.Password, config.Database.Name, ) - log.Println(connectionString) db, err := sql.Open("postgres", connectionString) if err != nil { @@ -90,6 +91,149 @@ func init() { } } +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(s *discordgo.Session, i *discordgo.InteractionCreate){ + "rickroll-em": func(s *discordgo.Session, i *discordgo.InteractionCreate) { + err := s.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 := s.UserChannelCreate( + i.ApplicationCommandData().TargetID, + ) + if err != nil { + _, err = s.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 = s.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 := s.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 := s.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 := s.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 := s.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 := s.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") @@ -100,6 +244,25 @@ func main() { 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) -- 2.45.2 From 2598aa854ad6dd4b8b77d565bb9dc97f2ad019c3 Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 17:52:22 +0200 Subject: [PATCH 04/10] Hm --- main.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/main.go b/main.go index 99b01c3..f32c8aa 100644 --- a/main.go +++ b/main.go @@ -26,7 +26,7 @@ type Config struct { } type Discord struct { - Token string `toml:"client"` + Token string `toml:"token"` AppID string `toml:"appid"` GuildID string `toml:"guildid"` } @@ -128,9 +128,9 @@ var ( Type: discordgo.MessageApplicationCommand, }, } - commandsHandlers = map[string]func(s *discordgo.Session, i *discordgo.InteractionCreate){ - "rickroll-em": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + 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", @@ -141,11 +141,11 @@ var ( panic(err) } - ch, err := s.UserChannelCreate( + ch, err := client.UserChannelCreate( i.ApplicationCommandData().TargetID, ) if err != nil { - _, err = s.FollowupMessageCreate(i.Interaction, true, &discordgo.WebhookParams{ + _, 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, }) @@ -153,7 +153,7 @@ var ( panic(err) } } - _, err = s.ChannelMessageSend( + _, err = client.ChannelMessageSend( ch.ID, fmt.Sprintf("%s sent you this: https://youtu.be/dQw4w9WgXcQ", i.Member.Mention()), ) @@ -162,7 +162,7 @@ var ( } }, "google-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( @@ -176,7 +176,7 @@ var ( } }, "stackoverflow-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( @@ -190,7 +190,7 @@ var ( } }, "godoc-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( @@ -204,7 +204,7 @@ var ( } }, "discordjs-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( @@ -218,7 +218,7 @@ var ( } }, "discordpy-it": func(s *discordgo.Session, i *discordgo.InteractionCreate) { - err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: searchLink( -- 2.45.2 From e57d3ff4fe5126df4a94ab84941a7808f134bc57 Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 18:06:55 +0200 Subject: [PATCH 05/10] Fix command creation --- main.go | 121 +++++++------------------------------------------------- 1 file changed, 15 insertions(+), 106 deletions(-) diff --git a/main.go b/main.go index f32c8aa..8bc2cb9 100644 --- a/main.go +++ b/main.go @@ -6,8 +6,8 @@ import ( "log" "os" "os/signal" - "syscall" "strings" + "syscall" "github.com/BurntSushi/toml" "github.com/bwmarrin/discordgo" @@ -27,7 +27,7 @@ type Config struct { type Discord struct { Token string `toml:"token"` - AppID string `toml:"appid"` + AppID string `toml:"appid"` GuildID string `toml:"guildid"` } @@ -105,27 +105,7 @@ 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, + Description: "List all webhooks in the server", }, } commandsHandlers = map[string]func(client *discordgo.Session, i *discordgo.InteractionCreate){ @@ -161,76 +141,6 @@ var ( 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) - } - }, } ) @@ -242,27 +152,26 @@ func main() { client.AddHandler(func(client *discordgo.Session, r *discordgo.Ready) { log.Println("Bot is online") + + cmdIDs := make(map[string]string, len(commands)) + + for _, cmd := range commands { + fmt.Println(cmd) + rcmd, err := client.ApplicationCommandCreate(client.State.User.ID, config.Discord.GuildID, &cmd) + if err != nil { + log.Fatalf("Cannot create slash command %q: %v", cmd.Name, err) + } + + cmdIDs[rcmd.ID] = rcmd.Name + } }) - 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) -- 2.45.2 From 3abc4f32652b7fc7a3e5bc1ed0d9d8e405d049cb Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 18:57:43 +0200 Subject: [PATCH 06/10] Make a ticket creation bot as a base --- main.go | 177 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 137 insertions(+), 40 deletions(-) diff --git a/main.go b/main.go index 8bc2cb9..3a0e69e 100644 --- a/main.go +++ b/main.go @@ -6,7 +6,6 @@ import ( "log" "os" "os/signal" - "strings" "syscall" "github.com/BurntSushi/toml" @@ -18,6 +17,8 @@ var ( config Config client *discordgo.Session db *sql.DB + + globalCategoryID string ) type Config struct { @@ -91,54 +92,145 @@ func init() { } } -func searchLink(message, format, sep string) string { - return fmt.Sprintf(format, strings.Join( - strings.Split( - message, - " ", - ), - sep, - )) -} - var ( commands = []discordgo.ApplicationCommand{ { - Name: "rickroll-em", - Description: "List all webhooks in the server", + Name: "setup", + Description: "Setup a channel for ticket creation", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionChannel, + Name: "channel", + Description: "Channel for ticket creation", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionChannel, + Name: "category", + Description: "Category for tickets", + Required: true, + }, + }, }, } commandsHandlers = map[string]func(client *discordgo.Session, i *discordgo.InteractionCreate){ - "rickroll-em": func(client *discordgo.Session, i *discordgo.InteractionCreate) { + "setup": func(client *discordgo.Session, i *discordgo.InteractionCreate) { + channelOption := i.ApplicationCommandData().Options[0].ChannelValue(client) + categoryOption := i.ApplicationCommandData().Options[1].ChannelValue(client) + + globalCategoryID = categoryOption.ID + + + _, err := client.ChannelMessageSendComplex(channelOption.ID, &discordgo.MessageSend{ + Content: "Click the button below to request a HWID reset:", + Components: []discordgo.MessageComponent{ + discordgo.ActionsRow{ + Components: []discordgo.MessageComponent{ + discordgo.Button{ + Label: "Request Reset", + Style: discordgo.PrimaryButton, + CustomID: "create_ticket", + }, + }, + }, + }, + }) + if err != nil { + log.Println("Error sending message to the specified channel:", err) + } + + + err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Setup completed! Request creation button has been added to %s", channelOption.Mention()), + }, + }) + if err != nil { + log.Println("Error sending interaction response:", err) + } + }, + } + componentsHandlers = map[string]func(client *discordgo.Session, i *discordgo.InteractionCreate){ + "create_ticket": 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", + Content: "For what would u like a HWID reset:", + Components: []discordgo.MessageComponent{ + discordgo.ActionsRow{ + Components: []discordgo.MessageComponent{ + discordgo.SelectMenu{ + CustomID: "select_software_type", + Placeholder: "Choose which software", + Options: []discordgo.SelectMenuOption{ + { + Label: "Vanity", + Value: "vanity", + Description: "Request a vanity reset", + }, + { + Label: "Mesa", + Value: "mesa", + Description: "Request a mesa reset", + }, + }, + }, + }, + }, + }, + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + if err != nil { + log.Println("Error sending interaction response:", err) + } + }, + "select_software_type": func(client *discordgo.Session, i *discordgo.InteractionCreate) { + selectedOption := i.MessageComponentData().Values[0] + var softwareType string + if selectedOption == "vanity" { + softwareType = "Vanity" + } else if selectedOption == "mesa" { + softwareType = "Mesa" + } + + guildID := i.GuildID + userID := i.Member.User.ID + userName := i.Member.User.Username + + + channel, err := client.GuildChannelCreateComplex(guildID, discordgo.GuildChannelCreateData{ + Name: fmt.Sprintf("reset-%s-%s", softwareType, userName), + Type: discordgo.ChannelTypeGuildText, + ParentID: globalCategoryID, + PermissionOverwrites: []*discordgo.PermissionOverwrite{ + { + ID: guildID, + Type: discordgo.PermissionOverwriteTypeRole, + Deny: discordgo.PermissionViewChannel, + }, + { + ID: userID, + Type: discordgo.PermissionOverwriteTypeMember, + Allow: discordgo.PermissionViewChannel, + }, + }, + }) + if err != nil { + log.Println("Error creating hwid request channel:", err) + return + } + + err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("Request created: %s", channel.Mention()), 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) + log.Println("Error sending interaction response:", err) } }, } @@ -156,7 +248,6 @@ func main() { cmdIDs := make(map[string]string, len(commands)) for _, cmd := range commands { - fmt.Println(cmd) rcmd, err := client.ApplicationCommandCreate(client.State.User.ID, config.Discord.GuildID, &cmd) if err != nil { log.Fatalf("Cannot create slash command %q: %v", cmd.Name, err) @@ -167,8 +258,14 @@ func main() { }) client.AddHandler(func(client *discordgo.Session, i *discordgo.InteractionCreate) { - if h, ok := commandsHandlers[i.ApplicationCommandData().Name]; ok { - h(client, i) + if i.Type == discordgo.InteractionApplicationCommand { + if h, ok := commandsHandlers[i.ApplicationCommandData().Name]; ok { + h(client, i) + } + } else if i.Type == discordgo.InteractionMessageComponent { + if h, ok := componentsHandlers[i.MessageComponentData().CustomID]; ok { + h(client, i) + } } }) @@ -176,7 +273,7 @@ func main() { if err != nil { log.Println("Error opening connection:", err) return - } + } stop := make(chan os.Signal, 1) signal.Notify(stop, os.Interrupt, syscall.SIGTERM) -- 2.45.2 From 71bec93b581c48e3555853785125e0867215795c Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 19:22:38 +0200 Subject: [PATCH 07/10] Impl Basc System --- main.go | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 126 insertions(+), 13 deletions(-) diff --git a/main.go b/main.go index 3a0e69e..958b7ec 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ import ( "log" "os" "os/signal" + "strings" "syscall" "github.com/BurntSushi/toml" @@ -22,8 +23,9 @@ var ( ) type Config struct { - Discord Discord `toml:"discord"` - Database Database `toml:"database"` + Discord Discord `toml:"discord"` + Database Database `toml:"database"` + AdminRoles []string `toml:"admin_roles"` } type Discord struct { @@ -92,6 +94,10 @@ func init() { } } +func reset(userNickname, softwareType string) { + log.Printf("Resetting %s for user %s\n", softwareType, userNickname) +} + var ( commands = []discordgo.ApplicationCommand{ { @@ -120,7 +126,6 @@ var ( globalCategoryID = categoryOption.ID - _, err := client.ChannelMessageSendComplex(channelOption.ID, &discordgo.MessageSend{ Content: "Click the button below to request a HWID reset:", Components: []discordgo.MessageComponent{ @@ -139,7 +144,6 @@ var ( log.Println("Error sending message to the specified channel:", err) } - err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ @@ -156,7 +160,7 @@ var ( err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Content: "For what would u like a HWID reset:", + Content: "For what would you like a HWID reset:", Components: []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ @@ -199,7 +203,6 @@ var ( userID := i.Member.User.ID userName := i.Member.User.Username - channel, err := client.GuildChannelCreateComplex(guildID, discordgo.GuildChannelCreateData{ Name: fmt.Sprintf("reset-%s-%s", softwareType, userName), Type: discordgo.ChannelTypeGuildText, @@ -210,11 +213,6 @@ var ( Type: discordgo.PermissionOverwriteTypeRole, Deny: discordgo.PermissionViewChannel, }, - { - ID: userID, - Type: discordgo.PermissionOverwriteTypeMember, - Allow: discordgo.PermissionViewChannel, - }, }, }) if err != nil { @@ -222,15 +220,125 @@ var ( return } + for _, roleID := range config.AdminRoles { + client.ChannelPermissionSet(channel.ID, roleID, discordgo.PermissionOverwriteTypeRole, discordgo.PermissionViewChannel, 0) + } + err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Content: fmt.Sprintf("Request created: %s", channel.Mention()), + Content: "Request submitted, you'll get a PM when it has been processed", Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { log.Println("Error sending interaction response:", err) + return + } + + _, err = client.ChannelMessageSendComplex(channel.ID, &discordgo.MessageSend{ + Content: fmt.Sprintf("Reset request by %s for %s", userName, softwareType), + Components: []discordgo.MessageComponent{ + discordgo.ActionsRow{ + Components: []discordgo.MessageComponent{ + discordgo.Button{ + Label: "Accept", + Style: discordgo.PrimaryButton, + CustomID: fmt.Sprintf("accept_%s_%s", userID, softwareType), + }, + discordgo.Button{ + Label: "Decline", + Style: discordgo.DangerButton, + CustomID: fmt.Sprintf("decline_%s_%s", userID, softwareType), + }, + }, + }, + }, + }) + if err != nil { + log.Println("Error sending message to the ticket channel:", err) + } + }, +"accept": func(client *discordgo.Session, i *discordgo.InteractionCreate) { + data := i.MessageComponentData().CustomID + parts := strings.Split(data, "_") + if len(parts) != 3 { + log.Println("Invalid accept button custom ID") + return + } + userID := parts[1] + softwareType := parts[2] + + + member, err := client.GuildMember(i.GuildID, userID) + if err != nil { + log.Println("Error fetching member info:", err) + return + } + var userName string + if member.Nick != "" { + userName = member.Nick + } else if member.User.GlobalName != "" { + userName = member.User.GlobalName + } else { + userName = member.User.Username + } + + reset(userName, softwareType) + + err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Request accepted and processed.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + if err != nil { + log.Println("Error sending interaction response:", err) + } + + dmChannel, err := client.UserChannelCreate(userID) + if err != nil { + log.Println("Error creating DM channel:", err) + return + } + + _, err = client.ChannelMessageSend(dmChannel.ID, fmt.Sprintf("Your reset request for %s has been accepted and processed.", softwareType)) + if err != nil { + log.Println("Error sending DM:", err) + } +}, + + "decline": func(client *discordgo.Session, i *discordgo.InteractionCreate) { + data := i.MessageComponentData().CustomID + parts := strings.Split(data, "_") + if len(parts) != 3 { + log.Println("Invalid decline button custom ID") + return + } + userID := parts[1] + + + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Request declined.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + if err != nil { + log.Println("Error sending interaction response:", err) + } + + dmChannel, err := client.UserChannelCreate(userID) + if err != nil { + log.Println("Error creating DM channel:", err) + return + } + + _, err = client.ChannelMessageSend(dmChannel.ID, "Your reset request has been declined.") + if err != nil { + log.Println("Error sending DM:", err) } }, } @@ -263,8 +371,13 @@ func main() { h(client, i) } } else if i.Type == discordgo.InteractionMessageComponent { - if h, ok := componentsHandlers[i.MessageComponentData().CustomID]; ok { + customID := i.MessageComponentData().CustomID + if h, ok := componentsHandlers[customID]; ok { h(client, i) + } else if strings.HasPrefix(customID, "accept_") { + componentsHandlers["accept"](client, i) + } else if strings.HasPrefix(customID, "decline_") { + componentsHandlers["decline"](client, i) } } }) -- 2.45.2 From d7c884bf1d5d696d3461161f6d64860bef1958a7 Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 20:07:07 +0200 Subject: [PATCH 08/10] Delete channel once request is accepted/declined --- main.go | 105 +++++++++++++++++++++++++++++++------------------------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/main.go b/main.go index 958b7ec..6751be1 100644 --- a/main.go +++ b/main.go @@ -183,7 +183,7 @@ var ( }, }, }, - Flags: discordgo.MessageFlagsEphemeral, + Flags: discordgo.MessageFlagsEphemeral, }, }) if err != nil { @@ -259,55 +259,62 @@ var ( log.Println("Error sending message to the ticket channel:", err) } }, -"accept": func(client *discordgo.Session, i *discordgo.InteractionCreate) { - data := i.MessageComponentData().CustomID - parts := strings.Split(data, "_") - if len(parts) != 3 { - log.Println("Invalid accept button custom ID") - return - } - userID := parts[1] - softwareType := parts[2] - - member, err := client.GuildMember(i.GuildID, userID) - if err != nil { - log.Println("Error fetching member info:", err) - return - } - var userName string - if member.Nick != "" { - userName = member.Nick - } else if member.User.GlobalName != "" { - userName = member.User.GlobalName - } else { - userName = member.User.Username - } + "accept": func(client *discordgo.Session, i *discordgo.InteractionCreate) { + data := i.MessageComponentData().CustomID + parts := strings.Split(data, "_") + if len(parts) != 3 { + log.Println("Invalid accept button custom ID") + return + } + userID := parts[1] + softwareType := parts[2] - reset(userName, softwareType) + member, err := client.GuildMember(i.GuildID, userID) + if err != nil { + log.Println("Error fetching member info:", err) + return + } + var userName string + if member.Nick != "" { + userName = member.Nick + } else if member.User.GlobalName != "" { + userName = member.User.GlobalName + } else { + userName = member.User.Username + } - err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Content: "Request accepted and processed.", - Flags: discordgo.MessageFlagsEphemeral, - }, - }) - if err != nil { - log.Println("Error sending interaction response:", err) - } + reset(userName, softwareType) - dmChannel, err := client.UserChannelCreate(userID) - if err != nil { - log.Println("Error creating DM channel:", err) - return - } + err = client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Request accepted and processed.", + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + if err != nil { + log.Println("Error sending interaction response:", err) + } - _, err = client.ChannelMessageSend(dmChannel.ID, fmt.Sprintf("Your reset request for %s has been accepted and processed.", softwareType)) - if err != nil { - log.Println("Error sending DM:", err) - } -}, + + _,err = client.ChannelDelete(i.ChannelID) + if err != nil { + log.Println("Error deleting channel:", err) + } + + + dmChannel, err := client.UserChannelCreate(userID) + if err != nil { + log.Println("Error creating DM channel:", err) + return + } + + _, err = client.ChannelMessageSend(dmChannel.ID, fmt.Sprintf("Your reset request for %s has been accepted and processed.", softwareType)) + if err != nil { + log.Println("Error sending DM:", err) + } + }, "decline": func(client *discordgo.Session, i *discordgo.InteractionCreate) { data := i.MessageComponentData().CustomID @@ -317,7 +324,6 @@ var ( return } userID := parts[1] - err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, @@ -330,6 +336,13 @@ var ( log.Println("Error sending interaction response:", err) } + + _,err = client.ChannelDelete(i.ChannelID) + if err != nil { + log.Println("Error deleting channel:", err) + } + + dmChannel, err := client.UserChannelCreate(userID) if err != nil { log.Println("Error creating DM channel:", err) -- 2.45.2 From 849f592fc044b513e736299e5e6cb9343924dc6d Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 20:37:14 +0200 Subject: [PATCH 09/10] Implement channel making system --- main.go | 91 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/main.go b/main.go index 6751be1..2110035 100644 --- a/main.go +++ b/main.go @@ -18,8 +18,6 @@ var ( config Config client *discordgo.Session db *sql.DB - - globalCategoryID string ) type Config struct { @@ -29,9 +27,10 @@ type Config struct { } type Discord struct { - Token string `toml:"token"` - AppID string `toml:"appid"` - GuildID string `toml:"guildid"` + Token string `toml:"token"` + AppID string `toml:"appid"` + GuildID string `toml:"guildid"` + CategoryID string `toml:"category_id"` } type Database struct { @@ -98,6 +97,23 @@ func reset(userNickname, softwareType string) { log.Printf("Resetting %s for user %s\n", softwareType, userNickname) } +func getUsernameFromMember(member *discordgo.Member) string { + if member == nil { + return "UnknownUser" + } + + var userName string + if member.Nick != "" { + userName = member.Nick + } else if member.User != nil && member.User.Username != "" { + userName = member.User.Username + } else { + userName = "UnknownUser" + } + return userName +} + + var ( commands = []discordgo.ApplicationCommand{ { @@ -110,22 +126,12 @@ var ( Description: "Channel for ticket creation", Required: true, }, - { - Type: discordgo.ApplicationCommandOptionChannel, - Name: "category", - Description: "Category for tickets", - Required: true, - }, }, }, } commandsHandlers = map[string]func(client *discordgo.Session, i *discordgo.InteractionCreate){ "setup": func(client *discordgo.Session, i *discordgo.InteractionCreate) { channelOption := i.ApplicationCommandData().Options[0].ChannelValue(client) - categoryOption := i.ApplicationCommandData().Options[1].ChannelValue(client) - - globalCategoryID = categoryOption.ID - _, err := client.ChannelMessageSendComplex(channelOption.ID, &discordgo.MessageSend{ Content: "Click the button below to request a HWID reset:", Components: []discordgo.MessageComponent{ @@ -199,14 +205,29 @@ var ( softwareType = "Mesa" } + categoryID := config.Discord.CategoryID guildID := i.GuildID userID := i.Member.User.ID - userName := i.Member.User.Username + userName := getUsernameFromMember(i.Member) + + if !canCreateTicket(userName, selectedOption) { + err := client.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf("You already have an active %s ticket. Please wait for the administrators to process it.", softwareType), + Flags: discordgo.MessageFlagsEphemeral, + }, + }) + if err != nil { + log.Println("Error sending interaction response:", err) + } + return + } channel, err := client.GuildChannelCreateComplex(guildID, discordgo.GuildChannelCreateData{ Name: fmt.Sprintf("reset-%s-%s", softwareType, userName), Type: discordgo.ChannelTypeGuildText, - ParentID: globalCategoryID, + ParentID: categoryID, PermissionOverwrites: []*discordgo.PermissionOverwrite{ { ID: guildID, @@ -275,14 +296,7 @@ var ( log.Println("Error fetching member info:", err) return } - var userName string - if member.Nick != "" { - userName = member.Nick - } else if member.User.GlobalName != "" { - userName = member.User.GlobalName - } else { - userName = member.User.Username - } + userName := getUsernameFromMember(member) reset(userName, softwareType) @@ -297,13 +311,11 @@ var ( log.Println("Error sending interaction response:", err) } - - _,err = client.ChannelDelete(i.ChannelID) + _, err = client.ChannelDelete(i.ChannelID) if err != nil { log.Println("Error deleting channel:", err) } - dmChannel, err := client.UserChannelCreate(userID) if err != nil { log.Println("Error creating DM channel:", err) @@ -336,13 +348,11 @@ var ( log.Println("Error sending interaction response:", err) } - - _,err = client.ChannelDelete(i.ChannelID) + _, err = client.ChannelDelete(i.ChannelID) if err != nil { log.Println("Error deleting channel:", err) } - dmChannel, err := client.UserChannelCreate(userID) if err != nil { log.Println("Error creating DM channel:", err) @@ -357,6 +367,25 @@ var ( } ) +func canCreateTicket(userName, softwareType string) bool { + guildChannels, err := client.GuildChannels(config.Discord.GuildID) + if err != nil { + log.Println("Error fetching guild channels:", err) + return false + } + + for _, channel := range guildChannels { + if channel.Type == discordgo.ChannelTypeGuildText && channel.ParentID == config.Discord.CategoryID { + expectedName := fmt.Sprintf("reset-%s-%s", softwareType, userName) + if channel.Name == expectedName { + return false + } + } + } + + return true +} + func main() { if client == nil { log.Println("Bot client is not initialized") -- 2.45.2 From 78e8a857e9e4874db7d4e4eba0254007ca673da0 Mon Sep 17 00:00:00 2001 From: Joren Date: Sat, 6 Jul 2024 20:38:03 +0200 Subject: [PATCH 10/10] Example config --- config.toml.example | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config.toml.example b/config.toml.example index 4fd6b6e..1b04778 100644 --- a/config.toml.example +++ b/config.toml.example @@ -1,6 +1,8 @@ [discord] -client = "" +token = "" +appid = "" guildid = "" +category_id = "" [database] host = "" @@ -9,3 +11,4 @@ name = "" username = "" password = "" +admin_roles = [""] -- 2.45.2