Compare commits
8 commits
2e455fc9f8
...
795e344198
Author | SHA1 | Date | |
---|---|---|---|
795e344198 | |||
f3a5887cb9 | |||
eb016b96e9 | |||
a0eab3b6ae | |||
878a898d4c | |||
b77bbaa6e0 | |||
84091f398d | |||
299b134b58 |
58 changed files with 2837 additions and 1594 deletions
160
cmd/enbas-codegen/main.go
Normal file
160
cmd/enbas-codegen/main.go
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
enbasCLISchemaFilepath string
|
||||||
|
packageName string
|
||||||
|
)
|
||||||
|
|
||||||
|
flag.StringVar(&enbasCLISchemaFilepath, "path-to-enbas-cli-schema", "", "The path to the Enbas CLI schema file")
|
||||||
|
flag.StringVar(&packageName, "package", "", "The name of the internal package")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
schema, err := newEnbasCLISchemaFromFile(enbasCLISchemaFilepath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("ERROR: Unable to read the schema file: %v.\n", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := generateExecutors(schema, packageName); err != nil {
|
||||||
|
fmt.Printf("ERROR: Unable to generate the executors: %v.\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:embed templates/*
|
||||||
|
var executorTemplates embed.FS
|
||||||
|
|
||||||
|
var errNoPackageFlag = errors.New("the --package flag must be used")
|
||||||
|
|
||||||
|
func generateExecutors(schema enbasCLISchema, packageName string) error {
|
||||||
|
if packageName == "" {
|
||||||
|
return errNoPackageFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
dirName := "templates/" + packageName
|
||||||
|
|
||||||
|
fsDir, err := executorTemplates.ReadDir(dirName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to read the template directory in the file system (FS): %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
funcMap := template.FuncMap{
|
||||||
|
"capitalise": capitalise,
|
||||||
|
"flagFieldName": flagFieldName,
|
||||||
|
"getFlagType": schema.Flags.getType,
|
||||||
|
"getFlagDescription": schema.Flags.getDescription,
|
||||||
|
"internalFlagValue": internalFlagValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, obj := range fsDir {
|
||||||
|
templateFilename := obj.Name()
|
||||||
|
|
||||||
|
if !strings.HasSuffix(templateFilename, ".go.gotmpl") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := func() error {
|
||||||
|
tmpl := template.Must(template.New(templateFilename).
|
||||||
|
Funcs(funcMap).
|
||||||
|
ParseFS(executorTemplates, dirName+"/"+templateFilename),
|
||||||
|
)
|
||||||
|
|
||||||
|
output := strings.TrimSuffix(templateFilename, ".gotmpl")
|
||||||
|
|
||||||
|
file, err := os.Create(output)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the output file: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if err := tmpl.Execute(file, schema.Commands); err != nil {
|
||||||
|
return fmt.Errorf("unable to generate the code from the template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := runGoImports(output); err != nil {
|
||||||
|
return fmt.Errorf("unable to run goimports: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}(); err != nil {
|
||||||
|
return fmt.Errorf("received an error after attempting to generate the code for %q: %w", templateFilename, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func runGoImports(path string) error {
|
||||||
|
imports := exec.Command("goimports", "-w", path)
|
||||||
|
|
||||||
|
if err := imports.Run(); err != nil {
|
||||||
|
return fmt.Errorf("received an error after running goimports: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func capitalise(str string) string {
|
||||||
|
runes := []rune(str)
|
||||||
|
|
||||||
|
runes[0] = unicode.ToUpper(runes[0])
|
||||||
|
|
||||||
|
return string(runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func flagFieldName(flagRef enbasCLISchemaFlagReference) string {
|
||||||
|
if flagRef.FieldName != "" {
|
||||||
|
return flagRef.FieldName
|
||||||
|
}
|
||||||
|
|
||||||
|
return convertFlagToMixedCaps(flagRef.Flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertFlagToMixedCaps(value string) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
runes := []rune(value)
|
||||||
|
numRunes := len(runes)
|
||||||
|
cursor := 0
|
||||||
|
|
||||||
|
for cursor < numRunes {
|
||||||
|
if runes[cursor] != '-' {
|
||||||
|
builder.WriteRune(runes[cursor])
|
||||||
|
|
||||||
|
cursor++
|
||||||
|
} else {
|
||||||
|
if cursor != numRunes-1 && unicode.IsLower(runes[cursor+1]) {
|
||||||
|
builder.WriteRune(unicode.ToUpper(runes[cursor+1]))
|
||||||
|
cursor += 2
|
||||||
|
} else {
|
||||||
|
cursor++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func internalFlagValue(flagType string) bool {
|
||||||
|
internalFlagValues := map[string]struct{}{
|
||||||
|
"StringSliceValue": {},
|
||||||
|
"IntSliceValue": {},
|
||||||
|
"TimeDurationValue": {},
|
||||||
|
"BoolPtrValue": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exists := internalFlagValues[flagType]
|
||||||
|
|
||||||
|
return exists
|
||||||
|
}
|
72
cmd/enbas-codegen/schema.go
Normal file
72
cmd/enbas-codegen/schema.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type enbasCLISchema struct {
|
||||||
|
Flags enbasCLISchemaFlagMap `json:"flags"`
|
||||||
|
Commands map[string]enbasCLISchemaCommand `json:"commands"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func newEnbasCLISchemaFromFile(path string) (enbasCLISchema, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return enbasCLISchema{}, fmt.Errorf("unable to open the schema file: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var schema enbasCLISchema
|
||||||
|
|
||||||
|
if err := json.NewDecoder(file).Decode(&schema); err != nil {
|
||||||
|
return enbasCLISchema{}, fmt.Errorf("unable to decode the JSON data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type enbasCLISchemaFlag struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type enbasCLISchemaFlagMap map[string]enbasCLISchemaFlag
|
||||||
|
|
||||||
|
func (e enbasCLISchemaFlagMap) getType(name string) string {
|
||||||
|
flag, ok := e[name]
|
||||||
|
if !ok {
|
||||||
|
return "UNKNOWN TYPE"
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e enbasCLISchemaFlagMap) getDescription(name string) string {
|
||||||
|
flag, ok := e[name]
|
||||||
|
if !ok {
|
||||||
|
return "UNKNOWN DESCRIPTION"
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
type enbasCLISchemaCommand struct {
|
||||||
|
AdditionalFields []enbasCLISchemaAdditionalFields `json:"additionalFields"`
|
||||||
|
Flags []enbasCLISchemaFlagReference `json:"flags"`
|
||||||
|
Summary string `json:"summary"`
|
||||||
|
UseConfig bool `json:"useConfig"`
|
||||||
|
UsePrinter bool `json:"usePrinter"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type enbasCLISchemaFlagReference struct {
|
||||||
|
Flag string `json:"flag"`
|
||||||
|
FieldName string `json:"fieldName"`
|
||||||
|
Default string `json:"default"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type enbasCLISchemaAdditionalFields struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
}
|
109
cmd/enbas-codegen/templates/executor/execute.go.gotmpl
Normal file
109
cmd/enbas-codegen/templates/executor/execute.go.gotmpl
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{{- /* vim: set noexpandtab : */ -}}
|
||||||
|
{{- /* vim: set tabstop=8 : */ -}}
|
||||||
|
{{- /* vim: set shiftwidth=8 : */ -}}
|
||||||
|
{{- /* vim: set softtabstop=8 : */ -}}
|
||||||
|
/*
|
||||||
|
This file is generated by the enbas-codegen
|
||||||
|
DO NOT EDIT.
|
||||||
|
*/
|
||||||
|
{{ print "" }}
|
||||||
|
package executor
|
||||||
|
{{ print "" }}
|
||||||
|
{{ print "" }}
|
||||||
|
import "fmt"
|
||||||
|
import "codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
import "codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
|
{{ print "" }}
|
||||||
|
{{ print "" }}
|
||||||
|
func Execute(
|
||||||
|
command string,
|
||||||
|
args []string,
|
||||||
|
noColor bool,
|
||||||
|
configDir string,
|
||||||
|
) error {
|
||||||
|
var (
|
||||||
|
enbasConfig *config.Config
|
||||||
|
enbasPrinter *printer.Printer
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "init", "version":
|
||||||
|
enbasPrinter = printer.NewPrinter(noColor, "", 0)
|
||||||
|
default:
|
||||||
|
enbasConfig, err = config.NewConfigFromFile(configDir)
|
||||||
|
if err != nil {
|
||||||
|
enbasPrinter = printer.NewPrinter(noColor, "", 0)
|
||||||
|
enbasPrinter.PrintFailure("unable to load the configuration: " + err.Error() + ".")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
enbasPrinter = printer.NewPrinter(
|
||||||
|
noColor,
|
||||||
|
enbasConfig.Integrations.Pager,
|
||||||
|
enbasConfig.LineWrapMaxWidth,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = execute(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
configDir,
|
||||||
|
); err != nil {
|
||||||
|
enbasPrinter.PrintFailure("(" + command + ") " + err.Error() + ".")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ print "" }}
|
||||||
|
func execute(
|
||||||
|
command string,
|
||||||
|
args []string,
|
||||||
|
enbasPrinter *printer.Printer,
|
||||||
|
enbasConfig *config.Config,
|
||||||
|
configDir string,
|
||||||
|
) error {
|
||||||
|
executorMap := map[string]Executor{
|
||||||
|
{{- range $name, $command := . -}}
|
||||||
|
{{- $new_executor_function_name := capitalise $name | printf "New%sExecutor" -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ printf "%q" $name }}: {{ $new_executor_function_name }}(
|
||||||
|
{{- if $command.UsePrinter -}}
|
||||||
|
{{ print "" }}
|
||||||
|
enbasPrinter,
|
||||||
|
{{- end -}}
|
||||||
|
{{- if $command.UseConfig -}}
|
||||||
|
{{ print "" }}
|
||||||
|
enbasConfig,
|
||||||
|
{{- end -}}
|
||||||
|
{{- range $field := $command.AdditionalFields -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ $field.Name }},
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
),
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
}
|
||||||
|
|
||||||
|
exe, ok := executorMap[command]
|
||||||
|
if !ok {
|
||||||
|
return UnknownCommandError{Command: command}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exe.Parse(args); err != nil {
|
||||||
|
return fmt.Errorf("flag parsing error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exe.Execute(); err != nil {
|
||||||
|
return fmt.Errorf("execution error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
109
cmd/enbas-codegen/templates/executor/executors.go.gotmpl
Normal file
109
cmd/enbas-codegen/templates/executor/executors.go.gotmpl
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
{{- /* vim: set noexpandtab : */ -}}
|
||||||
|
{{- /* vim: set tabstop=8 : */ -}}
|
||||||
|
{{- /* vim: set shiftwidth=8 : */ -}}
|
||||||
|
{{- /* vim: set softtabstop=8 : */ -}}
|
||||||
|
/*
|
||||||
|
This file is generated by the enbas-codegen
|
||||||
|
DO NOT EDIT.
|
||||||
|
*/
|
||||||
|
{{ print "" }}
|
||||||
|
package executor
|
||||||
|
{{ print "" }}
|
||||||
|
{{ print "" }}
|
||||||
|
import internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
|
||||||
|
import "codeflow.dananglin.me.uk/apollo/enbas/internal/usage"
|
||||||
|
{{ print "" }}
|
||||||
|
{{ print "" }}
|
||||||
|
type Executor interface {
|
||||||
|
Name() string
|
||||||
|
Parse(args []string) error
|
||||||
|
Execute() error
|
||||||
|
}
|
||||||
|
{{ range $name, $command := . }}
|
||||||
|
{{- $struct_name := capitalise $name | printf "%sExecutor" -}}
|
||||||
|
{{- $new_executor_function_name := capitalise $name | printf "New%sExecutor" -}}
|
||||||
|
{{ print "" }}
|
||||||
|
// {{ $struct_name }} is the executor for the {{ $name }} command.
|
||||||
|
type {{ $struct_name }} struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
{{- if $command.UsePrinter }}
|
||||||
|
printer *printer.Printer
|
||||||
|
{{- end }}
|
||||||
|
{{- if $command.UseConfig }}
|
||||||
|
config *config.Config
|
||||||
|
{{- end }}
|
||||||
|
{{- range $flag := $command.Flags -}}
|
||||||
|
{{- $flag_type := getFlagType $flag.Flag -}}
|
||||||
|
{{- if internalFlagValue $flag_type -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ flagFieldName $flag }} internalFlag.{{ $flag_type }}
|
||||||
|
{{- else -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ flagFieldName $flag }} {{ $flag_type }}
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range $field := $command.AdditionalFields -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ $field.Name }} {{ $field.Type }}
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
}
|
||||||
|
|
||||||
|
func {{ $new_executor_function_name }}(
|
||||||
|
{{- if $command.UsePrinter }}
|
||||||
|
printer *printer.Printer,
|
||||||
|
{{- end }}
|
||||||
|
{{- if $command.UseConfig }}
|
||||||
|
config *config.Config,
|
||||||
|
{{- end }}
|
||||||
|
{{- range $field := $command.AdditionalFields -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ $field.Name }} {{ $field.Type }},
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
) *{{ $struct_name }} {
|
||||||
|
exe := {{ $struct_name }}{
|
||||||
|
FlagSet: flag.NewFlagSet({{ printf "%q" $name }}, flag.ExitOnError),
|
||||||
|
{{- if $command.UsePrinter }}
|
||||||
|
printer: printer,
|
||||||
|
{{- end }}
|
||||||
|
{{- if $command.UseConfig }}
|
||||||
|
config: config,
|
||||||
|
{{- end }}
|
||||||
|
{{- range $flag := $command.Flags -}}
|
||||||
|
{{- $flag_type := getFlagType $flag.Flag -}}
|
||||||
|
{{- if internalFlagValue $flag_type -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ flagFieldName $flag }}: internalFlag.New{{ $flag_type }}(),
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{- range $field := $command.AdditionalFields -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ $field.Name }}: {{ $field.Name }},
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
}
|
||||||
|
{{ print "" }}
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc({{ printf "%q" $name }}, {{ printf "%q" $command.Summary }}, exe.FlagSet)
|
||||||
|
{{ print "" }}
|
||||||
|
{{- range $flag := $command.Flags -}}
|
||||||
|
{{- $flag_type := getFlagType $flag.Flag -}}
|
||||||
|
{{- if eq $flag_type "string" -}}
|
||||||
|
{{ print "" }}
|
||||||
|
exe.StringVar(&exe.{{ flagFieldName $flag }}, {{ printf "%q" $flag.Flag }}, {{ printf "%q" $flag.Default }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
|
||||||
|
{{- else if eq $flag_type "bool" -}}
|
||||||
|
{{ print "" }}
|
||||||
|
exe.BoolVar(&exe.{{ flagFieldName $flag }}, {{ printf "%q" $flag.Flag }}, {{ $flag.Default }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
|
||||||
|
{{- else if eq $flag_type "int" -}}
|
||||||
|
{{ print "" }}
|
||||||
|
exe.IntVar(&exe.{{ flagFieldName $flag }}, {{ printf "%q" $flag.Flag }}, {{ $flag.Default }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
|
||||||
|
{{- else if internalFlagValue $flag_type -}}
|
||||||
|
{{ print "" }}
|
||||||
|
exe.Var(&exe.{{ flagFieldName $flag }}, {{ printf "%q" $flag.Flag }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
|
||||||
|
{{- end -}}
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ print "" }}
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
{{ end }}
|
18
cmd/enbas-codegen/templates/usage/summaries.go.gotmpl
Normal file
18
cmd/enbas-codegen/templates/usage/summaries.go.gotmpl
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{{- /* vim: set noexpandtab : */ -}}
|
||||||
|
{{- /* vim: set tabstop=8 : */ -}}
|
||||||
|
{{- /* vim: set shiftwidth=8 : */ -}}
|
||||||
|
{{- /* vim: set softtabstop=8 : */ -}}
|
||||||
|
/*
|
||||||
|
This file is generated by the enbas-codegen
|
||||||
|
DO NOT EDIT.
|
||||||
|
*/
|
||||||
|
{{ print "" }}
|
||||||
|
package usage
|
||||||
|
{{ print "" }}
|
||||||
|
var summaries = map[string]string {
|
||||||
|
{{- range $name, $command := . -}}
|
||||||
|
{{ print "" }}
|
||||||
|
{{ printf "%q" $name }}: {{ printf "%q" $command.Summary }},
|
||||||
|
{{- end -}}
|
||||||
|
{{ print "" }}
|
||||||
|
}
|
|
@ -2,20 +2,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
|
||||||
)
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/usage"
|
||||||
|
|
||||||
var (
|
|
||||||
binaryVersion string //nolint:gochecknoglobals
|
|
||||||
buildTime string //nolint:gochecknoglobals
|
|
||||||
goVersion string //nolint:gochecknoglobals
|
|
||||||
gitCommit string //nolint:gochecknoglobals
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -27,23 +18,13 @@ func main() {
|
||||||
func run() error {
|
func run() error {
|
||||||
var (
|
var (
|
||||||
configDir string
|
configDir string
|
||||||
noColor *bool
|
noColorFlag internalFlag.BoolPtrValue
|
||||||
)
|
)
|
||||||
|
|
||||||
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
|
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
|
||||||
flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error {
|
flag.Var(&noColorFlag, "no-color", "Disable ANSI colour output when displaying text on screen")
|
||||||
boolVal, err := strconv.ParseBool(value)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse %q as a boolean: %w", value, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
noColor = new(bool)
|
flag.Usage = usage.AppUsageFunc()
|
||||||
*noColor = boolVal
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
flag.Usage = usageFunc(executor.CommandSummaryMap())
|
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
@ -53,176 +34,16 @@ func run() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If NoColor is still unspecified,
|
var noColor bool
|
||||||
// check to see if the NO_COLOR environment variable is set
|
|
||||||
if noColor == nil {
|
if noColorFlag.Value != nil {
|
||||||
noColor = new(bool)
|
noColor = *noColorFlag.Value
|
||||||
if os.Getenv("NO_COLOR") != "" {
|
} else if os.Getenv("NO_COLOR") != "" {
|
||||||
*noColor = true
|
noColor = true
|
||||||
} else {
|
|
||||||
*noColor = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
command := flag.Arg(0)
|
command := flag.Arg(0)
|
||||||
args := flag.Args()[1:]
|
args := flag.Args()[1:]
|
||||||
|
|
||||||
var (
|
return executor.Execute(command, args, noColor, configDir)
|
||||||
enbasConfig *config.Config
|
|
||||||
enbasPrinter *printer.Printer
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
switch command {
|
|
||||||
case executor.CommandInit, executor.CommandVersion:
|
|
||||||
enbasPrinter = printer.NewPrinter(*noColor, "", 0)
|
|
||||||
default:
|
|
||||||
enbasConfig, err = config.NewConfigFromFile(configDir)
|
|
||||||
if err != nil {
|
|
||||||
enbasPrinter = printer.NewPrinter(*noColor, "", 0)
|
|
||||||
enbasPrinter.PrintFailure("unable to load the configuration: " + err.Error() + ".")
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
enbasPrinter = printer.NewPrinter(*noColor, enbasConfig.Integrations.Pager, enbasConfig.LineWrapMaxWidth)
|
|
||||||
}
|
|
||||||
|
|
||||||
executorMap := map[string]executor.Executor{
|
|
||||||
executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandAccept,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandAccept),
|
|
||||||
),
|
|
||||||
executor.CommandAdd: executor.NewAddExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandAdd,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandAdd),
|
|
||||||
),
|
|
||||||
executor.CommandBlock: executor.NewBlockOrUnblockExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandBlock,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandBlock),
|
|
||||||
),
|
|
||||||
executor.CommandCreate: executor.NewCreateExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandCreate,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandCreate),
|
|
||||||
),
|
|
||||||
executor.CommandDelete: executor.NewDeleteExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandDelete,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandDelete),
|
|
||||||
),
|
|
||||||
executor.CommandEdit: executor.NewEditExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandEdit,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandEdit),
|
|
||||||
),
|
|
||||||
executor.CommandFollow: executor.NewFollowOrUnfollowExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandFollow,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandFollow),
|
|
||||||
),
|
|
||||||
executor.CommandInit: executor.NewInitExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
configDir,
|
|
||||||
executor.CommandInit,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandInit),
|
|
||||||
),
|
|
||||||
executor.CommandLogin: executor.NewLoginExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandLogin,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandLogin),
|
|
||||||
),
|
|
||||||
executor.CommandMute: executor.NewMuteOrUnmuteExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandMute,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandMute),
|
|
||||||
),
|
|
||||||
executor.CommandReject: executor.NewAcceptOrRejectExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandReject,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandReject),
|
|
||||||
),
|
|
||||||
executor.CommandRemove: executor.NewRemoveExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandRemove,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandRemove),
|
|
||||||
),
|
|
||||||
executor.CommandSwitch: executor.NewSwitchExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandSwitch,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandSwitch),
|
|
||||||
),
|
|
||||||
executor.CommandUnfollow: executor.NewFollowOrUnfollowExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandUnfollow,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandUnfollow),
|
|
||||||
),
|
|
||||||
executor.CommandUnmute: executor.NewMuteOrUnmuteExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandUnmute,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandUnmute),
|
|
||||||
),
|
|
||||||
executor.CommandUnblock: executor.NewBlockOrUnblockExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandUnblock,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandUnblock),
|
|
||||||
),
|
|
||||||
executor.CommandShow: executor.NewShowExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandShow,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandShow),
|
|
||||||
),
|
|
||||||
executor.CommandVersion: executor.NewVersionExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
executor.CommandVersion,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandVersion),
|
|
||||||
binaryVersion,
|
|
||||||
buildTime,
|
|
||||||
goVersion,
|
|
||||||
gitCommit,
|
|
||||||
),
|
|
||||||
executor.CommandWhoami: executor.NewWhoAmIExecutor(
|
|
||||||
enbasPrinter,
|
|
||||||
enbasConfig,
|
|
||||||
executor.CommandWhoami,
|
|
||||||
executor.CommandSummaryLookup(executor.CommandWhoami),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
exe, ok := executorMap[command]
|
|
||||||
if !ok {
|
|
||||||
err = executor.UnknownCommandError{Command: command}
|
|
||||||
|
|
||||||
enbasPrinter.PrintFailure(err.Error() + ".")
|
|
||||||
flag.Usage()
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = executor.Execute(exe, args); err != nil {
|
|
||||||
enbasPrinter.PrintFailure("(" + command + ") " + err.Error() + ".")
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
- [View a list of statuses that you've liked](#view-a-list-of-statuses-that-youve-liked)
|
- [View a list of statuses that you've liked](#view-a-list-of-statuses-that-youve-liked)
|
||||||
- [Mute a status](#mute-a-status)
|
- [Mute a status](#mute-a-status)
|
||||||
- [Unmute a status](#unmute-a-status)
|
- [Unmute a status](#unmute-a-status)
|
||||||
|
- [Vote in a poll within a status](#vote-in-a-poll-within-a-status)
|
||||||
- [Polls](#polls)
|
- [Polls](#polls)
|
||||||
- [Create a poll](#create-a-poll)
|
- [Create a poll](#create-a-poll)
|
||||||
- [View a poll](#view-a-poll)
|
- [View a poll](#view-a-poll)
|
||||||
|
@ -528,6 +529,21 @@ _Not yet supported_
|
||||||
|
|
||||||
_Not yet supported_
|
_Not yet supported_
|
||||||
|
|
||||||
|
### Vote in a poll within a status
|
||||||
|
|
||||||
|
Adds your vote(s) to a poll within a status.
|
||||||
|
|
||||||
|
```
|
||||||
|
enbas add --type vote --to status --status-id 01J55XVV2MM6MKQ7QHFBAVAE8R --vote 3
|
||||||
|
```
|
||||||
|
|
||||||
|
| flag | type | required | description | default |
|
||||||
|
|------|------|----------|-------------|---------|
|
||||||
|
| `type` | string | true | The resource you want to add.<br>Here this should be `vote`. | |
|
||||||
|
| `to` | string | true | The resource you want to add the vote to.<br>Here this should be `status`. | |
|
||||||
|
| `status-id` | string | true | The ID of the poll you want to add the votes to. | |
|
||||||
|
| `vote` | int | true | The ID of the option that you want to vote for.<br>You can use this flag multiple times to vote for more than one option if the poll allows multiple choices. | |
|
||||||
|
|
||||||
## Polls
|
## Polls
|
||||||
|
|
||||||
### Create a poll
|
### Create a poll
|
||||||
|
@ -536,31 +552,11 @@ See [Create a status](#create-a-status).
|
||||||
|
|
||||||
### View a poll
|
### View a poll
|
||||||
|
|
||||||
Prints the poll information to the screen.
|
You can view a poll within a [status](#view-a-status) or within a [timeline](#view-a-timeline).
|
||||||
|
|
||||||
```
|
|
||||||
enbas show --type poll --poll-id 01J0CEEZBZ6E6AYQSJPHCQYBDA
|
|
||||||
```
|
|
||||||
|
|
||||||
| flag | type | required | description | default |
|
|
||||||
|------|------|----------|-------------|---------|
|
|
||||||
| `type` | string | true | The resource you want to view.<br>Here this should be `poll`. | |
|
|
||||||
| `poll-id` | string | true | The ID of the poll that you want to view. | |
|
|
||||||
|
|
||||||
### Vote in a poll
|
### Vote in a poll
|
||||||
|
|
||||||
Add your vote(s) to a poll.
|
See [Vote in a poll within a status](#vote-in-a-poll-within-a-status)
|
||||||
|
|
||||||
```
|
|
||||||
enbas add --type vote --to poll --poll-id 01J1TVJ705VV3VP02FVVBSMX7E --vote 3
|
|
||||||
```
|
|
||||||
|
|
||||||
| flag | type | required | description | default |
|
|
||||||
|------|------|----------|-------------|---------|
|
|
||||||
| `type` | string | true | The resource you want to add.<br>Here this should be `vote`. | |
|
|
||||||
| `to` | string | true | The resource you want to add the vote to.<br>Here this should be `poll`. | |
|
|
||||||
| `poll-id` | string | true | The ID of the poll you want to add the votes to. | |
|
|
||||||
| `vote` | int | true | The ID of the option that you want to vote for.<br>You can use this flag multiple times to vote for more than one option if the poll allows multiple choices. | |
|
|
||||||
|
|
||||||
## Lists
|
## Lists
|
||||||
|
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -1,5 +1,5 @@
|
||||||
module codeflow.dananglin.me.uk/apollo/enbas
|
module codeflow.dananglin.me.uk/apollo/enbas
|
||||||
|
|
||||||
go 1.22.5
|
go 1.22.6
|
||||||
|
|
||||||
require golang.org/x/net v0.26.0
|
require golang.org/x/net v0.26.0
|
||||||
|
|
|
@ -1,8 +1,14 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
@ -31,12 +37,123 @@ func (g *Client) GetMediaAttachment(mediaAttachmentID string) (model.Attachment,
|
||||||
return attachment, nil
|
return attachment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//type CreateMediaAttachmentForm struct {
|
func (g *Client) CreateMediaAttachment(path, description, focus string) (model.Attachment, error) {
|
||||||
// Description string
|
file, err := os.Open(path)
|
||||||
// Focus string
|
if err != nil {
|
||||||
// Filepath string
|
return model.Attachment{}, fmt.Errorf("unable to open the file: %w", err)
|
||||||
//}
|
}
|
||||||
//
|
defer file.Close()
|
||||||
//func (g *Client) CreateMediaAttachment(form CreateMediaAttachmentForm) (model.Attachment, error) {
|
|
||||||
// return model.Attachment{}, nil
|
// create the request body using a writer from the multipart package
|
||||||
//}
|
requestBody := bytes.Buffer{}
|
||||||
|
requestBodyWriter := multipart.NewWriter(&requestBody)
|
||||||
|
|
||||||
|
filename := filepath.Base(path)
|
||||||
|
|
||||||
|
part, err := requestBodyWriter.CreateFormFile("file", filename)
|
||||||
|
if err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf("unable to create the new part: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.Copy(part, file); err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf("unable to copy the file contents to the form: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the description
|
||||||
|
if description != "" {
|
||||||
|
descriptionFormFieldWriter, err := requestBodyWriter.CreateFormField("description")
|
||||||
|
if err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf(
|
||||||
|
"unable to create the writer for the 'description' form field: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.WriteString(descriptionFormFieldWriter, description); err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf(
|
||||||
|
"unable to write the description to the form: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the focus values
|
||||||
|
if focus != "" {
|
||||||
|
focusFormFieldWriter, err := requestBodyWriter.CreateFormField("focus")
|
||||||
|
if err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf(
|
||||||
|
"unable to create the writer for the 'focus' form field: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := io.WriteString(focusFormFieldWriter, focus); err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf(
|
||||||
|
"unable to write the focus values to the form: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := requestBodyWriter.Close(); err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf("unable to close the writer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := g.Authentication.Instance + baseMediaPath
|
||||||
|
|
||||||
|
var attachment model.Attachment
|
||||||
|
|
||||||
|
params := requestParameters{
|
||||||
|
httpMethod: http.MethodPost,
|
||||||
|
url: url,
|
||||||
|
requestBody: &requestBody,
|
||||||
|
contentType: requestBodyWriter.FormDataContentType(),
|
||||||
|
output: &attachment,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := g.sendRequest(params); err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf(
|
||||||
|
"received an error after sending the request to create the media attachment: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return attachment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Client) UpdateMediaAttachment(mediaAttachmentID, description, focus string) (model.Attachment, error) {
|
||||||
|
form := struct {
|
||||||
|
Description string `json:"description"`
|
||||||
|
Focus string `json:"focus"`
|
||||||
|
}{
|
||||||
|
Description: description,
|
||||||
|
Focus: focus,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(form)
|
||||||
|
if err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf("unable to marshal the form: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBody := bytes.NewBuffer(data)
|
||||||
|
url := g.Authentication.Instance + baseMediaPath + "/" + mediaAttachmentID
|
||||||
|
|
||||||
|
var updatedMediaAttachment model.Attachment
|
||||||
|
|
||||||
|
params := requestParameters{
|
||||||
|
httpMethod: http.MethodPut,
|
||||||
|
url: url,
|
||||||
|
requestBody: requestBody,
|
||||||
|
contentType: applicationJSON,
|
||||||
|
output: &updatedMediaAttachment,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := g.sendRequest(params); err != nil {
|
||||||
|
return model.Attachment{}, fmt.Errorf(
|
||||||
|
"received an error after sending the request to update the media attachment: %w",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedMediaAttachment, nil
|
||||||
|
}
|
||||||
|
|
|
@ -5,36 +5,34 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
pollPath string = "/api/v1/polls"
|
pollPath string = "/api/v1/polls"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (g *Client) GetPoll(pollID string) (model.Poll, error) {
|
// func (g *Client) GetPoll(pollID string) (model.Poll, error) {
|
||||||
url := g.Authentication.Instance + pollPath + "/" + pollID
|
// url := g.Authentication.Instance + pollPath + "/" + pollID
|
||||||
|
//
|
||||||
var poll model.Poll
|
// var poll model.Poll
|
||||||
|
//
|
||||||
params := requestParameters{
|
// params := requestParameters{
|
||||||
httpMethod: http.MethodGet,
|
// httpMethod: http.MethodGet,
|
||||||
url: url,
|
// url: url,
|
||||||
requestBody: nil,
|
// requestBody: nil,
|
||||||
contentType: "",
|
// contentType: "",
|
||||||
output: &poll,
|
// output: &poll,
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if err := g.sendRequest(params); err != nil {
|
// if err := g.sendRequest(params); err != nil {
|
||||||
return model.Poll{}, fmt.Errorf(
|
// return model.Poll{}, fmt.Errorf(
|
||||||
"received an error after sending the request to get the poll: %w",
|
// "received an error after sending the request to get the poll: %w",
|
||||||
err,
|
// err,
|
||||||
)
|
// )
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return poll, nil
|
// return poll, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func (g *Client) VoteInPoll(pollID string, choices []int) error {
|
func (g *Client) VoteInPoll(pollID string, choices []int) error {
|
||||||
form := struct {
|
form := struct {
|
||||||
|
|
|
@ -50,6 +50,7 @@ type CreateStatusForm struct {
|
||||||
Poll *CreateStatusPollForm `json:"poll,omitempty"`
|
Poll *CreateStatusPollForm `json:"poll,omitempty"`
|
||||||
ContentType model.StatusContentType `json:"content_type"`
|
ContentType model.StatusContentType `json:"content_type"`
|
||||||
Visibility model.StatusVisibility `json:"visibility"`
|
Visibility model.StatusVisibility `json:"visibility"`
|
||||||
|
AttachmentIDs []string `json:"media_ids,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateStatusPollForm struct {
|
type CreateStatusPollForm struct {
|
||||||
|
|
40
internal/executor/accept.go
Normal file
40
internal/executor/accept.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *AcceptExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceFollowRequest: a.acceptFollowRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[a.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: a.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(a.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AcceptExecutor) acceptFollowRequest(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, a.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.AcceptFollowRequest(accountID); err != nil {
|
||||||
|
return fmt.Errorf("unable to accept the follow request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.printer.PrintSuccess("Successfully accepted the follow request.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,91 +0,0 @@
|
||||||
package executor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type AcceptOrRejectExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
accountName string
|
|
||||||
command string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAcceptOrRejectExecutor(enbasPrinter *printer.Printer, config *config.Config, name, summary string) *AcceptOrRejectExecutor {
|
|
||||||
acceptExe := AcceptOrRejectExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: enbasPrinter,
|
|
||||||
config: config,
|
|
||||||
command: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
acceptExe.StringVar(&acceptExe.resourceType, flagType, "", "Specify the type of resource to accept or reject")
|
|
||||||
acceptExe.StringVar(&acceptExe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
|
||||||
|
|
||||||
acceptExe.Usage = commandUsageFunc(name, summary, acceptExe.FlagSet)
|
|
||||||
|
|
||||||
return &acceptExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AcceptOrRejectExecutor) Execute() error {
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
|
||||||
resourceFollowRequest: a.acceptOrRejectFollowRequest,
|
|
||||||
}
|
|
||||||
|
|
||||||
doFunc, ok := funcMap[a.resourceType]
|
|
||||||
if !ok {
|
|
||||||
return UnsupportedTypeError{resourceType: a.resourceType}
|
|
||||||
}
|
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromFile(a.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return doFunc(gtsClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AcceptOrRejectExecutor) acceptOrRejectFollowRequest(gtsClient *client.Client) error {
|
|
||||||
accountID, err := getAccountID(gtsClient, false, a.accountName, a.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch a.command {
|
|
||||||
case CommandAccept:
|
|
||||||
return a.acceptFollowRequest(gtsClient, accountID)
|
|
||||||
case CommandReject:
|
|
||||||
return a.rejectFollowRequest(gtsClient, accountID)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AcceptOrRejectExecutor) acceptFollowRequest(gtsClient *client.Client, accountID string) error {
|
|
||||||
if err := gtsClient.AcceptFollowRequest(accountID); err != nil {
|
|
||||||
return fmt.Errorf("unable to accept the follow request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.printer.PrintSuccess("Successfully accepted the follow request.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AcceptOrRejectExecutor) rejectFollowRequest(gtsClient *client.Client, accountID string) error {
|
|
||||||
if err := gtsClient.RejectFollowRequest(accountID); err != nil {
|
|
||||||
return fmt.Errorf("unable to reject the follow request: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.printer.PrintSuccess("Successfully rejected the follow request.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -4,61 +4,53 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAccountID(gtsClient *client.Client, myAccount bool, accountName, path string) (string, error) {
|
func getAccountID(
|
||||||
|
gtsClient *client.Client,
|
||||||
|
myAccount bool,
|
||||||
|
accountNames internalFlag.StringSliceValue,
|
||||||
|
) (string, error) {
|
||||||
|
account, err := getAccount(gtsClient, myAccount, accountNames)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to get the account information: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return account.ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAccount(
|
||||||
|
gtsClient *client.Client,
|
||||||
|
myAccount bool,
|
||||||
|
accountNames internalFlag.StringSliceValue,
|
||||||
|
) (model.Account, error) {
|
||||||
var (
|
var (
|
||||||
accountID string
|
account model.Account
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case myAccount:
|
case myAccount:
|
||||||
accountID, err = getMyAccountID(gtsClient, path)
|
account, err = getMyAccount(gtsClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to get your account ID: %w", err)
|
return account, fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
}
|
}
|
||||||
case accountName != "":
|
case !accountNames.Empty():
|
||||||
accountID, err = getTheirAccountID(gtsClient, accountName)
|
account, err = getOtherAccount(gtsClient, accountNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("unable to get their account ID: %w", err)
|
return account, fmt.Errorf("unable to get the account ID: %w", err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return "", NoAccountSpecifiedError{}
|
return account, NoAccountSpecifiedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return accountID, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTheirAccountID(gtsClient *client.Client, accountURI string) (string, error) {
|
func getMyAccount(gtsClient *client.Client) (model.Account, error) {
|
||||||
account, err := getAccount(gtsClient, accountURI)
|
account, err := gtsClient.VerifyCredentials()
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("unable to retrieve your account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return account.ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMyAccountID(gtsClient *client.Client, path string) (string, error) {
|
|
||||||
account, err := getMyAccount(gtsClient, path)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("received an error while getting your account details: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return account.ID, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getMyAccount(gtsClient *client.Client, path string) (model.Account, error) {
|
|
||||||
authConfig, err := config.NewCredentialsConfigFromFile(path)
|
|
||||||
if err != nil {
|
|
||||||
return model.Account{}, fmt.Errorf("unable to retrieve the authentication configuration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
accountURI := authConfig.CurrentAccount
|
|
||||||
|
|
||||||
account, err := getAccount(gtsClient, accountURI)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Account{}, fmt.Errorf("unable to retrieve your account: %w", err)
|
return model.Account{}, fmt.Errorf("unable to retrieve your account: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -66,11 +58,35 @@ func getMyAccount(gtsClient *client.Client, path string) (model.Account, error)
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAccount(gtsClient *client.Client, accountURI string) (model.Account, error) {
|
func getOtherAccount(gtsClient *client.Client, accountNames internalFlag.StringSliceValue) (model.Account, error) {
|
||||||
account, err := gtsClient.GetAccount(accountURI)
|
expectedNumAccountNames := 1
|
||||||
|
if !accountNames.ExpectedLength(expectedNumAccountNames) {
|
||||||
|
return model.Account{}, fmt.Errorf(
|
||||||
|
"received an unexpected number of account names: want %d",
|
||||||
|
expectedNumAccountNames,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
account, err := gtsClient.GetAccount(accountNames[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return model.Account{}, fmt.Errorf("unable to retrieve the account details: %w", err)
|
return model.Account{}, fmt.Errorf("unable to retrieve the account details: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return account, nil
|
return account, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getOtherAccounts(gtsClient *client.Client, accountNames internalFlag.StringSliceValue) ([]model.Account, error) {
|
||||||
|
numAccountNames := len(accountNames)
|
||||||
|
accounts := make([]model.Account, numAccountNames)
|
||||||
|
|
||||||
|
for ind := 0; ind < numAccountNames; ind++ {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
accounts[ind], err = gtsClient.GetAccount(accountNames[ind])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("unable to retrieve the account information for %s: %w", accountNames[ind], err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,55 +1,11 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AddExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
toResourceType string
|
|
||||||
listID string
|
|
||||||
statusID string
|
|
||||||
pollID string
|
|
||||||
choices MultiIntFlagValue
|
|
||||||
accountNames MultiStringFlagValue
|
|
||||||
content string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAddExecutor(printer *printer.Printer, config *config.Config, name, summary string) *AddExecutor {
|
|
||||||
emptyArr := make([]string, 0, 3)
|
|
||||||
|
|
||||||
addExe := AddExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
accountNames: MultiStringFlagValue(emptyArr),
|
|
||||||
}
|
|
||||||
|
|
||||||
addExe.StringVar(&addExe.resourceType, flagType, "", "Specify the resource type to add (e.g. account, note)")
|
|
||||||
addExe.StringVar(&addExe.toResourceType, flagTo, "", "Specify the target resource type to add to (e.g. list, account, etc)")
|
|
||||||
addExe.StringVar(&addExe.listID, flagListID, "", "The ID of the list")
|
|
||||||
addExe.StringVar(&addExe.statusID, flagStatusID, "", "The ID of the status")
|
|
||||||
addExe.StringVar(&addExe.content, flagContent, "", "The content of the resource")
|
|
||||||
addExe.StringVar(&addExe.pollID, flagPollID, "", "The ID of the poll")
|
|
||||||
addExe.Var(&addExe.accountNames, flagAccountName, "The name of the account")
|
|
||||||
addExe.Var(&addExe.choices, flagVote, "Add a vote to an option in a poll")
|
|
||||||
|
|
||||||
addExe.Usage = commandUsageFunc(name, summary, addExe.FlagSet)
|
|
||||||
|
|
||||||
return &addExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AddExecutor) Execute() error {
|
func (a *AddExecutor) Execute() error {
|
||||||
if a.toResourceType == "" {
|
if a.toResourceType == "" {
|
||||||
return FlagNotSetError{flagText: flagTo}
|
return FlagNotSetError{flagText: flagTo}
|
||||||
|
@ -60,7 +16,6 @@ func (a *AddExecutor) Execute() error {
|
||||||
resourceAccount: a.addToAccount,
|
resourceAccount: a.addToAccount,
|
||||||
resourceBookmarks: a.addToBookmarks,
|
resourceBookmarks: a.addToBookmarks,
|
||||||
resourceStatus: a.addToStatus,
|
resourceStatus: a.addToStatus,
|
||||||
resourcePoll: a.addToPoll,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[a.toResourceType]
|
doFunc, ok := funcMap[a.toResourceType]
|
||||||
|
@ -97,28 +52,28 @@ func (a *AddExecutor) addAccountsToList(gtsClient *client.Client) error {
|
||||||
return FlagNotSetError{flagText: flagListID}
|
return FlagNotSetError{flagText: flagListID}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(a.accountNames) == 0 {
|
if a.accountNames.Empty() {
|
||||||
return NoAccountSpecifiedError{}
|
return NoAccountSpecifiedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountIDs := make([]string, len(a.accountNames))
|
accounts, err := getOtherAccounts(gtsClient, a.accountNames)
|
||||||
|
|
||||||
for ind := range a.accountNames {
|
|
||||||
accountID, err := getTheirAccountID(gtsClient, a.accountNames[ind])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to get the account ID for %s: %w", a.accountNames[ind], err)
|
return fmt.Errorf("unable to get the accounts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, err := gtsClient.GetAccountRelationship(accountID)
|
accountIDs := make([]string, len(accounts))
|
||||||
|
|
||||||
|
for ind := range accounts {
|
||||||
|
relationship, err := gtsClient.GetAccountRelationship(accounts[ind].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to get your relationship to %s: %w", a.accountNames[ind], err)
|
return fmt.Errorf("unable to get your relationship to %s: %w", accounts[ind].Acct, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !relationship.Following {
|
if !relationship.Following {
|
||||||
return NotFollowingError{Account: a.accountNames[ind]}
|
return NotFollowingError{Account: accounts[ind].Acct}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountIDs[ind] = accountID
|
accountIDs[ind] = accounts[ind].ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gtsClient.AddAccountsToList(a.listID, accountIDs); err != nil {
|
if err := gtsClient.AddAccountsToList(a.listID, accountIDs); err != nil {
|
||||||
|
@ -147,11 +102,7 @@ func (a *AddExecutor) addToAccount(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error {
|
func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error {
|
||||||
if len(a.accountNames) != 1 {
|
accountID, err := getAccountID(gtsClient, false, a.accountNames)
|
||||||
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(a.accountNames))
|
|
||||||
}
|
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, a.accountNames[0], a.config.CredentialsFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -211,6 +162,7 @@ func (a *AddExecutor) addToStatus(gtsClient *client.Client) error {
|
||||||
resourceStar: a.addStarToStatus,
|
resourceStar: a.addStarToStatus,
|
||||||
resourceLike: a.addStarToStatus,
|
resourceLike: a.addStarToStatus,
|
||||||
resourceBoost: a.addBoostToStatus,
|
resourceBoost: a.addBoostToStatus,
|
||||||
|
resourceVote: a.addVoteToStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[a.resourceType]
|
doFunc, ok := funcMap[a.resourceType]
|
||||||
|
@ -244,45 +196,40 @@ func (a *AddExecutor) addBoostToStatus(gtsClient *client.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AddExecutor) addToPoll(gtsClient *client.Client) error {
|
func (a *AddExecutor) addVoteToStatus(gtsClient *client.Client) error {
|
||||||
if a.pollID == "" {
|
if a.votes.Empty() {
|
||||||
return FlagNotSetError{flagText: flagPollID}
|
return NoVotesError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
status, err := gtsClient.GetStatus(a.statusID)
|
||||||
resourceVote: a.addVoteToPoll,
|
|
||||||
}
|
|
||||||
|
|
||||||
doFunc, ok := funcMap[a.resourceType]
|
|
||||||
if !ok {
|
|
||||||
return UnsupportedAddOperationError{
|
|
||||||
ResourceType: a.resourceType,
|
|
||||||
AddToResourceType: a.toResourceType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return doFunc(gtsClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *AddExecutor) addVoteToPoll(gtsClient *client.Client) error {
|
|
||||||
if len(a.choices) == 0 {
|
|
||||||
return errors.New("please use --" + flagVote + " to make a choice in this poll")
|
|
||||||
}
|
|
||||||
|
|
||||||
poll, err := gtsClient.GetPoll(a.pollID)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
return fmt.Errorf("unable to get the status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if poll.Expired {
|
if status.Poll == nil {
|
||||||
|
return NoPollInStatusError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.Poll.Expired {
|
||||||
return PollClosedError{}
|
return PollClosedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !poll.Multiple && len(a.choices) > 1 {
|
if !status.Poll.Multiple && !a.votes.ExpectedLength(1) {
|
||||||
return MultipleChoiceError{}
|
return MultipleChoiceError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gtsClient.VoteInPoll(a.pollID, []int(a.choices)); err != nil {
|
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.Account.ID == myAccountID {
|
||||||
|
return PollOwnerVoteError{}
|
||||||
|
}
|
||||||
|
|
||||||
|
pollID := status.Poll.ID
|
||||||
|
|
||||||
|
if err := gtsClient.VoteInPoll(pollID, []int(a.votes)); err != nil {
|
||||||
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
|
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
40
internal/executor/block.go
Normal file
40
internal/executor/block.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BlockExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceAccount: b.blockAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[b.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: b.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(b.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BlockExecutor) blockAccount(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, b.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.BlockAccount(accountID); err != nil {
|
||||||
|
return fmt.Errorf("unable to block the account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.printer.PrintSuccess("Successfully blocked the account.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,95 +0,0 @@
|
||||||
package executor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BlockOrUnblockExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
accountName string
|
|
||||||
command string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBlockOrUnblockExecutor(printer *printer.Printer, config *config.Config, name, summary string) *BlockOrUnblockExecutor {
|
|
||||||
blockExe := BlockOrUnblockExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
command: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
blockExe.StringVar(&blockExe.resourceType, flagType, "", "Specify the type of resource to block or unblock")
|
|
||||||
blockExe.StringVar(&blockExe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
|
||||||
|
|
||||||
blockExe.Usage = commandUsageFunc(name, summary, blockExe.FlagSet)
|
|
||||||
|
|
||||||
return &blockExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockOrUnblockExecutor) Execute() error {
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
|
||||||
resourceAccount: b.blockOrUnblockAccount,
|
|
||||||
}
|
|
||||||
|
|
||||||
doFunc, ok := funcMap[b.resourceType]
|
|
||||||
if !ok {
|
|
||||||
return UnsupportedTypeError{resourceType: b.resourceType}
|
|
||||||
}
|
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromFile(b.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return doFunc(gtsClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockOrUnblockExecutor) blockOrUnblockAccount(gtsClient *client.Client) error {
|
|
||||||
if b.accountName == "" {
|
|
||||||
return FlagNotSetError{flagText: flagAccountName}
|
|
||||||
}
|
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, b.accountName, b.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch b.command {
|
|
||||||
case CommandBlock:
|
|
||||||
return b.blockAccount(gtsClient, accountID)
|
|
||||||
case CommandUnblock:
|
|
||||||
return b.unblockAccount(gtsClient, accountID)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockOrUnblockExecutor) blockAccount(gtsClient *client.Client, accountID string) error {
|
|
||||||
if err := gtsClient.BlockAccount(accountID); err != nil {
|
|
||||||
return fmt.Errorf("unable to block the account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.printer.PrintSuccess("Successfully blocked the account.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BlockOrUnblockExecutor) unblockAccount(gtsClient *client.Client, accountID string) error {
|
|
||||||
if err := gtsClient.UnblockAccount(accountID); err != nil {
|
|
||||||
return fmt.Errorf("unable to unblock the account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.printer.PrintSuccess("Successfully unblocked the account.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
3
internal/executor/codegen.go
Normal file
3
internal/executor/codegen.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
//go:generate go run ../../cmd/enbas-codegen --package executor --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json
|
|
@ -1,78 +0,0 @@
|
||||||
package executor
|
|
||||||
|
|
||||||
const (
|
|
||||||
CommandAccept string = "accept"
|
|
||||||
CommandAdd string = "add"
|
|
||||||
CommandBlock string = "block"
|
|
||||||
CommandCreate string = "create"
|
|
||||||
CommandDelete string = "delete"
|
|
||||||
CommandEdit string = "edit"
|
|
||||||
CommandFollow string = "follow"
|
|
||||||
CommandInit string = "init"
|
|
||||||
CommandLogin string = "login"
|
|
||||||
CommandMute string = "mute"
|
|
||||||
CommandReject string = "reject"
|
|
||||||
CommandRemove string = "remove"
|
|
||||||
CommandShow string = "show"
|
|
||||||
CommandSwitch string = "switch"
|
|
||||||
CommandUnblock string = "unblock"
|
|
||||||
CommandUnfollow string = "unfollow"
|
|
||||||
CommandUnmute string = "unmute"
|
|
||||||
CommandVersion string = "version"
|
|
||||||
CommandWhoami string = "whoami"
|
|
||||||
|
|
||||||
commandAcceptSummary string = "Accept a request (e.g. a follow request)"
|
|
||||||
commandAddSummary string = "Add a resource to another resource"
|
|
||||||
commandBlockSummary string = "Block a resource (e.g. an account)"
|
|
||||||
commandCreateSummary string = "Create a specific resource"
|
|
||||||
commandDeleteSummary string = "Delete a specific resource"
|
|
||||||
commandEditSummary string = "Edit a specific resource"
|
|
||||||
commandFollowSummary string = "Follow a resource (e.g. an account)"
|
|
||||||
commandInitSummary string = "Create a new configuration file in the specified configuration directory"
|
|
||||||
commandLoginSummary string = "Login to an account on GoToSocial"
|
|
||||||
commandMuteSummary string = "Mute a resource (e.g. an account)"
|
|
||||||
commandRejectSummary string = "Reject a request (e.g. a follow request)"
|
|
||||||
commandRemoveSummary string = "Remove a resource from another resource"
|
|
||||||
commandShowSummary string = "Print details about a specified resource"
|
|
||||||
commandSwitchSummary string = "Perform a switch operation (e.g. switch logged in accounts)"
|
|
||||||
commandUnblockSummary string = "Unblock a resource (e.g. an account)"
|
|
||||||
commandUnfollowSummary string = "Unfollow a resource (e.g. an account)"
|
|
||||||
commandUnmuteSummary string = "Unmute a resource (e.g. an account)"
|
|
||||||
commandVersionSummary string = "Print the application's version and build information"
|
|
||||||
commandWhoamiSummary string = "Print the account that you are currently logged in to"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CommandSummaryMap() map[string]string {
|
|
||||||
return map[string]string{
|
|
||||||
CommandAccept: commandAcceptSummary,
|
|
||||||
CommandAdd: commandAddSummary,
|
|
||||||
CommandBlock: commandBlockSummary,
|
|
||||||
CommandCreate: commandCreateSummary,
|
|
||||||
CommandDelete: commandDeleteSummary,
|
|
||||||
CommandEdit: commandEditSummary,
|
|
||||||
CommandFollow: commandFollowSummary,
|
|
||||||
CommandInit: commandInitSummary,
|
|
||||||
CommandLogin: commandLoginSummary,
|
|
||||||
CommandMute: commandMuteSummary,
|
|
||||||
CommandReject: commandRejectSummary,
|
|
||||||
CommandRemove: commandRemoveSummary,
|
|
||||||
CommandShow: commandShowSummary,
|
|
||||||
CommandSwitch: commandSwitchSummary,
|
|
||||||
CommandUnblock: commandUnblockSummary,
|
|
||||||
CommandUnfollow: commandUnfollowSummary,
|
|
||||||
CommandUnmute: commandUnmuteSummary,
|
|
||||||
CommandVersion: commandVersionSummary,
|
|
||||||
CommandWhoami: commandWhoamiSummary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func CommandSummaryLookup(command string) string {
|
|
||||||
commandMap := CommandSummaryMap()
|
|
||||||
|
|
||||||
summary, ok := commandMap[command]
|
|
||||||
if !ok {
|
|
||||||
return "This command does not have a summary"
|
|
||||||
}
|
|
||||||
|
|
||||||
return summary
|
|
||||||
}
|
|
|
@ -1,94 +1,13 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
addPoll bool
|
|
||||||
boostable bool
|
|
||||||
federated bool
|
|
||||||
likeable bool
|
|
||||||
pollAllowsMultipleChoices bool
|
|
||||||
pollHidesVoteCounts bool
|
|
||||||
replyable bool
|
|
||||||
sensitive *bool
|
|
||||||
content string
|
|
||||||
contentType string
|
|
||||||
fromFile string
|
|
||||||
inReplyTo string
|
|
||||||
language string
|
|
||||||
resourceType string
|
|
||||||
listTitle string
|
|
||||||
listRepliesPolicy string
|
|
||||||
spoilerText string
|
|
||||||
visibility string
|
|
||||||
pollExpiresIn TimeDurationFlagValue
|
|
||||||
pollOptions MultiStringFlagValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCreateExecutor(printer *printer.Printer, config *config.Config, name, summary string) *CreateExecutor {
|
|
||||||
createExe := CreateExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
createExe.StringVar(&createExe.resourceType, flagType, "", "Specify the type of resource to create")
|
|
||||||
|
|
||||||
// Flags for statuses
|
|
||||||
createExe.BoolVar(&createExe.boostable, flagEnableReposts, true, "Specify if the status can be reposted/boosted by others")
|
|
||||||
createExe.StringVar(&createExe.content, flagContent, "", "The content of the status to create")
|
|
||||||
createExe.StringVar(&createExe.contentType, flagContentType, "plain", "The type that the contents should be parsed from (valid values are plain and markdown)")
|
|
||||||
createExe.BoolVar(&createExe.federated, flagEnableFederation, true, "Specify if the status can be federated beyond the local timelines")
|
|
||||||
createExe.StringVar(&createExe.fromFile, flagFromFile, "", "The file path where to read the contents from")
|
|
||||||
createExe.StringVar(&createExe.inReplyTo, flagInReplyTo, "", "The ID of the status that you want to reply to")
|
|
||||||
createExe.StringVar(&createExe.language, flagLanguage, "", "The ISO 639 language code for this status")
|
|
||||||
createExe.BoolVar(&createExe.likeable, flagEnableLikes, true, "Specify if the status can be liked/favourited")
|
|
||||||
createExe.BoolVar(&createExe.replyable, flagEnableReplies, true, "Specify if the status can be replied to")
|
|
||||||
createExe.StringVar(&createExe.spoilerText, flagSpoilerText, "", "The text to display as the status' warning or subject")
|
|
||||||
createExe.StringVar(&createExe.visibility, flagVisibility, "", "The visibility of the posted status")
|
|
||||||
createExe.BoolFunc(flagSensitive, "Specify if the status should be marked as sensitive", func(value string) error {
|
|
||||||
boolVal, err := strconv.ParseBool(value)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse %q as a boolean value: %w", value, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
createExe.sensitive = new(bool)
|
|
||||||
*createExe.sensitive = boolVal
|
|
||||||
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// Flags specifically for polls
|
|
||||||
createExe.BoolVar(&createExe.addPoll, flagAddPoll, false, "Add a poll to the status")
|
|
||||||
createExe.BoolVar(&createExe.pollAllowsMultipleChoices, flagPollAllowsMultipleChoices, false, "The poll allows viewers to make multiple choices in the poll")
|
|
||||||
createExe.BoolVar(&createExe.pollHidesVoteCounts, flagPollHidesVoteCounts, false, "The poll will hide the vote count until it is closed")
|
|
||||||
createExe.Var(&createExe.pollOptions, flagPollOption, "A poll option. Use this multiple times to set multiple options")
|
|
||||||
createExe.Var(&createExe.pollExpiresIn, flagPollExpiresIn, "The duration in which the poll is open for")
|
|
||||||
|
|
||||||
// Flags for lists
|
|
||||||
createExe.StringVar(&createExe.listTitle, flagListTitle, "", "Specify the title of the list")
|
|
||||||
createExe.StringVar(&createExe.listRepliesPolicy, flagListRepliesPolicy, "list", "Specify the policy of the replies for this list (valid values are followed, list and none)")
|
|
||||||
|
|
||||||
createExe.Usage = commandUsageFunc(name, summary, createExe.FlagSet)
|
|
||||||
|
|
||||||
return &createExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *CreateExecutor) Execute() error {
|
func (c *CreateExecutor) Execute() error {
|
||||||
if c.resourceType == "" {
|
if c.resourceType == "" {
|
||||||
return FlagNotSetError{flagText: flagType}
|
return FlagNotSetError{flagText: flagType}
|
||||||
|
@ -102,6 +21,7 @@ func (c *CreateExecutor) Execute() error {
|
||||||
funcMap := map[string]func(*client.Client) error{
|
funcMap := map[string]func(*client.Client) error{
|
||||||
resourceList: c.createList,
|
resourceList: c.createList,
|
||||||
resourceStatus: c.createStatus,
|
resourceStatus: c.createStatus,
|
||||||
|
resourceMediaAttachment: c.createMediaAttachment,
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[c.resourceType]
|
doFunc, ok := funcMap[c.resourceType]
|
||||||
|
@ -156,11 +76,18 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to get the status contents from %q: %w", c.fromFile, err)
|
return fmt.Errorf("unable to get the status contents from %q: %w", c.fromFile, err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
if c.attachmentIDs.Empty() {
|
||||||
|
// TODO: revisit this error type
|
||||||
return EmptyContentError{
|
return EmptyContentError{
|
||||||
ResourceType: resourceStatus,
|
ResourceType: resourceStatus,
|
||||||
Hint: "please use --" + flagContent + " or --" + flagFromFile,
|
Hint: "please use --" + flagContent + " or --" + flagFromFile,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.addPoll && !c.attachmentIDs.Empty() {
|
||||||
|
return fmt.Errorf("attaching media to a poll is not allowed")
|
||||||
|
}
|
||||||
|
|
||||||
preferences, err := gtsClient.GetUserPreferences()
|
preferences, err := gtsClient.GetUserPreferences()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -179,8 +106,8 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
visibility = preferences.PostingDefaultVisibility
|
visibility = preferences.PostingDefaultVisibility
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.sensitive != nil {
|
if c.sensitive.Value != nil {
|
||||||
sensitive = *c.sensitive
|
sensitive = *c.sensitive.Value
|
||||||
} else {
|
} else {
|
||||||
sensitive = preferences.PostingDefaultSensitive
|
sensitive = preferences.PostingDefaultSensitive
|
||||||
}
|
}
|
||||||
|
@ -208,6 +135,11 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
Sensitive: sensitive,
|
Sensitive: sensitive,
|
||||||
Visibility: parsedVisibility,
|
Visibility: parsedVisibility,
|
||||||
Poll: nil,
|
Poll: nil,
|
||||||
|
AttachmentIDs: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.attachmentIDs.Empty() {
|
||||||
|
form.AttachmentIDs = c.attachmentIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.addPoll {
|
if c.addPoll {
|
||||||
|
@ -230,8 +162,22 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to create the status: %w", err)
|
return fmt.Errorf("unable to create the status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.printer.PrintSuccess("Successfully created the following status:")
|
c.printer.PrintSuccess("Successfully created the status with ID: " + status.ID)
|
||||||
c.printer.PrintStatus(status)
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CreateExecutor) createMediaAttachment(gtsClient *client.Client) error {
|
||||||
|
if c.fromFile == "" {
|
||||||
|
return FlagNotSetError{flagText: flagFromFile}
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment, err := gtsClient.CreateMediaAttachment(c.fromFile, c.description, c.focus)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the media attachment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.printer.PrintSuccess("Successfully created the media attachment with ID: " + attachment.ID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,39 +1,11 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DeleteExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
listID string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDeleteExecutor(printer *printer.Printer, config *config.Config, name, summary string) *DeleteExecutor {
|
|
||||||
deleteExe := DeleteExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteExe.StringVar(&deleteExe.resourceType, flagType, "", "Specify the type of resource to delete")
|
|
||||||
deleteExe.StringVar(&deleteExe.listID, flagListID, "", "Specify the ID of the list to delete")
|
|
||||||
|
|
||||||
deleteExe.Usage = commandUsageFunc(name, summary, deleteExe.FlagSet)
|
|
||||||
|
|
||||||
return &deleteExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DeleteExecutor) Execute() error {
|
func (d *DeleteExecutor) Execute() error {
|
||||||
if d.resourceType == "" {
|
if d.resourceType == "" {
|
||||||
return FlagNotSetError{flagText: flagType}
|
return FlagNotSetError{flagText: flagType}
|
||||||
|
|
|
@ -1,44 +1,12 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type EditExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
listID string
|
|
||||||
listTitle string
|
|
||||||
listRepliesPolicy string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEditExecutor(printer *printer.Printer, config *config.Config, name, summary string) *EditExecutor {
|
|
||||||
editExe := EditExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
editExe.StringVar(&editExe.resourceType, flagType, "", "Specify the type of resource to update")
|
|
||||||
editExe.StringVar(&editExe.listID, flagListID, "", "Specify the ID of the list to update")
|
|
||||||
editExe.StringVar(&editExe.listTitle, flagListTitle, "", "Specify the title of the list")
|
|
||||||
editExe.StringVar(&editExe.listRepliesPolicy, flagListRepliesPolicy, "", "Specify the policy of the replies for this list (valid values are followed, list and none)")
|
|
||||||
|
|
||||||
editExe.Usage = commandUsageFunc(name, summary, editExe.FlagSet)
|
|
||||||
|
|
||||||
return &editExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *EditExecutor) Execute() error {
|
func (e *EditExecutor) Execute() error {
|
||||||
if e.resourceType == "" {
|
if e.resourceType == "" {
|
||||||
return FlagNotSetError{flagText: flagType}
|
return FlagNotSetError{flagText: flagType}
|
||||||
|
@ -46,6 +14,7 @@ func (e *EditExecutor) Execute() error {
|
||||||
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
funcMap := map[string]func(*client.Client) error{
|
||||||
resourceList: e.editList,
|
resourceList: e.editList,
|
||||||
|
resourceMediaAttachment: e.editMediaAttachment,
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[e.resourceType]
|
doFunc, ok := funcMap[e.resourceType]
|
||||||
|
@ -89,8 +58,41 @@ func (e *EditExecutor) editList(gtsClient *client.Client) error {
|
||||||
return fmt.Errorf("unable to update the list: %w", err)
|
return fmt.Errorf("unable to update the list: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
e.printer.PrintSuccess("Successfully updated the list.")
|
e.printer.PrintSuccess("Successfully edited the list.")
|
||||||
e.printer.PrintList(updatedList)
|
e.printer.PrintList(updatedList)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *EditExecutor) editMediaAttachment(gtsClient *client.Client) error {
|
||||||
|
expectedNumMediaAttachmentIDs := 1
|
||||||
|
if !e.attachmentIDs.ExpectedLength(expectedNumMediaAttachmentIDs) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"received an unexpected number of media attachment IDs: want %d",
|
||||||
|
expectedNumMediaAttachmentIDs,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
attachment, err := gtsClient.GetMediaAttachment(e.attachmentIDs[0])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get the media attachment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
description := e.description
|
||||||
|
if description == "" {
|
||||||
|
description = attachment.Description
|
||||||
|
}
|
||||||
|
|
||||||
|
focus := e.focus
|
||||||
|
if focus == "" {
|
||||||
|
focus = fmt.Sprintf("%f,%f", attachment.Meta.Focus.X, attachment.Meta.Focus.Y)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = gtsClient.UpdateMediaAttachment(e.attachmentIDs[0], description, focus); err != nil {
|
||||||
|
return fmt.Errorf("unable to update the media attachment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e.printer.PrintSuccess("Successfully edited the media attachment.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -104,6 +104,24 @@ func (e NoPollOptionError) Error() string {
|
||||||
" flag to add options to the poll"
|
" flag to add options to the poll"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NoVotesError struct{}
|
||||||
|
|
||||||
|
func (e NoVotesError) Error() string {
|
||||||
|
return "no votes were made, please add your vote(s) using the --vote flag"
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoPollInStatusError struct{}
|
||||||
|
|
||||||
|
func (e NoPollInStatusError) Error() string {
|
||||||
|
return "this status does not have a poll"
|
||||||
|
}
|
||||||
|
|
||||||
|
type PollOwnerVoteError struct{}
|
||||||
|
|
||||||
|
func (e PollOwnerVoteError) Error() string {
|
||||||
|
return "you cannot vote in your own poll"
|
||||||
|
}
|
||||||
|
|
||||||
type NotFollowingError struct {
|
type NotFollowingError struct {
|
||||||
Account string
|
Account string
|
||||||
}
|
}
|
||||||
|
|
160
internal/executor/execute.go
Normal file
160
internal/executor/execute.go
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
/*
|
||||||
|
This file is generated by the enbas-codegen
|
||||||
|
DO NOT EDIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Execute(
|
||||||
|
command string,
|
||||||
|
args []string,
|
||||||
|
noColor bool,
|
||||||
|
configDir string,
|
||||||
|
) error {
|
||||||
|
var (
|
||||||
|
enbasConfig *config.Config
|
||||||
|
enbasPrinter *printer.Printer
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "init", "version":
|
||||||
|
enbasPrinter = printer.NewPrinter(noColor, "", 0)
|
||||||
|
default:
|
||||||
|
enbasConfig, err = config.NewConfigFromFile(configDir)
|
||||||
|
if err != nil {
|
||||||
|
enbasPrinter = printer.NewPrinter(noColor, "", 0)
|
||||||
|
enbasPrinter.PrintFailure("unable to load the configuration: " + err.Error() + ".")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
enbasPrinter = printer.NewPrinter(
|
||||||
|
noColor,
|
||||||
|
enbasConfig.Integrations.Pager,
|
||||||
|
enbasConfig.LineWrapMaxWidth,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = execute(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
configDir,
|
||||||
|
); err != nil {
|
||||||
|
enbasPrinter.PrintFailure("(" + command + ") " + err.Error() + ".")
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func execute(
|
||||||
|
command string,
|
||||||
|
args []string,
|
||||||
|
enbasPrinter *printer.Printer,
|
||||||
|
enbasConfig *config.Config,
|
||||||
|
configDir string,
|
||||||
|
) error {
|
||||||
|
executorMap := map[string]Executor{
|
||||||
|
"accept": NewAcceptExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"add": NewAddExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"block": NewBlockExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"create": NewCreateExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"delete": NewDeleteExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"edit": NewEditExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"follow": NewFollowExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"init": NewInitExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
configDir,
|
||||||
|
),
|
||||||
|
"login": NewLoginExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"mute": NewMuteExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"reject": NewRejectExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"remove": NewRemoveExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"show": NewShowExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"switch": NewSwitchExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"unblock": NewUnblockExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"unfollow": NewUnfollowExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"unmute": NewUnmuteExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
"version": NewVersionExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
),
|
||||||
|
"whoami": NewWhoamiExecutor(
|
||||||
|
enbasPrinter,
|
||||||
|
enbasConfig,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe, ok := executorMap[command]
|
||||||
|
if !ok {
|
||||||
|
return UnknownCommandError{Command: command}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exe.Parse(args); err != nil {
|
||||||
|
return fmt.Errorf("flag parsing error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exe.Execute(); err != nil {
|
||||||
|
return fmt.Errorf("execution error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,21 +0,0 @@
|
||||||
package executor
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
type Executor interface {
|
|
||||||
Name() string
|
|
||||||
Parse(args []string) error
|
|
||||||
Execute() error
|
|
||||||
}
|
|
||||||
|
|
||||||
func Execute(executor Executor, args []string) error {
|
|
||||||
if err := executor.Parse(args); err != nil {
|
|
||||||
return fmt.Errorf("flag parsing error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := executor.Execute(); err != nil {
|
|
||||||
return fmt.Errorf("execution error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
651
internal/executor/executors.go
Normal file
651
internal/executor/executors.go
Normal file
|
@ -0,0 +1,651 @@
|
||||||
|
/*
|
||||||
|
This file is generated by the enbas-codegen
|
||||||
|
DO NOT EDIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
|
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/usage"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Executor interface {
|
||||||
|
Name() string
|
||||||
|
Parse(args []string) error
|
||||||
|
Execute() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AcceptExecutor is the executor for the accept command.
|
||||||
|
type AcceptExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAcceptExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *AcceptExecutor {
|
||||||
|
exe := AcceptExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("accept", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("accept", "Accepts a request (e.g. a follow request)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddExecutor is the executor for the add command.
|
||||||
|
type AddExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountNames internalFlag.StringSliceValue
|
||||||
|
content string
|
||||||
|
listID string
|
||||||
|
statusID string
|
||||||
|
toResourceType string
|
||||||
|
resourceType string
|
||||||
|
votes internalFlag.IntSliceValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAddExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *AddExecutor {
|
||||||
|
exe := AddExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("add", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountNames: internalFlag.NewStringSliceValue(),
|
||||||
|
votes: internalFlag.NewIntSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("add", "Adds a resource to another resource", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountNames, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.content, "content", "", "The content of the created resource")
|
||||||
|
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||||
|
exe.StringVar(&exe.statusID, "status-id", "", "The ID of the status")
|
||||||
|
exe.StringVar(&exe.toResourceType, "to", "", "The resource type to action the target resource to (e.g. status)")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
exe.Var(&exe.votes, "vote", "Add a vote to an option in a poll")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlockExecutor is the executor for the block command.
|
||||||
|
type BlockExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *BlockExecutor {
|
||||||
|
exe := BlockExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("block", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("block", "Blocks a resource (e.g. an account)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateExecutor is the executor for the create command.
|
||||||
|
type CreateExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
addPoll bool
|
||||||
|
attachmentIDs internalFlag.StringSliceValue
|
||||||
|
content string
|
||||||
|
contentType string
|
||||||
|
description string
|
||||||
|
federated bool
|
||||||
|
likeable bool
|
||||||
|
replyable bool
|
||||||
|
boostable bool
|
||||||
|
focus string
|
||||||
|
fromFile string
|
||||||
|
inReplyTo string
|
||||||
|
language string
|
||||||
|
listRepliesPolicy string
|
||||||
|
listTitle string
|
||||||
|
pollAllowsMultipleChoices bool
|
||||||
|
pollExpiresIn internalFlag.TimeDurationValue
|
||||||
|
pollHidesVoteCounts bool
|
||||||
|
pollOptions internalFlag.StringSliceValue
|
||||||
|
sensitive internalFlag.BoolPtrValue
|
||||||
|
spoilerText string
|
||||||
|
resourceType string
|
||||||
|
visibility string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCreateExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *CreateExecutor {
|
||||||
|
exe := CreateExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("create", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
attachmentIDs: internalFlag.NewStringSliceValue(),
|
||||||
|
pollExpiresIn: internalFlag.NewTimeDurationValue(),
|
||||||
|
pollOptions: internalFlag.NewStringSliceValue(),
|
||||||
|
sensitive: internalFlag.NewBoolPtrValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("create", "Creates a specific resource", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.BoolVar(&exe.addPoll, "add-poll", false, "Set to true to add a poll when creating a status")
|
||||||
|
exe.Var(&exe.attachmentIDs, "attachment-id", "The ID of the media attachment")
|
||||||
|
exe.StringVar(&exe.content, "content", "", "The content of the created resource")
|
||||||
|
exe.StringVar(&exe.contentType, "content-type", "plain", "The type that the contents should be parsed from (valid values are plain and markdown)")
|
||||||
|
exe.StringVar(&exe.description, "description", "", "The description of the media attachment that will be used as the alt-text")
|
||||||
|
exe.BoolVar(&exe.federated, "enable-federation", true, "Set to true to federate the status beyond the local timelines")
|
||||||
|
exe.BoolVar(&exe.likeable, "enable-likes", true, "Set to true to allow the status to be liked (favourited)")
|
||||||
|
exe.BoolVar(&exe.replyable, "enable-replies", true, "Set to true to allow viewers to reply to the status")
|
||||||
|
exe.BoolVar(&exe.boostable, "enable-reposts", true, "Set to true to allow the status to be reposted (boosted) by others")
|
||||||
|
exe.StringVar(&exe.focus, "focus", "", "The focus of the media file")
|
||||||
|
exe.StringVar(&exe.fromFile, "from-file", "", "The file path where to read the contents from")
|
||||||
|
exe.StringVar(&exe.inReplyTo, "in-reply-to", "", "The ID of the status that you want to reply to")
|
||||||
|
exe.StringVar(&exe.language, "language", "", "The ISO 639 language code for this status")
|
||||||
|
exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "list", "The replies policy of the list")
|
||||||
|
exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list")
|
||||||
|
exe.BoolVar(&exe.pollAllowsMultipleChoices, "poll-allows-multiple-choices", false, "Set to true to allow viewers to make multiple choices in the poll")
|
||||||
|
exe.Var(&exe.pollExpiresIn, "poll-expires-in", "The duration in which the poll is open for")
|
||||||
|
exe.BoolVar(&exe.pollHidesVoteCounts, "poll-hides-vote-counts", false, "Set to true to hide the vote count until the poll is closed")
|
||||||
|
exe.Var(&exe.pollOptions, "poll-option", "A poll option. Use this multiple times to set multiple options")
|
||||||
|
exe.Var(&exe.sensitive, "sensitive", "Set to true if the status should be marked as sensitive")
|
||||||
|
exe.StringVar(&exe.spoilerText, "spoiler-text", "", "The text to display as the status' warning or subject")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
exe.StringVar(&exe.visibility, "visibility", "", "The visibility of the posted status")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteExecutor is the executor for the delete command.
|
||||||
|
type DeleteExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
listID string
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDeleteExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *DeleteExecutor {
|
||||||
|
exe := DeleteExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("delete", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("delete", "Deletes a specific resource", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// EditExecutor is the executor for the edit command.
|
||||||
|
type EditExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
attachmentIDs internalFlag.StringSliceValue
|
||||||
|
description string
|
||||||
|
focus string
|
||||||
|
listID string
|
||||||
|
listTitle string
|
||||||
|
listRepliesPolicy string
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEditExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *EditExecutor {
|
||||||
|
exe := EditExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("edit", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
attachmentIDs: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("edit", "Edit a specific resource", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.attachmentIDs, "attachment-id", "The ID of the media attachment")
|
||||||
|
exe.StringVar(&exe.description, "description", "", "The description of the media attachment that will be used as the alt-text")
|
||||||
|
exe.StringVar(&exe.focus, "focus", "", "The focus of the media file")
|
||||||
|
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||||
|
exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list")
|
||||||
|
exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "", "The replies policy of the list")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// FollowExecutor is the executor for the follow command.
|
||||||
|
type FollowExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
notify bool
|
||||||
|
showReposts bool
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFollowExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *FollowExecutor {
|
||||||
|
exe := FollowExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("follow", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("follow", "Follow a resource (e.g. an account)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.BoolVar(&exe.notify, "notify", false, "Get notifications from statuses from the account you want to follow")
|
||||||
|
exe.BoolVar(&exe.showReposts, "show-reposts", true, "Show reposts from the account you want to follow")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitExecutor is the executor for the init command.
|
||||||
|
type InitExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
configDir string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInitExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
configDir string,
|
||||||
|
) *InitExecutor {
|
||||||
|
exe := InitExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("init", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
configDir: configDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("init", "Creates a new configuration file in the specified configuration directory", exe.FlagSet)
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoginExecutor is the executor for the login command.
|
||||||
|
type LoginExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
instance string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoginExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *LoginExecutor {
|
||||||
|
exe := LoginExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("login", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("login", "Logs into an account on GoToSocial", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.StringVar(&exe.instance, "instance", "", "The instance that you want to log into")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// MuteExecutor is the executor for the mute command.
|
||||||
|
type MuteExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
muteDuration internalFlag.TimeDurationValue
|
||||||
|
muteNotifications bool
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMuteExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *MuteExecutor {
|
||||||
|
exe := MuteExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("mute", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
muteDuration: internalFlag.NewTimeDurationValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("mute", "Mutes a specific resource (e.g. an account)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.Var(&exe.muteDuration, "mute-duration", "Specify how long the mute should last for. To mute indefinitely, set this to 0s")
|
||||||
|
exe.BoolVar(&exe.muteNotifications, "mute-notifications", false, "Set to true to mute notifications as well as posts")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// RejectExecutor is the executor for the reject command.
|
||||||
|
type RejectExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRejectExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *RejectExecutor {
|
||||||
|
exe := RejectExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("reject", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("reject", "Rejects a request (e.g. a follow request)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveExecutor is the executor for the remove command.
|
||||||
|
type RemoveExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountNames internalFlag.StringSliceValue
|
||||||
|
fromResourceType string
|
||||||
|
listID string
|
||||||
|
statusID string
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRemoveExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *RemoveExecutor {
|
||||||
|
exe := RemoveExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("remove", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountNames: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("remove", "Removes a resource from another resource", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountNames, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.fromResourceType, "from", "", "The resource type to action the target resource from (e.g. status)")
|
||||||
|
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||||
|
exe.StringVar(&exe.statusID, "status-id", "", "The ID of the status")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShowExecutor is the executor for the show command.
|
||||||
|
type ShowExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
getAllImages bool
|
||||||
|
getAllVideos bool
|
||||||
|
attachmentIDs internalFlag.StringSliceValue
|
||||||
|
showInBrowser bool
|
||||||
|
excludeBoosts bool
|
||||||
|
excludeReplies bool
|
||||||
|
fromResourceType string
|
||||||
|
limit int
|
||||||
|
listID string
|
||||||
|
myAccount bool
|
||||||
|
onlyMedia bool
|
||||||
|
onlyPinned bool
|
||||||
|
onlyPublic bool
|
||||||
|
showUserPreferences bool
|
||||||
|
showStatuses bool
|
||||||
|
skipAccountRelationship bool
|
||||||
|
statusID string
|
||||||
|
timelineCategory string
|
||||||
|
tag string
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewShowExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *ShowExecutor {
|
||||||
|
exe := ShowExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("show", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
attachmentIDs: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("show", "Shows details about a specified resource", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.BoolVar(&exe.getAllImages, "all-images", false, "Set to true to show all images from a status")
|
||||||
|
exe.BoolVar(&exe.getAllVideos, "all-videos", false, "Set to true to show all videos from a status")
|
||||||
|
exe.Var(&exe.attachmentIDs, "attachment-id", "The ID of the media attachment")
|
||||||
|
exe.BoolVar(&exe.showInBrowser, "browser", false, "Set to true to view in the your favourite browser")
|
||||||
|
exe.BoolVar(&exe.excludeBoosts, "exclude-boosts", false, "Set to true to exclude statuses that are boosts of another status")
|
||||||
|
exe.BoolVar(&exe.excludeReplies, "exclude-replies", false, "Set to true to exclude statuses that are a reply to another status")
|
||||||
|
exe.StringVar(&exe.fromResourceType, "from", "", "The resource type to action the target resource from (e.g. status)")
|
||||||
|
exe.IntVar(&exe.limit, "limit", 20, "Specify the limit of items to display")
|
||||||
|
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
|
||||||
|
exe.BoolVar(&exe.myAccount, "my-account", false, "Set to true to specify your account")
|
||||||
|
exe.BoolVar(&exe.onlyMedia, "only-media", false, "Set to true to show only the statuses with media attachments")
|
||||||
|
exe.BoolVar(&exe.onlyPinned, "only-pinned", false, "Set to true to show only the account's pinned statuses")
|
||||||
|
exe.BoolVar(&exe.onlyPublic, "only-public", false, "Set to true to show only the account's public posts")
|
||||||
|
exe.BoolVar(&exe.showUserPreferences, "show-preferences", false, "Set to true to view your posting preferences when viewing your account information")
|
||||||
|
exe.BoolVar(&exe.showStatuses, "show-statuses", false, "Set to true to view the statuses created from the account you are viewing")
|
||||||
|
exe.BoolVar(&exe.skipAccountRelationship, "skip-relationship", false, "Set to true to skip showing your relationship to the account that you are viewing")
|
||||||
|
exe.StringVar(&exe.statusID, "status-id", "", "The ID of the status")
|
||||||
|
exe.StringVar(&exe.timelineCategory, "timeline-category", "home", "The timeline category")
|
||||||
|
exe.StringVar(&exe.tag, "tag", "", "The name of the tag")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// SwitchExecutor is the executor for the switch command.
|
||||||
|
type SwitchExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
to string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSwitchExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *SwitchExecutor {
|
||||||
|
exe := SwitchExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("switch", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("switch", "Performs a switch operation (e.g. switching between logged in accounts)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.to, "to", "", "The resource type to action the target resource to (e.g. status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnblockExecutor is the executor for the unblock command.
|
||||||
|
type UnblockExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnblockExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *UnblockExecutor {
|
||||||
|
exe := UnblockExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("unblock", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("unblock", "Unblocks a resource (e.g. an account)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnfollowExecutor is the executor for the unfollow command.
|
||||||
|
type UnfollowExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnfollowExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *UnfollowExecutor {
|
||||||
|
exe := UnfollowExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("unfollow", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("unfollow", "Unfollows a resource (e.g. an account)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmuteExecutor is the executor for the unmute command.
|
||||||
|
type UnmuteExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
accountName internalFlag.StringSliceValue
|
||||||
|
resourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUnmuteExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *UnmuteExecutor {
|
||||||
|
exe := UnmuteExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("unmute", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
accountName: internalFlag.NewStringSliceValue(),
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("unmute", "Umutes a specific resource (e.g. an account)", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.Var(&exe.accountName, "account-name", "The name of the account")
|
||||||
|
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionExecutor is the executor for the version command.
|
||||||
|
type VersionExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
full bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVersionExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
) *VersionExecutor {
|
||||||
|
exe := VersionExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("version", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("version", "Prints the application's version and build information", exe.FlagSet)
|
||||||
|
|
||||||
|
exe.BoolVar(&exe.full, "full", false, "Set to true to print the build information in full")
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
||||||
|
|
||||||
|
// WhoamiExecutor is the executor for the whoami command.
|
||||||
|
type WhoamiExecutor struct {
|
||||||
|
*flag.FlagSet
|
||||||
|
printer *printer.Printer
|
||||||
|
config *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWhoamiExecutor(
|
||||||
|
printer *printer.Printer,
|
||||||
|
config *config.Config,
|
||||||
|
) *WhoamiExecutor {
|
||||||
|
exe := WhoamiExecutor{
|
||||||
|
FlagSet: flag.NewFlagSet("whoami", flag.ExitOnError),
|
||||||
|
printer: printer,
|
||||||
|
config: config,
|
||||||
|
}
|
||||||
|
|
||||||
|
exe.Usage = usage.ExecutorUsageFunc("whoami", "Prints the account that you are currently logged into", exe.FlagSet)
|
||||||
|
|
||||||
|
return &exe
|
||||||
|
}
|
|
@ -1,120 +1,16 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
flagAddPoll = "add-poll"
|
|
||||||
flagAccountName = "account-name"
|
|
||||||
flagAllImages = "all-images"
|
|
||||||
flagAllVideos = "all-videos"
|
|
||||||
flagAttachmentID = "attachment-id"
|
flagAttachmentID = "attachment-id"
|
||||||
flagBrowser = "browser"
|
|
||||||
flagContentType = "content-type"
|
|
||||||
flagContent = "content"
|
flagContent = "content"
|
||||||
flagEnableFederation = "enable-federation"
|
|
||||||
flagEnableLikes = "enable-likes"
|
|
||||||
flagEnableReplies = "enable-replies"
|
|
||||||
flagEnableReposts = "enable-reposts"
|
|
||||||
flagExcludeBoosts = "exclude-boosts"
|
|
||||||
flagExcludeReplies = "exclude-replies"
|
|
||||||
flagFrom = "from"
|
flagFrom = "from"
|
||||||
flagFromFile = "from-file"
|
flagFromFile = "from-file"
|
||||||
flagFull = "full"
|
|
||||||
flagInReplyTo = "in-reply-to"
|
|
||||||
flagInstance = "instance"
|
flagInstance = "instance"
|
||||||
flagLanguage = "language"
|
|
||||||
flagLimit = "limit"
|
|
||||||
flagListID = "list-id"
|
flagListID = "list-id"
|
||||||
flagListTitle = "list-title"
|
flagListTitle = "list-title"
|
||||||
flagListRepliesPolicy = "list-replies-policy"
|
|
||||||
flagMyAccount = "my-account"
|
|
||||||
flagMuteDuration = "mute-duration"
|
|
||||||
flagMuteNotifications = "mute-notifications"
|
|
||||||
flagNotify = "notify"
|
|
||||||
flagOnlyMedia = "only-media"
|
|
||||||
flagOnlyPinned = "only-pinned"
|
|
||||||
flagOnlyPublic = "only-public"
|
|
||||||
flagPollAllowsMultipleChoices = "poll-allows-multiple-choices"
|
|
||||||
flagPollExpiresIn = "poll-expires-in"
|
|
||||||
flagPollHidesVoteCounts = "poll-hides-vote-counts"
|
|
||||||
flagPollID = "poll-id"
|
|
||||||
flagPollOption = "poll-option"
|
flagPollOption = "poll-option"
|
||||||
flagSensitive = "sensitive"
|
|
||||||
flagSkipRelationship = "skip-relationship"
|
|
||||||
flagShowPreferences = "show-preferences"
|
|
||||||
flagShowReposts = "show-reposts"
|
|
||||||
flagShowStatuses = "show-statuses"
|
|
||||||
flagSpoilerText = "spoiler-text"
|
|
||||||
flagStatusID = "status-id"
|
flagStatusID = "status-id"
|
||||||
flagTag = "tag"
|
flagTag = "tag"
|
||||||
flagTimelineCategory = "timeline-category"
|
|
||||||
flagTo = "to"
|
flagTo = "to"
|
||||||
flagType = "type"
|
flagType = "type"
|
||||||
flagVisibility = "visibility"
|
|
||||||
flagVote = "vote"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MultiStringFlagValue []string
|
|
||||||
|
|
||||||
func (v *MultiStringFlagValue) String() string {
|
|
||||||
return strings.Join(*v, ", ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *MultiStringFlagValue) Set(value string) error {
|
|
||||||
if len(value) > 0 {
|
|
||||||
*v = append(*v, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type MultiIntFlagValue []int
|
|
||||||
|
|
||||||
func (v *MultiIntFlagValue) String() string {
|
|
||||||
value := "Choices: "
|
|
||||||
|
|
||||||
for ind, vote := range *v {
|
|
||||||
if ind == len(*v)-1 {
|
|
||||||
value += strconv.Itoa(vote)
|
|
||||||
} else {
|
|
||||||
value += strconv.Itoa(vote) + ", "
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *MultiIntFlagValue) Set(text string) error {
|
|
||||||
value, err := strconv.Atoi(text)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse the value to an integer: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
*v = append(*v, value)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type TimeDurationFlagValue struct {
|
|
||||||
Duration time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v TimeDurationFlagValue) String() string {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *TimeDurationFlagValue) Set(text string) error {
|
|
||||||
duration, err := time.ParseDuration(text)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to parse the value as time duration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
v.Duration = duration
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
46
internal/executor/follow.go
Normal file
46
internal/executor/follow.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *FollowExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceAccount: f.followAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[f.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: f.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(f.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FollowExecutor) followAccount(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, f.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
form := client.FollowAccountForm{
|
||||||
|
AccountID: accountID,
|
||||||
|
ShowReposts: f.showReposts,
|
||||||
|
Notify: f.notify,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.FollowAccount(form); err != nil {
|
||||||
|
return fmt.Errorf("unable to follow the account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.printer.PrintSuccess("Successfully sent the follow request.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,101 +0,0 @@
|
||||||
package executor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FollowOrUnfollowExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
accountName string
|
|
||||||
showReposts bool
|
|
||||||
notify bool
|
|
||||||
action string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFollowOrUnfollowExecutor(printer *printer.Printer, config *config.Config, name, summary string) *FollowOrUnfollowExecutor {
|
|
||||||
command := FollowOrUnfollowExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
action: name,
|
|
||||||
}
|
|
||||||
|
|
||||||
command.StringVar(&command.resourceType, flagType, "", "Specify the type of resource to follow")
|
|
||||||
command.StringVar(&command.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
|
||||||
command.BoolVar(&command.showReposts, flagShowReposts, true, "Show reposts from the account you want to follow")
|
|
||||||
command.BoolVar(&command.notify, flagNotify, false, "Get notifications when the account you want to follow posts a status")
|
|
||||||
|
|
||||||
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
|
||||||
|
|
||||||
return &command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FollowOrUnfollowExecutor) Execute() error {
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
|
||||||
resourceAccount: f.followOrUnfollowAccount,
|
|
||||||
}
|
|
||||||
|
|
||||||
doFunc, ok := funcMap[f.resourceType]
|
|
||||||
if !ok {
|
|
||||||
return UnsupportedTypeError{resourceType: f.resourceType}
|
|
||||||
}
|
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromFile(f.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return doFunc(gtsClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FollowOrUnfollowExecutor) followOrUnfollowAccount(gtsClient *client.Client) error {
|
|
||||||
accountID, err := getAccountID(gtsClient, false, f.accountName, f.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch f.action {
|
|
||||||
case CommandFollow:
|
|
||||||
return f.followAccount(gtsClient, accountID)
|
|
||||||
case CommandUnfollow:
|
|
||||||
return f.unfollowAccount(gtsClient, accountID)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FollowOrUnfollowExecutor) followAccount(gtsClient *client.Client, accountID string) error {
|
|
||||||
form := client.FollowAccountForm{
|
|
||||||
AccountID: accountID,
|
|
||||||
ShowReposts: f.showReposts,
|
|
||||||
Notify: f.notify,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gtsClient.FollowAccount(form); err != nil {
|
|
||||||
return fmt.Errorf("unable to follow the account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.printer.PrintSuccess("Successfully sent the follow request.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FollowOrUnfollowExecutor) unfollowAccount(gtsClient *client.Client, accountID string) error {
|
|
||||||
if err := gtsClient.UnfollowAccount(accountID); err != nil {
|
|
||||||
return fmt.Errorf("unable to unfollow the account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
f.printer.PrintSuccess("Successfully unfollowed the account.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,34 +1,12 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type InitExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
configDir string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInitExecutor(printer *printer.Printer, configDir, name, summary string) *InitExecutor {
|
|
||||||
initExe := InitExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
configDir: configDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
initExe.Usage = commandUsageFunc(name, summary, initExe.FlagSet)
|
|
||||||
|
|
||||||
return &initExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i *InitExecutor) Execute() error {
|
func (i *InitExecutor) Execute() error {
|
||||||
if err := utilities.EnsureDirectory(i.configDir); err != nil {
|
if err := utilities.EnsureDirectory(i.configDir); err != nil {
|
||||||
return fmt.Errorf("unable to ensure that the configuration directory is present: %w", err)
|
return fmt.Errorf("unable to ensure that the configuration directory is present: %w", err)
|
||||||
|
|
|
@ -1,40 +1,14 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LoginExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
instance string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLoginExecutor(printer *printer.Printer, config *config.Config, name, summary string) *LoginExecutor {
|
|
||||||
loginExe := LoginExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
instance: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
loginExe.StringVar(&loginExe.instance, flagInstance, "", "Specify the instance that you want to login to.")
|
|
||||||
|
|
||||||
loginExe.Usage = commandUsageFunc(name, summary, loginExe.FlagSet)
|
|
||||||
|
|
||||||
return &loginExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *LoginExecutor) Execute() error {
|
func (l *LoginExecutor) Execute() error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|
45
internal/executor/mute.go
Normal file
45
internal/executor/mute.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *MuteExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceAccount: m.muteAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[m.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: m.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(m.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MuteExecutor) muteAccount(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, m.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
form := client.MuteAccountForm{
|
||||||
|
Notifications: m.muteNotifications,
|
||||||
|
Duration: int(m.muteDuration.Duration.Seconds()),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.MuteAccount(accountID, form); err != nil {
|
||||||
|
return fmt.Errorf("unable to mute the account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.printer.PrintSuccess("Successfully muted the account.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,108 +0,0 @@
|
||||||
package executor
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MuteOrUnmuteExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
accountName string
|
|
||||||
command string
|
|
||||||
resourceType string
|
|
||||||
muteDuration TimeDurationFlagValue
|
|
||||||
muteNotifications bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMuteOrUnmuteExecutor(printer *printer.Printer, config *config.Config, name, summary string) *MuteOrUnmuteExecutor {
|
|
||||||
muteDuration := TimeDurationFlagValue{time.Duration(0 * time.Second)}
|
|
||||||
|
|
||||||
exe := MuteOrUnmuteExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
command: name,
|
|
||||||
muteDuration: muteDuration,
|
|
||||||
}
|
|
||||||
|
|
||||||
exe.StringVar(&exe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
|
||||||
exe.StringVar(&exe.resourceType, flagType, "", "Specify the type of resource to mute or unmute")
|
|
||||||
exe.BoolVar(&exe.muteNotifications, flagMuteNotifications, false, "Mute notifications as well as posts")
|
|
||||||
exe.Var(&exe.muteDuration, flagMuteDuration, "Specify how long the mute should last for. To mute indefinitely, set this to 0s")
|
|
||||||
|
|
||||||
exe.Usage = commandUsageFunc(name, summary, exe.FlagSet)
|
|
||||||
|
|
||||||
return &exe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MuteOrUnmuteExecutor) Execute() error {
|
|
||||||
funcMap := map[string]func(*client.Client) error{
|
|
||||||
resourceAccount: m.muteOrUnmuteAccount,
|
|
||||||
}
|
|
||||||
|
|
||||||
doFunc, ok := funcMap[m.resourceType]
|
|
||||||
if !ok {
|
|
||||||
return UnsupportedTypeError{resourceType: m.resourceType}
|
|
||||||
}
|
|
||||||
|
|
||||||
gtsClient, err := client.NewClientFromFile(m.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return doFunc(gtsClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MuteOrUnmuteExecutor) muteOrUnmuteAccount(gtsClient *client.Client) error {
|
|
||||||
if m.accountName == "" {
|
|
||||||
return FlagNotSetError{flagText: flagAccountName}
|
|
||||||
}
|
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, m.accountName, m.config.CredentialsFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch m.command {
|
|
||||||
case CommandMute:
|
|
||||||
return m.muteAccount(gtsClient, accountID)
|
|
||||||
case CommandUnmute:
|
|
||||||
return m.unmuteAccount(gtsClient, accountID)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MuteOrUnmuteExecutor) muteAccount(gtsClient *client.Client, accountID string) error {
|
|
||||||
form := client.MuteAccountForm{
|
|
||||||
Notifications: m.muteNotifications,
|
|
||||||
Duration: int(m.muteDuration.Duration.Seconds()),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gtsClient.MuteAccount(accountID, form); err != nil {
|
|
||||||
return fmt.Errorf("unable to mute the account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.printer.PrintSuccess("Successfully muted the account.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MuteOrUnmuteExecutor) unmuteAccount(gtsClient *client.Client, accountID string) error {
|
|
||||||
if err := gtsClient.UnmuteAccount(accountID); err != nil {
|
|
||||||
return fmt.Errorf("unable to unmute the account: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.printer.PrintSuccess("Successfully unmuted the account.")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
40
internal/executor/reject.go
Normal file
40
internal/executor/reject.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r *RejectExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceFollowRequest: r.rejectFollowRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[r.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: r.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(r.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RejectExecutor) rejectFollowRequest(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, r.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.RejectFollowRequest(accountID); err != nil {
|
||||||
|
return fmt.Errorf("unable to reject the follow request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r.printer.PrintSuccess("Successfully rejected the follow request.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,48 +1,11 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type RemoveExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
resourceType string
|
|
||||||
fromResourceType string
|
|
||||||
listID string
|
|
||||||
statusID string
|
|
||||||
accountNames MultiStringFlagValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRemoveExecutor(printer *printer.Printer, config *config.Config, name, summary string) *RemoveExecutor {
|
|
||||||
emptyArr := make([]string, 0, 3)
|
|
||||||
|
|
||||||
removeExe := RemoveExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
accountNames: MultiStringFlagValue(emptyArr),
|
|
||||||
}
|
|
||||||
|
|
||||||
removeExe.StringVar(&removeExe.resourceType, flagType, "", "Specify the resource type to remove (e.g. account, note)")
|
|
||||||
removeExe.StringVar(&removeExe.fromResourceType, flagFrom, "", "Specify the resource type to remove from (e.g. list, account, etc)")
|
|
||||||
removeExe.StringVar(&removeExe.listID, flagListID, "", "The ID of the list to remove from")
|
|
||||||
removeExe.StringVar(&removeExe.statusID, flagStatusID, "", "The ID of the status")
|
|
||||||
removeExe.Var(&removeExe.accountNames, flagAccountName, "The name of the account to remove from the resource")
|
|
||||||
|
|
||||||
removeExe.Usage = commandUsageFunc(name, summary, removeExe.FlagSet)
|
|
||||||
|
|
||||||
return &removeExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RemoveExecutor) Execute() error {
|
func (r *RemoveExecutor) Execute() error {
|
||||||
if r.fromResourceType == "" {
|
if r.fromResourceType == "" {
|
||||||
return FlagNotSetError{flagText: flagFrom}
|
return FlagNotSetError{flagText: flagFrom}
|
||||||
|
@ -89,19 +52,19 @@ func (r *RemoveExecutor) removeAccountsFromList(gtsClient *client.Client) error
|
||||||
return FlagNotSetError{flagText: flagListID}
|
return FlagNotSetError{flagText: flagListID}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r.accountNames) == 0 {
|
if r.accountNames.Empty() {
|
||||||
return NoAccountSpecifiedError{}
|
return NoAccountSpecifiedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
accountIDs := make([]string, len(r.accountNames))
|
accounts, err := getOtherAccounts(gtsClient, r.accountNames)
|
||||||
|
|
||||||
for ind := range r.accountNames {
|
|
||||||
accountID, err := getTheirAccountID(gtsClient, r.accountNames[ind])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to get the account ID for %s: %w", r.accountNames[ind], err)
|
return fmt.Errorf("unable to get the accounts: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
accountIDs[ind] = accountID
|
accountIDs := make([]string, len(accounts))
|
||||||
|
|
||||||
|
for ind := range accounts {
|
||||||
|
accountIDs[ind] = accounts[ind].ID
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := gtsClient.RemoveAccountsFromList(r.listID, accountIDs); err != nil {
|
if err := gtsClient.RemoveAccountsFromList(r.listID, accountIDs); err != nil {
|
||||||
|
@ -130,11 +93,7 @@ func (r *RemoveExecutor) removeFromAccount(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RemoveExecutor) removeNoteFromAccount(gtsClient *client.Client) error {
|
func (r *RemoveExecutor) removeNoteFromAccount(gtsClient *client.Client) error {
|
||||||
if len(r.accountNames) != 1 {
|
accountID, err := getAccountID(gtsClient, false, r.accountNames)
|
||||||
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(r.accountNames))
|
|
||||||
}
|
|
||||||
|
|
||||||
accountID, err := getAccountID(gtsClient, false, r.accountNames[0], r.config.CredentialsFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +1,15 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/media"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/media"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/utilities"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ShowExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
myAccount bool
|
|
||||||
excludeBoosts bool
|
|
||||||
excludeReplies bool
|
|
||||||
onlyMedia bool
|
|
||||||
onlyPinned bool
|
|
||||||
onlyPublic bool
|
|
||||||
showInBrowser bool
|
|
||||||
showUserPreferences bool
|
|
||||||
showStatuses bool
|
|
||||||
skipAccountRelationship bool
|
|
||||||
getAllImages bool
|
|
||||||
getAllVideos bool
|
|
||||||
resourceType string
|
|
||||||
accountName string
|
|
||||||
statusID string
|
|
||||||
timelineCategory string
|
|
||||||
listID string
|
|
||||||
tag string
|
|
||||||
pollID string
|
|
||||||
fromResourceType string
|
|
||||||
limit int
|
|
||||||
attachmentIDs MultiStringFlagValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewShowExecutor(printer *printer.Printer, config *config.Config, name, summary string) *ShowExecutor {
|
|
||||||
showExe := ShowExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
showExe.BoolVar(&showExe.myAccount, flagMyAccount, false, "Set to true to lookup your account")
|
|
||||||
showExe.BoolVar(&showExe.skipAccountRelationship, flagSkipRelationship, false, "Set to true to skip showing your relationship to the specified account")
|
|
||||||
showExe.BoolVar(&showExe.showUserPreferences, flagShowPreferences, false, "Show your preferences")
|
|
||||||
showExe.BoolVar(&showExe.showInBrowser, flagBrowser, false, "Set to true to view in the browser")
|
|
||||||
showExe.BoolVar(&showExe.showStatuses, flagShowStatuses, false, "Set to true to view the statuses created from the specified account")
|
|
||||||
showExe.BoolVar(&showExe.excludeReplies, flagExcludeReplies, false, "Set to true to exclude statuses that are a reply to another status")
|
|
||||||
showExe.BoolVar(&showExe.excludeBoosts, flagExcludeBoosts, false, "Set to true to exclude statuses that are boosts of another status")
|
|
||||||
showExe.BoolVar(&showExe.onlyPinned, flagOnlyPinned, false, "Set to true to show only the account's pinned statuses")
|
|
||||||
showExe.BoolVar(&showExe.onlyMedia, flagOnlyMedia, false, "Set to true to show only the statuses with media attachments")
|
|
||||||
showExe.BoolVar(&showExe.onlyPublic, flagOnlyPublic, false, "Set to true to show only the account's public posts")
|
|
||||||
showExe.BoolVar(&showExe.getAllImages, flagAllImages, false, "Set to true to show all images from a status")
|
|
||||||
showExe.BoolVar(&showExe.getAllVideos, flagAllVideos, false, "Set to true to show all videos from a status")
|
|
||||||
showExe.StringVar(&showExe.resourceType, flagType, "", "Specify the type of resource to display")
|
|
||||||
showExe.StringVar(&showExe.accountName, flagAccountName, "", "Specify the account name in full (username@domain)")
|
|
||||||
showExe.StringVar(&showExe.statusID, flagStatusID, "", "Specify the ID of the status to display")
|
|
||||||
showExe.StringVar(&showExe.timelineCategory, flagTimelineCategory, model.TimelineCategoryHome, "Specify the timeline category to view")
|
|
||||||
showExe.StringVar(&showExe.listID, flagListID, "", "Specify the ID of the list to display")
|
|
||||||
showExe.StringVar(&showExe.tag, flagTag, "", "Specify the name of the tag to use")
|
|
||||||
showExe.StringVar(&showExe.pollID, flagPollID, "", "Specify the ID of the poll to display")
|
|
||||||
showExe.Var(&showExe.attachmentIDs, flagAttachmentID, "Specify the ID of the media attachment to display")
|
|
||||||
showExe.StringVar(&showExe.fromResourceType, flagFrom, "", "Specify the resource type to view the target resource from (e.g. status for viewing media from, etc)")
|
|
||||||
showExe.IntVar(&showExe.limit, flagLimit, 20, "Specify the limit of items to display")
|
|
||||||
|
|
||||||
showExe.Usage = commandUsageFunc(name, summary, showExe.FlagSet)
|
|
||||||
|
|
||||||
return &showExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ShowExecutor) Execute() error {
|
func (s *ShowExecutor) Execute() error {
|
||||||
if s.resourceType == "" {
|
if s.resourceType == "" {
|
||||||
return FlagNotSetError{flagText: flagType}
|
return FlagNotSetError{flagText: flagType}
|
||||||
|
@ -96,7 +28,6 @@ func (s *ShowExecutor) Execute() error {
|
||||||
resourceLiked: s.showLiked,
|
resourceLiked: s.showLiked,
|
||||||
resourceStarred: s.showLiked,
|
resourceStarred: s.showLiked,
|
||||||
resourceFollowRequest: s.showFollowRequests,
|
resourceFollowRequest: s.showFollowRequests,
|
||||||
resourcePoll: s.showPoll,
|
|
||||||
resourceMutedAccounts: s.showMutedAccounts,
|
resourceMutedAccounts: s.showMutedAccounts,
|
||||||
resourceMedia: s.showMedia,
|
resourceMedia: s.showMedia,
|
||||||
resourceMediaAttachment: s.showMediaAttachment,
|
resourceMediaAttachment: s.showMediaAttachment,
|
||||||
|
@ -127,25 +58,9 @@ func (s *ShowExecutor) showInstance(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
var (
|
account, err := getAccount(gtsClient, s.myAccount, s.accountName)
|
||||||
account model.Account
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if s.myAccount {
|
|
||||||
account, err = getMyAccount(gtsClient, s.config.CredentialsFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account details: %w", err)
|
return fmt.Errorf("unable to get the account information: %w", err)
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if s.accountName == "" {
|
|
||||||
return FlagNotSetError{flagText: flagAccountName}
|
|
||||||
}
|
|
||||||
|
|
||||||
account, err = getAccount(gtsClient, s.accountName)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("received an error while getting the account details: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.showInBrowser {
|
if s.showInBrowser {
|
||||||
|
@ -160,6 +75,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
relationship *model.AccountRelationship
|
relationship *model.AccountRelationship
|
||||||
preferences *model.Preferences
|
preferences *model.Preferences
|
||||||
statuses *model.StatusList
|
statuses *model.StatusList
|
||||||
|
myAccountID string
|
||||||
)
|
)
|
||||||
|
|
||||||
if !s.myAccount && !s.skipAccountRelationship {
|
if !s.myAccount && !s.skipAccountRelationship {
|
||||||
|
@ -169,12 +85,15 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.myAccount && s.showUserPreferences {
|
if s.myAccount {
|
||||||
|
myAccountID = account.ID
|
||||||
|
if s.showUserPreferences {
|
||||||
preferences, err = gtsClient.GetUserPreferences()
|
preferences, err = gtsClient.GetUserPreferences()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if s.showStatuses {
|
if s.showStatuses {
|
||||||
form := client.GetAccountStatusesForm{
|
form := client.GetAccountStatusesForm{
|
||||||
|
@ -193,7 +112,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.printer.PrintAccount(account, relationship, preferences, statuses)
|
s.printer.PrintAccount(account, relationship, preferences, statuses, myAccountID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -216,7 +135,12 @@ func (s *ShowExecutor) showStatus(gtsClient *client.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.printer.PrintStatus(status)
|
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.printer.PrintStatus(status, myAccountID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -265,7 +189,12 @@ func (s *ShowExecutor) showTimeline(gtsClient *client.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.printer.PrintStatusList(timeline)
|
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.printer.PrintStatusList(timeline, myAccountID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -337,7 +266,7 @@ func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShowExecutor) showFollowersFromAccount(gtsClient *client.Client) error {
|
func (s *ShowExecutor) showFollowersFromAccount(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
|
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -377,7 +306,7 @@ func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShowExecutor) showFollowingFromAccount(gtsClient *client.Client) error {
|
func (s *ShowExecutor) showFollowingFromAccount(gtsClient *client.Client) error {
|
||||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
|
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -418,7 +347,12 @@ func (s *ShowExecutor) showBookmarks(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(bookmarks.Statuses) > 0 {
|
if len(bookmarks.Statuses) > 0 {
|
||||||
s.printer.PrintStatusList(bookmarks)
|
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.printer.PrintStatusList(bookmarks, myAccountID)
|
||||||
} else {
|
} else {
|
||||||
s.printer.PrintInfo("You have no bookmarks.\n")
|
s.printer.PrintInfo("You have no bookmarks.\n")
|
||||||
}
|
}
|
||||||
|
@ -433,7 +367,12 @@ func (s *ShowExecutor) showLiked(gtsClient *client.Client) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(liked.Statuses) > 0 {
|
if len(liked.Statuses) > 0 {
|
||||||
s.printer.PrintStatusList(liked)
|
myAccountID, err := getAccountID(gtsClient, true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get your account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.printer.PrintStatusList(liked, myAccountID)
|
||||||
} else {
|
} else {
|
||||||
s.printer.PrintInfo("You have no " + s.resourceType + " statuses.\n")
|
s.printer.PrintInfo("You have no " + s.resourceType + " statuses.\n")
|
||||||
}
|
}
|
||||||
|
@ -456,21 +395,6 @@ func (s *ShowExecutor) showFollowRequests(gtsClient *client.Client) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ShowExecutor) showPoll(gtsClient *client.Client) error {
|
|
||||||
if s.pollID == "" {
|
|
||||||
return FlagNotSetError{flagText: flagPollID}
|
|
||||||
}
|
|
||||||
|
|
||||||
poll, err := gtsClient.GetPoll(s.pollID)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.printer.PrintPoll(poll)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *ShowExecutor) showMutedAccounts(gtsClient *client.Client) error {
|
func (s *ShowExecutor) showMutedAccounts(gtsClient *client.Client) error {
|
||||||
muted, err := gtsClient.GetMutedAccounts(s.limit)
|
muted, err := gtsClient.GetMutedAccounts(s.limit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,61 +1,38 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type SwitchExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
config *config.Config
|
|
||||||
printer *printer.Printer
|
|
||||||
toResourceType string
|
|
||||||
accountName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSwitchExecutor(printer *printer.Printer, config *config.Config, name, summary string) *SwitchExecutor {
|
|
||||||
switchExe := SwitchExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
config: config,
|
|
||||||
printer: printer,
|
|
||||||
}
|
|
||||||
|
|
||||||
switchExe.StringVar(&switchExe.toResourceType, flagTo, "", "The account to switch to")
|
|
||||||
switchExe.StringVar(&switchExe.accountName, flagAccountName, "", "The name of the account to switch to")
|
|
||||||
|
|
||||||
switchExe.Usage = commandUsageFunc(name, summary, switchExe.FlagSet)
|
|
||||||
|
|
||||||
return &switchExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SwitchExecutor) Execute() error {
|
func (s *SwitchExecutor) Execute() error {
|
||||||
funcMap := map[string]func() error{
|
funcMap := map[string]func() error{
|
||||||
resourceAccount: s.switchToAccount,
|
resourceAccount: s.switchToAccount,
|
||||||
}
|
}
|
||||||
|
|
||||||
doFunc, ok := funcMap[s.toResourceType]
|
doFunc, ok := funcMap[s.to]
|
||||||
if !ok {
|
if !ok {
|
||||||
return UnsupportedTypeError{resourceType: s.toResourceType}
|
return UnsupportedTypeError{resourceType: s.to}
|
||||||
}
|
}
|
||||||
|
|
||||||
return doFunc()
|
return doFunc()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SwitchExecutor) switchToAccount() error {
|
func (s *SwitchExecutor) switchToAccount() error {
|
||||||
if s.accountName == "" {
|
expectedNumAccountNames := 1
|
||||||
return NoAccountSpecifiedError{}
|
if !s.accountName.ExpectedLength(expectedNumAccountNames) {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"found an unexpected number of --account-name flags: expected %d",
|
||||||
|
expectedNumAccountNames,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := config.UpdateCurrentAccount(s.accountName, s.config.CredentialsFile); err != nil {
|
if err := config.UpdateCurrentAccount(s.accountName[0], s.config.CredentialsFile); err != nil {
|
||||||
return fmt.Errorf("unable to switch account to the account: %w", err)
|
return fmt.Errorf("unable to switch account to the account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.printer.PrintSuccess("The current account is now set to '" + s.accountName + "'.")
|
s.printer.PrintSuccess("The current account is now set to '" + s.accountName[0] + "'.")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
40
internal/executor/unblock.go
Normal file
40
internal/executor/unblock.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *UnblockExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceAccount: b.unblockAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[b.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: b.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(b.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *UnblockExecutor) unblockAccount(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, b.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.UnblockAccount(accountID); err != nil {
|
||||||
|
return fmt.Errorf("unable to unblock the account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.printer.PrintSuccess("Successfully unblocked the account.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
40
internal/executor/unfollow.go
Normal file
40
internal/executor/unfollow.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *UnfollowExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceAccount: f.unfollowAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[f.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: f.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(f.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *UnfollowExecutor) unfollowAccount(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, f.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.UnfollowAccount(accountID); err != nil {
|
||||||
|
return fmt.Errorf("unable to unfollow the account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
f.printer.PrintSuccess("Successfully unfollowed the account.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
40
internal/executor/unmute.go
Normal file
40
internal/executor/unmute.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package executor
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *UnmuteExecutor) Execute() error {
|
||||||
|
funcMap := map[string]func(*client.Client) error{
|
||||||
|
resourceAccount: m.unmuteAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
doFunc, ok := funcMap[m.resourceType]
|
||||||
|
if !ok {
|
||||||
|
return UnsupportedTypeError{resourceType: m.resourceType}
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsClient, err := client.NewClientFromFile(m.config.CredentialsFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return doFunc(gtsClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *UnmuteExecutor) unmuteAccount(gtsClient *client.Client) error {
|
||||||
|
accountID, err := getAccountID(gtsClient, false, m.accountName)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := gtsClient.UnmuteAccount(accountID); err != nil {
|
||||||
|
return fmt.Errorf("unable to unmute the account: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
m.printer.PrintSuccess("Successfully unmuted the account.")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,51 +1,7 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type VersionExecutor struct {
|
|
||||||
*flag.FlagSet
|
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
showFullVersion bool
|
|
||||||
binaryVersion string
|
|
||||||
buildTime string
|
|
||||||
goVersion string
|
|
||||||
gitCommit string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewVersionExecutor(
|
|
||||||
printer *printer.Printer,
|
|
||||||
name,
|
|
||||||
summary,
|
|
||||||
binaryVersion,
|
|
||||||
buildTime,
|
|
||||||
goVersion,
|
|
||||||
gitCommit string,
|
|
||||||
) *VersionExecutor {
|
|
||||||
command := VersionExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
binaryVersion: binaryVersion,
|
|
||||||
buildTime: buildTime,
|
|
||||||
goVersion: goVersion,
|
|
||||||
gitCommit: gitCommit,
|
|
||||||
showFullVersion: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
command.BoolVar(&command.showFullVersion, flagFull, false, "prints the full build information")
|
|
||||||
|
|
||||||
command.Usage = commandUsageFunc(name, summary, command.FlagSet)
|
|
||||||
|
|
||||||
return &command
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *VersionExecutor) Execute() error {
|
func (v *VersionExecutor) Execute() error {
|
||||||
v.printer.PrintVersion(v.showFullVersion, v.binaryVersion, v.buildTime, v.goVersion, v.gitCommit)
|
v.printer.PrintVersion(v.full)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,18 @@
|
||||||
package executor
|
package executor
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type WhoAmIExecutor struct {
|
func (e *WhoamiExecutor) Execute() error {
|
||||||
*flag.FlagSet
|
config, err := config.NewCredentialsConfigFromFile(e.config.CredentialsFile)
|
||||||
|
|
||||||
printer *printer.Printer
|
|
||||||
config *config.Config
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWhoAmIExecutor(printer *printer.Printer, config *config.Config, name, summary string) *WhoAmIExecutor {
|
|
||||||
whoExe := WhoAmIExecutor{
|
|
||||||
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
|
|
||||||
|
|
||||||
printer: printer,
|
|
||||||
config: config,
|
|
||||||
}
|
|
||||||
|
|
||||||
whoExe.Usage = commandUsageFunc(name, summary, whoExe.FlagSet)
|
|
||||||
|
|
||||||
return &whoExe
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *WhoAmIExecutor) Execute() error {
|
|
||||||
config, err := config.NewCredentialsConfigFromFile(c.config.CredentialsFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to load the credential config: %w", err)
|
return fmt.Errorf("unable to load the credential config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.printer.PrintInfo("You are logged in as '" + config.CurrentAccount + "'.\n")
|
e.printer.PrintInfo("You are logged in as '" + config.CurrentAccount + "'.\n")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
36
internal/flag/boolptrvalue.go
Normal file
36
internal/flag/boolptrvalue.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package flag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BoolPtrValue struct {
|
||||||
|
Value *bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBoolPtrValue() BoolPtrValue {
|
||||||
|
return BoolPtrValue{
|
||||||
|
Value: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b BoolPtrValue) String() string {
|
||||||
|
if b.Value == nil {
|
||||||
|
return "NOT SET"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.FormatBool(*b.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BoolPtrValue) Set(value string) error {
|
||||||
|
boolVar, err := strconv.ParseBool(value)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse %q as a boolean value: %w", value, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b.Value = new(bool)
|
||||||
|
*b.Value = boolVar
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
49
internal/flag/intslice.go
Normal file
49
internal/flag/intslice.go
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
package flag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IntSliceValue []int
|
||||||
|
|
||||||
|
func NewIntSliceValue() IntSliceValue {
|
||||||
|
arr := make([]int, 0, 3)
|
||||||
|
|
||||||
|
return IntSliceValue(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v IntSliceValue) String() string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for ind, value := range v {
|
||||||
|
if ind == len(v)-1 {
|
||||||
|
builder.WriteString(strconv.Itoa(value))
|
||||||
|
} else {
|
||||||
|
builder.WriteString(strconv.Itoa(value))
|
||||||
|
builder.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *IntSliceValue) Set(text string) error {
|
||||||
|
value, err := strconv.Atoi(text)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse the value to an integer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*v = append(*v, value)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v IntSliceValue) Empty() bool {
|
||||||
|
return len(v) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v IntSliceValue) ExpectedLength(expectedLength int) bool {
|
||||||
|
return len(v) == expectedLength
|
||||||
|
}
|
31
internal/flag/stringslice.go
Normal file
31
internal/flag/stringslice.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package flag
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type StringSliceValue []string
|
||||||
|
|
||||||
|
func NewStringSliceValue() StringSliceValue {
|
||||||
|
arr := make([]string, 0, 3)
|
||||||
|
|
||||||
|
return StringSliceValue(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v StringSliceValue) String() string {
|
||||||
|
return strings.Join(v, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *StringSliceValue) Set(value string) error {
|
||||||
|
if len(value) > 0 {
|
||||||
|
*v = append(*v, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v StringSliceValue) Empty() bool {
|
||||||
|
return len(v) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v StringSliceValue) ExpectedLength(expectedLength int) bool {
|
||||||
|
return len(v) == expectedLength
|
||||||
|
}
|
31
internal/flag/timedurationvalue.go
Normal file
31
internal/flag/timedurationvalue.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package flag
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TimeDurationValue struct {
|
||||||
|
Duration time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTimeDurationValue() TimeDurationValue {
|
||||||
|
return TimeDurationValue{
|
||||||
|
Duration: 0 * time.Second,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v TimeDurationValue) String() string {
|
||||||
|
return v.Duration.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *TimeDurationValue) Set(text string) error {
|
||||||
|
duration, err := time.ParseDuration(text)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to parse the value as time duration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v.Duration = duration
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ func (p Printer) PrintAccount(
|
||||||
relationship *model.AccountRelationship,
|
relationship *model.AccountRelationship,
|
||||||
preferences *model.Preferences,
|
preferences *model.Preferences,
|
||||||
statuses *model.StatusList,
|
statuses *model.StatusList,
|
||||||
|
userAccountID string,
|
||||||
) {
|
) {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
|
@ -47,7 +48,7 @@ func (p Printer) PrintAccount(
|
||||||
}
|
}
|
||||||
|
|
||||||
if statuses != nil {
|
if statuses != nil {
|
||||||
builder.WriteString("\n\n" + p.statusList(*statuses))
|
builder.WriteString("\n\n" + p.statusList(*statuses, userAccountID))
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.WriteString("\n\n")
|
builder.WriteString("\n\n")
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
package printer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p Printer) PrintPoll(poll model.Poll) {
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
builder.WriteString("\n" + p.headerFormat("POLL ID:"))
|
|
||||||
builder.WriteString("\n" + poll.ID)
|
|
||||||
|
|
||||||
builder.WriteString("\n\n" + p.headerFormat("OPTIONS:"))
|
|
||||||
builder.WriteString(p.pollOptions(poll))
|
|
||||||
|
|
||||||
builder.WriteString("\n\n" + p.headerFormat("MULTIPLE CHOICES ALLOWED:"))
|
|
||||||
builder.WriteString("\n" + strconv.FormatBool(poll.Multiple))
|
|
||||||
|
|
||||||
builder.WriteString("\n\n" + p.headerFormat("YOU VOTED:"))
|
|
||||||
builder.WriteString("\n" + strconv.FormatBool(poll.Voted))
|
|
||||||
|
|
||||||
if len(poll.OwnVotes) > 0 {
|
|
||||||
builder.WriteString("\n\n" + p.headerFormat("YOUR VOTES:"))
|
|
||||||
|
|
||||||
for _, vote := range poll.OwnVotes {
|
|
||||||
builder.WriteString("\n" + "[" + strconv.Itoa(vote) + "] " + poll.Options[vote].Title)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString("\n\n" + p.headerFormat("EXPIRED:"))
|
|
||||||
builder.WriteString("\n" + strconv.FormatBool(poll.Expired))
|
|
||||||
builder.WriteString("\n\n")
|
|
||||||
|
|
||||||
p.print(builder.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Printer) pollOptions(poll model.Poll) string {
|
|
||||||
var builder strings.Builder
|
|
||||||
|
|
||||||
for ind, option := range poll.Options {
|
|
||||||
var (
|
|
||||||
votage float64
|
|
||||||
percentage int
|
|
||||||
)
|
|
||||||
|
|
||||||
if poll.VotesCount == 0 {
|
|
||||||
percentage = 0
|
|
||||||
} else {
|
|
||||||
votage = float64(option.VotesCount) / float64(poll.VotesCount)
|
|
||||||
percentage = int(math.Floor(100 * votage))
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString("\n\n" + "[" + strconv.Itoa(ind) + "] " + option.Title)
|
|
||||||
builder.WriteString(p.pollMeter(votage))
|
|
||||||
builder.WriteString("\n" + strconv.Itoa(option.VotesCount) + " votes " + "(" + strconv.Itoa(percentage) + "%)")
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.WriteString("\n\n" + p.fieldFormat("Total votes:") + " " + strconv.Itoa(poll.VotesCount))
|
|
||||||
builder.WriteString("\n" + p.fieldFormat("Poll ID:") + " " + poll.ID)
|
|
||||||
builder.WriteString("\n" + p.fieldFormat("Poll is open until:") + " " + p.formatDateTime(poll.ExpiredAt))
|
|
||||||
|
|
||||||
return builder.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Printer) pollMeter(votage float64) string {
|
|
||||||
numVoteBlocks := int(math.Floor(float64(p.lineWrapCharacterLimit) * votage))
|
|
||||||
numBackgroundBlocks := p.lineWrapCharacterLimit - numVoteBlocks
|
|
||||||
|
|
||||||
voteBlockColor := p.theme.boldgreen
|
|
||||||
backgroundBlockColor := p.theme.grey
|
|
||||||
|
|
||||||
if p.noColor {
|
|
||||||
voteBlockColor = p.theme.reset
|
|
||||||
|
|
||||||
if numVoteBlocks == 0 {
|
|
||||||
numVoteBlocks = 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meter := "\n" + voteBlockColor + strings.Repeat(symbolPollMeter, numVoteBlocks) + p.theme.reset
|
|
||||||
|
|
||||||
if !p.noColor {
|
|
||||||
meter += backgroundBlockColor + strings.Repeat(symbolPollMeter, numBackgroundBlocks) + p.theme.reset
|
|
||||||
}
|
|
||||||
|
|
||||||
return meter
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ const (
|
||||||
noMediaDescription = "This media attachment has no description."
|
noMediaDescription = "This media attachment has no description."
|
||||||
symbolBullet = "\u2022"
|
symbolBullet = "\u2022"
|
||||||
symbolPollMeter = "\u2501"
|
symbolPollMeter = "\u2501"
|
||||||
symbolSuccess = "\u2714"
|
symbolCheckMark = "\u2714"
|
||||||
symbolFailure = "\u2717"
|
symbolFailure = "\u2717"
|
||||||
symbolImage = "\uf03e"
|
symbolImage = "\uf03e"
|
||||||
symbolLiked = "\uf51f"
|
symbolLiked = "\uf51f"
|
||||||
|
@ -78,9 +78,9 @@ func NewPrinter(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Printer) PrintSuccess(text string) {
|
func (p Printer) PrintSuccess(text string) {
|
||||||
success := p.theme.boldgreen + symbolSuccess + p.theme.reset
|
success := p.theme.boldgreen + symbolCheckMark + p.theme.reset
|
||||||
if p.noColor {
|
if p.noColor {
|
||||||
success = symbolSuccess
|
success = symbolCheckMark
|
||||||
}
|
}
|
||||||
|
|
||||||
printToStdout(success + " " + text + "\n")
|
printToStdout(success + " " + text + "\n")
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
package printer
|
package printer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p Printer) PrintStatus(status model.Status) {
|
func (p Printer) PrintStatus(status model.Status, userAccountID string) {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
// The account information
|
// The account information
|
||||||
|
@ -41,7 +42,13 @@ func (p Printer) PrintStatus(status model.Status) {
|
||||||
|
|
||||||
// If a poll exists in a status, write the contents to the builder.
|
// If a poll exists in a status, write the contents to the builder.
|
||||||
if status.Poll != nil {
|
if status.Poll != nil {
|
||||||
builder.WriteString(p.pollOptions(*status.Poll))
|
pollOwner := false
|
||||||
|
if status.Account.ID == userAccountID {
|
||||||
|
pollOwner = true
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.headerFormat("POLL DETAILS:"))
|
||||||
|
builder.WriteString(p.pollDetails(*status.Poll, pollOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status creation time
|
// Status creation time
|
||||||
|
@ -72,17 +79,18 @@ func (p Printer) PrintStatus(status model.Status) {
|
||||||
p.print(builder.String())
|
p.print(builder.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Printer) PrintStatusList(list model.StatusList) {
|
func (p Printer) PrintStatusList(list model.StatusList, userAccountID string) {
|
||||||
p.print(p.statusList(list))
|
p.print(p.statusList(list, userAccountID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Printer) statusList(list model.StatusList) string {
|
func (p Printer) statusList(list model.StatusList, userAccountID string) string {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
||||||
builder.WriteString(p.headerFormat(list.Name) + "\n")
|
builder.WriteString(p.headerFormat(list.Name) + "\n")
|
||||||
|
|
||||||
for _, status := range list.Statuses {
|
for _, status := range list.Statuses {
|
||||||
statusID := status.ID
|
statusID := status.ID
|
||||||
|
statusOwnerID := status.Account.ID
|
||||||
createdAt := p.formatDateTime(status.CreatedAt)
|
createdAt := p.formatDateTime(status.CreatedAt)
|
||||||
boostedAt := ""
|
boostedAt := ""
|
||||||
content := status.Content
|
content := status.Content
|
||||||
|
@ -100,6 +108,7 @@ func (p Printer) statusList(list model.StatusList) string {
|
||||||
))
|
))
|
||||||
|
|
||||||
statusID = status.Reblog.ID
|
statusID = status.Reblog.ID
|
||||||
|
statusOwnerID = status.Reblog.Account.ID
|
||||||
createdAt = p.formatDateTime(status.Reblog.CreatedAt)
|
createdAt = p.formatDateTime(status.Reblog.CreatedAt)
|
||||||
boostedAt = p.formatDateTime(status.CreatedAt)
|
boostedAt = p.formatDateTime(status.CreatedAt)
|
||||||
content = status.Reblog.Content
|
content = status.Reblog.Content
|
||||||
|
@ -120,7 +129,12 @@ func (p Printer) statusList(list model.StatusList) string {
|
||||||
builder.WriteString("\n" + p.convertHTMLToText(content, true))
|
builder.WriteString("\n" + p.convertHTMLToText(content, true))
|
||||||
|
|
||||||
if poll != nil {
|
if poll != nil {
|
||||||
builder.WriteString(p.pollOptions(*poll))
|
pollOwner := false
|
||||||
|
if statusOwnerID == userAccountID {
|
||||||
|
pollOwner = true
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(p.pollDetails(*poll, pollOwner))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, media := range mediaAttachments {
|
for _, media := range mediaAttachments {
|
||||||
|
@ -172,3 +186,78 @@ func (p Printer) statusList(list model.StatusList) string {
|
||||||
|
|
||||||
return builder.String()
|
return builder.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Printer) pollDetails(poll model.Poll, owner bool) string {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for ind, option := range poll.Options {
|
||||||
|
var (
|
||||||
|
votage float64
|
||||||
|
percentage int
|
||||||
|
)
|
||||||
|
|
||||||
|
// Show the poll results under any of the following conditions:
|
||||||
|
// - the user is the owner of the poll
|
||||||
|
// - the poll has expired
|
||||||
|
// - the user has voted in the poll
|
||||||
|
if owner || poll.Expired || poll.Voted {
|
||||||
|
if poll.VotesCount == 0 {
|
||||||
|
percentage = 0
|
||||||
|
} else {
|
||||||
|
votage = float64(option.VotesCount) / float64(poll.VotesCount)
|
||||||
|
percentage = int(math.Floor(100 * votage))
|
||||||
|
}
|
||||||
|
|
||||||
|
optionTitle := "\n\n" + "[" + strconv.Itoa(ind) + "] " + option.Title
|
||||||
|
|
||||||
|
for _, vote := range poll.OwnVotes {
|
||||||
|
if ind == vote {
|
||||||
|
optionTitle += " " + symbolCheckMark
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString(optionTitle)
|
||||||
|
builder.WriteString(p.pollMeter(votage))
|
||||||
|
builder.WriteString("\n" + strconv.Itoa(option.VotesCount) + " votes " + "(" + strconv.Itoa(percentage) + "%)")
|
||||||
|
} else {
|
||||||
|
builder.WriteString("\n" + "[" + strconv.Itoa(ind) + "] " + option.Title)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pollStatusField := "Poll is open until: "
|
||||||
|
if poll.Expired {
|
||||||
|
pollStatusField = "Poll was closed on: "
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.WriteString("\n\n" + p.fieldFormat(pollStatusField) + p.formatDateTime(poll.ExpiredAt))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Total votes: ") + strconv.Itoa(poll.VotesCount))
|
||||||
|
builder.WriteString("\n" + p.fieldFormat("Multiple choices allowed: ") + strconv.FormatBool(poll.Multiple))
|
||||||
|
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Printer) pollMeter(votage float64) string {
|
||||||
|
numVoteBlocks := int(math.Floor(float64(p.lineWrapCharacterLimit) * votage))
|
||||||
|
numBackgroundBlocks := p.lineWrapCharacterLimit - numVoteBlocks
|
||||||
|
|
||||||
|
voteBlockColour := p.theme.boldgreen
|
||||||
|
backgroundBlockColor := p.theme.grey
|
||||||
|
|
||||||
|
if p.noColor {
|
||||||
|
voteBlockColour = p.theme.reset
|
||||||
|
|
||||||
|
if numVoteBlocks == 0 {
|
||||||
|
numVoteBlocks = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
meter := "\n" + voteBlockColour + strings.Repeat(symbolPollMeter, numVoteBlocks) + p.theme.reset
|
||||||
|
|
||||||
|
if !p.noColor {
|
||||||
|
meter += backgroundBlockColor + strings.Repeat(symbolPollMeter, numBackgroundBlocks) + p.theme.reset
|
||||||
|
}
|
||||||
|
|
||||||
|
return meter
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,13 @@ package printer
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p Printer) PrintVersion(showFullVersion bool, binaryVersion, buildTime, goVersion, gitCommit string) {
|
func (p Printer) PrintVersion(showFullVersion bool) {
|
||||||
if !showFullVersion {
|
if !showFullVersion {
|
||||||
printToStdout("Enbas " + binaryVersion + "\n")
|
printToStdout("Enbas " + version.BinaryVersion + "\n")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -18,10 +20,10 @@ func (p Printer) PrintVersion(showFullVersion bool, binaryVersion, buildTime, go
|
||||||
|
|
||||||
tableWriter := tabwriter.NewWriter(&builder, 0, 4, 1, ' ', 0)
|
tableWriter := tabwriter.NewWriter(&builder, 0, 4, 1, ' ', 0)
|
||||||
|
|
||||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Version:") + "\t" + binaryVersion + "\n"))
|
_, _ = tableWriter.Write([]byte(p.fieldFormat("Version:") + "\t" + version.BinaryVersion + "\n"))
|
||||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Git commit:") + "\t" + gitCommit + "\n"))
|
_, _ = tableWriter.Write([]byte(p.fieldFormat("Git commit:") + "\t" + version.GitCommit + "\n"))
|
||||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Go version:") + "\t" + goVersion + "\n"))
|
_, _ = tableWriter.Write([]byte(p.fieldFormat("Go version:") + "\t" + version.GoVersion + "\n"))
|
||||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Build date:") + "\t" + buildTime + "\n"))
|
_, _ = tableWriter.Write([]byte(p.fieldFormat("Build date:") + "\t" + version.BuildTime + "\n"))
|
||||||
|
|
||||||
tableWriter.Flush()
|
tableWriter.Flush()
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package usage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
@ -6,9 +6,11 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
|
|
||||||
|
"codeflow.dananglin.me.uk/apollo/enbas/internal/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
func usageFunc(summaries map[string]string) func() {
|
func AppUsageFunc() func() {
|
||||||
cmds := make([]string, len(summaries))
|
cmds := make([]string, len(summaries))
|
||||||
ind := 0
|
ind := 0
|
||||||
|
|
||||||
|
@ -24,8 +26,8 @@ func usageFunc(summaries map[string]string) func() {
|
||||||
|
|
||||||
builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n")
|
builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n")
|
||||||
|
|
||||||
if binaryVersion != "" {
|
if version.BinaryVersion != "" {
|
||||||
builder.WriteString("VERSION:\n " + binaryVersion + "\n\n")
|
builder.WriteString("VERSION:\n " + version.BinaryVersion + "\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.WriteString("USAGE:\n enbas [flags]\n enbas [flags] [command]\n\nCOMMANDS:")
|
builder.WriteString("USAGE:\n enbas [flags]\n enbas [flags] [command]\n\nCOMMANDS:")
|
3
internal/usage/codegen.go
Normal file
3
internal/usage/codegen.go
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
package usage
|
||||||
|
|
||||||
|
//go:generate go run ../../cmd/enbas-codegen --package usage --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json
|
|
@ -1,4 +1,4 @@
|
||||||
package executor
|
package usage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
@ -6,8 +6,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// commandUsageFunc returns the function used to print a command's help page.
|
// ExecutorUsageFunc returns the function used to print a command's help page.
|
||||||
func commandUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
func ExecutorUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
||||||
return func() {
|
return func() {
|
||||||
var builder strings.Builder
|
var builder strings.Builder
|
||||||
|
|
28
internal/usage/summaries.go
Normal file
28
internal/usage/summaries.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
This file is generated by the enbas-codegen
|
||||||
|
DO NOT EDIT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package usage
|
||||||
|
|
||||||
|
var summaries = map[string]string{
|
||||||
|
"accept": "Accepts a request (e.g. a follow request)",
|
||||||
|
"add": "Adds a resource to another resource",
|
||||||
|
"block": "Blocks a resource (e.g. an account)",
|
||||||
|
"create": "Creates a specific resource",
|
||||||
|
"delete": "Deletes a specific resource",
|
||||||
|
"edit": "Edit a specific resource",
|
||||||
|
"follow": "Follow a resource (e.g. an account)",
|
||||||
|
"init": "Creates a new configuration file in the specified configuration directory",
|
||||||
|
"login": "Logs into an account on GoToSocial",
|
||||||
|
"mute": "Mutes a specific resource (e.g. an account)",
|
||||||
|
"reject": "Rejects a request (e.g. a follow request)",
|
||||||
|
"remove": "Removes a resource from another resource",
|
||||||
|
"show": "Shows details about a specified resource",
|
||||||
|
"switch": "Performs a switch operation (e.g. switching between logged in accounts)",
|
||||||
|
"unblock": "Unblocks a resource (e.g. an account)",
|
||||||
|
"unfollow": "Unfollows a resource (e.g. an account)",
|
||||||
|
"unmute": "Umutes a specific resource (e.g. an account)",
|
||||||
|
"version": "Prints the application's version and build information",
|
||||||
|
"whoami": "Prints the account that you are currently logged into",
|
||||||
|
}
|
8
internal/version/version.go
Normal file
8
internal/version/version.go
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package version
|
||||||
|
|
||||||
|
var (
|
||||||
|
BinaryVersion string //nolint:gochecknoglobals
|
||||||
|
BuildTime string //nolint:gochecknoglobals
|
||||||
|
GoVersion string //nolint:gochecknoglobals
|
||||||
|
GitCommit string //nolint:gochecknoglobals
|
||||||
|
)
|
|
@ -110,10 +110,21 @@ func Clean() error {
|
||||||
|
|
||||||
// ldflags returns the build flags.
|
// ldflags returns the build flags.
|
||||||
func ldflags() string {
|
func ldflags() string {
|
||||||
ldflagsfmt := "-s -w -X main.binaryVersion=%s -X main.gitCommit=%s -X main.goVersion=%s -X main.buildTime=%s"
|
versionPackage := "codeflow.dananglin.me.uk/apollo/enbas/internal/version"
|
||||||
|
binaryVersionVar := versionPackage + "." + "BinaryVersion"
|
||||||
|
gitCommitVar := versionPackage + "." + "GitCommit"
|
||||||
|
goVersionVar := versionPackage + "." + "GoVersion"
|
||||||
|
buildTimeVar := versionPackage + "." + "BuildTime"
|
||||||
|
ldflagsfmt := "-s -w -X %s=%s -X %s=%s -X %s=%s -X %s=%s"
|
||||||
buildTime := time.Now().UTC().Format(time.RFC3339)
|
buildTime := time.Now().UTC().Format(time.RFC3339)
|
||||||
|
|
||||||
return fmt.Sprintf(ldflagsfmt, version(), gitCommit(), runtime.Version(), buildTime)
|
return fmt.Sprintf(
|
||||||
|
ldflagsfmt,
|
||||||
|
binaryVersionVar, version(),
|
||||||
|
gitCommitVar, gitCommit(),
|
||||||
|
goVersionVar, runtime.Version(),
|
||||||
|
buildTimeVar, buildTime,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// version returns the latest git tag using git describe.
|
// version returns the latest git tag using git describe.
|
||||||
|
|
448
schema/enbas_cli_schema.json
Normal file
448
schema/enbas_cli_schema.json
Normal file
|
@ -0,0 +1,448 @@
|
||||||
|
{
|
||||||
|
"flags": {
|
||||||
|
"account-name": {
|
||||||
|
"type": "StringSliceValue",
|
||||||
|
"description": "The name of the account"
|
||||||
|
},
|
||||||
|
"all-images": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to show all images from a status"
|
||||||
|
},
|
||||||
|
"all-videos": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to show all videos from a status"
|
||||||
|
},
|
||||||
|
"attachment-id": {
|
||||||
|
"type": "StringSliceValue",
|
||||||
|
"description": "The ID of the media attachment"
|
||||||
|
},
|
||||||
|
"add-poll": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to add a poll when creating a status"
|
||||||
|
},
|
||||||
|
"browser": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to view in the your favourite browser"
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The content of the created resource"
|
||||||
|
},
|
||||||
|
"content-type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The type that the contents should be parsed from (valid values are plain and markdown)"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The description of the media attachment that will be used as the alt-text"
|
||||||
|
},
|
||||||
|
"enable-federation": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to federate the status beyond the local timelines"
|
||||||
|
},
|
||||||
|
"enable-likes": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to allow the status to be liked (favourited)"
|
||||||
|
},
|
||||||
|
"enable-replies": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to allow viewers to reply to the status"
|
||||||
|
},
|
||||||
|
"enable-reposts": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to allow the status to be reposted (boosted) by others"
|
||||||
|
},
|
||||||
|
"exclude-boosts": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to exclude statuses that are boosts of another status"
|
||||||
|
},
|
||||||
|
"exclude-replies": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to exclude statuses that are a reply to another status"
|
||||||
|
},
|
||||||
|
"focus": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The focus of the media file"
|
||||||
|
},
|
||||||
|
"from": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The resource type to action the target resource from (e.g. status)"
|
||||||
|
},
|
||||||
|
"from-file": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The file path where to read the contents from"
|
||||||
|
},
|
||||||
|
"full": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to print the build information in full"
|
||||||
|
},
|
||||||
|
"in-reply-to": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the status that you want to reply to"
|
||||||
|
},
|
||||||
|
"instance": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The instance that you want to log into"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ISO 639 language code for this status"
|
||||||
|
},
|
||||||
|
"limit": {
|
||||||
|
"type": "int",
|
||||||
|
"description": "Specify the limit of items to display"
|
||||||
|
},
|
||||||
|
"list-id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the list in question"
|
||||||
|
},
|
||||||
|
"list-title": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The title of the list"
|
||||||
|
},
|
||||||
|
"list-replies-policy": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The replies policy of the list"
|
||||||
|
},
|
||||||
|
"mute-duration": {
|
||||||
|
"type": "TimeDurationValue",
|
||||||
|
"description": "Specify how long the mute should last for. To mute indefinitely, set this to 0s"
|
||||||
|
},
|
||||||
|
"mute-notifications": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to mute notifications as well as posts"
|
||||||
|
},
|
||||||
|
"my-account": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to specify your account"
|
||||||
|
},
|
||||||
|
"notify": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Get notifications from statuses from the account you want to follow"
|
||||||
|
},
|
||||||
|
"only-media": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to show only the statuses with media attachments"
|
||||||
|
},
|
||||||
|
"only-pinned": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to show only the account's pinned statuses"
|
||||||
|
},
|
||||||
|
"only-public": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to show only the account's public posts"
|
||||||
|
},
|
||||||
|
"poll-allows-multiple-choices": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to allow viewers to make multiple choices in the poll"
|
||||||
|
},
|
||||||
|
"poll-expires-in": {
|
||||||
|
"type": "TimeDurationValue",
|
||||||
|
"description": "The duration in which the poll is open for"
|
||||||
|
},
|
||||||
|
"poll-hides-vote-counts": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to hide the vote count until the poll is closed"
|
||||||
|
},
|
||||||
|
"poll-option": {
|
||||||
|
"type": "StringSliceValue",
|
||||||
|
"description": "A poll option. Use this multiple times to set multiple options"
|
||||||
|
},
|
||||||
|
"sensitive": {
|
||||||
|
"type": "BoolPtrValue",
|
||||||
|
"description": "Set to true if the status should be marked as sensitive"
|
||||||
|
},
|
||||||
|
"show-preferences": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to view your posting preferences when viewing your account information"
|
||||||
|
},
|
||||||
|
"show-reposts": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Show reposts from the account you want to follow"
|
||||||
|
},
|
||||||
|
"show-statuses": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to view the statuses created from the account you are viewing"
|
||||||
|
},
|
||||||
|
"skip-relationship": {
|
||||||
|
"type": "bool",
|
||||||
|
"description": "Set to true to skip showing your relationship to the account that you are viewing"
|
||||||
|
},
|
||||||
|
"spoiler-text": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The text to display as the status' warning or subject"
|
||||||
|
},
|
||||||
|
"status-id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the status"
|
||||||
|
},
|
||||||
|
"tag": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the tag"
|
||||||
|
},
|
||||||
|
"timeline-category": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The timeline category"
|
||||||
|
},
|
||||||
|
"to": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The resource type to action the target resource to (e.g. status)"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The type of resource you want to action on (e.g. account, status)"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The visibility of the posted status"
|
||||||
|
},
|
||||||
|
"vote": {
|
||||||
|
"type": "IntSliceValue",
|
||||||
|
"description": "Add a vote to an option in a poll"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"commands": {
|
||||||
|
"accept": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Accepts a request (e.g. a follow request)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"add": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name", "fieldName": "accountNames" },
|
||||||
|
{ "flag": "content", "default": "" },
|
||||||
|
{ "flag": "list-id", "fieldName": "listID", "default": "" },
|
||||||
|
{ "flag": "status-id", "fieldName": "statusID", "default": "" },
|
||||||
|
{ "flag": "to", "fieldName": "toResourceType", "default": "" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" },
|
||||||
|
{ "flag": "vote", "fieldName": "votes" }
|
||||||
|
],
|
||||||
|
"summary": "Adds a resource to another resource",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"block": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Blocks a resource (e.g. an account)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"create": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "add-poll", "default": "false" },
|
||||||
|
{ "flag": "attachment-id", "fieldName": "attachmentIDs" },
|
||||||
|
{ "flag": "content", "default": "" },
|
||||||
|
{ "flag": "content-type", "default": "plain" },
|
||||||
|
{ "flag": "description", "default": "" },
|
||||||
|
{ "flag": "enable-federation", "fieldName": "federated", "default": "true" },
|
||||||
|
{ "flag": "enable-likes", "fieldName": "likeable", "default": "true" },
|
||||||
|
{ "flag": "enable-replies", "fieldName": "replyable", "default": "true" },
|
||||||
|
{ "flag": "enable-reposts", "fieldName": "boostable", "default": "true" },
|
||||||
|
{ "flag": "focus", "default": "" },
|
||||||
|
{ "flag": "from-file", "default": "" },
|
||||||
|
{ "flag": "in-reply-to", "default": "" },
|
||||||
|
{ "flag": "language", "default": "" },
|
||||||
|
{ "flag": "list-replies-policy", "default": "list" },
|
||||||
|
{ "flag": "list-title", "default": "" },
|
||||||
|
{ "flag": "poll-allows-multiple-choices", "default": "false" },
|
||||||
|
{ "flag": "poll-expires-in" },
|
||||||
|
{ "flag": "poll-hides-vote-counts", "default": "false" },
|
||||||
|
{ "flag": "poll-option", "fieldName": "pollOptions" },
|
||||||
|
{ "flag": "sensitive" },
|
||||||
|
{ "flag": "spoiler-text", "default": "" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" },
|
||||||
|
{ "flag": "visibility", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Creates a specific resource",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "list-id", "fieldName": "listID", "default": ""},
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Deletes a specific resource",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"edit": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "attachment-id", "fieldName": "attachmentIDs" },
|
||||||
|
{ "flag": "description", "default": "" },
|
||||||
|
{ "flag": "focus", "default": "" },
|
||||||
|
{ "flag": "list-id", "fieldName": "listID", "default": ""},
|
||||||
|
{ "flag": "list-title", "default": "" },
|
||||||
|
{ "flag": "list-replies-policy", "default": "" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Edit a specific resource",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"follow": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "notify", "default": "false" },
|
||||||
|
{ "flag": "show-reposts", "default": "true" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Follow a resource (e.g. an account)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"init": {
|
||||||
|
"additionalFields": [
|
||||||
|
{ "name": "configDir", "type": "string"}
|
||||||
|
],
|
||||||
|
"flags": [],
|
||||||
|
"summary": "Creates a new configuration file in the specified configuration directory",
|
||||||
|
"useConfig": false,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "instance", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Logs into an account on GoToSocial",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"mute": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "mute-duration" },
|
||||||
|
{ "flag": "mute-notifications", "default": "false" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Mutes a specific resource (e.g. an account)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"reject": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Rejects a request (e.g. a follow request)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"remove": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name", "fieldName": "accountNames" },
|
||||||
|
{ "flag": "from", "fieldName": "fromResourceType", "default": "" },
|
||||||
|
{ "flag": "list-id", "fieldName": "listID", "default": "" },
|
||||||
|
{ "flag": "status-id", "fieldName": "statusID", "default": "" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Removes a resource from another resource",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"show": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "all-images", "fieldName": "getAllImages", "default": "false" },
|
||||||
|
{ "flag": "all-videos", "fieldName": "getAllVideos", "default": "false" },
|
||||||
|
{ "flag": "attachment-id", "fieldName": "attachmentIDs" },
|
||||||
|
{ "flag": "browser", "fieldName": "showInBrowser", "default": "false" },
|
||||||
|
{ "flag": "exclude-boosts", "default": "false" },
|
||||||
|
{ "flag": "exclude-replies", "default": "false" },
|
||||||
|
{ "flag": "from", "fieldName": "fromResourceType", "default": "" },
|
||||||
|
{ "flag": "limit", "default": "20" },
|
||||||
|
{ "flag": "list-id", "fieldName": "listID", "default": "" },
|
||||||
|
{ "flag": "my-account", "default": "false" },
|
||||||
|
{ "flag": "only-media", "default": "false" },
|
||||||
|
{ "flag": "only-pinned", "default": "false" },
|
||||||
|
{ "flag": "only-public", "default": "false" },
|
||||||
|
{ "flag": "show-preferences", "fieldName": "showUserPreferences", "default": "false" },
|
||||||
|
{ "flag": "show-statuses", "default": "false" },
|
||||||
|
{ "flag": "skip-relationship", "fieldName": "skipAccountRelationship", "default": "false" },
|
||||||
|
{ "flag": "status-id", "fieldName": "statusID", "default": "" },
|
||||||
|
{ "flag": "timeline-category", "default": "home" },
|
||||||
|
{ "flag": "tag", "default": "" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Shows details about a specified resource",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"switch": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "to", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Performs a switch operation (e.g. switching between logged in accounts)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"unblock": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Unblocks a resource (e.g. an account)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"unfollow": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Unfollows a resource (e.g. an account)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"unmute": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "account-name" },
|
||||||
|
{ "flag": "type", "fieldName": "resourceType", "default": "" }
|
||||||
|
],
|
||||||
|
"summary": "Umutes a specific resource (e.g. an account)",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [
|
||||||
|
{ "flag": "full", "default": "false" }
|
||||||
|
],
|
||||||
|
"summary": "Prints the application's version and build information",
|
||||||
|
"useConfig": false,
|
||||||
|
"usePrinter": true
|
||||||
|
},
|
||||||
|
"whoami": {
|
||||||
|
"additionalFields": [],
|
||||||
|
"flags": [],
|
||||||
|
"summary": "Prints the account that you are currently logged into",
|
||||||
|
"useConfig": true,
|
||||||
|
"usePrinter": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue