From 6e5450aa80eaaa3fb7333f12bc60ef4b536f2619 Mon Sep 17 00:00:00 2001 From: Layla Date: Fri, 31 Mar 2023 20:21:49 +0000 Subject: [PATCH] Plugins, Bugfixes & Cleanup --- .devcontainer/Dockerfile | 22 ++---------- .devcontainer/devcontainer.json | 25 ++------------ Makefile | 2 +- app/plugin_loader.go | 59 +++++++++++++++++++++++++++++++++ core/configuration.go | 24 +++++++++++--- events/announce_events.go | 1 + events/manage_event_channels.go | 16 +++++---- go.mod | 3 +- go.sum | 2 ++ main.go | 18 +++++++--- 10 files changed, 113 insertions(+), 59 deletions(-) create mode 100644 app/plugin_loader.go diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 3c594c7..549ca40 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,20 +1,2 @@ -# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/go/.devcontainer/base.Dockerfile - -# [Choice] Go version (use -bullseye variants on local arm64/Apple Silicon): 1, 1.19, 1.18, 1-bullseye, 1.19-bullseye, 1.18-bullseye, 1-buster, 1.19-buster, 1.18-buster -ARG VARIANT="1.19-bullseye" -FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT} - -# [Choice] Node.js version: none, lts/*, 18, 16, 14 -ARG NODE_VERSION="none" -RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi - -# [Optional] Uncomment this section to install additional OS packages. -# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ -# && apt-get -y install --no-install-recommends - -# [Optional] Uncomment the next lines to use go get to install anything else you need -# USER vscode -# RUN go get -x - -# [Optional] Uncomment this line to install global node packages. -# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 +ARG VARIANT="1.20-bullseye" +FROM mcr.microsoft.com/vscode/devcontainers/go:0-${VARIANT} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 92cf392..bdb2356 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,25 +1,12 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: -// https://github.com/microsoft/vscode-dev-containers/tree/v0.245.2/containers/go { "name": "Go", "build": { - "dockerfile": "Dockerfile", - "args": { - // Update the VARIANT arg to pick a version of Go: 1, 1.19, 1.18 - // Append -bullseye or -buster to pin to an OS version. - // Use -bullseye variants on local arm64/Apple Silicon. - "VARIANT": "1.19-bullseye", - // Options - "NODE_VERSION": "none" - } + "dockerfile": "Dockerfile" }, "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined" ], - // Configure tool-specific properties. "customizations": { - // Configure properties specific to VS Code. "vscode": { - // Set *default* container specific settings.json values on container create. "settings": { "go.toolsManagement.checkForUpdates": "local", "go.useLanguageServer": true, @@ -33,15 +20,9 @@ } }, - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "go version", - - // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "vscode", "features": { - "git": "os-provided" + "git": "os-provided", + "ghcr.io/guiyomh/features/mage:0": "latest" } } diff --git a/Makefile b/Makefile index 5676e10..5aa2d10 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ go-full-build: go-clean go-get go-build go-build: @echo " > Building binary..." @mkdir -p $(GOBIN) - @GOOS=linux CGO_ENABLED=0 go build -ldflags "-X github.com/yeslayla/birdbot/app.Version=$(VERSION) -X github.com/yeslayla/birdbot/app.Build=$(BUILD_NUMBER)" -o $(GOBIN)/$(PROJECT_BIN) $(GOFILES) + @go build -ldflags "-X github.com/yeslayla/birdbot/app.Version=$(VERSION) -X github.com/yeslayla/birdbot/app.Build=$(BUILD_NUMBER)" -o $(GOBIN)/$(PROJECT_BIN) $(GOFILES) @chmod 755 $(GOBIN)/$(PROJECT_BIN) go-generate: diff --git a/app/plugin_loader.go b/app/plugin_loader.go new file mode 100644 index 0000000..4ad2d49 --- /dev/null +++ b/app/plugin_loader.go @@ -0,0 +1,59 @@ +package app + +import ( + "log" + "os" + "plugin" + + "github.com/yeslayla/birdbot/common" +) + +type PluginLoader struct{} + +func NewPluginLoader() PluginLoader { + return PluginLoader{} +} + +func (loader PluginLoader) LoadPlugin(pluginPath string) common.Component { + plug, err := plugin.Open(pluginPath) + if err != nil { + log.Printf("Failed to load plugin '%s': %s", pluginPath, err) + return nil + } + + sym, err := plug.Lookup("Component") + if err != nil { + log.Printf("Failed to load plugin '%s': failed to get Component: %s", pluginPath, err) + return nil + } + + var component common.Component + component, ok := sym.(common.Component) + if !ok { + log.Printf("Failed to load plugin '%s': Plugin component does not properly implement interface!", pluginPath) + } + + return component +} + +func (loader PluginLoader) LoadPlugins(directory string) []common.Component { + + paths, err := os.ReadDir(directory) + if err != nil { + log.Printf("Failed to load plugins: %s", err) + return []common.Component{} + } + + var components []common.Component = make([]common.Component, 0) + for _, path := range paths { + if path.IsDir() { + continue + } + + if comp := loader.LoadPlugin(path.Name()); comp != nil { + components = append(components, comp) + } + } + + return components +} diff --git a/core/configuration.go b/core/configuration.go index 1d049ff..a98cedb 100644 --- a/core/configuration.go +++ b/core/configuration.go @@ -1,5 +1,7 @@ package core +import "strings" + type Config struct { Discord DiscordConfig `yaml:"discord"` Mastodon MastodonConfig `yaml:"mastodon"` @@ -24,8 +26,22 @@ type MastodonConfig struct { } type Features struct { - ManageEventChannels bool `yaml:"manage_event_channels" env:"BIRD_EVENT_CHANNELS" env-default:"true"` - AnnounceEvents bool `yaml:"announce_events" env:"BIRD_ANNOUNCE_EVENTS" env-default:"true"` - ReccurringEvents bool `yaml:"recurring_events" env:"BIRD_RECURRING_EVENTS" env-default:"true"` - LoadGamePlugins bool `yaml:"load_game_plugins" env:"BIRD_LOAD_GAME_PLUGINS" env-default:"true"` + ManageEventChannels Feature `yaml:"manage_event_channels" env:"BIRD_EVENT_CHANNELS"` + AnnounceEvents Feature `yaml:"announce_events" env:"BIRD_ANNOUNCE_EVENTS"` + ReccurringEvents Feature `yaml:"recurring_events" env:"BIRD_RECURRING_EVENTS"` + LoadGamePlugins Feature `yaml:"load_game_plugins" env:"BIRD_LOAD_GAME_PLUGINS"` +} + +type Feature string + +func (value Feature) IsEnabled() bool { + return strings.ToLower(string(value)) == "true" +} + +func (value Feature) IsEnabledByDefault() bool { + v := strings.ToLower(string(value)) + if v == "" { + v = "true" + } + return Feature(v).IsEnabled() } diff --git a/events/announce_events.go b/events/announce_events.go index 09c10f6..10a7648 100644 --- a/events/announce_events.go +++ b/events/announce_events.go @@ -24,6 +24,7 @@ func (c *AnnounceEventsComponent) Initialize(birdbot common.ComponentManager) er c.bot = birdbot _ = birdbot.OnEventCreate(c.OnEventCreate) + _ = birdbot.OnEventDelete(c.OnEventDelete) return nil } diff --git a/events/manage_event_channels.go b/events/manage_event_channels.go index 67893b2..d431b41 100644 --- a/events/manage_event_channels.go +++ b/events/manage_event_channels.go @@ -9,14 +9,16 @@ import ( ) type ManageEventChannelsComponent struct { - session *discord.Discord - categoryID string + session *discord.Discord + categoryID string + archiveCategoryID string } -func NewManageEventChannelsComponent(categoryID string, session *discord.Discord) *ManageEventChannelsComponent { +func NewManageEventChannelsComponent(categoryID string, archiveCategoryID string, session *discord.Discord) *ManageEventChannelsComponent { return &ManageEventChannelsComponent{ - session: session, - categoryID: categoryID, + session: session, + categoryID: categoryID, + archiveCategoryID: archiveCategoryID, } } @@ -54,9 +56,9 @@ func (c *ManageEventChannelsComponent) OnEventDelete(e common.Event) error { func (c *ManageEventChannelsComponent) OnEventComplete(e common.Event) error { channel := core.GenerateChannel(e) - if c.categoryID != "" { + if c.archiveCategoryID != "" { - if err := c.session.MoveChannelToCategory(channel, c.categoryID); err != nil { + if err := c.session.MoveChannelToCategory(channel, c.archiveCategoryID); err != nil { log.Print("Failed to move channel to archive category: ", err) } diff --git a/go.mod b/go.mod index 98eee0b..68fbe03 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/yeslayla/birdbot -go 1.19 +go 1.20 require ( github.com/bwmarrin/discordgo v0.26.1 @@ -13,6 +13,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/joho/godotenv v1.4.0 // indirect + github.com/magefile/mage v1.14.0 // indirect github.com/mattn/go-mastodon v0.0.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/objx v0.5.0 // indirect diff --git a/go.sum b/go.sum index c670bf9..8d73995 100644 --- a/go.sum +++ b/go.sum @@ -14,6 +14,8 @@ github.com/ilyakaznacheev/cleanenv v1.4.0 h1:Gvwxt6wAPUo9OOxyp5Xz9eqhLsAey4AtbCF github.com/ilyakaznacheev/cleanenv v1.4.0/go.mod h1:i0owW+HDxeGKE0/JPREJOdSCPIyOnmh6C0xhWAkF/xA= github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo= +github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/mattn/go-mastodon v0.0.5 h1:P0e1/R2v3ho6kM7BUW0noQm8gAqHE0p8Gq1TMapIVAc= github.com/mattn/go-mastodon v0.0.5/go.mod h1:cg7RFk2pcUfHZw/IvKe1FUzmlq5KnLFqs7eV2PHplV8= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= diff --git a/main.go b/main.go index 8a5c2b4..64bf43e 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,8 @@ import ( "github.com/yeslayla/birdbot/events" ) +const PluginsDirectory = "./plugins" + func main() { configDir, _ := os.UserConfigDir() @@ -56,16 +58,24 @@ func main() { loader := app.NewComponentLoader(bot) - if cfg.Features.AnnounceEvents { + if cfg.Features.AnnounceEvents.IsEnabledByDefault() { loader.LoadComponent(events.NewAnnounceEventsComponent(bot.Mastodon, cfg.Discord.NotificationChannel)) } - if cfg.Features.ManageEventChannels { - loader.LoadComponent(events.NewManageEventChannelsComponent(cfg.Discord.EventCategory, bot.Session)) + if cfg.Features.ManageEventChannels.IsEnabledByDefault() { + loader.LoadComponent(events.NewManageEventChannelsComponent(cfg.Discord.EventCategory, cfg.Discord.ArchiveCategory, bot.Session)) } - if cfg.Features.ReccurringEvents { + if cfg.Features.ReccurringEvents.IsEnabledByDefault() { loader.LoadComponent(events.NewRecurringEventsComponent()) } + if _, err := os.Stat(PluginsDirectory); !os.IsNotExist(err) { + pluginLoader := app.NewPluginLoader() + components := pluginLoader.LoadPlugins(PluginsDirectory) + for _, comp := range components { + loader.LoadComponent(comp) + } + } + if err := bot.Run(); err != nil { log.Fatal(err) }