From a2cb170137b322deb0787dd76ce7286204b5e27f Mon Sep 17 00:00:00 2001 From: topi314 Date: Wed, 5 Jun 2024 15:38:08 +0200 Subject: [PATCH] update bot template --- .dockerignore | 6 +- .editorconfig | 44 --------- .github/workflows/docker-build.yml | 45 --------- .github/workflows/docker.yml | 48 +++++++++ .github/workflows/go-test.yml | 26 ----- .github/workflows/test.yml | 29 ++++++ .gitignore | 2 +- .golangci.toml | 14 +++ cmd/Dockerfile => Dockerfile | 7 +- README.md | 62 +++++++++++- bot.go => bottemplate/bot.go | 34 ++++--- .../commands}/commands.go | 0 {commands => bottemplate/commands}/test.go | 4 +- {commands => bottemplate/commands}/version.go | 6 +- .../components}/test_component.go | 0 bottemplate/config.go | 39 ++++++++ .../handlers}/handlers.go | 4 +- cmd/main.go | 77 --------------- config.example.toml | 13 +++ config.go | 42 -------- docker-compose.yml | 9 +- example.config.json | 6 -- go.mod | 20 ++-- go.sum | 52 ++++++---- main.go | 97 +++++++++++++++++++ 25 files changed, 382 insertions(+), 304 deletions(-) delete mode 100644 .editorconfig delete mode 100644 .github/workflows/docker-build.yml create mode 100644 .github/workflows/docker.yml delete mode 100644 .github/workflows/go-test.yml create mode 100644 .github/workflows/test.yml create mode 100644 .golangci.toml rename cmd/Dockerfile => Dockerfile (59%) rename bot.go => bottemplate/bot.go (54%) rename {commands => bottemplate/commands}/commands.go (100%) rename {commands => bottemplate/commands}/test.go (88%) rename {commands => bottemplate/commands}/version.go (63%) rename {components => bottemplate/components}/test_component.go (100%) create mode 100644 bottemplate/config.go rename {handlers => bottemplate/handlers}/handlers.go (63%) delete mode 100644 cmd/main.go create mode 100644 config.example.toml delete mode 100644 config.go delete mode 100644 example.config.json create mode 100644 main.go diff --git a/.dockerignore b/.dockerignore index f1528f3..a691e70 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ +.idea/ .github/ -.editorconfig README.md LICENSE -config.json -config.json.example \ No newline at end of file +config.toml +config.example.toml \ No newline at end of file diff --git a/.editorconfig b/.editorconfig deleted file mode 100644 index 4d59f0c..0000000 --- a/.editorconfig +++ /dev/null @@ -1,44 +0,0 @@ -root = true - -[*] -charset = utf-8 -end_of_line = crlf -indent_size = 4 -indent_style = space -insert_final_newline = false -max_line_length = 120 -tab_width = 4 -ij_continuation_indent_size = 8 -ij_formatter_off_tag = @formatter:off -ij_formatter_on_tag = @formatter:on -ij_formatter_tags_enabled = false -ij_smart_tabs = false -ij_visual_guides = none -ij_wrap_on_typing = false - -[{*.go, *.go2}] -indent_style = tab -ij_continuation_indent_size = 4 -ij_go_add_leading_space_to_comments = false -ij_go_add_parentheses_for_single_import = false -ij_go_call_parameters_new_line_after_left_paren = true -ij_go_call_parameters_right_paren_on_new_line = true -ij_go_call_parameters_wrap = off -ij_go_fill_paragraph_width = 80 -ij_go_group_current_project_imports = true -ij_go_group_stdlib_imports = true -ij_go_import_sorting = gofmt -ij_go_keep_indents_on_empty_lines = false -ij_go_move_all_imports_in_one_declaration = true -ij_go_move_all_stdlib_imports_in_one_group = true -ij_go_remove_redundant_import_aliases = true -ij_go_use_back_quotes_for_imports = false -ij_go_wrap_comp_lit = off -ij_go_wrap_comp_lit_newline_after_lbrace = true -ij_go_wrap_comp_lit_newline_before_rbrace = true -ij_go_wrap_func_params = off -ij_go_wrap_func_params_newline_after_lparen = true -ij_go_wrap_func_params_newline_before_rparen = true -ij_go_wrap_func_result = off -ij_go_wrap_func_result_newline_after_lparen = true -ij_go_wrap_func_result_newline_before_rparen = true diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml deleted file mode 100644 index f9a78b8..0000000 --- a/.github/workflows/docker-build.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: Docker - -on: [push] - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v4 - with: - images: ghcr.io/${{ github.repository }} - tags: | - type=ref,event=branch - type=ref,event=tag - type=ref,event=pr - type=sha,prefix= - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to GitHub Container Registry - uses: docker/login-action@v2 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - file: ./cmd/Dockerfile - platforms: linux/amd64,linux/arm/v7,linux/arm64/v8 - push: true - build-args: VERSION=${{ steps.meta.outputs.version }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..5c2b085 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,48 @@ +name: Docker + +on: [ push ] + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v4 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=ref,event=pr + type=sha,prefix= + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + context: . + platforms: linux/amd64,linux/arm/v7,linux/arm64/v8 + push: true + build-args: VERSION=${{ steps.meta.outputs.version }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml deleted file mode 100644 index 95ab273..0000000 --- a/.github/workflows/go-test.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Lint - -on: [ push ] - -jobs: - gotest: - runs-on: ubuntu-latest - steps: - - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - uses: actions/checkout@v3 - - name: go build - run: go test -v ./... - - golangci: - runs-on: ubuntu-latest - steps: - - uses: actions/setup-go@v3 - with: - go-version: 1.18 - - uses: actions/checkout@v3 - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - version: latest \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6099728 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Test + +on: [ push ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.22.x + - name: Test + run: go test -v ./... + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.22.x + - name: Lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest \ No newline at end of file diff --git a/.gitignore b/.gitignore index 72e8273..32870ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .idea/ -config.json +config.toml diff --git a/.golangci.toml b/.golangci.toml new file mode 100644 index 0000000..c5cf3e3 --- /dev/null +++ b/.golangci.toml @@ -0,0 +1,14 @@ +[linters] +disable-all = true +enable = [ + "gosimple", + "govet", + "gofmt", + "ineffassign", + "staticcheck", + "sloglint", + "whitespace", + "errname", + "errorlint", + "importas" +] \ No newline at end of file diff --git a/cmd/Dockerfile b/Dockerfile similarity index 59% rename from cmd/Dockerfile rename to Dockerfile index 7e70e78..2ddeb1c 100644 --- a/cmd/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM --platform=$BUILDPLATFORM golang:1.18-alpine AS build +FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS build WORKDIR /build @@ -11,16 +11,19 @@ COPY . . ARG TARGETOS ARG TARGETARCH ARG VERSION=dev +ARG COMMIT=unknown RUN --mount=type=cache,target=/root/.cache/go-build \ --mount=type=cache,target=/go/pkg \ CGO_ENABLED=0 \ GOOS=$TARGETOS \ GOARCH=$TARGETARCH \ - go build -ldflags="-X 'main.version=${VERSION}'-w -s" -o bot cmd/main.go + go build -ldflags="-X 'main.version=${VERSION}' -X 'main.commit=${COMMIT}'" -o bot github.com/disgoorg/bot-template FROM alpine COPY --from=build /build/bot /bin/bot ENTRYPOINT ["/bin/bot"] + +CMD ["-config", "/var/lib/config.toml"] diff --git a/README.md b/README.md index aac1e8e..d999d39 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,65 @@ -[![Docker](https://github.com/disgoorg/bot-template/actions/workflows/docker-build.yml/badge.svg)](https://github.com/disgoorg/bot-template/actions/workflows/docker-build.yml) -[![Test](https://github.com/disgoorg/bot-template/actions/workflows/go-test.yml/badge.svg)](https://github.com/disgoorg/bot-template/actions/workflows/go-test.yml) +[![Go Report][0]][1] +[![Go Version][2]][3] +[![License][4]][5] +[![Docker][6]][7] +[![Test][8]][9] # bot-template This is a simple bot template for creating a bot which includes a config, application commands, components modals(using [handler](https://github.com/disgoorg/disgo/tree/master/handler) and event listeners. -Optional CLI Flags: +CLI Flags: +- `--config-path=your-config-path`: Path to the config file. - `--sync-commands=true`: Synchronize commands with the discord. + +## Usage + +1. Click on the `Use this template` button to create a new repository from this template. +2. Clone the repository to your local machine. +3. Open the project in your favorite IDE. +4. Copy the `config.example.toml` file to `config.toml` and fill in the required fields. +5. Run the bot using `go run .` +6. Invite the bot to your server using the invite link generated by [discord developer portal][10]. +7. + +## Configuration + +The configuration file is in TOML format. The format is as follows: + +```toml +[log] +# valid levels are "debug", "info", "warn", "error" +level = "info" +# valid formats are "text" and "json" +format = "text" +# whether to add the log source to the log message +add_source = true + +[bot] +# add guild ids the commands should sync to, leave empty to sync globally +dev_guilds = [] +# the bot token +token = "..." +``` + +## License + +The bot template is licensed under the [Apache License 2.0][5]. + + +[0]: https://goreportcard.com/badge/github.com/disgoorg/bot-template +[1]: https://goreportcard.com/report/github.com/disgoorg/bot-template + +[2]: https://img.shields.io/github/go-mod/go-version/disgoorg/bot-template +[3]: https://golang.org/doc/devel/release.html + +[4]: https://img.shields.io/github/license/disgoorg/bot-template +[5]: LICENSE + +[6]: https://github.com/disgoorg/bot-template/actions/workflows/docker.yml/badge.svg +[7]: https://github.com/disgoorg/bot-template/actions/workflows/docker.yml + +[8]: https://github.com/disgoorg/bot-template/actions/workflows/test.yml/badge.svg +[9]: https://github.com/disgoorg/bot-template/actions/workflows/test.yml + +[10]: https://discord.com/developers/applications diff --git a/bot.go b/bottemplate/bot.go similarity index 54% rename from bot.go rename to bottemplate/bot.go index 0b6b82d..3dea807 100644 --- a/bot.go +++ b/bottemplate/bot.go @@ -1,7 +1,9 @@ -package dbot +package bottemplate import ( "context" + "log/slog" + "time" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" @@ -9,44 +11,46 @@ import ( "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/gateway" - "github.com/disgoorg/log" "github.com/disgoorg/paginator" ) -func New(logger log.Logger, version string, config Config) *Bot { +func New(cfg Config, version string, commit string) *Bot { return &Bot{ - Logger: logger, - Config: config, + Cfg: cfg, Paginator: paginator.New(), Version: version, + Commit: commit, } } type Bot struct { - Logger log.Logger + Cfg Config Client bot.Client Paginator *paginator.Manager - Config Config Version string + Commit string } -func (b *Bot) SetupBot(listeners ...bot.EventListener) { - var err error - b.Client, err = disgo.New(b.Config.Token, - bot.WithLogger(b.Logger), +func (b *Bot) SetupBot(listeners ...bot.EventListener) error { + client, err := disgo.New(b.Cfg.Bot.Token, bot.WithGatewayConfigOpts(gateway.WithIntents(gateway.IntentGuilds, gateway.IntentGuildMessages, gateway.IntentMessageContent)), bot.WithCacheConfigOpts(cache.WithCaches(cache.FlagGuilds)), bot.WithEventListeners(b.Paginator), bot.WithEventListeners(listeners...), ) if err != nil { - b.Logger.Fatal("Failed to setup b: ", err) + return err } + + b.Client = client + return nil } func (b *Bot) OnReady(_ *events.Ready) { - b.Logger.Infof("Butler ready") - if err := b.Client.SetPresence(context.TODO(), gateway.WithListeningActivity("you"), gateway.WithOnlineStatus(discord.OnlineStatusOnline)); err != nil { - b.Logger.Errorf("Failed to set presence: %s", err) + slog.Info("bot-template ready") + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := b.Client.SetPresence(ctx, gateway.WithListeningActivity("you"), gateway.WithOnlineStatus(discord.OnlineStatusOnline)); err != nil { + slog.Error("Failed to set presence", slog.Any("err", err)) } } diff --git a/commands/commands.go b/bottemplate/commands/commands.go similarity index 100% rename from commands/commands.go rename to bottemplate/commands/commands.go diff --git a/commands/test.go b/bottemplate/commands/test.go similarity index 88% rename from commands/test.go rename to bottemplate/commands/test.go index f642287..f8a79a1 100644 --- a/commands/test.go +++ b/bottemplate/commands/test.go @@ -21,13 +21,13 @@ var test = discord.SlashCommandCreate{ func TestHandler(e *handler.CommandEvent) error { return e.CreateMessage(discord.NewMessageCreateBuilder(). SetContentf("test command. Choice: %s", e.SlashCommandInteractionData().String("choice")). - AddActionRow(discord.NewPrimaryButton("test", "test_button")). + AddActionRow(discord.NewPrimaryButton("test", "/test-button")). Build(), ) } func TestAutocompleteHandler(e *handler.AutocompleteEvent) error { - return e.Result([]discord.AutocompleteChoice{ + return e.AutocompleteResult([]discord.AutocompleteChoice{ discord.AutocompleteChoiceString{ Name: "1", Value: "1", diff --git a/commands/version.go b/bottemplate/commands/version.go similarity index 63% rename from commands/version.go rename to bottemplate/commands/version.go index 974af2a..92dc9c5 100644 --- a/commands/version.go +++ b/bottemplate/commands/version.go @@ -3,7 +3,7 @@ package commands import ( "fmt" - dbot "github.com/disgoorg/bot-template" + "github.com/disgoorg/bot-template/bottemplate" "github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/handler" ) @@ -13,10 +13,10 @@ var version = discord.SlashCommandCreate{ Description: "version command", } -func VersionHandler(b *dbot.Bot) handler.CommandHandler { +func VersionHandler(b *bottemplate.Bot) handler.CommandHandler { return func(e *handler.CommandEvent) error { return e.CreateMessage(discord.MessageCreate{ - Content: fmt.Sprintf("Version: %s", b.Version), + Content: fmt.Sprintf("Version: %s\nCommit: %s", b.Version, b.Commit), }) } } diff --git a/components/test_component.go b/bottemplate/components/test_component.go similarity index 100% rename from components/test_component.go rename to bottemplate/components/test_component.go diff --git a/bottemplate/config.go b/bottemplate/config.go new file mode 100644 index 0000000..76ff8c9 --- /dev/null +++ b/bottemplate/config.go @@ -0,0 +1,39 @@ +package bottemplate + +import ( + "fmt" + "log/slog" + "os" + + "github.com/disgoorg/snowflake/v2" + "github.com/pelletier/go-toml/v2" +) + +func LoadConfig(path string) (*Config, error) { + file, err := os.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to open config: %w", err) + } + + var cfg Config + if err = toml.NewDecoder(file).Decode(&cfg); err != nil { + return nil, err + } + return &cfg, nil +} + +type Config struct { + Log LogConfig `toml:"log"` + Bot BotConfig `toml:"bot"` +} + +type BotConfig struct { + DevGuilds []snowflake.ID `toml:"dev_guilds"` + Token string `toml:"token"` +} + +type LogConfig struct { + Level slog.Level `toml:"level"` + Format string `toml:"format"` + AddSource bool `toml:"add_source"` +} diff --git a/handlers/handlers.go b/bottemplate/handlers/handlers.go similarity index 63% rename from handlers/handlers.go rename to bottemplate/handlers/handlers.go index 33ad9c6..f9b0a51 100644 --- a/handlers/handlers.go +++ b/bottemplate/handlers/handlers.go @@ -1,12 +1,12 @@ package handlers import ( - "github.com/disgoorg/bot-template" + "github.com/disgoorg/bot-template/bottemplate" "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/events" ) -func MessageHandler(b *dbot.Bot) bot.EventListener { +func MessageHandler(b *bottemplate.Bot) bot.EventListener { return bot.NewListenerFunc(func(e *events.MessageCreate) { // TODO: handle message }) diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index c7b13ad..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "context" - "flag" - "os" - "os/signal" - "syscall" - "time" - - "github.com/disgoorg/disgo/bot" - "github.com/disgoorg/disgo/handler" - "github.com/disgoorg/log" - - "github.com/disgoorg/bot-template" - "github.com/disgoorg/bot-template/commands" - "github.com/disgoorg/bot-template/components" - "github.com/disgoorg/bot-template/handlers" -) - -var ( - shouldSyncCommands *bool - version = "dev" -) - -func init() { - shouldSyncCommands = flag.Bool("sync-commands", false, "Whether to sync commands to discord") - flag.Parse() -} - -func main() { - cfg, err := dbot.LoadConfig() - if err != nil { - panic("failed to load config: " + err.Error()) - } - - logger := log.New(log.Ldate | log.Ltime | log.Lshortfile) - logger.SetLevel(cfg.LogLevel) - logger.Infof("Starting bot version: %s", version) - logger.Infof("Syncing commands? %t", *shouldSyncCommands) - - b := dbot.New(logger, version, *cfg) - - h := handler.New() - h.Command("/test", commands.TestHandler) - h.Autocomplete("/test", commands.TestAutocompleteHandler) - h.Command("/version", commands.VersionHandler(b)) - h.Component("test_button", components.TestComponent) - - b.SetupBot(h, bot.NewListenerFunc(b.OnReady), handlers.MessageHandler(b)) - - if *shouldSyncCommands { - if cfg.DevMode { - logger.Info("Syncing commands in dev mode") - _, err = b.Client.Rest().SetGuildCommands(b.Client.ApplicationID(), cfg.DevGuildID, commands.Commands) - } else { - logger.Info("Syncing commands") - _, err = b.Client.Rest().SetGlobalCommands(b.Client.ApplicationID(), commands.Commands) - } - if err != nil { - logger.Errorf("failed to sync commands: %s", err.Error()) - } - } - - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - if err = b.Client.OpenGateway(ctx); err != nil { - b.Logger.Errorf("Failed to connect to gateway: %s", err) - } - defer b.Client.Close(context.TODO()) - - b.Logger.Info("Bot is running. Press CTRL-C to exit.") - s := make(chan os.Signal, 1) - signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) - <-s - b.Logger.Info("Shutting down...") -} diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..818596b --- /dev/null +++ b/config.example.toml @@ -0,0 +1,13 @@ +[log] +# valid levels are "debug", "info", "warn", "error" +level = "info" +# valid formats are "text" and "json" +format = "text" +# whether to add the log source to the log message +add_source = true + +[bot] +# add guild ids the commands should sync to, leave empty to sync globally +dev_guilds = [] +# the bot token +token = "..." diff --git a/config.go b/config.go deleted file mode 100644 index 338f750..0000000 --- a/config.go +++ /dev/null @@ -1,42 +0,0 @@ -package dbot - -import ( - "errors" - "os" - - "github.com/disgoorg/json" - "github.com/disgoorg/log" - "github.com/disgoorg/snowflake/v2" -) - -func LoadConfig() (*Config, error) { - file, err := os.Open("config.json") - if os.IsNotExist(err) { - if file, err = os.Create("config.json"); err != nil { - return nil, err - } - var data []byte - if data, err = json.MarshalIndent(Config{}, "", "\\t"); err != nil { - return nil, err - } - if _, err = file.Write(data); err != nil { - return nil, err - } - return nil, errors.New("config.json not found, created new one") - } else if err != nil { - return nil, err - } - - var cfg Config - if err = json.NewDecoder(file).Decode(&cfg); err != nil { - return nil, err - } - return &cfg, nil -} - -type Config struct { - DevMode bool `json:"dev_mode"` - DevGuildID snowflake.ID `json:"dev_guild_id"` - LogLevel log.Level `json:"log_level"` - Token string `json:"token"` -} diff --git a/docker-compose.yml b/docker-compose.yml index e4debe9..07cd601 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,12 +1,13 @@ services: bot: - image: hcr.io/disgoorg/disgo-butler/bot + image: ghcr.io/disgoorg/bot-template restart: unless-stopped volumes: - - ./config.json:/opt/bot/config.json - command: --sync-commands=true + - ./config.toml:/var/lib/config.toml + command: -config=/var/lib/config.toml --sync-commands=true networks: - bot networks: - bot: \ No newline at end of file + bot: + name: bot \ No newline at end of file diff --git a/example.config.json b/example.config.json deleted file mode 100644 index 1fb608c..0000000 --- a/example.config.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dev_mode": true, - "dev_guild_id": "", - "log_level": 2, - "token": "" -} diff --git a/go.mod b/go.mod index ce5c2fd..bcba730 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,19 @@ module github.com/disgoorg/bot-template -go 1.18 +go 1.22 require ( - github.com/disgoorg/disgo v0.15.1 - github.com/disgoorg/json v1.0.0 - github.com/disgoorg/log v1.2.0 - github.com/disgoorg/paginator v0.0.0-20230104145353-f988d828ede9 + github.com/disgoorg/disgo v0.18.7 + github.com/disgoorg/json v1.1.0 + github.com/disgoorg/paginator v0.0.0-20240407225836-102024af0cb8 github.com/disgoorg/snowflake/v2 v2.0.1 + github.com/pelletier/go-toml/v2 v2.2.2 ) require ( - github.com/gorilla/websocket v1.5.0 // indirect - github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b // indirect - golang.org/x/crypto v0.6.0 // indirect - golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect - golang.org/x/sys v0.5.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect + github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sys v0.21.0 // indirect ) diff --git a/go.sum b/go.sum index 7c534d6..1e688b2 100644 --- a/go.sum +++ b/go.sum @@ -1,24 +1,38 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/disgoorg/disgo v0.15.1 h1:EO7tV0r0VnktjubRaYi78APXrOLvIetPhwv+W61HkVY= -github.com/disgoorg/disgo v0.15.1/go.mod h1:j7MCI6foUipYNozxwttr2hcaEQa3gNEbcwgLnyLUf6E= -github.com/disgoorg/json v1.0.0 h1:kDhSM661fgIuNoZF3BO5/odaR5NSq80AWb937DH+Pdo= -github.com/disgoorg/json v1.0.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA= -github.com/disgoorg/log v1.2.0 h1:sqlXnu/ZKAlIlHV9IO+dbMto7/hCQ474vlIdMWk8QKo= -github.com/disgoorg/log v1.2.0/go.mod h1:3x1KDG6DI1CE2pDwi3qlwT3wlXpeHW/5rVay+1qDqOo= -github.com/disgoorg/paginator v0.0.0-20230104145353-f988d828ede9 h1:h7feN1V8sRJGKtd/5g8FtKT0IcVyCKABRSECOEf3oYc= -github.com/disgoorg/paginator v0.0.0-20230104145353-f988d828ede9/go.mod h1:k+g1jz3HxI97c5IGsScxOZYqhjZvDkNKLma7pUXiGcw= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disgoorg/disgo v0.18.7 h1:Xg5eiOdSo+wR3CDMIPh9Vmykdkwk/rdcs00vhr2U6m0= +github.com/disgoorg/disgo v0.18.7/go.mod h1:gkl6DBdbKUvmOOJayWPSvS52KPN/8uJGJ2f13gCEB1o= +github.com/disgoorg/json v1.1.0 h1:7xigHvomlVA9PQw9bMGO02PHGJJPqvX5AnwlYg/Tnys= +github.com/disgoorg/json v1.1.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA= +github.com/disgoorg/paginator v0.0.0-20240407225836-102024af0cb8 h1:Yv0wybsDYRbqaA8cHKC+72Atqv1zOb9lgcWa0lgTerk= +github.com/disgoorg/paginator v0.0.0-20240407225836-102024af0cb8/go.mod h1:k+g1jz3HxI97c5IGsScxOZYqhjZvDkNKLma7pUXiGcw= github.com/disgoorg/snowflake/v2 v2.0.1 h1:CuUxGLwggUxEswZOmZ+mZ5i0xSumQdXW9tXW7uGqe+0= github.com/disgoorg/snowflake/v2 v2.0.1/go.mod h1:SPU9c2CNn5DSyb86QcKtdZgix9osEtKrHLW4rMhfLCs= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b h1:qYTY2tN72LhgDj2rtWG+LI6TXFl2ygFQQ4YezfVaGQE= -github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= -golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad h1:qIQkSlF5vAUHxEmTbaqt1hkJ/t6skqEGYiMag343ucI= +github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go new file mode 100644 index 0000000..0843338 --- /dev/null +++ b/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "context" + "flag" + "log/slog" + "os" + "os/signal" + "syscall" + "time" + + "github.com/disgoorg/bot-template/bottemplate" + "github.com/disgoorg/bot-template/bottemplate/commands" + "github.com/disgoorg/bot-template/bottemplate/components" + "github.com/disgoorg/bot-template/bottemplate/handlers" + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/handler" +) + +var ( + version = "dev" + commit = "unknown" +) + +func main() { + shouldSyncCommands := flag.Bool("sync-commands", false, "Whether to sync commands to discord") + path := flag.String("config", "config.toml", "path to config") + flag.Parse() + + cfg, err := bottemplate.LoadConfig(*path) + if err != nil { + slog.Error("Failed to read config", slog.Any("err", err)) + os.Exit(-1) + } + + setupLogger(cfg.Log) + slog.Info("Starting bot-template...", slog.String("version", version), slog.String("commit", commit)) + slog.Info("Syncing commands", slog.Bool("sync", *shouldSyncCommands)) + + b := bottemplate.New(*cfg, version, commit) + + h := handler.New() + h.Command("/test", commands.TestHandler) + h.Autocomplete("/test", commands.TestAutocompleteHandler) + h.Command("/version", commands.VersionHandler(b)) + h.Component("/test-button", components.TestComponent) + + if err = b.SetupBot(h, bot.NewListenerFunc(b.OnReady), handlers.MessageHandler(b)); err != nil { + slog.Error("Failed to setup bot", slog.Any("err", err)) + os.Exit(-1) + } + + defer func() { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + b.Client.Close(ctx) + }() + + if *shouldSyncCommands { + slog.Info("Syncing commands", slog.Any("guild_ids", cfg.Bot.DevGuilds)) + if err = handler.SyncCommands(b.Client, commands.Commands, cfg.Bot.DevGuilds); err != nil { + slog.Error("Failed to sync commands", slog.Any("err", err)) + } + } + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + if err = b.Client.OpenGateway(ctx); err != nil { + slog.Error("Failed to open gateway", slog.Any("err", err)) + os.Exit(-1) + } + + slog.Info("Bot is running. Press CTRL-C to exit.") + s := make(chan os.Signal, 1) + signal.Notify(s, syscall.SIGINT, syscall.SIGTERM) + <-s + slog.Info("Shutting down bot...") +} + +func setupLogger(cfg bottemplate.LogConfig) { + opts := &slog.HandlerOptions{ + AddSource: cfg.AddSource, + Level: cfg.Level, + } + + var sHandler slog.Handler + switch cfg.Format { + case "json": + sHandler = slog.NewJSONHandler(os.Stdout, opts) + case "text": + sHandler = slog.NewTextHandler(os.Stdout, opts) + default: + slog.Error("Unknown log format", slog.String("format", cfg.Format)) + os.Exit(-1) + } + slog.SetDefault(slog.New(sHandler)) +}