aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-11-16 16:21:51 -0500
committerBobby <[email protected]>2024-11-16 16:21:51 -0500
commit2e79b5aa8cfe8433bcd9d1d415826056fea8fcb2 (patch)
treee9a8e4fe1f8dfc598f68f3a2c1909fa381f46961
parentc7e9ca772b03c2edc3a1893e9c9ac2eeba90acfa (diff)
downloadyuzaki-2e79b5aa8cfe8433bcd9d1d415826056fea8fcb2.tar.xz
yuzaki-2e79b5aa8cfe8433bcd9d1d415826056fea8fcb2.zip
bot setup with basic message handler
-rw-r--r--.air.toml74
-rw-r--r--config/config.go92
-rw-r--r--go.mod11
-rw-r--r--go.sum14
-rw-r--r--handlers/messageGatewayHandler.go18
-rw-r--r--yuzaki/main.go67
6 files changed, 237 insertions, 39 deletions
diff --git a/.air.toml b/.air.toml
index 498951f..9956803 100644
--- a/.air.toml
+++ b/.air.toml
@@ -3,50 +3,50 @@ testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
- args_bin = []
- bin = "./tmp/main"
- cmd = "go build -o ./tmp/main ."
- delay = 1000
- exclude_dir = ["assets", "tmp", "vendor", "testdata"]
- exclude_file = []
- exclude_regex = ["_test.go"]
- exclude_unchanged = false
- follow_symlink = false
- full_bin = ""
- include_dir = []
- include_ext = ["go", "tpl", "tmpl", "html"]
- include_file = []
- kill_delay = "0s"
- log = "build-errors.log"
- poll = false
- poll_interval = 0
- post_cmd = []
- pre_cmd = []
- rerun = false
- rerun_delay = 500
- send_interrupt = false
- stop_on_error = false
+args_bin = []
+bin = "./tmp/main"
+cmd = "go build -o ./tmp/main yuzaki/main.go"
+delay = 1000
+exclude_dir = ["assets", "tmp", "vendor", "testdata"]
+exclude_file = []
+exclude_regex = ["_test.go"]
+exclude_unchanged = false
+follow_symlink = false
+full_bin = ""
+include_dir = []
+include_ext = ["go", "tpl", "tmpl", "html"]
+include_file = []
+kill_delay = "0s"
+log = "build-errors.log"
+poll = false
+poll_interval = 0
+post_cmd = []
+pre_cmd = []
+rerun = false
+rerun_delay = 500
+send_interrupt = false
+stop_on_error = false
[color]
- app = ""
- build = "yellow"
- main = "magenta"
- runner = "green"
- watcher = "cyan"
+app = ""
+build = "yellow"
+main = "magenta"
+runner = "green"
+watcher = "cyan"
[log]
- main_only = false
- silent = false
- time = false
+main_only = false
+silent = false
+time = false
[misc]
- clean_on_exit = false
+clean_on_exit = false
[proxy]
- app_port = 0
- enabled = false
- proxy_port = 0
+app_port = 0
+enabled = false
+proxy_port = 0
[screen]
- clear_on_rebuild = false
- keep_scroll = true
+clear_on_rebuild = false
+keep_scroll = true
diff --git a/config/config.go b/config/config.go
new file mode 100644
index 0000000..4f7a666
--- /dev/null
+++ b/config/config.go
@@ -0,0 +1,92 @@
+package config
+
+import (
+ "fmt"
+ "os"
+ "strings"
+
+ "github.com/joho/godotenv"
+)
+
+type Config struct {
+ DiscordToken string
+ DataSourceName string
+ DatabaseDriver string
+}
+
+var validDBDrivers = map[string]bool{
+ "sqlite": true,
+ "postgres": true,
+ "mysql": true,
+ "mssql": true,
+}
+
+func Load() (*Config, error) {
+ if err := godotenv.Load(); err != nil {
+ return nil, fmt.Errorf("loading env file: %w", err)
+ }
+
+ config := &Config{}
+ requiredVars := map[string]*string{
+ "DISCORD_BOT_TOKEN": &config.DiscordToken,
+ "DSN": &config.DataSourceName,
+ "DATABASE_DRIVER": &config.DatabaseDriver,
+ }
+
+ for envKey, configVar := range requiredVars {
+ value, err := getEnv(envKey)
+ if err != nil {
+ return nil, fmt.Errorf("getting %s: %w", envKey, err)
+ }
+ *configVar = value
+ }
+
+ if err := validateConfig(config); err != nil {
+ return nil, fmt.Errorf("validating config: %w", err)
+ }
+
+ return config, nil
+}
+
+func validateConfig(config *Config) error {
+ if config.DiscordToken == "" {
+ return fmt.Errorf("discord token not found")
+ }
+ if config.DataSourceName == "" {
+ return fmt.Errorf("data source name not found")
+ }
+ if config.DatabaseDriver == "" {
+ return fmt.Errorf("database driver not found")
+ }
+
+ if !validDBDrivers[strings.ToLower(config.DatabaseDriver)] {
+ return fmt.Errorf("invalid database driver: %s", config.DatabaseDriver)
+ }
+
+ return nil
+}
+
+func getEnv(key string) (string, error) {
+ value, exists := os.LookupEnv(key)
+ if !exists {
+ return "", fmt.Errorf("environment variable %s not found", key)
+ }
+ return strings.TrimSpace(value), nil
+}
+
+// Future implementation for getting other types of environment variables
+// func getEnvInt(key string) (int, error) {
+// value, err := getEnv(key)
+// if err != nil {
+// return 0, err
+// }
+// return strconv.Atoi(value)
+// }
+
+// func getEnvBool(key string) (bool, error) {
+// value, err := getEnv(key)
+// if err != nil {
+// return false, err
+// }
+// return strconv.ParseBool(value)
+// }
diff --git a/go.mod b/go.mod
index 81da828..8b7ff6e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,14 @@
module yuzaki
go 1.23.1
+
+require (
+ github.com/bwmarrin/discordgo v0.28.1
+ github.com/joho/godotenv v1.5.1
+)
+
+require (
+ github.com/gorilla/websocket v1.4.2 // indirect
+ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b // indirect
+ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..1a619be
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,14 @@
+github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4=
+github.com/bwmarrin/discordgo v0.28.1/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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+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=
diff --git a/handlers/messageGatewayHandler.go b/handlers/messageGatewayHandler.go
new file mode 100644
index 0000000..a6ec4d7
--- /dev/null
+++ b/handlers/messageGatewayHandler.go
@@ -0,0 +1,18 @@
+package handlers
+
+import (
+ "strings"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+func MessageGatewayHandler(s *discordgo.Session, m *discordgo.MessageCreate) {
+ if m.Author.ID == s.State.User.ID || m.Author.Bot {
+ return
+ }
+
+ // if says "hello @bot" <- case insensitive <- bot responds with "Hello @user!"
+ if strings.EqualFold(strings.TrimSpace(m.Content), "hello "+s.State.User.Mention()) {
+ s.ChannelMessageSendReply(m.ChannelID, "Hello "+m.Author.Mention()+"!", m.Reference())
+ }
+}
diff --git a/yuzaki/main.go b/yuzaki/main.go
index a3dd973..c54962d 100644
--- a/yuzaki/main.go
+++ b/yuzaki/main.go
@@ -1,7 +1,70 @@
package main
-import "fmt"
+import (
+ "context"
+ "log"
+ "os"
+ "os/signal"
+ "syscall"
+ "yuzaki/config"
+ "yuzaki/handlers"
+
+ "github.com/bwmarrin/discordgo"
+)
+
+var (
+ configuration *config.Config
+ session *discordgo.Session
+)
+
+func init() {
+ var err error
+ configuration, err = config.Load()
+ if err != nil {
+ log.Fatalf("loading config: %v", err)
+ }
+
+ session, err = discordgo.New("Bot " + configuration.DiscordToken)
+ if err != nil {
+ log.Fatalf("creating discord session: %v", err)
+ }
+
+ session.Identify.Intents = discordgo.IntentsAll
+ session.AddHandler(ready)
+ session.AddHandler(handlers.MessageGatewayHandler)
+}
func main() {
- fmt.Println("Hello, World!")
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ if err := setupAndRun(ctx); err != nil {
+ log.Fatalf("error running bot: %v", err)
+ }
+}
+
+func setupAndRun(ctx context.Context) error {
+ if err := session.Open(); err != nil {
+ return err
+ }
+ defer session.Close()
+
+ shutdown := make(chan os.Signal, 1)
+ signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
+
+ select {
+ case <-ctx.Done():
+ return ctx.Err()
+ case <-shutdown:
+ log.Println("Shutting down gracefully...")
+ return nil
+ }
+}
+
+func ready(s *discordgo.Session, event *discordgo.Ready) {
+ log.Printf("Logged in as %s on %d guilds", event.User.String(), len(event.Guilds))
+ if err := s.UpdateWatchStatus(0, "all users in Yuzaki's Canyon!"); err != nil {
+ log.Printf("error setting status: %v", err)
+ }
+ log.Println("Bot is now running. Press CTRL-C to exit.")
}