Compare commits
3 commits
795e344198
...
2e455fc9f8
Author | SHA1 | Date | |
---|---|---|---|
2e455fc9f8 | |||
1debd1bbf7 | |||
41770e2b59 |
56 changed files with 1585 additions and 2635 deletions
|
@ -1,160 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
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"`
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
{{- /* 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
|
||||
}
|
|
@ -1,109 +0,0 @@
|
|||
{{- /* 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 }}
|
|
@ -1,18 +0,0 @@
|
|||
{{- /* 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,11 +2,20 @@ package main
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
|
||||
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/usage"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||
)
|
||||
|
||||
var (
|
||||
binaryVersion string //nolint:gochecknoglobals
|
||||
buildTime string //nolint:gochecknoglobals
|
||||
goVersion string //nolint:gochecknoglobals
|
||||
gitCommit string //nolint:gochecknoglobals
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -18,13 +27,23 @@ func main() {
|
|||
func run() error {
|
||||
var (
|
||||
configDir string
|
||||
noColorFlag internalFlag.BoolPtrValue
|
||||
noColor *bool
|
||||
)
|
||||
|
||||
flag.StringVar(&configDir, "config-dir", "", "Specify your config directory")
|
||||
flag.Var(&noColorFlag, "no-color", "Disable ANSI colour output when displaying text on screen")
|
||||
flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error {
|
||||
boolVal, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse %q as a boolean: %w", value, err)
|
||||
}
|
||||
|
||||
flag.Usage = usage.AppUsageFunc()
|
||||
noColor = new(bool)
|
||||
*noColor = boolVal
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
flag.Usage = usageFunc(executor.CommandSummaryMap())
|
||||
|
||||
flag.Parse()
|
||||
|
||||
|
@ -34,16 +53,176 @@ func run() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var noColor bool
|
||||
|
||||
if noColorFlag.Value != nil {
|
||||
noColor = *noColorFlag.Value
|
||||
} else if os.Getenv("NO_COLOR") != "" {
|
||||
noColor = true
|
||||
// If NoColor is still unspecified,
|
||||
// check to see if the NO_COLOR environment variable is set
|
||||
if noColor == nil {
|
||||
noColor = new(bool)
|
||||
if os.Getenv("NO_COLOR") != "" {
|
||||
*noColor = true
|
||||
} else {
|
||||
*noColor = false
|
||||
}
|
||||
}
|
||||
|
||||
command := flag.Arg(0)
|
||||
args := flag.Args()[1:]
|
||||
|
||||
return executor.Execute(command, args, noColor, configDir)
|
||||
var (
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package usage
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
@ -6,11 +6,9 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/version"
|
||||
)
|
||||
|
||||
func AppUsageFunc() func() {
|
||||
func usageFunc(summaries map[string]string) func() {
|
||||
cmds := make([]string, len(summaries))
|
||||
ind := 0
|
||||
|
||||
|
@ -26,8 +24,8 @@ func AppUsageFunc() func() {
|
|||
|
||||
builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n")
|
||||
|
||||
if version.BinaryVersion != "" {
|
||||
builder.WriteString("VERSION:\n " + version.BinaryVersion + "\n\n")
|
||||
if binaryVersion != "" {
|
||||
builder.WriteString("VERSION:\n " + binaryVersion + "\n\n")
|
||||
}
|
||||
|
||||
builder.WriteString("USAGE:\n enbas [flags]\n enbas [flags] [command]\n\nCOMMANDS:")
|
|
@ -39,7 +39,6 @@
|
|||
- [View a list of statuses that you've liked](#view-a-list-of-statuses-that-youve-liked)
|
||||
- [Mute a status](#mute-a-status)
|
||||
- [Unmute a status](#unmute-a-status)
|
||||
- [Vote in a poll within a status](#vote-in-a-poll-within-a-status)
|
||||
- [Polls](#polls)
|
||||
- [Create a poll](#create-a-poll)
|
||||
- [View a poll](#view-a-poll)
|
||||
|
@ -529,21 +528,6 @@ _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
|
||||
|
||||
### Create a poll
|
||||
|
@ -552,11 +536,31 @@ See [Create a status](#create-a-status).
|
|||
|
||||
### View a poll
|
||||
|
||||
You can view a poll within a [status](#view-a-status) or within a [timeline](#view-a-timeline).
|
||||
Prints the poll information to the screen.
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
See [Vote in a poll within a status](#vote-in-a-poll-within-a-status)
|
||||
Add your vote(s) to a poll.
|
||||
|
||||
```
|
||||
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
|
||||
|
||||
|
|
2
go.mod
2
go.mod
|
@ -1,5 +1,5 @@
|
|||
module codeflow.dananglin.me.uk/apollo/enbas
|
||||
|
||||
go 1.22.6
|
||||
go 1.22.5
|
||||
|
||||
require golang.org/x/net v0.26.0
|
||||
|
|
|
@ -5,34 +5,36 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||
)
|
||||
|
||||
const (
|
||||
pollPath string = "/api/v1/polls"
|
||||
)
|
||||
|
||||
// func (g *Client) GetPoll(pollID string) (model.Poll, error) {
|
||||
// url := g.Authentication.Instance + pollPath + "/" + pollID
|
||||
//
|
||||
// var poll model.Poll
|
||||
//
|
||||
// params := requestParameters{
|
||||
// httpMethod: http.MethodGet,
|
||||
// url: url,
|
||||
// requestBody: nil,
|
||||
// contentType: "",
|
||||
// output: &poll,
|
||||
// }
|
||||
//
|
||||
// if err := g.sendRequest(params); err != nil {
|
||||
// return model.Poll{}, fmt.Errorf(
|
||||
// "received an error after sending the request to get the poll: %w",
|
||||
// err,
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// return poll, nil
|
||||
// }
|
||||
func (g *Client) GetPoll(pollID string) (model.Poll, error) {
|
||||
url := g.Authentication.Instance + pollPath + "/" + pollID
|
||||
|
||||
var poll model.Poll
|
||||
|
||||
params := requestParameters{
|
||||
httpMethod: http.MethodGet,
|
||||
url: url,
|
||||
requestBody: nil,
|
||||
contentType: "",
|
||||
output: &poll,
|
||||
}
|
||||
|
||||
if err := g.sendRequest(params); err != nil {
|
||||
return model.Poll{}, fmt.Errorf(
|
||||
"received an error after sending the request to get the poll: %w",
|
||||
err,
|
||||
)
|
||||
}
|
||||
|
||||
return poll, nil
|
||||
}
|
||||
|
||||
func (g *Client) VoteInPoll(pollID string, choices []int) error {
|
||||
form := struct {
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
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
|
||||
}
|
91
internal/executor/accept_or_reject.go
Normal file
91
internal/executor/accept_or_reject.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
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,53 +4,61 @@ import (
|
|||
"fmt"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||
)
|
||||
|
||||
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) {
|
||||
func getAccountID(gtsClient *client.Client, myAccount bool, accountName, path string) (string, error) {
|
||||
var (
|
||||
account model.Account
|
||||
accountID string
|
||||
err error
|
||||
)
|
||||
|
||||
switch {
|
||||
case myAccount:
|
||||
account, err = getMyAccount(gtsClient)
|
||||
accountID, err = getMyAccountID(gtsClient, path)
|
||||
if err != nil {
|
||||
return account, fmt.Errorf("unable to get your account ID: %w", err)
|
||||
return "", fmt.Errorf("unable to get your account ID: %w", err)
|
||||
}
|
||||
case !accountNames.Empty():
|
||||
account, err = getOtherAccount(gtsClient, accountNames)
|
||||
case accountName != "":
|
||||
accountID, err = getTheirAccountID(gtsClient, accountName)
|
||||
if err != nil {
|
||||
return account, fmt.Errorf("unable to get the account ID: %w", err)
|
||||
return "", fmt.Errorf("unable to get their account ID: %w", err)
|
||||
}
|
||||
default:
|
||||
return account, NoAccountSpecifiedError{}
|
||||
return "", NoAccountSpecifiedError{}
|
||||
}
|
||||
|
||||
return account, nil
|
||||
return accountID, nil
|
||||
}
|
||||
|
||||
func getMyAccount(gtsClient *client.Client) (model.Account, error) {
|
||||
account, err := gtsClient.VerifyCredentials()
|
||||
func getTheirAccountID(gtsClient *client.Client, accountURI string) (string, error) {
|
||||
account, err := getAccount(gtsClient, accountURI)
|
||||
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 {
|
||||
return model.Account{}, fmt.Errorf("unable to retrieve your account: %w", err)
|
||||
}
|
||||
|
@ -58,35 +66,11 @@ func getMyAccount(gtsClient *client.Client) (model.Account, error) {
|
|||
return account, nil
|
||||
}
|
||||
|
||||
func getOtherAccount(gtsClient *client.Client, accountNames internalFlag.StringSliceValue) (model.Account, error) {
|
||||
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])
|
||||
func getAccount(gtsClient *client.Client, accountURI string) (model.Account, error) {
|
||||
account, err := gtsClient.GetAccount(accountURI)
|
||||
if err != nil {
|
||||
return model.Account{}, fmt.Errorf("unable to retrieve the account details: %w", err)
|
||||
}
|
||||
|
||||
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,11 +1,55 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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 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 {
|
||||
if a.toResourceType == "" {
|
||||
return FlagNotSetError{flagText: flagTo}
|
||||
|
@ -16,6 +60,7 @@ func (a *AddExecutor) Execute() error {
|
|||
resourceAccount: a.addToAccount,
|
||||
resourceBookmarks: a.addToBookmarks,
|
||||
resourceStatus: a.addToStatus,
|
||||
resourcePoll: a.addToPoll,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[a.toResourceType]
|
||||
|
@ -52,28 +97,28 @@ func (a *AddExecutor) addAccountsToList(gtsClient *client.Client) error {
|
|||
return FlagNotSetError{flagText: flagListID}
|
||||
}
|
||||
|
||||
if a.accountNames.Empty() {
|
||||
if len(a.accountNames) == 0 {
|
||||
return NoAccountSpecifiedError{}
|
||||
}
|
||||
|
||||
accounts, err := getOtherAccounts(gtsClient, a.accountNames)
|
||||
accountIDs := make([]string, len(a.accountNames))
|
||||
|
||||
for ind := range a.accountNames {
|
||||
accountID, err := getTheirAccountID(gtsClient, a.accountNames[ind])
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get the accounts: %w", err)
|
||||
return fmt.Errorf("unable to get the account ID for %s: %w", a.accountNames[ind], err)
|
||||
}
|
||||
|
||||
accountIDs := make([]string, len(accounts))
|
||||
|
||||
for ind := range accounts {
|
||||
relationship, err := gtsClient.GetAccountRelationship(accounts[ind].ID)
|
||||
relationship, err := gtsClient.GetAccountRelationship(accountID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get your relationship to %s: %w", accounts[ind].Acct, err)
|
||||
return fmt.Errorf("unable to get your relationship to %s: %w", a.accountNames[ind], err)
|
||||
}
|
||||
|
||||
if !relationship.Following {
|
||||
return NotFollowingError{Account: accounts[ind].Acct}
|
||||
return NotFollowingError{Account: a.accountNames[ind]}
|
||||
}
|
||||
|
||||
accountIDs[ind] = accounts[ind].ID
|
||||
accountIDs[ind] = accountID
|
||||
}
|
||||
|
||||
if err := gtsClient.AddAccountsToList(a.listID, accountIDs); err != nil {
|
||||
|
@ -102,7 +147,11 @@ func (a *AddExecutor) addToAccount(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error {
|
||||
accountID, err := getAccountID(gtsClient, false, a.accountNames)
|
||||
if len(a.accountNames) != 1 {
|
||||
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 {
|
||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||
}
|
||||
|
@ -162,7 +211,6 @@ func (a *AddExecutor) addToStatus(gtsClient *client.Client) error {
|
|||
resourceStar: a.addStarToStatus,
|
||||
resourceLike: a.addStarToStatus,
|
||||
resourceBoost: a.addBoostToStatus,
|
||||
resourceVote: a.addVoteToStatus,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[a.resourceType]
|
||||
|
@ -196,40 +244,45 @@ func (a *AddExecutor) addBoostToStatus(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *AddExecutor) addVoteToStatus(gtsClient *client.Client) error {
|
||||
if a.votes.Empty() {
|
||||
return NoVotesError{}
|
||||
func (a *AddExecutor) addToPoll(gtsClient *client.Client) error {
|
||||
if a.pollID == "" {
|
||||
return FlagNotSetError{flagText: flagPollID}
|
||||
}
|
||||
|
||||
status, err := gtsClient.GetStatus(a.statusID)
|
||||
funcMap := map[string]func(*client.Client) error{
|
||||
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 {
|
||||
return fmt.Errorf("unable to get the status: %w", err)
|
||||
return fmt.Errorf("unable to retrieve the poll: %w", err)
|
||||
}
|
||||
|
||||
if status.Poll == nil {
|
||||
return NoPollInStatusError{}
|
||||
}
|
||||
|
||||
if status.Poll.Expired {
|
||||
if poll.Expired {
|
||||
return PollClosedError{}
|
||||
}
|
||||
|
||||
if !status.Poll.Multiple && !a.votes.ExpectedLength(1) {
|
||||
if !poll.Multiple && len(a.choices) > 1 {
|
||||
return MultipleChoiceError{}
|
||||
}
|
||||
|
||||
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 {
|
||||
if err := gtsClient.VoteInPoll(a.pollID, []int(a.choices)); err != nil {
|
||||
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
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
|
||||
}
|
95
internal/executor/block_or_unblock.go
Normal file
95
internal/executor/block_or_unblock.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
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
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package executor
|
||||
|
||||
//go:generate go run ../../cmd/enbas-codegen --package executor --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json
|
78
internal/executor/commands.go
Normal file
78
internal/executor/commands.go
Normal file
|
@ -0,0 +1,78 @@
|
|||
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,13 +1,100 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"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/printer"
|
||||
"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
|
||||
description string
|
||||
fromFile string
|
||||
focus string
|
||||
inReplyTo string
|
||||
language string
|
||||
resourceType string
|
||||
listTitle string
|
||||
listRepliesPolicy string
|
||||
spoilerText string
|
||||
visibility string
|
||||
pollExpiresIn TimeDurationFlagValue
|
||||
pollOptions MultiStringFlagValue
|
||||
attachmentIDs 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
|
||||
})
|
||||
createExe.Var(&createExe.attachmentIDs, flagAttachmentID, "Specify the ID of the media attachment to add to the status")
|
||||
|
||||
// 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.StringVar(&createExe.description, flagDescription, "", "The description of the media attachment that will be used as the alt-text")
|
||||
createExe.StringVar(&createExe.focus, flagFocus, "", "The focus of the media file")
|
||||
|
||||
createExe.Usage = commandUsageFunc(name, summary, createExe.FlagSet)
|
||||
|
||||
return &createExe
|
||||
}
|
||||
|
||||
func (c *CreateExecutor) Execute() error {
|
||||
if c.resourceType == "" {
|
||||
return FlagNotSetError{flagText: flagType}
|
||||
|
@ -76,7 +163,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
|||
return fmt.Errorf("unable to get the status contents from %q: %w", c.fromFile, err)
|
||||
}
|
||||
default:
|
||||
if c.attachmentIDs.Empty() {
|
||||
if len(c.attachmentIDs) == 0 {
|
||||
// TODO: revisit this error type
|
||||
return EmptyContentError{
|
||||
ResourceType: resourceStatus,
|
||||
|
@ -85,7 +172,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
|||
}
|
||||
}
|
||||
|
||||
if c.addPoll && !c.attachmentIDs.Empty() {
|
||||
if len(c.attachmentIDs) > 0 && c.addPoll {
|
||||
return fmt.Errorf("attaching media to a poll is not allowed")
|
||||
}
|
||||
|
||||
|
@ -106,8 +193,8 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
|||
visibility = preferences.PostingDefaultVisibility
|
||||
}
|
||||
|
||||
if c.sensitive.Value != nil {
|
||||
sensitive = *c.sensitive.Value
|
||||
if c.sensitive != nil {
|
||||
sensitive = *c.sensitive
|
||||
} else {
|
||||
sensitive = preferences.PostingDefaultSensitive
|
||||
}
|
||||
|
@ -138,7 +225,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
|||
AttachmentIDs: nil,
|
||||
}
|
||||
|
||||
if !c.attachmentIDs.Empty() {
|
||||
if len(c.attachmentIDs) > 0 {
|
||||
form.AttachmentIDs = c.attachmentIDs
|
||||
}
|
||||
|
||||
|
@ -162,7 +249,8 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error {
|
|||
return fmt.Errorf("unable to create the status: %w", err)
|
||||
}
|
||||
|
||||
c.printer.PrintSuccess("Successfully created the status with ID: " + status.ID)
|
||||
c.printer.PrintSuccess("Successfully created the following status:")
|
||||
c.printer.PrintStatus(status)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -177,7 +265,8 @@ func (c *CreateExecutor) createMediaAttachment(gtsClient *client.Client) error {
|
|||
return fmt.Errorf("unable to create the media attachment: %w", err)
|
||||
}
|
||||
|
||||
c.printer.PrintSuccess("Successfully created the media attachment with ID: " + attachment.ID)
|
||||
c.printer.PrintSuccess("Successfully created the following media attachment:")
|
||||
c.printer.PrintMediaAttachment(attachment)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,11 +1,39 @@
|
|||
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 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 {
|
||||
if d.resourceType == "" {
|
||||
return FlagNotSetError{flagText: flagType}
|
||||
|
|
|
@ -1,12 +1,50 @@
|
|||
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/model"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||
)
|
||||
|
||||
type EditExecutor struct {
|
||||
*flag.FlagSet
|
||||
|
||||
printer *printer.Printer
|
||||
config *config.Config
|
||||
attachmentID string
|
||||
description string
|
||||
focus string
|
||||
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 edit")
|
||||
editExe.StringVar(&editExe.attachmentID, flagAttachmentID, "", "Specify the ID of the media attachment to edit")
|
||||
editExe.StringVar(&editExe.listID, flagListID, "", "Specify the ID of the list to edit")
|
||||
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.StringVar(&editExe.description, flagDescription, "", "The description of the media attachment that will be used as the alt-text")
|
||||
editExe.StringVar(&editExe.focus, flagFocus, "", "The focus of the media file")
|
||||
|
||||
editExe.Usage = commandUsageFunc(name, summary, editExe.FlagSet)
|
||||
|
||||
return &editExe
|
||||
}
|
||||
|
||||
func (e *EditExecutor) Execute() error {
|
||||
if e.resourceType == "" {
|
||||
return FlagNotSetError{flagText: flagType}
|
||||
|
@ -65,15 +103,11 @@ func (e *EditExecutor) editList(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
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,
|
||||
)
|
||||
if e.attachmentID == "" {
|
||||
return FlagNotSetError{flagText: flagAttachmentID}
|
||||
}
|
||||
|
||||
attachment, err := gtsClient.GetMediaAttachment(e.attachmentIDs[0])
|
||||
attachment, err := gtsClient.GetMediaAttachment(e.attachmentID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get the media attachment: %w", err)
|
||||
}
|
||||
|
@ -88,11 +122,13 @@ func (e *EditExecutor) editMediaAttachment(gtsClient *client.Client) error {
|
|||
focus = fmt.Sprintf("%f,%f", attachment.Meta.Focus.X, attachment.Meta.Focus.Y)
|
||||
}
|
||||
|
||||
if _, err = gtsClient.UpdateMediaAttachment(e.attachmentIDs[0], description, focus); err != nil {
|
||||
updatedAttachment, err := gtsClient.UpdateMediaAttachment(e.attachmentID, description, focus)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to update the media attachment: %w", err)
|
||||
}
|
||||
|
||||
e.printer.PrintSuccess("Successfully edited the media attachment.")
|
||||
e.printer.PrintMediaAttachment(updatedAttachment)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -104,24 +104,6 @@ func (e NoPollOptionError) Error() string {
|
|||
" 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 {
|
||||
Account string
|
||||
}
|
||||
|
|
|
@ -1,160 +0,0 @@
|
|||
/*
|
||||
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
|
||||
}
|
21
internal/executor/executor.go
Normal file
21
internal/executor/executor.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
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
|
||||
}
|
|
@ -1,651 +0,0 @@
|
|||
/*
|
||||
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,16 +1,122 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
flagAddPoll = "add-poll"
|
||||
flagAccountName = "account-name"
|
||||
flagAllImages = "all-images"
|
||||
flagAllVideos = "all-videos"
|
||||
flagAttachmentID = "attachment-id"
|
||||
flagBrowser = "browser"
|
||||
flagContentType = "content-type"
|
||||
flagContent = "content"
|
||||
flagDescription = "description"
|
||||
flagEnableFederation = "enable-federation"
|
||||
flagEnableLikes = "enable-likes"
|
||||
flagEnableReplies = "enable-replies"
|
||||
flagEnableReposts = "enable-reposts"
|
||||
flagExcludeBoosts = "exclude-boosts"
|
||||
flagExcludeReplies = "exclude-replies"
|
||||
flagFocus = "focus"
|
||||
flagFrom = "from"
|
||||
flagFromFile = "from-file"
|
||||
flagFull = "full"
|
||||
flagInReplyTo = "in-reply-to"
|
||||
flagInstance = "instance"
|
||||
flagLanguage = "language"
|
||||
flagLimit = "limit"
|
||||
flagListID = "list-id"
|
||||
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"
|
||||
flagSensitive = "sensitive"
|
||||
flagSkipRelationship = "skip-relationship"
|
||||
flagShowPreferences = "show-preferences"
|
||||
flagShowReposts = "show-reposts"
|
||||
flagShowStatuses = "show-statuses"
|
||||
flagSpoilerText = "spoiler-text"
|
||||
flagStatusID = "status-id"
|
||||
flagTag = "tag"
|
||||
flagTimelineCategory = "timeline-category"
|
||||
flagTo = "to"
|
||||
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
|
||||
}
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
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
|
||||
}
|
101
internal/executor/follow_or_unfollow.go
Normal file
101
internal/executor/follow_or_unfollow.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
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,12 +1,34 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||
"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 {
|
||||
if err := utilities.EnsureDirectory(i.configDir); err != nil {
|
||||
return fmt.Errorf("unable to ensure that the configuration directory is present: %w", err)
|
||||
|
|
|
@ -1,14 +1,40 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||
"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 {
|
||||
var err error
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
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
|
||||
}
|
108
internal/executor/mute_or_unmute.go
Normal file
108
internal/executor/mute_or_unmute.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
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
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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,11 +1,48 @@
|
|||
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 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 {
|
||||
if r.fromResourceType == "" {
|
||||
return FlagNotSetError{flagText: flagFrom}
|
||||
|
@ -52,19 +89,19 @@ func (r *RemoveExecutor) removeAccountsFromList(gtsClient *client.Client) error
|
|||
return FlagNotSetError{flagText: flagListID}
|
||||
}
|
||||
|
||||
if r.accountNames.Empty() {
|
||||
if len(r.accountNames) == 0 {
|
||||
return NoAccountSpecifiedError{}
|
||||
}
|
||||
|
||||
accounts, err := getOtherAccounts(gtsClient, r.accountNames)
|
||||
accountIDs := make([]string, len(r.accountNames))
|
||||
|
||||
for ind := range r.accountNames {
|
||||
accountID, err := getTheirAccountID(gtsClient, r.accountNames[ind])
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get the accounts: %w", err)
|
||||
return fmt.Errorf("unable to get the account ID for %s: %w", r.accountNames[ind], err)
|
||||
}
|
||||
|
||||
accountIDs := make([]string, len(accounts))
|
||||
|
||||
for ind := range accounts {
|
||||
accountIDs[ind] = accounts[ind].ID
|
||||
accountIDs[ind] = accountID
|
||||
}
|
||||
|
||||
if err := gtsClient.RemoveAccountsFromList(r.listID, accountIDs); err != nil {
|
||||
|
@ -93,7 +130,11 @@ func (r *RemoveExecutor) removeFromAccount(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
func (r *RemoveExecutor) removeNoteFromAccount(gtsClient *client.Client) error {
|
||||
accountID, err := getAccountID(gtsClient, false, r.accountNames)
|
||||
if len(r.accountNames) != 1 {
|
||||
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 {
|
||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||
}
|
||||
|
|
|
@ -1,15 +1,83 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"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/model"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||
"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 {
|
||||
if s.resourceType == "" {
|
||||
return FlagNotSetError{flagText: flagType}
|
||||
|
@ -28,6 +96,7 @@ func (s *ShowExecutor) Execute() error {
|
|||
resourceLiked: s.showLiked,
|
||||
resourceStarred: s.showLiked,
|
||||
resourceFollowRequest: s.showFollowRequests,
|
||||
resourcePoll: s.showPoll,
|
||||
resourceMutedAccounts: s.showMutedAccounts,
|
||||
resourceMedia: s.showMedia,
|
||||
resourceMediaAttachment: s.showMediaAttachment,
|
||||
|
@ -58,9 +127,25 @@ func (s *ShowExecutor) showInstance(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
||||
account, err := getAccount(gtsClient, s.myAccount, s.accountName)
|
||||
var (
|
||||
account model.Account
|
||||
err error
|
||||
)
|
||||
|
||||
if s.myAccount {
|
||||
account, err = getMyAccount(gtsClient, s.config.CredentialsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get the account information: %w", err)
|
||||
return fmt.Errorf("received an error while getting the account details: %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 {
|
||||
|
@ -75,7 +160,6 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
|||
relationship *model.AccountRelationship
|
||||
preferences *model.Preferences
|
||||
statuses *model.StatusList
|
||||
myAccountID string
|
||||
)
|
||||
|
||||
if !s.myAccount && !s.skipAccountRelationship {
|
||||
|
@ -85,15 +169,12 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
|||
}
|
||||
}
|
||||
|
||||
if s.myAccount {
|
||||
myAccountID = account.ID
|
||||
if s.showUserPreferences {
|
||||
if s.myAccount && s.showUserPreferences {
|
||||
preferences, err = gtsClient.GetUserPreferences()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to retrieve the user preferences: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.showStatuses {
|
||||
form := client.GetAccountStatusesForm{
|
||||
|
@ -112,7 +193,7 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
|
|||
}
|
||||
}
|
||||
|
||||
s.printer.PrintAccount(account, relationship, preferences, statuses, myAccountID)
|
||||
s.printer.PrintAccount(account, relationship, preferences, statuses)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -135,12 +216,7 @@ func (s *ShowExecutor) showStatus(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
s.printer.PrintStatus(status)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -189,12 +265,7 @@ func (s *ShowExecutor) showTimeline(gtsClient *client.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
s.printer.PrintStatusList(timeline)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -266,7 +337,7 @@ func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
func (s *ShowExecutor) showFollowersFromAccount(gtsClient *client.Client) error {
|
||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName)
|
||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||
}
|
||||
|
@ -306,7 +377,7 @@ func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
func (s *ShowExecutor) showFollowingFromAccount(gtsClient *client.Client) error {
|
||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName)
|
||||
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("received an error while getting the account ID: %w", err)
|
||||
}
|
||||
|
@ -347,12 +418,7 @@ func (s *ShowExecutor) showBookmarks(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
if len(bookmarks.Statuses) > 0 {
|
||||
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)
|
||||
s.printer.PrintStatusList(bookmarks)
|
||||
} else {
|
||||
s.printer.PrintInfo("You have no bookmarks.\n")
|
||||
}
|
||||
|
@ -367,12 +433,7 @@ func (s *ShowExecutor) showLiked(gtsClient *client.Client) error {
|
|||
}
|
||||
|
||||
if len(liked.Statuses) > 0 {
|
||||
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)
|
||||
s.printer.PrintStatusList(liked)
|
||||
} else {
|
||||
s.printer.PrintInfo("You have no " + s.resourceType + " statuses.\n")
|
||||
}
|
||||
|
@ -395,6 +456,21 @@ func (s *ShowExecutor) showFollowRequests(gtsClient *client.Client) error {
|
|||
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 {
|
||||
muted, err := gtsClient.GetMutedAccounts(s.limit)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,38 +1,61 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"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 {
|
||||
funcMap := map[string]func() error{
|
||||
resourceAccount: s.switchToAccount,
|
||||
}
|
||||
|
||||
doFunc, ok := funcMap[s.to]
|
||||
doFunc, ok := funcMap[s.toResourceType]
|
||||
if !ok {
|
||||
return UnsupportedTypeError{resourceType: s.to}
|
||||
return UnsupportedTypeError{resourceType: s.toResourceType}
|
||||
}
|
||||
|
||||
return doFunc()
|
||||
}
|
||||
|
||||
func (s *SwitchExecutor) switchToAccount() error {
|
||||
expectedNumAccountNames := 1
|
||||
if !s.accountName.ExpectedLength(expectedNumAccountNames) {
|
||||
return fmt.Errorf(
|
||||
"found an unexpected number of --account-name flags: expected %d",
|
||||
expectedNumAccountNames,
|
||||
)
|
||||
if s.accountName == "" {
|
||||
return NoAccountSpecifiedError{}
|
||||
}
|
||||
|
||||
if err := config.UpdateCurrentAccount(s.accountName[0], s.config.CredentialsFile); err != nil {
|
||||
if err := config.UpdateCurrentAccount(s.accountName, s.config.CredentialsFile); err != nil {
|
||||
return fmt.Errorf("unable to switch account to the account: %w", err)
|
||||
}
|
||||
|
||||
s.printer.PrintSuccess("The current account is now set to '" + s.accountName[0] + "'.")
|
||||
s.printer.PrintSuccess("The current account is now set to '" + s.accountName + "'.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
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,4 +1,4 @@
|
|||
package usage
|
||||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
@ -6,8 +6,8 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
// ExecutorUsageFunc returns the function used to print a command's help page.
|
||||
func ExecutorUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
||||
// commandUsageFunc returns the function used to print a command's help page.
|
||||
func commandUsageFunc(name, summary string, flagset *flag.FlagSet) func() {
|
||||
return func() {
|
||||
var builder strings.Builder
|
||||
|
|
@ -1,7 +1,51 @@
|
|||
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 {
|
||||
v.printer.PrintVersion(v.full)
|
||||
v.printer.PrintVersion(v.showFullVersion, v.binaryVersion, v.buildTime, v.goVersion, v.gitCommit)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,18 +1,40 @@
|
|||
package executor
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
|
||||
)
|
||||
|
||||
func (e *WhoamiExecutor) Execute() error {
|
||||
config, err := config.NewCredentialsConfigFromFile(e.config.CredentialsFile)
|
||||
type WhoAmIExecutor struct {
|
||||
*flag.FlagSet
|
||||
|
||||
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 {
|
||||
return fmt.Errorf("unable to load the credential config: %w", err)
|
||||
}
|
||||
|
||||
e.printer.PrintInfo("You are logged in as '" + config.CurrentAccount + "'.\n")
|
||||
c.printer.PrintInfo("You are logged in as '" + config.CurrentAccount + "'.\n")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
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
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
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,7 +12,6 @@ func (p Printer) PrintAccount(
|
|||
relationship *model.AccountRelationship,
|
||||
preferences *model.Preferences,
|
||||
statuses *model.StatusList,
|
||||
userAccountID string,
|
||||
) {
|
||||
var builder strings.Builder
|
||||
|
||||
|
@ -48,7 +47,7 @@ func (p Printer) PrintAccount(
|
|||
}
|
||||
|
||||
if statuses != nil {
|
||||
builder.WriteString("\n\n" + p.statusList(*statuses, userAccountID))
|
||||
builder.WriteString("\n\n" + p.statusList(*statuses))
|
||||
}
|
||||
|
||||
builder.WriteString("\n\n")
|
||||
|
|
91
internal/printer/poll.go
Normal file
91
internal/printer/poll.go
Normal file
|
@ -0,0 +1,91 @@
|
|||
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."
|
||||
symbolBullet = "\u2022"
|
||||
symbolPollMeter = "\u2501"
|
||||
symbolCheckMark = "\u2714"
|
||||
symbolSuccess = "\u2714"
|
||||
symbolFailure = "\u2717"
|
||||
symbolImage = "\uf03e"
|
||||
symbolLiked = "\uf51f"
|
||||
|
@ -78,9 +78,9 @@ func NewPrinter(
|
|||
}
|
||||
|
||||
func (p Printer) PrintSuccess(text string) {
|
||||
success := p.theme.boldgreen + symbolCheckMark + p.theme.reset
|
||||
success := p.theme.boldgreen + symbolSuccess + p.theme.reset
|
||||
if p.noColor {
|
||||
success = symbolCheckMark
|
||||
success = symbolSuccess
|
||||
}
|
||||
|
||||
printToStdout(success + " " + text + "\n")
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
package printer
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/model"
|
||||
)
|
||||
|
||||
func (p Printer) PrintStatus(status model.Status, userAccountID string) {
|
||||
func (p Printer) PrintStatus(status model.Status) {
|
||||
var builder strings.Builder
|
||||
|
||||
// The account information
|
||||
|
@ -42,13 +41,7 @@ func (p Printer) PrintStatus(status model.Status, userAccountID string) {
|
|||
|
||||
// If a poll exists in a status, write the contents to the builder.
|
||||
if status.Poll != nil {
|
||||
pollOwner := false
|
||||
if status.Account.ID == userAccountID {
|
||||
pollOwner = true
|
||||
}
|
||||
|
||||
builder.WriteString("\n\n" + p.headerFormat("POLL DETAILS:"))
|
||||
builder.WriteString(p.pollDetails(*status.Poll, pollOwner))
|
||||
builder.WriteString(p.pollOptions(*status.Poll))
|
||||
}
|
||||
|
||||
// Status creation time
|
||||
|
@ -79,18 +72,17 @@ func (p Printer) PrintStatus(status model.Status, userAccountID string) {
|
|||
p.print(builder.String())
|
||||
}
|
||||
|
||||
func (p Printer) PrintStatusList(list model.StatusList, userAccountID string) {
|
||||
p.print(p.statusList(list, userAccountID))
|
||||
func (p Printer) PrintStatusList(list model.StatusList) {
|
||||
p.print(p.statusList(list))
|
||||
}
|
||||
|
||||
func (p Printer) statusList(list model.StatusList, userAccountID string) string {
|
||||
func (p Printer) statusList(list model.StatusList) string {
|
||||
var builder strings.Builder
|
||||
|
||||
builder.WriteString(p.headerFormat(list.Name) + "\n")
|
||||
|
||||
for _, status := range list.Statuses {
|
||||
statusID := status.ID
|
||||
statusOwnerID := status.Account.ID
|
||||
createdAt := p.formatDateTime(status.CreatedAt)
|
||||
boostedAt := ""
|
||||
content := status.Content
|
||||
|
@ -108,7 +100,6 @@ func (p Printer) statusList(list model.StatusList, userAccountID string) string
|
|||
))
|
||||
|
||||
statusID = status.Reblog.ID
|
||||
statusOwnerID = status.Reblog.Account.ID
|
||||
createdAt = p.formatDateTime(status.Reblog.CreatedAt)
|
||||
boostedAt = p.formatDateTime(status.CreatedAt)
|
||||
content = status.Reblog.Content
|
||||
|
@ -129,12 +120,7 @@ func (p Printer) statusList(list model.StatusList, userAccountID string) string
|
|||
builder.WriteString("\n" + p.convertHTMLToText(content, true))
|
||||
|
||||
if poll != nil {
|
||||
pollOwner := false
|
||||
if statusOwnerID == userAccountID {
|
||||
pollOwner = true
|
||||
}
|
||||
|
||||
builder.WriteString(p.pollDetails(*poll, pollOwner))
|
||||
builder.WriteString(p.pollOptions(*poll))
|
||||
}
|
||||
|
||||
for _, media := range mediaAttachments {
|
||||
|
@ -186,78 +172,3 @@ func (p Printer) statusList(list model.StatusList, userAccountID string) 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,13 +3,11 @@ package printer
|
|||
import (
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"codeflow.dananglin.me.uk/apollo/enbas/internal/version"
|
||||
)
|
||||
|
||||
func (p Printer) PrintVersion(showFullVersion bool) {
|
||||
func (p Printer) PrintVersion(showFullVersion bool, binaryVersion, buildTime, goVersion, gitCommit string) {
|
||||
if !showFullVersion {
|
||||
printToStdout("Enbas " + version.BinaryVersion + "\n")
|
||||
printToStdout("Enbas " + binaryVersion + "\n")
|
||||
|
||||
return
|
||||
}
|
||||
|
@ -20,10 +18,10 @@ func (p Printer) PrintVersion(showFullVersion bool) {
|
|||
|
||||
tableWriter := tabwriter.NewWriter(&builder, 0, 4, 1, ' ', 0)
|
||||
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Version:") + "\t" + version.BinaryVersion + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Git commit:") + "\t" + version.GitCommit + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Go version:") + "\t" + version.GoVersion + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Build date:") + "\t" + version.BuildTime + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Version:") + "\t" + binaryVersion + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Git commit:") + "\t" + gitCommit + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Go version:") + "\t" + goVersion + "\n"))
|
||||
_, _ = tableWriter.Write([]byte(p.fieldFormat("Build date:") + "\t" + buildTime + "\n"))
|
||||
|
||||
tableWriter.Flush()
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
package usage
|
||||
|
||||
//go:generate go run ../../cmd/enbas-codegen --package usage --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
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",
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package version
|
||||
|
||||
var (
|
||||
BinaryVersion string //nolint:gochecknoglobals
|
||||
BuildTime string //nolint:gochecknoglobals
|
||||
GoVersion string //nolint:gochecknoglobals
|
||||
GitCommit string //nolint:gochecknoglobals
|
||||
)
|
|
@ -110,21 +110,10 @@ func Clean() error {
|
|||
|
||||
// ldflags returns the build flags.
|
||||
func ldflags() string {
|
||||
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"
|
||||
ldflagsfmt := "-s -w -X main.binaryVersion=%s -X main.gitCommit=%s -X main.goVersion=%s -X main.buildTime=%s"
|
||||
buildTime := time.Now().UTC().Format(time.RFC3339)
|
||||
|
||||
return fmt.Sprintf(
|
||||
ldflagsfmt,
|
||||
binaryVersionVar, version(),
|
||||
gitCommitVar, gitCommit(),
|
||||
goVersionVar, runtime.Version(),
|
||||
buildTimeVar, buildTime,
|
||||
)
|
||||
return fmt.Sprintf(ldflagsfmt, version(), gitCommit(), runtime.Version(), buildTime)
|
||||
}
|
||||
|
||||
// version returns the latest git tag using git describe.
|
||||
|
|
|
@ -1,448 +0,0 @@
|
|||
{
|
||||
"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