From b317dfddf48533aebf309269a19eff5e789a01b2 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Mon, 19 Jul 2021 17:06:54 -0500 Subject: [PATCH] trying out discord slash commands --- .gitignore | 3 ++ Makefile | 5 +++ commandLink.go | 53 +++++++++++++++++++++++ commands.go | 97 +++++++++++++++++++++++++++++++++++++++++++ config.go | 7 ++++ go.mod | 11 +++++ go.sum | 27 ++++++++++++ main.go | 73 ++++++++++++++++++++++++++++++++ models/automigrate.go | 7 ++++ models/user.go | 9 ++++ pepster.go | 34 +++++++++++++++ 11 files changed, 326 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 commandLink.go create mode 100644 commands.go create mode 100644 config.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 models/automigrate.go create mode 100644 models/user.go create mode 100644 pepster.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f7fade --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/pepster +/config.toml +/test.db \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b590908 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +SOURCES := $(shell find . -name "*.go" -print) +BIN := pepster + +$(BIN): $(SOURCES) + go build -o $@ diff --git a/commandLink.go b/commandLink.go new file mode 100644 index 0000000..69dafca --- /dev/null +++ b/commandLink.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm/clause" + + "pepster/models" +) + +func (p *Pepster) commandLink() Command { + return Command{ + Name: "link", + Description: "Links your osu! account to your Discord account.", + Options: []*CommandOption{ + &CommandOption{ + Type: CommandOptionString, + Name: "username", + Description: "Your osu! username", + Required: true, + }, + }, + Handler: func(s *discordgo.Session, i *discordgo.InteractionCreate) { + var user *discordgo.User + if i.User != nil { + user = i.User + } else if i.Member != nil { + user = i.Member.User + } + + dbUser := models.User{ + DiscordID: user.ID, + OsuID: 2688103, + } + + p.db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "discord_id"}}, + UpdateAll: true, + }).Create(&dbUser) + fmt.Println("Updated", dbUser) + + p.updateStatus <- true + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: fmt.Sprintf(":thumbsup: Linked osu! user **%s** to **%s**", "IOException", user.String()), + }, + }) + }, + } +} diff --git a/commands.go b/commands.go new file mode 100644 index 0000000..4910f77 --- /dev/null +++ b/commands.go @@ -0,0 +1,97 @@ +package main + +import ( + "log" + + "github.com/bwmarrin/discordgo" +) + +const ( + DEV_GUILD_ID = "665066008507187220" +) + +type Command struct { + Name string + Description string + Options []*CommandOption + Handler CommandHandler +} + +type CommandHandler = func(*discordgo.Session, *discordgo.InteractionCreate) + +type CommandOption struct { + Type CommandOptionType + Name string + Description string + Required bool +} + +type CommandOptionType uint8 + +const ( + CommandOptionSubCommand CommandOptionType = 1 + CommandOptionSubCommandGroup CommandOptionType = 2 + CommandOptionString CommandOptionType = 3 + CommandOptionInteger CommandOptionType = 4 + CommandOptionBoolean CommandOptionType = 5 + CommandOptionUser CommandOptionType = 6 + CommandOptionChannel CommandOptionType = 7 + CommandOptionRole CommandOptionType = 8 + CommandOptionMentionable CommandOptionType = 9 +) + +func guildID(global bool) string { + if global { + return "" + } else { + return DEV_GUILD_ID + } +} + +func (p *Pepster) registerCommands(commands []Command, global bool) { + handlers := make(map[string]CommandHandler, len(commands)) + + for _, command := range commands { + convertedOptions := make([]*discordgo.ApplicationCommandOption, len(command.Options)) + for i, option := range command.Options { + convertedOptions[i] = &discordgo.ApplicationCommandOption{ + Name: option.Name, + Description: option.Description, + Type: discordgo.ApplicationCommandOptionType(option.Type), + Required: option.Required, + } + } + + _, err := p.discord.ApplicationCommandCreate(p.config.AppID, guildID(global), &discordgo.ApplicationCommand{ + Name: command.Name, + Description: command.Description, + Options: convertedOptions, + }) + + if err != nil { + log.Println("could not register command", command.Name, ":", err) + continue + } + + handlers[command.Name] = command.Handler + } + + p.discord.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) { + if handler, ok := handlers[i.ApplicationCommandData().Name]; ok { + handler(s, i) + } + }) +} + +func (p *Pepster) unregisterAllCommands(global bool) { + commands, _ := p.discord.ApplicationCommands(p.config.AppID, guildID(global)) + for _, command := range commands { + p.discord.ApplicationCommandDelete(p.config.AppID, guildID(global), command.ID) + } +} + +func (p *Pepster) commandsList() []Command { + return []Command{ + p.commandLink(), + } +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..37b5e23 --- /dev/null +++ b/config.go @@ -0,0 +1,7 @@ +package main + +type Config struct { + Debug bool `toml:"debug,omitempty"` + AppID string `toml:"app_id"` + BotToken string `toml:"bot_token"` +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..85e7949 --- /dev/null +++ b/go.mod @@ -0,0 +1,11 @@ +module pepster + +go 1.16 + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/bwmarrin/discordgo v0.23.3-0.20210627161652-421e14965030 // indirect + github.com/mattn/go-sqlite3 v1.14.8 // indirect + gorm.io/driver/sqlite v1.1.4 // indirect + gorm.io/gorm v1.21.12 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..f304e9c --- /dev/null +++ b/go.sum @@ -0,0 +1,27 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bwmarrin/discordgo v0.23.3-0.20210627161652-421e14965030 h1:JgAlPJlCqsrUqXcClndnvF6LOjV528hpU/l5YzN++PA= +github.com/bwmarrin/discordgo v0.23.3-0.20210627161652-421e14965030/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= +github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-sqlite3 v1.14.8 h1:gDp86IdQsN/xWjIEmr9MF6o9mpksUgh0fu+9ByFxzIU= +github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= +gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= +gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.21.12 h1:3fQM0Eiz7jcJEhPggHEpoYnsGZqynMzverL77DV40RM= +gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= diff --git a/main.go b/main.go new file mode 100644 index 0000000..0cbb688 --- /dev/null +++ b/main.go @@ -0,0 +1,73 @@ +package main + +import ( + "flag" + "io/ioutil" + "log" + "os" + "os/signal" + + "github.com/BurntSushi/toml" + "github.com/bwmarrin/discordgo" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + + "pepster/models" +) + +func main() { + var err error + + // open config file + configPath := flag.String("config", "config.toml", "Path to the config file (defaults to config.toml)") + flag.Parse() + + var config Config + configData, err := ioutil.ReadFile(*configPath) + if err != nil { + log.Fatal("Failed to read config file", err) + } + + if err = toml.Unmarshal(configData, &config); err != nil { + log.Fatal("Invalid toml in config file", err) + } + + // open database + db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + + // migrate the schema + models.AutoMigrate(db) + + // connect to discord + discord, err := discordgo.New("Bot " + config.BotToken) + if err != nil { + log.Fatal("Could not connect to discord", err) + } + if err = discord.Open(); err != nil { + log.Fatal("Could not connect to discord", err) + } + + pepster := &Pepster{ + config: &config, + db: db, + discord: discord, + updateStatus: make(chan bool, 5), + } + pepster.Init() + pepster.updateStatus <- true + defer pepster.Close() + + // registering command handlers + pepster.registerCommands(pepster.commandsList(), false) + + // graceful shutdown handler + stop := make(chan os.Signal) + signal.Notify(stop, os.Interrupt) + <-stop + log.Println("Gracefully shutting down...") + + // deferred close +} diff --git a/models/automigrate.go b/models/automigrate.go new file mode 100644 index 0000000..fcb0682 --- /dev/null +++ b/models/automigrate.go @@ -0,0 +1,7 @@ +package models + +import "gorm.io/gorm" + +func AutoMigrate(db *gorm.DB) { + db.AutoMigrate(&User{}) +} diff --git a/models/user.go b/models/user.go new file mode 100644 index 0000000..55b9c61 --- /dev/null +++ b/models/user.go @@ -0,0 +1,9 @@ +package models + +import "gorm.io/gorm" + +type User struct { + gorm.Model + DiscordID string `gorm:"column:discord_id;unique"` + OsuID int `gorm:"column:osu_id"` +} diff --git a/pepster.go b/pepster.go new file mode 100644 index 0000000..ba3407b --- /dev/null +++ b/pepster.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + + "github.com/bwmarrin/discordgo" + "gorm.io/gorm" +) + +type Pepster struct { + config *Config + db *gorm.DB + discord *discordgo.Session + + updateStatus chan bool +} + +func (p *Pepster) Init() { + go (func() { + fmt.Println("listening") + for _ = range p.updateStatus { + _ = p.discord.UpdateStatusComplex(discordgo.UpdateStatusData{ + AFK: false, + Status: "hellosu", + }) + fmt.Println("lol Update") + } + })() +} + +func (p *Pepster) Close() { + p.unregisterAllCommands(false) + p.discord.Close() +}