mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-11-12 18:12:41 +00:00
[core] Split out cli commands
Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
parent
120f88ecbd
commit
d8c6f8d221
7 changed files with 232 additions and 88 deletions
85
cli.go
Normal file
85
cli.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
cliRegistry struct {
|
||||||
|
cmds map[string]cliRegistryEntry
|
||||||
|
sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
cliRegistryEntry struct {
|
||||||
|
Description string
|
||||||
|
Name string
|
||||||
|
Params []string
|
||||||
|
Run func([]string) error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
cli = newCLIRegistry()
|
||||||
|
errHelpCalled = errors.New("help called")
|
||||||
|
)
|
||||||
|
|
||||||
|
func newCLIRegistry() *cliRegistry {
|
||||||
|
return &cliRegistry{
|
||||||
|
cmds: make(map[string]cliRegistryEntry),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cliRegistry) Add(e cliRegistryEntry) {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
c.cmds[e.Name] = e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cliRegistry) Call(args []string) error {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
cmdEntry := c.cmds[args[0]]
|
||||||
|
if cmdEntry.Name != args[0] {
|
||||||
|
c.help()
|
||||||
|
return errHelpCalled
|
||||||
|
}
|
||||||
|
|
||||||
|
return cmdEntry.Run(args)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cliRegistry) help() {
|
||||||
|
// Called from Call, does not need lock
|
||||||
|
|
||||||
|
var (
|
||||||
|
maxCmdLen int
|
||||||
|
cmds []cliRegistryEntry
|
||||||
|
)
|
||||||
|
|
||||||
|
for name := range c.cmds {
|
||||||
|
entry := c.cmds[name]
|
||||||
|
if l := len(entry.CommandDisplay()); l > maxCmdLen {
|
||||||
|
maxCmdLen = l
|
||||||
|
}
|
||||||
|
cmds = append(cmds, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(cmds, func(i, j int) bool { return cmds[i].Name < cmds[j].Name })
|
||||||
|
|
||||||
|
tpl := fmt.Sprintf(" %%-%ds %%s\n", maxCmdLen)
|
||||||
|
fmt.Fprintln(os.Stdout, "Supported sub-commands are:")
|
||||||
|
for _, cmd := range cmds {
|
||||||
|
fmt.Fprintf(os.Stdout, tpl, cmd.CommandDisplay(), cmd.Description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c cliRegistryEntry) CommandDisplay() string {
|
||||||
|
return strings.Join(append([]string{c.Name}, c.Params...), " ")
|
||||||
|
}
|
26
cli_actorDocs.go
Normal file
26
cli_actorDocs.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.Add(cliRegistryEntry{
|
||||||
|
Name: "actor-docs",
|
||||||
|
Description: "Generate markdown documentation for available actors",
|
||||||
|
Run: func(args []string) error {
|
||||||
|
doc, err := generateActorDocs()
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "generating actor docs")
|
||||||
|
}
|
||||||
|
if _, err = os.Stdout.Write(append(bytes.TrimSpace(doc), '\n')); err != nil {
|
||||||
|
return errors.Wrap(err, "writing actor docs to stdout")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
43
cli_apiToken.go
Normal file
43
cli_apiToken.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gofrs/uuid/v3"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.Add(cliRegistryEntry{
|
||||||
|
Name: "api-token",
|
||||||
|
Description: "Generate an api-token to be entered into the config",
|
||||||
|
Params: []string{"<token-name>", "<scope>", "[...scope]"},
|
||||||
|
Run: func(args []string) error {
|
||||||
|
if len(args) < 3 { //nolint:gomnd // Just a count of parameters
|
||||||
|
return errors.New("Usage: twitch-bot api-token <token name> <scope> [...scope]")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := configAuthToken{
|
||||||
|
Name: args[1],
|
||||||
|
Modules: args[2:],
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fillAuthToken(&t); err != nil {
|
||||||
|
return errors.Wrap(err, "generating token")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithField("token", t.Token).Info("Token generated, add this to your config:")
|
||||||
|
if err := yaml.NewEncoder(os.Stdout).Encode(map[string]map[string]configAuthToken{
|
||||||
|
"auth_tokens": {
|
||||||
|
uuid.Must(uuid.NewV4()).String(): t,
|
||||||
|
},
|
||||||
|
}); err != nil {
|
||||||
|
return errors.Wrap(err, "printing token info")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
33
cli_migrateV2.go
Normal file
33
cli_migrateV2.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
"github.com/Luzifer/twitch-bot/v3/internal/v2migrator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.Add(cliRegistryEntry{
|
||||||
|
Name: "migrate-v2",
|
||||||
|
Description: "Migrate old (*.json.gz) storage file into new database",
|
||||||
|
Params: []string{"<old-file>"},
|
||||||
|
Run: func(args []string) error {
|
||||||
|
if len(args) < 2 { //nolint:gomnd // Just a count of parameters
|
||||||
|
return errors.New("Usage: twitch-bot migrate-v2 <old storage file>")
|
||||||
|
}
|
||||||
|
|
||||||
|
v2s := v2migrator.NewStorageFile()
|
||||||
|
if err := v2s.Load(args[1], cfg.StorageEncryptionPass); err != nil {
|
||||||
|
return errors.Wrap(err, "loading v2 storage file")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := v2s.Migrate(db); err != nil {
|
||||||
|
return errors.Wrap(err, "migrating v2 storage file")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info("v2 storage file was migrated")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
26
cli_resetSecrets.go
Normal file
26
cli_resetSecrets.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.Add(cliRegistryEntry{
|
||||||
|
Name: "reset-secrets",
|
||||||
|
Description: "Remove encrypted data to reset encryption passphrase",
|
||||||
|
Run: func(args []string) error {
|
||||||
|
if err := accessService.RemoveAllExtendedTwitchCredentials(); err != nil {
|
||||||
|
return errors.Wrap(err, "resetting Twitch credentials")
|
||||||
|
}
|
||||||
|
log.Info("removed stored Twitch credentials")
|
||||||
|
|
||||||
|
if err := db.ResetEncryptedCoreMeta(); err != nil {
|
||||||
|
return errors.Wrap(err, "resetting encrypted meta entries")
|
||||||
|
}
|
||||||
|
log.Info("removed encrypted meta entries")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
16
cli_validateConfig.go
Normal file
16
cli_validateConfig.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/pkg/errors"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
cli.Add(cliRegistryEntry{
|
||||||
|
Name: "validate-config",
|
||||||
|
Description: "Try to load configuration file and report errors if any",
|
||||||
|
Run: func(args []string) error {
|
||||||
|
return errors.Wrap(
|
||||||
|
loadConfig(cfg.Config),
|
||||||
|
"loading config",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
91
main.go
91
main.go
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
@ -23,14 +22,12 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/robfig/cron/v3"
|
"github.com/robfig/cron/v3"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/Luzifer/go_helpers/v2/backoff"
|
"github.com/Luzifer/go_helpers/v2/backoff"
|
||||||
"github.com/Luzifer/go_helpers/v2/str"
|
"github.com/Luzifer/go_helpers/v2/str"
|
||||||
"github.com/Luzifer/rconfig/v2"
|
"github.com/Luzifer/rconfig/v2"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/service/access"
|
"github.com/Luzifer/twitch-bot/v3/internal/service/access"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/service/timer"
|
"github.com/Luzifer/twitch-bot/v3/internal/service/timer"
|
||||||
"github.com/Luzifer/twitch-bot/v3/internal/v2migrator"
|
|
||||||
"github.com/Luzifer/twitch-bot/v3/pkg/database"
|
"github.com/Luzifer/twitch-bot/v3/pkg/database"
|
||||||
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
"github.com/Luzifer/twitch-bot/v3/pkg/twitch"
|
||||||
)
|
)
|
||||||
|
@ -158,90 +155,6 @@ func getEventSubSecret() (secret, handle string, err error) {
|
||||||
return eventSubSecret, eventSubSecret[:5], errors.Wrap(db.StoreEncryptedCoreMeta(coreMetaKeyEventSubSecret, eventSubSecret), "storing secret to database")
|
return eventSubSecret, eventSubSecret[:5], errors.Wrap(db.StoreEncryptedCoreMeta(coreMetaKeyEventSubSecret, eventSubSecret), "storing secret to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSubCommand(args []string) {
|
|
||||||
switch args[0] {
|
|
||||||
|
|
||||||
case "actor-docs":
|
|
||||||
doc, err := generateActorDocs()
|
|
||||||
if err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to generate actor docs")
|
|
||||||
}
|
|
||||||
if _, err = os.Stdout.Write(append(bytes.TrimSpace(doc), '\n')); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to write actor docs to stdout")
|
|
||||||
}
|
|
||||||
|
|
||||||
case "api-token":
|
|
||||||
if len(args) < 3 { //nolint:gomnd // Just a count of parameters
|
|
||||||
log.Fatalf("Usage: twitch-bot api-token <token name> <scope> [...scope]")
|
|
||||||
}
|
|
||||||
|
|
||||||
t := configAuthToken{
|
|
||||||
Name: args[1],
|
|
||||||
Modules: args[2:],
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := fillAuthToken(&t); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to generate token")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithField("token", t.Token).Info("Token generated, add this to your config:")
|
|
||||||
if err := yaml.NewEncoder(os.Stdout).Encode(map[string]map[string]configAuthToken{
|
|
||||||
"auth_tokens": {
|
|
||||||
uuid.Must(uuid.NewV4()).String(): t,
|
|
||||||
},
|
|
||||||
}); err != nil {
|
|
||||||
log.WithError(err).Fatal("Unable to output token info")
|
|
||||||
}
|
|
||||||
|
|
||||||
case "help":
|
|
||||||
fmt.Println("Supported sub-commands are:")
|
|
||||||
fmt.Println(" actor-docs Generate markdown documentation for available actors")
|
|
||||||
fmt.Println(" api-token <name> <scope...> Generate an api-token to be entered into the config")
|
|
||||||
fmt.Println(" migrate-v2 <old file> Migrate old (*.json.gz) storage file into new database")
|
|
||||||
fmt.Println(" reset-secrets Remove encrypted data to reset encryption passphrase")
|
|
||||||
fmt.Println(" validate-config Try to load configuration file and report errors if any")
|
|
||||||
fmt.Println(" help Prints this help message")
|
|
||||||
|
|
||||||
case "migrate-v2":
|
|
||||||
if len(args) < 2 { //nolint:gomnd // Just a count of parameters
|
|
||||||
log.Fatalf("Usage: twitch-bot migrate-v2 <old storage file>")
|
|
||||||
}
|
|
||||||
|
|
||||||
v2s := v2migrator.NewStorageFile()
|
|
||||||
if err := v2s.Load(args[1], cfg.StorageEncryptionPass); err != nil {
|
|
||||||
log.WithError(err).Fatal("loading v2 storage file")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v2s.Migrate(db); err != nil {
|
|
||||||
log.WithError(err).Fatal("migrating v2 storage file")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info("v2 storage file was migrated")
|
|
||||||
|
|
||||||
case "reset-secrets":
|
|
||||||
// Nuke permission table entries
|
|
||||||
if err := accessService.RemoveAllExtendedTwitchCredentials(); err != nil {
|
|
||||||
log.WithError(err).Fatal("resetting Twitch credentials")
|
|
||||||
}
|
|
||||||
log.Info("removed stored Twitch credentials")
|
|
||||||
|
|
||||||
if err := db.ResetEncryptedCoreMeta(); err != nil {
|
|
||||||
log.WithError(err).Fatal("resetting encrypted meta entries")
|
|
||||||
}
|
|
||||||
log.Info("removed encrypted meta entries")
|
|
||||||
|
|
||||||
case "validate-config":
|
|
||||||
if err := loadConfig(cfg.Config); err != nil {
|
|
||||||
log.WithError(err).Fatal("loading config")
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
handleSubCommand([]string{"help"})
|
|
||||||
log.Fatalf("Unknown sub-command %q", args[0])
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:funlen,gocognit,gocyclo // Complexity is a little too high but makes no sense to split
|
//nolint:funlen,gocognit,gocyclo // Complexity is a little too high but makes no sense to split
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
|
@ -315,7 +228,9 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(rconfig.Args()) > 1 {
|
if len(rconfig.Args()) > 1 {
|
||||||
handleSubCommand(rconfig.Args()[1:])
|
if err = cli.Call(rconfig.Args()[1:]); err != nil {
|
||||||
|
log.Fatalf("error in command: %s", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue