oklahomer/go-sarah

View on GitHub
_examples/simple/main.go

Summary

Maintainability
A
0 mins
Test Coverage
// Package main provides a simple bot experience using slack.Adapter with multiple plugin commands and scheduled tasks.
package main

import (
    "context"
    "flag"
    "github.com/oklahomer/go-kasumi/logger"
    "github.com/oklahomer/go-sarah/v4"
    "github.com/oklahomer/go-sarah/v4/alerter/line"
    "github.com/oklahomer/go-sarah/v4/slack"
    "github.com/oklahomer/go-sarah/v4/watchers"
    "gopkg.in/yaml.v2"
    "os"
    "os/signal"
    _ "simple/plugins/count"
    "simple/plugins/echo"
    _ "simple/plugins/fixedtimer"
    _ "simple/plugins/guess"
    _ "simple/plugins/hello"
    _ "simple/plugins/morning"
    _ "simple/plugins/timer"
    "simple/plugins/todo"
    "syscall"
)

type myConfig struct {
    CacheConfig     *sarah.CacheConfig `yaml:"cache"`
    Slack           *slack.Config      `yaml:"slack"`
    Runner          *sarah.Config      `yaml:"runner"`
    LineAlerter     *line.Config       `yaml:"line_alerter"`
    PluginConfigDir string             `yaml:"plugin_config_dir"`
}

func newMyConfig() *myConfig {
    // Use a constructor function for each config struct, so default values are pre-set.
    return &myConfig{
        CacheConfig: sarah.NewCacheConfig(),
        Slack:       slack.NewConfig(),
        Runner:      sarah.NewConfig(),
        LineAlerter: line.NewConfig(),
    }
}

func main() {
    var path = flag.String("config", "", "path to application configuration file.")
    flag.Parse()
    if *path == "" {
        panic("./bin/examples -config=/path/to/config/app.yml")
    }

    // Read a configuration file.
    config := readConfig(*path)

    // When the Bot encounters critical states, send an alert to LINE.
    // Any number of Alerter implementations can be registered.
    sarah.RegisterAlerter(line.New(config.LineAlerter))

    // Set up a storage that can be shared among different Bot implementations.
    storage := sarah.NewUserContextStorage(config.CacheConfig)

    // Set up Slack Bot.
    setupSlack(config.Slack, storage)

    // Set up some commands.
    todoCmd := todo.BuildCommand(&todo.DummyStorage{})
    sarah.RegisterCommand(slack.SLACK, todoCmd)

    // Directly add Command to Bot.
    // This Command is not subject to config file supervision.
    sarah.RegisterCommand(slack.SLACK, echo.Command)

    // Prepare Sarah's core context.
    ctx, cancel := context.WithCancel(context.Background())

    // Prepare a watcher that reads configuration from filesystem.
    if config.PluginConfigDir != "" {
        configWatcher, _ := watchers.NewFileWatcher(ctx, config.PluginConfigDir)
        sarah.RegisterConfigWatcher(configWatcher)
    }

    // Run.
    err := sarah.Run(ctx, config.Runner)
    if err != nil {
        panic(err)
    }

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    signal.Notify(c, syscall.SIGTERM)

    select {
    case <-c:
        logger.Info("Stopping due to signal reception.")
        cancel()

    }
}

func readConfig(path string) *myConfig {
    configBody, err := os.ReadFile(path)
    if err != nil {
        panic(err)
    }

    config := newMyConfig()
    err = yaml.Unmarshal(configBody, config)
    if err != nil {
        panic(err)
    }

    return config
}

func setupSlack(config *slack.Config, storage sarah.UserContextStorage) {
    //adapter, err := slack.NewAdapter(config, slack.WithEventsPayloadHandler(slack.DefaultEventsPayloadHandler))
    adapter, err := slack.NewAdapter(config, slack.WithRTMPayloadHandler(slack.DefaultRTMPayloadHandler))
    if err != nil {
        panic(err)
    }

    bot := sarah.NewBot(adapter, sarah.BotWithStorage(storage))

    // Register the bot to run.
    sarah.RegisterBot(bot)
}