mirror of
https://github.com/Luzifer/twitch-bot.git
synced 2024-12-20 11:51:17 +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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
|
@ -23,14 +22,12 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/robfig/cron/v3"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/Luzifer/go_helpers/v2/backoff"
|
||||
"github.com/Luzifer/go_helpers/v2/str"
|
||||
"github.com/Luzifer/rconfig/v2"
|
||||
"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/v2migrator"
|
||||
"github.com/Luzifer/twitch-bot/v3/pkg/database"
|
||||
"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")
|
||||
}
|
||||
|
||||
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
|
||||
func main() {
|
||||
var err error
|
||||
|
@ -315,7 +228,9 @@ func main() {
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue